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