##// END OF EJS Templates
Merge with crew-stable
Patrick Mezard -
r6656:2cbe0f72 merge default
parent child Browse files
Show More
@@ -1,1188 +1,1194 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 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 hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, bisect, stat
10 import os, sys, bisect, stat
11 import mdiff, bdiff, util, templater, templatefilters, patch, errno
11 import mdiff, bdiff, util, templater, templatefilters, patch, errno
12 import match as _match
12 import match as _match
13
13
14 revrangesep = ':'
14 revrangesep = ':'
15
15
16 class UnknownCommand(Exception):
16 class UnknownCommand(Exception):
17 """Exception raised if command is not in the command table."""
17 """Exception raised if command is not in the command table."""
18 class AmbiguousCommand(Exception):
18 class AmbiguousCommand(Exception):
19 """Exception raised if command shortcut matches more than one command."""
19 """Exception raised if command shortcut matches more than one command."""
20
20
21 def findpossible(ui, cmd, table):
21 def findpossible(ui, cmd, table):
22 """
22 """
23 Return cmd -> (aliases, command table entry)
23 Return cmd -> (aliases, command table entry)
24 for each matching command.
24 for each matching command.
25 Return debug commands (or their aliases) only if no normal command matches.
25 Return debug commands (or their aliases) only if no normal command matches.
26 """
26 """
27 choice = {}
27 choice = {}
28 debugchoice = {}
28 debugchoice = {}
29 for e in table.keys():
29 for e in table.keys():
30 aliases = e.lstrip("^").split("|")
30 aliases = e.lstrip("^").split("|")
31 found = None
31 found = None
32 if cmd in aliases:
32 if cmd in aliases:
33 found = cmd
33 found = cmd
34 elif not ui.config("ui", "strict"):
34 elif not ui.config("ui", "strict"):
35 for a in aliases:
35 for a in aliases:
36 if a.startswith(cmd):
36 if a.startswith(cmd):
37 found = a
37 found = a
38 break
38 break
39 if found is not None:
39 if found is not None:
40 if aliases[0].startswith("debug") or found.startswith("debug"):
40 if aliases[0].startswith("debug") or found.startswith("debug"):
41 debugchoice[found] = (aliases, table[e])
41 debugchoice[found] = (aliases, table[e])
42 else:
42 else:
43 choice[found] = (aliases, table[e])
43 choice[found] = (aliases, table[e])
44
44
45 if not choice and debugchoice:
45 if not choice and debugchoice:
46 choice = debugchoice
46 choice = debugchoice
47
47
48 return choice
48 return choice
49
49
50 def findcmd(ui, cmd, table):
50 def findcmd(ui, cmd, table):
51 """Return (aliases, command table entry) for command string."""
51 """Return (aliases, command table entry) for command string."""
52 choice = findpossible(ui, cmd, table)
52 choice = findpossible(ui, cmd, table)
53
53
54 if cmd in choice:
54 if cmd in choice:
55 return choice[cmd]
55 return choice[cmd]
56
56
57 if len(choice) > 1:
57 if len(choice) > 1:
58 clist = choice.keys()
58 clist = choice.keys()
59 clist.sort()
59 clist.sort()
60 raise AmbiguousCommand(cmd, clist)
60 raise AmbiguousCommand(cmd, clist)
61
61
62 if choice:
62 if choice:
63 return choice.values()[0]
63 return choice.values()[0]
64
64
65 raise UnknownCommand(cmd)
65 raise UnknownCommand(cmd)
66
66
67 def bail_if_changed(repo):
67 def bail_if_changed(repo):
68 if repo.dirstate.parents()[1] != nullid:
68 if repo.dirstate.parents()[1] != nullid:
69 raise util.Abort(_('outstanding uncommitted merge'))
69 raise util.Abort(_('outstanding uncommitted merge'))
70 modified, added, removed, deleted = repo.status()[:4]
70 modified, added, removed, deleted = repo.status()[:4]
71 if modified or added or removed or deleted:
71 if modified or added or removed or deleted:
72 raise util.Abort(_("outstanding uncommitted changes"))
72 raise util.Abort(_("outstanding uncommitted changes"))
73
73
74 def logmessage(opts):
74 def logmessage(opts):
75 """ get the log message according to -m and -l option """
75 """ get the log message according to -m and -l option """
76 message = opts['message']
76 message = opts['message']
77 logfile = opts['logfile']
77 logfile = opts['logfile']
78
78
79 if message and logfile:
79 if message and logfile:
80 raise util.Abort(_('options --message and --logfile are mutually '
80 raise util.Abort(_('options --message and --logfile are mutually '
81 'exclusive'))
81 'exclusive'))
82 if not message and logfile:
82 if not message and logfile:
83 try:
83 try:
84 if logfile == '-':
84 if logfile == '-':
85 message = sys.stdin.read()
85 message = sys.stdin.read()
86 else:
86 else:
87 message = open(logfile).read()
87 message = open(logfile).read()
88 except IOError, inst:
88 except IOError, inst:
89 raise util.Abort(_("can't read commit message '%s': %s") %
89 raise util.Abort(_("can't read commit message '%s': %s") %
90 (logfile, inst.strerror))
90 (logfile, inst.strerror))
91 return message
91 return message
92
92
93 def loglimit(opts):
93 def loglimit(opts):
94 """get the log limit according to option -l/--limit"""
94 """get the log limit according to option -l/--limit"""
95 limit = opts.get('limit')
95 limit = opts.get('limit')
96 if limit:
96 if limit:
97 try:
97 try:
98 limit = int(limit)
98 limit = int(limit)
99 except ValueError:
99 except ValueError:
100 raise util.Abort(_('limit must be a positive integer'))
100 raise util.Abort(_('limit must be a positive integer'))
101 if limit <= 0: raise util.Abort(_('limit must be positive'))
101 if limit <= 0: raise util.Abort(_('limit must be positive'))
102 else:
102 else:
103 limit = sys.maxint
103 limit = sys.maxint
104 return limit
104 return limit
105
105
106 def setremoteconfig(ui, opts):
106 def setremoteconfig(ui, opts):
107 "copy remote options to ui tree"
107 "copy remote options to ui tree"
108 if opts.get('ssh'):
108 if opts.get('ssh'):
109 ui.setconfig("ui", "ssh", opts['ssh'])
109 ui.setconfig("ui", "ssh", opts['ssh'])
110 if opts.get('remotecmd'):
110 if opts.get('remotecmd'):
111 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
111 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
112
112
113 def revpair(repo, revs):
113 def revpair(repo, revs):
114 '''return pair of nodes, given list of revisions. second item can
114 '''return pair of nodes, given list of revisions. second item can
115 be None, meaning use working dir.'''
115 be None, meaning use working dir.'''
116
116
117 def revfix(repo, val, defval):
117 def revfix(repo, val, defval):
118 if not val and val != 0 and defval is not None:
118 if not val and val != 0 and defval is not None:
119 val = defval
119 val = defval
120 return repo.lookup(val)
120 return repo.lookup(val)
121
121
122 if not revs:
122 if not revs:
123 return repo.dirstate.parents()[0], None
123 return repo.dirstate.parents()[0], None
124 end = None
124 end = None
125 if len(revs) == 1:
125 if len(revs) == 1:
126 if revrangesep in revs[0]:
126 if revrangesep in revs[0]:
127 start, end = revs[0].split(revrangesep, 1)
127 start, end = revs[0].split(revrangesep, 1)
128 start = revfix(repo, start, 0)
128 start = revfix(repo, start, 0)
129 end = revfix(repo, end, repo.changelog.count() - 1)
129 end = revfix(repo, end, repo.changelog.count() - 1)
130 else:
130 else:
131 start = revfix(repo, revs[0], None)
131 start = revfix(repo, revs[0], None)
132 elif len(revs) == 2:
132 elif len(revs) == 2:
133 if revrangesep in revs[0] or revrangesep in revs[1]:
133 if revrangesep in revs[0] or revrangesep in revs[1]:
134 raise util.Abort(_('too many revisions specified'))
134 raise util.Abort(_('too many revisions specified'))
135 start = revfix(repo, revs[0], None)
135 start = revfix(repo, revs[0], None)
136 end = revfix(repo, revs[1], None)
136 end = revfix(repo, revs[1], None)
137 else:
137 else:
138 raise util.Abort(_('too many revisions specified'))
138 raise util.Abort(_('too many revisions specified'))
139 return start, end
139 return start, end
140
140
141 def revrange(repo, revs):
141 def revrange(repo, revs):
142 """Yield revision as strings from a list of revision specifications."""
142 """Yield revision as strings from a list of revision specifications."""
143
143
144 def revfix(repo, val, defval):
144 def revfix(repo, val, defval):
145 if not val and val != 0 and defval is not None:
145 if not val and val != 0 and defval is not None:
146 return defval
146 return defval
147 return repo.changelog.rev(repo.lookup(val))
147 return repo.changelog.rev(repo.lookup(val))
148
148
149 seen, l = {}, []
149 seen, l = {}, []
150 for spec in revs:
150 for spec in revs:
151 if revrangesep in spec:
151 if revrangesep in spec:
152 start, end = spec.split(revrangesep, 1)
152 start, end = spec.split(revrangesep, 1)
153 start = revfix(repo, start, 0)
153 start = revfix(repo, start, 0)
154 end = revfix(repo, end, repo.changelog.count() - 1)
154 end = revfix(repo, end, repo.changelog.count() - 1)
155 step = start > end and -1 or 1
155 step = start > end and -1 or 1
156 for rev in xrange(start, end+step, step):
156 for rev in xrange(start, end+step, step):
157 if rev in seen:
157 if rev in seen:
158 continue
158 continue
159 seen[rev] = 1
159 seen[rev] = 1
160 l.append(rev)
160 l.append(rev)
161 else:
161 else:
162 rev = revfix(repo, spec, None)
162 rev = revfix(repo, spec, None)
163 if rev in seen:
163 if rev in seen:
164 continue
164 continue
165 seen[rev] = 1
165 seen[rev] = 1
166 l.append(rev)
166 l.append(rev)
167
167
168 return l
168 return l
169
169
170 def make_filename(repo, pat, node,
170 def make_filename(repo, pat, node,
171 total=None, seqno=None, revwidth=None, pathname=None):
171 total=None, seqno=None, revwidth=None, pathname=None):
172 node_expander = {
172 node_expander = {
173 'H': lambda: hex(node),
173 'H': lambda: hex(node),
174 'R': lambda: str(repo.changelog.rev(node)),
174 'R': lambda: str(repo.changelog.rev(node)),
175 'h': lambda: short(node),
175 'h': lambda: short(node),
176 }
176 }
177 expander = {
177 expander = {
178 '%': lambda: '%',
178 '%': lambda: '%',
179 'b': lambda: os.path.basename(repo.root),
179 'b': lambda: os.path.basename(repo.root),
180 }
180 }
181
181
182 try:
182 try:
183 if node:
183 if node:
184 expander.update(node_expander)
184 expander.update(node_expander)
185 if node:
185 if node:
186 expander['r'] = (lambda:
186 expander['r'] = (lambda:
187 str(repo.changelog.rev(node)).zfill(revwidth or 0))
187 str(repo.changelog.rev(node)).zfill(revwidth or 0))
188 if total is not None:
188 if total is not None:
189 expander['N'] = lambda: str(total)
189 expander['N'] = lambda: str(total)
190 if seqno is not None:
190 if seqno is not None:
191 expander['n'] = lambda: str(seqno)
191 expander['n'] = lambda: str(seqno)
192 if total is not None and seqno is not None:
192 if total is not None and seqno is not None:
193 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
193 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
194 if pathname is not None:
194 if pathname is not None:
195 expander['s'] = lambda: os.path.basename(pathname)
195 expander['s'] = lambda: os.path.basename(pathname)
196 expander['d'] = lambda: os.path.dirname(pathname) or '.'
196 expander['d'] = lambda: os.path.dirname(pathname) or '.'
197 expander['p'] = lambda: pathname
197 expander['p'] = lambda: pathname
198
198
199 newname = []
199 newname = []
200 patlen = len(pat)
200 patlen = len(pat)
201 i = 0
201 i = 0
202 while i < patlen:
202 while i < patlen:
203 c = pat[i]
203 c = pat[i]
204 if c == '%':
204 if c == '%':
205 i += 1
205 i += 1
206 c = pat[i]
206 c = pat[i]
207 c = expander[c]()
207 c = expander[c]()
208 newname.append(c)
208 newname.append(c)
209 i += 1
209 i += 1
210 return ''.join(newname)
210 return ''.join(newname)
211 except KeyError, inst:
211 except KeyError, inst:
212 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
212 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
213 inst.args[0])
213 inst.args[0])
214
214
215 def make_file(repo, pat, node=None,
215 def make_file(repo, pat, node=None,
216 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
216 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
217 if not pat or pat == '-':
217 if not pat or pat == '-':
218 return 'w' in mode and sys.stdout or sys.stdin
218 return 'w' in mode and sys.stdout or sys.stdin
219 if hasattr(pat, 'write') and 'w' in mode:
219 if hasattr(pat, 'write') and 'w' in mode:
220 return pat
220 return pat
221 if hasattr(pat, 'read') and 'r' in mode:
221 if hasattr(pat, 'read') and 'r' in mode:
222 return pat
222 return pat
223 return open(make_filename(repo, pat, node, total, seqno, revwidth,
223 return open(make_filename(repo, pat, node, total, seqno, revwidth,
224 pathname),
224 pathname),
225 mode)
225 mode)
226
226
227 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
227 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
228 if not globbed and default == 'relpath':
228 if not globbed and default == 'relpath':
229 pats = util.expand_glob(pats or [])
229 pats = util.expand_glob(pats or [])
230 m = _match.match(repo.root, repo.getcwd(), pats,
230 m = _match.match(repo.root, repo.getcwd(), pats,
231 opts.get('include'), opts.get('exclude'), default)
231 opts.get('include'), opts.get('exclude'), default)
232 def badfn(f, msg):
232 def badfn(f, msg):
233 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
233 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
234 return False
234 return False
235 m.bad = badfn
235 m.bad = badfn
236 return m
236 return m
237
237
238 def matchall(repo):
238 def matchall(repo):
239 return _match.always(repo.root, repo.getcwd())
239 return _match.always(repo.root, repo.getcwd())
240
240
241 def matchfiles(repo, files):
241 def matchfiles(repo, files):
242 return _match.exact(repo.root, repo.getcwd(), files)
242 return _match.exact(repo.root, repo.getcwd(), files)
243
243
244 def findrenames(repo, added=None, removed=None, threshold=0.5):
244 def findrenames(repo, added=None, removed=None, threshold=0.5):
245 '''find renamed files -- yields (before, after, score) tuples'''
245 '''find renamed files -- yields (before, after, score) tuples'''
246 if added is None or removed is None:
246 if added is None or removed is None:
247 added, removed = repo.status()[1:3]
247 added, removed = repo.status()[1:3]
248 ctx = repo.changectx()
248 ctx = repo.changectx()
249 for a in added:
249 for a in added:
250 aa = repo.wread(a)
250 aa = repo.wread(a)
251 bestname, bestscore = None, threshold
251 bestname, bestscore = None, threshold
252 for r in removed:
252 for r in removed:
253 rr = ctx.filectx(r).data()
253 rr = ctx.filectx(r).data()
254
254
255 # bdiff.blocks() returns blocks of matching lines
255 # bdiff.blocks() returns blocks of matching lines
256 # count the number of bytes in each
256 # count the number of bytes in each
257 equal = 0
257 equal = 0
258 alines = mdiff.splitnewlines(aa)
258 alines = mdiff.splitnewlines(aa)
259 matches = bdiff.blocks(aa, rr)
259 matches = bdiff.blocks(aa, rr)
260 for x1,x2,y1,y2 in matches:
260 for x1,x2,y1,y2 in matches:
261 for line in alines[x1:x2]:
261 for line in alines[x1:x2]:
262 equal += len(line)
262 equal += len(line)
263
263
264 lengths = len(aa) + len(rr)
264 lengths = len(aa) + len(rr)
265 if lengths:
265 if lengths:
266 myscore = equal*2.0 / lengths
266 myscore = equal*2.0 / lengths
267 if myscore >= bestscore:
267 if myscore >= bestscore:
268 bestname, bestscore = r, myscore
268 bestname, bestscore = r, myscore
269 if bestname:
269 if bestname:
270 yield bestname, a, bestscore
270 yield bestname, a, bestscore
271
271
272 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
272 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
273 if dry_run is None:
273 if dry_run is None:
274 dry_run = opts.get('dry_run')
274 dry_run = opts.get('dry_run')
275 if similarity is None:
275 if similarity is None:
276 similarity = float(opts.get('similarity') or 0)
276 similarity = float(opts.get('similarity') or 0)
277 add, remove = [], []
277 add, remove = [], []
278 mapping = {}
278 mapping = {}
279 audit_path = util.path_auditor(repo.root)
279 m = match(repo, pats, opts)
280 m = match(repo, pats, opts)
280 for abs in repo.walk(m):
281 for abs in repo.walk(m):
281 target = repo.wjoin(abs)
282 target = repo.wjoin(abs)
283 good = True
284 try:
285 audit_path(abs)
286 except:
287 good = False
282 rel = m.rel(abs)
288 rel = m.rel(abs)
283 exact = m.exact(abs)
289 exact = m.exact(abs)
284 if abs not in repo.dirstate:
290 if good and abs not in repo.dirstate:
285 add.append(abs)
291 add.append(abs)
286 mapping[abs] = rel, m.exact(abs)
292 mapping[abs] = rel, m.exact(abs)
287 if repo.ui.verbose or not exact:
293 if repo.ui.verbose or not exact:
288 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
294 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
289 if repo.dirstate[abs] != 'r' and (not util.lexists(target)
295 if repo.dirstate[abs] != 'r' and (not good or not util.lexists(target)
290 or (os.path.isdir(target) and not os.path.islink(target))):
296 or (os.path.isdir(target) and not os.path.islink(target))):
291 remove.append(abs)
297 remove.append(abs)
292 mapping[abs] = rel, exact
298 mapping[abs] = rel, exact
293 if repo.ui.verbose or not exact:
299 if repo.ui.verbose or not exact:
294 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
300 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
295 if not dry_run:
301 if not dry_run:
296 repo.remove(remove)
302 repo.remove(remove)
297 repo.add(add)
303 repo.add(add)
298 if similarity > 0:
304 if similarity > 0:
299 for old, new, score in findrenames(repo, add, remove, similarity):
305 for old, new, score in findrenames(repo, add, remove, similarity):
300 oldrel, oldexact = mapping[old]
306 oldrel, oldexact = mapping[old]
301 newrel, newexact = mapping[new]
307 newrel, newexact = mapping[new]
302 if repo.ui.verbose or not oldexact or not newexact:
308 if repo.ui.verbose or not oldexact or not newexact:
303 repo.ui.status(_('recording removal of %s as rename to %s '
309 repo.ui.status(_('recording removal of %s as rename to %s '
304 '(%d%% similar)\n') %
310 '(%d%% similar)\n') %
305 (oldrel, newrel, score * 100))
311 (oldrel, newrel, score * 100))
306 if not dry_run:
312 if not dry_run:
307 repo.copy(old, new)
313 repo.copy(old, new)
308
314
309 def copy(ui, repo, pats, opts, rename=False):
315 def copy(ui, repo, pats, opts, rename=False):
310 # called with the repo lock held
316 # called with the repo lock held
311 #
317 #
312 # hgsep => pathname that uses "/" to separate directories
318 # hgsep => pathname that uses "/" to separate directories
313 # ossep => pathname that uses os.sep to separate directories
319 # ossep => pathname that uses os.sep to separate directories
314 cwd = repo.getcwd()
320 cwd = repo.getcwd()
315 targets = {}
321 targets = {}
316 after = opts.get("after")
322 after = opts.get("after")
317 dryrun = opts.get("dry_run")
323 dryrun = opts.get("dry_run")
318
324
319 def walkpat(pat):
325 def walkpat(pat):
320 srcs = []
326 srcs = []
321 m = match(repo, [pat], opts, globbed=True)
327 m = match(repo, [pat], opts, globbed=True)
322 for abs in repo.walk(m):
328 for abs in repo.walk(m):
323 state = repo.dirstate[abs]
329 state = repo.dirstate[abs]
324 rel = m.rel(abs)
330 rel = m.rel(abs)
325 exact = m.exact(abs)
331 exact = m.exact(abs)
326 if state in '?r':
332 if state in '?r':
327 if exact and state == '?':
333 if exact and state == '?':
328 ui.warn(_('%s: not copying - file is not managed\n') % rel)
334 ui.warn(_('%s: not copying - file is not managed\n') % rel)
329 if exact and state == 'r':
335 if exact and state == 'r':
330 ui.warn(_('%s: not copying - file has been marked for'
336 ui.warn(_('%s: not copying - file has been marked for'
331 ' remove\n') % rel)
337 ' remove\n') % rel)
332 continue
338 continue
333 # abs: hgsep
339 # abs: hgsep
334 # rel: ossep
340 # rel: ossep
335 srcs.append((abs, rel, exact))
341 srcs.append((abs, rel, exact))
336 return srcs
342 return srcs
337
343
338 # abssrc: hgsep
344 # abssrc: hgsep
339 # relsrc: ossep
345 # relsrc: ossep
340 # otarget: ossep
346 # otarget: ossep
341 def copyfile(abssrc, relsrc, otarget, exact):
347 def copyfile(abssrc, relsrc, otarget, exact):
342 abstarget = util.canonpath(repo.root, cwd, otarget)
348 abstarget = util.canonpath(repo.root, cwd, otarget)
343 reltarget = repo.pathto(abstarget, cwd)
349 reltarget = repo.pathto(abstarget, cwd)
344 target = repo.wjoin(abstarget)
350 target = repo.wjoin(abstarget)
345 src = repo.wjoin(abssrc)
351 src = repo.wjoin(abssrc)
346 state = repo.dirstate[abstarget]
352 state = repo.dirstate[abstarget]
347
353
348 # check for collisions
354 # check for collisions
349 prevsrc = targets.get(abstarget)
355 prevsrc = targets.get(abstarget)
350 if prevsrc is not None:
356 if prevsrc is not None:
351 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
357 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
352 (reltarget, repo.pathto(abssrc, cwd),
358 (reltarget, repo.pathto(abssrc, cwd),
353 repo.pathto(prevsrc, cwd)))
359 repo.pathto(prevsrc, cwd)))
354 return
360 return
355
361
356 # check for overwrites
362 # check for overwrites
357 exists = os.path.exists(target)
363 exists = os.path.exists(target)
358 if (not after and exists or after and state in 'mn'):
364 if (not after and exists or after and state in 'mn'):
359 if not opts['force']:
365 if not opts['force']:
360 ui.warn(_('%s: not overwriting - file exists\n') %
366 ui.warn(_('%s: not overwriting - file exists\n') %
361 reltarget)
367 reltarget)
362 return
368 return
363
369
364 if after:
370 if after:
365 if not exists:
371 if not exists:
366 return
372 return
367 elif not dryrun:
373 elif not dryrun:
368 try:
374 try:
369 if exists:
375 if exists:
370 os.unlink(target)
376 os.unlink(target)
371 targetdir = os.path.dirname(target) or '.'
377 targetdir = os.path.dirname(target) or '.'
372 if not os.path.isdir(targetdir):
378 if not os.path.isdir(targetdir):
373 os.makedirs(targetdir)
379 os.makedirs(targetdir)
374 util.copyfile(src, target)
380 util.copyfile(src, target)
375 except IOError, inst:
381 except IOError, inst:
376 if inst.errno == errno.ENOENT:
382 if inst.errno == errno.ENOENT:
377 ui.warn(_('%s: deleted in working copy\n') % relsrc)
383 ui.warn(_('%s: deleted in working copy\n') % relsrc)
378 else:
384 else:
379 ui.warn(_('%s: cannot copy - %s\n') %
385 ui.warn(_('%s: cannot copy - %s\n') %
380 (relsrc, inst.strerror))
386 (relsrc, inst.strerror))
381 return True # report a failure
387 return True # report a failure
382
388
383 if ui.verbose or not exact:
389 if ui.verbose or not exact:
384 action = rename and "moving" or "copying"
390 action = rename and "moving" or "copying"
385 ui.status(_('%s %s to %s\n') % (action, relsrc, reltarget))
391 ui.status(_('%s %s to %s\n') % (action, relsrc, reltarget))
386
392
387 targets[abstarget] = abssrc
393 targets[abstarget] = abssrc
388
394
389 # fix up dirstate
395 # fix up dirstate
390 origsrc = repo.dirstate.copied(abssrc) or abssrc
396 origsrc = repo.dirstate.copied(abssrc) or abssrc
391 if abstarget == origsrc: # copying back a copy?
397 if abstarget == origsrc: # copying back a copy?
392 if state not in 'mn' and not dryrun:
398 if state not in 'mn' and not dryrun:
393 repo.dirstate.normallookup(abstarget)
399 repo.dirstate.normallookup(abstarget)
394 else:
400 else:
395 if repo.dirstate[origsrc] == 'a':
401 if repo.dirstate[origsrc] == 'a':
396 if not ui.quiet:
402 if not ui.quiet:
397 ui.warn(_("%s has not been committed yet, so no copy "
403 ui.warn(_("%s has not been committed yet, so no copy "
398 "data will be stored for %s.\n")
404 "data will be stored for %s.\n")
399 % (repo.pathto(origsrc, cwd), reltarget))
405 % (repo.pathto(origsrc, cwd), reltarget))
400 if abstarget not in repo.dirstate and not dryrun:
406 if abstarget not in repo.dirstate and not dryrun:
401 repo.add([abstarget])
407 repo.add([abstarget])
402 elif not dryrun:
408 elif not dryrun:
403 repo.copy(origsrc, abstarget)
409 repo.copy(origsrc, abstarget)
404
410
405 if rename and not dryrun:
411 if rename and not dryrun:
406 repo.remove([abssrc], not after)
412 repo.remove([abssrc], not after)
407
413
408 # pat: ossep
414 # pat: ossep
409 # dest ossep
415 # dest ossep
410 # srcs: list of (hgsep, hgsep, ossep, bool)
416 # srcs: list of (hgsep, hgsep, ossep, bool)
411 # return: function that takes hgsep and returns ossep
417 # return: function that takes hgsep and returns ossep
412 def targetpathfn(pat, dest, srcs):
418 def targetpathfn(pat, dest, srcs):
413 if os.path.isdir(pat):
419 if os.path.isdir(pat):
414 abspfx = util.canonpath(repo.root, cwd, pat)
420 abspfx = util.canonpath(repo.root, cwd, pat)
415 abspfx = util.localpath(abspfx)
421 abspfx = util.localpath(abspfx)
416 if destdirexists:
422 if destdirexists:
417 striplen = len(os.path.split(abspfx)[0])
423 striplen = len(os.path.split(abspfx)[0])
418 else:
424 else:
419 striplen = len(abspfx)
425 striplen = len(abspfx)
420 if striplen:
426 if striplen:
421 striplen += len(os.sep)
427 striplen += len(os.sep)
422 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
428 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
423 elif destdirexists:
429 elif destdirexists:
424 res = lambda p: os.path.join(dest,
430 res = lambda p: os.path.join(dest,
425 os.path.basename(util.localpath(p)))
431 os.path.basename(util.localpath(p)))
426 else:
432 else:
427 res = lambda p: dest
433 res = lambda p: dest
428 return res
434 return res
429
435
430 # pat: ossep
436 # pat: ossep
431 # dest ossep
437 # dest ossep
432 # srcs: list of (hgsep, hgsep, ossep, bool)
438 # srcs: list of (hgsep, hgsep, ossep, bool)
433 # return: function that takes hgsep and returns ossep
439 # return: function that takes hgsep and returns ossep
434 def targetpathafterfn(pat, dest, srcs):
440 def targetpathafterfn(pat, dest, srcs):
435 if util.patkind(pat, None)[0]:
441 if util.patkind(pat, None)[0]:
436 # a mercurial pattern
442 # a mercurial pattern
437 res = lambda p: os.path.join(dest,
443 res = lambda p: os.path.join(dest,
438 os.path.basename(util.localpath(p)))
444 os.path.basename(util.localpath(p)))
439 else:
445 else:
440 abspfx = util.canonpath(repo.root, cwd, pat)
446 abspfx = util.canonpath(repo.root, cwd, pat)
441 if len(abspfx) < len(srcs[0][0]):
447 if len(abspfx) < len(srcs[0][0]):
442 # A directory. Either the target path contains the last
448 # A directory. Either the target path contains the last
443 # component of the source path or it does not.
449 # component of the source path or it does not.
444 def evalpath(striplen):
450 def evalpath(striplen):
445 score = 0
451 score = 0
446 for s in srcs:
452 for s in srcs:
447 t = os.path.join(dest, util.localpath(s[0])[striplen:])
453 t = os.path.join(dest, util.localpath(s[0])[striplen:])
448 if os.path.exists(t):
454 if os.path.exists(t):
449 score += 1
455 score += 1
450 return score
456 return score
451
457
452 abspfx = util.localpath(abspfx)
458 abspfx = util.localpath(abspfx)
453 striplen = len(abspfx)
459 striplen = len(abspfx)
454 if striplen:
460 if striplen:
455 striplen += len(os.sep)
461 striplen += len(os.sep)
456 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
462 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
457 score = evalpath(striplen)
463 score = evalpath(striplen)
458 striplen1 = len(os.path.split(abspfx)[0])
464 striplen1 = len(os.path.split(abspfx)[0])
459 if striplen1:
465 if striplen1:
460 striplen1 += len(os.sep)
466 striplen1 += len(os.sep)
461 if evalpath(striplen1) > score:
467 if evalpath(striplen1) > score:
462 striplen = striplen1
468 striplen = striplen1
463 res = lambda p: os.path.join(dest,
469 res = lambda p: os.path.join(dest,
464 util.localpath(p)[striplen:])
470 util.localpath(p)[striplen:])
465 else:
471 else:
466 # a file
472 # a file
467 if destdirexists:
473 if destdirexists:
468 res = lambda p: os.path.join(dest,
474 res = lambda p: os.path.join(dest,
469 os.path.basename(util.localpath(p)))
475 os.path.basename(util.localpath(p)))
470 else:
476 else:
471 res = lambda p: dest
477 res = lambda p: dest
472 return res
478 return res
473
479
474
480
475 pats = util.expand_glob(pats)
481 pats = util.expand_glob(pats)
476 if not pats:
482 if not pats:
477 raise util.Abort(_('no source or destination specified'))
483 raise util.Abort(_('no source or destination specified'))
478 if len(pats) == 1:
484 if len(pats) == 1:
479 raise util.Abort(_('no destination specified'))
485 raise util.Abort(_('no destination specified'))
480 dest = pats.pop()
486 dest = pats.pop()
481 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
487 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
482 if not destdirexists:
488 if not destdirexists:
483 if len(pats) > 1 or util.patkind(pats[0], None)[0]:
489 if len(pats) > 1 or util.patkind(pats[0], None)[0]:
484 raise util.Abort(_('with multiple sources, destination must be an '
490 raise util.Abort(_('with multiple sources, destination must be an '
485 'existing directory'))
491 'existing directory'))
486 if util.endswithsep(dest):
492 if util.endswithsep(dest):
487 raise util.Abort(_('destination %s is not a directory') % dest)
493 raise util.Abort(_('destination %s is not a directory') % dest)
488
494
489 tfn = targetpathfn
495 tfn = targetpathfn
490 if after:
496 if after:
491 tfn = targetpathafterfn
497 tfn = targetpathafterfn
492 copylist = []
498 copylist = []
493 for pat in pats:
499 for pat in pats:
494 srcs = walkpat(pat)
500 srcs = walkpat(pat)
495 if not srcs:
501 if not srcs:
496 continue
502 continue
497 copylist.append((tfn(pat, dest, srcs), srcs))
503 copylist.append((tfn(pat, dest, srcs), srcs))
498 if not copylist:
504 if not copylist:
499 raise util.Abort(_('no files to copy'))
505 raise util.Abort(_('no files to copy'))
500
506
501 errors = 0
507 errors = 0
502 for targetpath, srcs in copylist:
508 for targetpath, srcs in copylist:
503 for abssrc, relsrc, exact in srcs:
509 for abssrc, relsrc, exact in srcs:
504 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
510 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
505 errors += 1
511 errors += 1
506
512
507 if errors:
513 if errors:
508 ui.warn(_('(consider using --after)\n'))
514 ui.warn(_('(consider using --after)\n'))
509
515
510 return errors
516 return errors
511
517
512 def service(opts, parentfn=None, initfn=None, runfn=None):
518 def service(opts, parentfn=None, initfn=None, runfn=None):
513 '''Run a command as a service.'''
519 '''Run a command as a service.'''
514
520
515 if opts['daemon'] and not opts['daemon_pipefds']:
521 if opts['daemon'] and not opts['daemon_pipefds']:
516 rfd, wfd = os.pipe()
522 rfd, wfd = os.pipe()
517 args = sys.argv[:]
523 args = sys.argv[:]
518 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
524 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
519 # Don't pass --cwd to the child process, because we've already
525 # Don't pass --cwd to the child process, because we've already
520 # changed directory.
526 # changed directory.
521 for i in xrange(1,len(args)):
527 for i in xrange(1,len(args)):
522 if args[i].startswith('--cwd='):
528 if args[i].startswith('--cwd='):
523 del args[i]
529 del args[i]
524 break
530 break
525 elif args[i].startswith('--cwd'):
531 elif args[i].startswith('--cwd'):
526 del args[i:i+2]
532 del args[i:i+2]
527 break
533 break
528 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
534 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
529 args[0], args)
535 args[0], args)
530 os.close(wfd)
536 os.close(wfd)
531 os.read(rfd, 1)
537 os.read(rfd, 1)
532 if parentfn:
538 if parentfn:
533 return parentfn(pid)
539 return parentfn(pid)
534 else:
540 else:
535 os._exit(0)
541 os._exit(0)
536
542
537 if initfn:
543 if initfn:
538 initfn()
544 initfn()
539
545
540 if opts['pid_file']:
546 if opts['pid_file']:
541 fp = open(opts['pid_file'], 'w')
547 fp = open(opts['pid_file'], 'w')
542 fp.write(str(os.getpid()) + '\n')
548 fp.write(str(os.getpid()) + '\n')
543 fp.close()
549 fp.close()
544
550
545 if opts['daemon_pipefds']:
551 if opts['daemon_pipefds']:
546 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
552 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
547 os.close(rfd)
553 os.close(rfd)
548 try:
554 try:
549 os.setsid()
555 os.setsid()
550 except AttributeError:
556 except AttributeError:
551 pass
557 pass
552 os.write(wfd, 'y')
558 os.write(wfd, 'y')
553 os.close(wfd)
559 os.close(wfd)
554 sys.stdout.flush()
560 sys.stdout.flush()
555 sys.stderr.flush()
561 sys.stderr.flush()
556 fd = os.open(util.nulldev, os.O_RDWR)
562 fd = os.open(util.nulldev, os.O_RDWR)
557 if fd != 0: os.dup2(fd, 0)
563 if fd != 0: os.dup2(fd, 0)
558 if fd != 1: os.dup2(fd, 1)
564 if fd != 1: os.dup2(fd, 1)
559 if fd != 2: os.dup2(fd, 2)
565 if fd != 2: os.dup2(fd, 2)
560 if fd not in (0, 1, 2): os.close(fd)
566 if fd not in (0, 1, 2): os.close(fd)
561
567
562 if runfn:
568 if runfn:
563 return runfn()
569 return runfn()
564
570
565 class changeset_printer(object):
571 class changeset_printer(object):
566 '''show changeset information when templating not requested.'''
572 '''show changeset information when templating not requested.'''
567
573
568 def __init__(self, ui, repo, patch, buffered):
574 def __init__(self, ui, repo, patch, buffered):
569 self.ui = ui
575 self.ui = ui
570 self.repo = repo
576 self.repo = repo
571 self.buffered = buffered
577 self.buffered = buffered
572 self.patch = patch
578 self.patch = patch
573 self.header = {}
579 self.header = {}
574 self.hunk = {}
580 self.hunk = {}
575 self.lastheader = None
581 self.lastheader = None
576
582
577 def flush(self, rev):
583 def flush(self, rev):
578 if rev in self.header:
584 if rev in self.header:
579 h = self.header[rev]
585 h = self.header[rev]
580 if h != self.lastheader:
586 if h != self.lastheader:
581 self.lastheader = h
587 self.lastheader = h
582 self.ui.write(h)
588 self.ui.write(h)
583 del self.header[rev]
589 del self.header[rev]
584 if rev in self.hunk:
590 if rev in self.hunk:
585 self.ui.write(self.hunk[rev])
591 self.ui.write(self.hunk[rev])
586 del self.hunk[rev]
592 del self.hunk[rev]
587 return 1
593 return 1
588 return 0
594 return 0
589
595
590 def show(self, rev=0, changenode=None, copies=(), **props):
596 def show(self, rev=0, changenode=None, copies=(), **props):
591 if self.buffered:
597 if self.buffered:
592 self.ui.pushbuffer()
598 self.ui.pushbuffer()
593 self._show(rev, changenode, copies, props)
599 self._show(rev, changenode, copies, props)
594 self.hunk[rev] = self.ui.popbuffer()
600 self.hunk[rev] = self.ui.popbuffer()
595 else:
601 else:
596 self._show(rev, changenode, copies, props)
602 self._show(rev, changenode, copies, props)
597
603
598 def _show(self, rev, changenode, copies, props):
604 def _show(self, rev, changenode, copies, props):
599 '''show a single changeset or file revision'''
605 '''show a single changeset or file revision'''
600 log = self.repo.changelog
606 log = self.repo.changelog
601 if changenode is None:
607 if changenode is None:
602 changenode = log.node(rev)
608 changenode = log.node(rev)
603 elif not rev:
609 elif not rev:
604 rev = log.rev(changenode)
610 rev = log.rev(changenode)
605
611
606 if self.ui.quiet:
612 if self.ui.quiet:
607 self.ui.write("%d:%s\n" % (rev, short(changenode)))
613 self.ui.write("%d:%s\n" % (rev, short(changenode)))
608 return
614 return
609
615
610 changes = log.read(changenode)
616 changes = log.read(changenode)
611 date = util.datestr(changes[2])
617 date = util.datestr(changes[2])
612 extra = changes[5]
618 extra = changes[5]
613 branch = extra.get("branch")
619 branch = extra.get("branch")
614
620
615 hexfunc = self.ui.debugflag and hex or short
621 hexfunc = self.ui.debugflag and hex or short
616
622
617 parents = [(p, hexfunc(log.node(p)))
623 parents = [(p, hexfunc(log.node(p)))
618 for p in self._meaningful_parentrevs(log, rev)]
624 for p in self._meaningful_parentrevs(log, rev)]
619
625
620 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
626 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
621
627
622 # don't show the default branch name
628 # don't show the default branch name
623 if branch != 'default':
629 if branch != 'default':
624 branch = util.tolocal(branch)
630 branch = util.tolocal(branch)
625 self.ui.write(_("branch: %s\n") % branch)
631 self.ui.write(_("branch: %s\n") % branch)
626 for tag in self.repo.nodetags(changenode):
632 for tag in self.repo.nodetags(changenode):
627 self.ui.write(_("tag: %s\n") % tag)
633 self.ui.write(_("tag: %s\n") % tag)
628 for parent in parents:
634 for parent in parents:
629 self.ui.write(_("parent: %d:%s\n") % parent)
635 self.ui.write(_("parent: %d:%s\n") % parent)
630
636
631 if self.ui.debugflag:
637 if self.ui.debugflag:
632 self.ui.write(_("manifest: %d:%s\n") %
638 self.ui.write(_("manifest: %d:%s\n") %
633 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
639 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
634 self.ui.write(_("user: %s\n") % changes[1])
640 self.ui.write(_("user: %s\n") % changes[1])
635 self.ui.write(_("date: %s\n") % date)
641 self.ui.write(_("date: %s\n") % date)
636
642
637 if self.ui.debugflag:
643 if self.ui.debugflag:
638 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
644 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
639 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
645 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
640 files):
646 files):
641 if value:
647 if value:
642 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
648 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
643 elif changes[3] and self.ui.verbose:
649 elif changes[3] and self.ui.verbose:
644 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
650 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
645 if copies and self.ui.verbose:
651 if copies and self.ui.verbose:
646 copies = ['%s (%s)' % c for c in copies]
652 copies = ['%s (%s)' % c for c in copies]
647 self.ui.write(_("copies: %s\n") % ' '.join(copies))
653 self.ui.write(_("copies: %s\n") % ' '.join(copies))
648
654
649 if extra and self.ui.debugflag:
655 if extra and self.ui.debugflag:
650 extraitems = extra.items()
656 extraitems = extra.items()
651 extraitems.sort()
657 extraitems.sort()
652 for key, value in extraitems:
658 for key, value in extraitems:
653 self.ui.write(_("extra: %s=%s\n")
659 self.ui.write(_("extra: %s=%s\n")
654 % (key, value.encode('string_escape')))
660 % (key, value.encode('string_escape')))
655
661
656 description = changes[4].strip()
662 description = changes[4].strip()
657 if description:
663 if description:
658 if self.ui.verbose:
664 if self.ui.verbose:
659 self.ui.write(_("description:\n"))
665 self.ui.write(_("description:\n"))
660 self.ui.write(description)
666 self.ui.write(description)
661 self.ui.write("\n\n")
667 self.ui.write("\n\n")
662 else:
668 else:
663 self.ui.write(_("summary: %s\n") %
669 self.ui.write(_("summary: %s\n") %
664 description.splitlines()[0])
670 description.splitlines()[0])
665 self.ui.write("\n")
671 self.ui.write("\n")
666
672
667 self.showpatch(changenode)
673 self.showpatch(changenode)
668
674
669 def showpatch(self, node):
675 def showpatch(self, node):
670 if self.patch:
676 if self.patch:
671 prev = self.repo.changelog.parents(node)[0]
677 prev = self.repo.changelog.parents(node)[0]
672 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
678 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
673 opts=patch.diffopts(self.ui))
679 opts=patch.diffopts(self.ui))
674 self.ui.write("\n")
680 self.ui.write("\n")
675
681
676 def _meaningful_parentrevs(self, log, rev):
682 def _meaningful_parentrevs(self, log, rev):
677 """Return list of meaningful (or all if debug) parentrevs for rev.
683 """Return list of meaningful (or all if debug) parentrevs for rev.
678
684
679 For merges (two non-nullrev revisions) both parents are meaningful.
685 For merges (two non-nullrev revisions) both parents are meaningful.
680 Otherwise the first parent revision is considered meaningful if it
686 Otherwise the first parent revision is considered meaningful if it
681 is not the preceding revision.
687 is not the preceding revision.
682 """
688 """
683 parents = log.parentrevs(rev)
689 parents = log.parentrevs(rev)
684 if not self.ui.debugflag and parents[1] == nullrev:
690 if not self.ui.debugflag and parents[1] == nullrev:
685 if parents[0] >= rev - 1:
691 if parents[0] >= rev - 1:
686 parents = []
692 parents = []
687 else:
693 else:
688 parents = [parents[0]]
694 parents = [parents[0]]
689 return parents
695 return parents
690
696
691
697
692 class changeset_templater(changeset_printer):
698 class changeset_templater(changeset_printer):
693 '''format changeset information.'''
699 '''format changeset information.'''
694
700
695 def __init__(self, ui, repo, patch, mapfile, buffered):
701 def __init__(self, ui, repo, patch, mapfile, buffered):
696 changeset_printer.__init__(self, ui, repo, patch, buffered)
702 changeset_printer.__init__(self, ui, repo, patch, buffered)
697 filters = templatefilters.filters.copy()
703 filters = templatefilters.filters.copy()
698 filters['formatnode'] = (ui.debugflag and (lambda x: x)
704 filters['formatnode'] = (ui.debugflag and (lambda x: x)
699 or (lambda x: x[:12]))
705 or (lambda x: x[:12]))
700 self.t = templater.templater(mapfile, filters,
706 self.t = templater.templater(mapfile, filters,
701 cache={
707 cache={
702 'parent': '{rev}:{node|formatnode} ',
708 'parent': '{rev}:{node|formatnode} ',
703 'manifest': '{rev}:{node|formatnode}',
709 'manifest': '{rev}:{node|formatnode}',
704 'filecopy': '{name} ({source})'})
710 'filecopy': '{name} ({source})'})
705
711
706 def use_template(self, t):
712 def use_template(self, t):
707 '''set template string to use'''
713 '''set template string to use'''
708 self.t.cache['changeset'] = t
714 self.t.cache['changeset'] = t
709
715
710 def _show(self, rev, changenode, copies, props):
716 def _show(self, rev, changenode, copies, props):
711 '''show a single changeset or file revision'''
717 '''show a single changeset or file revision'''
712 log = self.repo.changelog
718 log = self.repo.changelog
713 if changenode is None:
719 if changenode is None:
714 changenode = log.node(rev)
720 changenode = log.node(rev)
715 elif not rev:
721 elif not rev:
716 rev = log.rev(changenode)
722 rev = log.rev(changenode)
717
723
718 changes = log.read(changenode)
724 changes = log.read(changenode)
719
725
720 def showlist(name, values, plural=None, **args):
726 def showlist(name, values, plural=None, **args):
721 '''expand set of values.
727 '''expand set of values.
722 name is name of key in template map.
728 name is name of key in template map.
723 values is list of strings or dicts.
729 values is list of strings or dicts.
724 plural is plural of name, if not simply name + 's'.
730 plural is plural of name, if not simply name + 's'.
725
731
726 expansion works like this, given name 'foo'.
732 expansion works like this, given name 'foo'.
727
733
728 if values is empty, expand 'no_foos'.
734 if values is empty, expand 'no_foos'.
729
735
730 if 'foo' not in template map, return values as a string,
736 if 'foo' not in template map, return values as a string,
731 joined by space.
737 joined by space.
732
738
733 expand 'start_foos'.
739 expand 'start_foos'.
734
740
735 for each value, expand 'foo'. if 'last_foo' in template
741 for each value, expand 'foo'. if 'last_foo' in template
736 map, expand it instead of 'foo' for last key.
742 map, expand it instead of 'foo' for last key.
737
743
738 expand 'end_foos'.
744 expand 'end_foos'.
739 '''
745 '''
740 if plural: names = plural
746 if plural: names = plural
741 else: names = name + 's'
747 else: names = name + 's'
742 if not values:
748 if not values:
743 noname = 'no_' + names
749 noname = 'no_' + names
744 if noname in self.t:
750 if noname in self.t:
745 yield self.t(noname, **args)
751 yield self.t(noname, **args)
746 return
752 return
747 if name not in self.t:
753 if name not in self.t:
748 if isinstance(values[0], str):
754 if isinstance(values[0], str):
749 yield ' '.join(values)
755 yield ' '.join(values)
750 else:
756 else:
751 for v in values:
757 for v in values:
752 yield dict(v, **args)
758 yield dict(v, **args)
753 return
759 return
754 startname = 'start_' + names
760 startname = 'start_' + names
755 if startname in self.t:
761 if startname in self.t:
756 yield self.t(startname, **args)
762 yield self.t(startname, **args)
757 vargs = args.copy()
763 vargs = args.copy()
758 def one(v, tag=name):
764 def one(v, tag=name):
759 try:
765 try:
760 vargs.update(v)
766 vargs.update(v)
761 except (AttributeError, ValueError):
767 except (AttributeError, ValueError):
762 try:
768 try:
763 for a, b in v:
769 for a, b in v:
764 vargs[a] = b
770 vargs[a] = b
765 except ValueError:
771 except ValueError:
766 vargs[name] = v
772 vargs[name] = v
767 return self.t(tag, **vargs)
773 return self.t(tag, **vargs)
768 lastname = 'last_' + name
774 lastname = 'last_' + name
769 if lastname in self.t:
775 if lastname in self.t:
770 last = values.pop()
776 last = values.pop()
771 else:
777 else:
772 last = None
778 last = None
773 for v in values:
779 for v in values:
774 yield one(v)
780 yield one(v)
775 if last is not None:
781 if last is not None:
776 yield one(last, tag=lastname)
782 yield one(last, tag=lastname)
777 endname = 'end_' + names
783 endname = 'end_' + names
778 if endname in self.t:
784 if endname in self.t:
779 yield self.t(endname, **args)
785 yield self.t(endname, **args)
780
786
781 def showbranches(**args):
787 def showbranches(**args):
782 branch = changes[5].get("branch")
788 branch = changes[5].get("branch")
783 if branch != 'default':
789 if branch != 'default':
784 branch = util.tolocal(branch)
790 branch = util.tolocal(branch)
785 return showlist('branch', [branch], plural='branches', **args)
791 return showlist('branch', [branch], plural='branches', **args)
786
792
787 def showparents(**args):
793 def showparents(**args):
788 parents = [[('rev', p), ('node', hex(log.node(p)))]
794 parents = [[('rev', p), ('node', hex(log.node(p)))]
789 for p in self._meaningful_parentrevs(log, rev)]
795 for p in self._meaningful_parentrevs(log, rev)]
790 return showlist('parent', parents, **args)
796 return showlist('parent', parents, **args)
791
797
792 def showtags(**args):
798 def showtags(**args):
793 return showlist('tag', self.repo.nodetags(changenode), **args)
799 return showlist('tag', self.repo.nodetags(changenode), **args)
794
800
795 def showextras(**args):
801 def showextras(**args):
796 extras = changes[5].items()
802 extras = changes[5].items()
797 extras.sort()
803 extras.sort()
798 for key, value in extras:
804 for key, value in extras:
799 args = args.copy()
805 args = args.copy()
800 args.update(dict(key=key, value=value))
806 args.update(dict(key=key, value=value))
801 yield self.t('extra', **args)
807 yield self.t('extra', **args)
802
808
803 def showcopies(**args):
809 def showcopies(**args):
804 c = [{'name': x[0], 'source': x[1]} for x in copies]
810 c = [{'name': x[0], 'source': x[1]} for x in copies]
805 return showlist('file_copy', c, plural='file_copies', **args)
811 return showlist('file_copy', c, plural='file_copies', **args)
806
812
807 files = []
813 files = []
808 def getfiles():
814 def getfiles():
809 if not files:
815 if not files:
810 files[:] = self.repo.status(
816 files[:] = self.repo.status(
811 log.parents(changenode)[0], changenode)[:3]
817 log.parents(changenode)[0], changenode)[:3]
812 return files
818 return files
813 def showfiles(**args):
819 def showfiles(**args):
814 return showlist('file', changes[3], **args)
820 return showlist('file', changes[3], **args)
815 def showmods(**args):
821 def showmods(**args):
816 return showlist('file_mod', getfiles()[0], **args)
822 return showlist('file_mod', getfiles()[0], **args)
817 def showadds(**args):
823 def showadds(**args):
818 return showlist('file_add', getfiles()[1], **args)
824 return showlist('file_add', getfiles()[1], **args)
819 def showdels(**args):
825 def showdels(**args):
820 return showlist('file_del', getfiles()[2], **args)
826 return showlist('file_del', getfiles()[2], **args)
821 def showmanifest(**args):
827 def showmanifest(**args):
822 args = args.copy()
828 args = args.copy()
823 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
829 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
824 node=hex(changes[0])))
830 node=hex(changes[0])))
825 return self.t('manifest', **args)
831 return self.t('manifest', **args)
826
832
827 defprops = {
833 defprops = {
828 'author': changes[1],
834 'author': changes[1],
829 'branches': showbranches,
835 'branches': showbranches,
830 'date': changes[2],
836 'date': changes[2],
831 'desc': changes[4].strip(),
837 'desc': changes[4].strip(),
832 'file_adds': showadds,
838 'file_adds': showadds,
833 'file_dels': showdels,
839 'file_dels': showdels,
834 'file_mods': showmods,
840 'file_mods': showmods,
835 'files': showfiles,
841 'files': showfiles,
836 'file_copies': showcopies,
842 'file_copies': showcopies,
837 'manifest': showmanifest,
843 'manifest': showmanifest,
838 'node': hex(changenode),
844 'node': hex(changenode),
839 'parents': showparents,
845 'parents': showparents,
840 'rev': rev,
846 'rev': rev,
841 'tags': showtags,
847 'tags': showtags,
842 'extras': showextras,
848 'extras': showextras,
843 }
849 }
844 props = props.copy()
850 props = props.copy()
845 props.update(defprops)
851 props.update(defprops)
846
852
847 try:
853 try:
848 if self.ui.debugflag and 'header_debug' in self.t:
854 if self.ui.debugflag and 'header_debug' in self.t:
849 key = 'header_debug'
855 key = 'header_debug'
850 elif self.ui.quiet and 'header_quiet' in self.t:
856 elif self.ui.quiet and 'header_quiet' in self.t:
851 key = 'header_quiet'
857 key = 'header_quiet'
852 elif self.ui.verbose and 'header_verbose' in self.t:
858 elif self.ui.verbose and 'header_verbose' in self.t:
853 key = 'header_verbose'
859 key = 'header_verbose'
854 elif 'header' in self.t:
860 elif 'header' in self.t:
855 key = 'header'
861 key = 'header'
856 else:
862 else:
857 key = ''
863 key = ''
858 if key:
864 if key:
859 h = templater.stringify(self.t(key, **props))
865 h = templater.stringify(self.t(key, **props))
860 if self.buffered:
866 if self.buffered:
861 self.header[rev] = h
867 self.header[rev] = h
862 else:
868 else:
863 self.ui.write(h)
869 self.ui.write(h)
864 if self.ui.debugflag and 'changeset_debug' in self.t:
870 if self.ui.debugflag and 'changeset_debug' in self.t:
865 key = 'changeset_debug'
871 key = 'changeset_debug'
866 elif self.ui.quiet and 'changeset_quiet' in self.t:
872 elif self.ui.quiet and 'changeset_quiet' in self.t:
867 key = 'changeset_quiet'
873 key = 'changeset_quiet'
868 elif self.ui.verbose and 'changeset_verbose' in self.t:
874 elif self.ui.verbose and 'changeset_verbose' in self.t:
869 key = 'changeset_verbose'
875 key = 'changeset_verbose'
870 else:
876 else:
871 key = 'changeset'
877 key = 'changeset'
872 self.ui.write(templater.stringify(self.t(key, **props)))
878 self.ui.write(templater.stringify(self.t(key, **props)))
873 self.showpatch(changenode)
879 self.showpatch(changenode)
874 except KeyError, inst:
880 except KeyError, inst:
875 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
881 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
876 inst.args[0]))
882 inst.args[0]))
877 except SyntaxError, inst:
883 except SyntaxError, inst:
878 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
884 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
879
885
880 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
886 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
881 """show one changeset using template or regular display.
887 """show one changeset using template or regular display.
882
888
883 Display format will be the first non-empty hit of:
889 Display format will be the first non-empty hit of:
884 1. option 'template'
890 1. option 'template'
885 2. option 'style'
891 2. option 'style'
886 3. [ui] setting 'logtemplate'
892 3. [ui] setting 'logtemplate'
887 4. [ui] setting 'style'
893 4. [ui] setting 'style'
888 If all of these values are either the unset or the empty string,
894 If all of these values are either the unset or the empty string,
889 regular display via changeset_printer() is done.
895 regular display via changeset_printer() is done.
890 """
896 """
891 # options
897 # options
892 patch = False
898 patch = False
893 if opts.get('patch'):
899 if opts.get('patch'):
894 patch = matchfn or matchall(repo)
900 patch = matchfn or matchall(repo)
895
901
896 tmpl = opts.get('template')
902 tmpl = opts.get('template')
897 mapfile = None
903 mapfile = None
898 if tmpl:
904 if tmpl:
899 tmpl = templater.parsestring(tmpl, quoted=False)
905 tmpl = templater.parsestring(tmpl, quoted=False)
900 else:
906 else:
901 mapfile = opts.get('style')
907 mapfile = opts.get('style')
902 # ui settings
908 # ui settings
903 if not mapfile:
909 if not mapfile:
904 tmpl = ui.config('ui', 'logtemplate')
910 tmpl = ui.config('ui', 'logtemplate')
905 if tmpl:
911 if tmpl:
906 tmpl = templater.parsestring(tmpl)
912 tmpl = templater.parsestring(tmpl)
907 else:
913 else:
908 mapfile = ui.config('ui', 'style')
914 mapfile = ui.config('ui', 'style')
909
915
910 if tmpl or mapfile:
916 if tmpl or mapfile:
911 if mapfile:
917 if mapfile:
912 if not os.path.split(mapfile)[0]:
918 if not os.path.split(mapfile)[0]:
913 mapname = (templater.templatepath('map-cmdline.' + mapfile)
919 mapname = (templater.templatepath('map-cmdline.' + mapfile)
914 or templater.templatepath(mapfile))
920 or templater.templatepath(mapfile))
915 if mapname: mapfile = mapname
921 if mapname: mapfile = mapname
916 try:
922 try:
917 t = changeset_templater(ui, repo, patch, mapfile, buffered)
923 t = changeset_templater(ui, repo, patch, mapfile, buffered)
918 except SyntaxError, inst:
924 except SyntaxError, inst:
919 raise util.Abort(inst.args[0])
925 raise util.Abort(inst.args[0])
920 if tmpl: t.use_template(tmpl)
926 if tmpl: t.use_template(tmpl)
921 return t
927 return t
922 return changeset_printer(ui, repo, patch, buffered)
928 return changeset_printer(ui, repo, patch, buffered)
923
929
924 def finddate(ui, repo, date):
930 def finddate(ui, repo, date):
925 """Find the tipmost changeset that matches the given date spec"""
931 """Find the tipmost changeset that matches the given date spec"""
926 df = util.matchdate(date)
932 df = util.matchdate(date)
927 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
933 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
928 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
934 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
929 results = {}
935 results = {}
930 for st, rev, fns in changeiter:
936 for st, rev, fns in changeiter:
931 if st == 'add':
937 if st == 'add':
932 d = get(rev)[2]
938 d = get(rev)[2]
933 if df(d[0]):
939 if df(d[0]):
934 results[rev] = d
940 results[rev] = d
935 elif st == 'iter':
941 elif st == 'iter':
936 if rev in results:
942 if rev in results:
937 ui.status("Found revision %s from %s\n" %
943 ui.status("Found revision %s from %s\n" %
938 (rev, util.datestr(results[rev])))
944 (rev, util.datestr(results[rev])))
939 return str(rev)
945 return str(rev)
940
946
941 raise util.Abort(_("revision matching date not found"))
947 raise util.Abort(_("revision matching date not found"))
942
948
943 def walkchangerevs(ui, repo, pats, change, opts):
949 def walkchangerevs(ui, repo, pats, change, opts):
944 '''Iterate over files and the revs they changed in.
950 '''Iterate over files and the revs they changed in.
945
951
946 Callers most commonly need to iterate backwards over the history
952 Callers most commonly need to iterate backwards over the history
947 it is interested in. Doing so has awful (quadratic-looking)
953 it is interested in. Doing so has awful (quadratic-looking)
948 performance, so we use iterators in a "windowed" way.
954 performance, so we use iterators in a "windowed" way.
949
955
950 We walk a window of revisions in the desired order. Within the
956 We walk a window of revisions in the desired order. Within the
951 window, we first walk forwards to gather data, then in the desired
957 window, we first walk forwards to gather data, then in the desired
952 order (usually backwards) to display it.
958 order (usually backwards) to display it.
953
959
954 This function returns an (iterator, matchfn) tuple. The iterator
960 This function returns an (iterator, matchfn) tuple. The iterator
955 yields 3-tuples. They will be of one of the following forms:
961 yields 3-tuples. They will be of one of the following forms:
956
962
957 "window", incrementing, lastrev: stepping through a window,
963 "window", incrementing, lastrev: stepping through a window,
958 positive if walking forwards through revs, last rev in the
964 positive if walking forwards through revs, last rev in the
959 sequence iterated over - use to reset state for the current window
965 sequence iterated over - use to reset state for the current window
960
966
961 "add", rev, fns: out-of-order traversal of the given file names
967 "add", rev, fns: out-of-order traversal of the given file names
962 fns, which changed during revision rev - use to gather data for
968 fns, which changed during revision rev - use to gather data for
963 possible display
969 possible display
964
970
965 "iter", rev, None: in-order traversal of the revs earlier iterated
971 "iter", rev, None: in-order traversal of the revs earlier iterated
966 over with "add" - use to display data'''
972 over with "add" - use to display data'''
967
973
968 def increasing_windows(start, end, windowsize=8, sizelimit=512):
974 def increasing_windows(start, end, windowsize=8, sizelimit=512):
969 if start < end:
975 if start < end:
970 while start < end:
976 while start < end:
971 yield start, min(windowsize, end-start)
977 yield start, min(windowsize, end-start)
972 start += windowsize
978 start += windowsize
973 if windowsize < sizelimit:
979 if windowsize < sizelimit:
974 windowsize *= 2
980 windowsize *= 2
975 else:
981 else:
976 while start > end:
982 while start > end:
977 yield start, min(windowsize, start-end-1)
983 yield start, min(windowsize, start-end-1)
978 start -= windowsize
984 start -= windowsize
979 if windowsize < sizelimit:
985 if windowsize < sizelimit:
980 windowsize *= 2
986 windowsize *= 2
981
987
982 m = match(repo, pats, opts)
988 m = match(repo, pats, opts)
983 follow = opts.get('follow') or opts.get('follow_first')
989 follow = opts.get('follow') or opts.get('follow_first')
984
990
985 if repo.changelog.count() == 0:
991 if repo.changelog.count() == 0:
986 return [], m
992 return [], m
987
993
988 if follow:
994 if follow:
989 defrange = '%s:0' % repo.changectx().rev()
995 defrange = '%s:0' % repo.changectx().rev()
990 else:
996 else:
991 defrange = '-1:0'
997 defrange = '-1:0'
992 revs = revrange(repo, opts['rev'] or [defrange])
998 revs = revrange(repo, opts['rev'] or [defrange])
993 wanted = {}
999 wanted = {}
994 slowpath = m.anypats() or opts.get('removed')
1000 slowpath = m.anypats() or opts.get('removed')
995 fncache = {}
1001 fncache = {}
996
1002
997 if not slowpath and not m.files():
1003 if not slowpath and not m.files():
998 # No files, no patterns. Display all revs.
1004 # No files, no patterns. Display all revs.
999 wanted = dict.fromkeys(revs)
1005 wanted = dict.fromkeys(revs)
1000 copies = []
1006 copies = []
1001 if not slowpath:
1007 if not slowpath:
1002 # Only files, no patterns. Check the history of each file.
1008 # Only files, no patterns. Check the history of each file.
1003 def filerevgen(filelog, node):
1009 def filerevgen(filelog, node):
1004 cl_count = repo.changelog.count()
1010 cl_count = repo.changelog.count()
1005 if node is None:
1011 if node is None:
1006 last = filelog.count() - 1
1012 last = filelog.count() - 1
1007 else:
1013 else:
1008 last = filelog.rev(node)
1014 last = filelog.rev(node)
1009 for i, window in increasing_windows(last, nullrev):
1015 for i, window in increasing_windows(last, nullrev):
1010 revs = []
1016 revs = []
1011 for j in xrange(i - window, i + 1):
1017 for j in xrange(i - window, i + 1):
1012 n = filelog.node(j)
1018 n = filelog.node(j)
1013 revs.append((filelog.linkrev(n),
1019 revs.append((filelog.linkrev(n),
1014 follow and filelog.renamed(n)))
1020 follow and filelog.renamed(n)))
1015 revs.reverse()
1021 revs.reverse()
1016 for rev in revs:
1022 for rev in revs:
1017 # only yield rev for which we have the changelog, it can
1023 # only yield rev for which we have the changelog, it can
1018 # happen while doing "hg log" during a pull or commit
1024 # happen while doing "hg log" during a pull or commit
1019 if rev[0] < cl_count:
1025 if rev[0] < cl_count:
1020 yield rev
1026 yield rev
1021 def iterfiles():
1027 def iterfiles():
1022 for filename in m.files():
1028 for filename in m.files():
1023 yield filename, None
1029 yield filename, None
1024 for filename_node in copies:
1030 for filename_node in copies:
1025 yield filename_node
1031 yield filename_node
1026 minrev, maxrev = min(revs), max(revs)
1032 minrev, maxrev = min(revs), max(revs)
1027 for file_, node in iterfiles():
1033 for file_, node in iterfiles():
1028 filelog = repo.file(file_)
1034 filelog = repo.file(file_)
1029 if filelog.count() == 0:
1035 if filelog.count() == 0:
1030 if node is None:
1036 if node is None:
1031 # A zero count may be a directory or deleted file, so
1037 # A zero count may be a directory or deleted file, so
1032 # try to find matching entries on the slow path.
1038 # try to find matching entries on the slow path.
1033 slowpath = True
1039 slowpath = True
1034 break
1040 break
1035 else:
1041 else:
1036 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1042 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1037 % (file_, short(node)))
1043 % (file_, short(node)))
1038 continue
1044 continue
1039 for rev, copied in filerevgen(filelog, node):
1045 for rev, copied in filerevgen(filelog, node):
1040 if rev <= maxrev:
1046 if rev <= maxrev:
1041 if rev < minrev:
1047 if rev < minrev:
1042 break
1048 break
1043 fncache.setdefault(rev, [])
1049 fncache.setdefault(rev, [])
1044 fncache[rev].append(file_)
1050 fncache[rev].append(file_)
1045 wanted[rev] = 1
1051 wanted[rev] = 1
1046 if follow and copied:
1052 if follow and copied:
1047 copies.append(copied)
1053 copies.append(copied)
1048 if slowpath:
1054 if slowpath:
1049 if follow:
1055 if follow:
1050 raise util.Abort(_('can only follow copies/renames for explicit '
1056 raise util.Abort(_('can only follow copies/renames for explicit '
1051 'file names'))
1057 'file names'))
1052
1058
1053 # The slow path checks files modified in every changeset.
1059 # The slow path checks files modified in every changeset.
1054 def changerevgen():
1060 def changerevgen():
1055 for i, window in increasing_windows(repo.changelog.count()-1,
1061 for i, window in increasing_windows(repo.changelog.count()-1,
1056 nullrev):
1062 nullrev):
1057 for j in xrange(i - window, i + 1):
1063 for j in xrange(i - window, i + 1):
1058 yield j, change(j)[3]
1064 yield j, change(j)[3]
1059
1065
1060 for rev, changefiles in changerevgen():
1066 for rev, changefiles in changerevgen():
1061 matches = filter(m, changefiles)
1067 matches = filter(m, changefiles)
1062 if matches:
1068 if matches:
1063 fncache[rev] = matches
1069 fncache[rev] = matches
1064 wanted[rev] = 1
1070 wanted[rev] = 1
1065
1071
1066 class followfilter:
1072 class followfilter:
1067 def __init__(self, onlyfirst=False):
1073 def __init__(self, onlyfirst=False):
1068 self.startrev = nullrev
1074 self.startrev = nullrev
1069 self.roots = []
1075 self.roots = []
1070 self.onlyfirst = onlyfirst
1076 self.onlyfirst = onlyfirst
1071
1077
1072 def match(self, rev):
1078 def match(self, rev):
1073 def realparents(rev):
1079 def realparents(rev):
1074 if self.onlyfirst:
1080 if self.onlyfirst:
1075 return repo.changelog.parentrevs(rev)[0:1]
1081 return repo.changelog.parentrevs(rev)[0:1]
1076 else:
1082 else:
1077 return filter(lambda x: x != nullrev,
1083 return filter(lambda x: x != nullrev,
1078 repo.changelog.parentrevs(rev))
1084 repo.changelog.parentrevs(rev))
1079
1085
1080 if self.startrev == nullrev:
1086 if self.startrev == nullrev:
1081 self.startrev = rev
1087 self.startrev = rev
1082 return True
1088 return True
1083
1089
1084 if rev > self.startrev:
1090 if rev > self.startrev:
1085 # forward: all descendants
1091 # forward: all descendants
1086 if not self.roots:
1092 if not self.roots:
1087 self.roots.append(self.startrev)
1093 self.roots.append(self.startrev)
1088 for parent in realparents(rev):
1094 for parent in realparents(rev):
1089 if parent in self.roots:
1095 if parent in self.roots:
1090 self.roots.append(rev)
1096 self.roots.append(rev)
1091 return True
1097 return True
1092 else:
1098 else:
1093 # backwards: all parents
1099 # backwards: all parents
1094 if not self.roots:
1100 if not self.roots:
1095 self.roots.extend(realparents(self.startrev))
1101 self.roots.extend(realparents(self.startrev))
1096 if rev in self.roots:
1102 if rev in self.roots:
1097 self.roots.remove(rev)
1103 self.roots.remove(rev)
1098 self.roots.extend(realparents(rev))
1104 self.roots.extend(realparents(rev))
1099 return True
1105 return True
1100
1106
1101 return False
1107 return False
1102
1108
1103 # it might be worthwhile to do this in the iterator if the rev range
1109 # it might be worthwhile to do this in the iterator if the rev range
1104 # is descending and the prune args are all within that range
1110 # is descending and the prune args are all within that range
1105 for rev in opts.get('prune', ()):
1111 for rev in opts.get('prune', ()):
1106 rev = repo.changelog.rev(repo.lookup(rev))
1112 rev = repo.changelog.rev(repo.lookup(rev))
1107 ff = followfilter()
1113 ff = followfilter()
1108 stop = min(revs[0], revs[-1])
1114 stop = min(revs[0], revs[-1])
1109 for x in xrange(rev, stop-1, -1):
1115 for x in xrange(rev, stop-1, -1):
1110 if ff.match(x) and x in wanted:
1116 if ff.match(x) and x in wanted:
1111 del wanted[x]
1117 del wanted[x]
1112
1118
1113 def iterate():
1119 def iterate():
1114 if follow and not m.files():
1120 if follow and not m.files():
1115 ff = followfilter(onlyfirst=opts.get('follow_first'))
1121 ff = followfilter(onlyfirst=opts.get('follow_first'))
1116 def want(rev):
1122 def want(rev):
1117 if ff.match(rev) and rev in wanted:
1123 if ff.match(rev) and rev in wanted:
1118 return True
1124 return True
1119 return False
1125 return False
1120 else:
1126 else:
1121 def want(rev):
1127 def want(rev):
1122 return rev in wanted
1128 return rev in wanted
1123
1129
1124 for i, window in increasing_windows(0, len(revs)):
1130 for i, window in increasing_windows(0, len(revs)):
1125 yield 'window', revs[0] < revs[-1], revs[-1]
1131 yield 'window', revs[0] < revs[-1], revs[-1]
1126 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1132 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1127 srevs = list(nrevs)
1133 srevs = list(nrevs)
1128 srevs.sort()
1134 srevs.sort()
1129 for rev in srevs:
1135 for rev in srevs:
1130 fns = fncache.get(rev)
1136 fns = fncache.get(rev)
1131 if not fns:
1137 if not fns:
1132 def fns_generator():
1138 def fns_generator():
1133 for f in change(rev)[3]:
1139 for f in change(rev)[3]:
1134 if m(f):
1140 if m(f):
1135 yield f
1141 yield f
1136 fns = fns_generator()
1142 fns = fns_generator()
1137 yield 'add', rev, fns
1143 yield 'add', rev, fns
1138 for rev in nrevs:
1144 for rev in nrevs:
1139 yield 'iter', rev, None
1145 yield 'iter', rev, None
1140 return iterate(), m
1146 return iterate(), m
1141
1147
1142 def commit(ui, repo, commitfunc, pats, opts):
1148 def commit(ui, repo, commitfunc, pats, opts):
1143 '''commit the specified files or all outstanding changes'''
1149 '''commit the specified files or all outstanding changes'''
1144 date = opts.get('date')
1150 date = opts.get('date')
1145 if date:
1151 if date:
1146 opts['date'] = util.parsedate(date)
1152 opts['date'] = util.parsedate(date)
1147 message = logmessage(opts)
1153 message = logmessage(opts)
1148
1154
1149 # extract addremove carefully -- this function can be called from a command
1155 # extract addremove carefully -- this function can be called from a command
1150 # that doesn't support addremove
1156 # that doesn't support addremove
1151 if opts.get('addremove'):
1157 if opts.get('addremove'):
1152 addremove(repo, pats, opts)
1158 addremove(repo, pats, opts)
1153
1159
1154 m = match(repo, pats, opts)
1160 m = match(repo, pats, opts)
1155 if pats:
1161 if pats:
1156 status = repo.status(match=m)
1162 status = repo.status(match=m)
1157 modified, added, removed, deleted, unknown = status[:5]
1163 modified, added, removed, deleted, unknown = status[:5]
1158 files = modified + added + removed
1164 files = modified + added + removed
1159 slist = None
1165 slist = None
1160 for f in m.files():
1166 for f in m.files():
1161 if f == '.':
1167 if f == '.':
1162 continue
1168 continue
1163 if f not in files:
1169 if f not in files:
1164 rf = repo.wjoin(f)
1170 rf = repo.wjoin(f)
1165 rel = repo.pathto(f)
1171 rel = repo.pathto(f)
1166 try:
1172 try:
1167 mode = os.lstat(rf)[stat.ST_MODE]
1173 mode = os.lstat(rf)[stat.ST_MODE]
1168 except OSError:
1174 except OSError:
1169 raise util.Abort(_("file %s not found!") % rel)
1175 raise util.Abort(_("file %s not found!") % rel)
1170 if stat.S_ISDIR(mode):
1176 if stat.S_ISDIR(mode):
1171 name = f + '/'
1177 name = f + '/'
1172 if slist is None:
1178 if slist is None:
1173 slist = list(files)
1179 slist = list(files)
1174 slist.sort()
1180 slist.sort()
1175 i = bisect.bisect(slist, name)
1181 i = bisect.bisect(slist, name)
1176 if i >= len(slist) or not slist[i].startswith(name):
1182 if i >= len(slist) or not slist[i].startswith(name):
1177 raise util.Abort(_("no match under directory %s!")
1183 raise util.Abort(_("no match under directory %s!")
1178 % rel)
1184 % rel)
1179 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
1185 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
1180 raise util.Abort(_("can't commit %s: "
1186 raise util.Abort(_("can't commit %s: "
1181 "unsupported file type!") % rel)
1187 "unsupported file type!") % rel)
1182 elif f not in repo.dirstate:
1188 elif f not in repo.dirstate:
1183 raise util.Abort(_("file %s not tracked!") % rel)
1189 raise util.Abort(_("file %s not tracked!") % rel)
1184 m = matchfiles(repo, files)
1190 m = matchfiles(repo, files)
1185 try:
1191 try:
1186 return commitfunc(ui, repo, message, m, opts)
1192 return commitfunc(ui, repo, message, m, opts)
1187 except ValueError, inst:
1193 except ValueError, inst:
1188 raise util.Abort(str(inst))
1194 raise util.Abort(str(inst))
General Comments 0
You need to be logged in to leave comments. Login now