##// END OF EJS Templates
merge
Kevin Bullock -
r18783:b99e62a9 merge default
parent child Browse files
Show More
@@ -1,1165 +1,1166 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10
10
11 import os
11 import os
12 import copy
12 import copy
13
13
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
15 node, archival, error, merge, discovery
15 node, archival, error, merge, discovery
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 from mercurial.node import hex
17 from mercurial.node import hex
18 from hgext import rebase
18 from hgext import rebase
19
19
20 import lfutil
20 import lfutil
21 import lfcommands
21 import lfcommands
22
22
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
24
24
25 def installnormalfilesmatchfn(manifest):
25 def installnormalfilesmatchfn(manifest):
26 '''overrides scmutil.match so that the matcher it returns will ignore all
26 '''overrides scmutil.match so that the matcher it returns will ignore all
27 largefiles'''
27 largefiles'''
28 oldmatch = None # for the closure
28 oldmatch = None # for the closure
29 def overridematch(ctx, pats=[], opts={}, globbed=False,
29 def overridematch(ctx, pats=[], opts={}, globbed=False,
30 default='relpath'):
30 default='relpath'):
31 match = oldmatch(ctx, pats, opts, globbed, default)
31 match = oldmatch(ctx, pats, opts, globbed, default)
32 m = copy.copy(match)
32 m = copy.copy(match)
33 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
33 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
34 manifest)
34 manifest)
35 m._files = filter(notlfile, m._files)
35 m._files = filter(notlfile, m._files)
36 m._fmap = set(m._files)
36 m._fmap = set(m._files)
37 origmatchfn = m.matchfn
37 origmatchfn = m.matchfn
38 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
38 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
39 return m
39 return m
40 oldmatch = installmatchfn(overridematch)
40 oldmatch = installmatchfn(overridematch)
41
41
42 def installmatchfn(f):
42 def installmatchfn(f):
43 oldmatch = scmutil.match
43 oldmatch = scmutil.match
44 setattr(f, 'oldmatch', oldmatch)
44 setattr(f, 'oldmatch', oldmatch)
45 scmutil.match = f
45 scmutil.match = f
46 return oldmatch
46 return oldmatch
47
47
48 def restorematchfn():
48 def restorematchfn():
49 '''restores scmutil.match to what it was before installnormalfilesmatchfn
49 '''restores scmutil.match to what it was before installnormalfilesmatchfn
50 was called. no-op if scmutil.match is its original function.
50 was called. no-op if scmutil.match is its original function.
51
51
52 Note that n calls to installnormalfilesmatchfn will require n calls to
52 Note that n calls to installnormalfilesmatchfn will require n calls to
53 restore matchfn to reverse'''
53 restore matchfn to reverse'''
54 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
54 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
55
55
56 def addlargefiles(ui, repo, *pats, **opts):
56 def addlargefiles(ui, repo, *pats, **opts):
57 large = opts.pop('large', None)
57 large = opts.pop('large', None)
58 lfsize = lfutil.getminsize(
58 lfsize = lfutil.getminsize(
59 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
59 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
60
60
61 lfmatcher = None
61 lfmatcher = None
62 if lfutil.islfilesrepo(repo):
62 if lfutil.islfilesrepo(repo):
63 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
63 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
64 if lfpats:
64 if lfpats:
65 lfmatcher = match_.match(repo.root, '', list(lfpats))
65 lfmatcher = match_.match(repo.root, '', list(lfpats))
66
66
67 lfnames = []
67 lfnames = []
68 m = scmutil.match(repo[None], pats, opts)
68 m = scmutil.match(repo[None], pats, opts)
69 m.bad = lambda x, y: None
69 m.bad = lambda x, y: None
70 wctx = repo[None]
70 wctx = repo[None]
71 for f in repo.walk(m):
71 for f in repo.walk(m):
72 exact = m.exact(f)
72 exact = m.exact(f)
73 lfile = lfutil.standin(f) in wctx
73 lfile = lfutil.standin(f) in wctx
74 nfile = f in wctx
74 nfile = f in wctx
75 exists = lfile or nfile
75 exists = lfile or nfile
76
76
77 # Don't warn the user when they attempt to add a normal tracked file.
77 # Don't warn the user when they attempt to add a normal tracked file.
78 # The normal add code will do that for us.
78 # The normal add code will do that for us.
79 if exact and exists:
79 if exact and exists:
80 if lfile:
80 if lfile:
81 ui.warn(_('%s already a largefile\n') % f)
81 ui.warn(_('%s already a largefile\n') % f)
82 continue
82 continue
83
83
84 if (exact or not exists) and not lfutil.isstandin(f):
84 if (exact or not exists) and not lfutil.isstandin(f):
85 wfile = repo.wjoin(f)
85 wfile = repo.wjoin(f)
86
86
87 # In case the file was removed previously, but not committed
87 # In case the file was removed previously, but not committed
88 # (issue3507)
88 # (issue3507)
89 if not os.path.exists(wfile):
89 if not os.path.exists(wfile):
90 continue
90 continue
91
91
92 abovemin = (lfsize and
92 abovemin = (lfsize and
93 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
93 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
94 if large or abovemin or (lfmatcher and lfmatcher(f)):
94 if large or abovemin or (lfmatcher and lfmatcher(f)):
95 lfnames.append(f)
95 lfnames.append(f)
96 if ui.verbose or not exact:
96 if ui.verbose or not exact:
97 ui.status(_('adding %s as a largefile\n') % m.rel(f))
97 ui.status(_('adding %s as a largefile\n') % m.rel(f))
98
98
99 bad = []
99 bad = []
100 standins = []
100 standins = []
101
101
102 # Need to lock, otherwise there could be a race condition between
102 # Need to lock, otherwise there could be a race condition between
103 # when standins are created and added to the repo.
103 # when standins are created and added to the repo.
104 wlock = repo.wlock()
104 wlock = repo.wlock()
105 try:
105 try:
106 if not opts.get('dry_run'):
106 if not opts.get('dry_run'):
107 lfdirstate = lfutil.openlfdirstate(ui, repo)
107 lfdirstate = lfutil.openlfdirstate(ui, repo)
108 for f in lfnames:
108 for f in lfnames:
109 standinname = lfutil.standin(f)
109 standinname = lfutil.standin(f)
110 lfutil.writestandin(repo, standinname, hash='',
110 lfutil.writestandin(repo, standinname, hash='',
111 executable=lfutil.getexecutable(repo.wjoin(f)))
111 executable=lfutil.getexecutable(repo.wjoin(f)))
112 standins.append(standinname)
112 standins.append(standinname)
113 if lfdirstate[f] == 'r':
113 if lfdirstate[f] == 'r':
114 lfdirstate.normallookup(f)
114 lfdirstate.normallookup(f)
115 else:
115 else:
116 lfdirstate.add(f)
116 lfdirstate.add(f)
117 lfdirstate.write()
117 lfdirstate.write()
118 bad += [lfutil.splitstandin(f)
118 bad += [lfutil.splitstandin(f)
119 for f in repo[None].add(standins)
119 for f in repo[None].add(standins)
120 if f in m.files()]
120 if f in m.files()]
121 finally:
121 finally:
122 wlock.release()
122 wlock.release()
123 return bad
123 return bad
124
124
125 def removelargefiles(ui, repo, *pats, **opts):
125 def removelargefiles(ui, repo, *pats, **opts):
126 after = opts.get('after')
126 after = opts.get('after')
127 if not pats and not after:
127 if not pats and not after:
128 raise util.Abort(_('no files specified'))
128 raise util.Abort(_('no files specified'))
129 m = scmutil.match(repo[None], pats, opts)
129 m = scmutil.match(repo[None], pats, opts)
130 try:
130 try:
131 repo.lfstatus = True
131 repo.lfstatus = True
132 s = repo.status(match=m, clean=True)
132 s = repo.status(match=m, clean=True)
133 finally:
133 finally:
134 repo.lfstatus = False
134 repo.lfstatus = False
135 manifest = repo[None].manifest()
135 manifest = repo[None].manifest()
136 modified, added, deleted, clean = [[f for f in list
136 modified, added, deleted, clean = [[f for f in list
137 if lfutil.standin(f) in manifest]
137 if lfutil.standin(f) in manifest]
138 for list in [s[0], s[1], s[3], s[6]]]
138 for list in [s[0], s[1], s[3], s[6]]]
139
139
140 def warn(files, msg):
140 def warn(files, msg):
141 for f in files:
141 for f in files:
142 ui.warn(msg % m.rel(f))
142 ui.warn(msg % m.rel(f))
143 return int(len(files) > 0)
143 return int(len(files) > 0)
144
144
145 result = 0
145 result = 0
146
146
147 if after:
147 if after:
148 remove, forget = deleted, []
148 remove, forget = deleted, []
149 result = warn(modified + added + clean,
149 result = warn(modified + added + clean,
150 _('not removing %s: file still exists\n'))
150 _('not removing %s: file still exists\n'))
151 else:
151 else:
152 remove, forget = deleted + clean, []
152 remove, forget = deleted + clean, []
153 result = warn(modified, _('not removing %s: file is modified (use -f'
153 result = warn(modified, _('not removing %s: file is modified (use -f'
154 ' to force removal)\n'))
154 ' to force removal)\n'))
155 result = warn(added, _('not removing %s: file has been marked for add'
155 result = warn(added, _('not removing %s: file has been marked for add'
156 ' (use forget to undo)\n')) or result
156 ' (use forget to undo)\n')) or result
157
157
158 for f in sorted(remove + forget):
158 for f in sorted(remove + forget):
159 if ui.verbose or not m.exact(f):
159 if ui.verbose or not m.exact(f):
160 ui.status(_('removing %s\n') % m.rel(f))
160 ui.status(_('removing %s\n') % m.rel(f))
161
161
162 # Need to lock because standin files are deleted then removed from the
162 # Need to lock because standin files are deleted then removed from the
163 # repository and we could race in-between.
163 # repository and we could race in-between.
164 wlock = repo.wlock()
164 wlock = repo.wlock()
165 try:
165 try:
166 lfdirstate = lfutil.openlfdirstate(ui, repo)
166 lfdirstate = lfutil.openlfdirstate(ui, repo)
167 for f in remove:
167 for f in remove:
168 if not after:
168 if not after:
169 # If this is being called by addremove, notify the user that we
169 # If this is being called by addremove, notify the user that we
170 # are removing the file.
170 # are removing the file.
171 if getattr(repo, "_isaddremove", False):
171 if getattr(repo, "_isaddremove", False):
172 ui.status(_('removing %s\n') % f)
172 ui.status(_('removing %s\n') % f)
173 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
173 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
174 lfdirstate.remove(f)
174 lfdirstate.remove(f)
175 lfdirstate.write()
175 lfdirstate.write()
176 forget = [lfutil.standin(f) for f in forget]
176 forget = [lfutil.standin(f) for f in forget]
177 remove = [lfutil.standin(f) for f in remove]
177 remove = [lfutil.standin(f) for f in remove]
178 repo[None].forget(forget)
178 repo[None].forget(forget)
179 # If this is being called by addremove, let the original addremove
179 # If this is being called by addremove, let the original addremove
180 # function handle this.
180 # function handle this.
181 if not getattr(repo, "_isaddremove", False):
181 if not getattr(repo, "_isaddremove", False):
182 for f in remove:
182 for f in remove:
183 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
183 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
184 repo[None].forget(remove)
184 repo[None].forget(remove)
185 finally:
185 finally:
186 wlock.release()
186 wlock.release()
187
187
188 return result
188 return result
189
189
190 # For overriding mercurial.hgweb.webcommands so that largefiles will
190 # For overriding mercurial.hgweb.webcommands so that largefiles will
191 # appear at their right place in the manifests.
191 # appear at their right place in the manifests.
192 def decodepath(orig, path):
192 def decodepath(orig, path):
193 return lfutil.splitstandin(path) or path
193 return lfutil.splitstandin(path) or path
194
194
195 # -- Wrappers: modify existing commands --------------------------------
195 # -- Wrappers: modify existing commands --------------------------------
196
196
197 # Add works by going through the files that the user wanted to add and
197 # Add works by going through the files that the user wanted to add and
198 # checking if they should be added as largefiles. Then it makes a new
198 # checking if they should be added as largefiles. Then it makes a new
199 # matcher which matches only the normal files and runs the original
199 # matcher which matches only the normal files and runs the original
200 # version of add.
200 # version of add.
201 def overrideadd(orig, ui, repo, *pats, **opts):
201 def overrideadd(orig, ui, repo, *pats, **opts):
202 normal = opts.pop('normal')
202 normal = opts.pop('normal')
203 if normal:
203 if normal:
204 if opts.get('large'):
204 if opts.get('large'):
205 raise util.Abort(_('--normal cannot be used with --large'))
205 raise util.Abort(_('--normal cannot be used with --large'))
206 return orig(ui, repo, *pats, **opts)
206 return orig(ui, repo, *pats, **opts)
207 bad = addlargefiles(ui, repo, *pats, **opts)
207 bad = addlargefiles(ui, repo, *pats, **opts)
208 installnormalfilesmatchfn(repo[None].manifest())
208 installnormalfilesmatchfn(repo[None].manifest())
209 result = orig(ui, repo, *pats, **opts)
209 result = orig(ui, repo, *pats, **opts)
210 restorematchfn()
210 restorematchfn()
211
211
212 return (result == 1 or bad) and 1 or 0
212 return (result == 1 or bad) and 1 or 0
213
213
214 def overrideremove(orig, ui, repo, *pats, **opts):
214 def overrideremove(orig, ui, repo, *pats, **opts):
215 installnormalfilesmatchfn(repo[None].manifest())
215 installnormalfilesmatchfn(repo[None].manifest())
216 result = orig(ui, repo, *pats, **opts)
216 result = orig(ui, repo, *pats, **opts)
217 restorematchfn()
217 restorematchfn()
218 return removelargefiles(ui, repo, *pats, **opts) or result
218 return removelargefiles(ui, repo, *pats, **opts) or result
219
219
220 def overridestatusfn(orig, repo, rev2, **opts):
220 def overridestatusfn(orig, repo, rev2, **opts):
221 try:
221 try:
222 repo._repo.lfstatus = True
222 repo._repo.lfstatus = True
223 return orig(repo, rev2, **opts)
223 return orig(repo, rev2, **opts)
224 finally:
224 finally:
225 repo._repo.lfstatus = False
225 repo._repo.lfstatus = False
226
226
227 def overridestatus(orig, ui, repo, *pats, **opts):
227 def overridestatus(orig, ui, repo, *pats, **opts):
228 try:
228 try:
229 repo.lfstatus = True
229 repo.lfstatus = True
230 return orig(ui, repo, *pats, **opts)
230 return orig(ui, repo, *pats, **opts)
231 finally:
231 finally:
232 repo.lfstatus = False
232 repo.lfstatus = False
233
233
234 def overridedirty(orig, repo, ignoreupdate=False):
234 def overridedirty(orig, repo, ignoreupdate=False):
235 try:
235 try:
236 repo._repo.lfstatus = True
236 repo._repo.lfstatus = True
237 return orig(repo, ignoreupdate)
237 return orig(repo, ignoreupdate)
238 finally:
238 finally:
239 repo._repo.lfstatus = False
239 repo._repo.lfstatus = False
240
240
241 def overridelog(orig, ui, repo, *pats, **opts):
241 def overridelog(orig, ui, repo, *pats, **opts):
242 def overridematch(ctx, pats=[], opts={}, globbed=False,
242 def overridematch(ctx, pats=[], opts={}, globbed=False,
243 default='relpath'):
243 default='relpath'):
244 """Matcher that merges root directory with .hglf, suitable for log.
244 """Matcher that merges root directory with .hglf, suitable for log.
245 It is still possible to match .hglf directly.
245 It is still possible to match .hglf directly.
246 For any listed files run log on the standin too.
246 For any listed files run log on the standin too.
247 matchfn tries both the given filename and with .hglf stripped.
247 matchfn tries both the given filename and with .hglf stripped.
248 """
248 """
249 match = oldmatch(ctx, pats, opts, globbed, default)
249 match = oldmatch(ctx, pats, opts, globbed, default)
250 m = copy.copy(match)
250 m = copy.copy(match)
251 standins = [lfutil.standin(f) for f in m._files]
251 standins = [lfutil.standin(f) for f in m._files]
252 m._files.extend(standins)
252 m._files.extend(standins)
253 m._fmap = set(m._files)
253 m._fmap = set(m._files)
254 origmatchfn = m.matchfn
254 origmatchfn = m.matchfn
255 def lfmatchfn(f):
255 def lfmatchfn(f):
256 lf = lfutil.splitstandin(f)
256 lf = lfutil.splitstandin(f)
257 if lf is not None and origmatchfn(lf):
257 if lf is not None and origmatchfn(lf):
258 return True
258 return True
259 r = origmatchfn(f)
259 r = origmatchfn(f)
260 return r
260 return r
261 m.matchfn = lfmatchfn
261 m.matchfn = lfmatchfn
262 return m
262 return m
263 oldmatch = installmatchfn(overridematch)
263 oldmatch = installmatchfn(overridematch)
264 try:
264 try:
265 repo.lfstatus = True
265 repo.lfstatus = True
266 return orig(ui, repo, *pats, **opts)
266 return orig(ui, repo, *pats, **opts)
267 finally:
267 finally:
268 repo.lfstatus = False
268 repo.lfstatus = False
269 restorematchfn()
269 restorematchfn()
270
270
271 def overrideverify(orig, ui, repo, *pats, **opts):
271 def overrideverify(orig, ui, repo, *pats, **opts):
272 large = opts.pop('large', False)
272 large = opts.pop('large', False)
273 all = opts.pop('lfa', False)
273 all = opts.pop('lfa', False)
274 contents = opts.pop('lfc', False)
274 contents = opts.pop('lfc', False)
275
275
276 result = orig(ui, repo, *pats, **opts)
276 result = orig(ui, repo, *pats, **opts)
277 if large or all or contents:
277 if large or all or contents:
278 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
278 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
279 return result
279 return result
280
280
281 def overridedebugstate(orig, ui, repo, *pats, **opts):
281 def overridedebugstate(orig, ui, repo, *pats, **opts):
282 large = opts.pop('large', False)
282 large = opts.pop('large', False)
283 if large:
283 if large:
284 lfcommands.debugdirstate(ui, repo)
284 lfcommands.debugdirstate(ui, repo)
285 else:
285 else:
286 orig(ui, repo, *pats, **opts)
286 orig(ui, repo, *pats, **opts)
287
287
288 # Override needs to refresh standins so that update's normal merge
288 # Override needs to refresh standins so that update's normal merge
289 # will go through properly. Then the other update hook (overriding repo.update)
289 # will go through properly. Then the other update hook (overriding repo.update)
290 # will get the new files. Filemerge is also overridden so that the merge
290 # will get the new files. Filemerge is also overridden so that the merge
291 # will merge standins correctly.
291 # will merge standins correctly.
292 def overrideupdate(orig, ui, repo, *pats, **opts):
292 def overrideupdate(orig, ui, repo, *pats, **opts):
293 lfdirstate = lfutil.openlfdirstate(ui, repo)
293 lfdirstate = lfutil.openlfdirstate(ui, repo)
294 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
294 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
295 False, False)
295 False, False)
296 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
296 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
297
297
298 # Need to lock between the standins getting updated and their
298 # Need to lock between the standins getting updated and their
299 # largefiles getting updated
299 # largefiles getting updated
300 wlock = repo.wlock()
300 wlock = repo.wlock()
301 try:
301 try:
302 if opts['check']:
302 if opts['check']:
303 mod = len(modified) > 0
303 mod = len(modified) > 0
304 for lfile in unsure:
304 for lfile in unsure:
305 standin = lfutil.standin(lfile)
305 standin = lfutil.standin(lfile)
306 if repo['.'][standin].data().strip() != \
306 if repo['.'][standin].data().strip() != \
307 lfutil.hashfile(repo.wjoin(lfile)):
307 lfutil.hashfile(repo.wjoin(lfile)):
308 mod = True
308 mod = True
309 else:
309 else:
310 lfdirstate.normal(lfile)
310 lfdirstate.normal(lfile)
311 lfdirstate.write()
311 lfdirstate.write()
312 if mod:
312 if mod:
313 raise util.Abort(_('uncommitted local changes'))
313 raise util.Abort(_('uncommitted local changes'))
314 # XXX handle removed differently
314 # XXX handle removed differently
315 if not opts['clean']:
315 if not opts['clean']:
316 for lfile in unsure + modified + added:
316 for lfile in unsure + modified + added:
317 lfutil.updatestandin(repo, lfutil.standin(lfile))
317 lfutil.updatestandin(repo, lfutil.standin(lfile))
318 finally:
318 finally:
319 wlock.release()
319 wlock.release()
320 return orig(ui, repo, *pats, **opts)
320 return orig(ui, repo, *pats, **opts)
321
321
322 # Before starting the manifest merge, merge.updates will call
322 # Before starting the manifest merge, merge.updates will call
323 # _checkunknown to check if there are any files in the merged-in
323 # _checkunknown to check if there are any files in the merged-in
324 # changeset that collide with unknown files in the working copy.
324 # changeset that collide with unknown files in the working copy.
325 #
325 #
326 # The largefiles are seen as unknown, so this prevents us from merging
326 # The largefiles are seen as unknown, so this prevents us from merging
327 # in a file 'foo' if we already have a largefile with the same name.
327 # in a file 'foo' if we already have a largefile with the same name.
328 #
328 #
329 # The overridden function filters the unknown files by removing any
329 # The overridden function filters the unknown files by removing any
330 # largefiles. This makes the merge proceed and we can then handle this
330 # largefiles. This makes the merge proceed and we can then handle this
331 # case further in the overridden manifestmerge function below.
331 # case further in the overridden manifestmerge function below.
332 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
332 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
333 if lfutil.standin(f) in wctx:
333 if lfutil.standin(f) in wctx:
334 return False
334 return False
335 return origfn(repo, wctx, mctx, f)
335 return origfn(repo, wctx, mctx, f)
336
336
337 # The manifest merge handles conflicts on the manifest level. We want
337 # The manifest merge handles conflicts on the manifest level. We want
338 # to handle changes in largefile-ness of files at this level too.
338 # to handle changes in largefile-ness of files at this level too.
339 #
339 #
340 # The strategy is to run the original manifestmerge and then process
340 # The strategy is to run the original manifestmerge and then process
341 # the action list it outputs. There are two cases we need to deal with:
341 # the action list it outputs. There are two cases we need to deal with:
342 #
342 #
343 # 1. Normal file in p1, largefile in p2. Here the largefile is
343 # 1. Normal file in p1, largefile in p2. Here the largefile is
344 # detected via its standin file, which will enter the working copy
344 # detected via its standin file, which will enter the working copy
345 # with a "get" action. It is not "merge" since the standin is all
345 # with a "get" action. It is not "merge" since the standin is all
346 # Mercurial is concerned with at this level -- the link to the
346 # Mercurial is concerned with at this level -- the link to the
347 # existing normal file is not relevant here.
347 # existing normal file is not relevant here.
348 #
348 #
349 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
349 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
350 # since the largefile will be present in the working copy and
350 # since the largefile will be present in the working copy and
351 # different from the normal file in p2. Mercurial therefore
351 # different from the normal file in p2. Mercurial therefore
352 # triggers a merge action.
352 # triggers a merge action.
353 #
353 #
354 # In both cases, we prompt the user and emit new actions to either
354 # In both cases, we prompt the user and emit new actions to either
355 # remove the standin (if the normal file was kept) or to remove the
355 # remove the standin (if the normal file was kept) or to remove the
356 # normal file and get the standin (if the largefile was kept). The
356 # normal file and get the standin (if the largefile was kept). The
357 # default prompt answer is to use the largefile version since it was
357 # default prompt answer is to use the largefile version since it was
358 # presumably changed on purpose.
358 # presumably changed on purpose.
359 #
359 #
360 # Finally, the merge.applyupdates function will then take care of
360 # Finally, the merge.applyupdates function will then take care of
361 # writing the files into the working copy and lfcommands.updatelfiles
361 # writing the files into the working copy and lfcommands.updatelfiles
362 # will update the largefiles.
362 # will update the largefiles.
363 def overridemanifestmerge(origfn, repo, p1, p2, pa, branchmerge, force,
363 def overridemanifestmerge(origfn, repo, p1, p2, pa, branchmerge, force,
364 partial):
364 partial, acceptremote=False):
365 overwrite = force and not branchmerge
365 overwrite = force and not branchmerge
366 actions = origfn(repo, p1, p2, pa, branchmerge, force, partial)
366 actions = origfn(repo, p1, p2, pa, branchmerge, force, partial,
367 acceptremote)
367 processed = []
368 processed = []
368
369
369 for action in actions:
370 for action in actions:
370 if overwrite:
371 if overwrite:
371 processed.append(action)
372 processed.append(action)
372 continue
373 continue
373 f, m, args, msg = action
374 f, m, args, msg = action
374
375
375 choices = (_('&Largefile'), _('&Normal file'))
376 choices = (_('&Largefile'), _('&Normal file'))
376 if m == "g" and lfutil.splitstandin(f) in p1 and f in p2:
377 if m == "g" and lfutil.splitstandin(f) in p1 and f in p2:
377 # Case 1: normal file in the working copy, largefile in
378 # Case 1: normal file in the working copy, largefile in
378 # the second parent
379 # the second parent
379 lfile = lfutil.splitstandin(f)
380 lfile = lfutil.splitstandin(f)
380 standin = f
381 standin = f
381 msg = _('%s has been turned into a largefile\n'
382 msg = _('%s has been turned into a largefile\n'
382 'use (l)argefile or keep as (n)ormal file?') % lfile
383 'use (l)argefile or keep as (n)ormal file?') % lfile
383 if repo.ui.promptchoice(msg, choices, 0) == 0:
384 if repo.ui.promptchoice(msg, choices, 0) == 0:
384 processed.append((lfile, "r", None, msg))
385 processed.append((lfile, "r", None, msg))
385 processed.append((standin, "g", (p2.flags(standin),), msg))
386 processed.append((standin, "g", (p2.flags(standin),), msg))
386 else:
387 else:
387 processed.append((standin, "r", None, msg))
388 processed.append((standin, "r", None, msg))
388 elif m == "g" and lfutil.standin(f) in p1 and f in p2:
389 elif m == "g" and lfutil.standin(f) in p1 and f in p2:
389 # Case 2: largefile in the working copy, normal file in
390 # Case 2: largefile in the working copy, normal file in
390 # the second parent
391 # the second parent
391 standin = lfutil.standin(f)
392 standin = lfutil.standin(f)
392 lfile = f
393 lfile = f
393 msg = _('%s has been turned into a normal file\n'
394 msg = _('%s has been turned into a normal file\n'
394 'keep as (l)argefile or use (n)ormal file?') % lfile
395 'keep as (l)argefile or use (n)ormal file?') % lfile
395 if repo.ui.promptchoice(msg, choices, 0) == 0:
396 if repo.ui.promptchoice(msg, choices, 0) == 0:
396 processed.append((lfile, "r", None, msg))
397 processed.append((lfile, "r", None, msg))
397 else:
398 else:
398 processed.append((standin, "r", None, msg))
399 processed.append((standin, "r", None, msg))
399 processed.append((lfile, "g", (p2.flags(lfile),), msg))
400 processed.append((lfile, "g", (p2.flags(lfile),), msg))
400 else:
401 else:
401 processed.append(action)
402 processed.append(action)
402
403
403 return processed
404 return processed
404
405
405 # Override filemerge to prompt the user about how they wish to merge
406 # Override filemerge to prompt the user about how they wish to merge
406 # largefiles. This will handle identical edits, and copy/rename +
407 # largefiles. This will handle identical edits, and copy/rename +
407 # edit without prompting the user.
408 # edit without prompting the user.
408 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca):
409 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca):
409 # Use better variable names here. Because this is a wrapper we cannot
410 # Use better variable names here. Because this is a wrapper we cannot
410 # change the variable names in the function declaration.
411 # change the variable names in the function declaration.
411 fcdest, fcother, fcancestor = fcd, fco, fca
412 fcdest, fcother, fcancestor = fcd, fco, fca
412 if not lfutil.isstandin(orig):
413 if not lfutil.isstandin(orig):
413 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
414 return origfn(repo, mynode, orig, fcdest, fcother, fcancestor)
414 else:
415 else:
415 if not fcother.cmp(fcdest): # files identical?
416 if not fcother.cmp(fcdest): # files identical?
416 return None
417 return None
417
418
418 # backwards, use working dir parent as ancestor
419 # backwards, use working dir parent as ancestor
419 if fcancestor == fcother:
420 if fcancestor == fcother:
420 fcancestor = fcdest.parents()[0]
421 fcancestor = fcdest.parents()[0]
421
422
422 if orig != fcother.path():
423 if orig != fcother.path():
423 repo.ui.status(_('merging %s and %s to %s\n')
424 repo.ui.status(_('merging %s and %s to %s\n')
424 % (lfutil.splitstandin(orig),
425 % (lfutil.splitstandin(orig),
425 lfutil.splitstandin(fcother.path()),
426 lfutil.splitstandin(fcother.path()),
426 lfutil.splitstandin(fcdest.path())))
427 lfutil.splitstandin(fcdest.path())))
427 else:
428 else:
428 repo.ui.status(_('merging %s\n')
429 repo.ui.status(_('merging %s\n')
429 % lfutil.splitstandin(fcdest.path()))
430 % lfutil.splitstandin(fcdest.path()))
430
431
431 if fcancestor.path() != fcother.path() and fcother.data() == \
432 if fcancestor.path() != fcother.path() and fcother.data() == \
432 fcancestor.data():
433 fcancestor.data():
433 return 0
434 return 0
434 if fcancestor.path() != fcdest.path() and fcdest.data() == \
435 if fcancestor.path() != fcdest.path() and fcdest.data() == \
435 fcancestor.data():
436 fcancestor.data():
436 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
437 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
437 return 0
438 return 0
438
439
439 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
440 if repo.ui.promptchoice(_('largefile %s has a merge conflict\n'
440 'keep (l)ocal or take (o)ther?') %
441 'keep (l)ocal or take (o)ther?') %
441 lfutil.splitstandin(orig),
442 lfutil.splitstandin(orig),
442 (_('&Local'), _('&Other')), 0) == 0:
443 (_('&Local'), _('&Other')), 0) == 0:
443 return 0
444 return 0
444 else:
445 else:
445 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
446 repo.wwrite(fcdest.path(), fcother.data(), fcother.flags())
446 return 0
447 return 0
447
448
448 # Copy first changes the matchers to match standins instead of
449 # Copy first changes the matchers to match standins instead of
449 # largefiles. Then it overrides util.copyfile in that function it
450 # largefiles. Then it overrides util.copyfile in that function it
450 # checks if the destination largefile already exists. It also keeps a
451 # checks if the destination largefile already exists. It also keeps a
451 # list of copied files so that the largefiles can be copied and the
452 # list of copied files so that the largefiles can be copied and the
452 # dirstate updated.
453 # dirstate updated.
453 def overridecopy(orig, ui, repo, pats, opts, rename=False):
454 def overridecopy(orig, ui, repo, pats, opts, rename=False):
454 # doesn't remove largefile on rename
455 # doesn't remove largefile on rename
455 if len(pats) < 2:
456 if len(pats) < 2:
456 # this isn't legal, let the original function deal with it
457 # this isn't legal, let the original function deal with it
457 return orig(ui, repo, pats, opts, rename)
458 return orig(ui, repo, pats, opts, rename)
458
459
459 def makestandin(relpath):
460 def makestandin(relpath):
460 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
461 path = scmutil.canonpath(repo.root, repo.getcwd(), relpath)
461 return os.path.join(repo.wjoin(lfutil.standin(path)))
462 return os.path.join(repo.wjoin(lfutil.standin(path)))
462
463
463 fullpats = scmutil.expandpats(pats)
464 fullpats = scmutil.expandpats(pats)
464 dest = fullpats[-1]
465 dest = fullpats[-1]
465
466
466 if os.path.isdir(dest):
467 if os.path.isdir(dest):
467 if not os.path.isdir(makestandin(dest)):
468 if not os.path.isdir(makestandin(dest)):
468 os.makedirs(makestandin(dest))
469 os.makedirs(makestandin(dest))
469 # This could copy both lfiles and normal files in one command,
470 # This could copy both lfiles and normal files in one command,
470 # but we don't want to do that. First replace their matcher to
471 # but we don't want to do that. First replace their matcher to
471 # only match normal files and run it, then replace it to just
472 # only match normal files and run it, then replace it to just
472 # match largefiles and run it again.
473 # match largefiles and run it again.
473 nonormalfiles = False
474 nonormalfiles = False
474 nolfiles = False
475 nolfiles = False
475 try:
476 try:
476 try:
477 try:
477 installnormalfilesmatchfn(repo[None].manifest())
478 installnormalfilesmatchfn(repo[None].manifest())
478 result = orig(ui, repo, pats, opts, rename)
479 result = orig(ui, repo, pats, opts, rename)
479 except util.Abort, e:
480 except util.Abort, e:
480 if str(e) != _('no files to copy'):
481 if str(e) != _('no files to copy'):
481 raise e
482 raise e
482 else:
483 else:
483 nonormalfiles = True
484 nonormalfiles = True
484 result = 0
485 result = 0
485 finally:
486 finally:
486 restorematchfn()
487 restorematchfn()
487
488
488 # The first rename can cause our current working directory to be removed.
489 # The first rename can cause our current working directory to be removed.
489 # In that case there is nothing left to copy/rename so just quit.
490 # In that case there is nothing left to copy/rename so just quit.
490 try:
491 try:
491 repo.getcwd()
492 repo.getcwd()
492 except OSError:
493 except OSError:
493 return result
494 return result
494
495
495 try:
496 try:
496 try:
497 try:
497 # When we call orig below it creates the standins but we don't add
498 # When we call orig below it creates the standins but we don't add
498 # them to the dir state until later so lock during that time.
499 # them to the dir state until later so lock during that time.
499 wlock = repo.wlock()
500 wlock = repo.wlock()
500
501
501 manifest = repo[None].manifest()
502 manifest = repo[None].manifest()
502 oldmatch = None # for the closure
503 oldmatch = None # for the closure
503 def overridematch(ctx, pats=[], opts={}, globbed=False,
504 def overridematch(ctx, pats=[], opts={}, globbed=False,
504 default='relpath'):
505 default='relpath'):
505 newpats = []
506 newpats = []
506 # The patterns were previously mangled to add the standin
507 # The patterns were previously mangled to add the standin
507 # directory; we need to remove that now
508 # directory; we need to remove that now
508 for pat in pats:
509 for pat in pats:
509 if match_.patkind(pat) is None and lfutil.shortname in pat:
510 if match_.patkind(pat) is None and lfutil.shortname in pat:
510 newpats.append(pat.replace(lfutil.shortname, ''))
511 newpats.append(pat.replace(lfutil.shortname, ''))
511 else:
512 else:
512 newpats.append(pat)
513 newpats.append(pat)
513 match = oldmatch(ctx, newpats, opts, globbed, default)
514 match = oldmatch(ctx, newpats, opts, globbed, default)
514 m = copy.copy(match)
515 m = copy.copy(match)
515 lfile = lambda f: lfutil.standin(f) in manifest
516 lfile = lambda f: lfutil.standin(f) in manifest
516 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
517 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
517 m._fmap = set(m._files)
518 m._fmap = set(m._files)
518 origmatchfn = m.matchfn
519 origmatchfn = m.matchfn
519 m.matchfn = lambda f: (lfutil.isstandin(f) and
520 m.matchfn = lambda f: (lfutil.isstandin(f) and
520 (f in manifest) and
521 (f in manifest) and
521 origmatchfn(lfutil.splitstandin(f)) or
522 origmatchfn(lfutil.splitstandin(f)) or
522 None)
523 None)
523 return m
524 return m
524 oldmatch = installmatchfn(overridematch)
525 oldmatch = installmatchfn(overridematch)
525 listpats = []
526 listpats = []
526 for pat in pats:
527 for pat in pats:
527 if match_.patkind(pat) is not None:
528 if match_.patkind(pat) is not None:
528 listpats.append(pat)
529 listpats.append(pat)
529 else:
530 else:
530 listpats.append(makestandin(pat))
531 listpats.append(makestandin(pat))
531
532
532 try:
533 try:
533 origcopyfile = util.copyfile
534 origcopyfile = util.copyfile
534 copiedfiles = []
535 copiedfiles = []
535 def overridecopyfile(src, dest):
536 def overridecopyfile(src, dest):
536 if (lfutil.shortname in src and
537 if (lfutil.shortname in src and
537 dest.startswith(repo.wjoin(lfutil.shortname))):
538 dest.startswith(repo.wjoin(lfutil.shortname))):
538 destlfile = dest.replace(lfutil.shortname, '')
539 destlfile = dest.replace(lfutil.shortname, '')
539 if not opts['force'] and os.path.exists(destlfile):
540 if not opts['force'] and os.path.exists(destlfile):
540 raise IOError('',
541 raise IOError('',
541 _('destination largefile already exists'))
542 _('destination largefile already exists'))
542 copiedfiles.append((src, dest))
543 copiedfiles.append((src, dest))
543 origcopyfile(src, dest)
544 origcopyfile(src, dest)
544
545
545 util.copyfile = overridecopyfile
546 util.copyfile = overridecopyfile
546 result += orig(ui, repo, listpats, opts, rename)
547 result += orig(ui, repo, listpats, opts, rename)
547 finally:
548 finally:
548 util.copyfile = origcopyfile
549 util.copyfile = origcopyfile
549
550
550 lfdirstate = lfutil.openlfdirstate(ui, repo)
551 lfdirstate = lfutil.openlfdirstate(ui, repo)
551 for (src, dest) in copiedfiles:
552 for (src, dest) in copiedfiles:
552 if (lfutil.shortname in src and
553 if (lfutil.shortname in src and
553 dest.startswith(repo.wjoin(lfutil.shortname))):
554 dest.startswith(repo.wjoin(lfutil.shortname))):
554 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
555 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
555 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
556 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
556 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
557 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
557 if not os.path.isdir(destlfiledir):
558 if not os.path.isdir(destlfiledir):
558 os.makedirs(destlfiledir)
559 os.makedirs(destlfiledir)
559 if rename:
560 if rename:
560 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
561 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
561 lfdirstate.remove(srclfile)
562 lfdirstate.remove(srclfile)
562 else:
563 else:
563 util.copyfile(repo.wjoin(srclfile),
564 util.copyfile(repo.wjoin(srclfile),
564 repo.wjoin(destlfile))
565 repo.wjoin(destlfile))
565
566
566 lfdirstate.add(destlfile)
567 lfdirstate.add(destlfile)
567 lfdirstate.write()
568 lfdirstate.write()
568 except util.Abort, e:
569 except util.Abort, e:
569 if str(e) != _('no files to copy'):
570 if str(e) != _('no files to copy'):
570 raise e
571 raise e
571 else:
572 else:
572 nolfiles = True
573 nolfiles = True
573 finally:
574 finally:
574 restorematchfn()
575 restorematchfn()
575 wlock.release()
576 wlock.release()
576
577
577 if nolfiles and nonormalfiles:
578 if nolfiles and nonormalfiles:
578 raise util.Abort(_('no files to copy'))
579 raise util.Abort(_('no files to copy'))
579
580
580 return result
581 return result
581
582
582 # When the user calls revert, we have to be careful to not revert any
583 # When the user calls revert, we have to be careful to not revert any
583 # changes to other largefiles accidentally. This means we have to keep
584 # changes to other largefiles accidentally. This means we have to keep
584 # track of the largefiles that are being reverted so we only pull down
585 # track of the largefiles that are being reverted so we only pull down
585 # the necessary largefiles.
586 # the necessary largefiles.
586 #
587 #
587 # Standins are only updated (to match the hash of largefiles) before
588 # Standins are only updated (to match the hash of largefiles) before
588 # commits. Update the standins then run the original revert, changing
589 # commits. Update the standins then run the original revert, changing
589 # the matcher to hit standins instead of largefiles. Based on the
590 # the matcher to hit standins instead of largefiles. Based on the
590 # resulting standins update the largefiles. Then return the standins
591 # resulting standins update the largefiles. Then return the standins
591 # to their proper state
592 # to their proper state
592 def overriderevert(orig, ui, repo, *pats, **opts):
593 def overriderevert(orig, ui, repo, *pats, **opts):
593 # Because we put the standins in a bad state (by updating them)
594 # Because we put the standins in a bad state (by updating them)
594 # and then return them to a correct state we need to lock to
595 # and then return them to a correct state we need to lock to
595 # prevent others from changing them in their incorrect state.
596 # prevent others from changing them in their incorrect state.
596 wlock = repo.wlock()
597 wlock = repo.wlock()
597 try:
598 try:
598 lfdirstate = lfutil.openlfdirstate(ui, repo)
599 lfdirstate = lfutil.openlfdirstate(ui, repo)
599 (modified, added, removed, missing, unknown, ignored, clean) = \
600 (modified, added, removed, missing, unknown, ignored, clean) = \
600 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
601 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
601 lfdirstate.write()
602 lfdirstate.write()
602 for lfile in modified:
603 for lfile in modified:
603 lfutil.updatestandin(repo, lfutil.standin(lfile))
604 lfutil.updatestandin(repo, lfutil.standin(lfile))
604 for lfile in missing:
605 for lfile in missing:
605 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
606 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
606 os.unlink(repo.wjoin(lfutil.standin(lfile)))
607 os.unlink(repo.wjoin(lfutil.standin(lfile)))
607
608
608 try:
609 try:
609 ctx = scmutil.revsingle(repo, opts.get('rev'))
610 ctx = scmutil.revsingle(repo, opts.get('rev'))
610 oldmatch = None # for the closure
611 oldmatch = None # for the closure
611 def overridematch(ctx, pats=[], opts={}, globbed=False,
612 def overridematch(ctx, pats=[], opts={}, globbed=False,
612 default='relpath'):
613 default='relpath'):
613 match = oldmatch(ctx, pats, opts, globbed, default)
614 match = oldmatch(ctx, pats, opts, globbed, default)
614 m = copy.copy(match)
615 m = copy.copy(match)
615 def tostandin(f):
616 def tostandin(f):
616 if lfutil.standin(f) in ctx:
617 if lfutil.standin(f) in ctx:
617 return lfutil.standin(f)
618 return lfutil.standin(f)
618 elif lfutil.standin(f) in repo[None]:
619 elif lfutil.standin(f) in repo[None]:
619 return None
620 return None
620 return f
621 return f
621 m._files = [tostandin(f) for f in m._files]
622 m._files = [tostandin(f) for f in m._files]
622 m._files = [f for f in m._files if f is not None]
623 m._files = [f for f in m._files if f is not None]
623 m._fmap = set(m._files)
624 m._fmap = set(m._files)
624 origmatchfn = m.matchfn
625 origmatchfn = m.matchfn
625 def matchfn(f):
626 def matchfn(f):
626 if lfutil.isstandin(f):
627 if lfutil.isstandin(f):
627 # We need to keep track of what largefiles are being
628 # We need to keep track of what largefiles are being
628 # matched so we know which ones to update later --
629 # matched so we know which ones to update later --
629 # otherwise we accidentally revert changes to other
630 # otherwise we accidentally revert changes to other
630 # largefiles. This is repo-specific, so duckpunch the
631 # largefiles. This is repo-specific, so duckpunch the
631 # repo object to keep the list of largefiles for us
632 # repo object to keep the list of largefiles for us
632 # later.
633 # later.
633 if origmatchfn(lfutil.splitstandin(f)) and \
634 if origmatchfn(lfutil.splitstandin(f)) and \
634 (f in repo[None] or f in ctx):
635 (f in repo[None] or f in ctx):
635 lfileslist = getattr(repo, '_lfilestoupdate', [])
636 lfileslist = getattr(repo, '_lfilestoupdate', [])
636 lfileslist.append(lfutil.splitstandin(f))
637 lfileslist.append(lfutil.splitstandin(f))
637 repo._lfilestoupdate = lfileslist
638 repo._lfilestoupdate = lfileslist
638 return True
639 return True
639 else:
640 else:
640 return False
641 return False
641 return origmatchfn(f)
642 return origmatchfn(f)
642 m.matchfn = matchfn
643 m.matchfn = matchfn
643 return m
644 return m
644 oldmatch = installmatchfn(overridematch)
645 oldmatch = installmatchfn(overridematch)
645 scmutil.match
646 scmutil.match
646 matches = overridematch(repo[None], pats, opts)
647 matches = overridematch(repo[None], pats, opts)
647 orig(ui, repo, *pats, **opts)
648 orig(ui, repo, *pats, **opts)
648 finally:
649 finally:
649 restorematchfn()
650 restorematchfn()
650 lfileslist = getattr(repo, '_lfilestoupdate', [])
651 lfileslist = getattr(repo, '_lfilestoupdate', [])
651 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
652 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
652 printmessage=False)
653 printmessage=False)
653
654
654 # empty out the largefiles list so we start fresh next time
655 # empty out the largefiles list so we start fresh next time
655 repo._lfilestoupdate = []
656 repo._lfilestoupdate = []
656 for lfile in modified:
657 for lfile in modified:
657 if lfile in lfileslist:
658 if lfile in lfileslist:
658 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
659 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
659 in repo['.']:
660 in repo['.']:
660 lfutil.writestandin(repo, lfutil.standin(lfile),
661 lfutil.writestandin(repo, lfutil.standin(lfile),
661 repo['.'][lfile].data().strip(),
662 repo['.'][lfile].data().strip(),
662 'x' in repo['.'][lfile].flags())
663 'x' in repo['.'][lfile].flags())
663 lfdirstate = lfutil.openlfdirstate(ui, repo)
664 lfdirstate = lfutil.openlfdirstate(ui, repo)
664 for lfile in added:
665 for lfile in added:
665 standin = lfutil.standin(lfile)
666 standin = lfutil.standin(lfile)
666 if standin not in ctx and (standin in matches or opts.get('all')):
667 if standin not in ctx and (standin in matches or opts.get('all')):
667 if lfile in lfdirstate:
668 if lfile in lfdirstate:
668 lfdirstate.drop(lfile)
669 lfdirstate.drop(lfile)
669 util.unlinkpath(repo.wjoin(standin))
670 util.unlinkpath(repo.wjoin(standin))
670 lfdirstate.write()
671 lfdirstate.write()
671 finally:
672 finally:
672 wlock.release()
673 wlock.release()
673
674
674 def hgupdaterepo(orig, repo, node, overwrite):
675 def hgupdaterepo(orig, repo, node, overwrite):
675 if not overwrite:
676 if not overwrite:
676 # Only call updatelfiles on the standins that have changed to save time
677 # Only call updatelfiles on the standins that have changed to save time
677 oldstandins = lfutil.getstandinsstate(repo)
678 oldstandins = lfutil.getstandinsstate(repo)
678
679
679 result = orig(repo, node, overwrite)
680 result = orig(repo, node, overwrite)
680
681
681 filelist = None
682 filelist = None
682 if not overwrite:
683 if not overwrite:
683 newstandins = lfutil.getstandinsstate(repo)
684 newstandins = lfutil.getstandinsstate(repo)
684 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
685 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
685 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist)
686 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist)
686 return result
687 return result
687
688
688 def hgmerge(orig, repo, node, force=None, remind=True):
689 def hgmerge(orig, repo, node, force=None, remind=True):
689 result = orig(repo, node, force, remind)
690 result = orig(repo, node, force, remind)
690 lfcommands.updatelfiles(repo.ui, repo)
691 lfcommands.updatelfiles(repo.ui, repo)
691 return result
692 return result
692
693
693 # When we rebase a repository with remotely changed largefiles, we need to
694 # When we rebase a repository with remotely changed largefiles, we need to
694 # take some extra care so that the largefiles are correctly updated in the
695 # take some extra care so that the largefiles are correctly updated in the
695 # working copy
696 # working copy
696 def overridepull(orig, ui, repo, source=None, **opts):
697 def overridepull(orig, ui, repo, source=None, **opts):
697 revsprepull = len(repo)
698 revsprepull = len(repo)
698 if opts.get('rebase', False):
699 if opts.get('rebase', False):
699 repo._isrebasing = True
700 repo._isrebasing = True
700 try:
701 try:
701 if opts.get('update'):
702 if opts.get('update'):
702 del opts['update']
703 del opts['update']
703 ui.debug('--update and --rebase are not compatible, ignoring '
704 ui.debug('--update and --rebase are not compatible, ignoring '
704 'the update flag\n')
705 'the update flag\n')
705 del opts['rebase']
706 del opts['rebase']
706 cmdutil.bailifchanged(repo)
707 cmdutil.bailifchanged(repo)
707 origpostincoming = commands.postincoming
708 origpostincoming = commands.postincoming
708 def _dummy(*args, **kwargs):
709 def _dummy(*args, **kwargs):
709 pass
710 pass
710 commands.postincoming = _dummy
711 commands.postincoming = _dummy
711 if not source:
712 if not source:
712 source = 'default'
713 source = 'default'
713 repo.lfpullsource = source
714 repo.lfpullsource = source
714 try:
715 try:
715 result = commands.pull(ui, repo, source, **opts)
716 result = commands.pull(ui, repo, source, **opts)
716 finally:
717 finally:
717 commands.postincoming = origpostincoming
718 commands.postincoming = origpostincoming
718 revspostpull = len(repo)
719 revspostpull = len(repo)
719 if revspostpull > revsprepull:
720 if revspostpull > revsprepull:
720 result = result or rebase.rebase(ui, repo)
721 result = result or rebase.rebase(ui, repo)
721 finally:
722 finally:
722 repo._isrebasing = False
723 repo._isrebasing = False
723 else:
724 else:
724 if not source:
725 if not source:
725 source = 'default'
726 source = 'default'
726 repo.lfpullsource = source
727 repo.lfpullsource = source
727 oldheads = lfutil.getcurrentheads(repo)
728 oldheads = lfutil.getcurrentheads(repo)
728 result = orig(ui, repo, source, **opts)
729 result = orig(ui, repo, source, **opts)
729 if opts.get('cache_largefiles'):
730 if opts.get('cache_largefiles'):
730 # If you are pulling from a remote location that is not your
731 # If you are pulling from a remote location that is not your
731 # default location, you may want to cache largefiles for new heads
732 # default location, you may want to cache largefiles for new heads
732 # that have been pulled, so you can easily merge or rebase with
733 # that have been pulled, so you can easily merge or rebase with
733 # them later
734 # them later
734 numcached = 0
735 numcached = 0
735 heads = lfutil.getcurrentheads(repo)
736 heads = lfutil.getcurrentheads(repo)
736 newheads = set(heads).difference(set(oldheads))
737 newheads = set(heads).difference(set(oldheads))
737 if len(newheads) > 0:
738 if len(newheads) > 0:
738 ui.status(_("caching largefiles for %s heads\n") %
739 ui.status(_("caching largefiles for %s heads\n") %
739 len(newheads))
740 len(newheads))
740 for head in newheads:
741 for head in newheads:
741 (cached, missing) = lfcommands.cachelfiles(ui, repo, head)
742 (cached, missing) = lfcommands.cachelfiles(ui, repo, head)
742 numcached += len(cached)
743 numcached += len(cached)
743 ui.status(_("%d largefiles cached\n") % numcached)
744 ui.status(_("%d largefiles cached\n") % numcached)
744 if opts.get('all_largefiles'):
745 if opts.get('all_largefiles'):
745 revspostpull = len(repo)
746 revspostpull = len(repo)
746 revs = []
747 revs = []
747 for rev in xrange(revsprepull, revspostpull):
748 for rev in xrange(revsprepull, revspostpull):
748 revs.append(repo[rev].rev())
749 revs.append(repo[rev].rev())
749 lfcommands.downloadlfiles(ui, repo, revs)
750 lfcommands.downloadlfiles(ui, repo, revs)
750 return result
751 return result
751
752
752 def overrideclone(orig, ui, source, dest=None, **opts):
753 def overrideclone(orig, ui, source, dest=None, **opts):
753 d = dest
754 d = dest
754 if d is None:
755 if d is None:
755 d = hg.defaultdest(source)
756 d = hg.defaultdest(source)
756 if opts.get('all_largefiles') and not hg.islocal(d):
757 if opts.get('all_largefiles') and not hg.islocal(d):
757 raise util.Abort(_(
758 raise util.Abort(_(
758 '--all-largefiles is incompatible with non-local destination %s' %
759 '--all-largefiles is incompatible with non-local destination %s' %
759 d))
760 d))
760
761
761 return orig(ui, source, dest, **opts)
762 return orig(ui, source, dest, **opts)
762
763
763 def hgclone(orig, ui, opts, *args, **kwargs):
764 def hgclone(orig, ui, opts, *args, **kwargs):
764 result = orig(ui, opts, *args, **kwargs)
765 result = orig(ui, opts, *args, **kwargs)
765
766
766 if result is not None:
767 if result is not None:
767 sourcerepo, destrepo = result
768 sourcerepo, destrepo = result
768 repo = destrepo.local()
769 repo = destrepo.local()
769
770
770 # Caching is implicitly limited to 'rev' option, since the dest repo was
771 # Caching is implicitly limited to 'rev' option, since the dest repo was
771 # truncated at that point. The user may expect a download count with
772 # truncated at that point. The user may expect a download count with
772 # this option, so attempt whether or not this is a largefile repo.
773 # this option, so attempt whether or not this is a largefile repo.
773 if opts.get('all_largefiles'):
774 if opts.get('all_largefiles'):
774 success, missing = lfcommands.downloadlfiles(ui, repo, None)
775 success, missing = lfcommands.downloadlfiles(ui, repo, None)
775
776
776 if missing != 0:
777 if missing != 0:
777 return None
778 return None
778
779
779 return result
780 return result
780
781
781 def overriderebase(orig, ui, repo, **opts):
782 def overriderebase(orig, ui, repo, **opts):
782 repo._isrebasing = True
783 repo._isrebasing = True
783 try:
784 try:
784 return orig(ui, repo, **opts)
785 return orig(ui, repo, **opts)
785 finally:
786 finally:
786 repo._isrebasing = False
787 repo._isrebasing = False
787
788
788 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
789 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
789 prefix=None, mtime=None, subrepos=None):
790 prefix=None, mtime=None, subrepos=None):
790 # No need to lock because we are only reading history and
791 # No need to lock because we are only reading history and
791 # largefile caches, neither of which are modified.
792 # largefile caches, neither of which are modified.
792 lfcommands.cachelfiles(repo.ui, repo, node)
793 lfcommands.cachelfiles(repo.ui, repo, node)
793
794
794 if kind not in archival.archivers:
795 if kind not in archival.archivers:
795 raise util.Abort(_("unknown archive type '%s'") % kind)
796 raise util.Abort(_("unknown archive type '%s'") % kind)
796
797
797 ctx = repo[node]
798 ctx = repo[node]
798
799
799 if kind == 'files':
800 if kind == 'files':
800 if prefix:
801 if prefix:
801 raise util.Abort(
802 raise util.Abort(
802 _('cannot give prefix when archiving to files'))
803 _('cannot give prefix when archiving to files'))
803 else:
804 else:
804 prefix = archival.tidyprefix(dest, kind, prefix)
805 prefix = archival.tidyprefix(dest, kind, prefix)
805
806
806 def write(name, mode, islink, getdata):
807 def write(name, mode, islink, getdata):
807 if matchfn and not matchfn(name):
808 if matchfn and not matchfn(name):
808 return
809 return
809 data = getdata()
810 data = getdata()
810 if decode:
811 if decode:
811 data = repo.wwritedata(name, data)
812 data = repo.wwritedata(name, data)
812 archiver.addfile(prefix + name, mode, islink, data)
813 archiver.addfile(prefix + name, mode, islink, data)
813
814
814 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
815 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
815
816
816 if repo.ui.configbool("ui", "archivemeta", True):
817 if repo.ui.configbool("ui", "archivemeta", True):
817 def metadata():
818 def metadata():
818 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
819 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
819 hex(repo.changelog.node(0)), hex(node), ctx.branch())
820 hex(repo.changelog.node(0)), hex(node), ctx.branch())
820
821
821 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
822 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
822 if repo.tagtype(t) == 'global')
823 if repo.tagtype(t) == 'global')
823 if not tags:
824 if not tags:
824 repo.ui.pushbuffer()
825 repo.ui.pushbuffer()
825 opts = {'template': '{latesttag}\n{latesttagdistance}',
826 opts = {'template': '{latesttag}\n{latesttagdistance}',
826 'style': '', 'patch': None, 'git': None}
827 'style': '', 'patch': None, 'git': None}
827 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
828 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
828 ltags, dist = repo.ui.popbuffer().split('\n')
829 ltags, dist = repo.ui.popbuffer().split('\n')
829 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
830 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
830 tags += 'latesttagdistance: %s\n' % dist
831 tags += 'latesttagdistance: %s\n' % dist
831
832
832 return base + tags
833 return base + tags
833
834
834 write('.hg_archival.txt', 0644, False, metadata)
835 write('.hg_archival.txt', 0644, False, metadata)
835
836
836 for f in ctx:
837 for f in ctx:
837 ff = ctx.flags(f)
838 ff = ctx.flags(f)
838 getdata = ctx[f].data
839 getdata = ctx[f].data
839 if lfutil.isstandin(f):
840 if lfutil.isstandin(f):
840 path = lfutil.findfile(repo, getdata().strip())
841 path = lfutil.findfile(repo, getdata().strip())
841 if path is None:
842 if path is None:
842 raise util.Abort(
843 raise util.Abort(
843 _('largefile %s not found in repo store or system cache')
844 _('largefile %s not found in repo store or system cache')
844 % lfutil.splitstandin(f))
845 % lfutil.splitstandin(f))
845 f = lfutil.splitstandin(f)
846 f = lfutil.splitstandin(f)
846
847
847 def getdatafn():
848 def getdatafn():
848 fd = None
849 fd = None
849 try:
850 try:
850 fd = open(path, 'rb')
851 fd = open(path, 'rb')
851 return fd.read()
852 return fd.read()
852 finally:
853 finally:
853 if fd:
854 if fd:
854 fd.close()
855 fd.close()
855
856
856 getdata = getdatafn
857 getdata = getdatafn
857 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
858 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
858
859
859 if subrepos:
860 if subrepos:
860 for subpath in sorted(ctx.substate):
861 for subpath in sorted(ctx.substate):
861 sub = ctx.sub(subpath)
862 sub = ctx.sub(subpath)
862 submatch = match_.narrowmatcher(subpath, matchfn)
863 submatch = match_.narrowmatcher(subpath, matchfn)
863 sub.archive(repo.ui, archiver, prefix, submatch)
864 sub.archive(repo.ui, archiver, prefix, submatch)
864
865
865 archiver.done()
866 archiver.done()
866
867
867 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
868 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
868 repo._get(repo._state + ('hg',))
869 repo._get(repo._state + ('hg',))
869 rev = repo._state[1]
870 rev = repo._state[1]
870 ctx = repo._repo[rev]
871 ctx = repo._repo[rev]
871
872
872 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
873 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
873
874
874 def write(name, mode, islink, getdata):
875 def write(name, mode, islink, getdata):
875 # At this point, the standin has been replaced with the largefile name,
876 # At this point, the standin has been replaced with the largefile name,
876 # so the normal matcher works here without the lfutil variants.
877 # so the normal matcher works here without the lfutil variants.
877 if match and not match(f):
878 if match and not match(f):
878 return
879 return
879 data = getdata()
880 data = getdata()
880
881
881 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
882 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
882
883
883 for f in ctx:
884 for f in ctx:
884 ff = ctx.flags(f)
885 ff = ctx.flags(f)
885 getdata = ctx[f].data
886 getdata = ctx[f].data
886 if lfutil.isstandin(f):
887 if lfutil.isstandin(f):
887 path = lfutil.findfile(repo._repo, getdata().strip())
888 path = lfutil.findfile(repo._repo, getdata().strip())
888 if path is None:
889 if path is None:
889 raise util.Abort(
890 raise util.Abort(
890 _('largefile %s not found in repo store or system cache')
891 _('largefile %s not found in repo store or system cache')
891 % lfutil.splitstandin(f))
892 % lfutil.splitstandin(f))
892 f = lfutil.splitstandin(f)
893 f = lfutil.splitstandin(f)
893
894
894 def getdatafn():
895 def getdatafn():
895 fd = None
896 fd = None
896 try:
897 try:
897 fd = open(os.path.join(prefix, path), 'rb')
898 fd = open(os.path.join(prefix, path), 'rb')
898 return fd.read()
899 return fd.read()
899 finally:
900 finally:
900 if fd:
901 if fd:
901 fd.close()
902 fd.close()
902
903
903 getdata = getdatafn
904 getdata = getdatafn
904
905
905 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
906 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
906
907
907 for subpath in sorted(ctx.substate):
908 for subpath in sorted(ctx.substate):
908 sub = ctx.sub(subpath)
909 sub = ctx.sub(subpath)
909 submatch = match_.narrowmatcher(subpath, match)
910 submatch = match_.narrowmatcher(subpath, match)
910 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
911 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
911 submatch)
912 submatch)
912
913
913 # If a largefile is modified, the change is not reflected in its
914 # If a largefile is modified, the change is not reflected in its
914 # standin until a commit. cmdutil.bailifchanged() raises an exception
915 # standin until a commit. cmdutil.bailifchanged() raises an exception
915 # if the repo has uncommitted changes. Wrap it to also check if
916 # if the repo has uncommitted changes. Wrap it to also check if
916 # largefiles were changed. This is used by bisect and backout.
917 # largefiles were changed. This is used by bisect and backout.
917 def overridebailifchanged(orig, repo):
918 def overridebailifchanged(orig, repo):
918 orig(repo)
919 orig(repo)
919 repo.lfstatus = True
920 repo.lfstatus = True
920 modified, added, removed, deleted = repo.status()[:4]
921 modified, added, removed, deleted = repo.status()[:4]
921 repo.lfstatus = False
922 repo.lfstatus = False
922 if modified or added or removed or deleted:
923 if modified or added or removed or deleted:
923 raise util.Abort(_('outstanding uncommitted changes'))
924 raise util.Abort(_('outstanding uncommitted changes'))
924
925
925 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
926 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
926 def overridefetch(orig, ui, repo, *pats, **opts):
927 def overridefetch(orig, ui, repo, *pats, **opts):
927 repo.lfstatus = True
928 repo.lfstatus = True
928 modified, added, removed, deleted = repo.status()[:4]
929 modified, added, removed, deleted = repo.status()[:4]
929 repo.lfstatus = False
930 repo.lfstatus = False
930 if modified or added or removed or deleted:
931 if modified or added or removed or deleted:
931 raise util.Abort(_('outstanding uncommitted changes'))
932 raise util.Abort(_('outstanding uncommitted changes'))
932 return orig(ui, repo, *pats, **opts)
933 return orig(ui, repo, *pats, **opts)
933
934
934 def overrideforget(orig, ui, repo, *pats, **opts):
935 def overrideforget(orig, ui, repo, *pats, **opts):
935 installnormalfilesmatchfn(repo[None].manifest())
936 installnormalfilesmatchfn(repo[None].manifest())
936 result = orig(ui, repo, *pats, **opts)
937 result = orig(ui, repo, *pats, **opts)
937 restorematchfn()
938 restorematchfn()
938 m = scmutil.match(repo[None], pats, opts)
939 m = scmutil.match(repo[None], pats, opts)
939
940
940 try:
941 try:
941 repo.lfstatus = True
942 repo.lfstatus = True
942 s = repo.status(match=m, clean=True)
943 s = repo.status(match=m, clean=True)
943 finally:
944 finally:
944 repo.lfstatus = False
945 repo.lfstatus = False
945 forget = sorted(s[0] + s[1] + s[3] + s[6])
946 forget = sorted(s[0] + s[1] + s[3] + s[6])
946 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
947 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
947
948
948 for f in forget:
949 for f in forget:
949 if lfutil.standin(f) not in repo.dirstate and not \
950 if lfutil.standin(f) not in repo.dirstate and not \
950 os.path.isdir(m.rel(lfutil.standin(f))):
951 os.path.isdir(m.rel(lfutil.standin(f))):
951 ui.warn(_('not removing %s: file is already untracked\n')
952 ui.warn(_('not removing %s: file is already untracked\n')
952 % m.rel(f))
953 % m.rel(f))
953 result = 1
954 result = 1
954
955
955 for f in forget:
956 for f in forget:
956 if ui.verbose or not m.exact(f):
957 if ui.verbose or not m.exact(f):
957 ui.status(_('removing %s\n') % m.rel(f))
958 ui.status(_('removing %s\n') % m.rel(f))
958
959
959 # Need to lock because standin files are deleted then removed from the
960 # Need to lock because standin files are deleted then removed from the
960 # repository and we could race in-between.
961 # repository and we could race in-between.
961 wlock = repo.wlock()
962 wlock = repo.wlock()
962 try:
963 try:
963 lfdirstate = lfutil.openlfdirstate(ui, repo)
964 lfdirstate = lfutil.openlfdirstate(ui, repo)
964 for f in forget:
965 for f in forget:
965 if lfdirstate[f] == 'a':
966 if lfdirstate[f] == 'a':
966 lfdirstate.drop(f)
967 lfdirstate.drop(f)
967 else:
968 else:
968 lfdirstate.remove(f)
969 lfdirstate.remove(f)
969 lfdirstate.write()
970 lfdirstate.write()
970 standins = [lfutil.standin(f) for f in forget]
971 standins = [lfutil.standin(f) for f in forget]
971 for f in standins:
972 for f in standins:
972 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
973 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
973 repo[None].forget(standins)
974 repo[None].forget(standins)
974 finally:
975 finally:
975 wlock.release()
976 wlock.release()
976
977
977 return result
978 return result
978
979
979 def getoutgoinglfiles(ui, repo, dest=None, **opts):
980 def getoutgoinglfiles(ui, repo, dest=None, **opts):
980 dest = ui.expandpath(dest or 'default-push', dest or 'default')
981 dest = ui.expandpath(dest or 'default-push', dest or 'default')
981 dest, branches = hg.parseurl(dest, opts.get('branch'))
982 dest, branches = hg.parseurl(dest, opts.get('branch'))
982 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
983 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
983 if revs:
984 if revs:
984 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
985 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
985
986
986 try:
987 try:
987 remote = hg.peer(repo, opts, dest)
988 remote = hg.peer(repo, opts, dest)
988 except error.RepoError:
989 except error.RepoError:
989 return None
990 return None
990 outgoing = discovery.findcommonoutgoing(repo, remote.peer(), force=False)
991 outgoing = discovery.findcommonoutgoing(repo, remote.peer(), force=False)
991 if not outgoing.missing:
992 if not outgoing.missing:
992 return outgoing.missing
993 return outgoing.missing
993 o = repo.changelog.nodesbetween(outgoing.missing, revs)[0]
994 o = repo.changelog.nodesbetween(outgoing.missing, revs)[0]
994 if opts.get('newest_first'):
995 if opts.get('newest_first'):
995 o.reverse()
996 o.reverse()
996
997
997 toupload = set()
998 toupload = set()
998 for n in o:
999 for n in o:
999 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
1000 parents = [p for p in repo.changelog.parents(n) if p != node.nullid]
1000 ctx = repo[n]
1001 ctx = repo[n]
1001 files = set(ctx.files())
1002 files = set(ctx.files())
1002 if len(parents) == 2:
1003 if len(parents) == 2:
1003 mc = ctx.manifest()
1004 mc = ctx.manifest()
1004 mp1 = ctx.parents()[0].manifest()
1005 mp1 = ctx.parents()[0].manifest()
1005 mp2 = ctx.parents()[1].manifest()
1006 mp2 = ctx.parents()[1].manifest()
1006 for f in mp1:
1007 for f in mp1:
1007 if f not in mc:
1008 if f not in mc:
1008 files.add(f)
1009 files.add(f)
1009 for f in mp2:
1010 for f in mp2:
1010 if f not in mc:
1011 if f not in mc:
1011 files.add(f)
1012 files.add(f)
1012 for f in mc:
1013 for f in mc:
1013 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
1014 if mc[f] != mp1.get(f, None) or mc[f] != mp2.get(f, None):
1014 files.add(f)
1015 files.add(f)
1015 toupload = toupload.union(
1016 toupload = toupload.union(
1016 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
1017 set([f for f in files if lfutil.isstandin(f) and f in ctx]))
1017 return sorted(toupload)
1018 return sorted(toupload)
1018
1019
1019 def overrideoutgoing(orig, ui, repo, dest=None, **opts):
1020 def overrideoutgoing(orig, ui, repo, dest=None, **opts):
1020 result = orig(ui, repo, dest, **opts)
1021 result = orig(ui, repo, dest, **opts)
1021
1022
1022 if opts.pop('large', None):
1023 if opts.pop('large', None):
1023 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
1024 toupload = getoutgoinglfiles(ui, repo, dest, **opts)
1024 if toupload is None:
1025 if toupload is None:
1025 ui.status(_('largefiles: No remote repo\n'))
1026 ui.status(_('largefiles: No remote repo\n'))
1026 elif not toupload:
1027 elif not toupload:
1027 ui.status(_('largefiles: no files to upload\n'))
1028 ui.status(_('largefiles: no files to upload\n'))
1028 else:
1029 else:
1029 ui.status(_('largefiles to upload:\n'))
1030 ui.status(_('largefiles to upload:\n'))
1030 for file in toupload:
1031 for file in toupload:
1031 ui.status(lfutil.splitstandin(file) + '\n')
1032 ui.status(lfutil.splitstandin(file) + '\n')
1032 ui.status('\n')
1033 ui.status('\n')
1033
1034
1034 return result
1035 return result
1035
1036
1036 def overridesummary(orig, ui, repo, *pats, **opts):
1037 def overridesummary(orig, ui, repo, *pats, **opts):
1037 try:
1038 try:
1038 repo.lfstatus = True
1039 repo.lfstatus = True
1039 orig(ui, repo, *pats, **opts)
1040 orig(ui, repo, *pats, **opts)
1040 finally:
1041 finally:
1041 repo.lfstatus = False
1042 repo.lfstatus = False
1042
1043
1043 if opts.pop('large', None):
1044 if opts.pop('large', None):
1044 toupload = getoutgoinglfiles(ui, repo, None, **opts)
1045 toupload = getoutgoinglfiles(ui, repo, None, **opts)
1045 if toupload is None:
1046 if toupload is None:
1046 # i18n: column positioning for "hg summary"
1047 # i18n: column positioning for "hg summary"
1047 ui.status(_('largefiles: (no remote repo)\n'))
1048 ui.status(_('largefiles: (no remote repo)\n'))
1048 elif not toupload:
1049 elif not toupload:
1049 # i18n: column positioning for "hg summary"
1050 # i18n: column positioning for "hg summary"
1050 ui.status(_('largefiles: (no files to upload)\n'))
1051 ui.status(_('largefiles: (no files to upload)\n'))
1051 else:
1052 else:
1052 # i18n: column positioning for "hg summary"
1053 # i18n: column positioning for "hg summary"
1053 ui.status(_('largefiles: %d to upload\n') % len(toupload))
1054 ui.status(_('largefiles: %d to upload\n') % len(toupload))
1054
1055
1055 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1056 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1056 similarity=None):
1057 similarity=None):
1057 if not lfutil.islfilesrepo(repo):
1058 if not lfutil.islfilesrepo(repo):
1058 return orig(repo, pats, opts, dry_run, similarity)
1059 return orig(repo, pats, opts, dry_run, similarity)
1059 # Get the list of missing largefiles so we can remove them
1060 # Get the list of missing largefiles so we can remove them
1060 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1061 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1061 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
1062 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
1062 False, False)
1063 False, False)
1063 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
1064 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
1064
1065
1065 # Call into the normal remove code, but the removing of the standin, we want
1066 # Call into the normal remove code, but the removing of the standin, we want
1066 # to have handled by original addremove. Monkey patching here makes sure
1067 # to have handled by original addremove. Monkey patching here makes sure
1067 # we don't remove the standin in the largefiles code, preventing a very
1068 # we don't remove the standin in the largefiles code, preventing a very
1068 # confused state later.
1069 # confused state later.
1069 if missing:
1070 if missing:
1070 m = [repo.wjoin(f) for f in missing]
1071 m = [repo.wjoin(f) for f in missing]
1071 repo._isaddremove = True
1072 repo._isaddremove = True
1072 removelargefiles(repo.ui, repo, *m, **opts)
1073 removelargefiles(repo.ui, repo, *m, **opts)
1073 repo._isaddremove = False
1074 repo._isaddremove = False
1074 # Call into the normal add code, and any files that *should* be added as
1075 # Call into the normal add code, and any files that *should* be added as
1075 # largefiles will be
1076 # largefiles will be
1076 addlargefiles(repo.ui, repo, *pats, **opts)
1077 addlargefiles(repo.ui, repo, *pats, **opts)
1077 # Now that we've handled largefiles, hand off to the original addremove
1078 # Now that we've handled largefiles, hand off to the original addremove
1078 # function to take care of the rest. Make sure it doesn't do anything with
1079 # function to take care of the rest. Make sure it doesn't do anything with
1079 # largefiles by installing a matcher that will ignore them.
1080 # largefiles by installing a matcher that will ignore them.
1080 installnormalfilesmatchfn(repo[None].manifest())
1081 installnormalfilesmatchfn(repo[None].manifest())
1081 result = orig(repo, pats, opts, dry_run, similarity)
1082 result = orig(repo, pats, opts, dry_run, similarity)
1082 restorematchfn()
1083 restorematchfn()
1083 return result
1084 return result
1084
1085
1085 # Calling purge with --all will cause the largefiles to be deleted.
1086 # Calling purge with --all will cause the largefiles to be deleted.
1086 # Override repo.status to prevent this from happening.
1087 # Override repo.status to prevent this from happening.
1087 def overridepurge(orig, ui, repo, *dirs, **opts):
1088 def overridepurge(orig, ui, repo, *dirs, **opts):
1088 # XXX large file status is buggy when used on repo proxy.
1089 # XXX large file status is buggy when used on repo proxy.
1089 # XXX this needs to be investigate.
1090 # XXX this needs to be investigate.
1090 repo = repo.unfiltered()
1091 repo = repo.unfiltered()
1091 oldstatus = repo.status
1092 oldstatus = repo.status
1092 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1093 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1093 clean=False, unknown=False, listsubrepos=False):
1094 clean=False, unknown=False, listsubrepos=False):
1094 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1095 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1095 listsubrepos)
1096 listsubrepos)
1096 lfdirstate = lfutil.openlfdirstate(ui, repo)
1097 lfdirstate = lfutil.openlfdirstate(ui, repo)
1097 modified, added, removed, deleted, unknown, ignored, clean = r
1098 modified, added, removed, deleted, unknown, ignored, clean = r
1098 unknown = [f for f in unknown if lfdirstate[f] == '?']
1099 unknown = [f for f in unknown if lfdirstate[f] == '?']
1099 ignored = [f for f in ignored if lfdirstate[f] == '?']
1100 ignored = [f for f in ignored if lfdirstate[f] == '?']
1100 return modified, added, removed, deleted, unknown, ignored, clean
1101 return modified, added, removed, deleted, unknown, ignored, clean
1101 repo.status = overridestatus
1102 repo.status = overridestatus
1102 orig(ui, repo, *dirs, **opts)
1103 orig(ui, repo, *dirs, **opts)
1103 repo.status = oldstatus
1104 repo.status = oldstatus
1104
1105
1105 def overriderollback(orig, ui, repo, **opts):
1106 def overriderollback(orig, ui, repo, **opts):
1106 result = orig(ui, repo, **opts)
1107 result = orig(ui, repo, **opts)
1107 merge.update(repo, node=None, branchmerge=False, force=True,
1108 merge.update(repo, node=None, branchmerge=False, force=True,
1108 partial=lfutil.isstandin)
1109 partial=lfutil.isstandin)
1109 wlock = repo.wlock()
1110 wlock = repo.wlock()
1110 try:
1111 try:
1111 lfdirstate = lfutil.openlfdirstate(ui, repo)
1112 lfdirstate = lfutil.openlfdirstate(ui, repo)
1112 lfiles = lfutil.listlfiles(repo)
1113 lfiles = lfutil.listlfiles(repo)
1113 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
1114 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
1114 for file in lfiles:
1115 for file in lfiles:
1115 if file in oldlfiles:
1116 if file in oldlfiles:
1116 lfdirstate.normallookup(file)
1117 lfdirstate.normallookup(file)
1117 else:
1118 else:
1118 lfdirstate.add(file)
1119 lfdirstate.add(file)
1119 lfdirstate.write()
1120 lfdirstate.write()
1120 finally:
1121 finally:
1121 wlock.release()
1122 wlock.release()
1122 return result
1123 return result
1123
1124
1124 def overridetransplant(orig, ui, repo, *revs, **opts):
1125 def overridetransplant(orig, ui, repo, *revs, **opts):
1125 try:
1126 try:
1126 oldstandins = lfutil.getstandinsstate(repo)
1127 oldstandins = lfutil.getstandinsstate(repo)
1127 repo._istransplanting = True
1128 repo._istransplanting = True
1128 result = orig(ui, repo, *revs, **opts)
1129 result = orig(ui, repo, *revs, **opts)
1129 newstandins = lfutil.getstandinsstate(repo)
1130 newstandins = lfutil.getstandinsstate(repo)
1130 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1131 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1131 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1132 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1132 printmessage=True)
1133 printmessage=True)
1133 finally:
1134 finally:
1134 repo._istransplanting = False
1135 repo._istransplanting = False
1135 return result
1136 return result
1136
1137
1137 def overridecat(orig, ui, repo, file1, *pats, **opts):
1138 def overridecat(orig, ui, repo, file1, *pats, **opts):
1138 ctx = scmutil.revsingle(repo, opts.get('rev'))
1139 ctx = scmutil.revsingle(repo, opts.get('rev'))
1139 err = 1
1140 err = 1
1140 notbad = set()
1141 notbad = set()
1141 m = scmutil.match(ctx, (file1,) + pats, opts)
1142 m = scmutil.match(ctx, (file1,) + pats, opts)
1142 origmatchfn = m.matchfn
1143 origmatchfn = m.matchfn
1143 def lfmatchfn(f):
1144 def lfmatchfn(f):
1144 lf = lfutil.splitstandin(f)
1145 lf = lfutil.splitstandin(f)
1145 if lf is None:
1146 if lf is None:
1146 return origmatchfn(f)
1147 return origmatchfn(f)
1147 notbad.add(lf)
1148 notbad.add(lf)
1148 return origmatchfn(lf)
1149 return origmatchfn(lf)
1149 m.matchfn = lfmatchfn
1150 m.matchfn = lfmatchfn
1150 m.bad = lambda f, msg: f not in notbad
1151 m.bad = lambda f, msg: f not in notbad
1151 for f in ctx.walk(m):
1152 for f in ctx.walk(m):
1152 lf = lfutil.splitstandin(f)
1153 lf = lfutil.splitstandin(f)
1153 if lf is None:
1154 if lf is None:
1154 err = orig(ui, repo, f, **opts)
1155 err = orig(ui, repo, f, **opts)
1155 else:
1156 else:
1156 err = lfcommands.catlfile(repo, lf, ctx.rev(), opts.get('output'))
1157 err = lfcommands.catlfile(repo, lf, ctx.rev(), opts.get('output'))
1157 return err
1158 return err
1158
1159
1159 def mercurialsinkbefore(orig, sink):
1160 def mercurialsinkbefore(orig, sink):
1160 sink.repo._isconverting = True
1161 sink.repo._isconverting = True
1161 orig(sink)
1162 orig(sink)
1162
1163
1163 def mercurialsinkafter(orig, sink):
1164 def mercurialsinkafter(orig, sink):
1164 sink.repo._isconverting = False
1165 sink.repo._isconverting = False
1165 orig(sink)
1166 orig(sink)
@@ -1,5741 +1,5741 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for 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, bin, nullid, nullrev, short
8 from node import hex, bin, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _
10 from i18n import _
11 import os, re, difflib, time, tempfile, errno
11 import os, re, difflib, time, tempfile, errno
12 import hg, scmutil, util, revlog, copies, error, bookmarks
12 import hg, scmutil, util, revlog, copies, error, bookmarks
13 import patch, help, encoding, templatekw, discovery
13 import patch, help, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, hbisect
14 import archival, changegroup, cmdutil, hbisect
15 import sshserver, hgweb, hgweb.server, commandserver
15 import sshserver, hgweb, hgweb.server, commandserver
16 import merge as mergemod
16 import merge as mergemod
17 import minirst, revset, fileset
17 import minirst, revset, fileset
18 import dagparser, context, simplemerge, graphmod
18 import dagparser, context, simplemerge, graphmod
19 import random, setdiscovery, treediscovery, dagutil, pvec, localrepo
19 import random, setdiscovery, treediscovery, dagutil, pvec, localrepo
20 import phases, obsolete
20 import phases, obsolete
21
21
22 table = {}
22 table = {}
23
23
24 command = cmdutil.command(table)
24 command = cmdutil.command(table)
25
25
26 # common command options
26 # common command options
27
27
28 globalopts = [
28 globalopts = [
29 ('R', 'repository', '',
29 ('R', 'repository', '',
30 _('repository root directory or name of overlay bundle file'),
30 _('repository root directory or name of overlay bundle file'),
31 _('REPO')),
31 _('REPO')),
32 ('', 'cwd', '',
32 ('', 'cwd', '',
33 _('change working directory'), _('DIR')),
33 _('change working directory'), _('DIR')),
34 ('y', 'noninteractive', None,
34 ('y', 'noninteractive', None,
35 _('do not prompt, automatically pick the first choice for all prompts')),
35 _('do not prompt, automatically pick the first choice for all prompts')),
36 ('q', 'quiet', None, _('suppress output')),
36 ('q', 'quiet', None, _('suppress output')),
37 ('v', 'verbose', None, _('enable additional output')),
37 ('v', 'verbose', None, _('enable additional output')),
38 ('', 'config', [],
38 ('', 'config', [],
39 _('set/override config option (use \'section.name=value\')'),
39 _('set/override config option (use \'section.name=value\')'),
40 _('CONFIG')),
40 _('CONFIG')),
41 ('', 'debug', None, _('enable debugging output')),
41 ('', 'debug', None, _('enable debugging output')),
42 ('', 'debugger', None, _('start debugger')),
42 ('', 'debugger', None, _('start debugger')),
43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
44 _('ENCODE')),
44 _('ENCODE')),
45 ('', 'encodingmode', encoding.encodingmode,
45 ('', 'encodingmode', encoding.encodingmode,
46 _('set the charset encoding mode'), _('MODE')),
46 _('set the charset encoding mode'), _('MODE')),
47 ('', 'traceback', None, _('always print a traceback on exception')),
47 ('', 'traceback', None, _('always print a traceback on exception')),
48 ('', 'time', None, _('time how long the command takes')),
48 ('', 'time', None, _('time how long the command takes')),
49 ('', 'profile', None, _('print command execution profile')),
49 ('', 'profile', None, _('print command execution profile')),
50 ('', 'version', None, _('output version information and exit')),
50 ('', 'version', None, _('output version information and exit')),
51 ('h', 'help', None, _('display help and exit')),
51 ('h', 'help', None, _('display help and exit')),
52 ('', 'hidden', False, _('consider hidden changesets')),
52 ('', 'hidden', False, _('consider hidden changesets')),
53 ]
53 ]
54
54
55 dryrunopts = [('n', 'dry-run', None,
55 dryrunopts = [('n', 'dry-run', None,
56 _('do not perform actions, just print output'))]
56 _('do not perform actions, just print output'))]
57
57
58 remoteopts = [
58 remoteopts = [
59 ('e', 'ssh', '',
59 ('e', 'ssh', '',
60 _('specify ssh command to use'), _('CMD')),
60 _('specify ssh command to use'), _('CMD')),
61 ('', 'remotecmd', '',
61 ('', 'remotecmd', '',
62 _('specify hg command to run on the remote side'), _('CMD')),
62 _('specify hg command to run on the remote side'), _('CMD')),
63 ('', 'insecure', None,
63 ('', 'insecure', None,
64 _('do not verify server certificate (ignoring web.cacerts config)')),
64 _('do not verify server certificate (ignoring web.cacerts config)')),
65 ]
65 ]
66
66
67 walkopts = [
67 walkopts = [
68 ('I', 'include', [],
68 ('I', 'include', [],
69 _('include names matching the given patterns'), _('PATTERN')),
69 _('include names matching the given patterns'), _('PATTERN')),
70 ('X', 'exclude', [],
70 ('X', 'exclude', [],
71 _('exclude names matching the given patterns'), _('PATTERN')),
71 _('exclude names matching the given patterns'), _('PATTERN')),
72 ]
72 ]
73
73
74 commitopts = [
74 commitopts = [
75 ('m', 'message', '',
75 ('m', 'message', '',
76 _('use text as commit message'), _('TEXT')),
76 _('use text as commit message'), _('TEXT')),
77 ('l', 'logfile', '',
77 ('l', 'logfile', '',
78 _('read commit message from file'), _('FILE')),
78 _('read commit message from file'), _('FILE')),
79 ]
79 ]
80
80
81 commitopts2 = [
81 commitopts2 = [
82 ('d', 'date', '',
82 ('d', 'date', '',
83 _('record the specified date as commit date'), _('DATE')),
83 _('record the specified date as commit date'), _('DATE')),
84 ('u', 'user', '',
84 ('u', 'user', '',
85 _('record the specified user as committer'), _('USER')),
85 _('record the specified user as committer'), _('USER')),
86 ]
86 ]
87
87
88 templateopts = [
88 templateopts = [
89 ('', 'style', '',
89 ('', 'style', '',
90 _('display using template map file'), _('STYLE')),
90 _('display using template map file'), _('STYLE')),
91 ('', 'template', '',
91 ('', 'template', '',
92 _('display with template'), _('TEMPLATE')),
92 _('display with template'), _('TEMPLATE')),
93 ]
93 ]
94
94
95 logopts = [
95 logopts = [
96 ('p', 'patch', None, _('show patch')),
96 ('p', 'patch', None, _('show patch')),
97 ('g', 'git', None, _('use git extended diff format')),
97 ('g', 'git', None, _('use git extended diff format')),
98 ('l', 'limit', '',
98 ('l', 'limit', '',
99 _('limit number of changes displayed'), _('NUM')),
99 _('limit number of changes displayed'), _('NUM')),
100 ('M', 'no-merges', None, _('do not show merges')),
100 ('M', 'no-merges', None, _('do not show merges')),
101 ('', 'stat', None, _('output diffstat-style summary of changes')),
101 ('', 'stat', None, _('output diffstat-style summary of changes')),
102 ('G', 'graph', None, _("show the revision DAG")),
102 ('G', 'graph', None, _("show the revision DAG")),
103 ] + templateopts
103 ] + templateopts
104
104
105 diffopts = [
105 diffopts = [
106 ('a', 'text', None, _('treat all files as text')),
106 ('a', 'text', None, _('treat all files as text')),
107 ('g', 'git', None, _('use git extended diff format')),
107 ('g', 'git', None, _('use git extended diff format')),
108 ('', 'nodates', None, _('omit dates from diff headers'))
108 ('', 'nodates', None, _('omit dates from diff headers'))
109 ]
109 ]
110
110
111 diffwsopts = [
111 diffwsopts = [
112 ('w', 'ignore-all-space', None,
112 ('w', 'ignore-all-space', None,
113 _('ignore white space when comparing lines')),
113 _('ignore white space when comparing lines')),
114 ('b', 'ignore-space-change', None,
114 ('b', 'ignore-space-change', None,
115 _('ignore changes in the amount of white space')),
115 _('ignore changes in the amount of white space')),
116 ('B', 'ignore-blank-lines', None,
116 ('B', 'ignore-blank-lines', None,
117 _('ignore changes whose lines are all blank')),
117 _('ignore changes whose lines are all blank')),
118 ]
118 ]
119
119
120 diffopts2 = [
120 diffopts2 = [
121 ('p', 'show-function', None, _('show which function each change is in')),
121 ('p', 'show-function', None, _('show which function each change is in')),
122 ('', 'reverse', None, _('produce a diff that undoes the changes')),
122 ('', 'reverse', None, _('produce a diff that undoes the changes')),
123 ] + diffwsopts + [
123 ] + diffwsopts + [
124 ('U', 'unified', '',
124 ('U', 'unified', '',
125 _('number of lines of context to show'), _('NUM')),
125 _('number of lines of context to show'), _('NUM')),
126 ('', 'stat', None, _('output diffstat-style summary of changes')),
126 ('', 'stat', None, _('output diffstat-style summary of changes')),
127 ]
127 ]
128
128
129 mergetoolopts = [
129 mergetoolopts = [
130 ('t', 'tool', '', _('specify merge tool')),
130 ('t', 'tool', '', _('specify merge tool')),
131 ]
131 ]
132
132
133 similarityopts = [
133 similarityopts = [
134 ('s', 'similarity', '',
134 ('s', 'similarity', '',
135 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
135 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
136 ]
136 ]
137
137
138 subrepoopts = [
138 subrepoopts = [
139 ('S', 'subrepos', None,
139 ('S', 'subrepos', None,
140 _('recurse into subrepositories'))
140 _('recurse into subrepositories'))
141 ]
141 ]
142
142
143 # Commands start here, listed alphabetically
143 # Commands start here, listed alphabetically
144
144
145 @command('^add',
145 @command('^add',
146 walkopts + subrepoopts + dryrunopts,
146 walkopts + subrepoopts + dryrunopts,
147 _('[OPTION]... [FILE]...'))
147 _('[OPTION]... [FILE]...'))
148 def add(ui, repo, *pats, **opts):
148 def add(ui, repo, *pats, **opts):
149 """add the specified files on the next commit
149 """add the specified files on the next commit
150
150
151 Schedule files to be version controlled and added to the
151 Schedule files to be version controlled and added to the
152 repository.
152 repository.
153
153
154 The files will be added to the repository at the next commit. To
154 The files will be added to the repository at the next commit. To
155 undo an add before that, see :hg:`forget`.
155 undo an add before that, see :hg:`forget`.
156
156
157 If no names are given, add all files to the repository.
157 If no names are given, add all files to the repository.
158
158
159 .. container:: verbose
159 .. container:: verbose
160
160
161 An example showing how new (unknown) files are added
161 An example showing how new (unknown) files are added
162 automatically by :hg:`add`::
162 automatically by :hg:`add`::
163
163
164 $ ls
164 $ ls
165 foo.c
165 foo.c
166 $ hg status
166 $ hg status
167 ? foo.c
167 ? foo.c
168 $ hg add
168 $ hg add
169 adding foo.c
169 adding foo.c
170 $ hg status
170 $ hg status
171 A foo.c
171 A foo.c
172
172
173 Returns 0 if all files are successfully added.
173 Returns 0 if all files are successfully added.
174 """
174 """
175
175
176 m = scmutil.match(repo[None], pats, opts)
176 m = scmutil.match(repo[None], pats, opts)
177 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
177 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
178 opts.get('subrepos'), prefix="", explicitonly=False)
178 opts.get('subrepos'), prefix="", explicitonly=False)
179 return rejected and 1 or 0
179 return rejected and 1 or 0
180
180
181 @command('addremove',
181 @command('addremove',
182 similarityopts + walkopts + dryrunopts,
182 similarityopts + walkopts + dryrunopts,
183 _('[OPTION]... [FILE]...'))
183 _('[OPTION]... [FILE]...'))
184 def addremove(ui, repo, *pats, **opts):
184 def addremove(ui, repo, *pats, **opts):
185 """add all new files, delete all missing files
185 """add all new files, delete all missing files
186
186
187 Add all new files and remove all missing files from the
187 Add all new files and remove all missing files from the
188 repository.
188 repository.
189
189
190 New files are ignored if they match any of the patterns in
190 New files are ignored if they match any of the patterns in
191 ``.hgignore``. As with add, these changes take effect at the next
191 ``.hgignore``. As with add, these changes take effect at the next
192 commit.
192 commit.
193
193
194 Use the -s/--similarity option to detect renamed files. This
194 Use the -s/--similarity option to detect renamed files. This
195 option takes a percentage between 0 (disabled) and 100 (files must
195 option takes a percentage between 0 (disabled) and 100 (files must
196 be identical) as its parameter. With a parameter greater than 0,
196 be identical) as its parameter. With a parameter greater than 0,
197 this compares every removed file with every added file and records
197 this compares every removed file with every added file and records
198 those similar enough as renames. Detecting renamed files this way
198 those similar enough as renames. Detecting renamed files this way
199 can be expensive. After using this option, :hg:`status -C` can be
199 can be expensive. After using this option, :hg:`status -C` can be
200 used to check which files were identified as moved or renamed. If
200 used to check which files were identified as moved or renamed. If
201 not specified, -s/--similarity defaults to 100 and only renames of
201 not specified, -s/--similarity defaults to 100 and only renames of
202 identical files are detected.
202 identical files are detected.
203
203
204 Returns 0 if all files are successfully added.
204 Returns 0 if all files are successfully added.
205 """
205 """
206 try:
206 try:
207 sim = float(opts.get('similarity') or 100)
207 sim = float(opts.get('similarity') or 100)
208 except ValueError:
208 except ValueError:
209 raise util.Abort(_('similarity must be a number'))
209 raise util.Abort(_('similarity must be a number'))
210 if sim < 0 or sim > 100:
210 if sim < 0 or sim > 100:
211 raise util.Abort(_('similarity must be between 0 and 100'))
211 raise util.Abort(_('similarity must be between 0 and 100'))
212 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
212 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
213
213
214 @command('^annotate|blame',
214 @command('^annotate|blame',
215 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
215 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
216 ('', 'follow', None,
216 ('', 'follow', None,
217 _('follow copies/renames and list the filename (DEPRECATED)')),
217 _('follow copies/renames and list the filename (DEPRECATED)')),
218 ('', 'no-follow', None, _("don't follow copies and renames")),
218 ('', 'no-follow', None, _("don't follow copies and renames")),
219 ('a', 'text', None, _('treat all files as text')),
219 ('a', 'text', None, _('treat all files as text')),
220 ('u', 'user', None, _('list the author (long with -v)')),
220 ('u', 'user', None, _('list the author (long with -v)')),
221 ('f', 'file', None, _('list the filename')),
221 ('f', 'file', None, _('list the filename')),
222 ('d', 'date', None, _('list the date (short with -q)')),
222 ('d', 'date', None, _('list the date (short with -q)')),
223 ('n', 'number', None, _('list the revision number (default)')),
223 ('n', 'number', None, _('list the revision number (default)')),
224 ('c', 'changeset', None, _('list the changeset')),
224 ('c', 'changeset', None, _('list the changeset')),
225 ('l', 'line-number', None, _('show line number at the first appearance'))
225 ('l', 'line-number', None, _('show line number at the first appearance'))
226 ] + diffwsopts + walkopts,
226 ] + diffwsopts + walkopts,
227 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
227 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
228 def annotate(ui, repo, *pats, **opts):
228 def annotate(ui, repo, *pats, **opts):
229 """show changeset information by line for each file
229 """show changeset information by line for each file
230
230
231 List changes in files, showing the revision id responsible for
231 List changes in files, showing the revision id responsible for
232 each line
232 each line
233
233
234 This command is useful for discovering when a change was made and
234 This command is useful for discovering when a change was made and
235 by whom.
235 by whom.
236
236
237 Without the -a/--text option, annotate will avoid processing files
237 Without the -a/--text option, annotate will avoid processing files
238 it detects as binary. With -a, annotate will annotate the file
238 it detects as binary. With -a, annotate will annotate the file
239 anyway, although the results will probably be neither useful
239 anyway, although the results will probably be neither useful
240 nor desirable.
240 nor desirable.
241
241
242 Returns 0 on success.
242 Returns 0 on success.
243 """
243 """
244 if opts.get('follow'):
244 if opts.get('follow'):
245 # --follow is deprecated and now just an alias for -f/--file
245 # --follow is deprecated and now just an alias for -f/--file
246 # to mimic the behavior of Mercurial before version 1.5
246 # to mimic the behavior of Mercurial before version 1.5
247 opts['file'] = True
247 opts['file'] = True
248
248
249 datefunc = ui.quiet and util.shortdate or util.datestr
249 datefunc = ui.quiet and util.shortdate or util.datestr
250 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
250 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
251
251
252 if not pats:
252 if not pats:
253 raise util.Abort(_('at least one filename or pattern is required'))
253 raise util.Abort(_('at least one filename or pattern is required'))
254
254
255 hexfn = ui.debugflag and hex or short
255 hexfn = ui.debugflag and hex or short
256
256
257 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
257 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
258 ('number', ' ', lambda x: str(x[0].rev())),
258 ('number', ' ', lambda x: str(x[0].rev())),
259 ('changeset', ' ', lambda x: hexfn(x[0].node())),
259 ('changeset', ' ', lambda x: hexfn(x[0].node())),
260 ('date', ' ', getdate),
260 ('date', ' ', getdate),
261 ('file', ' ', lambda x: x[0].path()),
261 ('file', ' ', lambda x: x[0].path()),
262 ('line_number', ':', lambda x: str(x[1])),
262 ('line_number', ':', lambda x: str(x[1])),
263 ]
263 ]
264
264
265 if (not opts.get('user') and not opts.get('changeset')
265 if (not opts.get('user') and not opts.get('changeset')
266 and not opts.get('date') and not opts.get('file')):
266 and not opts.get('date') and not opts.get('file')):
267 opts['number'] = True
267 opts['number'] = True
268
268
269 linenumber = opts.get('line_number') is not None
269 linenumber = opts.get('line_number') is not None
270 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
270 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
271 raise util.Abort(_('at least one of -n/-c is required for -l'))
271 raise util.Abort(_('at least one of -n/-c is required for -l'))
272
272
273 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
273 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
274 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
274 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
275
275
276 def bad(x, y):
276 def bad(x, y):
277 raise util.Abort("%s: %s" % (x, y))
277 raise util.Abort("%s: %s" % (x, y))
278
278
279 ctx = scmutil.revsingle(repo, opts.get('rev'))
279 ctx = scmutil.revsingle(repo, opts.get('rev'))
280 m = scmutil.match(ctx, pats, opts)
280 m = scmutil.match(ctx, pats, opts)
281 m.bad = bad
281 m.bad = bad
282 follow = not opts.get('no_follow')
282 follow = not opts.get('no_follow')
283 diffopts = patch.diffopts(ui, opts, section='annotate')
283 diffopts = patch.diffopts(ui, opts, section='annotate')
284 for abs in ctx.walk(m):
284 for abs in ctx.walk(m):
285 fctx = ctx[abs]
285 fctx = ctx[abs]
286 if not opts.get('text') and util.binary(fctx.data()):
286 if not opts.get('text') and util.binary(fctx.data()):
287 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
287 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
288 continue
288 continue
289
289
290 lines = fctx.annotate(follow=follow, linenumber=linenumber,
290 lines = fctx.annotate(follow=follow, linenumber=linenumber,
291 diffopts=diffopts)
291 diffopts=diffopts)
292 pieces = []
292 pieces = []
293
293
294 for f, sep in funcmap:
294 for f, sep in funcmap:
295 l = [f(n) for n, dummy in lines]
295 l = [f(n) for n, dummy in lines]
296 if l:
296 if l:
297 sized = [(x, encoding.colwidth(x)) for x in l]
297 sized = [(x, encoding.colwidth(x)) for x in l]
298 ml = max([w for x, w in sized])
298 ml = max([w for x, w in sized])
299 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
299 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
300 for x, w in sized])
300 for x, w in sized])
301
301
302 if pieces:
302 if pieces:
303 for p, l in zip(zip(*pieces), lines):
303 for p, l in zip(zip(*pieces), lines):
304 ui.write("%s: %s" % ("".join(p), l[1]))
304 ui.write("%s: %s" % ("".join(p), l[1]))
305
305
306 if lines and not lines[-1][1].endswith('\n'):
306 if lines and not lines[-1][1].endswith('\n'):
307 ui.write('\n')
307 ui.write('\n')
308
308
309 @command('archive',
309 @command('archive',
310 [('', 'no-decode', None, _('do not pass files through decoders')),
310 [('', 'no-decode', None, _('do not pass files through decoders')),
311 ('p', 'prefix', '', _('directory prefix for files in archive'),
311 ('p', 'prefix', '', _('directory prefix for files in archive'),
312 _('PREFIX')),
312 _('PREFIX')),
313 ('r', 'rev', '', _('revision to distribute'), _('REV')),
313 ('r', 'rev', '', _('revision to distribute'), _('REV')),
314 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
314 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
315 ] + subrepoopts + walkopts,
315 ] + subrepoopts + walkopts,
316 _('[OPTION]... DEST'))
316 _('[OPTION]... DEST'))
317 def archive(ui, repo, dest, **opts):
317 def archive(ui, repo, dest, **opts):
318 '''create an unversioned archive of a repository revision
318 '''create an unversioned archive of a repository revision
319
319
320 By default, the revision used is the parent of the working
320 By default, the revision used is the parent of the working
321 directory; use -r/--rev to specify a different revision.
321 directory; use -r/--rev to specify a different revision.
322
322
323 The archive type is automatically detected based on file
323 The archive type is automatically detected based on file
324 extension (or override using -t/--type).
324 extension (or override using -t/--type).
325
325
326 .. container:: verbose
326 .. container:: verbose
327
327
328 Examples:
328 Examples:
329
329
330 - create a zip file containing the 1.0 release::
330 - create a zip file containing the 1.0 release::
331
331
332 hg archive -r 1.0 project-1.0.zip
332 hg archive -r 1.0 project-1.0.zip
333
333
334 - create a tarball excluding .hg files::
334 - create a tarball excluding .hg files::
335
335
336 hg archive project.tar.gz -X ".hg*"
336 hg archive project.tar.gz -X ".hg*"
337
337
338 Valid types are:
338 Valid types are:
339
339
340 :``files``: a directory full of files (default)
340 :``files``: a directory full of files (default)
341 :``tar``: tar archive, uncompressed
341 :``tar``: tar archive, uncompressed
342 :``tbz2``: tar archive, compressed using bzip2
342 :``tbz2``: tar archive, compressed using bzip2
343 :``tgz``: tar archive, compressed using gzip
343 :``tgz``: tar archive, compressed using gzip
344 :``uzip``: zip archive, uncompressed
344 :``uzip``: zip archive, uncompressed
345 :``zip``: zip archive, compressed using deflate
345 :``zip``: zip archive, compressed using deflate
346
346
347 The exact name of the destination archive or directory is given
347 The exact name of the destination archive or directory is given
348 using a format string; see :hg:`help export` for details.
348 using a format string; see :hg:`help export` for details.
349
349
350 Each member added to an archive file has a directory prefix
350 Each member added to an archive file has a directory prefix
351 prepended. Use -p/--prefix to specify a format string for the
351 prepended. Use -p/--prefix to specify a format string for the
352 prefix. The default is the basename of the archive, with suffixes
352 prefix. The default is the basename of the archive, with suffixes
353 removed.
353 removed.
354
354
355 Returns 0 on success.
355 Returns 0 on success.
356 '''
356 '''
357
357
358 ctx = scmutil.revsingle(repo, opts.get('rev'))
358 ctx = scmutil.revsingle(repo, opts.get('rev'))
359 if not ctx:
359 if not ctx:
360 raise util.Abort(_('no working directory: please specify a revision'))
360 raise util.Abort(_('no working directory: please specify a revision'))
361 node = ctx.node()
361 node = ctx.node()
362 dest = cmdutil.makefilename(repo, dest, node)
362 dest = cmdutil.makefilename(repo, dest, node)
363 if os.path.realpath(dest) == repo.root:
363 if os.path.realpath(dest) == repo.root:
364 raise util.Abort(_('repository root cannot be destination'))
364 raise util.Abort(_('repository root cannot be destination'))
365
365
366 kind = opts.get('type') or archival.guesskind(dest) or 'files'
366 kind = opts.get('type') or archival.guesskind(dest) or 'files'
367 prefix = opts.get('prefix')
367 prefix = opts.get('prefix')
368
368
369 if dest == '-':
369 if dest == '-':
370 if kind == 'files':
370 if kind == 'files':
371 raise util.Abort(_('cannot archive plain files to stdout'))
371 raise util.Abort(_('cannot archive plain files to stdout'))
372 dest = cmdutil.makefileobj(repo, dest)
372 dest = cmdutil.makefileobj(repo, dest)
373 if not prefix:
373 if not prefix:
374 prefix = os.path.basename(repo.root) + '-%h'
374 prefix = os.path.basename(repo.root) + '-%h'
375
375
376 prefix = cmdutil.makefilename(repo, prefix, node)
376 prefix = cmdutil.makefilename(repo, prefix, node)
377 matchfn = scmutil.match(ctx, [], opts)
377 matchfn = scmutil.match(ctx, [], opts)
378 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
378 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
379 matchfn, prefix, subrepos=opts.get('subrepos'))
379 matchfn, prefix, subrepos=opts.get('subrepos'))
380
380
381 @command('backout',
381 @command('backout',
382 [('', 'merge', None, _('merge with old dirstate parent after backout')),
382 [('', 'merge', None, _('merge with old dirstate parent after backout')),
383 ('', 'parent', '',
383 ('', 'parent', '',
384 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
384 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
385 ('r', 'rev', '', _('revision to backout'), _('REV')),
385 ('r', 'rev', '', _('revision to backout'), _('REV')),
386 ] + mergetoolopts + walkopts + commitopts + commitopts2,
386 ] + mergetoolopts + walkopts + commitopts + commitopts2,
387 _('[OPTION]... [-r] REV'))
387 _('[OPTION]... [-r] REV'))
388 def backout(ui, repo, node=None, rev=None, **opts):
388 def backout(ui, repo, node=None, rev=None, **opts):
389 '''reverse effect of earlier changeset
389 '''reverse effect of earlier changeset
390
390
391 Prepare a new changeset with the effect of REV undone in the
391 Prepare a new changeset with the effect of REV undone in the
392 current working directory.
392 current working directory.
393
393
394 If REV is the parent of the working directory, then this new changeset
394 If REV is the parent of the working directory, then this new changeset
395 is committed automatically. Otherwise, hg needs to merge the
395 is committed automatically. Otherwise, hg needs to merge the
396 changes and the merged result is left uncommitted.
396 changes and the merged result is left uncommitted.
397
397
398 .. note::
398 .. note::
399 backout cannot be used to fix either an unwanted or
399 backout cannot be used to fix either an unwanted or
400 incorrect merge.
400 incorrect merge.
401
401
402 .. container:: verbose
402 .. container:: verbose
403
403
404 By default, the pending changeset will have one parent,
404 By default, the pending changeset will have one parent,
405 maintaining a linear history. With --merge, the pending
405 maintaining a linear history. With --merge, the pending
406 changeset will instead have two parents: the old parent of the
406 changeset will instead have two parents: the old parent of the
407 working directory and a new child of REV that simply undoes REV.
407 working directory and a new child of REV that simply undoes REV.
408
408
409 Before version 1.7, the behavior without --merge was equivalent
409 Before version 1.7, the behavior without --merge was equivalent
410 to specifying --merge followed by :hg:`update --clean .` to
410 to specifying --merge followed by :hg:`update --clean .` to
411 cancel the merge and leave the child of REV as a head to be
411 cancel the merge and leave the child of REV as a head to be
412 merged separately.
412 merged separately.
413
413
414 See :hg:`help dates` for a list of formats valid for -d/--date.
414 See :hg:`help dates` for a list of formats valid for -d/--date.
415
415
416 Returns 0 on success.
416 Returns 0 on success.
417 '''
417 '''
418 if rev and node:
418 if rev and node:
419 raise util.Abort(_("please specify just one revision"))
419 raise util.Abort(_("please specify just one revision"))
420
420
421 if not rev:
421 if not rev:
422 rev = node
422 rev = node
423
423
424 if not rev:
424 if not rev:
425 raise util.Abort(_("please specify a revision to backout"))
425 raise util.Abort(_("please specify a revision to backout"))
426
426
427 date = opts.get('date')
427 date = opts.get('date')
428 if date:
428 if date:
429 opts['date'] = util.parsedate(date)
429 opts['date'] = util.parsedate(date)
430
430
431 cmdutil.bailifchanged(repo)
431 cmdutil.bailifchanged(repo)
432 node = scmutil.revsingle(repo, rev).node()
432 node = scmutil.revsingle(repo, rev).node()
433
433
434 op1, op2 = repo.dirstate.parents()
434 op1, op2 = repo.dirstate.parents()
435 a = repo.changelog.ancestor(op1, node)
435 a = repo.changelog.ancestor(op1, node)
436 if a != node:
436 if a != node:
437 raise util.Abort(_('cannot backout change on a different branch'))
437 raise util.Abort(_('cannot backout change on a different branch'))
438
438
439 p1, p2 = repo.changelog.parents(node)
439 p1, p2 = repo.changelog.parents(node)
440 if p1 == nullid:
440 if p1 == nullid:
441 raise util.Abort(_('cannot backout a change with no parents'))
441 raise util.Abort(_('cannot backout a change with no parents'))
442 if p2 != nullid:
442 if p2 != nullid:
443 if not opts.get('parent'):
443 if not opts.get('parent'):
444 raise util.Abort(_('cannot backout a merge changeset'))
444 raise util.Abort(_('cannot backout a merge changeset'))
445 p = repo.lookup(opts['parent'])
445 p = repo.lookup(opts['parent'])
446 if p not in (p1, p2):
446 if p not in (p1, p2):
447 raise util.Abort(_('%s is not a parent of %s') %
447 raise util.Abort(_('%s is not a parent of %s') %
448 (short(p), short(node)))
448 (short(p), short(node)))
449 parent = p
449 parent = p
450 else:
450 else:
451 if opts.get('parent'):
451 if opts.get('parent'):
452 raise util.Abort(_('cannot use --parent on non-merge changeset'))
452 raise util.Abort(_('cannot use --parent on non-merge changeset'))
453 parent = p1
453 parent = p1
454
454
455 # the backout should appear on the same branch
455 # the backout should appear on the same branch
456 wlock = repo.wlock()
456 wlock = repo.wlock()
457 try:
457 try:
458 branch = repo.dirstate.branch()
458 branch = repo.dirstate.branch()
459 bheads = repo.branchheads(branch)
459 bheads = repo.branchheads(branch)
460 hg.clean(repo, node, show_stats=False)
460 hg.clean(repo, node, show_stats=False)
461 repo.dirstate.setbranch(branch)
461 repo.dirstate.setbranch(branch)
462 rctx = scmutil.revsingle(repo, hex(parent))
462 rctx = scmutil.revsingle(repo, hex(parent))
463 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
463 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
464 if not opts.get('merge') and op1 != node:
464 if not opts.get('merge') and op1 != node:
465 try:
465 try:
466 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
466 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
467 return hg.update(repo, op1)
467 return hg.update(repo, op1)
468 finally:
468 finally:
469 ui.setconfig('ui', 'forcemerge', '')
469 ui.setconfig('ui', 'forcemerge', '')
470
470
471 e = cmdutil.commiteditor
471 e = cmdutil.commiteditor
472 if not opts['message'] and not opts['logfile']:
472 if not opts['message'] and not opts['logfile']:
473 # we don't translate commit messages
473 # we don't translate commit messages
474 opts['message'] = "Backed out changeset %s" % short(node)
474 opts['message'] = "Backed out changeset %s" % short(node)
475 e = cmdutil.commitforceeditor
475 e = cmdutil.commitforceeditor
476
476
477 def commitfunc(ui, repo, message, match, opts):
477 def commitfunc(ui, repo, message, match, opts):
478 return repo.commit(message, opts.get('user'), opts.get('date'),
478 return repo.commit(message, opts.get('user'), opts.get('date'),
479 match, editor=e)
479 match, editor=e)
480 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
480 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
481 cmdutil.commitstatus(repo, newnode, branch, bheads)
481 cmdutil.commitstatus(repo, newnode, branch, bheads)
482
482
483 def nice(node):
483 def nice(node):
484 return '%d:%s' % (repo.changelog.rev(node), short(node))
484 return '%d:%s' % (repo.changelog.rev(node), short(node))
485 ui.status(_('changeset %s backs out changeset %s\n') %
485 ui.status(_('changeset %s backs out changeset %s\n') %
486 (nice(repo.changelog.tip()), nice(node)))
486 (nice(repo.changelog.tip()), nice(node)))
487 if opts.get('merge') and op1 != node:
487 if opts.get('merge') and op1 != node:
488 hg.clean(repo, op1, show_stats=False)
488 hg.clean(repo, op1, show_stats=False)
489 ui.status(_('merging with changeset %s\n')
489 ui.status(_('merging with changeset %s\n')
490 % nice(repo.changelog.tip()))
490 % nice(repo.changelog.tip()))
491 try:
491 try:
492 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
492 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
493 return hg.merge(repo, hex(repo.changelog.tip()))
493 return hg.merge(repo, hex(repo.changelog.tip()))
494 finally:
494 finally:
495 ui.setconfig('ui', 'forcemerge', '')
495 ui.setconfig('ui', 'forcemerge', '')
496 finally:
496 finally:
497 wlock.release()
497 wlock.release()
498 return 0
498 return 0
499
499
500 @command('bisect',
500 @command('bisect',
501 [('r', 'reset', False, _('reset bisect state')),
501 [('r', 'reset', False, _('reset bisect state')),
502 ('g', 'good', False, _('mark changeset good')),
502 ('g', 'good', False, _('mark changeset good')),
503 ('b', 'bad', False, _('mark changeset bad')),
503 ('b', 'bad', False, _('mark changeset bad')),
504 ('s', 'skip', False, _('skip testing changeset')),
504 ('s', 'skip', False, _('skip testing changeset')),
505 ('e', 'extend', False, _('extend the bisect range')),
505 ('e', 'extend', False, _('extend the bisect range')),
506 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
506 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
507 ('U', 'noupdate', False, _('do not update to target'))],
507 ('U', 'noupdate', False, _('do not update to target'))],
508 _("[-gbsr] [-U] [-c CMD] [REV]"))
508 _("[-gbsr] [-U] [-c CMD] [REV]"))
509 def bisect(ui, repo, rev=None, extra=None, command=None,
509 def bisect(ui, repo, rev=None, extra=None, command=None,
510 reset=None, good=None, bad=None, skip=None, extend=None,
510 reset=None, good=None, bad=None, skip=None, extend=None,
511 noupdate=None):
511 noupdate=None):
512 """subdivision search of changesets
512 """subdivision search of changesets
513
513
514 This command helps to find changesets which introduce problems. To
514 This command helps to find changesets which introduce problems. To
515 use, mark the earliest changeset you know exhibits the problem as
515 use, mark the earliest changeset you know exhibits the problem as
516 bad, then mark the latest changeset which is free from the problem
516 bad, then mark the latest changeset which is free from the problem
517 as good. Bisect will update your working directory to a revision
517 as good. Bisect will update your working directory to a revision
518 for testing (unless the -U/--noupdate option is specified). Once
518 for testing (unless the -U/--noupdate option is specified). Once
519 you have performed tests, mark the working directory as good or
519 you have performed tests, mark the working directory as good or
520 bad, and bisect will either update to another candidate changeset
520 bad, and bisect will either update to another candidate changeset
521 or announce that it has found the bad revision.
521 or announce that it has found the bad revision.
522
522
523 As a shortcut, you can also use the revision argument to mark a
523 As a shortcut, you can also use the revision argument to mark a
524 revision as good or bad without checking it out first.
524 revision as good or bad without checking it out first.
525
525
526 If you supply a command, it will be used for automatic bisection.
526 If you supply a command, it will be used for automatic bisection.
527 The environment variable HG_NODE will contain the ID of the
527 The environment variable HG_NODE will contain the ID of the
528 changeset being tested. The exit status of the command will be
528 changeset being tested. The exit status of the command will be
529 used to mark revisions as good or bad: status 0 means good, 125
529 used to mark revisions as good or bad: status 0 means good, 125
530 means to skip the revision, 127 (command not found) will abort the
530 means to skip the revision, 127 (command not found) will abort the
531 bisection, and any other non-zero exit status means the revision
531 bisection, and any other non-zero exit status means the revision
532 is bad.
532 is bad.
533
533
534 .. container:: verbose
534 .. container:: verbose
535
535
536 Some examples:
536 Some examples:
537
537
538 - start a bisection with known bad revision 12, and good revision 34::
538 - start a bisection with known bad revision 12, and good revision 34::
539
539
540 hg bisect --bad 34
540 hg bisect --bad 34
541 hg bisect --good 12
541 hg bisect --good 12
542
542
543 - advance the current bisection by marking current revision as good or
543 - advance the current bisection by marking current revision as good or
544 bad::
544 bad::
545
545
546 hg bisect --good
546 hg bisect --good
547 hg bisect --bad
547 hg bisect --bad
548
548
549 - mark the current revision, or a known revision, to be skipped (e.g. if
549 - mark the current revision, or a known revision, to be skipped (e.g. if
550 that revision is not usable because of another issue)::
550 that revision is not usable because of another issue)::
551
551
552 hg bisect --skip
552 hg bisect --skip
553 hg bisect --skip 23
553 hg bisect --skip 23
554
554
555 - skip all revisions that do not touch directories ``foo`` or ``bar``
555 - skip all revisions that do not touch directories ``foo`` or ``bar``
556
556
557 hg bisect --skip '!( file("path:foo") & file("path:bar") )'
557 hg bisect --skip '!( file("path:foo") & file("path:bar") )'
558
558
559 - forget the current bisection::
559 - forget the current bisection::
560
560
561 hg bisect --reset
561 hg bisect --reset
562
562
563 - use 'make && make tests' to automatically find the first broken
563 - use 'make && make tests' to automatically find the first broken
564 revision::
564 revision::
565
565
566 hg bisect --reset
566 hg bisect --reset
567 hg bisect --bad 34
567 hg bisect --bad 34
568 hg bisect --good 12
568 hg bisect --good 12
569 hg bisect --command 'make && make tests'
569 hg bisect --command 'make && make tests'
570
570
571 - see all changesets whose states are already known in the current
571 - see all changesets whose states are already known in the current
572 bisection::
572 bisection::
573
573
574 hg log -r "bisect(pruned)"
574 hg log -r "bisect(pruned)"
575
575
576 - see the changeset currently being bisected (especially useful
576 - see the changeset currently being bisected (especially useful
577 if running with -U/--noupdate)::
577 if running with -U/--noupdate)::
578
578
579 hg log -r "bisect(current)"
579 hg log -r "bisect(current)"
580
580
581 - see all changesets that took part in the current bisection::
581 - see all changesets that took part in the current bisection::
582
582
583 hg log -r "bisect(range)"
583 hg log -r "bisect(range)"
584
584
585 - with the graphlog extension, you can even get a nice graph::
585 - with the graphlog extension, you can even get a nice graph::
586
586
587 hg log --graph -r "bisect(range)"
587 hg log --graph -r "bisect(range)"
588
588
589 See :hg:`help revsets` for more about the `bisect()` keyword.
589 See :hg:`help revsets` for more about the `bisect()` keyword.
590
590
591 Returns 0 on success.
591 Returns 0 on success.
592 """
592 """
593 def extendbisectrange(nodes, good):
593 def extendbisectrange(nodes, good):
594 # bisect is incomplete when it ends on a merge node and
594 # bisect is incomplete when it ends on a merge node and
595 # one of the parent was not checked.
595 # one of the parent was not checked.
596 parents = repo[nodes[0]].parents()
596 parents = repo[nodes[0]].parents()
597 if len(parents) > 1:
597 if len(parents) > 1:
598 side = good and state['bad'] or state['good']
598 side = good and state['bad'] or state['good']
599 num = len(set(i.node() for i in parents) & set(side))
599 num = len(set(i.node() for i in parents) & set(side))
600 if num == 1:
600 if num == 1:
601 return parents[0].ancestor(parents[1])
601 return parents[0].ancestor(parents[1])
602 return None
602 return None
603
603
604 def print_result(nodes, good):
604 def print_result(nodes, good):
605 displayer = cmdutil.show_changeset(ui, repo, {})
605 displayer = cmdutil.show_changeset(ui, repo, {})
606 if len(nodes) == 1:
606 if len(nodes) == 1:
607 # narrowed it down to a single revision
607 # narrowed it down to a single revision
608 if good:
608 if good:
609 ui.write(_("The first good revision is:\n"))
609 ui.write(_("The first good revision is:\n"))
610 else:
610 else:
611 ui.write(_("The first bad revision is:\n"))
611 ui.write(_("The first bad revision is:\n"))
612 displayer.show(repo[nodes[0]])
612 displayer.show(repo[nodes[0]])
613 extendnode = extendbisectrange(nodes, good)
613 extendnode = extendbisectrange(nodes, good)
614 if extendnode is not None:
614 if extendnode is not None:
615 ui.write(_('Not all ancestors of this changeset have been'
615 ui.write(_('Not all ancestors of this changeset have been'
616 ' checked.\nUse bisect --extend to continue the '
616 ' checked.\nUse bisect --extend to continue the '
617 'bisection from\nthe common ancestor, %s.\n')
617 'bisection from\nthe common ancestor, %s.\n')
618 % extendnode)
618 % extendnode)
619 else:
619 else:
620 # multiple possible revisions
620 # multiple possible revisions
621 if good:
621 if good:
622 ui.write(_("Due to skipped revisions, the first "
622 ui.write(_("Due to skipped revisions, the first "
623 "good revision could be any of:\n"))
623 "good revision could be any of:\n"))
624 else:
624 else:
625 ui.write(_("Due to skipped revisions, the first "
625 ui.write(_("Due to skipped revisions, the first "
626 "bad revision could be any of:\n"))
626 "bad revision could be any of:\n"))
627 for n in nodes:
627 for n in nodes:
628 displayer.show(repo[n])
628 displayer.show(repo[n])
629 displayer.close()
629 displayer.close()
630
630
631 def check_state(state, interactive=True):
631 def check_state(state, interactive=True):
632 if not state['good'] or not state['bad']:
632 if not state['good'] or not state['bad']:
633 if (good or bad or skip or reset) and interactive:
633 if (good or bad or skip or reset) and interactive:
634 return
634 return
635 if not state['good']:
635 if not state['good']:
636 raise util.Abort(_('cannot bisect (no known good revisions)'))
636 raise util.Abort(_('cannot bisect (no known good revisions)'))
637 else:
637 else:
638 raise util.Abort(_('cannot bisect (no known bad revisions)'))
638 raise util.Abort(_('cannot bisect (no known bad revisions)'))
639 return True
639 return True
640
640
641 # backward compatibility
641 # backward compatibility
642 if rev in "good bad reset init".split():
642 if rev in "good bad reset init".split():
643 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
643 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
644 cmd, rev, extra = rev, extra, None
644 cmd, rev, extra = rev, extra, None
645 if cmd == "good":
645 if cmd == "good":
646 good = True
646 good = True
647 elif cmd == "bad":
647 elif cmd == "bad":
648 bad = True
648 bad = True
649 else:
649 else:
650 reset = True
650 reset = True
651 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
651 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
652 raise util.Abort(_('incompatible arguments'))
652 raise util.Abort(_('incompatible arguments'))
653
653
654 if reset:
654 if reset:
655 p = repo.join("bisect.state")
655 p = repo.join("bisect.state")
656 if os.path.exists(p):
656 if os.path.exists(p):
657 os.unlink(p)
657 os.unlink(p)
658 return
658 return
659
659
660 state = hbisect.load_state(repo)
660 state = hbisect.load_state(repo)
661
661
662 if command:
662 if command:
663 changesets = 1
663 changesets = 1
664 try:
664 try:
665 node = state['current'][0]
665 node = state['current'][0]
666 except LookupError:
666 except LookupError:
667 if noupdate:
667 if noupdate:
668 raise util.Abort(_('current bisect revision is unknown - '
668 raise util.Abort(_('current bisect revision is unknown - '
669 'start a new bisect to fix'))
669 'start a new bisect to fix'))
670 node, p2 = repo.dirstate.parents()
670 node, p2 = repo.dirstate.parents()
671 if p2 != nullid:
671 if p2 != nullid:
672 raise util.Abort(_('current bisect revision is a merge'))
672 raise util.Abort(_('current bisect revision is a merge'))
673 try:
673 try:
674 while changesets:
674 while changesets:
675 # update state
675 # update state
676 state['current'] = [node]
676 state['current'] = [node]
677 hbisect.save_state(repo, state)
677 hbisect.save_state(repo, state)
678 status = util.system(command,
678 status = util.system(command,
679 environ={'HG_NODE': hex(node)},
679 environ={'HG_NODE': hex(node)},
680 out=ui.fout)
680 out=ui.fout)
681 if status == 125:
681 if status == 125:
682 transition = "skip"
682 transition = "skip"
683 elif status == 0:
683 elif status == 0:
684 transition = "good"
684 transition = "good"
685 # status < 0 means process was killed
685 # status < 0 means process was killed
686 elif status == 127:
686 elif status == 127:
687 raise util.Abort(_("failed to execute %s") % command)
687 raise util.Abort(_("failed to execute %s") % command)
688 elif status < 0:
688 elif status < 0:
689 raise util.Abort(_("%s killed") % command)
689 raise util.Abort(_("%s killed") % command)
690 else:
690 else:
691 transition = "bad"
691 transition = "bad"
692 ctx = scmutil.revsingle(repo, rev, node)
692 ctx = scmutil.revsingle(repo, rev, node)
693 rev = None # clear for future iterations
693 rev = None # clear for future iterations
694 state[transition].append(ctx.node())
694 state[transition].append(ctx.node())
695 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
695 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
696 check_state(state, interactive=False)
696 check_state(state, interactive=False)
697 # bisect
697 # bisect
698 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
698 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
699 # update to next check
699 # update to next check
700 node = nodes[0]
700 node = nodes[0]
701 if not noupdate:
701 if not noupdate:
702 cmdutil.bailifchanged(repo)
702 cmdutil.bailifchanged(repo)
703 hg.clean(repo, node, show_stats=False)
703 hg.clean(repo, node, show_stats=False)
704 finally:
704 finally:
705 state['current'] = [node]
705 state['current'] = [node]
706 hbisect.save_state(repo, state)
706 hbisect.save_state(repo, state)
707 print_result(nodes, good)
707 print_result(nodes, good)
708 return
708 return
709
709
710 # update state
710 # update state
711
711
712 if rev:
712 if rev:
713 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
713 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
714 else:
714 else:
715 nodes = [repo.lookup('.')]
715 nodes = [repo.lookup('.')]
716
716
717 if good or bad or skip:
717 if good or bad or skip:
718 if good:
718 if good:
719 state['good'] += nodes
719 state['good'] += nodes
720 elif bad:
720 elif bad:
721 state['bad'] += nodes
721 state['bad'] += nodes
722 elif skip:
722 elif skip:
723 state['skip'] += nodes
723 state['skip'] += nodes
724 hbisect.save_state(repo, state)
724 hbisect.save_state(repo, state)
725
725
726 if not check_state(state):
726 if not check_state(state):
727 return
727 return
728
728
729 # actually bisect
729 # actually bisect
730 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
730 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
731 if extend:
731 if extend:
732 if not changesets:
732 if not changesets:
733 extendnode = extendbisectrange(nodes, good)
733 extendnode = extendbisectrange(nodes, good)
734 if extendnode is not None:
734 if extendnode is not None:
735 ui.write(_("Extending search to changeset %d:%s\n"
735 ui.write(_("Extending search to changeset %d:%s\n"
736 % (extendnode.rev(), extendnode)))
736 % (extendnode.rev(), extendnode)))
737 state['current'] = [extendnode.node()]
737 state['current'] = [extendnode.node()]
738 hbisect.save_state(repo, state)
738 hbisect.save_state(repo, state)
739 if noupdate:
739 if noupdate:
740 return
740 return
741 cmdutil.bailifchanged(repo)
741 cmdutil.bailifchanged(repo)
742 return hg.clean(repo, extendnode.node())
742 return hg.clean(repo, extendnode.node())
743 raise util.Abort(_("nothing to extend"))
743 raise util.Abort(_("nothing to extend"))
744
744
745 if changesets == 0:
745 if changesets == 0:
746 print_result(nodes, good)
746 print_result(nodes, good)
747 else:
747 else:
748 assert len(nodes) == 1 # only a single node can be tested next
748 assert len(nodes) == 1 # only a single node can be tested next
749 node = nodes[0]
749 node = nodes[0]
750 # compute the approximate number of remaining tests
750 # compute the approximate number of remaining tests
751 tests, size = 0, 2
751 tests, size = 0, 2
752 while size <= changesets:
752 while size <= changesets:
753 tests, size = tests + 1, size * 2
753 tests, size = tests + 1, size * 2
754 rev = repo.changelog.rev(node)
754 rev = repo.changelog.rev(node)
755 ui.write(_("Testing changeset %d:%s "
755 ui.write(_("Testing changeset %d:%s "
756 "(%d changesets remaining, ~%d tests)\n")
756 "(%d changesets remaining, ~%d tests)\n")
757 % (rev, short(node), changesets, tests))
757 % (rev, short(node), changesets, tests))
758 state['current'] = [node]
758 state['current'] = [node]
759 hbisect.save_state(repo, state)
759 hbisect.save_state(repo, state)
760 if not noupdate:
760 if not noupdate:
761 cmdutil.bailifchanged(repo)
761 cmdutil.bailifchanged(repo)
762 return hg.clean(repo, node)
762 return hg.clean(repo, node)
763
763
764 @command('bookmarks|bookmark',
764 @command('bookmarks|bookmark',
765 [('f', 'force', False, _('force')),
765 [('f', 'force', False, _('force')),
766 ('r', 'rev', '', _('revision'), _('REV')),
766 ('r', 'rev', '', _('revision'), _('REV')),
767 ('d', 'delete', False, _('delete a given bookmark')),
767 ('d', 'delete', False, _('delete a given bookmark')),
768 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
768 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
769 ('i', 'inactive', False, _('mark a bookmark inactive'))],
769 ('i', 'inactive', False, _('mark a bookmark inactive'))],
770 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
770 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
771 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
771 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
772 rename=None, inactive=False):
772 rename=None, inactive=False):
773 '''track a line of development with movable markers
773 '''track a line of development with movable markers
774
774
775 Bookmarks are pointers to certain commits that move when committing.
775 Bookmarks are pointers to certain commits that move when committing.
776 Bookmarks are local. They can be renamed, copied and deleted. It is
776 Bookmarks are local. They can be renamed, copied and deleted. It is
777 possible to use :hg:`merge NAME` to merge from a given bookmark, and
777 possible to use :hg:`merge NAME` to merge from a given bookmark, and
778 :hg:`update NAME` to update to a given bookmark.
778 :hg:`update NAME` to update to a given bookmark.
779
779
780 You can use :hg:`bookmark NAME` to set a bookmark on the working
780 You can use :hg:`bookmark NAME` to set a bookmark on the working
781 directory's parent revision with the given name. If you specify
781 directory's parent revision with the given name. If you specify
782 a revision using -r REV (where REV may be an existing bookmark),
782 a revision using -r REV (where REV may be an existing bookmark),
783 the bookmark is assigned to that revision.
783 the bookmark is assigned to that revision.
784
784
785 Bookmarks can be pushed and pulled between repositories (see :hg:`help
785 Bookmarks can be pushed and pulled between repositories (see :hg:`help
786 push` and :hg:`help pull`). This requires both the local and remote
786 push` and :hg:`help pull`). This requires both the local and remote
787 repositories to support bookmarks. For versions prior to 1.8, this means
787 repositories to support bookmarks. For versions prior to 1.8, this means
788 the bookmarks extension must be enabled.
788 the bookmarks extension must be enabled.
789
789
790 If you set a bookmark called '@', new clones of the repository will
790 If you set a bookmark called '@', new clones of the repository will
791 have that revision checked out (and the bookmark made active) by
791 have that revision checked out (and the bookmark made active) by
792 default.
792 default.
793
793
794 With -i/--inactive, the new bookmark will not be made the active
794 With -i/--inactive, the new bookmark will not be made the active
795 bookmark. If -r/--rev is given, the new bookmark will not be made
795 bookmark. If -r/--rev is given, the new bookmark will not be made
796 active even if -i/--inactive is not given. If no NAME is given, the
796 active even if -i/--inactive is not given. If no NAME is given, the
797 current active bookmark will be marked inactive.
797 current active bookmark will be marked inactive.
798 '''
798 '''
799 hexfn = ui.debugflag and hex or short
799 hexfn = ui.debugflag and hex or short
800 marks = repo._bookmarks
800 marks = repo._bookmarks
801 cur = repo.changectx('.').node()
801 cur = repo.changectx('.').node()
802
802
803 def checkformat(mark):
803 def checkformat(mark):
804 mark = mark.strip()
804 mark = mark.strip()
805 if not mark:
805 if not mark:
806 raise util.Abort(_("bookmark names cannot consist entirely of "
806 raise util.Abort(_("bookmark names cannot consist entirely of "
807 "whitespace"))
807 "whitespace"))
808 scmutil.checknewlabel(repo, mark, 'bookmark')
808 scmutil.checknewlabel(repo, mark, 'bookmark')
809 return mark
809 return mark
810
810
811 def checkconflict(repo, mark, force=False, target=None):
811 def checkconflict(repo, mark, force=False, target=None):
812 if mark in marks and not force:
812 if mark in marks and not force:
813 if target:
813 if target:
814 if marks[mark] == target and target == cur:
814 if marks[mark] == target and target == cur:
815 # re-activating a bookmark
815 # re-activating a bookmark
816 return
816 return
817 anc = repo.changelog.ancestors([repo[target].rev()])
817 anc = repo.changelog.ancestors([repo[target].rev()])
818 bmctx = repo[marks[mark]]
818 bmctx = repo[marks[mark]]
819 if bmctx.rev() in anc:
819 if bmctx.rev() in anc:
820 ui.status(_("moving bookmark '%s' forward from %s\n") %
820 ui.status(_("moving bookmark '%s' forward from %s\n") %
821 (mark, short(bmctx.node())))
821 (mark, short(bmctx.node())))
822 return
822 return
823 raise util.Abort(_("bookmark '%s' already exists "
823 raise util.Abort(_("bookmark '%s' already exists "
824 "(use -f to force)") % mark)
824 "(use -f to force)") % mark)
825 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
825 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
826 and not force):
826 and not force):
827 raise util.Abort(
827 raise util.Abort(
828 _("a bookmark cannot have the name of an existing branch"))
828 _("a bookmark cannot have the name of an existing branch"))
829
829
830 if delete and rename:
830 if delete and rename:
831 raise util.Abort(_("--delete and --rename are incompatible"))
831 raise util.Abort(_("--delete and --rename are incompatible"))
832 if delete and rev:
832 if delete and rev:
833 raise util.Abort(_("--rev is incompatible with --delete"))
833 raise util.Abort(_("--rev is incompatible with --delete"))
834 if rename and rev:
834 if rename and rev:
835 raise util.Abort(_("--rev is incompatible with --rename"))
835 raise util.Abort(_("--rev is incompatible with --rename"))
836 if mark is None and (delete or rev):
836 if mark is None and (delete or rev):
837 raise util.Abort(_("bookmark name required"))
837 raise util.Abort(_("bookmark name required"))
838
838
839 if delete:
839 if delete:
840 if mark not in marks:
840 if mark not in marks:
841 raise util.Abort(_("bookmark '%s' does not exist") % mark)
841 raise util.Abort(_("bookmark '%s' does not exist") % mark)
842 if mark == repo._bookmarkcurrent:
842 if mark == repo._bookmarkcurrent:
843 bookmarks.setcurrent(repo, None)
843 bookmarks.setcurrent(repo, None)
844 del marks[mark]
844 del marks[mark]
845 marks.write()
845 marks.write()
846
846
847 elif rename:
847 elif rename:
848 if mark is None:
848 if mark is None:
849 raise util.Abort(_("new bookmark name required"))
849 raise util.Abort(_("new bookmark name required"))
850 mark = checkformat(mark)
850 mark = checkformat(mark)
851 if rename not in marks:
851 if rename not in marks:
852 raise util.Abort(_("bookmark '%s' does not exist") % rename)
852 raise util.Abort(_("bookmark '%s' does not exist") % rename)
853 checkconflict(repo, mark, force)
853 checkconflict(repo, mark, force)
854 marks[mark] = marks[rename]
854 marks[mark] = marks[rename]
855 if repo._bookmarkcurrent == rename and not inactive:
855 if repo._bookmarkcurrent == rename and not inactive:
856 bookmarks.setcurrent(repo, mark)
856 bookmarks.setcurrent(repo, mark)
857 del marks[rename]
857 del marks[rename]
858 marks.write()
858 marks.write()
859
859
860 elif mark is not None:
860 elif mark is not None:
861 mark = checkformat(mark)
861 mark = checkformat(mark)
862 if inactive and mark == repo._bookmarkcurrent:
862 if inactive and mark == repo._bookmarkcurrent:
863 bookmarks.setcurrent(repo, None)
863 bookmarks.setcurrent(repo, None)
864 return
864 return
865 tgt = cur
865 tgt = cur
866 if rev:
866 if rev:
867 tgt = scmutil.revsingle(repo, rev).node()
867 tgt = scmutil.revsingle(repo, rev).node()
868 checkconflict(repo, mark, force, tgt)
868 checkconflict(repo, mark, force, tgt)
869 marks[mark] = tgt
869 marks[mark] = tgt
870 if not inactive and cur == marks[mark]:
870 if not inactive and cur == marks[mark]:
871 bookmarks.setcurrent(repo, mark)
871 bookmarks.setcurrent(repo, mark)
872 elif cur != tgt and mark == repo._bookmarkcurrent:
872 elif cur != tgt and mark == repo._bookmarkcurrent:
873 bookmarks.setcurrent(repo, None)
873 bookmarks.setcurrent(repo, None)
874 marks.write()
874 marks.write()
875
875
876 # Same message whether trying to deactivate the current bookmark (-i
876 # Same message whether trying to deactivate the current bookmark (-i
877 # with no NAME) or listing bookmarks
877 # with no NAME) or listing bookmarks
878 elif len(marks) == 0:
878 elif len(marks) == 0:
879 ui.status(_("no bookmarks set\n"))
879 ui.status(_("no bookmarks set\n"))
880
880
881 elif inactive:
881 elif inactive:
882 if not repo._bookmarkcurrent:
882 if not repo._bookmarkcurrent:
883 ui.status(_("no active bookmark\n"))
883 ui.status(_("no active bookmark\n"))
884 else:
884 else:
885 bookmarks.setcurrent(repo, None)
885 bookmarks.setcurrent(repo, None)
886
886
887 else: # show bookmarks
887 else: # show bookmarks
888 for bmark, n in sorted(marks.iteritems()):
888 for bmark, n in sorted(marks.iteritems()):
889 current = repo._bookmarkcurrent
889 current = repo._bookmarkcurrent
890 if bmark == current:
890 if bmark == current:
891 prefix, label = '*', 'bookmarks.current'
891 prefix, label = '*', 'bookmarks.current'
892 else:
892 else:
893 prefix, label = ' ', ''
893 prefix, label = ' ', ''
894
894
895 if ui.quiet:
895 if ui.quiet:
896 ui.write("%s\n" % bmark, label=label)
896 ui.write("%s\n" % bmark, label=label)
897 else:
897 else:
898 ui.write(" %s %-25s %d:%s\n" % (
898 ui.write(" %s %-25s %d:%s\n" % (
899 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
899 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
900 label=label)
900 label=label)
901
901
902 @command('branch',
902 @command('branch',
903 [('f', 'force', None,
903 [('f', 'force', None,
904 _('set branch name even if it shadows an existing branch')),
904 _('set branch name even if it shadows an existing branch')),
905 ('C', 'clean', None, _('reset branch name to parent branch name'))],
905 ('C', 'clean', None, _('reset branch name to parent branch name'))],
906 _('[-fC] [NAME]'))
906 _('[-fC] [NAME]'))
907 def branch(ui, repo, label=None, **opts):
907 def branch(ui, repo, label=None, **opts):
908 """set or show the current branch name
908 """set or show the current branch name
909
909
910 .. note::
910 .. note::
911 Branch names are permanent and global. Use :hg:`bookmark` to create a
911 Branch names are permanent and global. Use :hg:`bookmark` to create a
912 light-weight bookmark instead. See :hg:`help glossary` for more
912 light-weight bookmark instead. See :hg:`help glossary` for more
913 information about named branches and bookmarks.
913 information about named branches and bookmarks.
914
914
915 With no argument, show the current branch name. With one argument,
915 With no argument, show the current branch name. With one argument,
916 set the working directory branch name (the branch will not exist
916 set the working directory branch name (the branch will not exist
917 in the repository until the next commit). Standard practice
917 in the repository until the next commit). Standard practice
918 recommends that primary development take place on the 'default'
918 recommends that primary development take place on the 'default'
919 branch.
919 branch.
920
920
921 Unless -f/--force is specified, branch will not let you set a
921 Unless -f/--force is specified, branch will not let you set a
922 branch name that already exists, even if it's inactive.
922 branch name that already exists, even if it's inactive.
923
923
924 Use -C/--clean to reset the working directory branch to that of
924 Use -C/--clean to reset the working directory branch to that of
925 the parent of the working directory, negating a previous branch
925 the parent of the working directory, negating a previous branch
926 change.
926 change.
927
927
928 Use the command :hg:`update` to switch to an existing branch. Use
928 Use the command :hg:`update` to switch to an existing branch. Use
929 :hg:`commit --close-branch` to mark this branch as closed.
929 :hg:`commit --close-branch` to mark this branch as closed.
930
930
931 Returns 0 on success.
931 Returns 0 on success.
932 """
932 """
933 if not opts.get('clean') and not label:
933 if not opts.get('clean') and not label:
934 ui.write("%s\n" % repo.dirstate.branch())
934 ui.write("%s\n" % repo.dirstate.branch())
935 return
935 return
936
936
937 wlock = repo.wlock()
937 wlock = repo.wlock()
938 try:
938 try:
939 if opts.get('clean'):
939 if opts.get('clean'):
940 label = repo[None].p1().branch()
940 label = repo[None].p1().branch()
941 repo.dirstate.setbranch(label)
941 repo.dirstate.setbranch(label)
942 ui.status(_('reset working directory to branch %s\n') % label)
942 ui.status(_('reset working directory to branch %s\n') % label)
943 elif label:
943 elif label:
944 if not opts.get('force') and label in repo.branchmap():
944 if not opts.get('force') and label in repo.branchmap():
945 if label not in [p.branch() for p in repo.parents()]:
945 if label not in [p.branch() for p in repo.parents()]:
946 raise util.Abort(_('a branch of the same name already'
946 raise util.Abort(_('a branch of the same name already'
947 ' exists'),
947 ' exists'),
948 # i18n: "it" refers to an existing branch
948 # i18n: "it" refers to an existing branch
949 hint=_("use 'hg update' to switch to it"))
949 hint=_("use 'hg update' to switch to it"))
950 scmutil.checknewlabel(repo, label, 'branch')
950 scmutil.checknewlabel(repo, label, 'branch')
951 repo.dirstate.setbranch(label)
951 repo.dirstate.setbranch(label)
952 ui.status(_('marked working directory as branch %s\n') % label)
952 ui.status(_('marked working directory as branch %s\n') % label)
953 ui.status(_('(branches are permanent and global, '
953 ui.status(_('(branches are permanent and global, '
954 'did you want a bookmark?)\n'))
954 'did you want a bookmark?)\n'))
955 finally:
955 finally:
956 wlock.release()
956 wlock.release()
957
957
958 @command('branches',
958 @command('branches',
959 [('a', 'active', False, _('show only branches that have unmerged heads')),
959 [('a', 'active', False, _('show only branches that have unmerged heads')),
960 ('c', 'closed', False, _('show normal and closed branches'))],
960 ('c', 'closed', False, _('show normal and closed branches'))],
961 _('[-ac]'))
961 _('[-ac]'))
962 def branches(ui, repo, active=False, closed=False):
962 def branches(ui, repo, active=False, closed=False):
963 """list repository named branches
963 """list repository named branches
964
964
965 List the repository's named branches, indicating which ones are
965 List the repository's named branches, indicating which ones are
966 inactive. If -c/--closed is specified, also list branches which have
966 inactive. If -c/--closed is specified, also list branches which have
967 been marked closed (see :hg:`commit --close-branch`).
967 been marked closed (see :hg:`commit --close-branch`).
968
968
969 If -a/--active is specified, only show active branches. A branch
969 If -a/--active is specified, only show active branches. A branch
970 is considered active if it contains repository heads.
970 is considered active if it contains repository heads.
971
971
972 Use the command :hg:`update` to switch to an existing branch.
972 Use the command :hg:`update` to switch to an existing branch.
973
973
974 Returns 0.
974 Returns 0.
975 """
975 """
976
976
977 hexfunc = ui.debugflag and hex or short
977 hexfunc = ui.debugflag and hex or short
978
978
979 activebranches = set([repo[n].branch() for n in repo.heads()])
979 activebranches = set([repo[n].branch() for n in repo.heads()])
980 branches = []
980 branches = []
981 for tag, heads in repo.branchmap().iteritems():
981 for tag, heads in repo.branchmap().iteritems():
982 for h in reversed(heads):
982 for h in reversed(heads):
983 ctx = repo[h]
983 ctx = repo[h]
984 isopen = not ctx.closesbranch()
984 isopen = not ctx.closesbranch()
985 if isopen:
985 if isopen:
986 tip = ctx
986 tip = ctx
987 break
987 break
988 else:
988 else:
989 tip = repo[heads[-1]]
989 tip = repo[heads[-1]]
990 isactive = tag in activebranches and isopen
990 isactive = tag in activebranches and isopen
991 branches.append((tip, isactive, isopen))
991 branches.append((tip, isactive, isopen))
992 branches.sort(key=lambda i: (i[1], i[0].rev(), i[0].branch(), i[2]),
992 branches.sort(key=lambda i: (i[1], i[0].rev(), i[0].branch(), i[2]),
993 reverse=True)
993 reverse=True)
994
994
995 for ctx, isactive, isopen in branches:
995 for ctx, isactive, isopen in branches:
996 if (not active) or isactive:
996 if (not active) or isactive:
997 if isactive:
997 if isactive:
998 label = 'branches.active'
998 label = 'branches.active'
999 notice = ''
999 notice = ''
1000 elif not isopen:
1000 elif not isopen:
1001 if not closed:
1001 if not closed:
1002 continue
1002 continue
1003 label = 'branches.closed'
1003 label = 'branches.closed'
1004 notice = _(' (closed)')
1004 notice = _(' (closed)')
1005 else:
1005 else:
1006 label = 'branches.inactive'
1006 label = 'branches.inactive'
1007 notice = _(' (inactive)')
1007 notice = _(' (inactive)')
1008 if ctx.branch() == repo.dirstate.branch():
1008 if ctx.branch() == repo.dirstate.branch():
1009 label = 'branches.current'
1009 label = 'branches.current'
1010 rev = str(ctx.rev()).rjust(31 - encoding.colwidth(ctx.branch()))
1010 rev = str(ctx.rev()).rjust(31 - encoding.colwidth(ctx.branch()))
1011 rev = ui.label('%s:%s' % (rev, hexfunc(ctx.node())),
1011 rev = ui.label('%s:%s' % (rev, hexfunc(ctx.node())),
1012 'log.changeset changeset.%s' % ctx.phasestr())
1012 'log.changeset changeset.%s' % ctx.phasestr())
1013 tag = ui.label(ctx.branch(), label)
1013 tag = ui.label(ctx.branch(), label)
1014 if ui.quiet:
1014 if ui.quiet:
1015 ui.write("%s\n" % tag)
1015 ui.write("%s\n" % tag)
1016 else:
1016 else:
1017 ui.write("%s %s%s\n" % (tag, rev, notice))
1017 ui.write("%s %s%s\n" % (tag, rev, notice))
1018
1018
1019 @command('bundle',
1019 @command('bundle',
1020 [('f', 'force', None, _('run even when the destination is unrelated')),
1020 [('f', 'force', None, _('run even when the destination is unrelated')),
1021 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1021 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1022 _('REV')),
1022 _('REV')),
1023 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1023 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1024 _('BRANCH')),
1024 _('BRANCH')),
1025 ('', 'base', [],
1025 ('', 'base', [],
1026 _('a base changeset assumed to be available at the destination'),
1026 _('a base changeset assumed to be available at the destination'),
1027 _('REV')),
1027 _('REV')),
1028 ('a', 'all', None, _('bundle all changesets in the repository')),
1028 ('a', 'all', None, _('bundle all changesets in the repository')),
1029 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1029 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1030 ] + remoteopts,
1030 ] + remoteopts,
1031 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1031 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1032 def bundle(ui, repo, fname, dest=None, **opts):
1032 def bundle(ui, repo, fname, dest=None, **opts):
1033 """create a changegroup file
1033 """create a changegroup file
1034
1034
1035 Generate a compressed changegroup file collecting changesets not
1035 Generate a compressed changegroup file collecting changesets not
1036 known to be in another repository.
1036 known to be in another repository.
1037
1037
1038 If you omit the destination repository, then hg assumes the
1038 If you omit the destination repository, then hg assumes the
1039 destination will have all the nodes you specify with --base
1039 destination will have all the nodes you specify with --base
1040 parameters. To create a bundle containing all changesets, use
1040 parameters. To create a bundle containing all changesets, use
1041 -a/--all (or --base null).
1041 -a/--all (or --base null).
1042
1042
1043 You can change compression method with the -t/--type option.
1043 You can change compression method with the -t/--type option.
1044 The available compression methods are: none, bzip2, and
1044 The available compression methods are: none, bzip2, and
1045 gzip (by default, bundles are compressed using bzip2).
1045 gzip (by default, bundles are compressed using bzip2).
1046
1046
1047 The bundle file can then be transferred using conventional means
1047 The bundle file can then be transferred using conventional means
1048 and applied to another repository with the unbundle or pull
1048 and applied to another repository with the unbundle or pull
1049 command. This is useful when direct push and pull are not
1049 command. This is useful when direct push and pull are not
1050 available or when exporting an entire repository is undesirable.
1050 available or when exporting an entire repository is undesirable.
1051
1051
1052 Applying bundles preserves all changeset contents including
1052 Applying bundles preserves all changeset contents including
1053 permissions, copy/rename information, and revision history.
1053 permissions, copy/rename information, and revision history.
1054
1054
1055 Returns 0 on success, 1 if no changes found.
1055 Returns 0 on success, 1 if no changes found.
1056 """
1056 """
1057 revs = None
1057 revs = None
1058 if 'rev' in opts:
1058 if 'rev' in opts:
1059 revs = scmutil.revrange(repo, opts['rev'])
1059 revs = scmutil.revrange(repo, opts['rev'])
1060
1060
1061 bundletype = opts.get('type', 'bzip2').lower()
1061 bundletype = opts.get('type', 'bzip2').lower()
1062 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1062 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1063 bundletype = btypes.get(bundletype)
1063 bundletype = btypes.get(bundletype)
1064 if bundletype not in changegroup.bundletypes:
1064 if bundletype not in changegroup.bundletypes:
1065 raise util.Abort(_('unknown bundle type specified with --type'))
1065 raise util.Abort(_('unknown bundle type specified with --type'))
1066
1066
1067 if opts.get('all'):
1067 if opts.get('all'):
1068 base = ['null']
1068 base = ['null']
1069 else:
1069 else:
1070 base = scmutil.revrange(repo, opts.get('base'))
1070 base = scmutil.revrange(repo, opts.get('base'))
1071 if base:
1071 if base:
1072 if dest:
1072 if dest:
1073 raise util.Abort(_("--base is incompatible with specifying "
1073 raise util.Abort(_("--base is incompatible with specifying "
1074 "a destination"))
1074 "a destination"))
1075 common = [repo.lookup(rev) for rev in base]
1075 common = [repo.lookup(rev) for rev in base]
1076 heads = revs and map(repo.lookup, revs) or revs
1076 heads = revs and map(repo.lookup, revs) or revs
1077 cg = repo.getbundle('bundle', heads=heads, common=common)
1077 cg = repo.getbundle('bundle', heads=heads, common=common)
1078 outgoing = None
1078 outgoing = None
1079 else:
1079 else:
1080 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1080 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1081 dest, branches = hg.parseurl(dest, opts.get('branch'))
1081 dest, branches = hg.parseurl(dest, opts.get('branch'))
1082 other = hg.peer(repo, opts, dest)
1082 other = hg.peer(repo, opts, dest)
1083 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1083 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1084 heads = revs and map(repo.lookup, revs) or revs
1084 heads = revs and map(repo.lookup, revs) or revs
1085 outgoing = discovery.findcommonoutgoing(repo, other,
1085 outgoing = discovery.findcommonoutgoing(repo, other,
1086 onlyheads=heads,
1086 onlyheads=heads,
1087 force=opts.get('force'),
1087 force=opts.get('force'),
1088 portable=True)
1088 portable=True)
1089 cg = repo.getlocalbundle('bundle', outgoing)
1089 cg = repo.getlocalbundle('bundle', outgoing)
1090 if not cg:
1090 if not cg:
1091 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1091 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1092 return 1
1092 return 1
1093
1093
1094 changegroup.writebundle(cg, fname, bundletype)
1094 changegroup.writebundle(cg, fname, bundletype)
1095
1095
1096 @command('cat',
1096 @command('cat',
1097 [('o', 'output', '',
1097 [('o', 'output', '',
1098 _('print output to file with formatted name'), _('FORMAT')),
1098 _('print output to file with formatted name'), _('FORMAT')),
1099 ('r', 'rev', '', _('print the given revision'), _('REV')),
1099 ('r', 'rev', '', _('print the given revision'), _('REV')),
1100 ('', 'decode', None, _('apply any matching decode filter')),
1100 ('', 'decode', None, _('apply any matching decode filter')),
1101 ] + walkopts,
1101 ] + walkopts,
1102 _('[OPTION]... FILE...'))
1102 _('[OPTION]... FILE...'))
1103 def cat(ui, repo, file1, *pats, **opts):
1103 def cat(ui, repo, file1, *pats, **opts):
1104 """output the current or given revision of files
1104 """output the current or given revision of files
1105
1105
1106 Print the specified files as they were at the given revision. If
1106 Print the specified files as they were at the given revision. If
1107 no revision is given, the parent of the working directory is used,
1107 no revision is given, the parent of the working directory is used,
1108 or tip if no revision is checked out.
1108 or tip if no revision is checked out.
1109
1109
1110 Output may be to a file, in which case the name of the file is
1110 Output may be to a file, in which case the name of the file is
1111 given using a format string. The formatting rules are the same as
1111 given using a format string. The formatting rules are the same as
1112 for the export command, with the following additions:
1112 for the export command, with the following additions:
1113
1113
1114 :``%s``: basename of file being printed
1114 :``%s``: basename of file being printed
1115 :``%d``: dirname of file being printed, or '.' if in repository root
1115 :``%d``: dirname of file being printed, or '.' if in repository root
1116 :``%p``: root-relative path name of file being printed
1116 :``%p``: root-relative path name of file being printed
1117
1117
1118 Returns 0 on success.
1118 Returns 0 on success.
1119 """
1119 """
1120 ctx = scmutil.revsingle(repo, opts.get('rev'))
1120 ctx = scmutil.revsingle(repo, opts.get('rev'))
1121 err = 1
1121 err = 1
1122 m = scmutil.match(ctx, (file1,) + pats, opts)
1122 m = scmutil.match(ctx, (file1,) + pats, opts)
1123 for abs in ctx.walk(m):
1123 for abs in ctx.walk(m):
1124 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1124 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1125 pathname=abs)
1125 pathname=abs)
1126 data = ctx[abs].data()
1126 data = ctx[abs].data()
1127 if opts.get('decode'):
1127 if opts.get('decode'):
1128 data = repo.wwritedata(abs, data)
1128 data = repo.wwritedata(abs, data)
1129 fp.write(data)
1129 fp.write(data)
1130 fp.close()
1130 fp.close()
1131 err = 0
1131 err = 0
1132 return err
1132 return err
1133
1133
1134 @command('^clone',
1134 @command('^clone',
1135 [('U', 'noupdate', None,
1135 [('U', 'noupdate', None,
1136 _('the clone will include an empty working copy (only a repository)')),
1136 _('the clone will include an empty working copy (only a repository)')),
1137 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1137 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1138 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1138 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1139 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1139 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1140 ('', 'pull', None, _('use pull protocol to copy metadata')),
1140 ('', 'pull', None, _('use pull protocol to copy metadata')),
1141 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1141 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1142 ] + remoteopts,
1142 ] + remoteopts,
1143 _('[OPTION]... SOURCE [DEST]'))
1143 _('[OPTION]... SOURCE [DEST]'))
1144 def clone(ui, source, dest=None, **opts):
1144 def clone(ui, source, dest=None, **opts):
1145 """make a copy of an existing repository
1145 """make a copy of an existing repository
1146
1146
1147 Create a copy of an existing repository in a new directory.
1147 Create a copy of an existing repository in a new directory.
1148
1148
1149 If no destination directory name is specified, it defaults to the
1149 If no destination directory name is specified, it defaults to the
1150 basename of the source.
1150 basename of the source.
1151
1151
1152 The location of the source is added to the new repository's
1152 The location of the source is added to the new repository's
1153 ``.hg/hgrc`` file, as the default to be used for future pulls.
1153 ``.hg/hgrc`` file, as the default to be used for future pulls.
1154
1154
1155 Only local paths and ``ssh://`` URLs are supported as
1155 Only local paths and ``ssh://`` URLs are supported as
1156 destinations. For ``ssh://`` destinations, no working directory or
1156 destinations. For ``ssh://`` destinations, no working directory or
1157 ``.hg/hgrc`` will be created on the remote side.
1157 ``.hg/hgrc`` will be created on the remote side.
1158
1158
1159 To pull only a subset of changesets, specify one or more revisions
1159 To pull only a subset of changesets, specify one or more revisions
1160 identifiers with -r/--rev or branches with -b/--branch. The
1160 identifiers with -r/--rev or branches with -b/--branch. The
1161 resulting clone will contain only the specified changesets and
1161 resulting clone will contain only the specified changesets and
1162 their ancestors. These options (or 'clone src#rev dest') imply
1162 their ancestors. These options (or 'clone src#rev dest') imply
1163 --pull, even for local source repositories. Note that specifying a
1163 --pull, even for local source repositories. Note that specifying a
1164 tag will include the tagged changeset but not the changeset
1164 tag will include the tagged changeset but not the changeset
1165 containing the tag.
1165 containing the tag.
1166
1166
1167 If the source repository has a bookmark called '@' set, that
1167 If the source repository has a bookmark called '@' set, that
1168 revision will be checked out in the new repository by default.
1168 revision will be checked out in the new repository by default.
1169
1169
1170 To check out a particular version, use -u/--update, or
1170 To check out a particular version, use -u/--update, or
1171 -U/--noupdate to create a clone with no working directory.
1171 -U/--noupdate to create a clone with no working directory.
1172
1172
1173 .. container:: verbose
1173 .. container:: verbose
1174
1174
1175 For efficiency, hardlinks are used for cloning whenever the
1175 For efficiency, hardlinks are used for cloning whenever the
1176 source and destination are on the same filesystem (note this
1176 source and destination are on the same filesystem (note this
1177 applies only to the repository data, not to the working
1177 applies only to the repository data, not to the working
1178 directory). Some filesystems, such as AFS, implement hardlinking
1178 directory). Some filesystems, such as AFS, implement hardlinking
1179 incorrectly, but do not report errors. In these cases, use the
1179 incorrectly, but do not report errors. In these cases, use the
1180 --pull option to avoid hardlinking.
1180 --pull option to avoid hardlinking.
1181
1181
1182 In some cases, you can clone repositories and the working
1182 In some cases, you can clone repositories and the working
1183 directory using full hardlinks with ::
1183 directory using full hardlinks with ::
1184
1184
1185 $ cp -al REPO REPOCLONE
1185 $ cp -al REPO REPOCLONE
1186
1186
1187 This is the fastest way to clone, but it is not always safe. The
1187 This is the fastest way to clone, but it is not always safe. The
1188 operation is not atomic (making sure REPO is not modified during
1188 operation is not atomic (making sure REPO is not modified during
1189 the operation is up to you) and you have to make sure your
1189 the operation is up to you) and you have to make sure your
1190 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1190 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1191 so). Also, this is not compatible with certain extensions that
1191 so). Also, this is not compatible with certain extensions that
1192 place their metadata under the .hg directory, such as mq.
1192 place their metadata under the .hg directory, such as mq.
1193
1193
1194 Mercurial will update the working directory to the first applicable
1194 Mercurial will update the working directory to the first applicable
1195 revision from this list:
1195 revision from this list:
1196
1196
1197 a) null if -U or the source repository has no changesets
1197 a) null if -U or the source repository has no changesets
1198 b) if -u . and the source repository is local, the first parent of
1198 b) if -u . and the source repository is local, the first parent of
1199 the source repository's working directory
1199 the source repository's working directory
1200 c) the changeset specified with -u (if a branch name, this means the
1200 c) the changeset specified with -u (if a branch name, this means the
1201 latest head of that branch)
1201 latest head of that branch)
1202 d) the changeset specified with -r
1202 d) the changeset specified with -r
1203 e) the tipmost head specified with -b
1203 e) the tipmost head specified with -b
1204 f) the tipmost head specified with the url#branch source syntax
1204 f) the tipmost head specified with the url#branch source syntax
1205 g) the revision marked with the '@' bookmark, if present
1205 g) the revision marked with the '@' bookmark, if present
1206 h) the tipmost head of the default branch
1206 h) the tipmost head of the default branch
1207 i) tip
1207 i) tip
1208
1208
1209 Examples:
1209 Examples:
1210
1210
1211 - clone a remote repository to a new directory named hg/::
1211 - clone a remote repository to a new directory named hg/::
1212
1212
1213 hg clone http://selenic.com/hg
1213 hg clone http://selenic.com/hg
1214
1214
1215 - create a lightweight local clone::
1215 - create a lightweight local clone::
1216
1216
1217 hg clone project/ project-feature/
1217 hg clone project/ project-feature/
1218
1218
1219 - clone from an absolute path on an ssh server (note double-slash)::
1219 - clone from an absolute path on an ssh server (note double-slash)::
1220
1220
1221 hg clone ssh://user@server//home/projects/alpha/
1221 hg clone ssh://user@server//home/projects/alpha/
1222
1222
1223 - do a high-speed clone over a LAN while checking out a
1223 - do a high-speed clone over a LAN while checking out a
1224 specified version::
1224 specified version::
1225
1225
1226 hg clone --uncompressed http://server/repo -u 1.5
1226 hg clone --uncompressed http://server/repo -u 1.5
1227
1227
1228 - create a repository without changesets after a particular revision::
1228 - create a repository without changesets after a particular revision::
1229
1229
1230 hg clone -r 04e544 experimental/ good/
1230 hg clone -r 04e544 experimental/ good/
1231
1231
1232 - clone (and track) a particular named branch::
1232 - clone (and track) a particular named branch::
1233
1233
1234 hg clone http://selenic.com/hg#stable
1234 hg clone http://selenic.com/hg#stable
1235
1235
1236 See :hg:`help urls` for details on specifying URLs.
1236 See :hg:`help urls` for details on specifying URLs.
1237
1237
1238 Returns 0 on success.
1238 Returns 0 on success.
1239 """
1239 """
1240 if opts.get('noupdate') and opts.get('updaterev'):
1240 if opts.get('noupdate') and opts.get('updaterev'):
1241 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1241 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1242
1242
1243 r = hg.clone(ui, opts, source, dest,
1243 r = hg.clone(ui, opts, source, dest,
1244 pull=opts.get('pull'),
1244 pull=opts.get('pull'),
1245 stream=opts.get('uncompressed'),
1245 stream=opts.get('uncompressed'),
1246 rev=opts.get('rev'),
1246 rev=opts.get('rev'),
1247 update=opts.get('updaterev') or not opts.get('noupdate'),
1247 update=opts.get('updaterev') or not opts.get('noupdate'),
1248 branch=opts.get('branch'))
1248 branch=opts.get('branch'))
1249
1249
1250 return r is None
1250 return r is None
1251
1251
1252 @command('^commit|ci',
1252 @command('^commit|ci',
1253 [('A', 'addremove', None,
1253 [('A', 'addremove', None,
1254 _('mark new/missing files as added/removed before committing')),
1254 _('mark new/missing files as added/removed before committing')),
1255 ('', 'close-branch', None,
1255 ('', 'close-branch', None,
1256 _('mark a branch as closed, hiding it from the branch list')),
1256 _('mark a branch as closed, hiding it from the branch list')),
1257 ('', 'amend', None, _('amend the parent of the working dir')),
1257 ('', 'amend', None, _('amend the parent of the working dir')),
1258 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1258 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1259 _('[OPTION]... [FILE]...'))
1259 _('[OPTION]... [FILE]...'))
1260 def commit(ui, repo, *pats, **opts):
1260 def commit(ui, repo, *pats, **opts):
1261 """commit the specified files or all outstanding changes
1261 """commit the specified files or all outstanding changes
1262
1262
1263 Commit changes to the given files into the repository. Unlike a
1263 Commit changes to the given files into the repository. Unlike a
1264 centralized SCM, this operation is a local operation. See
1264 centralized SCM, this operation is a local operation. See
1265 :hg:`push` for a way to actively distribute your changes.
1265 :hg:`push` for a way to actively distribute your changes.
1266
1266
1267 If a list of files is omitted, all changes reported by :hg:`status`
1267 If a list of files is omitted, all changes reported by :hg:`status`
1268 will be committed.
1268 will be committed.
1269
1269
1270 If you are committing the result of a merge, do not provide any
1270 If you are committing the result of a merge, do not provide any
1271 filenames or -I/-X filters.
1271 filenames or -I/-X filters.
1272
1272
1273 If no commit message is specified, Mercurial starts your
1273 If no commit message is specified, Mercurial starts your
1274 configured editor where you can enter a message. In case your
1274 configured editor where you can enter a message. In case your
1275 commit fails, you will find a backup of your message in
1275 commit fails, you will find a backup of your message in
1276 ``.hg/last-message.txt``.
1276 ``.hg/last-message.txt``.
1277
1277
1278 The --amend flag can be used to amend the parent of the
1278 The --amend flag can be used to amend the parent of the
1279 working directory with a new commit that contains the changes
1279 working directory with a new commit that contains the changes
1280 in the parent in addition to those currently reported by :hg:`status`,
1280 in the parent in addition to those currently reported by :hg:`status`,
1281 if there are any. The old commit is stored in a backup bundle in
1281 if there are any. The old commit is stored in a backup bundle in
1282 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1282 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1283 on how to restore it).
1283 on how to restore it).
1284
1284
1285 Message, user and date are taken from the amended commit unless
1285 Message, user and date are taken from the amended commit unless
1286 specified. When a message isn't specified on the command line,
1286 specified. When a message isn't specified on the command line,
1287 the editor will open with the message of the amended commit.
1287 the editor will open with the message of the amended commit.
1288
1288
1289 It is not possible to amend public changesets (see :hg:`help phases`)
1289 It is not possible to amend public changesets (see :hg:`help phases`)
1290 or changesets that have children.
1290 or changesets that have children.
1291
1291
1292 See :hg:`help dates` for a list of formats valid for -d/--date.
1292 See :hg:`help dates` for a list of formats valid for -d/--date.
1293
1293
1294 Returns 0 on success, 1 if nothing changed.
1294 Returns 0 on success, 1 if nothing changed.
1295 """
1295 """
1296 if opts.get('subrepos'):
1296 if opts.get('subrepos'):
1297 # Let --subrepos on the command line override config setting.
1297 # Let --subrepos on the command line override config setting.
1298 ui.setconfig('ui', 'commitsubrepos', True)
1298 ui.setconfig('ui', 'commitsubrepos', True)
1299
1299
1300 extra = {}
1300 extra = {}
1301 if opts.get('close_branch'):
1301 if opts.get('close_branch'):
1302 if repo['.'].node() not in repo.branchheads():
1302 if repo['.'].node() not in repo.branchheads():
1303 # The topo heads set is included in the branch heads set of the
1303 # The topo heads set is included in the branch heads set of the
1304 # current branch, so it's sufficient to test branchheads
1304 # current branch, so it's sufficient to test branchheads
1305 raise util.Abort(_('can only close branch heads'))
1305 raise util.Abort(_('can only close branch heads'))
1306 extra['close'] = 1
1306 extra['close'] = 1
1307
1307
1308 branch = repo[None].branch()
1308 branch = repo[None].branch()
1309 bheads = repo.branchheads(branch)
1309 bheads = repo.branchheads(branch)
1310
1310
1311 if opts.get('amend'):
1311 if opts.get('amend'):
1312 if ui.configbool('ui', 'commitsubrepos'):
1312 if ui.configbool('ui', 'commitsubrepos'):
1313 raise util.Abort(_('cannot amend recursively'))
1313 raise util.Abort(_('cannot amend recursively'))
1314
1314
1315 old = repo['.']
1315 old = repo['.']
1316 if old.phase() == phases.public:
1316 if old.phase() == phases.public:
1317 raise util.Abort(_('cannot amend public changesets'))
1317 raise util.Abort(_('cannot amend public changesets'))
1318 if len(old.parents()) > 1:
1318 if len(old.parents()) > 1:
1319 raise util.Abort(_('cannot amend merge changesets'))
1319 raise util.Abort(_('cannot amend merge changesets'))
1320 if len(repo[None].parents()) > 1:
1320 if len(repo[None].parents()) > 1:
1321 raise util.Abort(_('cannot amend while merging'))
1321 raise util.Abort(_('cannot amend while merging'))
1322 if (not obsolete._enabled) and old.children():
1322 if (not obsolete._enabled) and old.children():
1323 raise util.Abort(_('cannot amend changeset with children'))
1323 raise util.Abort(_('cannot amend changeset with children'))
1324
1324
1325 e = cmdutil.commiteditor
1325 e = cmdutil.commiteditor
1326 if opts.get('force_editor'):
1326 if opts.get('force_editor'):
1327 e = cmdutil.commitforceeditor
1327 e = cmdutil.commitforceeditor
1328
1328
1329 def commitfunc(ui, repo, message, match, opts):
1329 def commitfunc(ui, repo, message, match, opts):
1330 editor = e
1330 editor = e
1331 # message contains text from -m or -l, if it's empty,
1331 # message contains text from -m or -l, if it's empty,
1332 # open the editor with the old message
1332 # open the editor with the old message
1333 if not message:
1333 if not message:
1334 message = old.description()
1334 message = old.description()
1335 editor = cmdutil.commitforceeditor
1335 editor = cmdutil.commitforceeditor
1336 return repo.commit(message,
1336 return repo.commit(message,
1337 opts.get('user') or old.user(),
1337 opts.get('user') or old.user(),
1338 opts.get('date') or old.date(),
1338 opts.get('date') or old.date(),
1339 match,
1339 match,
1340 editor=editor,
1340 editor=editor,
1341 extra=extra)
1341 extra=extra)
1342
1342
1343 current = repo._bookmarkcurrent
1343 current = repo._bookmarkcurrent
1344 marks = old.bookmarks()
1344 marks = old.bookmarks()
1345 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1345 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1346 if node == old.node():
1346 if node == old.node():
1347 ui.status(_("nothing changed\n"))
1347 ui.status(_("nothing changed\n"))
1348 return 1
1348 return 1
1349 elif marks:
1349 elif marks:
1350 ui.debug('moving bookmarks %r from %s to %s\n' %
1350 ui.debug('moving bookmarks %r from %s to %s\n' %
1351 (marks, old.hex(), hex(node)))
1351 (marks, old.hex(), hex(node)))
1352 newmarks = repo._bookmarks
1352 newmarks = repo._bookmarks
1353 for bm in marks:
1353 for bm in marks:
1354 newmarks[bm] = node
1354 newmarks[bm] = node
1355 if bm == current:
1355 if bm == current:
1356 bookmarks.setcurrent(repo, bm)
1356 bookmarks.setcurrent(repo, bm)
1357 newmarks.write()
1357 newmarks.write()
1358 else:
1358 else:
1359 e = cmdutil.commiteditor
1359 e = cmdutil.commiteditor
1360 if opts.get('force_editor'):
1360 if opts.get('force_editor'):
1361 e = cmdutil.commitforceeditor
1361 e = cmdutil.commitforceeditor
1362
1362
1363 def commitfunc(ui, repo, message, match, opts):
1363 def commitfunc(ui, repo, message, match, opts):
1364 return repo.commit(message, opts.get('user'), opts.get('date'),
1364 return repo.commit(message, opts.get('user'), opts.get('date'),
1365 match, editor=e, extra=extra)
1365 match, editor=e, extra=extra)
1366
1366
1367 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1367 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1368
1368
1369 if not node:
1369 if not node:
1370 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1370 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1371 if stat[3]:
1371 if stat[3]:
1372 ui.status(_("nothing changed (%d missing files, see "
1372 ui.status(_("nothing changed (%d missing files, see "
1373 "'hg status')\n") % len(stat[3]))
1373 "'hg status')\n") % len(stat[3]))
1374 else:
1374 else:
1375 ui.status(_("nothing changed\n"))
1375 ui.status(_("nothing changed\n"))
1376 return 1
1376 return 1
1377
1377
1378 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1378 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1379
1379
1380 @command('copy|cp',
1380 @command('copy|cp',
1381 [('A', 'after', None, _('record a copy that has already occurred')),
1381 [('A', 'after', None, _('record a copy that has already occurred')),
1382 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1382 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1383 ] + walkopts + dryrunopts,
1383 ] + walkopts + dryrunopts,
1384 _('[OPTION]... [SOURCE]... DEST'))
1384 _('[OPTION]... [SOURCE]... DEST'))
1385 def copy(ui, repo, *pats, **opts):
1385 def copy(ui, repo, *pats, **opts):
1386 """mark files as copied for the next commit
1386 """mark files as copied for the next commit
1387
1387
1388 Mark dest as having copies of source files. If dest is a
1388 Mark dest as having copies of source files. If dest is a
1389 directory, copies are put in that directory. If dest is a file,
1389 directory, copies are put in that directory. If dest is a file,
1390 the source must be a single file.
1390 the source must be a single file.
1391
1391
1392 By default, this command copies the contents of files as they
1392 By default, this command copies the contents of files as they
1393 exist in the working directory. If invoked with -A/--after, the
1393 exist in the working directory. If invoked with -A/--after, the
1394 operation is recorded, but no copying is performed.
1394 operation is recorded, but no copying is performed.
1395
1395
1396 This command takes effect with the next commit. To undo a copy
1396 This command takes effect with the next commit. To undo a copy
1397 before that, see :hg:`revert`.
1397 before that, see :hg:`revert`.
1398
1398
1399 Returns 0 on success, 1 if errors are encountered.
1399 Returns 0 on success, 1 if errors are encountered.
1400 """
1400 """
1401 wlock = repo.wlock(False)
1401 wlock = repo.wlock(False)
1402 try:
1402 try:
1403 return cmdutil.copy(ui, repo, pats, opts)
1403 return cmdutil.copy(ui, repo, pats, opts)
1404 finally:
1404 finally:
1405 wlock.release()
1405 wlock.release()
1406
1406
1407 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1407 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1408 def debugancestor(ui, repo, *args):
1408 def debugancestor(ui, repo, *args):
1409 """find the ancestor revision of two revisions in a given index"""
1409 """find the ancestor revision of two revisions in a given index"""
1410 if len(args) == 3:
1410 if len(args) == 3:
1411 index, rev1, rev2 = args
1411 index, rev1, rev2 = args
1412 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1412 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1413 lookup = r.lookup
1413 lookup = r.lookup
1414 elif len(args) == 2:
1414 elif len(args) == 2:
1415 if not repo:
1415 if not repo:
1416 raise util.Abort(_("there is no Mercurial repository here "
1416 raise util.Abort(_("there is no Mercurial repository here "
1417 "(.hg not found)"))
1417 "(.hg not found)"))
1418 rev1, rev2 = args
1418 rev1, rev2 = args
1419 r = repo.changelog
1419 r = repo.changelog
1420 lookup = repo.lookup
1420 lookup = repo.lookup
1421 else:
1421 else:
1422 raise util.Abort(_('either two or three arguments required'))
1422 raise util.Abort(_('either two or three arguments required'))
1423 a = r.ancestor(lookup(rev1), lookup(rev2))
1423 a = r.ancestor(lookup(rev1), lookup(rev2))
1424 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1424 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1425
1425
1426 @command('debugbuilddag',
1426 @command('debugbuilddag',
1427 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1427 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1428 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1428 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1429 ('n', 'new-file', None, _('add new file at each rev'))],
1429 ('n', 'new-file', None, _('add new file at each rev'))],
1430 _('[OPTION]... [TEXT]'))
1430 _('[OPTION]... [TEXT]'))
1431 def debugbuilddag(ui, repo, text=None,
1431 def debugbuilddag(ui, repo, text=None,
1432 mergeable_file=False,
1432 mergeable_file=False,
1433 overwritten_file=False,
1433 overwritten_file=False,
1434 new_file=False):
1434 new_file=False):
1435 """builds a repo with a given DAG from scratch in the current empty repo
1435 """builds a repo with a given DAG from scratch in the current empty repo
1436
1436
1437 The description of the DAG is read from stdin if not given on the
1437 The description of the DAG is read from stdin if not given on the
1438 command line.
1438 command line.
1439
1439
1440 Elements:
1440 Elements:
1441
1441
1442 - "+n" is a linear run of n nodes based on the current default parent
1442 - "+n" is a linear run of n nodes based on the current default parent
1443 - "." is a single node based on the current default parent
1443 - "." is a single node based on the current default parent
1444 - "$" resets the default parent to null (implied at the start);
1444 - "$" resets the default parent to null (implied at the start);
1445 otherwise the default parent is always the last node created
1445 otherwise the default parent is always the last node created
1446 - "<p" sets the default parent to the backref p
1446 - "<p" sets the default parent to the backref p
1447 - "*p" is a fork at parent p, which is a backref
1447 - "*p" is a fork at parent p, which is a backref
1448 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1448 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1449 - "/p2" is a merge of the preceding node and p2
1449 - "/p2" is a merge of the preceding node and p2
1450 - ":tag" defines a local tag for the preceding node
1450 - ":tag" defines a local tag for the preceding node
1451 - "@branch" sets the named branch for subsequent nodes
1451 - "@branch" sets the named branch for subsequent nodes
1452 - "#...\\n" is a comment up to the end of the line
1452 - "#...\\n" is a comment up to the end of the line
1453
1453
1454 Whitespace between the above elements is ignored.
1454 Whitespace between the above elements is ignored.
1455
1455
1456 A backref is either
1456 A backref is either
1457
1457
1458 - a number n, which references the node curr-n, where curr is the current
1458 - a number n, which references the node curr-n, where curr is the current
1459 node, or
1459 node, or
1460 - the name of a local tag you placed earlier using ":tag", or
1460 - the name of a local tag you placed earlier using ":tag", or
1461 - empty to denote the default parent.
1461 - empty to denote the default parent.
1462
1462
1463 All string valued-elements are either strictly alphanumeric, or must
1463 All string valued-elements are either strictly alphanumeric, or must
1464 be enclosed in double quotes ("..."), with "\\" as escape character.
1464 be enclosed in double quotes ("..."), with "\\" as escape character.
1465 """
1465 """
1466
1466
1467 if text is None:
1467 if text is None:
1468 ui.status(_("reading DAG from stdin\n"))
1468 ui.status(_("reading DAG from stdin\n"))
1469 text = ui.fin.read()
1469 text = ui.fin.read()
1470
1470
1471 cl = repo.changelog
1471 cl = repo.changelog
1472 if len(cl) > 0:
1472 if len(cl) > 0:
1473 raise util.Abort(_('repository is not empty'))
1473 raise util.Abort(_('repository is not empty'))
1474
1474
1475 # determine number of revs in DAG
1475 # determine number of revs in DAG
1476 total = 0
1476 total = 0
1477 for type, data in dagparser.parsedag(text):
1477 for type, data in dagparser.parsedag(text):
1478 if type == 'n':
1478 if type == 'n':
1479 total += 1
1479 total += 1
1480
1480
1481 if mergeable_file:
1481 if mergeable_file:
1482 linesperrev = 2
1482 linesperrev = 2
1483 # make a file with k lines per rev
1483 # make a file with k lines per rev
1484 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1484 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1485 initialmergedlines.append("")
1485 initialmergedlines.append("")
1486
1486
1487 tags = []
1487 tags = []
1488
1488
1489 lock = tr = None
1489 lock = tr = None
1490 try:
1490 try:
1491 lock = repo.lock()
1491 lock = repo.lock()
1492 tr = repo.transaction("builddag")
1492 tr = repo.transaction("builddag")
1493
1493
1494 at = -1
1494 at = -1
1495 atbranch = 'default'
1495 atbranch = 'default'
1496 nodeids = []
1496 nodeids = []
1497 id = 0
1497 id = 0
1498 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1498 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1499 for type, data in dagparser.parsedag(text):
1499 for type, data in dagparser.parsedag(text):
1500 if type == 'n':
1500 if type == 'n':
1501 ui.note(('node %s\n' % str(data)))
1501 ui.note(('node %s\n' % str(data)))
1502 id, ps = data
1502 id, ps = data
1503
1503
1504 files = []
1504 files = []
1505 fctxs = {}
1505 fctxs = {}
1506
1506
1507 p2 = None
1507 p2 = None
1508 if mergeable_file:
1508 if mergeable_file:
1509 fn = "mf"
1509 fn = "mf"
1510 p1 = repo[ps[0]]
1510 p1 = repo[ps[0]]
1511 if len(ps) > 1:
1511 if len(ps) > 1:
1512 p2 = repo[ps[1]]
1512 p2 = repo[ps[1]]
1513 pa = p1.ancestor(p2)
1513 pa = p1.ancestor(p2)
1514 base, local, other = [x[fn].data() for x in (pa, p1,
1514 base, local, other = [x[fn].data() for x in (pa, p1,
1515 p2)]
1515 p2)]
1516 m3 = simplemerge.Merge3Text(base, local, other)
1516 m3 = simplemerge.Merge3Text(base, local, other)
1517 ml = [l.strip() for l in m3.merge_lines()]
1517 ml = [l.strip() for l in m3.merge_lines()]
1518 ml.append("")
1518 ml.append("")
1519 elif at > 0:
1519 elif at > 0:
1520 ml = p1[fn].data().split("\n")
1520 ml = p1[fn].data().split("\n")
1521 else:
1521 else:
1522 ml = initialmergedlines
1522 ml = initialmergedlines
1523 ml[id * linesperrev] += " r%i" % id
1523 ml[id * linesperrev] += " r%i" % id
1524 mergedtext = "\n".join(ml)
1524 mergedtext = "\n".join(ml)
1525 files.append(fn)
1525 files.append(fn)
1526 fctxs[fn] = context.memfilectx(fn, mergedtext)
1526 fctxs[fn] = context.memfilectx(fn, mergedtext)
1527
1527
1528 if overwritten_file:
1528 if overwritten_file:
1529 fn = "of"
1529 fn = "of"
1530 files.append(fn)
1530 files.append(fn)
1531 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1531 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1532
1532
1533 if new_file:
1533 if new_file:
1534 fn = "nf%i" % id
1534 fn = "nf%i" % id
1535 files.append(fn)
1535 files.append(fn)
1536 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1536 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1537 if len(ps) > 1:
1537 if len(ps) > 1:
1538 if not p2:
1538 if not p2:
1539 p2 = repo[ps[1]]
1539 p2 = repo[ps[1]]
1540 for fn in p2:
1540 for fn in p2:
1541 if fn.startswith("nf"):
1541 if fn.startswith("nf"):
1542 files.append(fn)
1542 files.append(fn)
1543 fctxs[fn] = p2[fn]
1543 fctxs[fn] = p2[fn]
1544
1544
1545 def fctxfn(repo, cx, path):
1545 def fctxfn(repo, cx, path):
1546 return fctxs.get(path)
1546 return fctxs.get(path)
1547
1547
1548 if len(ps) == 0 or ps[0] < 0:
1548 if len(ps) == 0 or ps[0] < 0:
1549 pars = [None, None]
1549 pars = [None, None]
1550 elif len(ps) == 1:
1550 elif len(ps) == 1:
1551 pars = [nodeids[ps[0]], None]
1551 pars = [nodeids[ps[0]], None]
1552 else:
1552 else:
1553 pars = [nodeids[p] for p in ps]
1553 pars = [nodeids[p] for p in ps]
1554 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1554 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1555 date=(id, 0),
1555 date=(id, 0),
1556 user="debugbuilddag",
1556 user="debugbuilddag",
1557 extra={'branch': atbranch})
1557 extra={'branch': atbranch})
1558 nodeid = repo.commitctx(cx)
1558 nodeid = repo.commitctx(cx)
1559 nodeids.append(nodeid)
1559 nodeids.append(nodeid)
1560 at = id
1560 at = id
1561 elif type == 'l':
1561 elif type == 'l':
1562 id, name = data
1562 id, name = data
1563 ui.note(('tag %s\n' % name))
1563 ui.note(('tag %s\n' % name))
1564 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1564 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1565 elif type == 'a':
1565 elif type == 'a':
1566 ui.note(('branch %s\n' % data))
1566 ui.note(('branch %s\n' % data))
1567 atbranch = data
1567 atbranch = data
1568 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1568 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1569 tr.close()
1569 tr.close()
1570
1570
1571 if tags:
1571 if tags:
1572 repo.opener.write("localtags", "".join(tags))
1572 repo.opener.write("localtags", "".join(tags))
1573 finally:
1573 finally:
1574 ui.progress(_('building'), None)
1574 ui.progress(_('building'), None)
1575 release(tr, lock)
1575 release(tr, lock)
1576
1576
1577 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1577 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1578 def debugbundle(ui, bundlepath, all=None, **opts):
1578 def debugbundle(ui, bundlepath, all=None, **opts):
1579 """lists the contents of a bundle"""
1579 """lists the contents of a bundle"""
1580 f = hg.openpath(ui, bundlepath)
1580 f = hg.openpath(ui, bundlepath)
1581 try:
1581 try:
1582 gen = changegroup.readbundle(f, bundlepath)
1582 gen = changegroup.readbundle(f, bundlepath)
1583 if all:
1583 if all:
1584 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1584 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1585
1585
1586 def showchunks(named):
1586 def showchunks(named):
1587 ui.write("\n%s\n" % named)
1587 ui.write("\n%s\n" % named)
1588 chain = None
1588 chain = None
1589 while True:
1589 while True:
1590 chunkdata = gen.deltachunk(chain)
1590 chunkdata = gen.deltachunk(chain)
1591 if not chunkdata:
1591 if not chunkdata:
1592 break
1592 break
1593 node = chunkdata['node']
1593 node = chunkdata['node']
1594 p1 = chunkdata['p1']
1594 p1 = chunkdata['p1']
1595 p2 = chunkdata['p2']
1595 p2 = chunkdata['p2']
1596 cs = chunkdata['cs']
1596 cs = chunkdata['cs']
1597 deltabase = chunkdata['deltabase']
1597 deltabase = chunkdata['deltabase']
1598 delta = chunkdata['delta']
1598 delta = chunkdata['delta']
1599 ui.write("%s %s %s %s %s %s\n" %
1599 ui.write("%s %s %s %s %s %s\n" %
1600 (hex(node), hex(p1), hex(p2),
1600 (hex(node), hex(p1), hex(p2),
1601 hex(cs), hex(deltabase), len(delta)))
1601 hex(cs), hex(deltabase), len(delta)))
1602 chain = node
1602 chain = node
1603
1603
1604 chunkdata = gen.changelogheader()
1604 chunkdata = gen.changelogheader()
1605 showchunks("changelog")
1605 showchunks("changelog")
1606 chunkdata = gen.manifestheader()
1606 chunkdata = gen.manifestheader()
1607 showchunks("manifest")
1607 showchunks("manifest")
1608 while True:
1608 while True:
1609 chunkdata = gen.filelogheader()
1609 chunkdata = gen.filelogheader()
1610 if not chunkdata:
1610 if not chunkdata:
1611 break
1611 break
1612 fname = chunkdata['filename']
1612 fname = chunkdata['filename']
1613 showchunks(fname)
1613 showchunks(fname)
1614 else:
1614 else:
1615 chunkdata = gen.changelogheader()
1615 chunkdata = gen.changelogheader()
1616 chain = None
1616 chain = None
1617 while True:
1617 while True:
1618 chunkdata = gen.deltachunk(chain)
1618 chunkdata = gen.deltachunk(chain)
1619 if not chunkdata:
1619 if not chunkdata:
1620 break
1620 break
1621 node = chunkdata['node']
1621 node = chunkdata['node']
1622 ui.write("%s\n" % hex(node))
1622 ui.write("%s\n" % hex(node))
1623 chain = node
1623 chain = node
1624 finally:
1624 finally:
1625 f.close()
1625 f.close()
1626
1626
1627 @command('debugcheckstate', [], '')
1627 @command('debugcheckstate', [], '')
1628 def debugcheckstate(ui, repo):
1628 def debugcheckstate(ui, repo):
1629 """validate the correctness of the current dirstate"""
1629 """validate the correctness of the current dirstate"""
1630 parent1, parent2 = repo.dirstate.parents()
1630 parent1, parent2 = repo.dirstate.parents()
1631 m1 = repo[parent1].manifest()
1631 m1 = repo[parent1].manifest()
1632 m2 = repo[parent2].manifest()
1632 m2 = repo[parent2].manifest()
1633 errors = 0
1633 errors = 0
1634 for f in repo.dirstate:
1634 for f in repo.dirstate:
1635 state = repo.dirstate[f]
1635 state = repo.dirstate[f]
1636 if state in "nr" and f not in m1:
1636 if state in "nr" and f not in m1:
1637 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1637 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1638 errors += 1
1638 errors += 1
1639 if state in "a" and f in m1:
1639 if state in "a" and f in m1:
1640 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1640 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1641 errors += 1
1641 errors += 1
1642 if state in "m" and f not in m1 and f not in m2:
1642 if state in "m" and f not in m1 and f not in m2:
1643 ui.warn(_("%s in state %s, but not in either manifest\n") %
1643 ui.warn(_("%s in state %s, but not in either manifest\n") %
1644 (f, state))
1644 (f, state))
1645 errors += 1
1645 errors += 1
1646 for f in m1:
1646 for f in m1:
1647 state = repo.dirstate[f]
1647 state = repo.dirstate[f]
1648 if state not in "nrm":
1648 if state not in "nrm":
1649 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1649 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1650 errors += 1
1650 errors += 1
1651 if errors:
1651 if errors:
1652 error = _(".hg/dirstate inconsistent with current parent's manifest")
1652 error = _(".hg/dirstate inconsistent with current parent's manifest")
1653 raise util.Abort(error)
1653 raise util.Abort(error)
1654
1654
1655 @command('debugcommands', [], _('[COMMAND]'))
1655 @command('debugcommands', [], _('[COMMAND]'))
1656 def debugcommands(ui, cmd='', *args):
1656 def debugcommands(ui, cmd='', *args):
1657 """list all available commands and options"""
1657 """list all available commands and options"""
1658 for cmd, vals in sorted(table.iteritems()):
1658 for cmd, vals in sorted(table.iteritems()):
1659 cmd = cmd.split('|')[0].strip('^')
1659 cmd = cmd.split('|')[0].strip('^')
1660 opts = ', '.join([i[1] for i in vals[1]])
1660 opts = ', '.join([i[1] for i in vals[1]])
1661 ui.write('%s: %s\n' % (cmd, opts))
1661 ui.write('%s: %s\n' % (cmd, opts))
1662
1662
1663 @command('debugcomplete',
1663 @command('debugcomplete',
1664 [('o', 'options', None, _('show the command options'))],
1664 [('o', 'options', None, _('show the command options'))],
1665 _('[-o] CMD'))
1665 _('[-o] CMD'))
1666 def debugcomplete(ui, cmd='', **opts):
1666 def debugcomplete(ui, cmd='', **opts):
1667 """returns the completion list associated with the given command"""
1667 """returns the completion list associated with the given command"""
1668
1668
1669 if opts.get('options'):
1669 if opts.get('options'):
1670 options = []
1670 options = []
1671 otables = [globalopts]
1671 otables = [globalopts]
1672 if cmd:
1672 if cmd:
1673 aliases, entry = cmdutil.findcmd(cmd, table, False)
1673 aliases, entry = cmdutil.findcmd(cmd, table, False)
1674 otables.append(entry[1])
1674 otables.append(entry[1])
1675 for t in otables:
1675 for t in otables:
1676 for o in t:
1676 for o in t:
1677 if "(DEPRECATED)" in o[3]:
1677 if "(DEPRECATED)" in o[3]:
1678 continue
1678 continue
1679 if o[0]:
1679 if o[0]:
1680 options.append('-%s' % o[0])
1680 options.append('-%s' % o[0])
1681 options.append('--%s' % o[1])
1681 options.append('--%s' % o[1])
1682 ui.write("%s\n" % "\n".join(options))
1682 ui.write("%s\n" % "\n".join(options))
1683 return
1683 return
1684
1684
1685 cmdlist = cmdutil.findpossible(cmd, table)
1685 cmdlist = cmdutil.findpossible(cmd, table)
1686 if ui.verbose:
1686 if ui.verbose:
1687 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1687 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1688 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1688 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1689
1689
1690 @command('debugdag',
1690 @command('debugdag',
1691 [('t', 'tags', None, _('use tags as labels')),
1691 [('t', 'tags', None, _('use tags as labels')),
1692 ('b', 'branches', None, _('annotate with branch names')),
1692 ('b', 'branches', None, _('annotate with branch names')),
1693 ('', 'dots', None, _('use dots for runs')),
1693 ('', 'dots', None, _('use dots for runs')),
1694 ('s', 'spaces', None, _('separate elements by spaces'))],
1694 ('s', 'spaces', None, _('separate elements by spaces'))],
1695 _('[OPTION]... [FILE [REV]...]'))
1695 _('[OPTION]... [FILE [REV]...]'))
1696 def debugdag(ui, repo, file_=None, *revs, **opts):
1696 def debugdag(ui, repo, file_=None, *revs, **opts):
1697 """format the changelog or an index DAG as a concise textual description
1697 """format the changelog or an index DAG as a concise textual description
1698
1698
1699 If you pass a revlog index, the revlog's DAG is emitted. If you list
1699 If you pass a revlog index, the revlog's DAG is emitted. If you list
1700 revision numbers, they get labeled in the output as rN.
1700 revision numbers, they get labeled in the output as rN.
1701
1701
1702 Otherwise, the changelog DAG of the current repo is emitted.
1702 Otherwise, the changelog DAG of the current repo is emitted.
1703 """
1703 """
1704 spaces = opts.get('spaces')
1704 spaces = opts.get('spaces')
1705 dots = opts.get('dots')
1705 dots = opts.get('dots')
1706 if file_:
1706 if file_:
1707 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1707 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1708 revs = set((int(r) for r in revs))
1708 revs = set((int(r) for r in revs))
1709 def events():
1709 def events():
1710 for r in rlog:
1710 for r in rlog:
1711 yield 'n', (r, list(set(p for p in rlog.parentrevs(r)
1711 yield 'n', (r, list(set(p for p in rlog.parentrevs(r)
1712 if p != -1)))
1712 if p != -1)))
1713 if r in revs:
1713 if r in revs:
1714 yield 'l', (r, "r%i" % r)
1714 yield 'l', (r, "r%i" % r)
1715 elif repo:
1715 elif repo:
1716 cl = repo.changelog
1716 cl = repo.changelog
1717 tags = opts.get('tags')
1717 tags = opts.get('tags')
1718 branches = opts.get('branches')
1718 branches = opts.get('branches')
1719 if tags:
1719 if tags:
1720 labels = {}
1720 labels = {}
1721 for l, n in repo.tags().items():
1721 for l, n in repo.tags().items():
1722 labels.setdefault(cl.rev(n), []).append(l)
1722 labels.setdefault(cl.rev(n), []).append(l)
1723 def events():
1723 def events():
1724 b = "default"
1724 b = "default"
1725 for r in cl:
1725 for r in cl:
1726 if branches:
1726 if branches:
1727 newb = cl.read(cl.node(r))[5]['branch']
1727 newb = cl.read(cl.node(r))[5]['branch']
1728 if newb != b:
1728 if newb != b:
1729 yield 'a', newb
1729 yield 'a', newb
1730 b = newb
1730 b = newb
1731 yield 'n', (r, list(set(p for p in cl.parentrevs(r)
1731 yield 'n', (r, list(set(p for p in cl.parentrevs(r)
1732 if p != -1)))
1732 if p != -1)))
1733 if tags:
1733 if tags:
1734 ls = labels.get(r)
1734 ls = labels.get(r)
1735 if ls:
1735 if ls:
1736 for l in ls:
1736 for l in ls:
1737 yield 'l', (r, l)
1737 yield 'l', (r, l)
1738 else:
1738 else:
1739 raise util.Abort(_('need repo for changelog dag'))
1739 raise util.Abort(_('need repo for changelog dag'))
1740
1740
1741 for line in dagparser.dagtextlines(events(),
1741 for line in dagparser.dagtextlines(events(),
1742 addspaces=spaces,
1742 addspaces=spaces,
1743 wraplabels=True,
1743 wraplabels=True,
1744 wrapannotations=True,
1744 wrapannotations=True,
1745 wrapnonlinear=dots,
1745 wrapnonlinear=dots,
1746 usedots=dots,
1746 usedots=dots,
1747 maxlinewidth=70):
1747 maxlinewidth=70):
1748 ui.write(line)
1748 ui.write(line)
1749 ui.write("\n")
1749 ui.write("\n")
1750
1750
1751 @command('debugdata',
1751 @command('debugdata',
1752 [('c', 'changelog', False, _('open changelog')),
1752 [('c', 'changelog', False, _('open changelog')),
1753 ('m', 'manifest', False, _('open manifest'))],
1753 ('m', 'manifest', False, _('open manifest'))],
1754 _('-c|-m|FILE REV'))
1754 _('-c|-m|FILE REV'))
1755 def debugdata(ui, repo, file_, rev = None, **opts):
1755 def debugdata(ui, repo, file_, rev = None, **opts):
1756 """dump the contents of a data file revision"""
1756 """dump the contents of a data file revision"""
1757 if opts.get('changelog') or opts.get('manifest'):
1757 if opts.get('changelog') or opts.get('manifest'):
1758 file_, rev = None, file_
1758 file_, rev = None, file_
1759 elif rev is None:
1759 elif rev is None:
1760 raise error.CommandError('debugdata', _('invalid arguments'))
1760 raise error.CommandError('debugdata', _('invalid arguments'))
1761 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1761 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1762 try:
1762 try:
1763 ui.write(r.revision(r.lookup(rev)))
1763 ui.write(r.revision(r.lookup(rev)))
1764 except KeyError:
1764 except KeyError:
1765 raise util.Abort(_('invalid revision identifier %s') % rev)
1765 raise util.Abort(_('invalid revision identifier %s') % rev)
1766
1766
1767 @command('debugdate',
1767 @command('debugdate',
1768 [('e', 'extended', None, _('try extended date formats'))],
1768 [('e', 'extended', None, _('try extended date formats'))],
1769 _('[-e] DATE [RANGE]'))
1769 _('[-e] DATE [RANGE]'))
1770 def debugdate(ui, date, range=None, **opts):
1770 def debugdate(ui, date, range=None, **opts):
1771 """parse and display a date"""
1771 """parse and display a date"""
1772 if opts["extended"]:
1772 if opts["extended"]:
1773 d = util.parsedate(date, util.extendeddateformats)
1773 d = util.parsedate(date, util.extendeddateformats)
1774 else:
1774 else:
1775 d = util.parsedate(date)
1775 d = util.parsedate(date)
1776 ui.write(("internal: %s %s\n") % d)
1776 ui.write(("internal: %s %s\n") % d)
1777 ui.write(("standard: %s\n") % util.datestr(d))
1777 ui.write(("standard: %s\n") % util.datestr(d))
1778 if range:
1778 if range:
1779 m = util.matchdate(range)
1779 m = util.matchdate(range)
1780 ui.write(("match: %s\n") % m(d[0]))
1780 ui.write(("match: %s\n") % m(d[0]))
1781
1781
1782 @command('debugdiscovery',
1782 @command('debugdiscovery',
1783 [('', 'old', None, _('use old-style discovery')),
1783 [('', 'old', None, _('use old-style discovery')),
1784 ('', 'nonheads', None,
1784 ('', 'nonheads', None,
1785 _('use old-style discovery with non-heads included')),
1785 _('use old-style discovery with non-heads included')),
1786 ] + remoteopts,
1786 ] + remoteopts,
1787 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1787 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1788 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1788 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1789 """runs the changeset discovery protocol in isolation"""
1789 """runs the changeset discovery protocol in isolation"""
1790 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1790 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1791 opts.get('branch'))
1791 opts.get('branch'))
1792 remote = hg.peer(repo, opts, remoteurl)
1792 remote = hg.peer(repo, opts, remoteurl)
1793 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1793 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1794
1794
1795 # make sure tests are repeatable
1795 # make sure tests are repeatable
1796 random.seed(12323)
1796 random.seed(12323)
1797
1797
1798 def doit(localheads, remoteheads, remote=remote):
1798 def doit(localheads, remoteheads, remote=remote):
1799 if opts.get('old'):
1799 if opts.get('old'):
1800 if localheads:
1800 if localheads:
1801 raise util.Abort('cannot use localheads with old style '
1801 raise util.Abort('cannot use localheads with old style '
1802 'discovery')
1802 'discovery')
1803 if not util.safehasattr(remote, 'branches'):
1803 if not util.safehasattr(remote, 'branches'):
1804 # enable in-client legacy support
1804 # enable in-client legacy support
1805 remote = localrepo.locallegacypeer(remote.local())
1805 remote = localrepo.locallegacypeer(remote.local())
1806 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1806 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1807 force=True)
1807 force=True)
1808 common = set(common)
1808 common = set(common)
1809 if not opts.get('nonheads'):
1809 if not opts.get('nonheads'):
1810 ui.write(("unpruned common: %s\n") %
1810 ui.write(("unpruned common: %s\n") %
1811 " ".join(sorted(short(n) for n in common)))
1811 " ".join(sorted(short(n) for n in common)))
1812 dag = dagutil.revlogdag(repo.changelog)
1812 dag = dagutil.revlogdag(repo.changelog)
1813 all = dag.ancestorset(dag.internalizeall(common))
1813 all = dag.ancestorset(dag.internalizeall(common))
1814 common = dag.externalizeall(dag.headsetofconnecteds(all))
1814 common = dag.externalizeall(dag.headsetofconnecteds(all))
1815 else:
1815 else:
1816 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1816 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1817 common = set(common)
1817 common = set(common)
1818 rheads = set(hds)
1818 rheads = set(hds)
1819 lheads = set(repo.heads())
1819 lheads = set(repo.heads())
1820 ui.write(("common heads: %s\n") %
1820 ui.write(("common heads: %s\n") %
1821 " ".join(sorted(short(n) for n in common)))
1821 " ".join(sorted(short(n) for n in common)))
1822 if lheads <= common:
1822 if lheads <= common:
1823 ui.write(("local is subset\n"))
1823 ui.write(("local is subset\n"))
1824 elif rheads <= common:
1824 elif rheads <= common:
1825 ui.write(("remote is subset\n"))
1825 ui.write(("remote is subset\n"))
1826
1826
1827 serverlogs = opts.get('serverlog')
1827 serverlogs = opts.get('serverlog')
1828 if serverlogs:
1828 if serverlogs:
1829 for filename in serverlogs:
1829 for filename in serverlogs:
1830 logfile = open(filename, 'r')
1830 logfile = open(filename, 'r')
1831 try:
1831 try:
1832 line = logfile.readline()
1832 line = logfile.readline()
1833 while line:
1833 while line:
1834 parts = line.strip().split(';')
1834 parts = line.strip().split(';')
1835 op = parts[1]
1835 op = parts[1]
1836 if op == 'cg':
1836 if op == 'cg':
1837 pass
1837 pass
1838 elif op == 'cgss':
1838 elif op == 'cgss':
1839 doit(parts[2].split(' '), parts[3].split(' '))
1839 doit(parts[2].split(' '), parts[3].split(' '))
1840 elif op == 'unb':
1840 elif op == 'unb':
1841 doit(parts[3].split(' '), parts[2].split(' '))
1841 doit(parts[3].split(' '), parts[2].split(' '))
1842 line = logfile.readline()
1842 line = logfile.readline()
1843 finally:
1843 finally:
1844 logfile.close()
1844 logfile.close()
1845
1845
1846 else:
1846 else:
1847 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1847 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1848 opts.get('remote_head'))
1848 opts.get('remote_head'))
1849 localrevs = opts.get('local_head')
1849 localrevs = opts.get('local_head')
1850 doit(localrevs, remoterevs)
1850 doit(localrevs, remoterevs)
1851
1851
1852 @command('debugfileset',
1852 @command('debugfileset',
1853 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
1853 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
1854 _('[-r REV] FILESPEC'))
1854 _('[-r REV] FILESPEC'))
1855 def debugfileset(ui, repo, expr, **opts):
1855 def debugfileset(ui, repo, expr, **opts):
1856 '''parse and apply a fileset specification'''
1856 '''parse and apply a fileset specification'''
1857 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
1857 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
1858 if ui.verbose:
1858 if ui.verbose:
1859 tree = fileset.parse(expr)[0]
1859 tree = fileset.parse(expr)[0]
1860 ui.note(tree, "\n")
1860 ui.note(tree, "\n")
1861
1861
1862 for f in fileset.getfileset(ctx, expr):
1862 for f in fileset.getfileset(ctx, expr):
1863 ui.write("%s\n" % f)
1863 ui.write("%s\n" % f)
1864
1864
1865 @command('debugfsinfo', [], _('[PATH]'))
1865 @command('debugfsinfo', [], _('[PATH]'))
1866 def debugfsinfo(ui, path = "."):
1866 def debugfsinfo(ui, path = "."):
1867 """show information detected about current filesystem"""
1867 """show information detected about current filesystem"""
1868 util.writefile('.debugfsinfo', '')
1868 util.writefile('.debugfsinfo', '')
1869 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
1869 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
1870 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
1870 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
1871 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
1871 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
1872 and 'yes' or 'no'))
1872 and 'yes' or 'no'))
1873 os.unlink('.debugfsinfo')
1873 os.unlink('.debugfsinfo')
1874
1874
1875 @command('debuggetbundle',
1875 @command('debuggetbundle',
1876 [('H', 'head', [], _('id of head node'), _('ID')),
1876 [('H', 'head', [], _('id of head node'), _('ID')),
1877 ('C', 'common', [], _('id of common node'), _('ID')),
1877 ('C', 'common', [], _('id of common node'), _('ID')),
1878 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1878 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1879 _('REPO FILE [-H|-C ID]...'))
1879 _('REPO FILE [-H|-C ID]...'))
1880 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1880 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1881 """retrieves a bundle from a repo
1881 """retrieves a bundle from a repo
1882
1882
1883 Every ID must be a full-length hex node id string. Saves the bundle to the
1883 Every ID must be a full-length hex node id string. Saves the bundle to the
1884 given file.
1884 given file.
1885 """
1885 """
1886 repo = hg.peer(ui, opts, repopath)
1886 repo = hg.peer(ui, opts, repopath)
1887 if not repo.capable('getbundle'):
1887 if not repo.capable('getbundle'):
1888 raise util.Abort("getbundle() not supported by target repository")
1888 raise util.Abort("getbundle() not supported by target repository")
1889 args = {}
1889 args = {}
1890 if common:
1890 if common:
1891 args['common'] = [bin(s) for s in common]
1891 args['common'] = [bin(s) for s in common]
1892 if head:
1892 if head:
1893 args['heads'] = [bin(s) for s in head]
1893 args['heads'] = [bin(s) for s in head]
1894 bundle = repo.getbundle('debug', **args)
1894 bundle = repo.getbundle('debug', **args)
1895
1895
1896 bundletype = opts.get('type', 'bzip2').lower()
1896 bundletype = opts.get('type', 'bzip2').lower()
1897 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1897 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1898 bundletype = btypes.get(bundletype)
1898 bundletype = btypes.get(bundletype)
1899 if bundletype not in changegroup.bundletypes:
1899 if bundletype not in changegroup.bundletypes:
1900 raise util.Abort(_('unknown bundle type specified with --type'))
1900 raise util.Abort(_('unknown bundle type specified with --type'))
1901 changegroup.writebundle(bundle, bundlepath, bundletype)
1901 changegroup.writebundle(bundle, bundlepath, bundletype)
1902
1902
1903 @command('debugignore', [], '')
1903 @command('debugignore', [], '')
1904 def debugignore(ui, repo, *values, **opts):
1904 def debugignore(ui, repo, *values, **opts):
1905 """display the combined ignore pattern"""
1905 """display the combined ignore pattern"""
1906 ignore = repo.dirstate._ignore
1906 ignore = repo.dirstate._ignore
1907 includepat = getattr(ignore, 'includepat', None)
1907 includepat = getattr(ignore, 'includepat', None)
1908 if includepat is not None:
1908 if includepat is not None:
1909 ui.write("%s\n" % includepat)
1909 ui.write("%s\n" % includepat)
1910 else:
1910 else:
1911 raise util.Abort(_("no ignore patterns found"))
1911 raise util.Abort(_("no ignore patterns found"))
1912
1912
1913 @command('debugindex',
1913 @command('debugindex',
1914 [('c', 'changelog', False, _('open changelog')),
1914 [('c', 'changelog', False, _('open changelog')),
1915 ('m', 'manifest', False, _('open manifest')),
1915 ('m', 'manifest', False, _('open manifest')),
1916 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1916 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1917 _('[-f FORMAT] -c|-m|FILE'))
1917 _('[-f FORMAT] -c|-m|FILE'))
1918 def debugindex(ui, repo, file_ = None, **opts):
1918 def debugindex(ui, repo, file_ = None, **opts):
1919 """dump the contents of an index file"""
1919 """dump the contents of an index file"""
1920 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1920 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1921 format = opts.get('format', 0)
1921 format = opts.get('format', 0)
1922 if format not in (0, 1):
1922 if format not in (0, 1):
1923 raise util.Abort(_("unknown format %d") % format)
1923 raise util.Abort(_("unknown format %d") % format)
1924
1924
1925 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1925 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1926 if generaldelta:
1926 if generaldelta:
1927 basehdr = ' delta'
1927 basehdr = ' delta'
1928 else:
1928 else:
1929 basehdr = ' base'
1929 basehdr = ' base'
1930
1930
1931 if format == 0:
1931 if format == 0:
1932 ui.write(" rev offset length " + basehdr + " linkrev"
1932 ui.write(" rev offset length " + basehdr + " linkrev"
1933 " nodeid p1 p2\n")
1933 " nodeid p1 p2\n")
1934 elif format == 1:
1934 elif format == 1:
1935 ui.write(" rev flag offset length"
1935 ui.write(" rev flag offset length"
1936 " size " + basehdr + " link p1 p2"
1936 " size " + basehdr + " link p1 p2"
1937 " nodeid\n")
1937 " nodeid\n")
1938
1938
1939 for i in r:
1939 for i in r:
1940 node = r.node(i)
1940 node = r.node(i)
1941 if generaldelta:
1941 if generaldelta:
1942 base = r.deltaparent(i)
1942 base = r.deltaparent(i)
1943 else:
1943 else:
1944 base = r.chainbase(i)
1944 base = r.chainbase(i)
1945 if format == 0:
1945 if format == 0:
1946 try:
1946 try:
1947 pp = r.parents(node)
1947 pp = r.parents(node)
1948 except Exception:
1948 except Exception:
1949 pp = [nullid, nullid]
1949 pp = [nullid, nullid]
1950 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1950 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1951 i, r.start(i), r.length(i), base, r.linkrev(i),
1951 i, r.start(i), r.length(i), base, r.linkrev(i),
1952 short(node), short(pp[0]), short(pp[1])))
1952 short(node), short(pp[0]), short(pp[1])))
1953 elif format == 1:
1953 elif format == 1:
1954 pr = r.parentrevs(i)
1954 pr = r.parentrevs(i)
1955 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1955 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1956 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1956 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1957 base, r.linkrev(i), pr[0], pr[1], short(node)))
1957 base, r.linkrev(i), pr[0], pr[1], short(node)))
1958
1958
1959 @command('debugindexdot', [], _('FILE'))
1959 @command('debugindexdot', [], _('FILE'))
1960 def debugindexdot(ui, repo, file_):
1960 def debugindexdot(ui, repo, file_):
1961 """dump an index DAG as a graphviz dot file"""
1961 """dump an index DAG as a graphviz dot file"""
1962 r = None
1962 r = None
1963 if repo:
1963 if repo:
1964 filelog = repo.file(file_)
1964 filelog = repo.file(file_)
1965 if len(filelog):
1965 if len(filelog):
1966 r = filelog
1966 r = filelog
1967 if not r:
1967 if not r:
1968 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1968 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1969 ui.write(("digraph G {\n"))
1969 ui.write(("digraph G {\n"))
1970 for i in r:
1970 for i in r:
1971 node = r.node(i)
1971 node = r.node(i)
1972 pp = r.parents(node)
1972 pp = r.parents(node)
1973 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1973 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1974 if pp[1] != nullid:
1974 if pp[1] != nullid:
1975 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1975 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1976 ui.write("}\n")
1976 ui.write("}\n")
1977
1977
1978 @command('debuginstall', [], '')
1978 @command('debuginstall', [], '')
1979 def debuginstall(ui):
1979 def debuginstall(ui):
1980 '''test Mercurial installation
1980 '''test Mercurial installation
1981
1981
1982 Returns 0 on success.
1982 Returns 0 on success.
1983 '''
1983 '''
1984
1984
1985 def writetemp(contents):
1985 def writetemp(contents):
1986 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1986 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1987 f = os.fdopen(fd, "wb")
1987 f = os.fdopen(fd, "wb")
1988 f.write(contents)
1988 f.write(contents)
1989 f.close()
1989 f.close()
1990 return name
1990 return name
1991
1991
1992 problems = 0
1992 problems = 0
1993
1993
1994 # encoding
1994 # encoding
1995 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
1995 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
1996 try:
1996 try:
1997 encoding.fromlocal("test")
1997 encoding.fromlocal("test")
1998 except util.Abort, inst:
1998 except util.Abort, inst:
1999 ui.write(" %s\n" % inst)
1999 ui.write(" %s\n" % inst)
2000 ui.write(_(" (check that your locale is properly set)\n"))
2000 ui.write(_(" (check that your locale is properly set)\n"))
2001 problems += 1
2001 problems += 1
2002
2002
2003 # Python lib
2003 # Python lib
2004 ui.status(_("checking Python lib (%s)...\n")
2004 ui.status(_("checking Python lib (%s)...\n")
2005 % os.path.dirname(os.__file__))
2005 % os.path.dirname(os.__file__))
2006
2006
2007 # compiled modules
2007 # compiled modules
2008 ui.status(_("checking installed modules (%s)...\n")
2008 ui.status(_("checking installed modules (%s)...\n")
2009 % os.path.dirname(__file__))
2009 % os.path.dirname(__file__))
2010 try:
2010 try:
2011 import bdiff, mpatch, base85, osutil
2011 import bdiff, mpatch, base85, osutil
2012 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2012 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2013 except Exception, inst:
2013 except Exception, inst:
2014 ui.write(" %s\n" % inst)
2014 ui.write(" %s\n" % inst)
2015 ui.write(_(" One or more extensions could not be found"))
2015 ui.write(_(" One or more extensions could not be found"))
2016 ui.write(_(" (check that you compiled the extensions)\n"))
2016 ui.write(_(" (check that you compiled the extensions)\n"))
2017 problems += 1
2017 problems += 1
2018
2018
2019 # templates
2019 # templates
2020 import templater
2020 import templater
2021 p = templater.templatepath()
2021 p = templater.templatepath()
2022 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2022 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2023 try:
2023 try:
2024 templater.templater(templater.templatepath("map-cmdline.default"))
2024 templater.templater(templater.templatepath("map-cmdline.default"))
2025 except Exception, inst:
2025 except Exception, inst:
2026 ui.write(" %s\n" % inst)
2026 ui.write(" %s\n" % inst)
2027 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2027 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2028 problems += 1
2028 problems += 1
2029
2029
2030 # editor
2030 # editor
2031 ui.status(_("checking commit editor...\n"))
2031 ui.status(_("checking commit editor...\n"))
2032 editor = ui.geteditor()
2032 editor = ui.geteditor()
2033 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
2033 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
2034 if not cmdpath:
2034 if not cmdpath:
2035 if editor == 'vi':
2035 if editor == 'vi':
2036 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2036 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2037 ui.write(_(" (specify a commit editor in your configuration"
2037 ui.write(_(" (specify a commit editor in your configuration"
2038 " file)\n"))
2038 " file)\n"))
2039 else:
2039 else:
2040 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2040 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2041 ui.write(_(" (specify a commit editor in your configuration"
2041 ui.write(_(" (specify a commit editor in your configuration"
2042 " file)\n"))
2042 " file)\n"))
2043 problems += 1
2043 problems += 1
2044
2044
2045 # check username
2045 # check username
2046 ui.status(_("checking username...\n"))
2046 ui.status(_("checking username...\n"))
2047 try:
2047 try:
2048 ui.username()
2048 ui.username()
2049 except util.Abort, e:
2049 except util.Abort, e:
2050 ui.write(" %s\n" % e)
2050 ui.write(" %s\n" % e)
2051 ui.write(_(" (specify a username in your configuration file)\n"))
2051 ui.write(_(" (specify a username in your configuration file)\n"))
2052 problems += 1
2052 problems += 1
2053
2053
2054 if not problems:
2054 if not problems:
2055 ui.status(_("no problems detected\n"))
2055 ui.status(_("no problems detected\n"))
2056 else:
2056 else:
2057 ui.write(_("%s problems detected,"
2057 ui.write(_("%s problems detected,"
2058 " please check your install!\n") % problems)
2058 " please check your install!\n") % problems)
2059
2059
2060 return problems
2060 return problems
2061
2061
2062 @command('debugknown', [], _('REPO ID...'))
2062 @command('debugknown', [], _('REPO ID...'))
2063 def debugknown(ui, repopath, *ids, **opts):
2063 def debugknown(ui, repopath, *ids, **opts):
2064 """test whether node ids are known to a repo
2064 """test whether node ids are known to a repo
2065
2065
2066 Every ID must be a full-length hex node id string. Returns a list of 0s
2066 Every ID must be a full-length hex node id string. Returns a list of 0s
2067 and 1s indicating unknown/known.
2067 and 1s indicating unknown/known.
2068 """
2068 """
2069 repo = hg.peer(ui, opts, repopath)
2069 repo = hg.peer(ui, opts, repopath)
2070 if not repo.capable('known'):
2070 if not repo.capable('known'):
2071 raise util.Abort("known() not supported by target repository")
2071 raise util.Abort("known() not supported by target repository")
2072 flags = repo.known([bin(s) for s in ids])
2072 flags = repo.known([bin(s) for s in ids])
2073 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2073 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2074
2074
2075 @command('debugobsolete',
2075 @command('debugobsolete',
2076 [('', 'flags', 0, _('markers flag')),
2076 [('', 'flags', 0, _('markers flag')),
2077 ] + commitopts2,
2077 ] + commitopts2,
2078 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2078 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2079 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2079 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2080 """create arbitrary obsolete marker
2080 """create arbitrary obsolete marker
2081
2081
2082 With no arguments it it display the list obsolescence marker."""
2082 With no arguments it it display the list obsolescence marker."""
2083 def parsenodeid(s):
2083 def parsenodeid(s):
2084 try:
2084 try:
2085 # We do not use revsingle/revrange functions here to accept
2085 # We do not use revsingle/revrange functions here to accept
2086 # arbitrary node identifiers, possibly not present in the
2086 # arbitrary node identifiers, possibly not present in the
2087 # local repository.
2087 # local repository.
2088 n = bin(s)
2088 n = bin(s)
2089 if len(n) != len(nullid):
2089 if len(n) != len(nullid):
2090 raise TypeError()
2090 raise TypeError()
2091 return n
2091 return n
2092 except TypeError:
2092 except TypeError:
2093 raise util.Abort('changeset references must be full hexadecimal '
2093 raise util.Abort('changeset references must be full hexadecimal '
2094 'node identifiers')
2094 'node identifiers')
2095
2095
2096 if precursor is not None:
2096 if precursor is not None:
2097 metadata = {}
2097 metadata = {}
2098 if 'date' in opts:
2098 if 'date' in opts:
2099 metadata['date'] = opts['date']
2099 metadata['date'] = opts['date']
2100 metadata['user'] = opts['user'] or ui.username()
2100 metadata['user'] = opts['user'] or ui.username()
2101 succs = tuple(parsenodeid(succ) for succ in successors)
2101 succs = tuple(parsenodeid(succ) for succ in successors)
2102 l = repo.lock()
2102 l = repo.lock()
2103 try:
2103 try:
2104 tr = repo.transaction('debugobsolete')
2104 tr = repo.transaction('debugobsolete')
2105 try:
2105 try:
2106 repo.obsstore.create(tr, parsenodeid(precursor), succs,
2106 repo.obsstore.create(tr, parsenodeid(precursor), succs,
2107 opts['flags'], metadata)
2107 opts['flags'], metadata)
2108 tr.close()
2108 tr.close()
2109 finally:
2109 finally:
2110 tr.release()
2110 tr.release()
2111 finally:
2111 finally:
2112 l.release()
2112 l.release()
2113 else:
2113 else:
2114 for m in obsolete.allmarkers(repo):
2114 for m in obsolete.allmarkers(repo):
2115 ui.write(hex(m.precnode()))
2115 ui.write(hex(m.precnode()))
2116 for repl in m.succnodes():
2116 for repl in m.succnodes():
2117 ui.write(' ')
2117 ui.write(' ')
2118 ui.write(hex(repl))
2118 ui.write(hex(repl))
2119 ui.write(' %X ' % m._data[2])
2119 ui.write(' %X ' % m._data[2])
2120 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
2120 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
2121 sorted(m.metadata().items()))))
2121 sorted(m.metadata().items()))))
2122 ui.write('\n')
2122 ui.write('\n')
2123
2123
2124 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
2124 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
2125 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2125 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2126 '''access the pushkey key/value protocol
2126 '''access the pushkey key/value protocol
2127
2127
2128 With two args, list the keys in the given namespace.
2128 With two args, list the keys in the given namespace.
2129
2129
2130 With five args, set a key to new if it currently is set to old.
2130 With five args, set a key to new if it currently is set to old.
2131 Reports success or failure.
2131 Reports success or failure.
2132 '''
2132 '''
2133
2133
2134 target = hg.peer(ui, {}, repopath)
2134 target = hg.peer(ui, {}, repopath)
2135 if keyinfo:
2135 if keyinfo:
2136 key, old, new = keyinfo
2136 key, old, new = keyinfo
2137 r = target.pushkey(namespace, key, old, new)
2137 r = target.pushkey(namespace, key, old, new)
2138 ui.status(str(r) + '\n')
2138 ui.status(str(r) + '\n')
2139 return not r
2139 return not r
2140 else:
2140 else:
2141 for k, v in sorted(target.listkeys(namespace).iteritems()):
2141 for k, v in sorted(target.listkeys(namespace).iteritems()):
2142 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2142 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2143 v.encode('string-escape')))
2143 v.encode('string-escape')))
2144
2144
2145 @command('debugpvec', [], _('A B'))
2145 @command('debugpvec', [], _('A B'))
2146 def debugpvec(ui, repo, a, b=None):
2146 def debugpvec(ui, repo, a, b=None):
2147 ca = scmutil.revsingle(repo, a)
2147 ca = scmutil.revsingle(repo, a)
2148 cb = scmutil.revsingle(repo, b)
2148 cb = scmutil.revsingle(repo, b)
2149 pa = pvec.ctxpvec(ca)
2149 pa = pvec.ctxpvec(ca)
2150 pb = pvec.ctxpvec(cb)
2150 pb = pvec.ctxpvec(cb)
2151 if pa == pb:
2151 if pa == pb:
2152 rel = "="
2152 rel = "="
2153 elif pa > pb:
2153 elif pa > pb:
2154 rel = ">"
2154 rel = ">"
2155 elif pa < pb:
2155 elif pa < pb:
2156 rel = "<"
2156 rel = "<"
2157 elif pa | pb:
2157 elif pa | pb:
2158 rel = "|"
2158 rel = "|"
2159 ui.write(_("a: %s\n") % pa)
2159 ui.write(_("a: %s\n") % pa)
2160 ui.write(_("b: %s\n") % pb)
2160 ui.write(_("b: %s\n") % pb)
2161 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2161 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2162 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2162 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2163 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2163 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2164 pa.distance(pb), rel))
2164 pa.distance(pb), rel))
2165
2165
2166 @command('debugrebuildstate',
2166 @command('debugrebuildstate',
2167 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2167 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2168 _('[-r REV] [REV]'))
2168 _('[-r REV] [REV]'))
2169 def debugrebuildstate(ui, repo, rev="tip"):
2169 def debugrebuildstate(ui, repo, rev="tip"):
2170 """rebuild the dirstate as it would look like for the given revision"""
2170 """rebuild the dirstate as it would look like for the given revision"""
2171 ctx = scmutil.revsingle(repo, rev)
2171 ctx = scmutil.revsingle(repo, rev)
2172 wlock = repo.wlock()
2172 wlock = repo.wlock()
2173 try:
2173 try:
2174 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2174 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2175 finally:
2175 finally:
2176 wlock.release()
2176 wlock.release()
2177
2177
2178 @command('debugrename',
2178 @command('debugrename',
2179 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2179 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2180 _('[-r REV] FILE'))
2180 _('[-r REV] FILE'))
2181 def debugrename(ui, repo, file1, *pats, **opts):
2181 def debugrename(ui, repo, file1, *pats, **opts):
2182 """dump rename information"""
2182 """dump rename information"""
2183
2183
2184 ctx = scmutil.revsingle(repo, opts.get('rev'))
2184 ctx = scmutil.revsingle(repo, opts.get('rev'))
2185 m = scmutil.match(ctx, (file1,) + pats, opts)
2185 m = scmutil.match(ctx, (file1,) + pats, opts)
2186 for abs in ctx.walk(m):
2186 for abs in ctx.walk(m):
2187 fctx = ctx[abs]
2187 fctx = ctx[abs]
2188 o = fctx.filelog().renamed(fctx.filenode())
2188 o = fctx.filelog().renamed(fctx.filenode())
2189 rel = m.rel(abs)
2189 rel = m.rel(abs)
2190 if o:
2190 if o:
2191 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2191 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2192 else:
2192 else:
2193 ui.write(_("%s not renamed\n") % rel)
2193 ui.write(_("%s not renamed\n") % rel)
2194
2194
2195 @command('debugrevlog',
2195 @command('debugrevlog',
2196 [('c', 'changelog', False, _('open changelog')),
2196 [('c', 'changelog', False, _('open changelog')),
2197 ('m', 'manifest', False, _('open manifest')),
2197 ('m', 'manifest', False, _('open manifest')),
2198 ('d', 'dump', False, _('dump index data'))],
2198 ('d', 'dump', False, _('dump index data'))],
2199 _('-c|-m|FILE'))
2199 _('-c|-m|FILE'))
2200 def debugrevlog(ui, repo, file_ = None, **opts):
2200 def debugrevlog(ui, repo, file_ = None, **opts):
2201 """show data and statistics about a revlog"""
2201 """show data and statistics about a revlog"""
2202 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2202 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2203
2203
2204 if opts.get("dump"):
2204 if opts.get("dump"):
2205 numrevs = len(r)
2205 numrevs = len(r)
2206 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2206 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2207 " rawsize totalsize compression heads\n")
2207 " rawsize totalsize compression heads\n")
2208 ts = 0
2208 ts = 0
2209 heads = set()
2209 heads = set()
2210 for rev in xrange(numrevs):
2210 for rev in xrange(numrevs):
2211 dbase = r.deltaparent(rev)
2211 dbase = r.deltaparent(rev)
2212 if dbase == -1:
2212 if dbase == -1:
2213 dbase = rev
2213 dbase = rev
2214 cbase = r.chainbase(rev)
2214 cbase = r.chainbase(rev)
2215 p1, p2 = r.parentrevs(rev)
2215 p1, p2 = r.parentrevs(rev)
2216 rs = r.rawsize(rev)
2216 rs = r.rawsize(rev)
2217 ts = ts + rs
2217 ts = ts + rs
2218 heads -= set(r.parentrevs(rev))
2218 heads -= set(r.parentrevs(rev))
2219 heads.add(rev)
2219 heads.add(rev)
2220 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2220 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2221 (rev, p1, p2, r.start(rev), r.end(rev),
2221 (rev, p1, p2, r.start(rev), r.end(rev),
2222 r.start(dbase), r.start(cbase),
2222 r.start(dbase), r.start(cbase),
2223 r.start(p1), r.start(p2),
2223 r.start(p1), r.start(p2),
2224 rs, ts, ts / r.end(rev), len(heads)))
2224 rs, ts, ts / r.end(rev), len(heads)))
2225 return 0
2225 return 0
2226
2226
2227 v = r.version
2227 v = r.version
2228 format = v & 0xFFFF
2228 format = v & 0xFFFF
2229 flags = []
2229 flags = []
2230 gdelta = False
2230 gdelta = False
2231 if v & revlog.REVLOGNGINLINEDATA:
2231 if v & revlog.REVLOGNGINLINEDATA:
2232 flags.append('inline')
2232 flags.append('inline')
2233 if v & revlog.REVLOGGENERALDELTA:
2233 if v & revlog.REVLOGGENERALDELTA:
2234 gdelta = True
2234 gdelta = True
2235 flags.append('generaldelta')
2235 flags.append('generaldelta')
2236 if not flags:
2236 if not flags:
2237 flags = ['(none)']
2237 flags = ['(none)']
2238
2238
2239 nummerges = 0
2239 nummerges = 0
2240 numfull = 0
2240 numfull = 0
2241 numprev = 0
2241 numprev = 0
2242 nump1 = 0
2242 nump1 = 0
2243 nump2 = 0
2243 nump2 = 0
2244 numother = 0
2244 numother = 0
2245 nump1prev = 0
2245 nump1prev = 0
2246 nump2prev = 0
2246 nump2prev = 0
2247 chainlengths = []
2247 chainlengths = []
2248
2248
2249 datasize = [None, 0, 0L]
2249 datasize = [None, 0, 0L]
2250 fullsize = [None, 0, 0L]
2250 fullsize = [None, 0, 0L]
2251 deltasize = [None, 0, 0L]
2251 deltasize = [None, 0, 0L]
2252
2252
2253 def addsize(size, l):
2253 def addsize(size, l):
2254 if l[0] is None or size < l[0]:
2254 if l[0] is None or size < l[0]:
2255 l[0] = size
2255 l[0] = size
2256 if size > l[1]:
2256 if size > l[1]:
2257 l[1] = size
2257 l[1] = size
2258 l[2] += size
2258 l[2] += size
2259
2259
2260 numrevs = len(r)
2260 numrevs = len(r)
2261 for rev in xrange(numrevs):
2261 for rev in xrange(numrevs):
2262 p1, p2 = r.parentrevs(rev)
2262 p1, p2 = r.parentrevs(rev)
2263 delta = r.deltaparent(rev)
2263 delta = r.deltaparent(rev)
2264 if format > 0:
2264 if format > 0:
2265 addsize(r.rawsize(rev), datasize)
2265 addsize(r.rawsize(rev), datasize)
2266 if p2 != nullrev:
2266 if p2 != nullrev:
2267 nummerges += 1
2267 nummerges += 1
2268 size = r.length(rev)
2268 size = r.length(rev)
2269 if delta == nullrev:
2269 if delta == nullrev:
2270 chainlengths.append(0)
2270 chainlengths.append(0)
2271 numfull += 1
2271 numfull += 1
2272 addsize(size, fullsize)
2272 addsize(size, fullsize)
2273 else:
2273 else:
2274 chainlengths.append(chainlengths[delta] + 1)
2274 chainlengths.append(chainlengths[delta] + 1)
2275 addsize(size, deltasize)
2275 addsize(size, deltasize)
2276 if delta == rev - 1:
2276 if delta == rev - 1:
2277 numprev += 1
2277 numprev += 1
2278 if delta == p1:
2278 if delta == p1:
2279 nump1prev += 1
2279 nump1prev += 1
2280 elif delta == p2:
2280 elif delta == p2:
2281 nump2prev += 1
2281 nump2prev += 1
2282 elif delta == p1:
2282 elif delta == p1:
2283 nump1 += 1
2283 nump1 += 1
2284 elif delta == p2:
2284 elif delta == p2:
2285 nump2 += 1
2285 nump2 += 1
2286 elif delta != nullrev:
2286 elif delta != nullrev:
2287 numother += 1
2287 numother += 1
2288
2288
2289 # Adjust size min value for empty cases
2289 # Adjust size min value for empty cases
2290 for size in (datasize, fullsize, deltasize):
2290 for size in (datasize, fullsize, deltasize):
2291 if size[0] is None:
2291 if size[0] is None:
2292 size[0] = 0
2292 size[0] = 0
2293
2293
2294 numdeltas = numrevs - numfull
2294 numdeltas = numrevs - numfull
2295 numoprev = numprev - nump1prev - nump2prev
2295 numoprev = numprev - nump1prev - nump2prev
2296 totalrawsize = datasize[2]
2296 totalrawsize = datasize[2]
2297 datasize[2] /= numrevs
2297 datasize[2] /= numrevs
2298 fulltotal = fullsize[2]
2298 fulltotal = fullsize[2]
2299 fullsize[2] /= numfull
2299 fullsize[2] /= numfull
2300 deltatotal = deltasize[2]
2300 deltatotal = deltasize[2]
2301 if numrevs - numfull > 0:
2301 if numrevs - numfull > 0:
2302 deltasize[2] /= numrevs - numfull
2302 deltasize[2] /= numrevs - numfull
2303 totalsize = fulltotal + deltatotal
2303 totalsize = fulltotal + deltatotal
2304 avgchainlen = sum(chainlengths) / numrevs
2304 avgchainlen = sum(chainlengths) / numrevs
2305 compratio = totalrawsize / totalsize
2305 compratio = totalrawsize / totalsize
2306
2306
2307 basedfmtstr = '%%%dd\n'
2307 basedfmtstr = '%%%dd\n'
2308 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2308 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2309
2309
2310 def dfmtstr(max):
2310 def dfmtstr(max):
2311 return basedfmtstr % len(str(max))
2311 return basedfmtstr % len(str(max))
2312 def pcfmtstr(max, padding=0):
2312 def pcfmtstr(max, padding=0):
2313 return basepcfmtstr % (len(str(max)), ' ' * padding)
2313 return basepcfmtstr % (len(str(max)), ' ' * padding)
2314
2314
2315 def pcfmt(value, total):
2315 def pcfmt(value, total):
2316 return (value, 100 * float(value) / total)
2316 return (value, 100 * float(value) / total)
2317
2317
2318 ui.write(('format : %d\n') % format)
2318 ui.write(('format : %d\n') % format)
2319 ui.write(('flags : %s\n') % ', '.join(flags))
2319 ui.write(('flags : %s\n') % ', '.join(flags))
2320
2320
2321 ui.write('\n')
2321 ui.write('\n')
2322 fmt = pcfmtstr(totalsize)
2322 fmt = pcfmtstr(totalsize)
2323 fmt2 = dfmtstr(totalsize)
2323 fmt2 = dfmtstr(totalsize)
2324 ui.write(('revisions : ') + fmt2 % numrevs)
2324 ui.write(('revisions : ') + fmt2 % numrevs)
2325 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2325 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2326 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2326 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2327 ui.write(('revisions : ') + fmt2 % numrevs)
2327 ui.write(('revisions : ') + fmt2 % numrevs)
2328 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2328 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2329 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2329 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2330 ui.write(('revision size : ') + fmt2 % totalsize)
2330 ui.write(('revision size : ') + fmt2 % totalsize)
2331 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2331 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2332 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2332 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2333
2333
2334 ui.write('\n')
2334 ui.write('\n')
2335 fmt = dfmtstr(max(avgchainlen, compratio))
2335 fmt = dfmtstr(max(avgchainlen, compratio))
2336 ui.write(('avg chain length : ') + fmt % avgchainlen)
2336 ui.write(('avg chain length : ') + fmt % avgchainlen)
2337 ui.write(('compression ratio : ') + fmt % compratio)
2337 ui.write(('compression ratio : ') + fmt % compratio)
2338
2338
2339 if format > 0:
2339 if format > 0:
2340 ui.write('\n')
2340 ui.write('\n')
2341 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2341 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2342 % tuple(datasize))
2342 % tuple(datasize))
2343 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2343 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2344 % tuple(fullsize))
2344 % tuple(fullsize))
2345 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2345 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2346 % tuple(deltasize))
2346 % tuple(deltasize))
2347
2347
2348 if numdeltas > 0:
2348 if numdeltas > 0:
2349 ui.write('\n')
2349 ui.write('\n')
2350 fmt = pcfmtstr(numdeltas)
2350 fmt = pcfmtstr(numdeltas)
2351 fmt2 = pcfmtstr(numdeltas, 4)
2351 fmt2 = pcfmtstr(numdeltas, 4)
2352 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2352 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2353 if numprev > 0:
2353 if numprev > 0:
2354 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2354 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2355 numprev))
2355 numprev))
2356 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2356 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2357 numprev))
2357 numprev))
2358 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2358 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2359 numprev))
2359 numprev))
2360 if gdelta:
2360 if gdelta:
2361 ui.write(('deltas against p1 : ')
2361 ui.write(('deltas against p1 : ')
2362 + fmt % pcfmt(nump1, numdeltas))
2362 + fmt % pcfmt(nump1, numdeltas))
2363 ui.write(('deltas against p2 : ')
2363 ui.write(('deltas against p2 : ')
2364 + fmt % pcfmt(nump2, numdeltas))
2364 + fmt % pcfmt(nump2, numdeltas))
2365 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2365 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2366 numdeltas))
2366 numdeltas))
2367
2367
2368 @command('debugrevspec', [], ('REVSPEC'))
2368 @command('debugrevspec', [], ('REVSPEC'))
2369 def debugrevspec(ui, repo, expr):
2369 def debugrevspec(ui, repo, expr):
2370 """parse and apply a revision specification
2370 """parse and apply a revision specification
2371
2371
2372 Use --verbose to print the parsed tree before and after aliases
2372 Use --verbose to print the parsed tree before and after aliases
2373 expansion.
2373 expansion.
2374 """
2374 """
2375 if ui.verbose:
2375 if ui.verbose:
2376 tree = revset.parse(expr)[0]
2376 tree = revset.parse(expr)[0]
2377 ui.note(revset.prettyformat(tree), "\n")
2377 ui.note(revset.prettyformat(tree), "\n")
2378 newtree = revset.findaliases(ui, tree)
2378 newtree = revset.findaliases(ui, tree)
2379 if newtree != tree:
2379 if newtree != tree:
2380 ui.note(revset.prettyformat(newtree), "\n")
2380 ui.note(revset.prettyformat(newtree), "\n")
2381 func = revset.match(ui, expr)
2381 func = revset.match(ui, expr)
2382 for c in func(repo, range(len(repo))):
2382 for c in func(repo, range(len(repo))):
2383 ui.write("%s\n" % c)
2383 ui.write("%s\n" % c)
2384
2384
2385 @command('debugsetparents', [], _('REV1 [REV2]'))
2385 @command('debugsetparents', [], _('REV1 [REV2]'))
2386 def debugsetparents(ui, repo, rev1, rev2=None):
2386 def debugsetparents(ui, repo, rev1, rev2=None):
2387 """manually set the parents of the current working directory
2387 """manually set the parents of the current working directory
2388
2388
2389 This is useful for writing repository conversion tools, but should
2389 This is useful for writing repository conversion tools, but should
2390 be used with care.
2390 be used with care.
2391
2391
2392 Returns 0 on success.
2392 Returns 0 on success.
2393 """
2393 """
2394
2394
2395 r1 = scmutil.revsingle(repo, rev1).node()
2395 r1 = scmutil.revsingle(repo, rev1).node()
2396 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2396 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2397
2397
2398 wlock = repo.wlock()
2398 wlock = repo.wlock()
2399 try:
2399 try:
2400 repo.setparents(r1, r2)
2400 repo.setparents(r1, r2)
2401 finally:
2401 finally:
2402 wlock.release()
2402 wlock.release()
2403
2403
2404 @command('debugstate',
2404 @command('debugstate',
2405 [('', 'nodates', None, _('do not display the saved mtime')),
2405 [('', 'nodates', None, _('do not display the saved mtime')),
2406 ('', 'datesort', None, _('sort by saved mtime'))],
2406 ('', 'datesort', None, _('sort by saved mtime'))],
2407 _('[OPTION]...'))
2407 _('[OPTION]...'))
2408 def debugstate(ui, repo, nodates=None, datesort=None):
2408 def debugstate(ui, repo, nodates=None, datesort=None):
2409 """show the contents of the current dirstate"""
2409 """show the contents of the current dirstate"""
2410 timestr = ""
2410 timestr = ""
2411 showdate = not nodates
2411 showdate = not nodates
2412 if datesort:
2412 if datesort:
2413 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2413 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2414 else:
2414 else:
2415 keyfunc = None # sort by filename
2415 keyfunc = None # sort by filename
2416 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2416 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2417 if showdate:
2417 if showdate:
2418 if ent[3] == -1:
2418 if ent[3] == -1:
2419 # Pad or slice to locale representation
2419 # Pad or slice to locale representation
2420 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2420 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2421 time.localtime(0)))
2421 time.localtime(0)))
2422 timestr = 'unset'
2422 timestr = 'unset'
2423 timestr = (timestr[:locale_len] +
2423 timestr = (timestr[:locale_len] +
2424 ' ' * (locale_len - len(timestr)))
2424 ' ' * (locale_len - len(timestr)))
2425 else:
2425 else:
2426 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2426 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2427 time.localtime(ent[3]))
2427 time.localtime(ent[3]))
2428 if ent[1] & 020000:
2428 if ent[1] & 020000:
2429 mode = 'lnk'
2429 mode = 'lnk'
2430 else:
2430 else:
2431 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2431 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2432 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2432 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2433 for f in repo.dirstate.copies():
2433 for f in repo.dirstate.copies():
2434 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2434 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2435
2435
2436 @command('debugsub',
2436 @command('debugsub',
2437 [('r', 'rev', '',
2437 [('r', 'rev', '',
2438 _('revision to check'), _('REV'))],
2438 _('revision to check'), _('REV'))],
2439 _('[-r REV] [REV]'))
2439 _('[-r REV] [REV]'))
2440 def debugsub(ui, repo, rev=None):
2440 def debugsub(ui, repo, rev=None):
2441 ctx = scmutil.revsingle(repo, rev, None)
2441 ctx = scmutil.revsingle(repo, rev, None)
2442 for k, v in sorted(ctx.substate.items()):
2442 for k, v in sorted(ctx.substate.items()):
2443 ui.write(('path %s\n') % k)
2443 ui.write(('path %s\n') % k)
2444 ui.write((' source %s\n') % v[0])
2444 ui.write((' source %s\n') % v[0])
2445 ui.write((' revision %s\n') % v[1])
2445 ui.write((' revision %s\n') % v[1])
2446
2446
2447 @command('debugsuccessorssets',
2447 @command('debugsuccessorssets',
2448 [],
2448 [],
2449 _('[REV]'))
2449 _('[REV]'))
2450 def debugsuccessorssets(ui, repo, *revs):
2450 def debugsuccessorssets(ui, repo, *revs):
2451 """show set of successors for revision
2451 """show set of successors for revision
2452
2452
2453 A successors set of changeset A is a consistent group of revisions that
2453 A successors set of changeset A is a consistent group of revisions that
2454 succeed A. It contains non-obsolete changesets only.
2454 succeed A. It contains non-obsolete changesets only.
2455
2455
2456 In most cases a changeset A has a single successors set containing a single
2456 In most cases a changeset A has a single successors set containing a single
2457 successor (changeset A replaced by A').
2457 successor (changeset A replaced by A').
2458
2458
2459 A changeset that is made obsolete with no successors are called "pruned".
2459 A changeset that is made obsolete with no successors are called "pruned".
2460 Such changesets have no successors sets at all.
2460 Such changesets have no successors sets at all.
2461
2461
2462 A changeset that has been "split" will have a successors set containing
2462 A changeset that has been "split" will have a successors set containing
2463 more than one successor.
2463 more than one successor.
2464
2464
2465 A changeset that has been rewritten in multiple different ways is called
2465 A changeset that has been rewritten in multiple different ways is called
2466 "divergent". Such changesets have multiple successor sets (each of which
2466 "divergent". Such changesets have multiple successor sets (each of which
2467 may also be split, i.e. have multiple successors).
2467 may also be split, i.e. have multiple successors).
2468
2468
2469 Results are displayed as follows::
2469 Results are displayed as follows::
2470
2470
2471 <rev1>
2471 <rev1>
2472 <successors-1A>
2472 <successors-1A>
2473 <rev2>
2473 <rev2>
2474 <successors-2A>
2474 <successors-2A>
2475 <successors-2B1> <successors-2B2> <successors-2B3>
2475 <successors-2B1> <successors-2B2> <successors-2B3>
2476
2476
2477 Here rev2 has two possible (i.e. divergent) successors sets. The first
2477 Here rev2 has two possible (i.e. divergent) successors sets. The first
2478 holds one element, whereas the second holds three (i.e. the changeset has
2478 holds one element, whereas the second holds three (i.e. the changeset has
2479 been split).
2479 been split).
2480 """
2480 """
2481 # passed to successorssets caching computation from one call to another
2481 # passed to successorssets caching computation from one call to another
2482 cache = {}
2482 cache = {}
2483 ctx2str = str
2483 ctx2str = str
2484 node2str = short
2484 node2str = short
2485 if ui.debug():
2485 if ui.debug():
2486 def ctx2str(ctx):
2486 def ctx2str(ctx):
2487 return ctx.hex()
2487 return ctx.hex()
2488 node2str = hex
2488 node2str = hex
2489 for rev in scmutil.revrange(repo, revs):
2489 for rev in scmutil.revrange(repo, revs):
2490 ctx = repo[rev]
2490 ctx = repo[rev]
2491 ui.write('%s\n'% ctx2str(ctx))
2491 ui.write('%s\n'% ctx2str(ctx))
2492 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2492 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2493 if succsset:
2493 if succsset:
2494 ui.write(' ')
2494 ui.write(' ')
2495 ui.write(node2str(succsset[0]))
2495 ui.write(node2str(succsset[0]))
2496 for node in succsset[1:]:
2496 for node in succsset[1:]:
2497 ui.write(' ')
2497 ui.write(' ')
2498 ui.write(node2str(node))
2498 ui.write(node2str(node))
2499 ui.write('\n')
2499 ui.write('\n')
2500
2500
2501 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2501 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2502 def debugwalk(ui, repo, *pats, **opts):
2502 def debugwalk(ui, repo, *pats, **opts):
2503 """show how files match on given patterns"""
2503 """show how files match on given patterns"""
2504 m = scmutil.match(repo[None], pats, opts)
2504 m = scmutil.match(repo[None], pats, opts)
2505 items = list(repo.walk(m))
2505 items = list(repo.walk(m))
2506 if not items:
2506 if not items:
2507 return
2507 return
2508 f = lambda fn: fn
2508 f = lambda fn: fn
2509 if ui.configbool('ui', 'slash') and os.sep != '/':
2509 if ui.configbool('ui', 'slash') and os.sep != '/':
2510 f = lambda fn: util.normpath(fn)
2510 f = lambda fn: util.normpath(fn)
2511 fmt = 'f %%-%ds %%-%ds %%s' % (
2511 fmt = 'f %%-%ds %%-%ds %%s' % (
2512 max([len(abs) for abs in items]),
2512 max([len(abs) for abs in items]),
2513 max([len(m.rel(abs)) for abs in items]))
2513 max([len(m.rel(abs)) for abs in items]))
2514 for abs in items:
2514 for abs in items:
2515 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2515 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2516 ui.write("%s\n" % line.rstrip())
2516 ui.write("%s\n" % line.rstrip())
2517
2517
2518 @command('debugwireargs',
2518 @command('debugwireargs',
2519 [('', 'three', '', 'three'),
2519 [('', 'three', '', 'three'),
2520 ('', 'four', '', 'four'),
2520 ('', 'four', '', 'four'),
2521 ('', 'five', '', 'five'),
2521 ('', 'five', '', 'five'),
2522 ] + remoteopts,
2522 ] + remoteopts,
2523 _('REPO [OPTIONS]... [ONE [TWO]]'))
2523 _('REPO [OPTIONS]... [ONE [TWO]]'))
2524 def debugwireargs(ui, repopath, *vals, **opts):
2524 def debugwireargs(ui, repopath, *vals, **opts):
2525 repo = hg.peer(ui, opts, repopath)
2525 repo = hg.peer(ui, opts, repopath)
2526 for opt in remoteopts:
2526 for opt in remoteopts:
2527 del opts[opt[1]]
2527 del opts[opt[1]]
2528 args = {}
2528 args = {}
2529 for k, v in opts.iteritems():
2529 for k, v in opts.iteritems():
2530 if v:
2530 if v:
2531 args[k] = v
2531 args[k] = v
2532 # run twice to check that we don't mess up the stream for the next command
2532 # run twice to check that we don't mess up the stream for the next command
2533 res1 = repo.debugwireargs(*vals, **args)
2533 res1 = repo.debugwireargs(*vals, **args)
2534 res2 = repo.debugwireargs(*vals, **args)
2534 res2 = repo.debugwireargs(*vals, **args)
2535 ui.write("%s\n" % res1)
2535 ui.write("%s\n" % res1)
2536 if res1 != res2:
2536 if res1 != res2:
2537 ui.warn("%s\n" % res2)
2537 ui.warn("%s\n" % res2)
2538
2538
2539 @command('^diff',
2539 @command('^diff',
2540 [('r', 'rev', [], _('revision'), _('REV')),
2540 [('r', 'rev', [], _('revision'), _('REV')),
2541 ('c', 'change', '', _('change made by revision'), _('REV'))
2541 ('c', 'change', '', _('change made by revision'), _('REV'))
2542 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2542 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2543 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2543 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2544 def diff(ui, repo, *pats, **opts):
2544 def diff(ui, repo, *pats, **opts):
2545 """diff repository (or selected files)
2545 """diff repository (or selected files)
2546
2546
2547 Show differences between revisions for the specified files.
2547 Show differences between revisions for the specified files.
2548
2548
2549 Differences between files are shown using the unified diff format.
2549 Differences between files are shown using the unified diff format.
2550
2550
2551 .. note::
2551 .. note::
2552 diff may generate unexpected results for merges, as it will
2552 diff may generate unexpected results for merges, as it will
2553 default to comparing against the working directory's first
2553 default to comparing against the working directory's first
2554 parent changeset if no revisions are specified.
2554 parent changeset if no revisions are specified.
2555
2555
2556 When two revision arguments are given, then changes are shown
2556 When two revision arguments are given, then changes are shown
2557 between those revisions. If only one revision is specified then
2557 between those revisions. If only one revision is specified then
2558 that revision is compared to the working directory, and, when no
2558 that revision is compared to the working directory, and, when no
2559 revisions are specified, the working directory files are compared
2559 revisions are specified, the working directory files are compared
2560 to its parent.
2560 to its parent.
2561
2561
2562 Alternatively you can specify -c/--change with a revision to see
2562 Alternatively you can specify -c/--change with a revision to see
2563 the changes in that changeset relative to its first parent.
2563 the changes in that changeset relative to its first parent.
2564
2564
2565 Without the -a/--text option, diff will avoid generating diffs of
2565 Without the -a/--text option, diff will avoid generating diffs of
2566 files it detects as binary. With -a, diff will generate a diff
2566 files it detects as binary. With -a, diff will generate a diff
2567 anyway, probably with undesirable results.
2567 anyway, probably with undesirable results.
2568
2568
2569 Use the -g/--git option to generate diffs in the git extended diff
2569 Use the -g/--git option to generate diffs in the git extended diff
2570 format. For more information, read :hg:`help diffs`.
2570 format. For more information, read :hg:`help diffs`.
2571
2571
2572 .. container:: verbose
2572 .. container:: verbose
2573
2573
2574 Examples:
2574 Examples:
2575
2575
2576 - compare a file in the current working directory to its parent::
2576 - compare a file in the current working directory to its parent::
2577
2577
2578 hg diff foo.c
2578 hg diff foo.c
2579
2579
2580 - compare two historical versions of a directory, with rename info::
2580 - compare two historical versions of a directory, with rename info::
2581
2581
2582 hg diff --git -r 1.0:1.2 lib/
2582 hg diff --git -r 1.0:1.2 lib/
2583
2583
2584 - get change stats relative to the last change on some date::
2584 - get change stats relative to the last change on some date::
2585
2585
2586 hg diff --stat -r "date('may 2')"
2586 hg diff --stat -r "date('may 2')"
2587
2587
2588 - diff all newly-added files that contain a keyword::
2588 - diff all newly-added files that contain a keyword::
2589
2589
2590 hg diff "set:added() and grep(GNU)"
2590 hg diff "set:added() and grep(GNU)"
2591
2591
2592 - compare a revision and its parents::
2592 - compare a revision and its parents::
2593
2593
2594 hg diff -c 9353 # compare against first parent
2594 hg diff -c 9353 # compare against first parent
2595 hg diff -r 9353^:9353 # same using revset syntax
2595 hg diff -r 9353^:9353 # same using revset syntax
2596 hg diff -r 9353^2:9353 # compare against the second parent
2596 hg diff -r 9353^2:9353 # compare against the second parent
2597
2597
2598 Returns 0 on success.
2598 Returns 0 on success.
2599 """
2599 """
2600
2600
2601 revs = opts.get('rev')
2601 revs = opts.get('rev')
2602 change = opts.get('change')
2602 change = opts.get('change')
2603 stat = opts.get('stat')
2603 stat = opts.get('stat')
2604 reverse = opts.get('reverse')
2604 reverse = opts.get('reverse')
2605
2605
2606 if revs and change:
2606 if revs and change:
2607 msg = _('cannot specify --rev and --change at the same time')
2607 msg = _('cannot specify --rev and --change at the same time')
2608 raise util.Abort(msg)
2608 raise util.Abort(msg)
2609 elif change:
2609 elif change:
2610 node2 = scmutil.revsingle(repo, change, None).node()
2610 node2 = scmutil.revsingle(repo, change, None).node()
2611 node1 = repo[node2].p1().node()
2611 node1 = repo[node2].p1().node()
2612 else:
2612 else:
2613 node1, node2 = scmutil.revpair(repo, revs)
2613 node1, node2 = scmutil.revpair(repo, revs)
2614
2614
2615 if reverse:
2615 if reverse:
2616 node1, node2 = node2, node1
2616 node1, node2 = node2, node1
2617
2617
2618 diffopts = patch.diffopts(ui, opts)
2618 diffopts = patch.diffopts(ui, opts)
2619 m = scmutil.match(repo[node2], pats, opts)
2619 m = scmutil.match(repo[node2], pats, opts)
2620 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2620 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2621 listsubrepos=opts.get('subrepos'))
2621 listsubrepos=opts.get('subrepos'))
2622
2622
2623 @command('^export',
2623 @command('^export',
2624 [('o', 'output', '',
2624 [('o', 'output', '',
2625 _('print output to file with formatted name'), _('FORMAT')),
2625 _('print output to file with formatted name'), _('FORMAT')),
2626 ('', 'switch-parent', None, _('diff against the second parent')),
2626 ('', 'switch-parent', None, _('diff against the second parent')),
2627 ('r', 'rev', [], _('revisions to export'), _('REV')),
2627 ('r', 'rev', [], _('revisions to export'), _('REV')),
2628 ] + diffopts,
2628 ] + diffopts,
2629 _('[OPTION]... [-o OUTFILESPEC] [-r] REV...'))
2629 _('[OPTION]... [-o OUTFILESPEC] [-r] REV...'))
2630 def export(ui, repo, *changesets, **opts):
2630 def export(ui, repo, *changesets, **opts):
2631 """dump the header and diffs for one or more changesets
2631 """dump the header and diffs for one or more changesets
2632
2632
2633 Print the changeset header and diffs for one or more revisions.
2633 Print the changeset header and diffs for one or more revisions.
2634
2634
2635 The information shown in the changeset header is: author, date,
2635 The information shown in the changeset header is: author, date,
2636 branch name (if non-default), changeset hash, parent(s) and commit
2636 branch name (if non-default), changeset hash, parent(s) and commit
2637 comment.
2637 comment.
2638
2638
2639 .. note::
2639 .. note::
2640 export may generate unexpected diff output for merge
2640 export may generate unexpected diff output for merge
2641 changesets, as it will compare the merge changeset against its
2641 changesets, as it will compare the merge changeset against its
2642 first parent only.
2642 first parent only.
2643
2643
2644 Output may be to a file, in which case the name of the file is
2644 Output may be to a file, in which case the name of the file is
2645 given using a format string. The formatting rules are as follows:
2645 given using a format string. The formatting rules are as follows:
2646
2646
2647 :``%%``: literal "%" character
2647 :``%%``: literal "%" character
2648 :``%H``: changeset hash (40 hexadecimal digits)
2648 :``%H``: changeset hash (40 hexadecimal digits)
2649 :``%N``: number of patches being generated
2649 :``%N``: number of patches being generated
2650 :``%R``: changeset revision number
2650 :``%R``: changeset revision number
2651 :``%b``: basename of the exporting repository
2651 :``%b``: basename of the exporting repository
2652 :``%h``: short-form changeset hash (12 hexadecimal digits)
2652 :``%h``: short-form changeset hash (12 hexadecimal digits)
2653 :``%m``: first line of the commit message (only alphanumeric characters)
2653 :``%m``: first line of the commit message (only alphanumeric characters)
2654 :``%n``: zero-padded sequence number, starting at 1
2654 :``%n``: zero-padded sequence number, starting at 1
2655 :``%r``: zero-padded changeset revision number
2655 :``%r``: zero-padded changeset revision number
2656
2656
2657 Without the -a/--text option, export will avoid generating diffs
2657 Without the -a/--text option, export will avoid generating diffs
2658 of files it detects as binary. With -a, export will generate a
2658 of files it detects as binary. With -a, export will generate a
2659 diff anyway, probably with undesirable results.
2659 diff anyway, probably with undesirable results.
2660
2660
2661 Use the -g/--git option to generate diffs in the git extended diff
2661 Use the -g/--git option to generate diffs in the git extended diff
2662 format. See :hg:`help diffs` for more information.
2662 format. See :hg:`help diffs` for more information.
2663
2663
2664 With the --switch-parent option, the diff will be against the
2664 With the --switch-parent option, the diff will be against the
2665 second parent. It can be useful to review a merge.
2665 second parent. It can be useful to review a merge.
2666
2666
2667 .. container:: verbose
2667 .. container:: verbose
2668
2668
2669 Examples:
2669 Examples:
2670
2670
2671 - use export and import to transplant a bugfix to the current
2671 - use export and import to transplant a bugfix to the current
2672 branch::
2672 branch::
2673
2673
2674 hg export -r 9353 | hg import -
2674 hg export -r 9353 | hg import -
2675
2675
2676 - export all the changesets between two revisions to a file with
2676 - export all the changesets between two revisions to a file with
2677 rename information::
2677 rename information::
2678
2678
2679 hg export --git -r 123:150 > changes.txt
2679 hg export --git -r 123:150 > changes.txt
2680
2680
2681 - split outgoing changes into a series of patches with
2681 - split outgoing changes into a series of patches with
2682 descriptive names::
2682 descriptive names::
2683
2683
2684 hg export -r "outgoing()" -o "%n-%m.patch"
2684 hg export -r "outgoing()" -o "%n-%m.patch"
2685
2685
2686 Returns 0 on success.
2686 Returns 0 on success.
2687 """
2687 """
2688 changesets += tuple(opts.get('rev', []))
2688 changesets += tuple(opts.get('rev', []))
2689 revs = scmutil.revrange(repo, changesets)
2689 revs = scmutil.revrange(repo, changesets)
2690 if not revs:
2690 if not revs:
2691 raise util.Abort(_("export requires at least one changeset"))
2691 raise util.Abort(_("export requires at least one changeset"))
2692 if len(revs) > 1:
2692 if len(revs) > 1:
2693 ui.note(_('exporting patches:\n'))
2693 ui.note(_('exporting patches:\n'))
2694 else:
2694 else:
2695 ui.note(_('exporting patch:\n'))
2695 ui.note(_('exporting patch:\n'))
2696 cmdutil.export(repo, revs, template=opts.get('output'),
2696 cmdutil.export(repo, revs, template=opts.get('output'),
2697 switch_parent=opts.get('switch_parent'),
2697 switch_parent=opts.get('switch_parent'),
2698 opts=patch.diffopts(ui, opts))
2698 opts=patch.diffopts(ui, opts))
2699
2699
2700 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2700 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2701 def forget(ui, repo, *pats, **opts):
2701 def forget(ui, repo, *pats, **opts):
2702 """forget the specified files on the next commit
2702 """forget the specified files on the next commit
2703
2703
2704 Mark the specified files so they will no longer be tracked
2704 Mark the specified files so they will no longer be tracked
2705 after the next commit.
2705 after the next commit.
2706
2706
2707 This only removes files from the current branch, not from the
2707 This only removes files from the current branch, not from the
2708 entire project history, and it does not delete them from the
2708 entire project history, and it does not delete them from the
2709 working directory.
2709 working directory.
2710
2710
2711 To undo a forget before the next commit, see :hg:`add`.
2711 To undo a forget before the next commit, see :hg:`add`.
2712
2712
2713 .. container:: verbose
2713 .. container:: verbose
2714
2714
2715 Examples:
2715 Examples:
2716
2716
2717 - forget newly-added binary files::
2717 - forget newly-added binary files::
2718
2718
2719 hg forget "set:added() and binary()"
2719 hg forget "set:added() and binary()"
2720
2720
2721 - forget files that would be excluded by .hgignore::
2721 - forget files that would be excluded by .hgignore::
2722
2722
2723 hg forget "set:hgignore()"
2723 hg forget "set:hgignore()"
2724
2724
2725 Returns 0 on success.
2725 Returns 0 on success.
2726 """
2726 """
2727
2727
2728 if not pats:
2728 if not pats:
2729 raise util.Abort(_('no files specified'))
2729 raise util.Abort(_('no files specified'))
2730
2730
2731 m = scmutil.match(repo[None], pats, opts)
2731 m = scmutil.match(repo[None], pats, opts)
2732 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2732 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2733 return rejected and 1 or 0
2733 return rejected and 1 or 0
2734
2734
2735 @command(
2735 @command(
2736 'graft',
2736 'graft',
2737 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2737 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2738 ('c', 'continue', False, _('resume interrupted graft')),
2738 ('c', 'continue', False, _('resume interrupted graft')),
2739 ('e', 'edit', False, _('invoke editor on commit messages')),
2739 ('e', 'edit', False, _('invoke editor on commit messages')),
2740 ('', 'log', None, _('append graft info to log message')),
2740 ('', 'log', None, _('append graft info to log message')),
2741 ('D', 'currentdate', False,
2741 ('D', 'currentdate', False,
2742 _('record the current date as commit date')),
2742 _('record the current date as commit date')),
2743 ('U', 'currentuser', False,
2743 ('U', 'currentuser', False,
2744 _('record the current user as committer'), _('DATE'))]
2744 _('record the current user as committer'), _('DATE'))]
2745 + commitopts2 + mergetoolopts + dryrunopts,
2745 + commitopts2 + mergetoolopts + dryrunopts,
2746 _('[OPTION]... [-r] REV...'))
2746 _('[OPTION]... [-r] REV...'))
2747 def graft(ui, repo, *revs, **opts):
2747 def graft(ui, repo, *revs, **opts):
2748 '''copy changes from other branches onto the current branch
2748 '''copy changes from other branches onto the current branch
2749
2749
2750 This command uses Mercurial's merge logic to copy individual
2750 This command uses Mercurial's merge logic to copy individual
2751 changes from other branches without merging branches in the
2751 changes from other branches without merging branches in the
2752 history graph. This is sometimes known as 'backporting' or
2752 history graph. This is sometimes known as 'backporting' or
2753 'cherry-picking'. By default, graft will copy user, date, and
2753 'cherry-picking'. By default, graft will copy user, date, and
2754 description from the source changesets.
2754 description from the source changesets.
2755
2755
2756 Changesets that are ancestors of the current revision, that have
2756 Changesets that are ancestors of the current revision, that have
2757 already been grafted, or that are merges will be skipped.
2757 already been grafted, or that are merges will be skipped.
2758
2758
2759 If --log is specified, log messages will have a comment appended
2759 If --log is specified, log messages will have a comment appended
2760 of the form::
2760 of the form::
2761
2761
2762 (grafted from CHANGESETHASH)
2762 (grafted from CHANGESETHASH)
2763
2763
2764 If a graft merge results in conflicts, the graft process is
2764 If a graft merge results in conflicts, the graft process is
2765 interrupted so that the current merge can be manually resolved.
2765 interrupted so that the current merge can be manually resolved.
2766 Once all conflicts are addressed, the graft process can be
2766 Once all conflicts are addressed, the graft process can be
2767 continued with the -c/--continue option.
2767 continued with the -c/--continue option.
2768
2768
2769 .. note::
2769 .. note::
2770 The -c/--continue option does not reapply earlier options.
2770 The -c/--continue option does not reapply earlier options.
2771
2771
2772 .. container:: verbose
2772 .. container:: verbose
2773
2773
2774 Examples:
2774 Examples:
2775
2775
2776 - copy a single change to the stable branch and edit its description::
2776 - copy a single change to the stable branch and edit its description::
2777
2777
2778 hg update stable
2778 hg update stable
2779 hg graft --edit 9393
2779 hg graft --edit 9393
2780
2780
2781 - graft a range of changesets with one exception, updating dates::
2781 - graft a range of changesets with one exception, updating dates::
2782
2782
2783 hg graft -D "2085::2093 and not 2091"
2783 hg graft -D "2085::2093 and not 2091"
2784
2784
2785 - continue a graft after resolving conflicts::
2785 - continue a graft after resolving conflicts::
2786
2786
2787 hg graft -c
2787 hg graft -c
2788
2788
2789 - show the source of a grafted changeset::
2789 - show the source of a grafted changeset::
2790
2790
2791 hg log --debug -r tip
2791 hg log --debug -r tip
2792
2792
2793 Returns 0 on successful completion.
2793 Returns 0 on successful completion.
2794 '''
2794 '''
2795
2795
2796 revs = list(revs)
2796 revs = list(revs)
2797 revs.extend(opts['rev'])
2797 revs.extend(opts['rev'])
2798
2798
2799 if not opts.get('user') and opts.get('currentuser'):
2799 if not opts.get('user') and opts.get('currentuser'):
2800 opts['user'] = ui.username()
2800 opts['user'] = ui.username()
2801 if not opts.get('date') and opts.get('currentdate'):
2801 if not opts.get('date') and opts.get('currentdate'):
2802 opts['date'] = "%d %d" % util.makedate()
2802 opts['date'] = "%d %d" % util.makedate()
2803
2803
2804 editor = None
2804 editor = None
2805 if opts.get('edit'):
2805 if opts.get('edit'):
2806 editor = cmdutil.commitforceeditor
2806 editor = cmdutil.commitforceeditor
2807
2807
2808 cont = False
2808 cont = False
2809 if opts['continue']:
2809 if opts['continue']:
2810 cont = True
2810 cont = True
2811 if revs:
2811 if revs:
2812 raise util.Abort(_("can't specify --continue and revisions"))
2812 raise util.Abort(_("can't specify --continue and revisions"))
2813 # read in unfinished revisions
2813 # read in unfinished revisions
2814 try:
2814 try:
2815 nodes = repo.opener.read('graftstate').splitlines()
2815 nodes = repo.opener.read('graftstate').splitlines()
2816 revs = [repo[node].rev() for node in nodes]
2816 revs = [repo[node].rev() for node in nodes]
2817 except IOError, inst:
2817 except IOError, inst:
2818 if inst.errno != errno.ENOENT:
2818 if inst.errno != errno.ENOENT:
2819 raise
2819 raise
2820 raise util.Abort(_("no graft state found, can't continue"))
2820 raise util.Abort(_("no graft state found, can't continue"))
2821 else:
2821 else:
2822 cmdutil.bailifchanged(repo)
2822 cmdutil.bailifchanged(repo)
2823 if not revs:
2823 if not revs:
2824 raise util.Abort(_('no revisions specified'))
2824 raise util.Abort(_('no revisions specified'))
2825 revs = scmutil.revrange(repo, revs)
2825 revs = scmutil.revrange(repo, revs)
2826
2826
2827 # check for merges
2827 # check for merges
2828 for rev in repo.revs('%ld and merge()', revs):
2828 for rev in repo.revs('%ld and merge()', revs):
2829 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2829 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2830 revs.remove(rev)
2830 revs.remove(rev)
2831 if not revs:
2831 if not revs:
2832 return -1
2832 return -1
2833
2833
2834 # check for ancestors of dest branch
2834 # check for ancestors of dest branch
2835 for rev in repo.revs('::. and %ld', revs):
2835 for rev in repo.revs('::. and %ld', revs):
2836 ui.warn(_('skipping ancestor revision %s\n') % rev)
2836 ui.warn(_('skipping ancestor revision %s\n') % rev)
2837 revs.remove(rev)
2837 revs.remove(rev)
2838 if not revs:
2838 if not revs:
2839 return -1
2839 return -1
2840
2840
2841 # analyze revs for earlier grafts
2841 # analyze revs for earlier grafts
2842 ids = {}
2842 ids = {}
2843 for ctx in repo.set("%ld", revs):
2843 for ctx in repo.set("%ld", revs):
2844 ids[ctx.hex()] = ctx.rev()
2844 ids[ctx.hex()] = ctx.rev()
2845 n = ctx.extra().get('source')
2845 n = ctx.extra().get('source')
2846 if n:
2846 if n:
2847 ids[n] = ctx.rev()
2847 ids[n] = ctx.rev()
2848
2848
2849 # check ancestors for earlier grafts
2849 # check ancestors for earlier grafts
2850 ui.debug('scanning for duplicate grafts\n')
2850 ui.debug('scanning for duplicate grafts\n')
2851 for ctx in repo.set("::. - ::%ld", revs):
2851 for ctx in repo.set("::. - ::%ld", revs):
2852 n = ctx.extra().get('source')
2852 n = ctx.extra().get('source')
2853 if n in ids:
2853 if n in ids:
2854 r = repo[n].rev()
2854 r = repo[n].rev()
2855 if r in revs:
2855 if r in revs:
2856 ui.warn(_('skipping already grafted revision %s\n') % r)
2856 ui.warn(_('skipping already grafted revision %s\n') % r)
2857 revs.remove(r)
2857 revs.remove(r)
2858 elif ids[n] in revs:
2858 elif ids[n] in revs:
2859 ui.warn(_('skipping already grafted revision %s '
2859 ui.warn(_('skipping already grafted revision %s '
2860 '(same origin %d)\n') % (ids[n], r))
2860 '(same origin %d)\n') % (ids[n], r))
2861 revs.remove(ids[n])
2861 revs.remove(ids[n])
2862 elif ctx.hex() in ids:
2862 elif ctx.hex() in ids:
2863 r = ids[ctx.hex()]
2863 r = ids[ctx.hex()]
2864 ui.warn(_('skipping already grafted revision %s '
2864 ui.warn(_('skipping already grafted revision %s '
2865 '(was grafted from %d)\n') % (r, ctx.rev()))
2865 '(was grafted from %d)\n') % (r, ctx.rev()))
2866 revs.remove(r)
2866 revs.remove(r)
2867 if not revs:
2867 if not revs:
2868 return -1
2868 return -1
2869
2869
2870 wlock = repo.wlock()
2870 wlock = repo.wlock()
2871 try:
2871 try:
2872 current = repo['.']
2872 current = repo['.']
2873 for pos, ctx in enumerate(repo.set("%ld", revs)):
2873 for pos, ctx in enumerate(repo.set("%ld", revs)):
2874
2874
2875 ui.status(_('grafting revision %s\n') % ctx.rev())
2875 ui.status(_('grafting revision %s\n') % ctx.rev())
2876 if opts.get('dry_run'):
2876 if opts.get('dry_run'):
2877 continue
2877 continue
2878
2878
2879 source = ctx.extra().get('source')
2879 source = ctx.extra().get('source')
2880 if not source:
2880 if not source:
2881 source = ctx.hex()
2881 source = ctx.hex()
2882 extra = {'source': source}
2882 extra = {'source': source}
2883 user = ctx.user()
2883 user = ctx.user()
2884 if opts.get('user'):
2884 if opts.get('user'):
2885 user = opts['user']
2885 user = opts['user']
2886 date = ctx.date()
2886 date = ctx.date()
2887 if opts.get('date'):
2887 if opts.get('date'):
2888 date = opts['date']
2888 date = opts['date']
2889 message = ctx.description()
2889 message = ctx.description()
2890 if opts.get('log'):
2890 if opts.get('log'):
2891 message += '\n(grafted from %s)' % ctx.hex()
2891 message += '\n(grafted from %s)' % ctx.hex()
2892
2892
2893 # we don't merge the first commit when continuing
2893 # we don't merge the first commit when continuing
2894 if not cont:
2894 if not cont:
2895 # perform the graft merge with p1(rev) as 'ancestor'
2895 # perform the graft merge with p1(rev) as 'ancestor'
2896 try:
2896 try:
2897 # ui.forcemerge is an internal variable, do not document
2897 # ui.forcemerge is an internal variable, do not document
2898 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2898 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2899 stats = mergemod.update(repo, ctx.node(), True, True, False,
2899 stats = mergemod.update(repo, ctx.node(), True, True, False,
2900 ctx.p1().node())
2900 ctx.p1().node())
2901 finally:
2901 finally:
2902 repo.ui.setconfig('ui', 'forcemerge', '')
2902 repo.ui.setconfig('ui', 'forcemerge', '')
2903 # report any conflicts
2903 # report any conflicts
2904 if stats and stats[3] > 0:
2904 if stats and stats[3] > 0:
2905 # write out state for --continue
2905 # write out state for --continue
2906 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2906 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2907 repo.opener.write('graftstate', ''.join(nodelines))
2907 repo.opener.write('graftstate', ''.join(nodelines))
2908 raise util.Abort(
2908 raise util.Abort(
2909 _("unresolved conflicts, can't continue"),
2909 _("unresolved conflicts, can't continue"),
2910 hint=_('use hg resolve and hg graft --continue'))
2910 hint=_('use hg resolve and hg graft --continue'))
2911 else:
2911 else:
2912 cont = False
2912 cont = False
2913
2913
2914 # drop the second merge parent
2914 # drop the second merge parent
2915 repo.setparents(current.node(), nullid)
2915 repo.setparents(current.node(), nullid)
2916 repo.dirstate.write()
2916 repo.dirstate.write()
2917 # fix up dirstate for copies and renames
2917 # fix up dirstate for copies and renames
2918 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
2918 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
2919
2919
2920 # commit
2920 # commit
2921 node = repo.commit(text=message, user=user,
2921 node = repo.commit(text=message, user=user,
2922 date=date, extra=extra, editor=editor)
2922 date=date, extra=extra, editor=editor)
2923 if node is None:
2923 if node is None:
2924 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
2924 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
2925 else:
2925 else:
2926 current = repo[node]
2926 current = repo[node]
2927 finally:
2927 finally:
2928 wlock.release()
2928 wlock.release()
2929
2929
2930 # remove state when we complete successfully
2930 # remove state when we complete successfully
2931 if not opts.get('dry_run'):
2931 if not opts.get('dry_run'):
2932 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
2932 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
2933
2933
2934 return 0
2934 return 0
2935
2935
2936 @command('grep',
2936 @command('grep',
2937 [('0', 'print0', None, _('end fields with NUL')),
2937 [('0', 'print0', None, _('end fields with NUL')),
2938 ('', 'all', None, _('print all revisions that match')),
2938 ('', 'all', None, _('print all revisions that match')),
2939 ('a', 'text', None, _('treat all files as text')),
2939 ('a', 'text', None, _('treat all files as text')),
2940 ('f', 'follow', None,
2940 ('f', 'follow', None,
2941 _('follow changeset history,'
2941 _('follow changeset history,'
2942 ' or file history across copies and renames')),
2942 ' or file history across copies and renames')),
2943 ('i', 'ignore-case', None, _('ignore case when matching')),
2943 ('i', 'ignore-case', None, _('ignore case when matching')),
2944 ('l', 'files-with-matches', None,
2944 ('l', 'files-with-matches', None,
2945 _('print only filenames and revisions that match')),
2945 _('print only filenames and revisions that match')),
2946 ('n', 'line-number', None, _('print matching line numbers')),
2946 ('n', 'line-number', None, _('print matching line numbers')),
2947 ('r', 'rev', [],
2947 ('r', 'rev', [],
2948 _('only search files changed within revision range'), _('REV')),
2948 _('only search files changed within revision range'), _('REV')),
2949 ('u', 'user', None, _('list the author (long with -v)')),
2949 ('u', 'user', None, _('list the author (long with -v)')),
2950 ('d', 'date', None, _('list the date (short with -q)')),
2950 ('d', 'date', None, _('list the date (short with -q)')),
2951 ] + walkopts,
2951 ] + walkopts,
2952 _('[OPTION]... PATTERN [FILE]...'))
2952 _('[OPTION]... PATTERN [FILE]...'))
2953 def grep(ui, repo, pattern, *pats, **opts):
2953 def grep(ui, repo, pattern, *pats, **opts):
2954 """search for a pattern in specified files and revisions
2954 """search for a pattern in specified files and revisions
2955
2955
2956 Search revisions of files for a regular expression.
2956 Search revisions of files for a regular expression.
2957
2957
2958 This command behaves differently than Unix grep. It only accepts
2958 This command behaves differently than Unix grep. It only accepts
2959 Python/Perl regexps. It searches repository history, not the
2959 Python/Perl regexps. It searches repository history, not the
2960 working directory. It always prints the revision number in which a
2960 working directory. It always prints the revision number in which a
2961 match appears.
2961 match appears.
2962
2962
2963 By default, grep only prints output for the first revision of a
2963 By default, grep only prints output for the first revision of a
2964 file in which it finds a match. To get it to print every revision
2964 file in which it finds a match. To get it to print every revision
2965 that contains a change in match status ("-" for a match that
2965 that contains a change in match status ("-" for a match that
2966 becomes a non-match, or "+" for a non-match that becomes a match),
2966 becomes a non-match, or "+" for a non-match that becomes a match),
2967 use the --all flag.
2967 use the --all flag.
2968
2968
2969 Returns 0 if a match is found, 1 otherwise.
2969 Returns 0 if a match is found, 1 otherwise.
2970 """
2970 """
2971 reflags = re.M
2971 reflags = re.M
2972 if opts.get('ignore_case'):
2972 if opts.get('ignore_case'):
2973 reflags |= re.I
2973 reflags |= re.I
2974 try:
2974 try:
2975 regexp = re.compile(pattern, reflags)
2975 regexp = util.compilere(pattern, reflags)
2976 except re.error, inst:
2976 except re.error, inst:
2977 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2977 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2978 return 1
2978 return 1
2979 sep, eol = ':', '\n'
2979 sep, eol = ':', '\n'
2980 if opts.get('print0'):
2980 if opts.get('print0'):
2981 sep = eol = '\0'
2981 sep = eol = '\0'
2982
2982
2983 getfile = util.lrucachefunc(repo.file)
2983 getfile = util.lrucachefunc(repo.file)
2984
2984
2985 def matchlines(body):
2985 def matchlines(body):
2986 begin = 0
2986 begin = 0
2987 linenum = 0
2987 linenum = 0
2988 while begin < len(body):
2988 while begin < len(body):
2989 match = regexp.search(body, begin)
2989 match = regexp.search(body, begin)
2990 if not match:
2990 if not match:
2991 break
2991 break
2992 mstart, mend = match.span()
2992 mstart, mend = match.span()
2993 linenum += body.count('\n', begin, mstart) + 1
2993 linenum += body.count('\n', begin, mstart) + 1
2994 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2994 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2995 begin = body.find('\n', mend) + 1 or len(body) + 1
2995 begin = body.find('\n', mend) + 1 or len(body) + 1
2996 lend = begin - 1
2996 lend = begin - 1
2997 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2997 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2998
2998
2999 class linestate(object):
2999 class linestate(object):
3000 def __init__(self, line, linenum, colstart, colend):
3000 def __init__(self, line, linenum, colstart, colend):
3001 self.line = line
3001 self.line = line
3002 self.linenum = linenum
3002 self.linenum = linenum
3003 self.colstart = colstart
3003 self.colstart = colstart
3004 self.colend = colend
3004 self.colend = colend
3005
3005
3006 def __hash__(self):
3006 def __hash__(self):
3007 return hash((self.linenum, self.line))
3007 return hash((self.linenum, self.line))
3008
3008
3009 def __eq__(self, other):
3009 def __eq__(self, other):
3010 return self.line == other.line
3010 return self.line == other.line
3011
3011
3012 matches = {}
3012 matches = {}
3013 copies = {}
3013 copies = {}
3014 def grepbody(fn, rev, body):
3014 def grepbody(fn, rev, body):
3015 matches[rev].setdefault(fn, [])
3015 matches[rev].setdefault(fn, [])
3016 m = matches[rev][fn]
3016 m = matches[rev][fn]
3017 for lnum, cstart, cend, line in matchlines(body):
3017 for lnum, cstart, cend, line in matchlines(body):
3018 s = linestate(line, lnum, cstart, cend)
3018 s = linestate(line, lnum, cstart, cend)
3019 m.append(s)
3019 m.append(s)
3020
3020
3021 def difflinestates(a, b):
3021 def difflinestates(a, b):
3022 sm = difflib.SequenceMatcher(None, a, b)
3022 sm = difflib.SequenceMatcher(None, a, b)
3023 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3023 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3024 if tag == 'insert':
3024 if tag == 'insert':
3025 for i in xrange(blo, bhi):
3025 for i in xrange(blo, bhi):
3026 yield ('+', b[i])
3026 yield ('+', b[i])
3027 elif tag == 'delete':
3027 elif tag == 'delete':
3028 for i in xrange(alo, ahi):
3028 for i in xrange(alo, ahi):
3029 yield ('-', a[i])
3029 yield ('-', a[i])
3030 elif tag == 'replace':
3030 elif tag == 'replace':
3031 for i in xrange(alo, ahi):
3031 for i in xrange(alo, ahi):
3032 yield ('-', a[i])
3032 yield ('-', a[i])
3033 for i in xrange(blo, bhi):
3033 for i in xrange(blo, bhi):
3034 yield ('+', b[i])
3034 yield ('+', b[i])
3035
3035
3036 def display(fn, ctx, pstates, states):
3036 def display(fn, ctx, pstates, states):
3037 rev = ctx.rev()
3037 rev = ctx.rev()
3038 datefunc = ui.quiet and util.shortdate or util.datestr
3038 datefunc = ui.quiet and util.shortdate or util.datestr
3039 found = False
3039 found = False
3040 filerevmatches = {}
3040 filerevmatches = {}
3041 def binary():
3041 def binary():
3042 flog = getfile(fn)
3042 flog = getfile(fn)
3043 return util.binary(flog.read(ctx.filenode(fn)))
3043 return util.binary(flog.read(ctx.filenode(fn)))
3044
3044
3045 if opts.get('all'):
3045 if opts.get('all'):
3046 iter = difflinestates(pstates, states)
3046 iter = difflinestates(pstates, states)
3047 else:
3047 else:
3048 iter = [('', l) for l in states]
3048 iter = [('', l) for l in states]
3049 for change, l in iter:
3049 for change, l in iter:
3050 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3050 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3051 before, match, after = None, None, None
3051 before, match, after = None, None, None
3052
3052
3053 if opts.get('line_number'):
3053 if opts.get('line_number'):
3054 cols.append((str(l.linenum), 'grep.linenumber'))
3054 cols.append((str(l.linenum), 'grep.linenumber'))
3055 if opts.get('all'):
3055 if opts.get('all'):
3056 cols.append((change, 'grep.change'))
3056 cols.append((change, 'grep.change'))
3057 if opts.get('user'):
3057 if opts.get('user'):
3058 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3058 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3059 if opts.get('date'):
3059 if opts.get('date'):
3060 cols.append((datefunc(ctx.date()), 'grep.date'))
3060 cols.append((datefunc(ctx.date()), 'grep.date'))
3061 if opts.get('files_with_matches'):
3061 if opts.get('files_with_matches'):
3062 c = (fn, rev)
3062 c = (fn, rev)
3063 if c in filerevmatches:
3063 if c in filerevmatches:
3064 continue
3064 continue
3065 filerevmatches[c] = 1
3065 filerevmatches[c] = 1
3066 else:
3066 else:
3067 before = l.line[:l.colstart]
3067 before = l.line[:l.colstart]
3068 match = l.line[l.colstart:l.colend]
3068 match = l.line[l.colstart:l.colend]
3069 after = l.line[l.colend:]
3069 after = l.line[l.colend:]
3070 for col, label in cols[:-1]:
3070 for col, label in cols[:-1]:
3071 ui.write(col, label=label)
3071 ui.write(col, label=label)
3072 ui.write(sep, label='grep.sep')
3072 ui.write(sep, label='grep.sep')
3073 ui.write(cols[-1][0], label=cols[-1][1])
3073 ui.write(cols[-1][0], label=cols[-1][1])
3074 if before is not None:
3074 if before is not None:
3075 ui.write(sep, label='grep.sep')
3075 ui.write(sep, label='grep.sep')
3076 if not opts.get('text') and binary():
3076 if not opts.get('text') and binary():
3077 ui.write(" Binary file matches")
3077 ui.write(" Binary file matches")
3078 else:
3078 else:
3079 ui.write(before)
3079 ui.write(before)
3080 ui.write(match, label='grep.match')
3080 ui.write(match, label='grep.match')
3081 ui.write(after)
3081 ui.write(after)
3082 ui.write(eol)
3082 ui.write(eol)
3083 found = True
3083 found = True
3084 return found
3084 return found
3085
3085
3086 skip = {}
3086 skip = {}
3087 revfiles = {}
3087 revfiles = {}
3088 matchfn = scmutil.match(repo[None], pats, opts)
3088 matchfn = scmutil.match(repo[None], pats, opts)
3089 found = False
3089 found = False
3090 follow = opts.get('follow')
3090 follow = opts.get('follow')
3091
3091
3092 def prep(ctx, fns):
3092 def prep(ctx, fns):
3093 rev = ctx.rev()
3093 rev = ctx.rev()
3094 pctx = ctx.p1()
3094 pctx = ctx.p1()
3095 parent = pctx.rev()
3095 parent = pctx.rev()
3096 matches.setdefault(rev, {})
3096 matches.setdefault(rev, {})
3097 matches.setdefault(parent, {})
3097 matches.setdefault(parent, {})
3098 files = revfiles.setdefault(rev, [])
3098 files = revfiles.setdefault(rev, [])
3099 for fn in fns:
3099 for fn in fns:
3100 flog = getfile(fn)
3100 flog = getfile(fn)
3101 try:
3101 try:
3102 fnode = ctx.filenode(fn)
3102 fnode = ctx.filenode(fn)
3103 except error.LookupError:
3103 except error.LookupError:
3104 continue
3104 continue
3105
3105
3106 copied = flog.renamed(fnode)
3106 copied = flog.renamed(fnode)
3107 copy = follow and copied and copied[0]
3107 copy = follow and copied and copied[0]
3108 if copy:
3108 if copy:
3109 copies.setdefault(rev, {})[fn] = copy
3109 copies.setdefault(rev, {})[fn] = copy
3110 if fn in skip:
3110 if fn in skip:
3111 if copy:
3111 if copy:
3112 skip[copy] = True
3112 skip[copy] = True
3113 continue
3113 continue
3114 files.append(fn)
3114 files.append(fn)
3115
3115
3116 if fn not in matches[rev]:
3116 if fn not in matches[rev]:
3117 grepbody(fn, rev, flog.read(fnode))
3117 grepbody(fn, rev, flog.read(fnode))
3118
3118
3119 pfn = copy or fn
3119 pfn = copy or fn
3120 if pfn not in matches[parent]:
3120 if pfn not in matches[parent]:
3121 try:
3121 try:
3122 fnode = pctx.filenode(pfn)
3122 fnode = pctx.filenode(pfn)
3123 grepbody(pfn, parent, flog.read(fnode))
3123 grepbody(pfn, parent, flog.read(fnode))
3124 except error.LookupError:
3124 except error.LookupError:
3125 pass
3125 pass
3126
3126
3127 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3127 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3128 rev = ctx.rev()
3128 rev = ctx.rev()
3129 parent = ctx.p1().rev()
3129 parent = ctx.p1().rev()
3130 for fn in sorted(revfiles.get(rev, [])):
3130 for fn in sorted(revfiles.get(rev, [])):
3131 states = matches[rev][fn]
3131 states = matches[rev][fn]
3132 copy = copies.get(rev, {}).get(fn)
3132 copy = copies.get(rev, {}).get(fn)
3133 if fn in skip:
3133 if fn in skip:
3134 if copy:
3134 if copy:
3135 skip[copy] = True
3135 skip[copy] = True
3136 continue
3136 continue
3137 pstates = matches.get(parent, {}).get(copy or fn, [])
3137 pstates = matches.get(parent, {}).get(copy or fn, [])
3138 if pstates or states:
3138 if pstates or states:
3139 r = display(fn, ctx, pstates, states)
3139 r = display(fn, ctx, pstates, states)
3140 found = found or r
3140 found = found or r
3141 if r and not opts.get('all'):
3141 if r and not opts.get('all'):
3142 skip[fn] = True
3142 skip[fn] = True
3143 if copy:
3143 if copy:
3144 skip[copy] = True
3144 skip[copy] = True
3145 del matches[rev]
3145 del matches[rev]
3146 del revfiles[rev]
3146 del revfiles[rev]
3147
3147
3148 return not found
3148 return not found
3149
3149
3150 @command('heads',
3150 @command('heads',
3151 [('r', 'rev', '',
3151 [('r', 'rev', '',
3152 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3152 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3153 ('t', 'topo', False, _('show topological heads only')),
3153 ('t', 'topo', False, _('show topological heads only')),
3154 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3154 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3155 ('c', 'closed', False, _('show normal and closed branch heads')),
3155 ('c', 'closed', False, _('show normal and closed branch heads')),
3156 ] + templateopts,
3156 ] + templateopts,
3157 _('[-ct] [-r STARTREV] [REV]...'))
3157 _('[-ct] [-r STARTREV] [REV]...'))
3158 def heads(ui, repo, *branchrevs, **opts):
3158 def heads(ui, repo, *branchrevs, **opts):
3159 """show current repository heads or show branch heads
3159 """show current repository heads or show branch heads
3160
3160
3161 With no arguments, show all repository branch heads.
3161 With no arguments, show all repository branch heads.
3162
3162
3163 Repository "heads" are changesets with no child changesets. They are
3163 Repository "heads" are changesets with no child changesets. They are
3164 where development generally takes place and are the usual targets
3164 where development generally takes place and are the usual targets
3165 for update and merge operations. Branch heads are changesets that have
3165 for update and merge operations. Branch heads are changesets that have
3166 no child changeset on the same branch.
3166 no child changeset on the same branch.
3167
3167
3168 If one or more REVs are given, only branch heads on the branches
3168 If one or more REVs are given, only branch heads on the branches
3169 associated with the specified changesets are shown. This means
3169 associated with the specified changesets are shown. This means
3170 that you can use :hg:`heads foo` to see the heads on a branch
3170 that you can use :hg:`heads foo` to see the heads on a branch
3171 named ``foo``.
3171 named ``foo``.
3172
3172
3173 If -c/--closed is specified, also show branch heads marked closed
3173 If -c/--closed is specified, also show branch heads marked closed
3174 (see :hg:`commit --close-branch`).
3174 (see :hg:`commit --close-branch`).
3175
3175
3176 If STARTREV is specified, only those heads that are descendants of
3176 If STARTREV is specified, only those heads that are descendants of
3177 STARTREV will be displayed.
3177 STARTREV will be displayed.
3178
3178
3179 If -t/--topo is specified, named branch mechanics will be ignored and only
3179 If -t/--topo is specified, named branch mechanics will be ignored and only
3180 changesets without children will be shown.
3180 changesets without children will be shown.
3181
3181
3182 Returns 0 if matching heads are found, 1 if not.
3182 Returns 0 if matching heads are found, 1 if not.
3183 """
3183 """
3184
3184
3185 start = None
3185 start = None
3186 if 'rev' in opts:
3186 if 'rev' in opts:
3187 start = scmutil.revsingle(repo, opts['rev'], None).node()
3187 start = scmutil.revsingle(repo, opts['rev'], None).node()
3188
3188
3189 if opts.get('topo'):
3189 if opts.get('topo'):
3190 heads = [repo[h] for h in repo.heads(start)]
3190 heads = [repo[h] for h in repo.heads(start)]
3191 else:
3191 else:
3192 heads = []
3192 heads = []
3193 for branch in repo.branchmap():
3193 for branch in repo.branchmap():
3194 heads += repo.branchheads(branch, start, opts.get('closed'))
3194 heads += repo.branchheads(branch, start, opts.get('closed'))
3195 heads = [repo[h] for h in heads]
3195 heads = [repo[h] for h in heads]
3196
3196
3197 if branchrevs:
3197 if branchrevs:
3198 branches = set(repo[br].branch() for br in branchrevs)
3198 branches = set(repo[br].branch() for br in branchrevs)
3199 heads = [h for h in heads if h.branch() in branches]
3199 heads = [h for h in heads if h.branch() in branches]
3200
3200
3201 if opts.get('active') and branchrevs:
3201 if opts.get('active') and branchrevs:
3202 dagheads = repo.heads(start)
3202 dagheads = repo.heads(start)
3203 heads = [h for h in heads if h.node() in dagheads]
3203 heads = [h for h in heads if h.node() in dagheads]
3204
3204
3205 if branchrevs:
3205 if branchrevs:
3206 haveheads = set(h.branch() for h in heads)
3206 haveheads = set(h.branch() for h in heads)
3207 if branches - haveheads:
3207 if branches - haveheads:
3208 headless = ', '.join(b for b in branches - haveheads)
3208 headless = ', '.join(b for b in branches - haveheads)
3209 msg = _('no open branch heads found on branches %s')
3209 msg = _('no open branch heads found on branches %s')
3210 if opts.get('rev'):
3210 if opts.get('rev'):
3211 msg += _(' (started at %s)') % opts['rev']
3211 msg += _(' (started at %s)') % opts['rev']
3212 ui.warn((msg + '\n') % headless)
3212 ui.warn((msg + '\n') % headless)
3213
3213
3214 if not heads:
3214 if not heads:
3215 return 1
3215 return 1
3216
3216
3217 heads = sorted(heads, key=lambda x: -x.rev())
3217 heads = sorted(heads, key=lambda x: -x.rev())
3218 displayer = cmdutil.show_changeset(ui, repo, opts)
3218 displayer = cmdutil.show_changeset(ui, repo, opts)
3219 for ctx in heads:
3219 for ctx in heads:
3220 displayer.show(ctx)
3220 displayer.show(ctx)
3221 displayer.close()
3221 displayer.close()
3222
3222
3223 @command('help',
3223 @command('help',
3224 [('e', 'extension', None, _('show only help for extensions')),
3224 [('e', 'extension', None, _('show only help for extensions')),
3225 ('c', 'command', None, _('show only help for commands')),
3225 ('c', 'command', None, _('show only help for commands')),
3226 ('k', 'keyword', '', _('show topics matching keyword')),
3226 ('k', 'keyword', '', _('show topics matching keyword')),
3227 ],
3227 ],
3228 _('[-ec] [TOPIC]'))
3228 _('[-ec] [TOPIC]'))
3229 def help_(ui, name=None, **opts):
3229 def help_(ui, name=None, **opts):
3230 """show help for a given topic or a help overview
3230 """show help for a given topic or a help overview
3231
3231
3232 With no arguments, print a list of commands with short help messages.
3232 With no arguments, print a list of commands with short help messages.
3233
3233
3234 Given a topic, extension, or command name, print help for that
3234 Given a topic, extension, or command name, print help for that
3235 topic.
3235 topic.
3236
3236
3237 Returns 0 if successful.
3237 Returns 0 if successful.
3238 """
3238 """
3239
3239
3240 textwidth = min(ui.termwidth(), 80) - 2
3240 textwidth = min(ui.termwidth(), 80) - 2
3241
3241
3242 keep = ui.verbose and ['verbose'] or []
3242 keep = ui.verbose and ['verbose'] or []
3243 text = help.help_(ui, name, **opts)
3243 text = help.help_(ui, name, **opts)
3244
3244
3245 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3245 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3246 if 'verbose' in pruned:
3246 if 'verbose' in pruned:
3247 keep.append('omitted')
3247 keep.append('omitted')
3248 else:
3248 else:
3249 keep.append('notomitted')
3249 keep.append('notomitted')
3250 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3250 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3251 ui.write(formatted)
3251 ui.write(formatted)
3252
3252
3253
3253
3254 @command('identify|id',
3254 @command('identify|id',
3255 [('r', 'rev', '',
3255 [('r', 'rev', '',
3256 _('identify the specified revision'), _('REV')),
3256 _('identify the specified revision'), _('REV')),
3257 ('n', 'num', None, _('show local revision number')),
3257 ('n', 'num', None, _('show local revision number')),
3258 ('i', 'id', None, _('show global revision id')),
3258 ('i', 'id', None, _('show global revision id')),
3259 ('b', 'branch', None, _('show branch')),
3259 ('b', 'branch', None, _('show branch')),
3260 ('t', 'tags', None, _('show tags')),
3260 ('t', 'tags', None, _('show tags')),
3261 ('B', 'bookmarks', None, _('show bookmarks')),
3261 ('B', 'bookmarks', None, _('show bookmarks')),
3262 ] + remoteopts,
3262 ] + remoteopts,
3263 _('[-nibtB] [-r REV] [SOURCE]'))
3263 _('[-nibtB] [-r REV] [SOURCE]'))
3264 def identify(ui, repo, source=None, rev=None,
3264 def identify(ui, repo, source=None, rev=None,
3265 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3265 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3266 """identify the working copy or specified revision
3266 """identify the working copy or specified revision
3267
3267
3268 Print a summary identifying the repository state at REV using one or
3268 Print a summary identifying the repository state at REV using one or
3269 two parent hash identifiers, followed by a "+" if the working
3269 two parent hash identifiers, followed by a "+" if the working
3270 directory has uncommitted changes, the branch name (if not default),
3270 directory has uncommitted changes, the branch name (if not default),
3271 a list of tags, and a list of bookmarks.
3271 a list of tags, and a list of bookmarks.
3272
3272
3273 When REV is not given, print a summary of the current state of the
3273 When REV is not given, print a summary of the current state of the
3274 repository.
3274 repository.
3275
3275
3276 Specifying a path to a repository root or Mercurial bundle will
3276 Specifying a path to a repository root or Mercurial bundle will
3277 cause lookup to operate on that repository/bundle.
3277 cause lookup to operate on that repository/bundle.
3278
3278
3279 .. container:: verbose
3279 .. container:: verbose
3280
3280
3281 Examples:
3281 Examples:
3282
3282
3283 - generate a build identifier for the working directory::
3283 - generate a build identifier for the working directory::
3284
3284
3285 hg id --id > build-id.dat
3285 hg id --id > build-id.dat
3286
3286
3287 - find the revision corresponding to a tag::
3287 - find the revision corresponding to a tag::
3288
3288
3289 hg id -n -r 1.3
3289 hg id -n -r 1.3
3290
3290
3291 - check the most recent revision of a remote repository::
3291 - check the most recent revision of a remote repository::
3292
3292
3293 hg id -r tip http://selenic.com/hg/
3293 hg id -r tip http://selenic.com/hg/
3294
3294
3295 Returns 0 if successful.
3295 Returns 0 if successful.
3296 """
3296 """
3297
3297
3298 if not repo and not source:
3298 if not repo and not source:
3299 raise util.Abort(_("there is no Mercurial repository here "
3299 raise util.Abort(_("there is no Mercurial repository here "
3300 "(.hg not found)"))
3300 "(.hg not found)"))
3301
3301
3302 hexfunc = ui.debugflag and hex or short
3302 hexfunc = ui.debugflag and hex or short
3303 default = not (num or id or branch or tags or bookmarks)
3303 default = not (num or id or branch or tags or bookmarks)
3304 output = []
3304 output = []
3305 revs = []
3305 revs = []
3306
3306
3307 if source:
3307 if source:
3308 source, branches = hg.parseurl(ui.expandpath(source))
3308 source, branches = hg.parseurl(ui.expandpath(source))
3309 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3309 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3310 repo = peer.local()
3310 repo = peer.local()
3311 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3311 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3312
3312
3313 if not repo:
3313 if not repo:
3314 if num or branch or tags:
3314 if num or branch or tags:
3315 raise util.Abort(
3315 raise util.Abort(
3316 _("can't query remote revision number, branch, or tags"))
3316 _("can't query remote revision number, branch, or tags"))
3317 if not rev and revs:
3317 if not rev and revs:
3318 rev = revs[0]
3318 rev = revs[0]
3319 if not rev:
3319 if not rev:
3320 rev = "tip"
3320 rev = "tip"
3321
3321
3322 remoterev = peer.lookup(rev)
3322 remoterev = peer.lookup(rev)
3323 if default or id:
3323 if default or id:
3324 output = [hexfunc(remoterev)]
3324 output = [hexfunc(remoterev)]
3325
3325
3326 def getbms():
3326 def getbms():
3327 bms = []
3327 bms = []
3328
3328
3329 if 'bookmarks' in peer.listkeys('namespaces'):
3329 if 'bookmarks' in peer.listkeys('namespaces'):
3330 hexremoterev = hex(remoterev)
3330 hexremoterev = hex(remoterev)
3331 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3331 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3332 if bmr == hexremoterev]
3332 if bmr == hexremoterev]
3333
3333
3334 return sorted(bms)
3334 return sorted(bms)
3335
3335
3336 if bookmarks:
3336 if bookmarks:
3337 output.extend(getbms())
3337 output.extend(getbms())
3338 elif default and not ui.quiet:
3338 elif default and not ui.quiet:
3339 # multiple bookmarks for a single parent separated by '/'
3339 # multiple bookmarks for a single parent separated by '/'
3340 bm = '/'.join(getbms())
3340 bm = '/'.join(getbms())
3341 if bm:
3341 if bm:
3342 output.append(bm)
3342 output.append(bm)
3343 else:
3343 else:
3344 if not rev:
3344 if not rev:
3345 ctx = repo[None]
3345 ctx = repo[None]
3346 parents = ctx.parents()
3346 parents = ctx.parents()
3347 changed = ""
3347 changed = ""
3348 if default or id or num:
3348 if default or id or num:
3349 if (util.any(repo.status())
3349 if (util.any(repo.status())
3350 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
3350 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
3351 changed = '+'
3351 changed = '+'
3352 if default or id:
3352 if default or id:
3353 output = ["%s%s" %
3353 output = ["%s%s" %
3354 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3354 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3355 if num:
3355 if num:
3356 output.append("%s%s" %
3356 output.append("%s%s" %
3357 ('+'.join([str(p.rev()) for p in parents]), changed))
3357 ('+'.join([str(p.rev()) for p in parents]), changed))
3358 else:
3358 else:
3359 ctx = scmutil.revsingle(repo, rev)
3359 ctx = scmutil.revsingle(repo, rev)
3360 if default or id:
3360 if default or id:
3361 output = [hexfunc(ctx.node())]
3361 output = [hexfunc(ctx.node())]
3362 if num:
3362 if num:
3363 output.append(str(ctx.rev()))
3363 output.append(str(ctx.rev()))
3364
3364
3365 if default and not ui.quiet:
3365 if default and not ui.quiet:
3366 b = ctx.branch()
3366 b = ctx.branch()
3367 if b != 'default':
3367 if b != 'default':
3368 output.append("(%s)" % b)
3368 output.append("(%s)" % b)
3369
3369
3370 # multiple tags for a single parent separated by '/'
3370 # multiple tags for a single parent separated by '/'
3371 t = '/'.join(ctx.tags())
3371 t = '/'.join(ctx.tags())
3372 if t:
3372 if t:
3373 output.append(t)
3373 output.append(t)
3374
3374
3375 # multiple bookmarks for a single parent separated by '/'
3375 # multiple bookmarks for a single parent separated by '/'
3376 bm = '/'.join(ctx.bookmarks())
3376 bm = '/'.join(ctx.bookmarks())
3377 if bm:
3377 if bm:
3378 output.append(bm)
3378 output.append(bm)
3379 else:
3379 else:
3380 if branch:
3380 if branch:
3381 output.append(ctx.branch())
3381 output.append(ctx.branch())
3382
3382
3383 if tags:
3383 if tags:
3384 output.extend(ctx.tags())
3384 output.extend(ctx.tags())
3385
3385
3386 if bookmarks:
3386 if bookmarks:
3387 output.extend(ctx.bookmarks())
3387 output.extend(ctx.bookmarks())
3388
3388
3389 ui.write("%s\n" % ' '.join(output))
3389 ui.write("%s\n" % ' '.join(output))
3390
3390
3391 @command('import|patch',
3391 @command('import|patch',
3392 [('p', 'strip', 1,
3392 [('p', 'strip', 1,
3393 _('directory strip option for patch. This has the same '
3393 _('directory strip option for patch. This has the same '
3394 'meaning as the corresponding patch option'), _('NUM')),
3394 'meaning as the corresponding patch option'), _('NUM')),
3395 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3395 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3396 ('e', 'edit', False, _('invoke editor on commit messages')),
3396 ('e', 'edit', False, _('invoke editor on commit messages')),
3397 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3397 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3398 ('', 'no-commit', None,
3398 ('', 'no-commit', None,
3399 _("don't commit, just update the working directory")),
3399 _("don't commit, just update the working directory")),
3400 ('', 'bypass', None,
3400 ('', 'bypass', None,
3401 _("apply patch without touching the working directory")),
3401 _("apply patch without touching the working directory")),
3402 ('', 'exact', None,
3402 ('', 'exact', None,
3403 _('apply patch to the nodes from which it was generated')),
3403 _('apply patch to the nodes from which it was generated')),
3404 ('', 'import-branch', None,
3404 ('', 'import-branch', None,
3405 _('use any branch information in patch (implied by --exact)'))] +
3405 _('use any branch information in patch (implied by --exact)'))] +
3406 commitopts + commitopts2 + similarityopts,
3406 commitopts + commitopts2 + similarityopts,
3407 _('[OPTION]... PATCH...'))
3407 _('[OPTION]... PATCH...'))
3408 def import_(ui, repo, patch1=None, *patches, **opts):
3408 def import_(ui, repo, patch1=None, *patches, **opts):
3409 """import an ordered set of patches
3409 """import an ordered set of patches
3410
3410
3411 Import a list of patches and commit them individually (unless
3411 Import a list of patches and commit them individually (unless
3412 --no-commit is specified).
3412 --no-commit is specified).
3413
3413
3414 If there are outstanding changes in the working directory, import
3414 If there are outstanding changes in the working directory, import
3415 will abort unless given the -f/--force flag.
3415 will abort unless given the -f/--force flag.
3416
3416
3417 You can import a patch straight from a mail message. Even patches
3417 You can import a patch straight from a mail message. Even patches
3418 as attachments work (to use the body part, it must have type
3418 as attachments work (to use the body part, it must have type
3419 text/plain or text/x-patch). From and Subject headers of email
3419 text/plain or text/x-patch). From and Subject headers of email
3420 message are used as default committer and commit message. All
3420 message are used as default committer and commit message. All
3421 text/plain body parts before first diff are added to commit
3421 text/plain body parts before first diff are added to commit
3422 message.
3422 message.
3423
3423
3424 If the imported patch was generated by :hg:`export`, user and
3424 If the imported patch was generated by :hg:`export`, user and
3425 description from patch override values from message headers and
3425 description from patch override values from message headers and
3426 body. Values given on command line with -m/--message and -u/--user
3426 body. Values given on command line with -m/--message and -u/--user
3427 override these.
3427 override these.
3428
3428
3429 If --exact is specified, import will set the working directory to
3429 If --exact is specified, import will set the working directory to
3430 the parent of each patch before applying it, and will abort if the
3430 the parent of each patch before applying it, and will abort if the
3431 resulting changeset has a different ID than the one recorded in
3431 resulting changeset has a different ID than the one recorded in
3432 the patch. This may happen due to character set problems or other
3432 the patch. This may happen due to character set problems or other
3433 deficiencies in the text patch format.
3433 deficiencies in the text patch format.
3434
3434
3435 Use --bypass to apply and commit patches directly to the
3435 Use --bypass to apply and commit patches directly to the
3436 repository, not touching the working directory. Without --exact,
3436 repository, not touching the working directory. Without --exact,
3437 patches will be applied on top of the working directory parent
3437 patches will be applied on top of the working directory parent
3438 revision.
3438 revision.
3439
3439
3440 With -s/--similarity, hg will attempt to discover renames and
3440 With -s/--similarity, hg will attempt to discover renames and
3441 copies in the patch in the same way as :hg:`addremove`.
3441 copies in the patch in the same way as :hg:`addremove`.
3442
3442
3443 To read a patch from standard input, use "-" as the patch name. If
3443 To read a patch from standard input, use "-" as the patch name. If
3444 a URL is specified, the patch will be downloaded from it.
3444 a URL is specified, the patch will be downloaded from it.
3445 See :hg:`help dates` for a list of formats valid for -d/--date.
3445 See :hg:`help dates` for a list of formats valid for -d/--date.
3446
3446
3447 .. container:: verbose
3447 .. container:: verbose
3448
3448
3449 Examples:
3449 Examples:
3450
3450
3451 - import a traditional patch from a website and detect renames::
3451 - import a traditional patch from a website and detect renames::
3452
3452
3453 hg import -s 80 http://example.com/bugfix.patch
3453 hg import -s 80 http://example.com/bugfix.patch
3454
3454
3455 - import a changeset from an hgweb server::
3455 - import a changeset from an hgweb server::
3456
3456
3457 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3457 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3458
3458
3459 - import all the patches in an Unix-style mbox::
3459 - import all the patches in an Unix-style mbox::
3460
3460
3461 hg import incoming-patches.mbox
3461 hg import incoming-patches.mbox
3462
3462
3463 - attempt to exactly restore an exported changeset (not always
3463 - attempt to exactly restore an exported changeset (not always
3464 possible)::
3464 possible)::
3465
3465
3466 hg import --exact proposed-fix.patch
3466 hg import --exact proposed-fix.patch
3467
3467
3468 Returns 0 on success.
3468 Returns 0 on success.
3469 """
3469 """
3470
3470
3471 if not patch1:
3471 if not patch1:
3472 raise util.Abort(_('need at least one patch to import'))
3472 raise util.Abort(_('need at least one patch to import'))
3473
3473
3474 patches = (patch1,) + patches
3474 patches = (patch1,) + patches
3475
3475
3476 date = opts.get('date')
3476 date = opts.get('date')
3477 if date:
3477 if date:
3478 opts['date'] = util.parsedate(date)
3478 opts['date'] = util.parsedate(date)
3479
3479
3480 editor = cmdutil.commiteditor
3480 editor = cmdutil.commiteditor
3481 if opts.get('edit'):
3481 if opts.get('edit'):
3482 editor = cmdutil.commitforceeditor
3482 editor = cmdutil.commitforceeditor
3483
3483
3484 update = not opts.get('bypass')
3484 update = not opts.get('bypass')
3485 if not update and opts.get('no_commit'):
3485 if not update and opts.get('no_commit'):
3486 raise util.Abort(_('cannot use --no-commit with --bypass'))
3486 raise util.Abort(_('cannot use --no-commit with --bypass'))
3487 try:
3487 try:
3488 sim = float(opts.get('similarity') or 0)
3488 sim = float(opts.get('similarity') or 0)
3489 except ValueError:
3489 except ValueError:
3490 raise util.Abort(_('similarity must be a number'))
3490 raise util.Abort(_('similarity must be a number'))
3491 if sim < 0 or sim > 100:
3491 if sim < 0 or sim > 100:
3492 raise util.Abort(_('similarity must be between 0 and 100'))
3492 raise util.Abort(_('similarity must be between 0 and 100'))
3493 if sim and not update:
3493 if sim and not update:
3494 raise util.Abort(_('cannot use --similarity with --bypass'))
3494 raise util.Abort(_('cannot use --similarity with --bypass'))
3495
3495
3496 if (opts.get('exact') or not opts.get('force')) and update:
3496 if (opts.get('exact') or not opts.get('force')) and update:
3497 cmdutil.bailifchanged(repo)
3497 cmdutil.bailifchanged(repo)
3498
3498
3499 base = opts["base"]
3499 base = opts["base"]
3500 strip = opts["strip"]
3500 strip = opts["strip"]
3501 wlock = lock = tr = None
3501 wlock = lock = tr = None
3502 msgs = []
3502 msgs = []
3503
3503
3504 def checkexact(repo, n, nodeid):
3504 def checkexact(repo, n, nodeid):
3505 if opts.get('exact') and hex(n) != nodeid:
3505 if opts.get('exact') and hex(n) != nodeid:
3506 raise util.Abort(_('patch is damaged or loses information'))
3506 raise util.Abort(_('patch is damaged or loses information'))
3507
3507
3508 def tryone(ui, hunk, parents):
3508 def tryone(ui, hunk, parents):
3509 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3509 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3510 patch.extract(ui, hunk)
3510 patch.extract(ui, hunk)
3511
3511
3512 if not tmpname:
3512 if not tmpname:
3513 return (None, None)
3513 return (None, None)
3514 msg = _('applied to working directory')
3514 msg = _('applied to working directory')
3515
3515
3516 try:
3516 try:
3517 cmdline_message = cmdutil.logmessage(ui, opts)
3517 cmdline_message = cmdutil.logmessage(ui, opts)
3518 if cmdline_message:
3518 if cmdline_message:
3519 # pickup the cmdline msg
3519 # pickup the cmdline msg
3520 message = cmdline_message
3520 message = cmdline_message
3521 elif message:
3521 elif message:
3522 # pickup the patch msg
3522 # pickup the patch msg
3523 message = message.strip()
3523 message = message.strip()
3524 else:
3524 else:
3525 # launch the editor
3525 # launch the editor
3526 message = None
3526 message = None
3527 ui.debug('message:\n%s\n' % message)
3527 ui.debug('message:\n%s\n' % message)
3528
3528
3529 if len(parents) == 1:
3529 if len(parents) == 1:
3530 parents.append(repo[nullid])
3530 parents.append(repo[nullid])
3531 if opts.get('exact'):
3531 if opts.get('exact'):
3532 if not nodeid or not p1:
3532 if not nodeid or not p1:
3533 raise util.Abort(_('not a Mercurial patch'))
3533 raise util.Abort(_('not a Mercurial patch'))
3534 p1 = repo[p1]
3534 p1 = repo[p1]
3535 p2 = repo[p2 or nullid]
3535 p2 = repo[p2 or nullid]
3536 elif p2:
3536 elif p2:
3537 try:
3537 try:
3538 p1 = repo[p1]
3538 p1 = repo[p1]
3539 p2 = repo[p2]
3539 p2 = repo[p2]
3540 # Without any options, consider p2 only if the
3540 # Without any options, consider p2 only if the
3541 # patch is being applied on top of the recorded
3541 # patch is being applied on top of the recorded
3542 # first parent.
3542 # first parent.
3543 if p1 != parents[0]:
3543 if p1 != parents[0]:
3544 p1 = parents[0]
3544 p1 = parents[0]
3545 p2 = repo[nullid]
3545 p2 = repo[nullid]
3546 except error.RepoError:
3546 except error.RepoError:
3547 p1, p2 = parents
3547 p1, p2 = parents
3548 else:
3548 else:
3549 p1, p2 = parents
3549 p1, p2 = parents
3550
3550
3551 n = None
3551 n = None
3552 if update:
3552 if update:
3553 if p1 != parents[0]:
3553 if p1 != parents[0]:
3554 hg.clean(repo, p1.node())
3554 hg.clean(repo, p1.node())
3555 if p2 != parents[1]:
3555 if p2 != parents[1]:
3556 repo.setparents(p1.node(), p2.node())
3556 repo.setparents(p1.node(), p2.node())
3557
3557
3558 if opts.get('exact') or opts.get('import_branch'):
3558 if opts.get('exact') or opts.get('import_branch'):
3559 repo.dirstate.setbranch(branch or 'default')
3559 repo.dirstate.setbranch(branch or 'default')
3560
3560
3561 files = set()
3561 files = set()
3562 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3562 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3563 eolmode=None, similarity=sim / 100.0)
3563 eolmode=None, similarity=sim / 100.0)
3564 files = list(files)
3564 files = list(files)
3565 if opts.get('no_commit'):
3565 if opts.get('no_commit'):
3566 if message:
3566 if message:
3567 msgs.append(message)
3567 msgs.append(message)
3568 else:
3568 else:
3569 if opts.get('exact') or p2:
3569 if opts.get('exact') or p2:
3570 # If you got here, you either use --force and know what
3570 # If you got here, you either use --force and know what
3571 # you are doing or used --exact or a merge patch while
3571 # you are doing or used --exact or a merge patch while
3572 # being updated to its first parent.
3572 # being updated to its first parent.
3573 m = None
3573 m = None
3574 else:
3574 else:
3575 m = scmutil.matchfiles(repo, files or [])
3575 m = scmutil.matchfiles(repo, files or [])
3576 n = repo.commit(message, opts.get('user') or user,
3576 n = repo.commit(message, opts.get('user') or user,
3577 opts.get('date') or date, match=m,
3577 opts.get('date') or date, match=m,
3578 editor=editor)
3578 editor=editor)
3579 checkexact(repo, n, nodeid)
3579 checkexact(repo, n, nodeid)
3580 else:
3580 else:
3581 if opts.get('exact') or opts.get('import_branch'):
3581 if opts.get('exact') or opts.get('import_branch'):
3582 branch = branch or 'default'
3582 branch = branch or 'default'
3583 else:
3583 else:
3584 branch = p1.branch()
3584 branch = p1.branch()
3585 store = patch.filestore()
3585 store = patch.filestore()
3586 try:
3586 try:
3587 files = set()
3587 files = set()
3588 try:
3588 try:
3589 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3589 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3590 files, eolmode=None)
3590 files, eolmode=None)
3591 except patch.PatchError, e:
3591 except patch.PatchError, e:
3592 raise util.Abort(str(e))
3592 raise util.Abort(str(e))
3593 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3593 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3594 message,
3594 message,
3595 opts.get('user') or user,
3595 opts.get('user') or user,
3596 opts.get('date') or date,
3596 opts.get('date') or date,
3597 branch, files, store,
3597 branch, files, store,
3598 editor=cmdutil.commiteditor)
3598 editor=cmdutil.commiteditor)
3599 repo.savecommitmessage(memctx.description())
3599 repo.savecommitmessage(memctx.description())
3600 n = memctx.commit()
3600 n = memctx.commit()
3601 checkexact(repo, n, nodeid)
3601 checkexact(repo, n, nodeid)
3602 finally:
3602 finally:
3603 store.close()
3603 store.close()
3604 if n:
3604 if n:
3605 # i18n: refers to a short changeset id
3605 # i18n: refers to a short changeset id
3606 msg = _('created %s') % short(n)
3606 msg = _('created %s') % short(n)
3607 return (msg, n)
3607 return (msg, n)
3608 finally:
3608 finally:
3609 os.unlink(tmpname)
3609 os.unlink(tmpname)
3610
3610
3611 try:
3611 try:
3612 try:
3612 try:
3613 wlock = repo.wlock()
3613 wlock = repo.wlock()
3614 if not opts.get('no_commit'):
3614 if not opts.get('no_commit'):
3615 lock = repo.lock()
3615 lock = repo.lock()
3616 tr = repo.transaction('import')
3616 tr = repo.transaction('import')
3617 parents = repo.parents()
3617 parents = repo.parents()
3618 for patchurl in patches:
3618 for patchurl in patches:
3619 if patchurl == '-':
3619 if patchurl == '-':
3620 ui.status(_('applying patch from stdin\n'))
3620 ui.status(_('applying patch from stdin\n'))
3621 patchfile = ui.fin
3621 patchfile = ui.fin
3622 patchurl = 'stdin' # for error message
3622 patchurl = 'stdin' # for error message
3623 else:
3623 else:
3624 patchurl = os.path.join(base, patchurl)
3624 patchurl = os.path.join(base, patchurl)
3625 ui.status(_('applying %s\n') % patchurl)
3625 ui.status(_('applying %s\n') % patchurl)
3626 patchfile = hg.openpath(ui, patchurl)
3626 patchfile = hg.openpath(ui, patchurl)
3627
3627
3628 haspatch = False
3628 haspatch = False
3629 for hunk in patch.split(patchfile):
3629 for hunk in patch.split(patchfile):
3630 (msg, node) = tryone(ui, hunk, parents)
3630 (msg, node) = tryone(ui, hunk, parents)
3631 if msg:
3631 if msg:
3632 haspatch = True
3632 haspatch = True
3633 ui.note(msg + '\n')
3633 ui.note(msg + '\n')
3634 if update or opts.get('exact'):
3634 if update or opts.get('exact'):
3635 parents = repo.parents()
3635 parents = repo.parents()
3636 else:
3636 else:
3637 parents = [repo[node]]
3637 parents = [repo[node]]
3638
3638
3639 if not haspatch:
3639 if not haspatch:
3640 raise util.Abort(_('%s: no diffs found') % patchurl)
3640 raise util.Abort(_('%s: no diffs found') % patchurl)
3641
3641
3642 if tr:
3642 if tr:
3643 tr.close()
3643 tr.close()
3644 if msgs:
3644 if msgs:
3645 repo.savecommitmessage('\n* * *\n'.join(msgs))
3645 repo.savecommitmessage('\n* * *\n'.join(msgs))
3646 except: # re-raises
3646 except: # re-raises
3647 # wlock.release() indirectly calls dirstate.write(): since
3647 # wlock.release() indirectly calls dirstate.write(): since
3648 # we're crashing, we do not want to change the working dir
3648 # we're crashing, we do not want to change the working dir
3649 # parent after all, so make sure it writes nothing
3649 # parent after all, so make sure it writes nothing
3650 repo.dirstate.invalidate()
3650 repo.dirstate.invalidate()
3651 raise
3651 raise
3652 finally:
3652 finally:
3653 if tr:
3653 if tr:
3654 tr.release()
3654 tr.release()
3655 release(lock, wlock)
3655 release(lock, wlock)
3656
3656
3657 @command('incoming|in',
3657 @command('incoming|in',
3658 [('f', 'force', None,
3658 [('f', 'force', None,
3659 _('run even if remote repository is unrelated')),
3659 _('run even if remote repository is unrelated')),
3660 ('n', 'newest-first', None, _('show newest record first')),
3660 ('n', 'newest-first', None, _('show newest record first')),
3661 ('', 'bundle', '',
3661 ('', 'bundle', '',
3662 _('file to store the bundles into'), _('FILE')),
3662 _('file to store the bundles into'), _('FILE')),
3663 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3663 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3664 ('B', 'bookmarks', False, _("compare bookmarks")),
3664 ('B', 'bookmarks', False, _("compare bookmarks")),
3665 ('b', 'branch', [],
3665 ('b', 'branch', [],
3666 _('a specific branch you would like to pull'), _('BRANCH')),
3666 _('a specific branch you would like to pull'), _('BRANCH')),
3667 ] + logopts + remoteopts + subrepoopts,
3667 ] + logopts + remoteopts + subrepoopts,
3668 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3668 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3669 def incoming(ui, repo, source="default", **opts):
3669 def incoming(ui, repo, source="default", **opts):
3670 """show new changesets found in source
3670 """show new changesets found in source
3671
3671
3672 Show new changesets found in the specified path/URL or the default
3672 Show new changesets found in the specified path/URL or the default
3673 pull location. These are the changesets that would have been pulled
3673 pull location. These are the changesets that would have been pulled
3674 if a pull at the time you issued this command.
3674 if a pull at the time you issued this command.
3675
3675
3676 For remote repository, using --bundle avoids downloading the
3676 For remote repository, using --bundle avoids downloading the
3677 changesets twice if the incoming is followed by a pull.
3677 changesets twice if the incoming is followed by a pull.
3678
3678
3679 See pull for valid source format details.
3679 See pull for valid source format details.
3680
3680
3681 Returns 0 if there are incoming changes, 1 otherwise.
3681 Returns 0 if there are incoming changes, 1 otherwise.
3682 """
3682 """
3683 if opts.get('graph'):
3683 if opts.get('graph'):
3684 cmdutil.checkunsupportedgraphflags([], opts)
3684 cmdutil.checkunsupportedgraphflags([], opts)
3685 def display(other, chlist, displayer):
3685 def display(other, chlist, displayer):
3686 revdag = cmdutil.graphrevs(other, chlist, opts)
3686 revdag = cmdutil.graphrevs(other, chlist, opts)
3687 showparents = [ctx.node() for ctx in repo[None].parents()]
3687 showparents = [ctx.node() for ctx in repo[None].parents()]
3688 cmdutil.displaygraph(ui, revdag, displayer, showparents,
3688 cmdutil.displaygraph(ui, revdag, displayer, showparents,
3689 graphmod.asciiedges)
3689 graphmod.asciiedges)
3690
3690
3691 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3691 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3692 return 0
3692 return 0
3693
3693
3694 if opts.get('bundle') and opts.get('subrepos'):
3694 if opts.get('bundle') and opts.get('subrepos'):
3695 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3695 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3696
3696
3697 if opts.get('bookmarks'):
3697 if opts.get('bookmarks'):
3698 source, branches = hg.parseurl(ui.expandpath(source),
3698 source, branches = hg.parseurl(ui.expandpath(source),
3699 opts.get('branch'))
3699 opts.get('branch'))
3700 other = hg.peer(repo, opts, source)
3700 other = hg.peer(repo, opts, source)
3701 if 'bookmarks' not in other.listkeys('namespaces'):
3701 if 'bookmarks' not in other.listkeys('namespaces'):
3702 ui.warn(_("remote doesn't support bookmarks\n"))
3702 ui.warn(_("remote doesn't support bookmarks\n"))
3703 return 0
3703 return 0
3704 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3704 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3705 return bookmarks.diff(ui, repo, other)
3705 return bookmarks.diff(ui, repo, other)
3706
3706
3707 repo._subtoppath = ui.expandpath(source)
3707 repo._subtoppath = ui.expandpath(source)
3708 try:
3708 try:
3709 return hg.incoming(ui, repo, source, opts)
3709 return hg.incoming(ui, repo, source, opts)
3710 finally:
3710 finally:
3711 del repo._subtoppath
3711 del repo._subtoppath
3712
3712
3713
3713
3714 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3714 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3715 def init(ui, dest=".", **opts):
3715 def init(ui, dest=".", **opts):
3716 """create a new repository in the given directory
3716 """create a new repository in the given directory
3717
3717
3718 Initialize a new repository in the given directory. If the given
3718 Initialize a new repository in the given directory. If the given
3719 directory does not exist, it will be created.
3719 directory does not exist, it will be created.
3720
3720
3721 If no directory is given, the current directory is used.
3721 If no directory is given, the current directory is used.
3722
3722
3723 It is possible to specify an ``ssh://`` URL as the destination.
3723 It is possible to specify an ``ssh://`` URL as the destination.
3724 See :hg:`help urls` for more information.
3724 See :hg:`help urls` for more information.
3725
3725
3726 Returns 0 on success.
3726 Returns 0 on success.
3727 """
3727 """
3728 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3728 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3729
3729
3730 @command('locate',
3730 @command('locate',
3731 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3731 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3732 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3732 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3733 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3733 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3734 ] + walkopts,
3734 ] + walkopts,
3735 _('[OPTION]... [PATTERN]...'))
3735 _('[OPTION]... [PATTERN]...'))
3736 def locate(ui, repo, *pats, **opts):
3736 def locate(ui, repo, *pats, **opts):
3737 """locate files matching specific patterns
3737 """locate files matching specific patterns
3738
3738
3739 Print files under Mercurial control in the working directory whose
3739 Print files under Mercurial control in the working directory whose
3740 names match the given patterns.
3740 names match the given patterns.
3741
3741
3742 By default, this command searches all directories in the working
3742 By default, this command searches all directories in the working
3743 directory. To search just the current directory and its
3743 directory. To search just the current directory and its
3744 subdirectories, use "--include .".
3744 subdirectories, use "--include .".
3745
3745
3746 If no patterns are given to match, this command prints the names
3746 If no patterns are given to match, this command prints the names
3747 of all files under Mercurial control in the working directory.
3747 of all files under Mercurial control in the working directory.
3748
3748
3749 If you want to feed the output of this command into the "xargs"
3749 If you want to feed the output of this command into the "xargs"
3750 command, use the -0 option to both this command and "xargs". This
3750 command, use the -0 option to both this command and "xargs". This
3751 will avoid the problem of "xargs" treating single filenames that
3751 will avoid the problem of "xargs" treating single filenames that
3752 contain whitespace as multiple filenames.
3752 contain whitespace as multiple filenames.
3753
3753
3754 Returns 0 if a match is found, 1 otherwise.
3754 Returns 0 if a match is found, 1 otherwise.
3755 """
3755 """
3756 end = opts.get('print0') and '\0' or '\n'
3756 end = opts.get('print0') and '\0' or '\n'
3757 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3757 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3758
3758
3759 ret = 1
3759 ret = 1
3760 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3760 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3761 m.bad = lambda x, y: False
3761 m.bad = lambda x, y: False
3762 for abs in repo[rev].walk(m):
3762 for abs in repo[rev].walk(m):
3763 if not rev and abs not in repo.dirstate:
3763 if not rev and abs not in repo.dirstate:
3764 continue
3764 continue
3765 if opts.get('fullpath'):
3765 if opts.get('fullpath'):
3766 ui.write(repo.wjoin(abs), end)
3766 ui.write(repo.wjoin(abs), end)
3767 else:
3767 else:
3768 ui.write(((pats and m.rel(abs)) or abs), end)
3768 ui.write(((pats and m.rel(abs)) or abs), end)
3769 ret = 0
3769 ret = 0
3770
3770
3771 return ret
3771 return ret
3772
3772
3773 @command('^log|history',
3773 @command('^log|history',
3774 [('f', 'follow', None,
3774 [('f', 'follow', None,
3775 _('follow changeset history, or file history across copies and renames')),
3775 _('follow changeset history, or file history across copies and renames')),
3776 ('', 'follow-first', None,
3776 ('', 'follow-first', None,
3777 _('only follow the first parent of merge changesets (DEPRECATED)')),
3777 _('only follow the first parent of merge changesets (DEPRECATED)')),
3778 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3778 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3779 ('C', 'copies', None, _('show copied files')),
3779 ('C', 'copies', None, _('show copied files')),
3780 ('k', 'keyword', [],
3780 ('k', 'keyword', [],
3781 _('do case-insensitive search for a given text'), _('TEXT')),
3781 _('do case-insensitive search for a given text'), _('TEXT')),
3782 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3782 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3783 ('', 'removed', None, _('include revisions where files were removed')),
3783 ('', 'removed', None, _('include revisions where files were removed')),
3784 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3784 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3785 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3785 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3786 ('', 'only-branch', [],
3786 ('', 'only-branch', [],
3787 _('show only changesets within the given named branch (DEPRECATED)'),
3787 _('show only changesets within the given named branch (DEPRECATED)'),
3788 _('BRANCH')),
3788 _('BRANCH')),
3789 ('b', 'branch', [],
3789 ('b', 'branch', [],
3790 _('show changesets within the given named branch'), _('BRANCH')),
3790 _('show changesets within the given named branch'), _('BRANCH')),
3791 ('P', 'prune', [],
3791 ('P', 'prune', [],
3792 _('do not display revision or any of its ancestors'), _('REV')),
3792 _('do not display revision or any of its ancestors'), _('REV')),
3793 ] + logopts + walkopts,
3793 ] + logopts + walkopts,
3794 _('[OPTION]... [FILE]'))
3794 _('[OPTION]... [FILE]'))
3795 def log(ui, repo, *pats, **opts):
3795 def log(ui, repo, *pats, **opts):
3796 """show revision history of entire repository or files
3796 """show revision history of entire repository or files
3797
3797
3798 Print the revision history of the specified files or the entire
3798 Print the revision history of the specified files or the entire
3799 project.
3799 project.
3800
3800
3801 If no revision range is specified, the default is ``tip:0`` unless
3801 If no revision range is specified, the default is ``tip:0`` unless
3802 --follow is set, in which case the working directory parent is
3802 --follow is set, in which case the working directory parent is
3803 used as the starting revision.
3803 used as the starting revision.
3804
3804
3805 File history is shown without following rename or copy history of
3805 File history is shown without following rename or copy history of
3806 files. Use -f/--follow with a filename to follow history across
3806 files. Use -f/--follow with a filename to follow history across
3807 renames and copies. --follow without a filename will only show
3807 renames and copies. --follow without a filename will only show
3808 ancestors or descendants of the starting revision.
3808 ancestors or descendants of the starting revision.
3809
3809
3810 By default this command prints revision number and changeset id,
3810 By default this command prints revision number and changeset id,
3811 tags, non-trivial parents, user, date and time, and a summary for
3811 tags, non-trivial parents, user, date and time, and a summary for
3812 each commit. When the -v/--verbose switch is used, the list of
3812 each commit. When the -v/--verbose switch is used, the list of
3813 changed files and full commit message are shown.
3813 changed files and full commit message are shown.
3814
3814
3815 .. note::
3815 .. note::
3816 log -p/--patch may generate unexpected diff output for merge
3816 log -p/--patch may generate unexpected diff output for merge
3817 changesets, as it will only compare the merge changeset against
3817 changesets, as it will only compare the merge changeset against
3818 its first parent. Also, only files different from BOTH parents
3818 its first parent. Also, only files different from BOTH parents
3819 will appear in files:.
3819 will appear in files:.
3820
3820
3821 .. note::
3821 .. note::
3822 for performance reasons, log FILE may omit duplicate changes
3822 for performance reasons, log FILE may omit duplicate changes
3823 made on branches and will not show deletions. To see all
3823 made on branches and will not show deletions. To see all
3824 changes including duplicates and deletions, use the --removed
3824 changes including duplicates and deletions, use the --removed
3825 switch.
3825 switch.
3826
3826
3827 .. container:: verbose
3827 .. container:: verbose
3828
3828
3829 Some examples:
3829 Some examples:
3830
3830
3831 - changesets with full descriptions and file lists::
3831 - changesets with full descriptions and file lists::
3832
3832
3833 hg log -v
3833 hg log -v
3834
3834
3835 - changesets ancestral to the working directory::
3835 - changesets ancestral to the working directory::
3836
3836
3837 hg log -f
3837 hg log -f
3838
3838
3839 - last 10 commits on the current branch::
3839 - last 10 commits on the current branch::
3840
3840
3841 hg log -l 10 -b .
3841 hg log -l 10 -b .
3842
3842
3843 - changesets showing all modifications of a file, including removals::
3843 - changesets showing all modifications of a file, including removals::
3844
3844
3845 hg log --removed file.c
3845 hg log --removed file.c
3846
3846
3847 - all changesets that touch a directory, with diffs, excluding merges::
3847 - all changesets that touch a directory, with diffs, excluding merges::
3848
3848
3849 hg log -Mp lib/
3849 hg log -Mp lib/
3850
3850
3851 - all revision numbers that match a keyword::
3851 - all revision numbers that match a keyword::
3852
3852
3853 hg log -k bug --template "{rev}\\n"
3853 hg log -k bug --template "{rev}\\n"
3854
3854
3855 - check if a given changeset is included is a tagged release::
3855 - check if a given changeset is included is a tagged release::
3856
3856
3857 hg log -r "a21ccf and ancestor(1.9)"
3857 hg log -r "a21ccf and ancestor(1.9)"
3858
3858
3859 - find all changesets by some user in a date range::
3859 - find all changesets by some user in a date range::
3860
3860
3861 hg log -k alice -d "may 2008 to jul 2008"
3861 hg log -k alice -d "may 2008 to jul 2008"
3862
3862
3863 - summary of all changesets after the last tag::
3863 - summary of all changesets after the last tag::
3864
3864
3865 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3865 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3866
3866
3867 See :hg:`help dates` for a list of formats valid for -d/--date.
3867 See :hg:`help dates` for a list of formats valid for -d/--date.
3868
3868
3869 See :hg:`help revisions` and :hg:`help revsets` for more about
3869 See :hg:`help revisions` and :hg:`help revsets` for more about
3870 specifying revisions.
3870 specifying revisions.
3871
3871
3872 See :hg:`help templates` for more about pre-packaged styles and
3872 See :hg:`help templates` for more about pre-packaged styles and
3873 specifying custom templates.
3873 specifying custom templates.
3874
3874
3875 Returns 0 on success.
3875 Returns 0 on success.
3876 """
3876 """
3877 if opts.get('graph'):
3877 if opts.get('graph'):
3878 return cmdutil.graphlog(ui, repo, *pats, **opts)
3878 return cmdutil.graphlog(ui, repo, *pats, **opts)
3879
3879
3880 matchfn = scmutil.match(repo[None], pats, opts)
3880 matchfn = scmutil.match(repo[None], pats, opts)
3881 limit = cmdutil.loglimit(opts)
3881 limit = cmdutil.loglimit(opts)
3882 count = 0
3882 count = 0
3883
3883
3884 getrenamed, endrev = None, None
3884 getrenamed, endrev = None, None
3885 if opts.get('copies'):
3885 if opts.get('copies'):
3886 if opts.get('rev'):
3886 if opts.get('rev'):
3887 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3887 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3888 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3888 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3889
3889
3890 df = False
3890 df = False
3891 if opts.get("date"):
3891 if opts.get("date"):
3892 df = util.matchdate(opts["date"])
3892 df = util.matchdate(opts["date"])
3893
3893
3894 branches = opts.get('branch', []) + opts.get('only_branch', [])
3894 branches = opts.get('branch', []) + opts.get('only_branch', [])
3895 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3895 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3896
3896
3897 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3897 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3898 def prep(ctx, fns):
3898 def prep(ctx, fns):
3899 rev = ctx.rev()
3899 rev = ctx.rev()
3900 parents = [p for p in repo.changelog.parentrevs(rev)
3900 parents = [p for p in repo.changelog.parentrevs(rev)
3901 if p != nullrev]
3901 if p != nullrev]
3902 if opts.get('no_merges') and len(parents) == 2:
3902 if opts.get('no_merges') and len(parents) == 2:
3903 return
3903 return
3904 if opts.get('only_merges') and len(parents) != 2:
3904 if opts.get('only_merges') and len(parents) != 2:
3905 return
3905 return
3906 if opts.get('branch') and ctx.branch() not in opts['branch']:
3906 if opts.get('branch') and ctx.branch() not in opts['branch']:
3907 return
3907 return
3908 if df and not df(ctx.date()[0]):
3908 if df and not df(ctx.date()[0]):
3909 return
3909 return
3910
3910
3911 lower = encoding.lower
3911 lower = encoding.lower
3912 if opts.get('user'):
3912 if opts.get('user'):
3913 luser = lower(ctx.user())
3913 luser = lower(ctx.user())
3914 for k in [lower(x) for x in opts['user']]:
3914 for k in [lower(x) for x in opts['user']]:
3915 if (k in luser):
3915 if (k in luser):
3916 break
3916 break
3917 else:
3917 else:
3918 return
3918 return
3919 if opts.get('keyword'):
3919 if opts.get('keyword'):
3920 luser = lower(ctx.user())
3920 luser = lower(ctx.user())
3921 ldesc = lower(ctx.description())
3921 ldesc = lower(ctx.description())
3922 lfiles = lower(" ".join(ctx.files()))
3922 lfiles = lower(" ".join(ctx.files()))
3923 for k in [lower(x) for x in opts['keyword']]:
3923 for k in [lower(x) for x in opts['keyword']]:
3924 if (k in luser or k in ldesc or k in lfiles):
3924 if (k in luser or k in ldesc or k in lfiles):
3925 break
3925 break
3926 else:
3926 else:
3927 return
3927 return
3928
3928
3929 copies = None
3929 copies = None
3930 if getrenamed is not None and rev:
3930 if getrenamed is not None and rev:
3931 copies = []
3931 copies = []
3932 for fn in ctx.files():
3932 for fn in ctx.files():
3933 rename = getrenamed(fn, rev)
3933 rename = getrenamed(fn, rev)
3934 if rename:
3934 if rename:
3935 copies.append((fn, rename[0]))
3935 copies.append((fn, rename[0]))
3936
3936
3937 revmatchfn = None
3937 revmatchfn = None
3938 if opts.get('patch') or opts.get('stat'):
3938 if opts.get('patch') or opts.get('stat'):
3939 if opts.get('follow') or opts.get('follow_first'):
3939 if opts.get('follow') or opts.get('follow_first'):
3940 # note: this might be wrong when following through merges
3940 # note: this might be wrong when following through merges
3941 revmatchfn = scmutil.match(repo[None], fns, default='path')
3941 revmatchfn = scmutil.match(repo[None], fns, default='path')
3942 else:
3942 else:
3943 revmatchfn = matchfn
3943 revmatchfn = matchfn
3944
3944
3945 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3945 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3946
3946
3947 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3947 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3948 if displayer.flush(ctx.rev()):
3948 if displayer.flush(ctx.rev()):
3949 count += 1
3949 count += 1
3950 if count == limit:
3950 if count == limit:
3951 break
3951 break
3952 displayer.close()
3952 displayer.close()
3953
3953
3954 @command('manifest',
3954 @command('manifest',
3955 [('r', 'rev', '', _('revision to display'), _('REV')),
3955 [('r', 'rev', '', _('revision to display'), _('REV')),
3956 ('', 'all', False, _("list files from all revisions"))],
3956 ('', 'all', False, _("list files from all revisions"))],
3957 _('[-r REV]'))
3957 _('[-r REV]'))
3958 def manifest(ui, repo, node=None, rev=None, **opts):
3958 def manifest(ui, repo, node=None, rev=None, **opts):
3959 """output the current or given revision of the project manifest
3959 """output the current or given revision of the project manifest
3960
3960
3961 Print a list of version controlled files for the given revision.
3961 Print a list of version controlled files for the given revision.
3962 If no revision is given, the first parent of the working directory
3962 If no revision is given, the first parent of the working directory
3963 is used, or the null revision if no revision is checked out.
3963 is used, or the null revision if no revision is checked out.
3964
3964
3965 With -v, print file permissions, symlink and executable bits.
3965 With -v, print file permissions, symlink and executable bits.
3966 With --debug, print file revision hashes.
3966 With --debug, print file revision hashes.
3967
3967
3968 If option --all is specified, the list of all files from all revisions
3968 If option --all is specified, the list of all files from all revisions
3969 is printed. This includes deleted and renamed files.
3969 is printed. This includes deleted and renamed files.
3970
3970
3971 Returns 0 on success.
3971 Returns 0 on success.
3972 """
3972 """
3973
3973
3974 fm = ui.formatter('manifest', opts)
3974 fm = ui.formatter('manifest', opts)
3975
3975
3976 if opts.get('all'):
3976 if opts.get('all'):
3977 if rev or node:
3977 if rev or node:
3978 raise util.Abort(_("can't specify a revision with --all"))
3978 raise util.Abort(_("can't specify a revision with --all"))
3979
3979
3980 res = []
3980 res = []
3981 prefix = "data/"
3981 prefix = "data/"
3982 suffix = ".i"
3982 suffix = ".i"
3983 plen = len(prefix)
3983 plen = len(prefix)
3984 slen = len(suffix)
3984 slen = len(suffix)
3985 lock = repo.lock()
3985 lock = repo.lock()
3986 try:
3986 try:
3987 for fn, b, size in repo.store.datafiles():
3987 for fn, b, size in repo.store.datafiles():
3988 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3988 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3989 res.append(fn[plen:-slen])
3989 res.append(fn[plen:-slen])
3990 finally:
3990 finally:
3991 lock.release()
3991 lock.release()
3992 for f in res:
3992 for f in res:
3993 fm.startitem()
3993 fm.startitem()
3994 fm.write("path", '%s\n', f)
3994 fm.write("path", '%s\n', f)
3995 fm.end()
3995 fm.end()
3996 return
3996 return
3997
3997
3998 if rev and node:
3998 if rev and node:
3999 raise util.Abort(_("please specify just one revision"))
3999 raise util.Abort(_("please specify just one revision"))
4000
4000
4001 if not node:
4001 if not node:
4002 node = rev
4002 node = rev
4003
4003
4004 char = {'l': '@', 'x': '*', '': ''}
4004 char = {'l': '@', 'x': '*', '': ''}
4005 mode = {'l': '644', 'x': '755', '': '644'}
4005 mode = {'l': '644', 'x': '755', '': '644'}
4006 ctx = scmutil.revsingle(repo, node)
4006 ctx = scmutil.revsingle(repo, node)
4007 mf = ctx.manifest()
4007 mf = ctx.manifest()
4008 for f in ctx:
4008 for f in ctx:
4009 fm.startitem()
4009 fm.startitem()
4010 fl = ctx[f].flags()
4010 fl = ctx[f].flags()
4011 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4011 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4012 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4012 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4013 fm.write('path', '%s\n', f)
4013 fm.write('path', '%s\n', f)
4014 fm.end()
4014 fm.end()
4015
4015
4016 @command('^merge',
4016 @command('^merge',
4017 [('f', 'force', None, _('force a merge with outstanding changes')),
4017 [('f', 'force', None, _('force a merge with outstanding changes')),
4018 ('r', 'rev', '', _('revision to merge'), _('REV')),
4018 ('r', 'rev', '', _('revision to merge'), _('REV')),
4019 ('P', 'preview', None,
4019 ('P', 'preview', None,
4020 _('review revisions to merge (no merge is performed)'))
4020 _('review revisions to merge (no merge is performed)'))
4021 ] + mergetoolopts,
4021 ] + mergetoolopts,
4022 _('[-P] [-f] [[-r] REV]'))
4022 _('[-P] [-f] [[-r] REV]'))
4023 def merge(ui, repo, node=None, **opts):
4023 def merge(ui, repo, node=None, **opts):
4024 """merge working directory with another revision
4024 """merge working directory with another revision
4025
4025
4026 The current working directory is updated with all changes made in
4026 The current working directory is updated with all changes made in
4027 the requested revision since the last common predecessor revision.
4027 the requested revision since the last common predecessor revision.
4028
4028
4029 Files that changed between either parent are marked as changed for
4029 Files that changed between either parent are marked as changed for
4030 the next commit and a commit must be performed before any further
4030 the next commit and a commit must be performed before any further
4031 updates to the repository are allowed. The next commit will have
4031 updates to the repository are allowed. The next commit will have
4032 two parents.
4032 two parents.
4033
4033
4034 ``--tool`` can be used to specify the merge tool used for file
4034 ``--tool`` can be used to specify the merge tool used for file
4035 merges. It overrides the HGMERGE environment variable and your
4035 merges. It overrides the HGMERGE environment variable and your
4036 configuration files. See :hg:`help merge-tools` for options.
4036 configuration files. See :hg:`help merge-tools` for options.
4037
4037
4038 If no revision is specified, the working directory's parent is a
4038 If no revision is specified, the working directory's parent is a
4039 head revision, and the current branch contains exactly one other
4039 head revision, and the current branch contains exactly one other
4040 head, the other head is merged with by default. Otherwise, an
4040 head, the other head is merged with by default. Otherwise, an
4041 explicit revision with which to merge with must be provided.
4041 explicit revision with which to merge with must be provided.
4042
4042
4043 :hg:`resolve` must be used to resolve unresolved files.
4043 :hg:`resolve` must be used to resolve unresolved files.
4044
4044
4045 To undo an uncommitted merge, use :hg:`update --clean .` which
4045 To undo an uncommitted merge, use :hg:`update --clean .` which
4046 will check out a clean copy of the original merge parent, losing
4046 will check out a clean copy of the original merge parent, losing
4047 all changes.
4047 all changes.
4048
4048
4049 Returns 0 on success, 1 if there are unresolved files.
4049 Returns 0 on success, 1 if there are unresolved files.
4050 """
4050 """
4051
4051
4052 if opts.get('rev') and node:
4052 if opts.get('rev') and node:
4053 raise util.Abort(_("please specify just one revision"))
4053 raise util.Abort(_("please specify just one revision"))
4054 if not node:
4054 if not node:
4055 node = opts.get('rev')
4055 node = opts.get('rev')
4056
4056
4057 if node:
4057 if node:
4058 node = scmutil.revsingle(repo, node).node()
4058 node = scmutil.revsingle(repo, node).node()
4059
4059
4060 if not node and repo._bookmarkcurrent:
4060 if not node and repo._bookmarkcurrent:
4061 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4061 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4062 curhead = repo[repo._bookmarkcurrent].node()
4062 curhead = repo[repo._bookmarkcurrent].node()
4063 if len(bmheads) == 2:
4063 if len(bmheads) == 2:
4064 if curhead == bmheads[0]:
4064 if curhead == bmheads[0]:
4065 node = bmheads[1]
4065 node = bmheads[1]
4066 else:
4066 else:
4067 node = bmheads[0]
4067 node = bmheads[0]
4068 elif len(bmheads) > 2:
4068 elif len(bmheads) > 2:
4069 raise util.Abort(_("multiple matching bookmarks to merge - "
4069 raise util.Abort(_("multiple matching bookmarks to merge - "
4070 "please merge with an explicit rev or bookmark"),
4070 "please merge with an explicit rev or bookmark"),
4071 hint=_("run 'hg heads' to see all heads"))
4071 hint=_("run 'hg heads' to see all heads"))
4072 elif len(bmheads) <= 1:
4072 elif len(bmheads) <= 1:
4073 raise util.Abort(_("no matching bookmark to merge - "
4073 raise util.Abort(_("no matching bookmark to merge - "
4074 "please merge with an explicit rev or bookmark"),
4074 "please merge with an explicit rev or bookmark"),
4075 hint=_("run 'hg heads' to see all heads"))
4075 hint=_("run 'hg heads' to see all heads"))
4076
4076
4077 if not node and not repo._bookmarkcurrent:
4077 if not node and not repo._bookmarkcurrent:
4078 branch = repo[None].branch()
4078 branch = repo[None].branch()
4079 bheads = repo.branchheads(branch)
4079 bheads = repo.branchheads(branch)
4080 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4080 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4081
4081
4082 if len(nbhs) > 2:
4082 if len(nbhs) > 2:
4083 raise util.Abort(_("branch '%s' has %d heads - "
4083 raise util.Abort(_("branch '%s' has %d heads - "
4084 "please merge with an explicit rev")
4084 "please merge with an explicit rev")
4085 % (branch, len(bheads)),
4085 % (branch, len(bheads)),
4086 hint=_("run 'hg heads .' to see heads"))
4086 hint=_("run 'hg heads .' to see heads"))
4087
4087
4088 parent = repo.dirstate.p1()
4088 parent = repo.dirstate.p1()
4089 if len(nbhs) <= 1:
4089 if len(nbhs) <= 1:
4090 if len(bheads) > 1:
4090 if len(bheads) > 1:
4091 raise util.Abort(_("heads are bookmarked - "
4091 raise util.Abort(_("heads are bookmarked - "
4092 "please merge with an explicit rev"),
4092 "please merge with an explicit rev"),
4093 hint=_("run 'hg heads' to see all heads"))
4093 hint=_("run 'hg heads' to see all heads"))
4094 if len(repo.heads()) > 1:
4094 if len(repo.heads()) > 1:
4095 raise util.Abort(_("branch '%s' has one head - "
4095 raise util.Abort(_("branch '%s' has one head - "
4096 "please merge with an explicit rev")
4096 "please merge with an explicit rev")
4097 % branch,
4097 % branch,
4098 hint=_("run 'hg heads' to see all heads"))
4098 hint=_("run 'hg heads' to see all heads"))
4099 msg, hint = _('nothing to merge'), None
4099 msg, hint = _('nothing to merge'), None
4100 if parent != repo.lookup(branch):
4100 if parent != repo.lookup(branch):
4101 hint = _("use 'hg update' instead")
4101 hint = _("use 'hg update' instead")
4102 raise util.Abort(msg, hint=hint)
4102 raise util.Abort(msg, hint=hint)
4103
4103
4104 if parent not in bheads:
4104 if parent not in bheads:
4105 raise util.Abort(_('working directory not at a head revision'),
4105 raise util.Abort(_('working directory not at a head revision'),
4106 hint=_("use 'hg update' or merge with an "
4106 hint=_("use 'hg update' or merge with an "
4107 "explicit revision"))
4107 "explicit revision"))
4108 if parent == nbhs[0]:
4108 if parent == nbhs[0]:
4109 node = nbhs[-1]
4109 node = nbhs[-1]
4110 else:
4110 else:
4111 node = nbhs[0]
4111 node = nbhs[0]
4112
4112
4113 if opts.get('preview'):
4113 if opts.get('preview'):
4114 # find nodes that are ancestors of p2 but not of p1
4114 # find nodes that are ancestors of p2 but not of p1
4115 p1 = repo.lookup('.')
4115 p1 = repo.lookup('.')
4116 p2 = repo.lookup(node)
4116 p2 = repo.lookup(node)
4117 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4117 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4118
4118
4119 displayer = cmdutil.show_changeset(ui, repo, opts)
4119 displayer = cmdutil.show_changeset(ui, repo, opts)
4120 for node in nodes:
4120 for node in nodes:
4121 displayer.show(repo[node])
4121 displayer.show(repo[node])
4122 displayer.close()
4122 displayer.close()
4123 return 0
4123 return 0
4124
4124
4125 try:
4125 try:
4126 # ui.forcemerge is an internal variable, do not document
4126 # ui.forcemerge is an internal variable, do not document
4127 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4127 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4128 return hg.merge(repo, node, force=opts.get('force'))
4128 return hg.merge(repo, node, force=opts.get('force'))
4129 finally:
4129 finally:
4130 ui.setconfig('ui', 'forcemerge', '')
4130 ui.setconfig('ui', 'forcemerge', '')
4131
4131
4132 @command('outgoing|out',
4132 @command('outgoing|out',
4133 [('f', 'force', None, _('run even when the destination is unrelated')),
4133 [('f', 'force', None, _('run even when the destination is unrelated')),
4134 ('r', 'rev', [],
4134 ('r', 'rev', [],
4135 _('a changeset intended to be included in the destination'), _('REV')),
4135 _('a changeset intended to be included in the destination'), _('REV')),
4136 ('n', 'newest-first', None, _('show newest record first')),
4136 ('n', 'newest-first', None, _('show newest record first')),
4137 ('B', 'bookmarks', False, _('compare bookmarks')),
4137 ('B', 'bookmarks', False, _('compare bookmarks')),
4138 ('b', 'branch', [], _('a specific branch you would like to push'),
4138 ('b', 'branch', [], _('a specific branch you would like to push'),
4139 _('BRANCH')),
4139 _('BRANCH')),
4140 ] + logopts + remoteopts + subrepoopts,
4140 ] + logopts + remoteopts + subrepoopts,
4141 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4141 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4142 def outgoing(ui, repo, dest=None, **opts):
4142 def outgoing(ui, repo, dest=None, **opts):
4143 """show changesets not found in the destination
4143 """show changesets not found in the destination
4144
4144
4145 Show changesets not found in the specified destination repository
4145 Show changesets not found in the specified destination repository
4146 or the default push location. These are the changesets that would
4146 or the default push location. These are the changesets that would
4147 be pushed if a push was requested.
4147 be pushed if a push was requested.
4148
4148
4149 See pull for details of valid destination formats.
4149 See pull for details of valid destination formats.
4150
4150
4151 Returns 0 if there are outgoing changes, 1 otherwise.
4151 Returns 0 if there are outgoing changes, 1 otherwise.
4152 """
4152 """
4153 if opts.get('graph'):
4153 if opts.get('graph'):
4154 cmdutil.checkunsupportedgraphflags([], opts)
4154 cmdutil.checkunsupportedgraphflags([], opts)
4155 o = hg._outgoing(ui, repo, dest, opts)
4155 o = hg._outgoing(ui, repo, dest, opts)
4156 if o is None:
4156 if o is None:
4157 return
4157 return
4158
4158
4159 revdag = cmdutil.graphrevs(repo, o, opts)
4159 revdag = cmdutil.graphrevs(repo, o, opts)
4160 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4160 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4161 showparents = [ctx.node() for ctx in repo[None].parents()]
4161 showparents = [ctx.node() for ctx in repo[None].parents()]
4162 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4162 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4163 graphmod.asciiedges)
4163 graphmod.asciiedges)
4164 return 0
4164 return 0
4165
4165
4166 if opts.get('bookmarks'):
4166 if opts.get('bookmarks'):
4167 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4167 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4168 dest, branches = hg.parseurl(dest, opts.get('branch'))
4168 dest, branches = hg.parseurl(dest, opts.get('branch'))
4169 other = hg.peer(repo, opts, dest)
4169 other = hg.peer(repo, opts, dest)
4170 if 'bookmarks' not in other.listkeys('namespaces'):
4170 if 'bookmarks' not in other.listkeys('namespaces'):
4171 ui.warn(_("remote doesn't support bookmarks\n"))
4171 ui.warn(_("remote doesn't support bookmarks\n"))
4172 return 0
4172 return 0
4173 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4173 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4174 return bookmarks.diff(ui, other, repo)
4174 return bookmarks.diff(ui, other, repo)
4175
4175
4176 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4176 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4177 try:
4177 try:
4178 return hg.outgoing(ui, repo, dest, opts)
4178 return hg.outgoing(ui, repo, dest, opts)
4179 finally:
4179 finally:
4180 del repo._subtoppath
4180 del repo._subtoppath
4181
4181
4182 @command('parents',
4182 @command('parents',
4183 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4183 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4184 ] + templateopts,
4184 ] + templateopts,
4185 _('[-r REV] [FILE]'))
4185 _('[-r REV] [FILE]'))
4186 def parents(ui, repo, file_=None, **opts):
4186 def parents(ui, repo, file_=None, **opts):
4187 """show the parents of the working directory or revision
4187 """show the parents of the working directory or revision
4188
4188
4189 Print the working directory's parent revisions. If a revision is
4189 Print the working directory's parent revisions. If a revision is
4190 given via -r/--rev, the parent of that revision will be printed.
4190 given via -r/--rev, the parent of that revision will be printed.
4191 If a file argument is given, the revision in which the file was
4191 If a file argument is given, the revision in which the file was
4192 last changed (before the working directory revision or the
4192 last changed (before the working directory revision or the
4193 argument to --rev if given) is printed.
4193 argument to --rev if given) is printed.
4194
4194
4195 Returns 0 on success.
4195 Returns 0 on success.
4196 """
4196 """
4197
4197
4198 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4198 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4199
4199
4200 if file_:
4200 if file_:
4201 m = scmutil.match(ctx, (file_,), opts)
4201 m = scmutil.match(ctx, (file_,), opts)
4202 if m.anypats() or len(m.files()) != 1:
4202 if m.anypats() or len(m.files()) != 1:
4203 raise util.Abort(_('can only specify an explicit filename'))
4203 raise util.Abort(_('can only specify an explicit filename'))
4204 file_ = m.files()[0]
4204 file_ = m.files()[0]
4205 filenodes = []
4205 filenodes = []
4206 for cp in ctx.parents():
4206 for cp in ctx.parents():
4207 if not cp:
4207 if not cp:
4208 continue
4208 continue
4209 try:
4209 try:
4210 filenodes.append(cp.filenode(file_))
4210 filenodes.append(cp.filenode(file_))
4211 except error.LookupError:
4211 except error.LookupError:
4212 pass
4212 pass
4213 if not filenodes:
4213 if not filenodes:
4214 raise util.Abort(_("'%s' not found in manifest!") % file_)
4214 raise util.Abort(_("'%s' not found in manifest!") % file_)
4215 fl = repo.file(file_)
4215 fl = repo.file(file_)
4216 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4216 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4217 else:
4217 else:
4218 p = [cp.node() for cp in ctx.parents()]
4218 p = [cp.node() for cp in ctx.parents()]
4219
4219
4220 displayer = cmdutil.show_changeset(ui, repo, opts)
4220 displayer = cmdutil.show_changeset(ui, repo, opts)
4221 for n in p:
4221 for n in p:
4222 if n != nullid:
4222 if n != nullid:
4223 displayer.show(repo[n])
4223 displayer.show(repo[n])
4224 displayer.close()
4224 displayer.close()
4225
4225
4226 @command('paths', [], _('[NAME]'))
4226 @command('paths', [], _('[NAME]'))
4227 def paths(ui, repo, search=None):
4227 def paths(ui, repo, search=None):
4228 """show aliases for remote repositories
4228 """show aliases for remote repositories
4229
4229
4230 Show definition of symbolic path name NAME. If no name is given,
4230 Show definition of symbolic path name NAME. If no name is given,
4231 show definition of all available names.
4231 show definition of all available names.
4232
4232
4233 Option -q/--quiet suppresses all output when searching for NAME
4233 Option -q/--quiet suppresses all output when searching for NAME
4234 and shows only the path names when listing all definitions.
4234 and shows only the path names when listing all definitions.
4235
4235
4236 Path names are defined in the [paths] section of your
4236 Path names are defined in the [paths] section of your
4237 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4237 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4238 repository, ``.hg/hgrc`` is used, too.
4238 repository, ``.hg/hgrc`` is used, too.
4239
4239
4240 The path names ``default`` and ``default-push`` have a special
4240 The path names ``default`` and ``default-push`` have a special
4241 meaning. When performing a push or pull operation, they are used
4241 meaning. When performing a push or pull operation, they are used
4242 as fallbacks if no location is specified on the command-line.
4242 as fallbacks if no location is specified on the command-line.
4243 When ``default-push`` is set, it will be used for push and
4243 When ``default-push`` is set, it will be used for push and
4244 ``default`` will be used for pull; otherwise ``default`` is used
4244 ``default`` will be used for pull; otherwise ``default`` is used
4245 as the fallback for both. When cloning a repository, the clone
4245 as the fallback for both. When cloning a repository, the clone
4246 source is written as ``default`` in ``.hg/hgrc``. Note that
4246 source is written as ``default`` in ``.hg/hgrc``. Note that
4247 ``default`` and ``default-push`` apply to all inbound (e.g.
4247 ``default`` and ``default-push`` apply to all inbound (e.g.
4248 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4248 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4249 :hg:`bundle`) operations.
4249 :hg:`bundle`) operations.
4250
4250
4251 See :hg:`help urls` for more information.
4251 See :hg:`help urls` for more information.
4252
4252
4253 Returns 0 on success.
4253 Returns 0 on success.
4254 """
4254 """
4255 if search:
4255 if search:
4256 for name, path in ui.configitems("paths"):
4256 for name, path in ui.configitems("paths"):
4257 if name == search:
4257 if name == search:
4258 ui.status("%s\n" % util.hidepassword(path))
4258 ui.status("%s\n" % util.hidepassword(path))
4259 return
4259 return
4260 if not ui.quiet:
4260 if not ui.quiet:
4261 ui.warn(_("not found!\n"))
4261 ui.warn(_("not found!\n"))
4262 return 1
4262 return 1
4263 else:
4263 else:
4264 for name, path in ui.configitems("paths"):
4264 for name, path in ui.configitems("paths"):
4265 if ui.quiet:
4265 if ui.quiet:
4266 ui.write("%s\n" % name)
4266 ui.write("%s\n" % name)
4267 else:
4267 else:
4268 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4268 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4269
4269
4270 @command('phase',
4270 @command('phase',
4271 [('p', 'public', False, _('set changeset phase to public')),
4271 [('p', 'public', False, _('set changeset phase to public')),
4272 ('d', 'draft', False, _('set changeset phase to draft')),
4272 ('d', 'draft', False, _('set changeset phase to draft')),
4273 ('s', 'secret', False, _('set changeset phase to secret')),
4273 ('s', 'secret', False, _('set changeset phase to secret')),
4274 ('f', 'force', False, _('allow to move boundary backward')),
4274 ('f', 'force', False, _('allow to move boundary backward')),
4275 ('r', 'rev', [], _('target revision'), _('REV')),
4275 ('r', 'rev', [], _('target revision'), _('REV')),
4276 ],
4276 ],
4277 _('[-p|-d|-s] [-f] [-r] REV...'))
4277 _('[-p|-d|-s] [-f] [-r] REV...'))
4278 def phase(ui, repo, *revs, **opts):
4278 def phase(ui, repo, *revs, **opts):
4279 """set or show the current phase name
4279 """set or show the current phase name
4280
4280
4281 With no argument, show the phase name of specified revisions.
4281 With no argument, show the phase name of specified revisions.
4282
4282
4283 With one of -p/--public, -d/--draft or -s/--secret, change the
4283 With one of -p/--public, -d/--draft or -s/--secret, change the
4284 phase value of the specified revisions.
4284 phase value of the specified revisions.
4285
4285
4286 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4286 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4287 lower phase to an higher phase. Phases are ordered as follows::
4287 lower phase to an higher phase. Phases are ordered as follows::
4288
4288
4289 public < draft < secret
4289 public < draft < secret
4290
4290
4291 Return 0 on success, 1 if no phases were changed or some could not
4291 Return 0 on success, 1 if no phases were changed or some could not
4292 be changed.
4292 be changed.
4293 """
4293 """
4294 # search for a unique phase argument
4294 # search for a unique phase argument
4295 targetphase = None
4295 targetphase = None
4296 for idx, name in enumerate(phases.phasenames):
4296 for idx, name in enumerate(phases.phasenames):
4297 if opts[name]:
4297 if opts[name]:
4298 if targetphase is not None:
4298 if targetphase is not None:
4299 raise util.Abort(_('only one phase can be specified'))
4299 raise util.Abort(_('only one phase can be specified'))
4300 targetphase = idx
4300 targetphase = idx
4301
4301
4302 # look for specified revision
4302 # look for specified revision
4303 revs = list(revs)
4303 revs = list(revs)
4304 revs.extend(opts['rev'])
4304 revs.extend(opts['rev'])
4305 if not revs:
4305 if not revs:
4306 raise util.Abort(_('no revisions specified'))
4306 raise util.Abort(_('no revisions specified'))
4307
4307
4308 revs = scmutil.revrange(repo, revs)
4308 revs = scmutil.revrange(repo, revs)
4309
4309
4310 lock = None
4310 lock = None
4311 ret = 0
4311 ret = 0
4312 if targetphase is None:
4312 if targetphase is None:
4313 # display
4313 # display
4314 for r in revs:
4314 for r in revs:
4315 ctx = repo[r]
4315 ctx = repo[r]
4316 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4316 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4317 else:
4317 else:
4318 lock = repo.lock()
4318 lock = repo.lock()
4319 try:
4319 try:
4320 # set phase
4320 # set phase
4321 if not revs:
4321 if not revs:
4322 raise util.Abort(_('empty revision set'))
4322 raise util.Abort(_('empty revision set'))
4323 nodes = [repo[r].node() for r in revs]
4323 nodes = [repo[r].node() for r in revs]
4324 olddata = repo._phasecache.getphaserevs(repo)[:]
4324 olddata = repo._phasecache.getphaserevs(repo)[:]
4325 phases.advanceboundary(repo, targetphase, nodes)
4325 phases.advanceboundary(repo, targetphase, nodes)
4326 if opts['force']:
4326 if opts['force']:
4327 phases.retractboundary(repo, targetphase, nodes)
4327 phases.retractboundary(repo, targetphase, nodes)
4328 finally:
4328 finally:
4329 lock.release()
4329 lock.release()
4330 # moving revision from public to draft may hide them
4330 # moving revision from public to draft may hide them
4331 # We have to check result on an unfiltered repository
4331 # We have to check result on an unfiltered repository
4332 unfi = repo.unfiltered()
4332 unfi = repo.unfiltered()
4333 newdata = repo._phasecache.getphaserevs(unfi)
4333 newdata = repo._phasecache.getphaserevs(unfi)
4334 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4334 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4335 cl = unfi.changelog
4335 cl = unfi.changelog
4336 rejected = [n for n in nodes
4336 rejected = [n for n in nodes
4337 if newdata[cl.rev(n)] < targetphase]
4337 if newdata[cl.rev(n)] < targetphase]
4338 if rejected:
4338 if rejected:
4339 ui.warn(_('cannot move %i changesets to a more permissive '
4339 ui.warn(_('cannot move %i changesets to a more permissive '
4340 'phase, use --force\n') % len(rejected))
4340 'phase, use --force\n') % len(rejected))
4341 ret = 1
4341 ret = 1
4342 if changes:
4342 if changes:
4343 msg = _('phase changed for %i changesets\n') % changes
4343 msg = _('phase changed for %i changesets\n') % changes
4344 if ret:
4344 if ret:
4345 ui.status(msg)
4345 ui.status(msg)
4346 else:
4346 else:
4347 ui.note(msg)
4347 ui.note(msg)
4348 else:
4348 else:
4349 ui.warn(_('no phases changed\n'))
4349 ui.warn(_('no phases changed\n'))
4350 ret = 1
4350 ret = 1
4351 return ret
4351 return ret
4352
4352
4353 def postincoming(ui, repo, modheads, optupdate, checkout):
4353 def postincoming(ui, repo, modheads, optupdate, checkout):
4354 if modheads == 0:
4354 if modheads == 0:
4355 return
4355 return
4356 if optupdate:
4356 if optupdate:
4357 movemarkfrom = repo['.'].node()
4357 movemarkfrom = repo['.'].node()
4358 try:
4358 try:
4359 ret = hg.update(repo, checkout)
4359 ret = hg.update(repo, checkout)
4360 except util.Abort, inst:
4360 except util.Abort, inst:
4361 ui.warn(_("not updating: %s\n") % str(inst))
4361 ui.warn(_("not updating: %s\n") % str(inst))
4362 return 0
4362 return 0
4363 if not ret and not checkout:
4363 if not ret and not checkout:
4364 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4364 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4365 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4365 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4366 return ret
4366 return ret
4367 if modheads > 1:
4367 if modheads > 1:
4368 currentbranchheads = len(repo.branchheads())
4368 currentbranchheads = len(repo.branchheads())
4369 if currentbranchheads == modheads:
4369 if currentbranchheads == modheads:
4370 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4370 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4371 elif currentbranchheads > 1:
4371 elif currentbranchheads > 1:
4372 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4372 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4373 "merge)\n"))
4373 "merge)\n"))
4374 else:
4374 else:
4375 ui.status(_("(run 'hg heads' to see heads)\n"))
4375 ui.status(_("(run 'hg heads' to see heads)\n"))
4376 else:
4376 else:
4377 ui.status(_("(run 'hg update' to get a working copy)\n"))
4377 ui.status(_("(run 'hg update' to get a working copy)\n"))
4378
4378
4379 @command('^pull',
4379 @command('^pull',
4380 [('u', 'update', None,
4380 [('u', 'update', None,
4381 _('update to new branch head if changesets were pulled')),
4381 _('update to new branch head if changesets were pulled')),
4382 ('f', 'force', None, _('run even when remote repository is unrelated')),
4382 ('f', 'force', None, _('run even when remote repository is unrelated')),
4383 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4383 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4384 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4384 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4385 ('b', 'branch', [], _('a specific branch you would like to pull'),
4385 ('b', 'branch', [], _('a specific branch you would like to pull'),
4386 _('BRANCH')),
4386 _('BRANCH')),
4387 ] + remoteopts,
4387 ] + remoteopts,
4388 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4388 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4389 def pull(ui, repo, source="default", **opts):
4389 def pull(ui, repo, source="default", **opts):
4390 """pull changes from the specified source
4390 """pull changes from the specified source
4391
4391
4392 Pull changes from a remote repository to a local one.
4392 Pull changes from a remote repository to a local one.
4393
4393
4394 This finds all changes from the repository at the specified path
4394 This finds all changes from the repository at the specified path
4395 or URL and adds them to a local repository (the current one unless
4395 or URL and adds them to a local repository (the current one unless
4396 -R is specified). By default, this does not update the copy of the
4396 -R is specified). By default, this does not update the copy of the
4397 project in the working directory.
4397 project in the working directory.
4398
4398
4399 Use :hg:`incoming` if you want to see what would have been added
4399 Use :hg:`incoming` if you want to see what would have been added
4400 by a pull at the time you issued this command. If you then decide
4400 by a pull at the time you issued this command. If you then decide
4401 to add those changes to the repository, you should use :hg:`pull
4401 to add those changes to the repository, you should use :hg:`pull
4402 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4402 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4403
4403
4404 If SOURCE is omitted, the 'default' path will be used.
4404 If SOURCE is omitted, the 'default' path will be used.
4405 See :hg:`help urls` for more information.
4405 See :hg:`help urls` for more information.
4406
4406
4407 Returns 0 on success, 1 if an update had unresolved files.
4407 Returns 0 on success, 1 if an update had unresolved files.
4408 """
4408 """
4409 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4409 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4410 other = hg.peer(repo, opts, source)
4410 other = hg.peer(repo, opts, source)
4411 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4411 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4412 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4412 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4413
4413
4414 if opts.get('bookmark'):
4414 if opts.get('bookmark'):
4415 if not revs:
4415 if not revs:
4416 revs = []
4416 revs = []
4417 rb = other.listkeys('bookmarks')
4417 rb = other.listkeys('bookmarks')
4418 for b in opts['bookmark']:
4418 for b in opts['bookmark']:
4419 if b not in rb:
4419 if b not in rb:
4420 raise util.Abort(_('remote bookmark %s not found!') % b)
4420 raise util.Abort(_('remote bookmark %s not found!') % b)
4421 revs.append(rb[b])
4421 revs.append(rb[b])
4422
4422
4423 if revs:
4423 if revs:
4424 try:
4424 try:
4425 revs = [other.lookup(rev) for rev in revs]
4425 revs = [other.lookup(rev) for rev in revs]
4426 except error.CapabilityError:
4426 except error.CapabilityError:
4427 err = _("other repository doesn't support revision lookup, "
4427 err = _("other repository doesn't support revision lookup, "
4428 "so a rev cannot be specified.")
4428 "so a rev cannot be specified.")
4429 raise util.Abort(err)
4429 raise util.Abort(err)
4430
4430
4431 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4431 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4432 bookmarks.updatefromremote(ui, repo, other, source)
4432 bookmarks.updatefromremote(ui, repo, other, source)
4433 if checkout:
4433 if checkout:
4434 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4434 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4435 repo._subtoppath = source
4435 repo._subtoppath = source
4436 try:
4436 try:
4437 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4437 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4438
4438
4439 finally:
4439 finally:
4440 del repo._subtoppath
4440 del repo._subtoppath
4441
4441
4442 # update specified bookmarks
4442 # update specified bookmarks
4443 if opts.get('bookmark'):
4443 if opts.get('bookmark'):
4444 marks = repo._bookmarks
4444 marks = repo._bookmarks
4445 for b in opts['bookmark']:
4445 for b in opts['bookmark']:
4446 # explicit pull overrides local bookmark if any
4446 # explicit pull overrides local bookmark if any
4447 ui.status(_("importing bookmark %s\n") % b)
4447 ui.status(_("importing bookmark %s\n") % b)
4448 marks[b] = repo[rb[b]].node()
4448 marks[b] = repo[rb[b]].node()
4449 marks.write()
4449 marks.write()
4450
4450
4451 return ret
4451 return ret
4452
4452
4453 @command('^push',
4453 @command('^push',
4454 [('f', 'force', None, _('force push')),
4454 [('f', 'force', None, _('force push')),
4455 ('r', 'rev', [],
4455 ('r', 'rev', [],
4456 _('a changeset intended to be included in the destination'),
4456 _('a changeset intended to be included in the destination'),
4457 _('REV')),
4457 _('REV')),
4458 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4458 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4459 ('b', 'branch', [],
4459 ('b', 'branch', [],
4460 _('a specific branch you would like to push'), _('BRANCH')),
4460 _('a specific branch you would like to push'), _('BRANCH')),
4461 ('', 'new-branch', False, _('allow pushing a new branch')),
4461 ('', 'new-branch', False, _('allow pushing a new branch')),
4462 ] + remoteopts,
4462 ] + remoteopts,
4463 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4463 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4464 def push(ui, repo, dest=None, **opts):
4464 def push(ui, repo, dest=None, **opts):
4465 """push changes to the specified destination
4465 """push changes to the specified destination
4466
4466
4467 Push changesets from the local repository to the specified
4467 Push changesets from the local repository to the specified
4468 destination.
4468 destination.
4469
4469
4470 This operation is symmetrical to pull: it is identical to a pull
4470 This operation is symmetrical to pull: it is identical to a pull
4471 in the destination repository from the current one.
4471 in the destination repository from the current one.
4472
4472
4473 By default, push will not allow creation of new heads at the
4473 By default, push will not allow creation of new heads at the
4474 destination, since multiple heads would make it unclear which head
4474 destination, since multiple heads would make it unclear which head
4475 to use. In this situation, it is recommended to pull and merge
4475 to use. In this situation, it is recommended to pull and merge
4476 before pushing.
4476 before pushing.
4477
4477
4478 Use --new-branch if you want to allow push to create a new named
4478 Use --new-branch if you want to allow push to create a new named
4479 branch that is not present at the destination. This allows you to
4479 branch that is not present at the destination. This allows you to
4480 only create a new branch without forcing other changes.
4480 only create a new branch without forcing other changes.
4481
4481
4482 Use -f/--force to override the default behavior and push all
4482 Use -f/--force to override the default behavior and push all
4483 changesets on all branches.
4483 changesets on all branches.
4484
4484
4485 If -r/--rev is used, the specified revision and all its ancestors
4485 If -r/--rev is used, the specified revision and all its ancestors
4486 will be pushed to the remote repository.
4486 will be pushed to the remote repository.
4487
4487
4488 If -B/--bookmark is used, the specified bookmarked revision, its
4488 If -B/--bookmark is used, the specified bookmarked revision, its
4489 ancestors, and the bookmark will be pushed to the remote
4489 ancestors, and the bookmark will be pushed to the remote
4490 repository.
4490 repository.
4491
4491
4492 Please see :hg:`help urls` for important details about ``ssh://``
4492 Please see :hg:`help urls` for important details about ``ssh://``
4493 URLs. If DESTINATION is omitted, a default path will be used.
4493 URLs. If DESTINATION is omitted, a default path will be used.
4494
4494
4495 Returns 0 if push was successful, 1 if nothing to push.
4495 Returns 0 if push was successful, 1 if nothing to push.
4496 """
4496 """
4497
4497
4498 if opts.get('bookmark'):
4498 if opts.get('bookmark'):
4499 for b in opts['bookmark']:
4499 for b in opts['bookmark']:
4500 # translate -B options to -r so changesets get pushed
4500 # translate -B options to -r so changesets get pushed
4501 if b in repo._bookmarks:
4501 if b in repo._bookmarks:
4502 opts.setdefault('rev', []).append(b)
4502 opts.setdefault('rev', []).append(b)
4503 else:
4503 else:
4504 # if we try to push a deleted bookmark, translate it to null
4504 # if we try to push a deleted bookmark, translate it to null
4505 # this lets simultaneous -r, -b options continue working
4505 # this lets simultaneous -r, -b options continue working
4506 opts.setdefault('rev', []).append("null")
4506 opts.setdefault('rev', []).append("null")
4507
4507
4508 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4508 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4509 dest, branches = hg.parseurl(dest, opts.get('branch'))
4509 dest, branches = hg.parseurl(dest, opts.get('branch'))
4510 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4510 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4511 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4511 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4512 other = hg.peer(repo, opts, dest)
4512 other = hg.peer(repo, opts, dest)
4513 if revs:
4513 if revs:
4514 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4514 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4515
4515
4516 repo._subtoppath = dest
4516 repo._subtoppath = dest
4517 try:
4517 try:
4518 # push subrepos depth-first for coherent ordering
4518 # push subrepos depth-first for coherent ordering
4519 c = repo['']
4519 c = repo['']
4520 subs = c.substate # only repos that are committed
4520 subs = c.substate # only repos that are committed
4521 for s in sorted(subs):
4521 for s in sorted(subs):
4522 if c.sub(s).push(opts) == 0:
4522 if c.sub(s).push(opts) == 0:
4523 return False
4523 return False
4524 finally:
4524 finally:
4525 del repo._subtoppath
4525 del repo._subtoppath
4526 result = repo.push(other, opts.get('force'), revs=revs,
4526 result = repo.push(other, opts.get('force'), revs=revs,
4527 newbranch=opts.get('new_branch'))
4527 newbranch=opts.get('new_branch'))
4528
4528
4529 result = not result
4529 result = not result
4530
4530
4531 if opts.get('bookmark'):
4531 if opts.get('bookmark'):
4532 rb = other.listkeys('bookmarks')
4532 rb = other.listkeys('bookmarks')
4533 for b in opts['bookmark']:
4533 for b in opts['bookmark']:
4534 # explicit push overrides remote bookmark if any
4534 # explicit push overrides remote bookmark if any
4535 if b in repo._bookmarks:
4535 if b in repo._bookmarks:
4536 ui.status(_("exporting bookmark %s\n") % b)
4536 ui.status(_("exporting bookmark %s\n") % b)
4537 new = repo[b].hex()
4537 new = repo[b].hex()
4538 elif b in rb:
4538 elif b in rb:
4539 ui.status(_("deleting remote bookmark %s\n") % b)
4539 ui.status(_("deleting remote bookmark %s\n") % b)
4540 new = '' # delete
4540 new = '' # delete
4541 else:
4541 else:
4542 ui.warn(_('bookmark %s does not exist on the local '
4542 ui.warn(_('bookmark %s does not exist on the local '
4543 'or remote repository!\n') % b)
4543 'or remote repository!\n') % b)
4544 return 2
4544 return 2
4545 old = rb.get(b, '')
4545 old = rb.get(b, '')
4546 r = other.pushkey('bookmarks', b, old, new)
4546 r = other.pushkey('bookmarks', b, old, new)
4547 if not r:
4547 if not r:
4548 ui.warn(_('updating bookmark %s failed!\n') % b)
4548 ui.warn(_('updating bookmark %s failed!\n') % b)
4549 if not result:
4549 if not result:
4550 result = 2
4550 result = 2
4551
4551
4552 return result
4552 return result
4553
4553
4554 @command('recover', [])
4554 @command('recover', [])
4555 def recover(ui, repo):
4555 def recover(ui, repo):
4556 """roll back an interrupted transaction
4556 """roll back an interrupted transaction
4557
4557
4558 Recover from an interrupted commit or pull.
4558 Recover from an interrupted commit or pull.
4559
4559
4560 This command tries to fix the repository status after an
4560 This command tries to fix the repository status after an
4561 interrupted operation. It should only be necessary when Mercurial
4561 interrupted operation. It should only be necessary when Mercurial
4562 suggests it.
4562 suggests it.
4563
4563
4564 Returns 0 if successful, 1 if nothing to recover or verify fails.
4564 Returns 0 if successful, 1 if nothing to recover or verify fails.
4565 """
4565 """
4566 if repo.recover():
4566 if repo.recover():
4567 return hg.verify(repo)
4567 return hg.verify(repo)
4568 return 1
4568 return 1
4569
4569
4570 @command('^remove|rm',
4570 @command('^remove|rm',
4571 [('A', 'after', None, _('record delete for missing files')),
4571 [('A', 'after', None, _('record delete for missing files')),
4572 ('f', 'force', None,
4572 ('f', 'force', None,
4573 _('remove (and delete) file even if added or modified')),
4573 _('remove (and delete) file even if added or modified')),
4574 ] + walkopts,
4574 ] + walkopts,
4575 _('[OPTION]... FILE...'))
4575 _('[OPTION]... FILE...'))
4576 def remove(ui, repo, *pats, **opts):
4576 def remove(ui, repo, *pats, **opts):
4577 """remove the specified files on the next commit
4577 """remove the specified files on the next commit
4578
4578
4579 Schedule the indicated files for removal from the current branch.
4579 Schedule the indicated files for removal from the current branch.
4580
4580
4581 This command schedules the files to be removed at the next commit.
4581 This command schedules the files to be removed at the next commit.
4582 To undo a remove before that, see :hg:`revert`. To undo added
4582 To undo a remove before that, see :hg:`revert`. To undo added
4583 files, see :hg:`forget`.
4583 files, see :hg:`forget`.
4584
4584
4585 .. container:: verbose
4585 .. container:: verbose
4586
4586
4587 -A/--after can be used to remove only files that have already
4587 -A/--after can be used to remove only files that have already
4588 been deleted, -f/--force can be used to force deletion, and -Af
4588 been deleted, -f/--force can be used to force deletion, and -Af
4589 can be used to remove files from the next revision without
4589 can be used to remove files from the next revision without
4590 deleting them from the working directory.
4590 deleting them from the working directory.
4591
4591
4592 The following table details the behavior of remove for different
4592 The following table details the behavior of remove for different
4593 file states (columns) and option combinations (rows). The file
4593 file states (columns) and option combinations (rows). The file
4594 states are Added [A], Clean [C], Modified [M] and Missing [!]
4594 states are Added [A], Clean [C], Modified [M] and Missing [!]
4595 (as reported by :hg:`status`). The actions are Warn, Remove
4595 (as reported by :hg:`status`). The actions are Warn, Remove
4596 (from branch) and Delete (from disk):
4596 (from branch) and Delete (from disk):
4597
4597
4598 ======= == == == ==
4598 ======= == == == ==
4599 A C M !
4599 A C M !
4600 ======= == == == ==
4600 ======= == == == ==
4601 none W RD W R
4601 none W RD W R
4602 -f R RD RD R
4602 -f R RD RD R
4603 -A W W W R
4603 -A W W W R
4604 -Af R R R R
4604 -Af R R R R
4605 ======= == == == ==
4605 ======= == == == ==
4606
4606
4607 Note that remove never deletes files in Added [A] state from the
4607 Note that remove never deletes files in Added [A] state from the
4608 working directory, not even if option --force is specified.
4608 working directory, not even if option --force is specified.
4609
4609
4610 Returns 0 on success, 1 if any warnings encountered.
4610 Returns 0 on success, 1 if any warnings encountered.
4611 """
4611 """
4612
4612
4613 ret = 0
4613 ret = 0
4614 after, force = opts.get('after'), opts.get('force')
4614 after, force = opts.get('after'), opts.get('force')
4615 if not pats and not after:
4615 if not pats and not after:
4616 raise util.Abort(_('no files specified'))
4616 raise util.Abort(_('no files specified'))
4617
4617
4618 m = scmutil.match(repo[None], pats, opts)
4618 m = scmutil.match(repo[None], pats, opts)
4619 s = repo.status(match=m, clean=True)
4619 s = repo.status(match=m, clean=True)
4620 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4620 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4621
4621
4622 # warn about failure to delete explicit files/dirs
4622 # warn about failure to delete explicit files/dirs
4623 wctx = repo[None]
4623 wctx = repo[None]
4624 for f in m.files():
4624 for f in m.files():
4625 if f in repo.dirstate or f in wctx.dirs():
4625 if f in repo.dirstate or f in wctx.dirs():
4626 continue
4626 continue
4627 if os.path.exists(m.rel(f)):
4627 if os.path.exists(m.rel(f)):
4628 if os.path.isdir(m.rel(f)):
4628 if os.path.isdir(m.rel(f)):
4629 ui.warn(_('not removing %s: no tracked files\n') % m.rel(f))
4629 ui.warn(_('not removing %s: no tracked files\n') % m.rel(f))
4630 else:
4630 else:
4631 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4631 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4632 # missing files will generate a warning elsewhere
4632 # missing files will generate a warning elsewhere
4633 ret = 1
4633 ret = 1
4634
4634
4635 if force:
4635 if force:
4636 list = modified + deleted + clean + added
4636 list = modified + deleted + clean + added
4637 elif after:
4637 elif after:
4638 list = deleted
4638 list = deleted
4639 for f in modified + added + clean:
4639 for f in modified + added + clean:
4640 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
4640 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
4641 ret = 1
4641 ret = 1
4642 else:
4642 else:
4643 list = deleted + clean
4643 list = deleted + clean
4644 for f in modified:
4644 for f in modified:
4645 ui.warn(_('not removing %s: file is modified (use -f'
4645 ui.warn(_('not removing %s: file is modified (use -f'
4646 ' to force removal)\n') % m.rel(f))
4646 ' to force removal)\n') % m.rel(f))
4647 ret = 1
4647 ret = 1
4648 for f in added:
4648 for f in added:
4649 ui.warn(_('not removing %s: file has been marked for add'
4649 ui.warn(_('not removing %s: file has been marked for add'
4650 ' (use forget to undo)\n') % m.rel(f))
4650 ' (use forget to undo)\n') % m.rel(f))
4651 ret = 1
4651 ret = 1
4652
4652
4653 for f in sorted(list):
4653 for f in sorted(list):
4654 if ui.verbose or not m.exact(f):
4654 if ui.verbose or not m.exact(f):
4655 ui.status(_('removing %s\n') % m.rel(f))
4655 ui.status(_('removing %s\n') % m.rel(f))
4656
4656
4657 wlock = repo.wlock()
4657 wlock = repo.wlock()
4658 try:
4658 try:
4659 if not after:
4659 if not after:
4660 for f in list:
4660 for f in list:
4661 if f in added:
4661 if f in added:
4662 continue # we never unlink added files on remove
4662 continue # we never unlink added files on remove
4663 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
4663 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
4664 repo[None].forget(list)
4664 repo[None].forget(list)
4665 finally:
4665 finally:
4666 wlock.release()
4666 wlock.release()
4667
4667
4668 return ret
4668 return ret
4669
4669
4670 @command('rename|move|mv',
4670 @command('rename|move|mv',
4671 [('A', 'after', None, _('record a rename that has already occurred')),
4671 [('A', 'after', None, _('record a rename that has already occurred')),
4672 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4672 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4673 ] + walkopts + dryrunopts,
4673 ] + walkopts + dryrunopts,
4674 _('[OPTION]... SOURCE... DEST'))
4674 _('[OPTION]... SOURCE... DEST'))
4675 def rename(ui, repo, *pats, **opts):
4675 def rename(ui, repo, *pats, **opts):
4676 """rename files; equivalent of copy + remove
4676 """rename files; equivalent of copy + remove
4677
4677
4678 Mark dest as copies of sources; mark sources for deletion. If dest
4678 Mark dest as copies of sources; mark sources for deletion. If dest
4679 is a directory, copies are put in that directory. If dest is a
4679 is a directory, copies are put in that directory. If dest is a
4680 file, there can only be one source.
4680 file, there can only be one source.
4681
4681
4682 By default, this command copies the contents of files as they
4682 By default, this command copies the contents of files as they
4683 exist in the working directory. If invoked with -A/--after, the
4683 exist in the working directory. If invoked with -A/--after, the
4684 operation is recorded, but no copying is performed.
4684 operation is recorded, but no copying is performed.
4685
4685
4686 This command takes effect at the next commit. To undo a rename
4686 This command takes effect at the next commit. To undo a rename
4687 before that, see :hg:`revert`.
4687 before that, see :hg:`revert`.
4688
4688
4689 Returns 0 on success, 1 if errors are encountered.
4689 Returns 0 on success, 1 if errors are encountered.
4690 """
4690 """
4691 wlock = repo.wlock(False)
4691 wlock = repo.wlock(False)
4692 try:
4692 try:
4693 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4693 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4694 finally:
4694 finally:
4695 wlock.release()
4695 wlock.release()
4696
4696
4697 @command('resolve',
4697 @command('resolve',
4698 [('a', 'all', None, _('select all unresolved files')),
4698 [('a', 'all', None, _('select all unresolved files')),
4699 ('l', 'list', None, _('list state of files needing merge')),
4699 ('l', 'list', None, _('list state of files needing merge')),
4700 ('m', 'mark', None, _('mark files as resolved')),
4700 ('m', 'mark', None, _('mark files as resolved')),
4701 ('u', 'unmark', None, _('mark files as unresolved')),
4701 ('u', 'unmark', None, _('mark files as unresolved')),
4702 ('n', 'no-status', None, _('hide status prefix'))]
4702 ('n', 'no-status', None, _('hide status prefix'))]
4703 + mergetoolopts + walkopts,
4703 + mergetoolopts + walkopts,
4704 _('[OPTION]... [FILE]...'))
4704 _('[OPTION]... [FILE]...'))
4705 def resolve(ui, repo, *pats, **opts):
4705 def resolve(ui, repo, *pats, **opts):
4706 """redo merges or set/view the merge status of files
4706 """redo merges or set/view the merge status of files
4707
4707
4708 Merges with unresolved conflicts are often the result of
4708 Merges with unresolved conflicts are often the result of
4709 non-interactive merging using the ``internal:merge`` configuration
4709 non-interactive merging using the ``internal:merge`` configuration
4710 setting, or a command-line merge tool like ``diff3``. The resolve
4710 setting, or a command-line merge tool like ``diff3``. The resolve
4711 command is used to manage the files involved in a merge, after
4711 command is used to manage the files involved in a merge, after
4712 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4712 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4713 working directory must have two parents). See :hg:`help
4713 working directory must have two parents). See :hg:`help
4714 merge-tools` for information on configuring merge tools.
4714 merge-tools` for information on configuring merge tools.
4715
4715
4716 The resolve command can be used in the following ways:
4716 The resolve command can be used in the following ways:
4717
4717
4718 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4718 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4719 files, discarding any previous merge attempts. Re-merging is not
4719 files, discarding any previous merge attempts. Re-merging is not
4720 performed for files already marked as resolved. Use ``--all/-a``
4720 performed for files already marked as resolved. Use ``--all/-a``
4721 to select all unresolved files. ``--tool`` can be used to specify
4721 to select all unresolved files. ``--tool`` can be used to specify
4722 the merge tool used for the given files. It overrides the HGMERGE
4722 the merge tool used for the given files. It overrides the HGMERGE
4723 environment variable and your configuration files. Previous file
4723 environment variable and your configuration files. Previous file
4724 contents are saved with a ``.orig`` suffix.
4724 contents are saved with a ``.orig`` suffix.
4725
4725
4726 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4726 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4727 (e.g. after having manually fixed-up the files). The default is
4727 (e.g. after having manually fixed-up the files). The default is
4728 to mark all unresolved files.
4728 to mark all unresolved files.
4729
4729
4730 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4730 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4731 default is to mark all resolved files.
4731 default is to mark all resolved files.
4732
4732
4733 - :hg:`resolve -l`: list files which had or still have conflicts.
4733 - :hg:`resolve -l`: list files which had or still have conflicts.
4734 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4734 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4735
4735
4736 Note that Mercurial will not let you commit files with unresolved
4736 Note that Mercurial will not let you commit files with unresolved
4737 merge conflicts. You must use :hg:`resolve -m ...` before you can
4737 merge conflicts. You must use :hg:`resolve -m ...` before you can
4738 commit after a conflicting merge.
4738 commit after a conflicting merge.
4739
4739
4740 Returns 0 on success, 1 if any files fail a resolve attempt.
4740 Returns 0 on success, 1 if any files fail a resolve attempt.
4741 """
4741 """
4742
4742
4743 all, mark, unmark, show, nostatus = \
4743 all, mark, unmark, show, nostatus = \
4744 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4744 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4745
4745
4746 if (show and (mark or unmark)) or (mark and unmark):
4746 if (show and (mark or unmark)) or (mark and unmark):
4747 raise util.Abort(_("too many options specified"))
4747 raise util.Abort(_("too many options specified"))
4748 if pats and all:
4748 if pats and all:
4749 raise util.Abort(_("can't specify --all and patterns"))
4749 raise util.Abort(_("can't specify --all and patterns"))
4750 if not (all or pats or show or mark or unmark):
4750 if not (all or pats or show or mark or unmark):
4751 raise util.Abort(_('no files or directories specified; '
4751 raise util.Abort(_('no files or directories specified; '
4752 'use --all to remerge all files'))
4752 'use --all to remerge all files'))
4753
4753
4754 ms = mergemod.mergestate(repo)
4754 ms = mergemod.mergestate(repo)
4755 m = scmutil.match(repo[None], pats, opts)
4755 m = scmutil.match(repo[None], pats, opts)
4756 ret = 0
4756 ret = 0
4757
4757
4758 for f in ms:
4758 for f in ms:
4759 if m(f):
4759 if m(f):
4760 if show:
4760 if show:
4761 if nostatus:
4761 if nostatus:
4762 ui.write("%s\n" % f)
4762 ui.write("%s\n" % f)
4763 else:
4763 else:
4764 ui.write("%s %s\n" % (ms[f].upper(), f),
4764 ui.write("%s %s\n" % (ms[f].upper(), f),
4765 label='resolve.' +
4765 label='resolve.' +
4766 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4766 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4767 elif mark:
4767 elif mark:
4768 ms.mark(f, "r")
4768 ms.mark(f, "r")
4769 elif unmark:
4769 elif unmark:
4770 ms.mark(f, "u")
4770 ms.mark(f, "u")
4771 else:
4771 else:
4772 wctx = repo[None]
4772 wctx = repo[None]
4773 mctx = wctx.parents()[-1]
4773 mctx = wctx.parents()[-1]
4774
4774
4775 # backup pre-resolve (merge uses .orig for its own purposes)
4775 # backup pre-resolve (merge uses .orig for its own purposes)
4776 a = repo.wjoin(f)
4776 a = repo.wjoin(f)
4777 util.copyfile(a, a + ".resolve")
4777 util.copyfile(a, a + ".resolve")
4778
4778
4779 try:
4779 try:
4780 # resolve file
4780 # resolve file
4781 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4781 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4782 if ms.resolve(f, wctx, mctx):
4782 if ms.resolve(f, wctx, mctx):
4783 ret = 1
4783 ret = 1
4784 finally:
4784 finally:
4785 ui.setconfig('ui', 'forcemerge', '')
4785 ui.setconfig('ui', 'forcemerge', '')
4786 ms.commit()
4786 ms.commit()
4787
4787
4788 # replace filemerge's .orig file with our resolve file
4788 # replace filemerge's .orig file with our resolve file
4789 util.rename(a + ".resolve", a + ".orig")
4789 util.rename(a + ".resolve", a + ".orig")
4790
4790
4791 ms.commit()
4791 ms.commit()
4792 return ret
4792 return ret
4793
4793
4794 @command('revert',
4794 @command('revert',
4795 [('a', 'all', None, _('revert all changes when no arguments given')),
4795 [('a', 'all', None, _('revert all changes when no arguments given')),
4796 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4796 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4797 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4797 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4798 ('C', 'no-backup', None, _('do not save backup copies of files')),
4798 ('C', 'no-backup', None, _('do not save backup copies of files')),
4799 ] + walkopts + dryrunopts,
4799 ] + walkopts + dryrunopts,
4800 _('[OPTION]... [-r REV] [NAME]...'))
4800 _('[OPTION]... [-r REV] [NAME]...'))
4801 def revert(ui, repo, *pats, **opts):
4801 def revert(ui, repo, *pats, **opts):
4802 """restore files to their checkout state
4802 """restore files to their checkout state
4803
4803
4804 .. note::
4804 .. note::
4805
4805
4806 To check out earlier revisions, you should use :hg:`update REV`.
4806 To check out earlier revisions, you should use :hg:`update REV`.
4807 To cancel an uncommitted merge (and lose your changes), use
4807 To cancel an uncommitted merge (and lose your changes), use
4808 :hg:`update --clean .`.
4808 :hg:`update --clean .`.
4809
4809
4810 With no revision specified, revert the specified files or directories
4810 With no revision specified, revert the specified files or directories
4811 to the contents they had in the parent of the working directory.
4811 to the contents they had in the parent of the working directory.
4812 This restores the contents of files to an unmodified
4812 This restores the contents of files to an unmodified
4813 state and unschedules adds, removes, copies, and renames. If the
4813 state and unschedules adds, removes, copies, and renames. If the
4814 working directory has two parents, you must explicitly specify a
4814 working directory has two parents, you must explicitly specify a
4815 revision.
4815 revision.
4816
4816
4817 Using the -r/--rev or -d/--date options, revert the given files or
4817 Using the -r/--rev or -d/--date options, revert the given files or
4818 directories to their states as of a specific revision. Because
4818 directories to their states as of a specific revision. Because
4819 revert does not change the working directory parents, this will
4819 revert does not change the working directory parents, this will
4820 cause these files to appear modified. This can be helpful to "back
4820 cause these files to appear modified. This can be helpful to "back
4821 out" some or all of an earlier change. See :hg:`backout` for a
4821 out" some or all of an earlier change. See :hg:`backout` for a
4822 related method.
4822 related method.
4823
4823
4824 Modified files are saved with a .orig suffix before reverting.
4824 Modified files are saved with a .orig suffix before reverting.
4825 To disable these backups, use --no-backup.
4825 To disable these backups, use --no-backup.
4826
4826
4827 See :hg:`help dates` for a list of formats valid for -d/--date.
4827 See :hg:`help dates` for a list of formats valid for -d/--date.
4828
4828
4829 Returns 0 on success.
4829 Returns 0 on success.
4830 """
4830 """
4831
4831
4832 if opts.get("date"):
4832 if opts.get("date"):
4833 if opts.get("rev"):
4833 if opts.get("rev"):
4834 raise util.Abort(_("you can't specify a revision and a date"))
4834 raise util.Abort(_("you can't specify a revision and a date"))
4835 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4835 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4836
4836
4837 parent, p2 = repo.dirstate.parents()
4837 parent, p2 = repo.dirstate.parents()
4838 if not opts.get('rev') and p2 != nullid:
4838 if not opts.get('rev') and p2 != nullid:
4839 # revert after merge is a trap for new users (issue2915)
4839 # revert after merge is a trap for new users (issue2915)
4840 raise util.Abort(_('uncommitted merge with no revision specified'),
4840 raise util.Abort(_('uncommitted merge with no revision specified'),
4841 hint=_('use "hg update" or see "hg help revert"'))
4841 hint=_('use "hg update" or see "hg help revert"'))
4842
4842
4843 ctx = scmutil.revsingle(repo, opts.get('rev'))
4843 ctx = scmutil.revsingle(repo, opts.get('rev'))
4844
4844
4845 if not pats and not opts.get('all'):
4845 if not pats and not opts.get('all'):
4846 msg = _("no files or directories specified")
4846 msg = _("no files or directories specified")
4847 if p2 != nullid:
4847 if p2 != nullid:
4848 hint = _("uncommitted merge, use --all to discard all changes,"
4848 hint = _("uncommitted merge, use --all to discard all changes,"
4849 " or 'hg update -C .' to abort the merge")
4849 " or 'hg update -C .' to abort the merge")
4850 raise util.Abort(msg, hint=hint)
4850 raise util.Abort(msg, hint=hint)
4851 dirty = util.any(repo.status())
4851 dirty = util.any(repo.status())
4852 node = ctx.node()
4852 node = ctx.node()
4853 if node != parent:
4853 if node != parent:
4854 if dirty:
4854 if dirty:
4855 hint = _("uncommitted changes, use --all to discard all"
4855 hint = _("uncommitted changes, use --all to discard all"
4856 " changes, or 'hg update %s' to update") % ctx.rev()
4856 " changes, or 'hg update %s' to update") % ctx.rev()
4857 else:
4857 else:
4858 hint = _("use --all to revert all files,"
4858 hint = _("use --all to revert all files,"
4859 " or 'hg update %s' to update") % ctx.rev()
4859 " or 'hg update %s' to update") % ctx.rev()
4860 elif dirty:
4860 elif dirty:
4861 hint = _("uncommitted changes, use --all to discard all changes")
4861 hint = _("uncommitted changes, use --all to discard all changes")
4862 else:
4862 else:
4863 hint = _("use --all to revert all files")
4863 hint = _("use --all to revert all files")
4864 raise util.Abort(msg, hint=hint)
4864 raise util.Abort(msg, hint=hint)
4865
4865
4866 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4866 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4867
4867
4868 @command('rollback', dryrunopts +
4868 @command('rollback', dryrunopts +
4869 [('f', 'force', False, _('ignore safety measures'))])
4869 [('f', 'force', False, _('ignore safety measures'))])
4870 def rollback(ui, repo, **opts):
4870 def rollback(ui, repo, **opts):
4871 """roll back the last transaction (dangerous)
4871 """roll back the last transaction (dangerous)
4872
4872
4873 This command should be used with care. There is only one level of
4873 This command should be used with care. There is only one level of
4874 rollback, and there is no way to undo a rollback. It will also
4874 rollback, and there is no way to undo a rollback. It will also
4875 restore the dirstate at the time of the last transaction, losing
4875 restore the dirstate at the time of the last transaction, losing
4876 any dirstate changes since that time. This command does not alter
4876 any dirstate changes since that time. This command does not alter
4877 the working directory.
4877 the working directory.
4878
4878
4879 Transactions are used to encapsulate the effects of all commands
4879 Transactions are used to encapsulate the effects of all commands
4880 that create new changesets or propagate existing changesets into a
4880 that create new changesets or propagate existing changesets into a
4881 repository.
4881 repository.
4882
4882
4883 .. container:: verbose
4883 .. container:: verbose
4884
4884
4885 For example, the following commands are transactional, and their
4885 For example, the following commands are transactional, and their
4886 effects can be rolled back:
4886 effects can be rolled back:
4887
4887
4888 - commit
4888 - commit
4889 - import
4889 - import
4890 - pull
4890 - pull
4891 - push (with this repository as the destination)
4891 - push (with this repository as the destination)
4892 - unbundle
4892 - unbundle
4893
4893
4894 To avoid permanent data loss, rollback will refuse to rollback a
4894 To avoid permanent data loss, rollback will refuse to rollback a
4895 commit transaction if it isn't checked out. Use --force to
4895 commit transaction if it isn't checked out. Use --force to
4896 override this protection.
4896 override this protection.
4897
4897
4898 This command is not intended for use on public repositories. Once
4898 This command is not intended for use on public repositories. Once
4899 changes are visible for pull by other users, rolling a transaction
4899 changes are visible for pull by other users, rolling a transaction
4900 back locally is ineffective (someone else may already have pulled
4900 back locally is ineffective (someone else may already have pulled
4901 the changes). Furthermore, a race is possible with readers of the
4901 the changes). Furthermore, a race is possible with readers of the
4902 repository; for example an in-progress pull from the repository
4902 repository; for example an in-progress pull from the repository
4903 may fail if a rollback is performed.
4903 may fail if a rollback is performed.
4904
4904
4905 Returns 0 on success, 1 if no rollback data is available.
4905 Returns 0 on success, 1 if no rollback data is available.
4906 """
4906 """
4907 return repo.rollback(dryrun=opts.get('dry_run'),
4907 return repo.rollback(dryrun=opts.get('dry_run'),
4908 force=opts.get('force'))
4908 force=opts.get('force'))
4909
4909
4910 @command('root', [])
4910 @command('root', [])
4911 def root(ui, repo):
4911 def root(ui, repo):
4912 """print the root (top) of the current working directory
4912 """print the root (top) of the current working directory
4913
4913
4914 Print the root directory of the current repository.
4914 Print the root directory of the current repository.
4915
4915
4916 Returns 0 on success.
4916 Returns 0 on success.
4917 """
4917 """
4918 ui.write(repo.root + "\n")
4918 ui.write(repo.root + "\n")
4919
4919
4920 @command('^serve',
4920 @command('^serve',
4921 [('A', 'accesslog', '', _('name of access log file to write to'),
4921 [('A', 'accesslog', '', _('name of access log file to write to'),
4922 _('FILE')),
4922 _('FILE')),
4923 ('d', 'daemon', None, _('run server in background')),
4923 ('d', 'daemon', None, _('run server in background')),
4924 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4924 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4925 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4925 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4926 # use string type, then we can check if something was passed
4926 # use string type, then we can check if something was passed
4927 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4927 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4928 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4928 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4929 _('ADDR')),
4929 _('ADDR')),
4930 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4930 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4931 _('PREFIX')),
4931 _('PREFIX')),
4932 ('n', 'name', '',
4932 ('n', 'name', '',
4933 _('name to show in web pages (default: working directory)'), _('NAME')),
4933 _('name to show in web pages (default: working directory)'), _('NAME')),
4934 ('', 'web-conf', '',
4934 ('', 'web-conf', '',
4935 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4935 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4936 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4936 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4937 _('FILE')),
4937 _('FILE')),
4938 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4938 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4939 ('', 'stdio', None, _('for remote clients')),
4939 ('', 'stdio', None, _('for remote clients')),
4940 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
4940 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
4941 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4941 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4942 ('', 'style', '', _('template style to use'), _('STYLE')),
4942 ('', 'style', '', _('template style to use'), _('STYLE')),
4943 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4943 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4944 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4944 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4945 _('[OPTION]...'))
4945 _('[OPTION]...'))
4946 def serve(ui, repo, **opts):
4946 def serve(ui, repo, **opts):
4947 """start stand-alone webserver
4947 """start stand-alone webserver
4948
4948
4949 Start a local HTTP repository browser and pull server. You can use
4949 Start a local HTTP repository browser and pull server. You can use
4950 this for ad-hoc sharing and browsing of repositories. It is
4950 this for ad-hoc sharing and browsing of repositories. It is
4951 recommended to use a real web server to serve a repository for
4951 recommended to use a real web server to serve a repository for
4952 longer periods of time.
4952 longer periods of time.
4953
4953
4954 Please note that the server does not implement access control.
4954 Please note that the server does not implement access control.
4955 This means that, by default, anybody can read from the server and
4955 This means that, by default, anybody can read from the server and
4956 nobody can write to it by default. Set the ``web.allow_push``
4956 nobody can write to it by default. Set the ``web.allow_push``
4957 option to ``*`` to allow everybody to push to the server. You
4957 option to ``*`` to allow everybody to push to the server. You
4958 should use a real web server if you need to authenticate users.
4958 should use a real web server if you need to authenticate users.
4959
4959
4960 By default, the server logs accesses to stdout and errors to
4960 By default, the server logs accesses to stdout and errors to
4961 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4961 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4962 files.
4962 files.
4963
4963
4964 To have the server choose a free port number to listen on, specify
4964 To have the server choose a free port number to listen on, specify
4965 a port number of 0; in this case, the server will print the port
4965 a port number of 0; in this case, the server will print the port
4966 number it uses.
4966 number it uses.
4967
4967
4968 Returns 0 on success.
4968 Returns 0 on success.
4969 """
4969 """
4970
4970
4971 if opts["stdio"] and opts["cmdserver"]:
4971 if opts["stdio"] and opts["cmdserver"]:
4972 raise util.Abort(_("cannot use --stdio with --cmdserver"))
4972 raise util.Abort(_("cannot use --stdio with --cmdserver"))
4973
4973
4974 def checkrepo():
4974 def checkrepo():
4975 if repo is None:
4975 if repo is None:
4976 raise error.RepoError(_("there is no Mercurial repository here"
4976 raise error.RepoError(_("there is no Mercurial repository here"
4977 " (.hg not found)"))
4977 " (.hg not found)"))
4978
4978
4979 if opts["stdio"]:
4979 if opts["stdio"]:
4980 checkrepo()
4980 checkrepo()
4981 s = sshserver.sshserver(ui, repo)
4981 s = sshserver.sshserver(ui, repo)
4982 s.serve_forever()
4982 s.serve_forever()
4983
4983
4984 if opts["cmdserver"]:
4984 if opts["cmdserver"]:
4985 checkrepo()
4985 checkrepo()
4986 s = commandserver.server(ui, repo, opts["cmdserver"])
4986 s = commandserver.server(ui, repo, opts["cmdserver"])
4987 return s.serve()
4987 return s.serve()
4988
4988
4989 # this way we can check if something was given in the command-line
4989 # this way we can check if something was given in the command-line
4990 if opts.get('port'):
4990 if opts.get('port'):
4991 opts['port'] = util.getport(opts.get('port'))
4991 opts['port'] = util.getport(opts.get('port'))
4992
4992
4993 baseui = repo and repo.baseui or ui
4993 baseui = repo and repo.baseui or ui
4994 optlist = ("name templates style address port prefix ipv6"
4994 optlist = ("name templates style address port prefix ipv6"
4995 " accesslog errorlog certificate encoding")
4995 " accesslog errorlog certificate encoding")
4996 for o in optlist.split():
4996 for o in optlist.split():
4997 val = opts.get(o, '')
4997 val = opts.get(o, '')
4998 if val in (None, ''): # should check against default options instead
4998 if val in (None, ''): # should check against default options instead
4999 continue
4999 continue
5000 baseui.setconfig("web", o, val)
5000 baseui.setconfig("web", o, val)
5001 if repo and repo.ui != baseui:
5001 if repo and repo.ui != baseui:
5002 repo.ui.setconfig("web", o, val)
5002 repo.ui.setconfig("web", o, val)
5003
5003
5004 o = opts.get('web_conf') or opts.get('webdir_conf')
5004 o = opts.get('web_conf') or opts.get('webdir_conf')
5005 if not o:
5005 if not o:
5006 if not repo:
5006 if not repo:
5007 raise error.RepoError(_("there is no Mercurial repository"
5007 raise error.RepoError(_("there is no Mercurial repository"
5008 " here (.hg not found)"))
5008 " here (.hg not found)"))
5009 o = repo.root
5009 o = repo.root
5010
5010
5011 app = hgweb.hgweb(o, baseui=ui)
5011 app = hgweb.hgweb(o, baseui=ui)
5012
5012
5013 class service(object):
5013 class service(object):
5014 def init(self):
5014 def init(self):
5015 util.setsignalhandler()
5015 util.setsignalhandler()
5016 self.httpd = hgweb.server.create_server(ui, app)
5016 self.httpd = hgweb.server.create_server(ui, app)
5017
5017
5018 if opts['port'] and not ui.verbose:
5018 if opts['port'] and not ui.verbose:
5019 return
5019 return
5020
5020
5021 if self.httpd.prefix:
5021 if self.httpd.prefix:
5022 prefix = self.httpd.prefix.strip('/') + '/'
5022 prefix = self.httpd.prefix.strip('/') + '/'
5023 else:
5023 else:
5024 prefix = ''
5024 prefix = ''
5025
5025
5026 port = ':%d' % self.httpd.port
5026 port = ':%d' % self.httpd.port
5027 if port == ':80':
5027 if port == ':80':
5028 port = ''
5028 port = ''
5029
5029
5030 bindaddr = self.httpd.addr
5030 bindaddr = self.httpd.addr
5031 if bindaddr == '0.0.0.0':
5031 if bindaddr == '0.0.0.0':
5032 bindaddr = '*'
5032 bindaddr = '*'
5033 elif ':' in bindaddr: # IPv6
5033 elif ':' in bindaddr: # IPv6
5034 bindaddr = '[%s]' % bindaddr
5034 bindaddr = '[%s]' % bindaddr
5035
5035
5036 fqaddr = self.httpd.fqaddr
5036 fqaddr = self.httpd.fqaddr
5037 if ':' in fqaddr:
5037 if ':' in fqaddr:
5038 fqaddr = '[%s]' % fqaddr
5038 fqaddr = '[%s]' % fqaddr
5039 if opts['port']:
5039 if opts['port']:
5040 write = ui.status
5040 write = ui.status
5041 else:
5041 else:
5042 write = ui.write
5042 write = ui.write
5043 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5043 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5044 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5044 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5045
5045
5046 def run(self):
5046 def run(self):
5047 self.httpd.serve_forever()
5047 self.httpd.serve_forever()
5048
5048
5049 service = service()
5049 service = service()
5050
5050
5051 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5051 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5052
5052
5053 @command('showconfig|debugconfig',
5053 @command('showconfig|debugconfig',
5054 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5054 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5055 _('[-u] [NAME]...'))
5055 _('[-u] [NAME]...'))
5056 def showconfig(ui, repo, *values, **opts):
5056 def showconfig(ui, repo, *values, **opts):
5057 """show combined config settings from all hgrc files
5057 """show combined config settings from all hgrc files
5058
5058
5059 With no arguments, print names and values of all config items.
5059 With no arguments, print names and values of all config items.
5060
5060
5061 With one argument of the form section.name, print just the value
5061 With one argument of the form section.name, print just the value
5062 of that config item.
5062 of that config item.
5063
5063
5064 With multiple arguments, print names and values of all config
5064 With multiple arguments, print names and values of all config
5065 items with matching section names.
5065 items with matching section names.
5066
5066
5067 With --debug, the source (filename and line number) is printed
5067 With --debug, the source (filename and line number) is printed
5068 for each config item.
5068 for each config item.
5069
5069
5070 Returns 0 on success.
5070 Returns 0 on success.
5071 """
5071 """
5072
5072
5073 for f in scmutil.rcpath():
5073 for f in scmutil.rcpath():
5074 ui.debug('read config from: %s\n' % f)
5074 ui.debug('read config from: %s\n' % f)
5075 untrusted = bool(opts.get('untrusted'))
5075 untrusted = bool(opts.get('untrusted'))
5076 if values:
5076 if values:
5077 sections = [v for v in values if '.' not in v]
5077 sections = [v for v in values if '.' not in v]
5078 items = [v for v in values if '.' in v]
5078 items = [v for v in values if '.' in v]
5079 if len(items) > 1 or items and sections:
5079 if len(items) > 1 or items and sections:
5080 raise util.Abort(_('only one config item permitted'))
5080 raise util.Abort(_('only one config item permitted'))
5081 for section, name, value in ui.walkconfig(untrusted=untrusted):
5081 for section, name, value in ui.walkconfig(untrusted=untrusted):
5082 value = str(value).replace('\n', '\\n')
5082 value = str(value).replace('\n', '\\n')
5083 sectname = section + '.' + name
5083 sectname = section + '.' + name
5084 if values:
5084 if values:
5085 for v in values:
5085 for v in values:
5086 if v == section:
5086 if v == section:
5087 ui.debug('%s: ' %
5087 ui.debug('%s: ' %
5088 ui.configsource(section, name, untrusted))
5088 ui.configsource(section, name, untrusted))
5089 ui.write('%s=%s\n' % (sectname, value))
5089 ui.write('%s=%s\n' % (sectname, value))
5090 elif v == sectname:
5090 elif v == sectname:
5091 ui.debug('%s: ' %
5091 ui.debug('%s: ' %
5092 ui.configsource(section, name, untrusted))
5092 ui.configsource(section, name, untrusted))
5093 ui.write(value, '\n')
5093 ui.write(value, '\n')
5094 else:
5094 else:
5095 ui.debug('%s: ' %
5095 ui.debug('%s: ' %
5096 ui.configsource(section, name, untrusted))
5096 ui.configsource(section, name, untrusted))
5097 ui.write('%s=%s\n' % (sectname, value))
5097 ui.write('%s=%s\n' % (sectname, value))
5098
5098
5099 @command('^status|st',
5099 @command('^status|st',
5100 [('A', 'all', None, _('show status of all files')),
5100 [('A', 'all', None, _('show status of all files')),
5101 ('m', 'modified', None, _('show only modified files')),
5101 ('m', 'modified', None, _('show only modified files')),
5102 ('a', 'added', None, _('show only added files')),
5102 ('a', 'added', None, _('show only added files')),
5103 ('r', 'removed', None, _('show only removed files')),
5103 ('r', 'removed', None, _('show only removed files')),
5104 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5104 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5105 ('c', 'clean', None, _('show only files without changes')),
5105 ('c', 'clean', None, _('show only files without changes')),
5106 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5106 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5107 ('i', 'ignored', None, _('show only ignored files')),
5107 ('i', 'ignored', None, _('show only ignored files')),
5108 ('n', 'no-status', None, _('hide status prefix')),
5108 ('n', 'no-status', None, _('hide status prefix')),
5109 ('C', 'copies', None, _('show source of copied files')),
5109 ('C', 'copies', None, _('show source of copied files')),
5110 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5110 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5111 ('', 'rev', [], _('show difference from revision'), _('REV')),
5111 ('', 'rev', [], _('show difference from revision'), _('REV')),
5112 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5112 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5113 ] + walkopts + subrepoopts,
5113 ] + walkopts + subrepoopts,
5114 _('[OPTION]... [FILE]...'))
5114 _('[OPTION]... [FILE]...'))
5115 def status(ui, repo, *pats, **opts):
5115 def status(ui, repo, *pats, **opts):
5116 """show changed files in the working directory
5116 """show changed files in the working directory
5117
5117
5118 Show status of files in the repository. If names are given, only
5118 Show status of files in the repository. If names are given, only
5119 files that match are shown. Files that are clean or ignored or
5119 files that match are shown. Files that are clean or ignored or
5120 the source of a copy/move operation, are not listed unless
5120 the source of a copy/move operation, are not listed unless
5121 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5121 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5122 Unless options described with "show only ..." are given, the
5122 Unless options described with "show only ..." are given, the
5123 options -mardu are used.
5123 options -mardu are used.
5124
5124
5125 Option -q/--quiet hides untracked (unknown and ignored) files
5125 Option -q/--quiet hides untracked (unknown and ignored) files
5126 unless explicitly requested with -u/--unknown or -i/--ignored.
5126 unless explicitly requested with -u/--unknown or -i/--ignored.
5127
5127
5128 .. note::
5128 .. note::
5129 status may appear to disagree with diff if permissions have
5129 status may appear to disagree with diff if permissions have
5130 changed or a merge has occurred. The standard diff format does
5130 changed or a merge has occurred. The standard diff format does
5131 not report permission changes and diff only reports changes
5131 not report permission changes and diff only reports changes
5132 relative to one merge parent.
5132 relative to one merge parent.
5133
5133
5134 If one revision is given, it is used as the base revision.
5134 If one revision is given, it is used as the base revision.
5135 If two revisions are given, the differences between them are
5135 If two revisions are given, the differences between them are
5136 shown. The --change option can also be used as a shortcut to list
5136 shown. The --change option can also be used as a shortcut to list
5137 the changed files of a revision from its first parent.
5137 the changed files of a revision from its first parent.
5138
5138
5139 The codes used to show the status of files are::
5139 The codes used to show the status of files are::
5140
5140
5141 M = modified
5141 M = modified
5142 A = added
5142 A = added
5143 R = removed
5143 R = removed
5144 C = clean
5144 C = clean
5145 ! = missing (deleted by non-hg command, but still tracked)
5145 ! = missing (deleted by non-hg command, but still tracked)
5146 ? = not tracked
5146 ? = not tracked
5147 I = ignored
5147 I = ignored
5148 = origin of the previous file listed as A (added)
5148 = origin of the previous file listed as A (added)
5149
5149
5150 .. container:: verbose
5150 .. container:: verbose
5151
5151
5152 Examples:
5152 Examples:
5153
5153
5154 - show changes in the working directory relative to a
5154 - show changes in the working directory relative to a
5155 changeset::
5155 changeset::
5156
5156
5157 hg status --rev 9353
5157 hg status --rev 9353
5158
5158
5159 - show all changes including copies in an existing changeset::
5159 - show all changes including copies in an existing changeset::
5160
5160
5161 hg status --copies --change 9353
5161 hg status --copies --change 9353
5162
5162
5163 - get a NUL separated list of added files, suitable for xargs::
5163 - get a NUL separated list of added files, suitable for xargs::
5164
5164
5165 hg status -an0
5165 hg status -an0
5166
5166
5167 Returns 0 on success.
5167 Returns 0 on success.
5168 """
5168 """
5169
5169
5170 revs = opts.get('rev')
5170 revs = opts.get('rev')
5171 change = opts.get('change')
5171 change = opts.get('change')
5172
5172
5173 if revs and change:
5173 if revs and change:
5174 msg = _('cannot specify --rev and --change at the same time')
5174 msg = _('cannot specify --rev and --change at the same time')
5175 raise util.Abort(msg)
5175 raise util.Abort(msg)
5176 elif change:
5176 elif change:
5177 node2 = scmutil.revsingle(repo, change, None).node()
5177 node2 = scmutil.revsingle(repo, change, None).node()
5178 node1 = repo[node2].p1().node()
5178 node1 = repo[node2].p1().node()
5179 else:
5179 else:
5180 node1, node2 = scmutil.revpair(repo, revs)
5180 node1, node2 = scmutil.revpair(repo, revs)
5181
5181
5182 cwd = (pats and repo.getcwd()) or ''
5182 cwd = (pats and repo.getcwd()) or ''
5183 end = opts.get('print0') and '\0' or '\n'
5183 end = opts.get('print0') and '\0' or '\n'
5184 copy = {}
5184 copy = {}
5185 states = 'modified added removed deleted unknown ignored clean'.split()
5185 states = 'modified added removed deleted unknown ignored clean'.split()
5186 show = [k for k in states if opts.get(k)]
5186 show = [k for k in states if opts.get(k)]
5187 if opts.get('all'):
5187 if opts.get('all'):
5188 show += ui.quiet and (states[:4] + ['clean']) or states
5188 show += ui.quiet and (states[:4] + ['clean']) or states
5189 if not show:
5189 if not show:
5190 show = ui.quiet and states[:4] or states[:5]
5190 show = ui.quiet and states[:4] or states[:5]
5191
5191
5192 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5192 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5193 'ignored' in show, 'clean' in show, 'unknown' in show,
5193 'ignored' in show, 'clean' in show, 'unknown' in show,
5194 opts.get('subrepos'))
5194 opts.get('subrepos'))
5195 changestates = zip(states, 'MAR!?IC', stat)
5195 changestates = zip(states, 'MAR!?IC', stat)
5196
5196
5197 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5197 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5198 copy = copies.pathcopies(repo[node1], repo[node2])
5198 copy = copies.pathcopies(repo[node1], repo[node2])
5199
5199
5200 fm = ui.formatter('status', opts)
5200 fm = ui.formatter('status', opts)
5201 fmt = '%s' + end
5201 fmt = '%s' + end
5202 showchar = not opts.get('no_status')
5202 showchar = not opts.get('no_status')
5203
5203
5204 for state, char, files in changestates:
5204 for state, char, files in changestates:
5205 if state in show:
5205 if state in show:
5206 label = 'status.' + state
5206 label = 'status.' + state
5207 for f in files:
5207 for f in files:
5208 fm.startitem()
5208 fm.startitem()
5209 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5209 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5210 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5210 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5211 if f in copy:
5211 if f in copy:
5212 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5212 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5213 label='status.copied')
5213 label='status.copied')
5214 fm.end()
5214 fm.end()
5215
5215
5216 @command('^summary|sum',
5216 @command('^summary|sum',
5217 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5217 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5218 def summary(ui, repo, **opts):
5218 def summary(ui, repo, **opts):
5219 """summarize working directory state
5219 """summarize working directory state
5220
5220
5221 This generates a brief summary of the working directory state,
5221 This generates a brief summary of the working directory state,
5222 including parents, branch, commit status, and available updates.
5222 including parents, branch, commit status, and available updates.
5223
5223
5224 With the --remote option, this will check the default paths for
5224 With the --remote option, this will check the default paths for
5225 incoming and outgoing changes. This can be time-consuming.
5225 incoming and outgoing changes. This can be time-consuming.
5226
5226
5227 Returns 0 on success.
5227 Returns 0 on success.
5228 """
5228 """
5229
5229
5230 ctx = repo[None]
5230 ctx = repo[None]
5231 parents = ctx.parents()
5231 parents = ctx.parents()
5232 pnode = parents[0].node()
5232 pnode = parents[0].node()
5233 marks = []
5233 marks = []
5234
5234
5235 for p in parents:
5235 for p in parents:
5236 # label with log.changeset (instead of log.parent) since this
5236 # label with log.changeset (instead of log.parent) since this
5237 # shows a working directory parent *changeset*:
5237 # shows a working directory parent *changeset*:
5238 # i18n: column positioning for "hg summary"
5238 # i18n: column positioning for "hg summary"
5239 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5239 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5240 label='log.changeset changeset.%s' % p.phasestr())
5240 label='log.changeset changeset.%s' % p.phasestr())
5241 ui.write(' '.join(p.tags()), label='log.tag')
5241 ui.write(' '.join(p.tags()), label='log.tag')
5242 if p.bookmarks():
5242 if p.bookmarks():
5243 marks.extend(p.bookmarks())
5243 marks.extend(p.bookmarks())
5244 if p.rev() == -1:
5244 if p.rev() == -1:
5245 if not len(repo):
5245 if not len(repo):
5246 ui.write(_(' (empty repository)'))
5246 ui.write(_(' (empty repository)'))
5247 else:
5247 else:
5248 ui.write(_(' (no revision checked out)'))
5248 ui.write(_(' (no revision checked out)'))
5249 ui.write('\n')
5249 ui.write('\n')
5250 if p.description():
5250 if p.description():
5251 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5251 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5252 label='log.summary')
5252 label='log.summary')
5253
5253
5254 branch = ctx.branch()
5254 branch = ctx.branch()
5255 bheads = repo.branchheads(branch)
5255 bheads = repo.branchheads(branch)
5256 # i18n: column positioning for "hg summary"
5256 # i18n: column positioning for "hg summary"
5257 m = _('branch: %s\n') % branch
5257 m = _('branch: %s\n') % branch
5258 if branch != 'default':
5258 if branch != 'default':
5259 ui.write(m, label='log.branch')
5259 ui.write(m, label='log.branch')
5260 else:
5260 else:
5261 ui.status(m, label='log.branch')
5261 ui.status(m, label='log.branch')
5262
5262
5263 if marks:
5263 if marks:
5264 current = repo._bookmarkcurrent
5264 current = repo._bookmarkcurrent
5265 # i18n: column positioning for "hg summary"
5265 # i18n: column positioning for "hg summary"
5266 ui.write(_('bookmarks:'), label='log.bookmark')
5266 ui.write(_('bookmarks:'), label='log.bookmark')
5267 if current is not None:
5267 if current is not None:
5268 if current in marks:
5268 if current in marks:
5269 ui.write(' *' + current, label='bookmarks.current')
5269 ui.write(' *' + current, label='bookmarks.current')
5270 marks.remove(current)
5270 marks.remove(current)
5271 else:
5271 else:
5272 ui.write(' [%s]' % current, label='bookmarks.current')
5272 ui.write(' [%s]' % current, label='bookmarks.current')
5273 for m in marks:
5273 for m in marks:
5274 ui.write(' ' + m, label='log.bookmark')
5274 ui.write(' ' + m, label='log.bookmark')
5275 ui.write('\n', label='log.bookmark')
5275 ui.write('\n', label='log.bookmark')
5276
5276
5277 st = list(repo.status(unknown=True))[:6]
5277 st = list(repo.status(unknown=True))[:6]
5278
5278
5279 c = repo.dirstate.copies()
5279 c = repo.dirstate.copies()
5280 copied, renamed = [], []
5280 copied, renamed = [], []
5281 for d, s in c.iteritems():
5281 for d, s in c.iteritems():
5282 if s in st[2]:
5282 if s in st[2]:
5283 st[2].remove(s)
5283 st[2].remove(s)
5284 renamed.append(d)
5284 renamed.append(d)
5285 else:
5285 else:
5286 copied.append(d)
5286 copied.append(d)
5287 if d in st[1]:
5287 if d in st[1]:
5288 st[1].remove(d)
5288 st[1].remove(d)
5289 st.insert(3, renamed)
5289 st.insert(3, renamed)
5290 st.insert(4, copied)
5290 st.insert(4, copied)
5291
5291
5292 ms = mergemod.mergestate(repo)
5292 ms = mergemod.mergestate(repo)
5293 st.append([f for f in ms if ms[f] == 'u'])
5293 st.append([f for f in ms if ms[f] == 'u'])
5294
5294
5295 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5295 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5296 st.append(subs)
5296 st.append(subs)
5297
5297
5298 labels = [ui.label(_('%d modified'), 'status.modified'),
5298 labels = [ui.label(_('%d modified'), 'status.modified'),
5299 ui.label(_('%d added'), 'status.added'),
5299 ui.label(_('%d added'), 'status.added'),
5300 ui.label(_('%d removed'), 'status.removed'),
5300 ui.label(_('%d removed'), 'status.removed'),
5301 ui.label(_('%d renamed'), 'status.copied'),
5301 ui.label(_('%d renamed'), 'status.copied'),
5302 ui.label(_('%d copied'), 'status.copied'),
5302 ui.label(_('%d copied'), 'status.copied'),
5303 ui.label(_('%d deleted'), 'status.deleted'),
5303 ui.label(_('%d deleted'), 'status.deleted'),
5304 ui.label(_('%d unknown'), 'status.unknown'),
5304 ui.label(_('%d unknown'), 'status.unknown'),
5305 ui.label(_('%d ignored'), 'status.ignored'),
5305 ui.label(_('%d ignored'), 'status.ignored'),
5306 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5306 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5307 ui.label(_('%d subrepos'), 'status.modified')]
5307 ui.label(_('%d subrepos'), 'status.modified')]
5308 t = []
5308 t = []
5309 for s, l in zip(st, labels):
5309 for s, l in zip(st, labels):
5310 if s:
5310 if s:
5311 t.append(l % len(s))
5311 t.append(l % len(s))
5312
5312
5313 t = ', '.join(t)
5313 t = ', '.join(t)
5314 cleanworkdir = False
5314 cleanworkdir = False
5315
5315
5316 if len(parents) > 1:
5316 if len(parents) > 1:
5317 t += _(' (merge)')
5317 t += _(' (merge)')
5318 elif branch != parents[0].branch():
5318 elif branch != parents[0].branch():
5319 t += _(' (new branch)')
5319 t += _(' (new branch)')
5320 elif (parents[0].closesbranch() and
5320 elif (parents[0].closesbranch() and
5321 pnode in repo.branchheads(branch, closed=True)):
5321 pnode in repo.branchheads(branch, closed=True)):
5322 t += _(' (head closed)')
5322 t += _(' (head closed)')
5323 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5323 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5324 t += _(' (clean)')
5324 t += _(' (clean)')
5325 cleanworkdir = True
5325 cleanworkdir = True
5326 elif pnode not in bheads:
5326 elif pnode not in bheads:
5327 t += _(' (new branch head)')
5327 t += _(' (new branch head)')
5328
5328
5329 if cleanworkdir:
5329 if cleanworkdir:
5330 # i18n: column positioning for "hg summary"
5330 # i18n: column positioning for "hg summary"
5331 ui.status(_('commit: %s\n') % t.strip())
5331 ui.status(_('commit: %s\n') % t.strip())
5332 else:
5332 else:
5333 # i18n: column positioning for "hg summary"
5333 # i18n: column positioning for "hg summary"
5334 ui.write(_('commit: %s\n') % t.strip())
5334 ui.write(_('commit: %s\n') % t.strip())
5335
5335
5336 # all ancestors of branch heads - all ancestors of parent = new csets
5336 # all ancestors of branch heads - all ancestors of parent = new csets
5337 new = [0] * len(repo)
5337 new = [0] * len(repo)
5338 cl = repo.changelog
5338 cl = repo.changelog
5339 for a in [cl.rev(n) for n in bheads]:
5339 for a in [cl.rev(n) for n in bheads]:
5340 new[a] = 1
5340 new[a] = 1
5341 for a in cl.ancestors([cl.rev(n) for n in bheads]):
5341 for a in cl.ancestors([cl.rev(n) for n in bheads]):
5342 new[a] = 1
5342 new[a] = 1
5343 for a in [p.rev() for p in parents]:
5343 for a in [p.rev() for p in parents]:
5344 if a >= 0:
5344 if a >= 0:
5345 new[a] = 0
5345 new[a] = 0
5346 for a in cl.ancestors([p.rev() for p in parents]):
5346 for a in cl.ancestors([p.rev() for p in parents]):
5347 new[a] = 0
5347 new[a] = 0
5348 new = sum(new)
5348 new = sum(new)
5349
5349
5350 if new == 0:
5350 if new == 0:
5351 # i18n: column positioning for "hg summary"
5351 # i18n: column positioning for "hg summary"
5352 ui.status(_('update: (current)\n'))
5352 ui.status(_('update: (current)\n'))
5353 elif pnode not in bheads:
5353 elif pnode not in bheads:
5354 # i18n: column positioning for "hg summary"
5354 # i18n: column positioning for "hg summary"
5355 ui.write(_('update: %d new changesets (update)\n') % new)
5355 ui.write(_('update: %d new changesets (update)\n') % new)
5356 else:
5356 else:
5357 # i18n: column positioning for "hg summary"
5357 # i18n: column positioning for "hg summary"
5358 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5358 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5359 (new, len(bheads)))
5359 (new, len(bheads)))
5360
5360
5361 if opts.get('remote'):
5361 if opts.get('remote'):
5362 t = []
5362 t = []
5363 source, branches = hg.parseurl(ui.expandpath('default'))
5363 source, branches = hg.parseurl(ui.expandpath('default'))
5364 other = hg.peer(repo, {}, source)
5364 other = hg.peer(repo, {}, source)
5365 revs, checkout = hg.addbranchrevs(repo, other, branches,
5365 revs, checkout = hg.addbranchrevs(repo, other, branches,
5366 opts.get('rev'))
5366 opts.get('rev'))
5367 ui.debug('comparing with %s\n' % util.hidepassword(source))
5367 ui.debug('comparing with %s\n' % util.hidepassword(source))
5368 repo.ui.pushbuffer()
5368 repo.ui.pushbuffer()
5369 commoninc = discovery.findcommonincoming(repo, other)
5369 commoninc = discovery.findcommonincoming(repo, other)
5370 _common, incoming, _rheads = commoninc
5370 _common, incoming, _rheads = commoninc
5371 repo.ui.popbuffer()
5371 repo.ui.popbuffer()
5372 if incoming:
5372 if incoming:
5373 t.append(_('1 or more incoming'))
5373 t.append(_('1 or more incoming'))
5374
5374
5375 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5375 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5376 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5376 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5377 if source != dest:
5377 if source != dest:
5378 other = hg.peer(repo, {}, dest)
5378 other = hg.peer(repo, {}, dest)
5379 commoninc = None
5379 commoninc = None
5380 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5380 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5381 repo.ui.pushbuffer()
5381 repo.ui.pushbuffer()
5382 outgoing = discovery.findcommonoutgoing(repo, other,
5382 outgoing = discovery.findcommonoutgoing(repo, other,
5383 commoninc=commoninc)
5383 commoninc=commoninc)
5384 repo.ui.popbuffer()
5384 repo.ui.popbuffer()
5385 o = outgoing.missing
5385 o = outgoing.missing
5386 if o:
5386 if o:
5387 t.append(_('%d outgoing') % len(o))
5387 t.append(_('%d outgoing') % len(o))
5388 if 'bookmarks' in other.listkeys('namespaces'):
5388 if 'bookmarks' in other.listkeys('namespaces'):
5389 lmarks = repo.listkeys('bookmarks')
5389 lmarks = repo.listkeys('bookmarks')
5390 rmarks = other.listkeys('bookmarks')
5390 rmarks = other.listkeys('bookmarks')
5391 diff = set(rmarks) - set(lmarks)
5391 diff = set(rmarks) - set(lmarks)
5392 if len(diff) > 0:
5392 if len(diff) > 0:
5393 t.append(_('%d incoming bookmarks') % len(diff))
5393 t.append(_('%d incoming bookmarks') % len(diff))
5394 diff = set(lmarks) - set(rmarks)
5394 diff = set(lmarks) - set(rmarks)
5395 if len(diff) > 0:
5395 if len(diff) > 0:
5396 t.append(_('%d outgoing bookmarks') % len(diff))
5396 t.append(_('%d outgoing bookmarks') % len(diff))
5397
5397
5398 if t:
5398 if t:
5399 # i18n: column positioning for "hg summary"
5399 # i18n: column positioning for "hg summary"
5400 ui.write(_('remote: %s\n') % (', '.join(t)))
5400 ui.write(_('remote: %s\n') % (', '.join(t)))
5401 else:
5401 else:
5402 # i18n: column positioning for "hg summary"
5402 # i18n: column positioning for "hg summary"
5403 ui.status(_('remote: (synced)\n'))
5403 ui.status(_('remote: (synced)\n'))
5404
5404
5405 @command('tag',
5405 @command('tag',
5406 [('f', 'force', None, _('force tag')),
5406 [('f', 'force', None, _('force tag')),
5407 ('l', 'local', None, _('make the tag local')),
5407 ('l', 'local', None, _('make the tag local')),
5408 ('r', 'rev', '', _('revision to tag'), _('REV')),
5408 ('r', 'rev', '', _('revision to tag'), _('REV')),
5409 ('', 'remove', None, _('remove a tag')),
5409 ('', 'remove', None, _('remove a tag')),
5410 # -l/--local is already there, commitopts cannot be used
5410 # -l/--local is already there, commitopts cannot be used
5411 ('e', 'edit', None, _('edit commit message')),
5411 ('e', 'edit', None, _('edit commit message')),
5412 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5412 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5413 ] + commitopts2,
5413 ] + commitopts2,
5414 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5414 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5415 def tag(ui, repo, name1, *names, **opts):
5415 def tag(ui, repo, name1, *names, **opts):
5416 """add one or more tags for the current or given revision
5416 """add one or more tags for the current or given revision
5417
5417
5418 Name a particular revision using <name>.
5418 Name a particular revision using <name>.
5419
5419
5420 Tags are used to name particular revisions of the repository and are
5420 Tags are used to name particular revisions of the repository and are
5421 very useful to compare different revisions, to go back to significant
5421 very useful to compare different revisions, to go back to significant
5422 earlier versions or to mark branch points as releases, etc. Changing
5422 earlier versions or to mark branch points as releases, etc. Changing
5423 an existing tag is normally disallowed; use -f/--force to override.
5423 an existing tag is normally disallowed; use -f/--force to override.
5424
5424
5425 If no revision is given, the parent of the working directory is
5425 If no revision is given, the parent of the working directory is
5426 used, or tip if no revision is checked out.
5426 used, or tip if no revision is checked out.
5427
5427
5428 To facilitate version control, distribution, and merging of tags,
5428 To facilitate version control, distribution, and merging of tags,
5429 they are stored as a file named ".hgtags" which is managed similarly
5429 they are stored as a file named ".hgtags" which is managed similarly
5430 to other project files and can be hand-edited if necessary. This
5430 to other project files and can be hand-edited if necessary. This
5431 also means that tagging creates a new commit. The file
5431 also means that tagging creates a new commit. The file
5432 ".hg/localtags" is used for local tags (not shared among
5432 ".hg/localtags" is used for local tags (not shared among
5433 repositories).
5433 repositories).
5434
5434
5435 Tag commits are usually made at the head of a branch. If the parent
5435 Tag commits are usually made at the head of a branch. If the parent
5436 of the working directory is not a branch head, :hg:`tag` aborts; use
5436 of the working directory is not a branch head, :hg:`tag` aborts; use
5437 -f/--force to force the tag commit to be based on a non-head
5437 -f/--force to force the tag commit to be based on a non-head
5438 changeset.
5438 changeset.
5439
5439
5440 See :hg:`help dates` for a list of formats valid for -d/--date.
5440 See :hg:`help dates` for a list of formats valid for -d/--date.
5441
5441
5442 Since tag names have priority over branch names during revision
5442 Since tag names have priority over branch names during revision
5443 lookup, using an existing branch name as a tag name is discouraged.
5443 lookup, using an existing branch name as a tag name is discouraged.
5444
5444
5445 Returns 0 on success.
5445 Returns 0 on success.
5446 """
5446 """
5447 wlock = lock = None
5447 wlock = lock = None
5448 try:
5448 try:
5449 wlock = repo.wlock()
5449 wlock = repo.wlock()
5450 lock = repo.lock()
5450 lock = repo.lock()
5451 rev_ = "."
5451 rev_ = "."
5452 names = [t.strip() for t in (name1,) + names]
5452 names = [t.strip() for t in (name1,) + names]
5453 if len(names) != len(set(names)):
5453 if len(names) != len(set(names)):
5454 raise util.Abort(_('tag names must be unique'))
5454 raise util.Abort(_('tag names must be unique'))
5455 for n in names:
5455 for n in names:
5456 scmutil.checknewlabel(repo, n, 'tag')
5456 scmutil.checknewlabel(repo, n, 'tag')
5457 if not n:
5457 if not n:
5458 raise util.Abort(_('tag names cannot consist entirely of '
5458 raise util.Abort(_('tag names cannot consist entirely of '
5459 'whitespace'))
5459 'whitespace'))
5460 if opts.get('rev') and opts.get('remove'):
5460 if opts.get('rev') and opts.get('remove'):
5461 raise util.Abort(_("--rev and --remove are incompatible"))
5461 raise util.Abort(_("--rev and --remove are incompatible"))
5462 if opts.get('rev'):
5462 if opts.get('rev'):
5463 rev_ = opts['rev']
5463 rev_ = opts['rev']
5464 message = opts.get('message')
5464 message = opts.get('message')
5465 if opts.get('remove'):
5465 if opts.get('remove'):
5466 expectedtype = opts.get('local') and 'local' or 'global'
5466 expectedtype = opts.get('local') and 'local' or 'global'
5467 for n in names:
5467 for n in names:
5468 if not repo.tagtype(n):
5468 if not repo.tagtype(n):
5469 raise util.Abort(_("tag '%s' does not exist") % n)
5469 raise util.Abort(_("tag '%s' does not exist") % n)
5470 if repo.tagtype(n) != expectedtype:
5470 if repo.tagtype(n) != expectedtype:
5471 if expectedtype == 'global':
5471 if expectedtype == 'global':
5472 raise util.Abort(_("tag '%s' is not a global tag") % n)
5472 raise util.Abort(_("tag '%s' is not a global tag") % n)
5473 else:
5473 else:
5474 raise util.Abort(_("tag '%s' is not a local tag") % n)
5474 raise util.Abort(_("tag '%s' is not a local tag") % n)
5475 rev_ = nullid
5475 rev_ = nullid
5476 if not message:
5476 if not message:
5477 # we don't translate commit messages
5477 # we don't translate commit messages
5478 message = 'Removed tag %s' % ', '.join(names)
5478 message = 'Removed tag %s' % ', '.join(names)
5479 elif not opts.get('force'):
5479 elif not opts.get('force'):
5480 for n in names:
5480 for n in names:
5481 if n in repo.tags():
5481 if n in repo.tags():
5482 raise util.Abort(_("tag '%s' already exists "
5482 raise util.Abort(_("tag '%s' already exists "
5483 "(use -f to force)") % n)
5483 "(use -f to force)") % n)
5484 if not opts.get('local'):
5484 if not opts.get('local'):
5485 p1, p2 = repo.dirstate.parents()
5485 p1, p2 = repo.dirstate.parents()
5486 if p2 != nullid:
5486 if p2 != nullid:
5487 raise util.Abort(_('uncommitted merge'))
5487 raise util.Abort(_('uncommitted merge'))
5488 bheads = repo.branchheads()
5488 bheads = repo.branchheads()
5489 if not opts.get('force') and bheads and p1 not in bheads:
5489 if not opts.get('force') and bheads and p1 not in bheads:
5490 raise util.Abort(_('not at a branch head (use -f to force)'))
5490 raise util.Abort(_('not at a branch head (use -f to force)'))
5491 r = scmutil.revsingle(repo, rev_).node()
5491 r = scmutil.revsingle(repo, rev_).node()
5492
5492
5493 if not message:
5493 if not message:
5494 # we don't translate commit messages
5494 # we don't translate commit messages
5495 message = ('Added tag %s for changeset %s' %
5495 message = ('Added tag %s for changeset %s' %
5496 (', '.join(names), short(r)))
5496 (', '.join(names), short(r)))
5497
5497
5498 date = opts.get('date')
5498 date = opts.get('date')
5499 if date:
5499 if date:
5500 date = util.parsedate(date)
5500 date = util.parsedate(date)
5501
5501
5502 if opts.get('edit'):
5502 if opts.get('edit'):
5503 message = ui.edit(message, ui.username())
5503 message = ui.edit(message, ui.username())
5504
5504
5505 # don't allow tagging the null rev
5505 # don't allow tagging the null rev
5506 if (not opts.get('remove') and
5506 if (not opts.get('remove') and
5507 scmutil.revsingle(repo, rev_).rev() == nullrev):
5507 scmutil.revsingle(repo, rev_).rev() == nullrev):
5508 raise util.Abort(_("null revision specified"))
5508 raise util.Abort(_("null revision specified"))
5509
5509
5510 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5510 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5511 finally:
5511 finally:
5512 release(lock, wlock)
5512 release(lock, wlock)
5513
5513
5514 @command('tags', [], '')
5514 @command('tags', [], '')
5515 def tags(ui, repo, **opts):
5515 def tags(ui, repo, **opts):
5516 """list repository tags
5516 """list repository tags
5517
5517
5518 This lists both regular and local tags. When the -v/--verbose
5518 This lists both regular and local tags. When the -v/--verbose
5519 switch is used, a third column "local" is printed for local tags.
5519 switch is used, a third column "local" is printed for local tags.
5520
5520
5521 Returns 0 on success.
5521 Returns 0 on success.
5522 """
5522 """
5523
5523
5524 fm = ui.formatter('tags', opts)
5524 fm = ui.formatter('tags', opts)
5525 hexfunc = ui.debugflag and hex or short
5525 hexfunc = ui.debugflag and hex or short
5526 tagtype = ""
5526 tagtype = ""
5527
5527
5528 for t, n in reversed(repo.tagslist()):
5528 for t, n in reversed(repo.tagslist()):
5529 hn = hexfunc(n)
5529 hn = hexfunc(n)
5530 label = 'tags.normal'
5530 label = 'tags.normal'
5531 tagtype = ''
5531 tagtype = ''
5532 if repo.tagtype(t) == 'local':
5532 if repo.tagtype(t) == 'local':
5533 label = 'tags.local'
5533 label = 'tags.local'
5534 tagtype = 'local'
5534 tagtype = 'local'
5535
5535
5536 fm.startitem()
5536 fm.startitem()
5537 fm.write('tag', '%s', t, label=label)
5537 fm.write('tag', '%s', t, label=label)
5538 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5538 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
5539 fm.condwrite(not ui.quiet, 'rev id', fmt,
5539 fm.condwrite(not ui.quiet, 'rev id', fmt,
5540 repo.changelog.rev(n), hn, label=label)
5540 repo.changelog.rev(n), hn, label=label)
5541 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5541 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
5542 tagtype, label=label)
5542 tagtype, label=label)
5543 fm.plain('\n')
5543 fm.plain('\n')
5544 fm.end()
5544 fm.end()
5545
5545
5546 @command('tip',
5546 @command('tip',
5547 [('p', 'patch', None, _('show patch')),
5547 [('p', 'patch', None, _('show patch')),
5548 ('g', 'git', None, _('use git extended diff format')),
5548 ('g', 'git', None, _('use git extended diff format')),
5549 ] + templateopts,
5549 ] + templateopts,
5550 _('[-p] [-g]'))
5550 _('[-p] [-g]'))
5551 def tip(ui, repo, **opts):
5551 def tip(ui, repo, **opts):
5552 """show the tip revision
5552 """show the tip revision
5553
5553
5554 The tip revision (usually just called the tip) is the changeset
5554 The tip revision (usually just called the tip) is the changeset
5555 most recently added to the repository (and therefore the most
5555 most recently added to the repository (and therefore the most
5556 recently changed head).
5556 recently changed head).
5557
5557
5558 If you have just made a commit, that commit will be the tip. If
5558 If you have just made a commit, that commit will be the tip. If
5559 you have just pulled changes from another repository, the tip of
5559 you have just pulled changes from another repository, the tip of
5560 that repository becomes the current tip. The "tip" tag is special
5560 that repository becomes the current tip. The "tip" tag is special
5561 and cannot be renamed or assigned to a different changeset.
5561 and cannot be renamed or assigned to a different changeset.
5562
5562
5563 Returns 0 on success.
5563 Returns 0 on success.
5564 """
5564 """
5565 displayer = cmdutil.show_changeset(ui, repo, opts)
5565 displayer = cmdutil.show_changeset(ui, repo, opts)
5566 displayer.show(repo['tip'])
5566 displayer.show(repo['tip'])
5567 displayer.close()
5567 displayer.close()
5568
5568
5569 @command('unbundle',
5569 @command('unbundle',
5570 [('u', 'update', None,
5570 [('u', 'update', None,
5571 _('update to new branch head if changesets were unbundled'))],
5571 _('update to new branch head if changesets were unbundled'))],
5572 _('[-u] FILE...'))
5572 _('[-u] FILE...'))
5573 def unbundle(ui, repo, fname1, *fnames, **opts):
5573 def unbundle(ui, repo, fname1, *fnames, **opts):
5574 """apply one or more changegroup files
5574 """apply one or more changegroup files
5575
5575
5576 Apply one or more compressed changegroup files generated by the
5576 Apply one or more compressed changegroup files generated by the
5577 bundle command.
5577 bundle command.
5578
5578
5579 Returns 0 on success, 1 if an update has unresolved files.
5579 Returns 0 on success, 1 if an update has unresolved files.
5580 """
5580 """
5581 fnames = (fname1,) + fnames
5581 fnames = (fname1,) + fnames
5582
5582
5583 lock = repo.lock()
5583 lock = repo.lock()
5584 wc = repo['.']
5584 wc = repo['.']
5585 try:
5585 try:
5586 for fname in fnames:
5586 for fname in fnames:
5587 f = hg.openpath(ui, fname)
5587 f = hg.openpath(ui, fname)
5588 gen = changegroup.readbundle(f, fname)
5588 gen = changegroup.readbundle(f, fname)
5589 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5589 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5590 finally:
5590 finally:
5591 lock.release()
5591 lock.release()
5592 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5592 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5593 return postincoming(ui, repo, modheads, opts.get('update'), None)
5593 return postincoming(ui, repo, modheads, opts.get('update'), None)
5594
5594
5595 @command('^update|up|checkout|co',
5595 @command('^update|up|checkout|co',
5596 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5596 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5597 ('c', 'check', None,
5597 ('c', 'check', None,
5598 _('update across branches if no uncommitted changes')),
5598 _('update across branches if no uncommitted changes')),
5599 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5599 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5600 ('r', 'rev', '', _('revision'), _('REV'))],
5600 ('r', 'rev', '', _('revision'), _('REV'))],
5601 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5601 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5602 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5602 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5603 """update working directory (or switch revisions)
5603 """update working directory (or switch revisions)
5604
5604
5605 Update the repository's working directory to the specified
5605 Update the repository's working directory to the specified
5606 changeset. If no changeset is specified, update to the tip of the
5606 changeset. If no changeset is specified, update to the tip of the
5607 current named branch and move the current bookmark (see :hg:`help
5607 current named branch and move the current bookmark (see :hg:`help
5608 bookmarks`).
5608 bookmarks`).
5609
5609
5610 Update sets the working directory's parent revision to the specified
5610 Update sets the working directory's parent revision to the specified
5611 changeset (see :hg:`help parents`).
5611 changeset (see :hg:`help parents`).
5612
5612
5613 If the changeset is not a descendant or ancestor of the working
5613 If the changeset is not a descendant or ancestor of the working
5614 directory's parent, the update is aborted. With the -c/--check
5614 directory's parent, the update is aborted. With the -c/--check
5615 option, the working directory is checked for uncommitted changes; if
5615 option, the working directory is checked for uncommitted changes; if
5616 none are found, the working directory is updated to the specified
5616 none are found, the working directory is updated to the specified
5617 changeset.
5617 changeset.
5618
5618
5619 .. container:: verbose
5619 .. container:: verbose
5620
5620
5621 The following rules apply when the working directory contains
5621 The following rules apply when the working directory contains
5622 uncommitted changes:
5622 uncommitted changes:
5623
5623
5624 1. If neither -c/--check nor -C/--clean is specified, and if
5624 1. If neither -c/--check nor -C/--clean is specified, and if
5625 the requested changeset is an ancestor or descendant of
5625 the requested changeset is an ancestor or descendant of
5626 the working directory's parent, the uncommitted changes
5626 the working directory's parent, the uncommitted changes
5627 are merged into the requested changeset and the merged
5627 are merged into the requested changeset and the merged
5628 result is left uncommitted. If the requested changeset is
5628 result is left uncommitted. If the requested changeset is
5629 not an ancestor or descendant (that is, it is on another
5629 not an ancestor or descendant (that is, it is on another
5630 branch), the update is aborted and the uncommitted changes
5630 branch), the update is aborted and the uncommitted changes
5631 are preserved.
5631 are preserved.
5632
5632
5633 2. With the -c/--check option, the update is aborted and the
5633 2. With the -c/--check option, the update is aborted and the
5634 uncommitted changes are preserved.
5634 uncommitted changes are preserved.
5635
5635
5636 3. With the -C/--clean option, uncommitted changes are discarded and
5636 3. With the -C/--clean option, uncommitted changes are discarded and
5637 the working directory is updated to the requested changeset.
5637 the working directory is updated to the requested changeset.
5638
5638
5639 To cancel an uncommitted merge (and lose your changes), use
5639 To cancel an uncommitted merge (and lose your changes), use
5640 :hg:`update --clean .`.
5640 :hg:`update --clean .`.
5641
5641
5642 Use null as the changeset to remove the working directory (like
5642 Use null as the changeset to remove the working directory (like
5643 :hg:`clone -U`).
5643 :hg:`clone -U`).
5644
5644
5645 If you want to revert just one file to an older revision, use
5645 If you want to revert just one file to an older revision, use
5646 :hg:`revert [-r REV] NAME`.
5646 :hg:`revert [-r REV] NAME`.
5647
5647
5648 See :hg:`help dates` for a list of formats valid for -d/--date.
5648 See :hg:`help dates` for a list of formats valid for -d/--date.
5649
5649
5650 Returns 0 on success, 1 if there are unresolved files.
5650 Returns 0 on success, 1 if there are unresolved files.
5651 """
5651 """
5652 if rev and node:
5652 if rev and node:
5653 raise util.Abort(_("please specify just one revision"))
5653 raise util.Abort(_("please specify just one revision"))
5654
5654
5655 if rev is None or rev == '':
5655 if rev is None or rev == '':
5656 rev = node
5656 rev = node
5657
5657
5658 # with no argument, we also move the current bookmark, if any
5658 # with no argument, we also move the current bookmark, if any
5659 movemarkfrom = None
5659 movemarkfrom = None
5660 if rev is None:
5660 if rev is None:
5661 curmark = repo._bookmarkcurrent
5661 curmark = repo._bookmarkcurrent
5662 if bookmarks.iscurrent(repo):
5662 if bookmarks.iscurrent(repo):
5663 movemarkfrom = repo['.'].node()
5663 movemarkfrom = repo['.'].node()
5664 elif curmark:
5664 elif curmark:
5665 ui.status(_("updating to active bookmark %s\n") % curmark)
5665 ui.status(_("updating to active bookmark %s\n") % curmark)
5666 rev = curmark
5666 rev = curmark
5667
5667
5668 # if we defined a bookmark, we have to remember the original bookmark name
5668 # if we defined a bookmark, we have to remember the original bookmark name
5669 brev = rev
5669 brev = rev
5670 rev = scmutil.revsingle(repo, rev, rev).rev()
5670 rev = scmutil.revsingle(repo, rev, rev).rev()
5671
5671
5672 if check and clean:
5672 if check and clean:
5673 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5673 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5674
5674
5675 if date:
5675 if date:
5676 if rev is not None:
5676 if rev is not None:
5677 raise util.Abort(_("you can't specify a revision and a date"))
5677 raise util.Abort(_("you can't specify a revision and a date"))
5678 rev = cmdutil.finddate(ui, repo, date)
5678 rev = cmdutil.finddate(ui, repo, date)
5679
5679
5680 if check:
5680 if check:
5681 c = repo[None]
5681 c = repo[None]
5682 if c.dirty(merge=False, branch=False, missing=True):
5682 if c.dirty(merge=False, branch=False, missing=True):
5683 raise util.Abort(_("uncommitted local changes"))
5683 raise util.Abort(_("uncommitted local changes"))
5684 if rev is None:
5684 if rev is None:
5685 rev = repo[repo[None].branch()].rev()
5685 rev = repo[repo[None].branch()].rev()
5686 mergemod._checkunknown(repo, repo[None], repo[rev])
5686 mergemod._checkunknown(repo, repo[None], repo[rev])
5687
5687
5688 if clean:
5688 if clean:
5689 ret = hg.clean(repo, rev)
5689 ret = hg.clean(repo, rev)
5690 else:
5690 else:
5691 ret = hg.update(repo, rev)
5691 ret = hg.update(repo, rev)
5692
5692
5693 if not ret and movemarkfrom:
5693 if not ret and movemarkfrom:
5694 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5694 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5695 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5695 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5696 elif brev in repo._bookmarks:
5696 elif brev in repo._bookmarks:
5697 bookmarks.setcurrent(repo, brev)
5697 bookmarks.setcurrent(repo, brev)
5698 elif brev:
5698 elif brev:
5699 bookmarks.unsetcurrent(repo)
5699 bookmarks.unsetcurrent(repo)
5700
5700
5701 return ret
5701 return ret
5702
5702
5703 @command('verify', [])
5703 @command('verify', [])
5704 def verify(ui, repo):
5704 def verify(ui, repo):
5705 """verify the integrity of the repository
5705 """verify the integrity of the repository
5706
5706
5707 Verify the integrity of the current repository.
5707 Verify the integrity of the current repository.
5708
5708
5709 This will perform an extensive check of the repository's
5709 This will perform an extensive check of the repository's
5710 integrity, validating the hashes and checksums of each entry in
5710 integrity, validating the hashes and checksums of each entry in
5711 the changelog, manifest, and tracked files, as well as the
5711 the changelog, manifest, and tracked files, as well as the
5712 integrity of their crosslinks and indices.
5712 integrity of their crosslinks and indices.
5713
5713
5714 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
5714 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
5715 for more information about recovery from corruption of the
5715 for more information about recovery from corruption of the
5716 repository.
5716 repository.
5717
5717
5718 Returns 0 on success, 1 if errors are encountered.
5718 Returns 0 on success, 1 if errors are encountered.
5719 """
5719 """
5720 return hg.verify(repo)
5720 return hg.verify(repo)
5721
5721
5722 @command('version', [])
5722 @command('version', [])
5723 def version_(ui):
5723 def version_(ui):
5724 """output version and copyright information"""
5724 """output version and copyright information"""
5725 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5725 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5726 % util.version())
5726 % util.version())
5727 ui.status(_(
5727 ui.status(_(
5728 "(see http://mercurial.selenic.com for more information)\n"
5728 "(see http://mercurial.selenic.com for more information)\n"
5729 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
5729 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
5730 "This is free software; see the source for copying conditions. "
5730 "This is free software; see the source for copying conditions. "
5731 "There is NO\nwarranty; "
5731 "There is NO\nwarranty; "
5732 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5732 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5733 ))
5733 ))
5734
5734
5735 norepo = ("clone init version help debugcommands debugcomplete"
5735 norepo = ("clone init version help debugcommands debugcomplete"
5736 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5736 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5737 " debugknown debuggetbundle debugbundle")
5737 " debugknown debuggetbundle debugbundle")
5738 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5738 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5739 " debugdata debugindex debugindexdot debugrevlog")
5739 " debugdata debugindex debugindexdot debugrevlog")
5740 inferrepo = ("add addremove annotate cat commit diff grep forget log parents"
5740 inferrepo = ("add addremove annotate cat commit diff grep forget log parents"
5741 " remove resolve status debugwalk")
5741 " remove resolve status debugwalk")
@@ -1,716 +1,724 b''
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 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 nullid, nullrev, hex, bin
8 from node import nullid, nullrev, hex, bin
9 from i18n import _
9 from i18n import _
10 import error, util, filemerge, copies, subrepo, worker
10 import error, util, filemerge, copies, subrepo, worker
11 import errno, os, shutil
11 import errno, os, shutil
12
12
13 class mergestate(object):
13 class mergestate(object):
14 '''track 3-way merge state of individual files'''
14 '''track 3-way merge state of individual files'''
15 def __init__(self, repo):
15 def __init__(self, repo):
16 self._repo = repo
16 self._repo = repo
17 self._dirty = False
17 self._dirty = False
18 self._read()
18 self._read()
19 def reset(self, node=None):
19 def reset(self, node=None):
20 self._state = {}
20 self._state = {}
21 if node:
21 if node:
22 self._local = node
22 self._local = node
23 shutil.rmtree(self._repo.join("merge"), True)
23 shutil.rmtree(self._repo.join("merge"), True)
24 self._dirty = False
24 self._dirty = False
25 def _read(self):
25 def _read(self):
26 self._state = {}
26 self._state = {}
27 try:
27 try:
28 f = self._repo.opener("merge/state")
28 f = self._repo.opener("merge/state")
29 for i, l in enumerate(f):
29 for i, l in enumerate(f):
30 if i == 0:
30 if i == 0:
31 self._local = bin(l[:-1])
31 self._local = bin(l[:-1])
32 else:
32 else:
33 bits = l[:-1].split("\0")
33 bits = l[:-1].split("\0")
34 self._state[bits[0]] = bits[1:]
34 self._state[bits[0]] = bits[1:]
35 f.close()
35 f.close()
36 except IOError, err:
36 except IOError, err:
37 if err.errno != errno.ENOENT:
37 if err.errno != errno.ENOENT:
38 raise
38 raise
39 self._dirty = False
39 self._dirty = False
40 def commit(self):
40 def commit(self):
41 if self._dirty:
41 if self._dirty:
42 f = self._repo.opener("merge/state", "w")
42 f = self._repo.opener("merge/state", "w")
43 f.write(hex(self._local) + "\n")
43 f.write(hex(self._local) + "\n")
44 for d, v in self._state.iteritems():
44 for d, v in self._state.iteritems():
45 f.write("\0".join([d] + v) + "\n")
45 f.write("\0".join([d] + v) + "\n")
46 f.close()
46 f.close()
47 self._dirty = False
47 self._dirty = False
48 def add(self, fcl, fco, fca, fd):
48 def add(self, fcl, fco, fca, fd):
49 hash = util.sha1(fcl.path()).hexdigest()
49 hash = util.sha1(fcl.path()).hexdigest()
50 self._repo.opener.write("merge/" + hash, fcl.data())
50 self._repo.opener.write("merge/" + hash, fcl.data())
51 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
51 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
52 hex(fca.filenode()), fco.path(), fcl.flags()]
52 hex(fca.filenode()), fco.path(), fcl.flags()]
53 self._dirty = True
53 self._dirty = True
54 def __contains__(self, dfile):
54 def __contains__(self, dfile):
55 return dfile in self._state
55 return dfile in self._state
56 def __getitem__(self, dfile):
56 def __getitem__(self, dfile):
57 return self._state[dfile][0]
57 return self._state[dfile][0]
58 def __iter__(self):
58 def __iter__(self):
59 l = self._state.keys()
59 l = self._state.keys()
60 l.sort()
60 l.sort()
61 for f in l:
61 for f in l:
62 yield f
62 yield f
63 def mark(self, dfile, state):
63 def mark(self, dfile, state):
64 self._state[dfile][0] = state
64 self._state[dfile][0] = state
65 self._dirty = True
65 self._dirty = True
66 def resolve(self, dfile, wctx, octx):
66 def resolve(self, dfile, wctx, octx):
67 if self[dfile] == 'r':
67 if self[dfile] == 'r':
68 return 0
68 return 0
69 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
69 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
70 fcd = wctx[dfile]
70 fcd = wctx[dfile]
71 fco = octx[ofile]
71 fco = octx[ofile]
72 fca = self._repo.filectx(afile, fileid=anode)
72 fca = self._repo.filectx(afile, fileid=anode)
73 # "premerge" x flags
73 # "premerge" x flags
74 flo = fco.flags()
74 flo = fco.flags()
75 fla = fca.flags()
75 fla = fca.flags()
76 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
76 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
77 if fca.node() == nullid:
77 if fca.node() == nullid:
78 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
78 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
79 afile)
79 afile)
80 elif flags == fla:
80 elif flags == fla:
81 flags = flo
81 flags = flo
82 # restore local
82 # restore local
83 f = self._repo.opener("merge/" + hash)
83 f = self._repo.opener("merge/" + hash)
84 self._repo.wwrite(dfile, f.read(), flags)
84 self._repo.wwrite(dfile, f.read(), flags)
85 f.close()
85 f.close()
86 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
86 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
87 if r is None:
87 if r is None:
88 # no real conflict
88 # no real conflict
89 del self._state[dfile]
89 del self._state[dfile]
90 elif not r:
90 elif not r:
91 self.mark(dfile, 'r')
91 self.mark(dfile, 'r')
92 return r
92 return r
93
93
94 def _checkunknownfile(repo, wctx, mctx, f):
94 def _checkunknownfile(repo, wctx, mctx, f):
95 return (not repo.dirstate._ignore(f)
95 return (not repo.dirstate._ignore(f)
96 and os.path.isfile(repo.wjoin(f))
96 and os.path.isfile(repo.wjoin(f))
97 and repo.dirstate.normalize(f) not in repo.dirstate
97 and repo.dirstate.normalize(f) not in repo.dirstate
98 and mctx[f].cmp(wctx[f]))
98 and mctx[f].cmp(wctx[f]))
99
99
100 def _checkunknown(repo, wctx, mctx):
100 def _checkunknown(repo, wctx, mctx):
101 "check for collisions between unknown files and files in mctx"
101 "check for collisions between unknown files and files in mctx"
102
102
103 error = False
103 error = False
104 for f in mctx:
104 for f in mctx:
105 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
105 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
106 error = True
106 error = True
107 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
107 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
108 if error:
108 if error:
109 raise util.Abort(_("untracked files in working directory differ "
109 raise util.Abort(_("untracked files in working directory differ "
110 "from files in requested revision"))
110 "from files in requested revision"))
111
111
112 def _remains(f, m, ma, workingctx=False):
112 def _remains(f, m, ma, workingctx=False):
113 """check whether specified file remains after merge.
113 """check whether specified file remains after merge.
114
114
115 It is assumed that specified file is not contained in the manifest
115 It is assumed that specified file is not contained in the manifest
116 of the other context.
116 of the other context.
117 """
117 """
118 if f in ma:
118 if f in ma:
119 n = m[f]
119 n = m[f]
120 if n != ma[f]:
120 if n != ma[f]:
121 return True # because it is changed locally
121 return True # because it is changed locally
122 # even though it doesn't remain, if "remote deleted" is
122 # even though it doesn't remain, if "remote deleted" is
123 # chosen in manifestmerge()
123 # chosen in manifestmerge()
124 elif workingctx and n[20:] == "a":
124 elif workingctx and n[20:] == "a":
125 return True # because it is added locally (linear merge specific)
125 return True # because it is added locally (linear merge specific)
126 else:
126 else:
127 return False # because it is removed remotely
127 return False # because it is removed remotely
128 else:
128 else:
129 return True # because it is added locally
129 return True # because it is added locally
130
130
131 def _checkcollision(mctx, extractxs):
131 def _checkcollision(mctx, extractxs):
132 "check for case folding collisions in the destination context"
132 "check for case folding collisions in the destination context"
133 folded = {}
133 folded = {}
134 for fn in mctx:
134 for fn in mctx:
135 fold = util.normcase(fn)
135 fold = util.normcase(fn)
136 if fold in folded:
136 if fold in folded:
137 raise util.Abort(_("case-folding collision between %s and %s")
137 raise util.Abort(_("case-folding collision between %s and %s")
138 % (fn, folded[fold]))
138 % (fn, folded[fold]))
139 folded[fold] = fn
139 folded[fold] = fn
140
140
141 if extractxs:
141 if extractxs:
142 wctx, actx = extractxs
142 wctx, actx = extractxs
143 # class to delay looking up copy mapping
143 # class to delay looking up copy mapping
144 class pathcopies(object):
144 class pathcopies(object):
145 @util.propertycache
145 @util.propertycache
146 def map(self):
146 def map(self):
147 # {dst@mctx: src@wctx} copy mapping
147 # {dst@mctx: src@wctx} copy mapping
148 return copies.pathcopies(wctx, mctx)
148 return copies.pathcopies(wctx, mctx)
149 pc = pathcopies()
149 pc = pathcopies()
150
150
151 for fn in wctx:
151 for fn in wctx:
152 fold = util.normcase(fn)
152 fold = util.normcase(fn)
153 mfn = folded.get(fold, None)
153 mfn = folded.get(fold, None)
154 if (mfn and mfn != fn and pc.map.get(mfn) != fn and
154 if (mfn and mfn != fn and pc.map.get(mfn) != fn and
155 _remains(fn, wctx.manifest(), actx.manifest(), True) and
155 _remains(fn, wctx.manifest(), actx.manifest(), True) and
156 _remains(mfn, mctx.manifest(), actx.manifest())):
156 _remains(mfn, mctx.manifest(), actx.manifest())):
157 raise util.Abort(_("case-folding collision between %s and %s")
157 raise util.Abort(_("case-folding collision between %s and %s")
158 % (mfn, fn))
158 % (mfn, fn))
159
159
160 def _forgetremoved(wctx, mctx, branchmerge):
160 def _forgetremoved(wctx, mctx, branchmerge):
161 """
161 """
162 Forget removed files
162 Forget removed files
163
163
164 If we're jumping between revisions (as opposed to merging), and if
164 If we're jumping between revisions (as opposed to merging), and if
165 neither the working directory nor the target rev has the file,
165 neither the working directory nor the target rev has the file,
166 then we need to remove it from the dirstate, to prevent the
166 then we need to remove it from the dirstate, to prevent the
167 dirstate from listing the file when it is no longer in the
167 dirstate from listing the file when it is no longer in the
168 manifest.
168 manifest.
169
169
170 If we're merging, and the other revision has removed a file
170 If we're merging, and the other revision has removed a file
171 that is not present in the working directory, we need to mark it
171 that is not present in the working directory, we need to mark it
172 as removed.
172 as removed.
173 """
173 """
174
174
175 actions = []
175 actions = []
176 state = branchmerge and 'r' or 'f'
176 state = branchmerge and 'r' or 'f'
177 for f in wctx.deleted():
177 for f in wctx.deleted():
178 if f not in mctx:
178 if f not in mctx:
179 actions.append((f, state, None, "forget deleted"))
179 actions.append((f, state, None, "forget deleted"))
180
180
181 if not branchmerge:
181 if not branchmerge:
182 for f in wctx.removed():
182 for f in wctx.removed():
183 if f not in mctx:
183 if f not in mctx:
184 actions.append((f, "f", None, "forget removed"))
184 actions.append((f, "f", None, "forget removed"))
185
185
186 return actions
186 return actions
187
187
188 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial):
188 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
189 acceptremote=False):
189 """
190 """
190 Merge p1 and p2 with ancestor pa and generate merge action list
191 Merge p1 and p2 with ancestor pa and generate merge action list
191
192
192 branchmerge and force are as passed in to update
193 branchmerge and force are as passed in to update
193 partial = function to filter file lists
194 partial = function to filter file lists
195 acceptremote = accept the incoming changes without prompting
194 """
196 """
195
197
196 overwrite = force and not branchmerge
198 overwrite = force and not branchmerge
197 actions, copy, movewithdir = [], {}, {}
199 actions, copy, movewithdir = [], {}, {}
198
200
199 followcopies = False
201 followcopies = False
200 if overwrite:
202 if overwrite:
201 pa = wctx
203 pa = wctx
202 elif pa == p2: # backwards
204 elif pa == p2: # backwards
203 pa = wctx.p1()
205 pa = wctx.p1()
204 elif not branchmerge and not wctx.dirty(missing=True):
206 elif not branchmerge and not wctx.dirty(missing=True):
205 pass
207 pass
206 elif pa and repo.ui.configbool("merge", "followcopies", True):
208 elif pa and repo.ui.configbool("merge", "followcopies", True):
207 followcopies = True
209 followcopies = True
208
210
209 # manifests fetched in order are going to be faster, so prime the caches
211 # manifests fetched in order are going to be faster, so prime the caches
210 [x.manifest() for x in
212 [x.manifest() for x in
211 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
213 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
212
214
213 if followcopies:
215 if followcopies:
214 ret = copies.mergecopies(repo, wctx, p2, pa)
216 ret = copies.mergecopies(repo, wctx, p2, pa)
215 copy, movewithdir, diverge, renamedelete = ret
217 copy, movewithdir, diverge, renamedelete = ret
216 for of, fl in diverge.iteritems():
218 for of, fl in diverge.iteritems():
217 actions.append((of, "dr", (fl,), "divergent renames"))
219 actions.append((of, "dr", (fl,), "divergent renames"))
218 for of, fl in renamedelete.iteritems():
220 for of, fl in renamedelete.iteritems():
219 actions.append((of, "rd", (fl,), "rename and delete"))
221 actions.append((of, "rd", (fl,), "rename and delete"))
220
222
221 repo.ui.note(_("resolving manifests\n"))
223 repo.ui.note(_("resolving manifests\n"))
222 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
224 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
223 % (bool(branchmerge), bool(force), bool(partial)))
225 % (bool(branchmerge), bool(force), bool(partial)))
224 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
226 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
225
227
226 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
228 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
227 copied = set(copy.values())
229 copied = set(copy.values())
228 copied.update(movewithdir.values())
230 copied.update(movewithdir.values())
229
231
230 if '.hgsubstate' in m1:
232 if '.hgsubstate' in m1:
231 # check whether sub state is modified
233 # check whether sub state is modified
232 for s in sorted(wctx.substate):
234 for s in sorted(wctx.substate):
233 if wctx.sub(s).dirty():
235 if wctx.sub(s).dirty():
234 m1['.hgsubstate'] += "+"
236 m1['.hgsubstate'] += "+"
235 break
237 break
236
238
237 aborts, prompts = [], []
239 aborts, prompts = [], []
238 # Compare manifests
240 # Compare manifests
239 for f, n in m1.iteritems():
241 for f, n in m1.iteritems():
240 if partial and not partial(f):
242 if partial and not partial(f):
241 continue
243 continue
242 if f in m2:
244 if f in m2:
243 n2 = m2[f]
245 n2 = m2[f]
244 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
246 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
245 nol = 'l' not in fl1 + fl2 + fla
247 nol = 'l' not in fl1 + fl2 + fla
246 a = ma.get(f, nullid)
248 a = ma.get(f, nullid)
247 if n == n2 and fl1 == fl2:
249 if n == n2 and fl1 == fl2:
248 pass # same - keep local
250 pass # same - keep local
249 elif n2 == a and fl2 == fla:
251 elif n2 == a and fl2 == fla:
250 pass # remote unchanged - keep local
252 pass # remote unchanged - keep local
251 elif n == a and fl1 == fla: # local unchanged - use remote
253 elif n == a and fl1 == fla: # local unchanged - use remote
252 if n == n2: # optimization: keep local content
254 if n == n2: # optimization: keep local content
253 actions.append((f, "e", (fl2,), "update permissions"))
255 actions.append((f, "e", (fl2,), "update permissions"))
254 else:
256 else:
255 actions.append((f, "g", (fl2,), "remote is newer"))
257 actions.append((f, "g", (fl2,), "remote is newer"))
256 elif nol and n2 == a: # remote only changed 'x'
258 elif nol and n2 == a: # remote only changed 'x'
257 actions.append((f, "e", (fl2,), "update permissions"))
259 actions.append((f, "e", (fl2,), "update permissions"))
258 elif nol and n == a: # local only changed 'x'
260 elif nol and n == a: # local only changed 'x'
259 actions.append((f, "g", (fl1,), "remote is newer"))
261 actions.append((f, "g", (fl1,), "remote is newer"))
260 else: # both changed something
262 else: # both changed something
261 actions.append((f, "m", (f, f, False), "versions differ"))
263 actions.append((f, "m", (f, f, False), "versions differ"))
262 elif f in copied: # files we'll deal with on m2 side
264 elif f in copied: # files we'll deal with on m2 side
263 pass
265 pass
264 elif f in movewithdir: # directory rename
266 elif f in movewithdir: # directory rename
265 f2 = movewithdir[f]
267 f2 = movewithdir[f]
266 actions.append((f, "d", (None, f2, m1.flags(f)),
268 actions.append((f, "d", (None, f2, m1.flags(f)),
267 "remote renamed directory to " + f2))
269 "remote renamed directory to " + f2))
268 elif f in copy:
270 elif f in copy:
269 f2 = copy[f]
271 f2 = copy[f]
270 actions.append((f, "m", (f2, f, False),
272 actions.append((f, "m", (f2, f, False),
271 "local copied/moved to " + f2))
273 "local copied/moved to " + f2))
272 elif f in ma: # clean, a different, no remote
274 elif f in ma: # clean, a different, no remote
273 if n != ma[f]:
275 if n != ma[f]:
274 prompts.append((f, "cd")) # prompt changed/deleted
276 prompts.append((f, "cd")) # prompt changed/deleted
275 elif n[20:] == "a": # added, no remote
277 elif n[20:] == "a": # added, no remote
276 actions.append((f, "f", None, "remote deleted"))
278 actions.append((f, "f", None, "remote deleted"))
277 else:
279 else:
278 actions.append((f, "r", None, "other deleted"))
280 actions.append((f, "r", None, "other deleted"))
279
281
280 for f, n in m2.iteritems():
282 for f, n in m2.iteritems():
281 if partial and not partial(f):
283 if partial and not partial(f):
282 continue
284 continue
283 if f in m1 or f in copied: # files already visited
285 if f in m1 or f in copied: # files already visited
284 continue
286 continue
285 if f in movewithdir:
287 if f in movewithdir:
286 f2 = movewithdir[f]
288 f2 = movewithdir[f]
287 actions.append((None, "d", (f, f2, m2.flags(f)),
289 actions.append((None, "d", (f, f2, m2.flags(f)),
288 "local renamed directory to " + f2))
290 "local renamed directory to " + f2))
289 elif f in copy:
291 elif f in copy:
290 f2 = copy[f]
292 f2 = copy[f]
291 if f2 in m2:
293 if f2 in m2:
292 actions.append((f2, "m", (f, f, False),
294 actions.append((f2, "m", (f, f, False),
293 "remote copied to " + f))
295 "remote copied to " + f))
294 else:
296 else:
295 actions.append((f2, "m", (f, f, True),
297 actions.append((f2, "m", (f, f, True),
296 "remote moved to " + f))
298 "remote moved to " + f))
297 elif f not in ma:
299 elif f not in ma:
298 # local unknown, remote created: the logic is described by the
300 # local unknown, remote created: the logic is described by the
299 # following table:
301 # following table:
300 #
302 #
301 # force branchmerge different | action
303 # force branchmerge different | action
302 # n * n | get
304 # n * n | get
303 # n * y | abort
305 # n * y | abort
304 # y n * | get
306 # y n * | get
305 # y y n | get
307 # y y n | get
306 # y y y | merge
308 # y y y | merge
307 #
309 #
308 # Checking whether the files are different is expensive, so we
310 # Checking whether the files are different is expensive, so we
309 # don't do that when we can avoid it.
311 # don't do that when we can avoid it.
310 if force and not branchmerge:
312 if force and not branchmerge:
311 actions.append((f, "g", (m2.flags(f),), "remote created"))
313 actions.append((f, "g", (m2.flags(f),), "remote created"))
312 else:
314 else:
313 different = _checkunknownfile(repo, wctx, p2, f)
315 different = _checkunknownfile(repo, wctx, p2, f)
314 if force and branchmerge and different:
316 if force and branchmerge and different:
315 actions.append((f, "m", (f, f, False),
317 actions.append((f, "m", (f, f, False),
316 "remote differs from untracked local"))
318 "remote differs from untracked local"))
317 elif not force and different:
319 elif not force and different:
318 aborts.append((f, "ud"))
320 aborts.append((f, "ud"))
319 else:
321 else:
320 actions.append((f, "g", (m2.flags(f),), "remote created"))
322 actions.append((f, "g", (m2.flags(f),), "remote created"))
321 elif n != ma[f]:
323 elif n != ma[f]:
322 prompts.append((f, "dc")) # prompt deleted/changed
324 prompts.append((f, "dc")) # prompt deleted/changed
323
325
324 for f, m in sorted(aborts):
326 for f, m in sorted(aborts):
325 if m == "ud":
327 if m == "ud":
326 repo.ui.warn(_("%s: untracked file differs\n") % f)
328 repo.ui.warn(_("%s: untracked file differs\n") % f)
327 else: assert False, m
329 else: assert False, m
328 if aborts:
330 if aborts:
329 raise util.Abort(_("untracked files in working directory differ "
331 raise util.Abort(_("untracked files in working directory differ "
330 "from files in requested revision"))
332 "from files in requested revision"))
331
333
332 for f, m in sorted(prompts):
334 for f, m in sorted(prompts):
333 if m == "cd":
335 if m == "cd":
334 if repo.ui.promptchoice(
336 if acceptremote:
337 actions.append((f, "r", None, "remote delete"))
338 elif repo.ui.promptchoice(
335 _("local changed %s which remote deleted\n"
339 _("local changed %s which remote deleted\n"
336 "use (c)hanged version or (d)elete?") % f,
340 "use (c)hanged version or (d)elete?") % f,
337 (_("&Changed"), _("&Delete")), 0):
341 (_("&Changed"), _("&Delete")), 0):
338 actions.append((f, "r", None, "prompt delete"))
342 actions.append((f, "r", None, "prompt delete"))
339 else:
343 else:
340 actions.append((f, "a", None, "prompt keep"))
344 actions.append((f, "a", None, "prompt keep"))
341 elif m == "dc":
345 elif m == "dc":
342 if repo.ui.promptchoice(
346 if acceptremote:
347 actions.append((f, "g", (m2.flags(f),), "remote recreating"))
348 elif repo.ui.promptchoice(
343 _("remote changed %s which local deleted\n"
349 _("remote changed %s which local deleted\n"
344 "use (c)hanged version or leave (d)eleted?") % f,
350 "use (c)hanged version or leave (d)eleted?") % f,
345 (_("&Changed"), _("&Deleted")), 0) == 0:
351 (_("&Changed"), _("&Deleted")), 0) == 0:
346 actions.append((f, "g", (m2.flags(f),), "prompt recreating"))
352 actions.append((f, "g", (m2.flags(f),), "prompt recreating"))
347 else: assert False, m
353 else: assert False, m
348 return actions
354 return actions
349
355
350 def actionkey(a):
356 def actionkey(a):
351 return a[1] == "r" and -1 or 0, a
357 return a[1] == "r" and -1 or 0, a
352
358
353 def getremove(repo, mctx, overwrite, args):
359 def getremove(repo, mctx, overwrite, args):
354 """apply usually-non-interactive updates to the working directory
360 """apply usually-non-interactive updates to the working directory
355
361
356 mctx is the context to be merged into the working copy
362 mctx is the context to be merged into the working copy
357
363
358 yields tuples for progress updates
364 yields tuples for progress updates
359 """
365 """
360 verbose = repo.ui.verbose
366 verbose = repo.ui.verbose
361 unlink = util.unlinkpath
367 unlink = util.unlinkpath
362 wjoin = repo.wjoin
368 wjoin = repo.wjoin
363 fctx = mctx.filectx
369 fctx = mctx.filectx
364 wwrite = repo.wwrite
370 wwrite = repo.wwrite
365 audit = repo.wopener.audit
371 audit = repo.wopener.audit
366 i = 0
372 i = 0
367 for arg in args:
373 for arg in args:
368 f = arg[0]
374 f = arg[0]
369 if arg[1] == 'r':
375 if arg[1] == 'r':
370 if verbose:
376 if verbose:
371 repo.ui.note(_("removing %s\n") % f)
377 repo.ui.note(_("removing %s\n") % f)
372 audit(f)
378 audit(f)
373 try:
379 try:
374 unlink(wjoin(f), ignoremissing=True)
380 unlink(wjoin(f), ignoremissing=True)
375 except OSError, inst:
381 except OSError, inst:
376 repo.ui.warn(_("update failed to remove %s: %s!\n") %
382 repo.ui.warn(_("update failed to remove %s: %s!\n") %
377 (f, inst.strerror))
383 (f, inst.strerror))
378 else:
384 else:
379 if verbose:
385 if verbose:
380 repo.ui.note(_("getting %s\n") % f)
386 repo.ui.note(_("getting %s\n") % f)
381 wwrite(f, fctx(f).data(), arg[2][0])
387 wwrite(f, fctx(f).data(), arg[2][0])
382 if i == 100:
388 if i == 100:
383 yield i, f
389 yield i, f
384 i = 0
390 i = 0
385 i += 1
391 i += 1
386 if i > 0:
392 if i > 0:
387 yield i, f
393 yield i, f
388
394
389 def applyupdates(repo, actions, wctx, mctx, actx, overwrite):
395 def applyupdates(repo, actions, wctx, mctx, actx, overwrite):
390 """apply the merge action list to the working directory
396 """apply the merge action list to the working directory
391
397
392 wctx is the working copy context
398 wctx is the working copy context
393 mctx is the context to be merged into the working copy
399 mctx is the context to be merged into the working copy
394 actx is the context of the common ancestor
400 actx is the context of the common ancestor
395
401
396 Return a tuple of counts (updated, merged, removed, unresolved) that
402 Return a tuple of counts (updated, merged, removed, unresolved) that
397 describes how many files were affected by the update.
403 describes how many files were affected by the update.
398 """
404 """
399
405
400 updated, merged, removed, unresolved = 0, 0, 0, 0
406 updated, merged, removed, unresolved = 0, 0, 0, 0
401 ms = mergestate(repo)
407 ms = mergestate(repo)
402 ms.reset(wctx.p1().node())
408 ms.reset(wctx.p1().node())
403 moves = []
409 moves = []
404 actions.sort(key=actionkey)
410 actions.sort(key=actionkey)
405
411
406 # prescan for merges
412 # prescan for merges
407 for a in actions:
413 for a in actions:
408 f, m, args, msg = a
414 f, m, args, msg = a
409 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
415 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
410 if m == "m": # merge
416 if m == "m": # merge
411 f2, fd, move = args
417 f2, fd, move = args
412 if fd == '.hgsubstate': # merged internally
418 if fd == '.hgsubstate': # merged internally
413 continue
419 continue
414 repo.ui.debug(" preserving %s for resolve of %s\n" % (f, fd))
420 repo.ui.debug(" preserving %s for resolve of %s\n" % (f, fd))
415 fcl = wctx[f]
421 fcl = wctx[f]
416 fco = mctx[f2]
422 fco = mctx[f2]
417 if mctx == actx: # backwards, use working dir parent as ancestor
423 if mctx == actx: # backwards, use working dir parent as ancestor
418 if fcl.parents():
424 if fcl.parents():
419 fca = fcl.p1()
425 fca = fcl.p1()
420 else:
426 else:
421 fca = repo.filectx(f, fileid=nullrev)
427 fca = repo.filectx(f, fileid=nullrev)
422 else:
428 else:
423 fca = fcl.ancestor(fco, actx)
429 fca = fcl.ancestor(fco, actx)
424 if not fca:
430 if not fca:
425 fca = repo.filectx(f, fileid=nullrev)
431 fca = repo.filectx(f, fileid=nullrev)
426 ms.add(fcl, fco, fca, fd)
432 ms.add(fcl, fco, fca, fd)
427 if f != fd and move:
433 if f != fd and move:
428 moves.append(f)
434 moves.append(f)
429
435
430 audit = repo.wopener.audit
436 audit = repo.wopener.audit
431
437
432 # remove renamed files after safely stored
438 # remove renamed files after safely stored
433 for f in moves:
439 for f in moves:
434 if os.path.lexists(repo.wjoin(f)):
440 if os.path.lexists(repo.wjoin(f)):
435 repo.ui.debug("removing %s\n" % f)
441 repo.ui.debug("removing %s\n" % f)
436 audit(f)
442 audit(f)
437 util.unlinkpath(repo.wjoin(f))
443 util.unlinkpath(repo.wjoin(f))
438
444
439 numupdates = len(actions)
445 numupdates = len(actions)
440 workeractions = [a for a in actions if a[1] in 'gr']
446 workeractions = [a for a in actions if a[1] in 'gr']
441 updated = len([a for a in workeractions if a[1] == 'g'])
447 updated = len([a for a in workeractions if a[1] == 'g'])
442 removed = len([a for a in workeractions if a[1] == 'r'])
448 removed = len([a for a in workeractions if a[1] == 'r'])
443 actions = [a for a in actions if a[1] not in 'gr']
449 actions = [a for a in actions if a[1] not in 'gr']
444
450
445 hgsub = [a[1] for a in workeractions if a[0] == '.hgsubstate']
451 hgsub = [a[1] for a in workeractions if a[0] == '.hgsubstate']
446 if hgsub and hgsub[0] == 'r':
452 if hgsub and hgsub[0] == 'r':
447 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
453 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
448
454
449 z = 0
455 z = 0
450 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
456 prog = worker.worker(repo.ui, 0.001, getremove, (repo, mctx, overwrite),
451 workeractions)
457 workeractions)
452 for i, item in prog:
458 for i, item in prog:
453 z += i
459 z += i
454 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
460 repo.ui.progress(_('updating'), z, item=item, total=numupdates,
455 unit=_('files'))
461 unit=_('files'))
456
462
457 if hgsub and hgsub[0] == 'g':
463 if hgsub and hgsub[0] == 'g':
458 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
464 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
459
465
460 _updating = _('updating')
466 _updating = _('updating')
461 _files = _('files')
467 _files = _('files')
462 progress = repo.ui.progress
468 progress = repo.ui.progress
463
469
464 for i, a in enumerate(actions):
470 for i, a in enumerate(actions):
465 f, m, args, msg = a
471 f, m, args, msg = a
466 progress(_updating, z + i + 1, item=f, total=numupdates, unit=_files)
472 progress(_updating, z + i + 1, item=f, total=numupdates, unit=_files)
467 if m == "m": # merge
473 if m == "m": # merge
474 f2, fd, move = args
468 if fd == '.hgsubstate': # subrepo states need updating
475 if fd == '.hgsubstate': # subrepo states need updating
469 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
476 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
470 overwrite)
477 overwrite)
471 continue
478 continue
472 f2, fd, move = args
473 audit(fd)
479 audit(fd)
474 r = ms.resolve(fd, wctx, mctx)
480 r = ms.resolve(fd, wctx, mctx)
475 if r is not None and r > 0:
481 if r is not None and r > 0:
476 unresolved += 1
482 unresolved += 1
477 else:
483 else:
478 if r is None:
484 if r is None:
479 updated += 1
485 updated += 1
480 else:
486 else:
481 merged += 1
487 merged += 1
482 elif m == "d": # directory rename
488 elif m == "d": # directory rename
483 f2, fd, flags = args
489 f2, fd, flags = args
484 if f:
490 if f:
485 repo.ui.note(_("moving %s to %s\n") % (f, fd))
491 repo.ui.note(_("moving %s to %s\n") % (f, fd))
486 audit(f)
492 audit(f)
487 repo.wwrite(fd, wctx.filectx(f).data(), flags)
493 repo.wwrite(fd, wctx.filectx(f).data(), flags)
488 util.unlinkpath(repo.wjoin(f))
494 util.unlinkpath(repo.wjoin(f))
489 if f2:
495 if f2:
490 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
496 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
491 repo.wwrite(fd, mctx.filectx(f2).data(), flags)
497 repo.wwrite(fd, mctx.filectx(f2).data(), flags)
492 updated += 1
498 updated += 1
493 elif m == "dr": # divergent renames
499 elif m == "dr": # divergent renames
494 fl, = args
500 fl, = args
495 repo.ui.warn(_("note: possible conflict - %s was renamed "
501 repo.ui.warn(_("note: possible conflict - %s was renamed "
496 "multiple times to:\n") % f)
502 "multiple times to:\n") % f)
497 for nf in fl:
503 for nf in fl:
498 repo.ui.warn(" %s\n" % nf)
504 repo.ui.warn(" %s\n" % nf)
499 elif m == "rd": # rename and delete
505 elif m == "rd": # rename and delete
500 fl, = args
506 fl, = args
501 repo.ui.warn(_("note: possible conflict - %s was deleted "
507 repo.ui.warn(_("note: possible conflict - %s was deleted "
502 "and renamed to:\n") % f)
508 "and renamed to:\n") % f)
503 for nf in fl:
509 for nf in fl:
504 repo.ui.warn(" %s\n" % nf)
510 repo.ui.warn(" %s\n" % nf)
505 elif m == "e": # exec
511 elif m == "e": # exec
506 flags, = args
512 flags, = args
507 audit(f)
513 audit(f)
508 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
514 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
509 updated += 1
515 updated += 1
510 ms.commit()
516 ms.commit()
511 progress(_updating, None, total=numupdates, unit=_files)
517 progress(_updating, None, total=numupdates, unit=_files)
512
518
513 return updated, merged, removed, unresolved
519 return updated, merged, removed, unresolved
514
520
515 def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial):
521 def calculateupdates(repo, tctx, mctx, ancestor, branchmerge, force, partial,
522 acceptremote=False):
516 "Calculate the actions needed to merge mctx into tctx"
523 "Calculate the actions needed to merge mctx into tctx"
517 actions = []
524 actions = []
518 folding = not util.checkcase(repo.path)
525 folding = not util.checkcase(repo.path)
519 if folding:
526 if folding:
520 # collision check is not needed for clean update
527 # collision check is not needed for clean update
521 if (not branchmerge and
528 if (not branchmerge and
522 (force or not tctx.dirty(missing=True, branch=False))):
529 (force or not tctx.dirty(missing=True, branch=False))):
523 _checkcollision(mctx, None)
530 _checkcollision(mctx, None)
524 else:
531 else:
525 _checkcollision(mctx, (tctx, ancestor))
532 _checkcollision(mctx, (tctx, ancestor))
526 actions += manifestmerge(repo, tctx, mctx,
533 actions += manifestmerge(repo, tctx, mctx,
527 ancestor,
534 ancestor,
528 branchmerge, force,
535 branchmerge, force,
529 partial)
536 partial, acceptremote)
530 if tctx.rev() is None:
537 if tctx.rev() is None:
531 actions += _forgetremoved(tctx, mctx, branchmerge)
538 actions += _forgetremoved(tctx, mctx, branchmerge)
532 return actions
539 return actions
533
540
534 def recordupdates(repo, actions, branchmerge):
541 def recordupdates(repo, actions, branchmerge):
535 "record merge actions to the dirstate"
542 "record merge actions to the dirstate"
536
543
537 for a in actions:
544 for a in actions:
538 f, m, args, msg = a
545 f, m, args, msg = a
539 if m == "r": # remove
546 if m == "r": # remove
540 if branchmerge:
547 if branchmerge:
541 repo.dirstate.remove(f)
548 repo.dirstate.remove(f)
542 else:
549 else:
543 repo.dirstate.drop(f)
550 repo.dirstate.drop(f)
544 elif m == "a": # re-add
551 elif m == "a": # re-add
545 if not branchmerge:
552 if not branchmerge:
546 repo.dirstate.add(f)
553 repo.dirstate.add(f)
547 elif m == "f": # forget
554 elif m == "f": # forget
548 repo.dirstate.drop(f)
555 repo.dirstate.drop(f)
549 elif m == "e": # exec change
556 elif m == "e": # exec change
550 repo.dirstate.normallookup(f)
557 repo.dirstate.normallookup(f)
551 elif m == "g": # get
558 elif m == "g": # get
552 if branchmerge:
559 if branchmerge:
553 repo.dirstate.otherparent(f)
560 repo.dirstate.otherparent(f)
554 else:
561 else:
555 repo.dirstate.normal(f)
562 repo.dirstate.normal(f)
556 elif m == "m": # merge
563 elif m == "m": # merge
557 f2, fd, move = args
564 f2, fd, move = args
558 if branchmerge:
565 if branchmerge:
559 # We've done a branch merge, mark this file as merged
566 # We've done a branch merge, mark this file as merged
560 # so that we properly record the merger later
567 # so that we properly record the merger later
561 repo.dirstate.merge(fd)
568 repo.dirstate.merge(fd)
562 if f != f2: # copy/rename
569 if f != f2: # copy/rename
563 if move:
570 if move:
564 repo.dirstate.remove(f)
571 repo.dirstate.remove(f)
565 if f != fd:
572 if f != fd:
566 repo.dirstate.copy(f, fd)
573 repo.dirstate.copy(f, fd)
567 else:
574 else:
568 repo.dirstate.copy(f2, fd)
575 repo.dirstate.copy(f2, fd)
569 else:
576 else:
570 # We've update-merged a locally modified file, so
577 # We've update-merged a locally modified file, so
571 # we set the dirstate to emulate a normal checkout
578 # we set the dirstate to emulate a normal checkout
572 # of that file some time in the past. Thus our
579 # of that file some time in the past. Thus our
573 # merge will appear as a normal local file
580 # merge will appear as a normal local file
574 # modification.
581 # modification.
575 if f2 == fd: # file not locally copied/moved
582 if f2 == fd: # file not locally copied/moved
576 repo.dirstate.normallookup(fd)
583 repo.dirstate.normallookup(fd)
577 if move:
584 if move:
578 repo.dirstate.drop(f)
585 repo.dirstate.drop(f)
579 elif m == "d": # directory rename
586 elif m == "d": # directory rename
580 f2, fd, flag = args
587 f2, fd, flag = args
581 if not f2 and f not in repo.dirstate:
588 if not f2 and f not in repo.dirstate:
582 # untracked file moved
589 # untracked file moved
583 continue
590 continue
584 if branchmerge:
591 if branchmerge:
585 repo.dirstate.add(fd)
592 repo.dirstate.add(fd)
586 if f:
593 if f:
587 repo.dirstate.remove(f)
594 repo.dirstate.remove(f)
588 repo.dirstate.copy(f, fd)
595 repo.dirstate.copy(f, fd)
589 if f2:
596 if f2:
590 repo.dirstate.copy(f2, fd)
597 repo.dirstate.copy(f2, fd)
591 else:
598 else:
592 repo.dirstate.normal(fd)
599 repo.dirstate.normal(fd)
593 if f:
600 if f:
594 repo.dirstate.drop(f)
601 repo.dirstate.drop(f)
595
602
596 def update(repo, node, branchmerge, force, partial, ancestor=None,
603 def update(repo, node, branchmerge, force, partial, ancestor=None,
597 mergeancestor=False):
604 mergeancestor=False):
598 """
605 """
599 Perform a merge between the working directory and the given node
606 Perform a merge between the working directory and the given node
600
607
601 node = the node to update to, or None if unspecified
608 node = the node to update to, or None if unspecified
602 branchmerge = whether to merge between branches
609 branchmerge = whether to merge between branches
603 force = whether to force branch merging or file overwriting
610 force = whether to force branch merging or file overwriting
604 partial = a function to filter file lists (dirstate not updated)
611 partial = a function to filter file lists (dirstate not updated)
605 mergeancestor = if false, merging with an ancestor (fast-forward)
612 mergeancestor = whether it is merging with an ancestor. If true,
606 is only allowed between different named branches. This flag
613 we should accept the incoming changes for any prompts that occur.
607 is used by rebase extension as a temporary fix and should be
614 If false, merging with an ancestor (fast-forward) is only allowed
608 avoided in general.
615 between different named branches. This flag is used by rebase extension
616 as a temporary fix and should be avoided in general.
609
617
610 The table below shows all the behaviors of the update command
618 The table below shows all the behaviors of the update command
611 given the -c and -C or no options, whether the working directory
619 given the -c and -C or no options, whether the working directory
612 is dirty, whether a revision is specified, and the relationship of
620 is dirty, whether a revision is specified, and the relationship of
613 the parent rev to the target rev (linear, on the same named
621 the parent rev to the target rev (linear, on the same named
614 branch, or on another named branch).
622 branch, or on another named branch).
615
623
616 This logic is tested by test-update-branches.t.
624 This logic is tested by test-update-branches.t.
617
625
618 -c -C dirty rev | linear same cross
626 -c -C dirty rev | linear same cross
619 n n n n | ok (1) x
627 n n n n | ok (1) x
620 n n n y | ok ok ok
628 n n n y | ok ok ok
621 n n y * | merge (2) (2)
629 n n y * | merge (2) (2)
622 n y * * | --- discard ---
630 n y * * | --- discard ---
623 y n y * | --- (3) ---
631 y n y * | --- (3) ---
624 y n n * | --- ok ---
632 y n n * | --- ok ---
625 y y * * | --- (4) ---
633 y y * * | --- (4) ---
626
634
627 x = can't happen
635 x = can't happen
628 * = don't-care
636 * = don't-care
629 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
637 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
630 2 = abort: crosses branches (use 'hg merge' to merge or
638 2 = abort: crosses branches (use 'hg merge' to merge or
631 use 'hg update -C' to discard changes)
639 use 'hg update -C' to discard changes)
632 3 = abort: uncommitted local changes
640 3 = abort: uncommitted local changes
633 4 = incompatible options (checked in commands.py)
641 4 = incompatible options (checked in commands.py)
634
642
635 Return the same tuple as applyupdates().
643 Return the same tuple as applyupdates().
636 """
644 """
637
645
638 onode = node
646 onode = node
639 wlock = repo.wlock()
647 wlock = repo.wlock()
640 try:
648 try:
641 wc = repo[None]
649 wc = repo[None]
642 if node is None:
650 if node is None:
643 # tip of current branch
651 # tip of current branch
644 try:
652 try:
645 node = repo.branchtip(wc.branch())
653 node = repo.branchtip(wc.branch())
646 except error.RepoLookupError:
654 except error.RepoLookupError:
647 if wc.branch() == "default": # no default branch!
655 if wc.branch() == "default": # no default branch!
648 node = repo.lookup("tip") # update to tip
656 node = repo.lookup("tip") # update to tip
649 else:
657 else:
650 raise util.Abort(_("branch %s not found") % wc.branch())
658 raise util.Abort(_("branch %s not found") % wc.branch())
651 overwrite = force and not branchmerge
659 overwrite = force and not branchmerge
652 pl = wc.parents()
660 pl = wc.parents()
653 p1, p2 = pl[0], repo[node]
661 p1, p2 = pl[0], repo[node]
654 if ancestor:
662 if ancestor:
655 pa = repo[ancestor]
663 pa = repo[ancestor]
656 else:
664 else:
657 pa = p1.ancestor(p2)
665 pa = p1.ancestor(p2)
658
666
659 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
667 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
660
668
661 ### check phase
669 ### check phase
662 if not overwrite and len(pl) > 1:
670 if not overwrite and len(pl) > 1:
663 raise util.Abort(_("outstanding uncommitted merges"))
671 raise util.Abort(_("outstanding uncommitted merges"))
664 if branchmerge:
672 if branchmerge:
665 if pa == p2:
673 if pa == p2:
666 raise util.Abort(_("merging with a working directory ancestor"
674 raise util.Abort(_("merging with a working directory ancestor"
667 " has no effect"))
675 " has no effect"))
668 elif pa == p1:
676 elif pa == p1:
669 if not mergeancestor and p1.branch() == p2.branch():
677 if not mergeancestor and p1.branch() == p2.branch():
670 raise util.Abort(_("nothing to merge"),
678 raise util.Abort(_("nothing to merge"),
671 hint=_("use 'hg update' "
679 hint=_("use 'hg update' "
672 "or check 'hg heads'"))
680 "or check 'hg heads'"))
673 if not force and (wc.files() or wc.deleted()):
681 if not force and (wc.files() or wc.deleted()):
674 raise util.Abort(_("outstanding uncommitted changes"),
682 raise util.Abort(_("outstanding uncommitted changes"),
675 hint=_("use 'hg status' to list changes"))
683 hint=_("use 'hg status' to list changes"))
676 for s in sorted(wc.substate):
684 for s in sorted(wc.substate):
677 if wc.sub(s).dirty():
685 if wc.sub(s).dirty():
678 raise util.Abort(_("outstanding uncommitted changes in "
686 raise util.Abort(_("outstanding uncommitted changes in "
679 "subrepository '%s'") % s)
687 "subrepository '%s'") % s)
680
688
681 elif not overwrite:
689 elif not overwrite:
682 if pa == p1 or pa == p2: # linear
690 if pa == p1 or pa == p2: # linear
683 pass # all good
691 pass # all good
684 elif wc.dirty(missing=True):
692 elif wc.dirty(missing=True):
685 raise util.Abort(_("crosses branches (merge branches or use"
693 raise util.Abort(_("crosses branches (merge branches or use"
686 " --clean to discard changes)"))
694 " --clean to discard changes)"))
687 elif onode is None:
695 elif onode is None:
688 raise util.Abort(_("crosses branches (merge branches or update"
696 raise util.Abort(_("crosses branches (merge branches or update"
689 " --check to force update)"))
697 " --check to force update)"))
690 else:
698 else:
691 # Allow jumping branches if clean and specific rev given
699 # Allow jumping branches if clean and specific rev given
692 pa = p1
700 pa = p1
693
701
694 ### calculate phase
702 ### calculate phase
695 actions = calculateupdates(repo, wc, p2, pa,
703 actions = calculateupdates(repo, wc, p2, pa,
696 branchmerge, force, partial)
704 branchmerge, force, partial, mergeancestor)
697
705
698 ### apply phase
706 ### apply phase
699 if not branchmerge: # just jump to the new rev
707 if not branchmerge: # just jump to the new rev
700 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
708 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
701 if not partial:
709 if not partial:
702 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
710 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
703
711
704 stats = applyupdates(repo, actions, wc, p2, pa, overwrite)
712 stats = applyupdates(repo, actions, wc, p2, pa, overwrite)
705
713
706 if not partial:
714 if not partial:
707 repo.setparents(fp1, fp2)
715 repo.setparents(fp1, fp2)
708 recordupdates(repo, actions, branchmerge)
716 recordupdates(repo, actions, branchmerge)
709 if not branchmerge:
717 if not branchmerge:
710 repo.dirstate.setbranch(p2.branch())
718 repo.dirstate.setbranch(p2.branch())
711 finally:
719 finally:
712 wlock.release()
720 wlock.release()
713
721
714 if not partial:
722 if not partial:
715 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
723 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
716 return stats
724 return stats
@@ -1,1943 +1,1941 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 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 import re
8 import re
9 import parser, util, error, discovery, hbisect, phases
9 import parser, util, error, discovery, hbisect, phases
10 import node
10 import node
11 import match as matchmod
11 import match as matchmod
12 from i18n import _
12 from i18n import _
13 import encoding
13 import encoding
14 import obsolete as obsmod
14 import obsolete as obsmod
15 import repoview
15 import repoview
16
16
17 def _revancestors(repo, revs, followfirst):
17 def _revancestors(repo, revs, followfirst):
18 """Like revlog.ancestors(), but supports followfirst."""
18 """Like revlog.ancestors(), but supports followfirst."""
19 cut = followfirst and 1 or None
19 cut = followfirst and 1 or None
20 cl = repo.changelog
20 cl = repo.changelog
21 visit = util.deque(revs)
21 visit = util.deque(revs)
22 seen = set([node.nullrev])
22 seen = set([node.nullrev])
23 while visit:
23 while visit:
24 for parent in cl.parentrevs(visit.popleft())[:cut]:
24 for parent in cl.parentrevs(visit.popleft())[:cut]:
25 if parent not in seen:
25 if parent not in seen:
26 visit.append(parent)
26 visit.append(parent)
27 seen.add(parent)
27 seen.add(parent)
28 yield parent
28 yield parent
29
29
30 def _revdescendants(repo, revs, followfirst):
30 def _revdescendants(repo, revs, followfirst):
31 """Like revlog.descendants() but supports followfirst."""
31 """Like revlog.descendants() but supports followfirst."""
32 cut = followfirst and 1 or None
32 cut = followfirst and 1 or None
33 cl = repo.changelog
33 cl = repo.changelog
34 first = min(revs)
34 first = min(revs)
35 nullrev = node.nullrev
35 nullrev = node.nullrev
36 if first == nullrev:
36 if first == nullrev:
37 # Are there nodes with a null first parent and a non-null
37 # Are there nodes with a null first parent and a non-null
38 # second one? Maybe. Do we care? Probably not.
38 # second one? Maybe. Do we care? Probably not.
39 for i in cl:
39 for i in cl:
40 yield i
40 yield i
41 return
41 return
42
42
43 seen = set(revs)
43 seen = set(revs)
44 for i in cl.revs(first + 1):
44 for i in cl.revs(first + 1):
45 for x in cl.parentrevs(i)[:cut]:
45 for x in cl.parentrevs(i)[:cut]:
46 if x != nullrev and x in seen:
46 if x != nullrev and x in seen:
47 seen.add(i)
47 seen.add(i)
48 yield i
48 yield i
49 break
49 break
50
50
51 def _revsbetween(repo, roots, heads):
51 def _revsbetween(repo, roots, heads):
52 """Return all paths between roots and heads, inclusive of both endpoint
52 """Return all paths between roots and heads, inclusive of both endpoint
53 sets."""
53 sets."""
54 if not roots:
54 if not roots:
55 return []
55 return []
56 parentrevs = repo.changelog.parentrevs
56 parentrevs = repo.changelog.parentrevs
57 visit = heads[:]
57 visit = heads[:]
58 reachable = set()
58 reachable = set()
59 seen = {}
59 seen = {}
60 minroot = min(roots)
60 minroot = min(roots)
61 roots = set(roots)
61 roots = set(roots)
62 # open-code the post-order traversal due to the tiny size of
62 # open-code the post-order traversal due to the tiny size of
63 # sys.getrecursionlimit()
63 # sys.getrecursionlimit()
64 while visit:
64 while visit:
65 rev = visit.pop()
65 rev = visit.pop()
66 if rev in roots:
66 if rev in roots:
67 reachable.add(rev)
67 reachable.add(rev)
68 parents = parentrevs(rev)
68 parents = parentrevs(rev)
69 seen[rev] = parents
69 seen[rev] = parents
70 for parent in parents:
70 for parent in parents:
71 if parent >= minroot and parent not in seen:
71 if parent >= minroot and parent not in seen:
72 visit.append(parent)
72 visit.append(parent)
73 if not reachable:
73 if not reachable:
74 return []
74 return []
75 for rev in sorted(seen):
75 for rev in sorted(seen):
76 for parent in seen[rev]:
76 for parent in seen[rev]:
77 if parent in reachable:
77 if parent in reachable:
78 reachable.add(rev)
78 reachable.add(rev)
79 return sorted(reachable)
79 return sorted(reachable)
80
80
81 elements = {
81 elements = {
82 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
82 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
83 "~": (18, None, ("ancestor", 18)),
83 "~": (18, None, ("ancestor", 18)),
84 "^": (18, None, ("parent", 18), ("parentpost", 18)),
84 "^": (18, None, ("parent", 18), ("parentpost", 18)),
85 "-": (5, ("negate", 19), ("minus", 5)),
85 "-": (5, ("negate", 19), ("minus", 5)),
86 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
86 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
87 ("dagrangepost", 17)),
87 ("dagrangepost", 17)),
88 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
88 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
89 ("dagrangepost", 17)),
89 ("dagrangepost", 17)),
90 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
90 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
91 "not": (10, ("not", 10)),
91 "not": (10, ("not", 10)),
92 "!": (10, ("not", 10)),
92 "!": (10, ("not", 10)),
93 "and": (5, None, ("and", 5)),
93 "and": (5, None, ("and", 5)),
94 "&": (5, None, ("and", 5)),
94 "&": (5, None, ("and", 5)),
95 "or": (4, None, ("or", 4)),
95 "or": (4, None, ("or", 4)),
96 "|": (4, None, ("or", 4)),
96 "|": (4, None, ("or", 4)),
97 "+": (4, None, ("or", 4)),
97 "+": (4, None, ("or", 4)),
98 ",": (2, None, ("list", 2)),
98 ",": (2, None, ("list", 2)),
99 ")": (0, None, None),
99 ")": (0, None, None),
100 "symbol": (0, ("symbol",), None),
100 "symbol": (0, ("symbol",), None),
101 "string": (0, ("string",), None),
101 "string": (0, ("string",), None),
102 "end": (0, None, None),
102 "end": (0, None, None),
103 }
103 }
104
104
105 keywords = set(['and', 'or', 'not'])
105 keywords = set(['and', 'or', 'not'])
106
106
107 def tokenize(program):
107 def tokenize(program):
108 '''
108 '''
109 Parse a revset statement into a stream of tokens
109 Parse a revset statement into a stream of tokens
110
110
111 Check that @ is a valid unquoted token character (issue3686):
111 Check that @ is a valid unquoted token character (issue3686):
112 >>> list(tokenize("@::"))
112 >>> list(tokenize("@::"))
113 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
113 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
114
114
115 '''
115 '''
116
116
117 pos, l = 0, len(program)
117 pos, l = 0, len(program)
118 while pos < l:
118 while pos < l:
119 c = program[pos]
119 c = program[pos]
120 if c.isspace(): # skip inter-token whitespace
120 if c.isspace(): # skip inter-token whitespace
121 pass
121 pass
122 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
122 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
123 yield ('::', None, pos)
123 yield ('::', None, pos)
124 pos += 1 # skip ahead
124 pos += 1 # skip ahead
125 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
125 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
126 yield ('..', None, pos)
126 yield ('..', None, pos)
127 pos += 1 # skip ahead
127 pos += 1 # skip ahead
128 elif c in "():,-|&+!~^": # handle simple operators
128 elif c in "():,-|&+!~^": # handle simple operators
129 yield (c, None, pos)
129 yield (c, None, pos)
130 elif (c in '"\'' or c == 'r' and
130 elif (c in '"\'' or c == 'r' and
131 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
131 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
132 if c == 'r':
132 if c == 'r':
133 pos += 1
133 pos += 1
134 c = program[pos]
134 c = program[pos]
135 decode = lambda x: x
135 decode = lambda x: x
136 else:
136 else:
137 decode = lambda x: x.decode('string-escape')
137 decode = lambda x: x.decode('string-escape')
138 pos += 1
138 pos += 1
139 s = pos
139 s = pos
140 while pos < l: # find closing quote
140 while pos < l: # find closing quote
141 d = program[pos]
141 d = program[pos]
142 if d == '\\': # skip over escaped characters
142 if d == '\\': # skip over escaped characters
143 pos += 2
143 pos += 2
144 continue
144 continue
145 if d == c:
145 if d == c:
146 yield ('string', decode(program[s:pos]), s)
146 yield ('string', decode(program[s:pos]), s)
147 break
147 break
148 pos += 1
148 pos += 1
149 else:
149 else:
150 raise error.ParseError(_("unterminated string"), s)
150 raise error.ParseError(_("unterminated string"), s)
151 # gather up a symbol/keyword
151 # gather up a symbol/keyword
152 elif c.isalnum() or c in '._@' or ord(c) > 127:
152 elif c.isalnum() or c in '._@' or ord(c) > 127:
153 s = pos
153 s = pos
154 pos += 1
154 pos += 1
155 while pos < l: # find end of symbol
155 while pos < l: # find end of symbol
156 d = program[pos]
156 d = program[pos]
157 if not (d.isalnum() or d in "._/@" or ord(d) > 127):
157 if not (d.isalnum() or d in "._/@" or ord(d) > 127):
158 break
158 break
159 if d == '.' and program[pos - 1] == '.': # special case for ..
159 if d == '.' and program[pos - 1] == '.': # special case for ..
160 pos -= 1
160 pos -= 1
161 break
161 break
162 pos += 1
162 pos += 1
163 sym = program[s:pos]
163 sym = program[s:pos]
164 if sym in keywords: # operator keywords
164 if sym in keywords: # operator keywords
165 yield (sym, None, s)
165 yield (sym, None, s)
166 else:
166 else:
167 yield ('symbol', sym, s)
167 yield ('symbol', sym, s)
168 pos -= 1
168 pos -= 1
169 else:
169 else:
170 raise error.ParseError(_("syntax error"), pos)
170 raise error.ParseError(_("syntax error"), pos)
171 pos += 1
171 pos += 1
172 yield ('end', None, pos)
172 yield ('end', None, pos)
173
173
174 # helpers
174 # helpers
175
175
176 def getstring(x, err):
176 def getstring(x, err):
177 if x and (x[0] == 'string' or x[0] == 'symbol'):
177 if x and (x[0] == 'string' or x[0] == 'symbol'):
178 return x[1]
178 return x[1]
179 raise error.ParseError(err)
179 raise error.ParseError(err)
180
180
181 def getlist(x):
181 def getlist(x):
182 if not x:
182 if not x:
183 return []
183 return []
184 if x[0] == 'list':
184 if x[0] == 'list':
185 return getlist(x[1]) + [x[2]]
185 return getlist(x[1]) + [x[2]]
186 return [x]
186 return [x]
187
187
188 def getargs(x, min, max, err):
188 def getargs(x, min, max, err):
189 l = getlist(x)
189 l = getlist(x)
190 if len(l) < min or (max >= 0 and len(l) > max):
190 if len(l) < min or (max >= 0 and len(l) > max):
191 raise error.ParseError(err)
191 raise error.ParseError(err)
192 return l
192 return l
193
193
194 def getset(repo, subset, x):
194 def getset(repo, subset, x):
195 if not x:
195 if not x:
196 raise error.ParseError(_("missing argument"))
196 raise error.ParseError(_("missing argument"))
197 return methods[x[0]](repo, subset, *x[1:])
197 return methods[x[0]](repo, subset, *x[1:])
198
198
199 def _getrevsource(repo, r):
199 def _getrevsource(repo, r):
200 extra = repo[r].extra()
200 extra = repo[r].extra()
201 for label in ('source', 'transplant_source', 'rebase_source'):
201 for label in ('source', 'transplant_source', 'rebase_source'):
202 if label in extra:
202 if label in extra:
203 try:
203 try:
204 return repo[extra[label]].rev()
204 return repo[extra[label]].rev()
205 except error.RepoLookupError:
205 except error.RepoLookupError:
206 pass
206 pass
207 return None
207 return None
208
208
209 # operator methods
209 # operator methods
210
210
211 def stringset(repo, subset, x):
211 def stringset(repo, subset, x):
212 x = repo[x].rev()
212 x = repo[x].rev()
213 if x == -1 and len(subset) == len(repo):
213 if x == -1 and len(subset) == len(repo):
214 return [-1]
214 return [-1]
215 if len(subset) == len(repo) or x in subset:
215 if len(subset) == len(repo) or x in subset:
216 return [x]
216 return [x]
217 return []
217 return []
218
218
219 def symbolset(repo, subset, x):
219 def symbolset(repo, subset, x):
220 if x in symbols:
220 if x in symbols:
221 raise error.ParseError(_("can't use %s here") % x)
221 raise error.ParseError(_("can't use %s here") % x)
222 return stringset(repo, subset, x)
222 return stringset(repo, subset, x)
223
223
224 def rangeset(repo, subset, x, y):
224 def rangeset(repo, subset, x, y):
225 cl = repo.changelog
225 cl = repo.changelog
226 m = getset(repo, cl, x)
226 m = getset(repo, cl, x)
227 n = getset(repo, cl, y)
227 n = getset(repo, cl, y)
228
228
229 if not m or not n:
229 if not m or not n:
230 return []
230 return []
231 m, n = m[0], n[-1]
231 m, n = m[0], n[-1]
232
232
233 if m < n:
233 if m < n:
234 r = range(m, n + 1)
234 r = range(m, n + 1)
235 else:
235 else:
236 r = range(m, n - 1, -1)
236 r = range(m, n - 1, -1)
237 s = set(subset)
237 s = set(subset)
238 return [x for x in r if x in s]
238 return [x for x in r if x in s]
239
239
240 def dagrange(repo, subset, x, y):
240 def dagrange(repo, subset, x, y):
241 if subset:
241 if subset:
242 r = list(repo)
242 r = list(repo)
243 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
243 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
244 s = set(subset)
244 s = set(subset)
245 return [r for r in xs if r in s]
245 return [r for r in xs if r in s]
246 return []
246 return []
247
247
248 def andset(repo, subset, x, y):
248 def andset(repo, subset, x, y):
249 return getset(repo, getset(repo, subset, x), y)
249 return getset(repo, getset(repo, subset, x), y)
250
250
251 def orset(repo, subset, x, y):
251 def orset(repo, subset, x, y):
252 xl = getset(repo, subset, x)
252 xl = getset(repo, subset, x)
253 s = set(xl)
253 s = set(xl)
254 yl = getset(repo, [r for r in subset if r not in s], y)
254 yl = getset(repo, [r for r in subset if r not in s], y)
255 return xl + yl
255 return xl + yl
256
256
257 def notset(repo, subset, x):
257 def notset(repo, subset, x):
258 s = set(getset(repo, subset, x))
258 s = set(getset(repo, subset, x))
259 return [r for r in subset if r not in s]
259 return [r for r in subset if r not in s]
260
260
261 def listset(repo, subset, a, b):
261 def listset(repo, subset, a, b):
262 raise error.ParseError(_("can't use a list in this context"))
262 raise error.ParseError(_("can't use a list in this context"))
263
263
264 def func(repo, subset, a, b):
264 def func(repo, subset, a, b):
265 if a[0] == 'symbol' and a[1] in symbols:
265 if a[0] == 'symbol' and a[1] in symbols:
266 return symbols[a[1]](repo, subset, b)
266 return symbols[a[1]](repo, subset, b)
267 raise error.ParseError(_("not a function: %s") % a[1])
267 raise error.ParseError(_("not a function: %s") % a[1])
268
268
269 # functions
269 # functions
270
270
271 def adds(repo, subset, x):
271 def adds(repo, subset, x):
272 """``adds(pattern)``
272 """``adds(pattern)``
273 Changesets that add a file matching pattern.
273 Changesets that add a file matching pattern.
274 """
274 """
275 # i18n: "adds" is a keyword
275 # i18n: "adds" is a keyword
276 pat = getstring(x, _("adds requires a pattern"))
276 pat = getstring(x, _("adds requires a pattern"))
277 return checkstatus(repo, subset, pat, 1)
277 return checkstatus(repo, subset, pat, 1)
278
278
279 def ancestor(repo, subset, x):
279 def ancestor(repo, subset, x):
280 """``ancestor(*changeset)``
280 """``ancestor(*changeset)``
281 Greatest common ancestor of the changesets.
281 Greatest common ancestor of the changesets.
282
282
283 Accepts 0 or more changesets.
283 Accepts 0 or more changesets.
284 Will return empty list when passed no args.
284 Will return empty list when passed no args.
285 Greatest common ancestor of a single changeset is that changeset.
285 Greatest common ancestor of a single changeset is that changeset.
286 """
286 """
287 # i18n: "ancestor" is a keyword
287 # i18n: "ancestor" is a keyword
288 l = getlist(x)
288 l = getlist(x)
289 rl = list(repo)
289 rl = list(repo)
290 anc = None
290 anc = None
291
291
292 # (getset(repo, rl, i) for i in l) generates a list of lists
292 # (getset(repo, rl, i) for i in l) generates a list of lists
293 rev = repo.changelog.rev
293 rev = repo.changelog.rev
294 ancestor = repo.changelog.ancestor
294 ancestor = repo.changelog.ancestor
295 node = repo.changelog.node
295 node = repo.changelog.node
296 for revs in (getset(repo, rl, i) for i in l):
296 for revs in (getset(repo, rl, i) for i in l):
297 for r in revs:
297 for r in revs:
298 if anc is None:
298 if anc is None:
299 anc = r
299 anc = r
300 else:
300 else:
301 anc = rev(ancestor(node(anc), node(r)))
301 anc = rev(ancestor(node(anc), node(r)))
302
302
303 if anc is not None and anc in subset:
303 if anc is not None and anc in subset:
304 return [anc]
304 return [anc]
305 return []
305 return []
306
306
307 def _ancestors(repo, subset, x, followfirst=False):
307 def _ancestors(repo, subset, x, followfirst=False):
308 args = getset(repo, list(repo), x)
308 args = getset(repo, list(repo), x)
309 if not args:
309 if not args:
310 return []
310 return []
311 s = set(_revancestors(repo, args, followfirst)) | set(args)
311 s = set(_revancestors(repo, args, followfirst)) | set(args)
312 return [r for r in subset if r in s]
312 return [r for r in subset if r in s]
313
313
314 def ancestors(repo, subset, x):
314 def ancestors(repo, subset, x):
315 """``ancestors(set)``
315 """``ancestors(set)``
316 Changesets that are ancestors of a changeset in set.
316 Changesets that are ancestors of a changeset in set.
317 """
317 """
318 return _ancestors(repo, subset, x)
318 return _ancestors(repo, subset, x)
319
319
320 def _firstancestors(repo, subset, x):
320 def _firstancestors(repo, subset, x):
321 # ``_firstancestors(set)``
321 # ``_firstancestors(set)``
322 # Like ``ancestors(set)`` but follows only the first parents.
322 # Like ``ancestors(set)`` but follows only the first parents.
323 return _ancestors(repo, subset, x, followfirst=True)
323 return _ancestors(repo, subset, x, followfirst=True)
324
324
325 def ancestorspec(repo, subset, x, n):
325 def ancestorspec(repo, subset, x, n):
326 """``set~n``
326 """``set~n``
327 Changesets that are the Nth ancestor (first parents only) of a changeset
327 Changesets that are the Nth ancestor (first parents only) of a changeset
328 in set.
328 in set.
329 """
329 """
330 try:
330 try:
331 n = int(n[1])
331 n = int(n[1])
332 except (TypeError, ValueError):
332 except (TypeError, ValueError):
333 raise error.ParseError(_("~ expects a number"))
333 raise error.ParseError(_("~ expects a number"))
334 ps = set()
334 ps = set()
335 cl = repo.changelog
335 cl = repo.changelog
336 for r in getset(repo, cl, x):
336 for r in getset(repo, cl, x):
337 for i in range(n):
337 for i in range(n):
338 r = cl.parentrevs(r)[0]
338 r = cl.parentrevs(r)[0]
339 ps.add(r)
339 ps.add(r)
340 return [r for r in subset if r in ps]
340 return [r for r in subset if r in ps]
341
341
342 def author(repo, subset, x):
342 def author(repo, subset, x):
343 """``author(string)``
343 """``author(string)``
344 Alias for ``user(string)``.
344 Alias for ``user(string)``.
345 """
345 """
346 # i18n: "author" is a keyword
346 # i18n: "author" is a keyword
347 n = encoding.lower(getstring(x, _("author requires a string")))
347 n = encoding.lower(getstring(x, _("author requires a string")))
348 kind, pattern, matcher = _substringmatcher(n)
348 kind, pattern, matcher = _substringmatcher(n)
349 return [r for r in subset if matcher(encoding.lower(repo[r].user()))]
349 return [r for r in subset if matcher(encoding.lower(repo[r].user()))]
350
350
351 def bisect(repo, subset, x):
351 def bisect(repo, subset, x):
352 """``bisect(string)``
352 """``bisect(string)``
353 Changesets marked in the specified bisect status:
353 Changesets marked in the specified bisect status:
354
354
355 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
355 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
356 - ``goods``, ``bads`` : csets topologically good/bad
356 - ``goods``, ``bads`` : csets topologically good/bad
357 - ``range`` : csets taking part in the bisection
357 - ``range`` : csets taking part in the bisection
358 - ``pruned`` : csets that are goods, bads or skipped
358 - ``pruned`` : csets that are goods, bads or skipped
359 - ``untested`` : csets whose fate is yet unknown
359 - ``untested`` : csets whose fate is yet unknown
360 - ``ignored`` : csets ignored due to DAG topology
360 - ``ignored`` : csets ignored due to DAG topology
361 - ``current`` : the cset currently being bisected
361 - ``current`` : the cset currently being bisected
362 """
362 """
363 # i18n: "bisect" is a keyword
363 # i18n: "bisect" is a keyword
364 status = getstring(x, _("bisect requires a string")).lower()
364 status = getstring(x, _("bisect requires a string")).lower()
365 state = set(hbisect.get(repo, status))
365 state = set(hbisect.get(repo, status))
366 return [r for r in subset if r in state]
366 return [r for r in subset if r in state]
367
367
368 # Backward-compatibility
368 # Backward-compatibility
369 # - no help entry so that we do not advertise it any more
369 # - no help entry so that we do not advertise it any more
370 def bisected(repo, subset, x):
370 def bisected(repo, subset, x):
371 return bisect(repo, subset, x)
371 return bisect(repo, subset, x)
372
372
373 def bookmark(repo, subset, x):
373 def bookmark(repo, subset, x):
374 """``bookmark([name])``
374 """``bookmark([name])``
375 The named bookmark or all bookmarks.
375 The named bookmark or all bookmarks.
376
376
377 If `name` starts with `re:`, the remainder of the name is treated as
377 If `name` starts with `re:`, the remainder of the name is treated as
378 a regular expression. To match a bookmark that actually starts with `re:`,
378 a regular expression. To match a bookmark that actually starts with `re:`,
379 use the prefix `literal:`.
379 use the prefix `literal:`.
380 """
380 """
381 # i18n: "bookmark" is a keyword
381 # i18n: "bookmark" is a keyword
382 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
382 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
383 if args:
383 if args:
384 bm = getstring(args[0],
384 bm = getstring(args[0],
385 # i18n: "bookmark" is a keyword
385 # i18n: "bookmark" is a keyword
386 _('the argument to bookmark must be a string'))
386 _('the argument to bookmark must be a string'))
387 kind, pattern, matcher = _stringmatcher(bm)
387 kind, pattern, matcher = _stringmatcher(bm)
388 if kind == 'literal':
388 if kind == 'literal':
389 bmrev = repo._bookmarks.get(bm, None)
389 bmrev = repo._bookmarks.get(bm, None)
390 if not bmrev:
390 if not bmrev:
391 raise util.Abort(_("bookmark '%s' does not exist") % bm)
391 raise util.Abort(_("bookmark '%s' does not exist") % bm)
392 bmrev = repo[bmrev].rev()
392 bmrev = repo[bmrev].rev()
393 return [r for r in subset if r == bmrev]
393 return [r for r in subset if r == bmrev]
394 else:
394 else:
395 matchrevs = set()
395 matchrevs = set()
396 for name, bmrev in repo._bookmarks.iteritems():
396 for name, bmrev in repo._bookmarks.iteritems():
397 if matcher(name):
397 if matcher(name):
398 matchrevs.add(bmrev)
398 matchrevs.add(bmrev)
399 if not matchrevs:
399 if not matchrevs:
400 raise util.Abort(_("no bookmarks exist that match '%s'")
400 raise util.Abort(_("no bookmarks exist that match '%s'")
401 % pattern)
401 % pattern)
402 bmrevs = set()
402 bmrevs = set()
403 for bmrev in matchrevs:
403 for bmrev in matchrevs:
404 bmrevs.add(repo[bmrev].rev())
404 bmrevs.add(repo[bmrev].rev())
405 return [r for r in subset if r in bmrevs]
405 return [r for r in subset if r in bmrevs]
406
406
407 bms = set([repo[r].rev()
407 bms = set([repo[r].rev()
408 for r in repo._bookmarks.values()])
408 for r in repo._bookmarks.values()])
409 return [r for r in subset if r in bms]
409 return [r for r in subset if r in bms]
410
410
411 def branch(repo, subset, x):
411 def branch(repo, subset, x):
412 """``branch(string or set)``
412 """``branch(string or set)``
413 All changesets belonging to the given branch or the branches of the given
413 All changesets belonging to the given branch or the branches of the given
414 changesets.
414 changesets.
415
415
416 If `string` starts with `re:`, the remainder of the name is treated as
416 If `string` starts with `re:`, the remainder of the name is treated as
417 a regular expression. To match a branch that actually starts with `re:`,
417 a regular expression. To match a branch that actually starts with `re:`,
418 use the prefix `literal:`.
418 use the prefix `literal:`.
419 """
419 """
420 try:
420 try:
421 b = getstring(x, '')
421 b = getstring(x, '')
422 except error.ParseError:
422 except error.ParseError:
423 # not a string, but another revspec, e.g. tip()
423 # not a string, but another revspec, e.g. tip()
424 pass
424 pass
425 else:
425 else:
426 kind, pattern, matcher = _stringmatcher(b)
426 kind, pattern, matcher = _stringmatcher(b)
427 if kind == 'literal':
427 if kind == 'literal':
428 # note: falls through to the revspec case if no branch with
428 # note: falls through to the revspec case if no branch with
429 # this name exists
429 # this name exists
430 if pattern in repo.branchmap():
430 if pattern in repo.branchmap():
431 return [r for r in subset if matcher(repo[r].branch())]
431 return [r for r in subset if matcher(repo[r].branch())]
432 else:
432 else:
433 return [r for r in subset if matcher(repo[r].branch())]
433 return [r for r in subset if matcher(repo[r].branch())]
434
434
435 s = getset(repo, list(repo), x)
435 s = getset(repo, list(repo), x)
436 b = set()
436 b = set()
437 for r in s:
437 for r in s:
438 b.add(repo[r].branch())
438 b.add(repo[r].branch())
439 s = set(s)
439 s = set(s)
440 return [r for r in subset if r in s or repo[r].branch() in b]
440 return [r for r in subset if r in s or repo[r].branch() in b]
441
441
442 def bumped(repo, subset, x):
442 def bumped(repo, subset, x):
443 """``bumped()``
443 """``bumped()``
444 Mutable changesets marked as successors of public changesets.
444 Mutable changesets marked as successors of public changesets.
445
445
446 Only non-public and non-obsolete changesets can be `bumped`.
446 Only non-public and non-obsolete changesets can be `bumped`.
447 """
447 """
448 # i18n: "bumped" is a keyword
448 # i18n: "bumped" is a keyword
449 getargs(x, 0, 0, _("bumped takes no arguments"))
449 getargs(x, 0, 0, _("bumped takes no arguments"))
450 bumped = obsmod.getrevs(repo, 'bumped')
450 bumped = obsmod.getrevs(repo, 'bumped')
451 return [r for r in subset if r in bumped]
451 return [r for r in subset if r in bumped]
452
452
453 def bundle(repo, subset, x):
453 def bundle(repo, subset, x):
454 """``bundle()``
454 """``bundle()``
455 Changesets in the bundle.
455 Changesets in the bundle.
456
456
457 Bundle must be specified by the -R option."""
457 Bundle must be specified by the -R option."""
458
458
459 try:
459 try:
460 bundlerevs = repo.changelog.bundlerevs
460 bundlerevs = repo.changelog.bundlerevs
461 except AttributeError:
461 except AttributeError:
462 raise util.Abort(_("no bundle provided - specify with -R"))
462 raise util.Abort(_("no bundle provided - specify with -R"))
463 return [r for r in subset if r in bundlerevs]
463 return [r for r in subset if r in bundlerevs]
464
464
465 def checkstatus(repo, subset, pat, field):
465 def checkstatus(repo, subset, pat, field):
466 m = None
466 m = None
467 s = []
467 s = []
468 hasset = matchmod.patkind(pat) == 'set'
468 hasset = matchmod.patkind(pat) == 'set'
469 fname = None
469 fname = None
470 for r in subset:
470 for r in subset:
471 c = repo[r]
471 c = repo[r]
472 if not m or hasset:
472 if not m or hasset:
473 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
473 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
474 if not m.anypats() and len(m.files()) == 1:
474 if not m.anypats() and len(m.files()) == 1:
475 fname = m.files()[0]
475 fname = m.files()[0]
476 if fname is not None:
476 if fname is not None:
477 if fname not in c.files():
477 if fname not in c.files():
478 continue
478 continue
479 else:
479 else:
480 for f in c.files():
480 for f in c.files():
481 if m(f):
481 if m(f):
482 break
482 break
483 else:
483 else:
484 continue
484 continue
485 files = repo.status(c.p1().node(), c.node())[field]
485 files = repo.status(c.p1().node(), c.node())[field]
486 if fname is not None:
486 if fname is not None:
487 if fname in files:
487 if fname in files:
488 s.append(r)
488 s.append(r)
489 else:
489 else:
490 for f in files:
490 for f in files:
491 if m(f):
491 if m(f):
492 s.append(r)
492 s.append(r)
493 break
493 break
494 return s
494 return s
495
495
496 def _children(repo, narrow, parentset):
496 def _children(repo, narrow, parentset):
497 cs = set()
497 cs = set()
498 if not parentset:
498 if not parentset:
499 return cs
499 return cs
500 pr = repo.changelog.parentrevs
500 pr = repo.changelog.parentrevs
501 minrev = min(parentset)
501 minrev = min(parentset)
502 for r in narrow:
502 for r in narrow:
503 if r <= minrev:
503 if r <= minrev:
504 continue
504 continue
505 for p in pr(r):
505 for p in pr(r):
506 if p in parentset:
506 if p in parentset:
507 cs.add(r)
507 cs.add(r)
508 return cs
508 return cs
509
509
510 def children(repo, subset, x):
510 def children(repo, subset, x):
511 """``children(set)``
511 """``children(set)``
512 Child changesets of changesets in set.
512 Child changesets of changesets in set.
513 """
513 """
514 s = set(getset(repo, list(repo), x))
514 s = set(getset(repo, list(repo), x))
515 cs = _children(repo, subset, s)
515 cs = _children(repo, subset, s)
516 return [r for r in subset if r in cs]
516 return [r for r in subset if r in cs]
517
517
518 def closed(repo, subset, x):
518 def closed(repo, subset, x):
519 """``closed()``
519 """``closed()``
520 Changeset is closed.
520 Changeset is closed.
521 """
521 """
522 # i18n: "closed" is a keyword
522 # i18n: "closed" is a keyword
523 getargs(x, 0, 0, _("closed takes no arguments"))
523 getargs(x, 0, 0, _("closed takes no arguments"))
524 return [r for r in subset if repo[r].closesbranch()]
524 return [r for r in subset if repo[r].closesbranch()]
525
525
526 def contains(repo, subset, x):
526 def contains(repo, subset, x):
527 """``contains(pattern)``
527 """``contains(pattern)``
528 Revision contains a file matching pattern. See :hg:`help patterns`
528 Revision contains a file matching pattern. See :hg:`help patterns`
529 for information about file patterns.
529 for information about file patterns.
530 """
530 """
531 # i18n: "contains" is a keyword
531 # i18n: "contains" is a keyword
532 pat = getstring(x, _("contains requires a pattern"))
532 pat = getstring(x, _("contains requires a pattern"))
533 m = None
533 m = None
534 s = []
534 s = []
535 if not matchmod.patkind(pat):
535 if not matchmod.patkind(pat):
536 for r in subset:
536 for r in subset:
537 if pat in repo[r]:
537 if pat in repo[r]:
538 s.append(r)
538 s.append(r)
539 else:
539 else:
540 for r in subset:
540 for r in subset:
541 c = repo[r]
541 c = repo[r]
542 if not m or matchmod.patkind(pat) == 'set':
542 if not m or matchmod.patkind(pat) == 'set':
543 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
543 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
544 for f in c.manifest():
544 for f in c.manifest():
545 if m(f):
545 if m(f):
546 s.append(r)
546 s.append(r)
547 break
547 break
548 return s
548 return s
549
549
550 def converted(repo, subset, x):
550 def converted(repo, subset, x):
551 """``converted([id])``
551 """``converted([id])``
552 Changesets converted from the given identifier in the old repository if
552 Changesets converted from the given identifier in the old repository if
553 present, or all converted changesets if no identifier is specified.
553 present, or all converted changesets if no identifier is specified.
554 """
554 """
555
555
556 # There is exactly no chance of resolving the revision, so do a simple
556 # There is exactly no chance of resolving the revision, so do a simple
557 # string compare and hope for the best
557 # string compare and hope for the best
558
558
559 rev = None
559 rev = None
560 # i18n: "converted" is a keyword
560 # i18n: "converted" is a keyword
561 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
561 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
562 if l:
562 if l:
563 # i18n: "converted" is a keyword
563 # i18n: "converted" is a keyword
564 rev = getstring(l[0], _('converted requires a revision'))
564 rev = getstring(l[0], _('converted requires a revision'))
565
565
566 def _matchvalue(r):
566 def _matchvalue(r):
567 source = repo[r].extra().get('convert_revision', None)
567 source = repo[r].extra().get('convert_revision', None)
568 return source is not None and (rev is None or source.startswith(rev))
568 return source is not None and (rev is None or source.startswith(rev))
569
569
570 return [r for r in subset if _matchvalue(r)]
570 return [r for r in subset if _matchvalue(r)]
571
571
572 def date(repo, subset, x):
572 def date(repo, subset, x):
573 """``date(interval)``
573 """``date(interval)``
574 Changesets within the interval, see :hg:`help dates`.
574 Changesets within the interval, see :hg:`help dates`.
575 """
575 """
576 # i18n: "date" is a keyword
576 # i18n: "date" is a keyword
577 ds = getstring(x, _("date requires a string"))
577 ds = getstring(x, _("date requires a string"))
578 dm = util.matchdate(ds)
578 dm = util.matchdate(ds)
579 return [r for r in subset if dm(repo[r].date()[0])]
579 return [r for r in subset if dm(repo[r].date()[0])]
580
580
581 def desc(repo, subset, x):
581 def desc(repo, subset, x):
582 """``desc(string)``
582 """``desc(string)``
583 Search commit message for string. The match is case-insensitive.
583 Search commit message for string. The match is case-insensitive.
584 """
584 """
585 # i18n: "desc" is a keyword
585 # i18n: "desc" is a keyword
586 ds = encoding.lower(getstring(x, _("desc requires a string")))
586 ds = encoding.lower(getstring(x, _("desc requires a string")))
587 l = []
587 l = []
588 for r in subset:
588 for r in subset:
589 c = repo[r]
589 c = repo[r]
590 if ds in encoding.lower(c.description()):
590 if ds in encoding.lower(c.description()):
591 l.append(r)
591 l.append(r)
592 return l
592 return l
593
593
594 def _descendants(repo, subset, x, followfirst=False):
594 def _descendants(repo, subset, x, followfirst=False):
595 args = getset(repo, list(repo), x)
595 args = getset(repo, list(repo), x)
596 if not args:
596 if not args:
597 return []
597 return []
598 s = set(_revdescendants(repo, args, followfirst)) | set(args)
598 s = set(_revdescendants(repo, args, followfirst)) | set(args)
599 return [r for r in subset if r in s]
599 return [r for r in subset if r in s]
600
600
601 def descendants(repo, subset, x):
601 def descendants(repo, subset, x):
602 """``descendants(set)``
602 """``descendants(set)``
603 Changesets which are descendants of changesets in set.
603 Changesets which are descendants of changesets in set.
604 """
604 """
605 return _descendants(repo, subset, x)
605 return _descendants(repo, subset, x)
606
606
607 def _firstdescendants(repo, subset, x):
607 def _firstdescendants(repo, subset, x):
608 # ``_firstdescendants(set)``
608 # ``_firstdescendants(set)``
609 # Like ``descendants(set)`` but follows only the first parents.
609 # Like ``descendants(set)`` but follows only the first parents.
610 return _descendants(repo, subset, x, followfirst=True)
610 return _descendants(repo, subset, x, followfirst=True)
611
611
612 def destination(repo, subset, x):
612 def destination(repo, subset, x):
613 """``destination([set])``
613 """``destination([set])``
614 Changesets that were created by a graft, transplant or rebase operation,
614 Changesets that were created by a graft, transplant or rebase operation,
615 with the given revisions specified as the source. Omitting the optional set
615 with the given revisions specified as the source. Omitting the optional set
616 is the same as passing all().
616 is the same as passing all().
617 """
617 """
618 if x is not None:
618 if x is not None:
619 args = set(getset(repo, list(repo), x))
619 args = set(getset(repo, list(repo), x))
620 else:
620 else:
621 args = set(getall(repo, list(repo), x))
621 args = set(getall(repo, list(repo), x))
622
622
623 dests = set()
623 dests = set()
624
624
625 # subset contains all of the possible destinations that can be returned, so
625 # subset contains all of the possible destinations that can be returned, so
626 # iterate over them and see if their source(s) were provided in the args.
626 # iterate over them and see if their source(s) were provided in the args.
627 # Even if the immediate src of r is not in the args, src's source (or
627 # Even if the immediate src of r is not in the args, src's source (or
628 # further back) may be. Scanning back further than the immediate src allows
628 # further back) may be. Scanning back further than the immediate src allows
629 # transitive transplants and rebases to yield the same results as transitive
629 # transitive transplants and rebases to yield the same results as transitive
630 # grafts.
630 # grafts.
631 for r in subset:
631 for r in subset:
632 src = _getrevsource(repo, r)
632 src = _getrevsource(repo, r)
633 lineage = None
633 lineage = None
634
634
635 while src is not None:
635 while src is not None:
636 if lineage is None:
636 if lineage is None:
637 lineage = list()
637 lineage = list()
638
638
639 lineage.append(r)
639 lineage.append(r)
640
640
641 # The visited lineage is a match if the current source is in the arg
641 # The visited lineage is a match if the current source is in the arg
642 # set. Since every candidate dest is visited by way of iterating
642 # set. Since every candidate dest is visited by way of iterating
643 # subset, any dests further back in the lineage will be tested by a
643 # subset, any dests further back in the lineage will be tested by a
644 # different iteration over subset. Likewise, if the src was already
644 # different iteration over subset. Likewise, if the src was already
645 # selected, the current lineage can be selected without going back
645 # selected, the current lineage can be selected without going back
646 # further.
646 # further.
647 if src in args or src in dests:
647 if src in args or src in dests:
648 dests.update(lineage)
648 dests.update(lineage)
649 break
649 break
650
650
651 r = src
651 r = src
652 src = _getrevsource(repo, r)
652 src = _getrevsource(repo, r)
653
653
654 return [r for r in subset if r in dests]
654 return [r for r in subset if r in dests]
655
655
656 def divergent(repo, subset, x):
656 def divergent(repo, subset, x):
657 """``divergent()``
657 """``divergent()``
658 Final successors of changesets with an alternative set of final successors.
658 Final successors of changesets with an alternative set of final successors.
659 """
659 """
660 # i18n: "divergent" is a keyword
660 # i18n: "divergent" is a keyword
661 getargs(x, 0, 0, _("divergent takes no arguments"))
661 getargs(x, 0, 0, _("divergent takes no arguments"))
662 divergent = obsmod.getrevs(repo, 'divergent')
662 divergent = obsmod.getrevs(repo, 'divergent')
663 return [r for r in subset if r in divergent]
663 return [r for r in subset if r in divergent]
664
664
665 def draft(repo, subset, x):
665 def draft(repo, subset, x):
666 """``draft()``
666 """``draft()``
667 Changeset in draft phase."""
667 Changeset in draft phase."""
668 # i18n: "draft" is a keyword
668 # i18n: "draft" is a keyword
669 getargs(x, 0, 0, _("draft takes no arguments"))
669 getargs(x, 0, 0, _("draft takes no arguments"))
670 pc = repo._phasecache
670 pc = repo._phasecache
671 return [r for r in subset if pc.phase(repo, r) == phases.draft]
671 return [r for r in subset if pc.phase(repo, r) == phases.draft]
672
672
673 def extinct(repo, subset, x):
673 def extinct(repo, subset, x):
674 """``extinct()``
674 """``extinct()``
675 Obsolete changesets with obsolete descendants only.
675 Obsolete changesets with obsolete descendants only.
676 """
676 """
677 # i18n: "extinct" is a keyword
677 # i18n: "extinct" is a keyword
678 getargs(x, 0, 0, _("extinct takes no arguments"))
678 getargs(x, 0, 0, _("extinct takes no arguments"))
679 extincts = obsmod.getrevs(repo, 'extinct')
679 extincts = obsmod.getrevs(repo, 'extinct')
680 return [r for r in subset if r in extincts]
680 return [r for r in subset if r in extincts]
681
681
682 def extra(repo, subset, x):
682 def extra(repo, subset, x):
683 """``extra(label, [value])``
683 """``extra(label, [value])``
684 Changesets with the given label in the extra metadata, with the given
684 Changesets with the given label in the extra metadata, with the given
685 optional value.
685 optional value.
686
686
687 If `value` starts with `re:`, the remainder of the value is treated as
687 If `value` starts with `re:`, the remainder of the value is treated as
688 a regular expression. To match a value that actually starts with `re:`,
688 a regular expression. To match a value that actually starts with `re:`,
689 use the prefix `literal:`.
689 use the prefix `literal:`.
690 """
690 """
691
691
692 # i18n: "extra" is a keyword
692 # i18n: "extra" is a keyword
693 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
693 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
694 # i18n: "extra" is a keyword
694 # i18n: "extra" is a keyword
695 label = getstring(l[0], _('first argument to extra must be a string'))
695 label = getstring(l[0], _('first argument to extra must be a string'))
696 value = None
696 value = None
697
697
698 if len(l) > 1:
698 if len(l) > 1:
699 # i18n: "extra" is a keyword
699 # i18n: "extra" is a keyword
700 value = getstring(l[1], _('second argument to extra must be a string'))
700 value = getstring(l[1], _('second argument to extra must be a string'))
701 kind, value, matcher = _stringmatcher(value)
701 kind, value, matcher = _stringmatcher(value)
702
702
703 def _matchvalue(r):
703 def _matchvalue(r):
704 extra = repo[r].extra()
704 extra = repo[r].extra()
705 return label in extra and (value is None or matcher(extra[label]))
705 return label in extra and (value is None or matcher(extra[label]))
706
706
707 return [r for r in subset if _matchvalue(r)]
707 return [r for r in subset if _matchvalue(r)]
708
708
709 def filelog(repo, subset, x):
709 def filelog(repo, subset, x):
710 """``filelog(pattern)``
710 """``filelog(pattern)``
711 Changesets connected to the specified filelog.
711 Changesets connected to the specified filelog.
712
712
713 For performance reasons, ``filelog()`` does not show every changeset
713 For performance reasons, ``filelog()`` does not show every changeset
714 that affects the requested file(s). See :hg:`help log` for details. For
714 that affects the requested file(s). See :hg:`help log` for details. For
715 a slower, more accurate result, use ``file()``.
715 a slower, more accurate result, use ``file()``.
716 """
716 """
717
717
718 # i18n: "filelog" is a keyword
718 # i18n: "filelog" is a keyword
719 pat = getstring(x, _("filelog requires a pattern"))
719 pat = getstring(x, _("filelog requires a pattern"))
720 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath',
720 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath',
721 ctx=repo[None])
721 ctx=repo[None])
722 s = set()
722 s = set()
723
723
724 if not matchmod.patkind(pat):
724 if not matchmod.patkind(pat):
725 for f in m.files():
725 for f in m.files():
726 fl = repo.file(f)
726 fl = repo.file(f)
727 for fr in fl:
727 for fr in fl:
728 s.add(fl.linkrev(fr))
728 s.add(fl.linkrev(fr))
729 else:
729 else:
730 for f in repo[None]:
730 for f in repo[None]:
731 if m(f):
731 if m(f):
732 fl = repo.file(f)
732 fl = repo.file(f)
733 for fr in fl:
733 for fr in fl:
734 s.add(fl.linkrev(fr))
734 s.add(fl.linkrev(fr))
735
735
736 return [r for r in subset if r in s]
736 return [r for r in subset if r in s]
737
737
738 def first(repo, subset, x):
738 def first(repo, subset, x):
739 """``first(set, [n])``
739 """``first(set, [n])``
740 An alias for limit().
740 An alias for limit().
741 """
741 """
742 return limit(repo, subset, x)
742 return limit(repo, subset, x)
743
743
744 def _follow(repo, subset, x, name, followfirst=False):
744 def _follow(repo, subset, x, name, followfirst=False):
745 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
745 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
746 c = repo['.']
746 c = repo['.']
747 if l:
747 if l:
748 x = getstring(l[0], _("%s expected a filename") % name)
748 x = getstring(l[0], _("%s expected a filename") % name)
749 if x in c:
749 if x in c:
750 cx = c[x]
750 cx = c[x]
751 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
751 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
752 # include the revision responsible for the most recent version
752 # include the revision responsible for the most recent version
753 s.add(cx.linkrev())
753 s.add(cx.linkrev())
754 else:
754 else:
755 return []
755 return []
756 else:
756 else:
757 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
757 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
758
758
759 return [r for r in subset if r in s]
759 return [r for r in subset if r in s]
760
760
761 def follow(repo, subset, x):
761 def follow(repo, subset, x):
762 """``follow([file])``
762 """``follow([file])``
763 An alias for ``::.`` (ancestors of the working copy's first parent).
763 An alias for ``::.`` (ancestors of the working copy's first parent).
764 If a filename is specified, the history of the given file is followed,
764 If a filename is specified, the history of the given file is followed,
765 including copies.
765 including copies.
766 """
766 """
767 return _follow(repo, subset, x, 'follow')
767 return _follow(repo, subset, x, 'follow')
768
768
769 def _followfirst(repo, subset, x):
769 def _followfirst(repo, subset, x):
770 # ``followfirst([file])``
770 # ``followfirst([file])``
771 # Like ``follow([file])`` but follows only the first parent of
771 # Like ``follow([file])`` but follows only the first parent of
772 # every revision or file revision.
772 # every revision or file revision.
773 return _follow(repo, subset, x, '_followfirst', followfirst=True)
773 return _follow(repo, subset, x, '_followfirst', followfirst=True)
774
774
775 def getall(repo, subset, x):
775 def getall(repo, subset, x):
776 """``all()``
776 """``all()``
777 All changesets, the same as ``0:tip``.
777 All changesets, the same as ``0:tip``.
778 """
778 """
779 # i18n: "all" is a keyword
779 # i18n: "all" is a keyword
780 getargs(x, 0, 0, _("all takes no arguments"))
780 getargs(x, 0, 0, _("all takes no arguments"))
781 return subset
781 return subset
782
782
783 def grep(repo, subset, x):
783 def grep(repo, subset, x):
784 """``grep(regex)``
784 """``grep(regex)``
785 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
785 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
786 to ensure special escape characters are handled correctly. Unlike
786 to ensure special escape characters are handled correctly. Unlike
787 ``keyword(string)``, the match is case-sensitive.
787 ``keyword(string)``, the match is case-sensitive.
788 """
788 """
789 try:
789 try:
790 # i18n: "grep" is a keyword
790 # i18n: "grep" is a keyword
791 gr = re.compile(getstring(x, _("grep requires a string")))
791 gr = re.compile(getstring(x, _("grep requires a string")))
792 except re.error, e:
792 except re.error, e:
793 raise error.ParseError(_('invalid match pattern: %s') % e)
793 raise error.ParseError(_('invalid match pattern: %s') % e)
794 l = []
794 l = []
795 for r in subset:
795 for r in subset:
796 c = repo[r]
796 c = repo[r]
797 for e in c.files() + [c.user(), c.description()]:
797 for e in c.files() + [c.user(), c.description()]:
798 if gr.search(e):
798 if gr.search(e):
799 l.append(r)
799 l.append(r)
800 break
800 break
801 return l
801 return l
802
802
803 def _matchfiles(repo, subset, x):
803 def _matchfiles(repo, subset, x):
804 # _matchfiles takes a revset list of prefixed arguments:
804 # _matchfiles takes a revset list of prefixed arguments:
805 #
805 #
806 # [p:foo, i:bar, x:baz]
806 # [p:foo, i:bar, x:baz]
807 #
807 #
808 # builds a match object from them and filters subset. Allowed
808 # builds a match object from them and filters subset. Allowed
809 # prefixes are 'p:' for regular patterns, 'i:' for include
809 # prefixes are 'p:' for regular patterns, 'i:' for include
810 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
810 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
811 # a revision identifier, or the empty string to reference the
811 # a revision identifier, or the empty string to reference the
812 # working directory, from which the match object is
812 # working directory, from which the match object is
813 # initialized. Use 'd:' to set the default matching mode, default
813 # initialized. Use 'd:' to set the default matching mode, default
814 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
814 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
815
815
816 # i18n: "_matchfiles" is a keyword
816 # i18n: "_matchfiles" is a keyword
817 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
817 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
818 pats, inc, exc = [], [], []
818 pats, inc, exc = [], [], []
819 hasset = False
819 hasset = False
820 rev, default = None, None
820 rev, default = None, None
821 for arg in l:
821 for arg in l:
822 # i18n: "_matchfiles" is a keyword
822 # i18n: "_matchfiles" is a keyword
823 s = getstring(arg, _("_matchfiles requires string arguments"))
823 s = getstring(arg, _("_matchfiles requires string arguments"))
824 prefix, value = s[:2], s[2:]
824 prefix, value = s[:2], s[2:]
825 if prefix == 'p:':
825 if prefix == 'p:':
826 pats.append(value)
826 pats.append(value)
827 elif prefix == 'i:':
827 elif prefix == 'i:':
828 inc.append(value)
828 inc.append(value)
829 elif prefix == 'x:':
829 elif prefix == 'x:':
830 exc.append(value)
830 exc.append(value)
831 elif prefix == 'r:':
831 elif prefix == 'r:':
832 if rev is not None:
832 if rev is not None:
833 # i18n: "_matchfiles" is a keyword
833 # i18n: "_matchfiles" is a keyword
834 raise error.ParseError(_('_matchfiles expected at most one '
834 raise error.ParseError(_('_matchfiles expected at most one '
835 'revision'))
835 'revision'))
836 rev = value
836 rev = value
837 elif prefix == 'd:':
837 elif prefix == 'd:':
838 if default is not None:
838 if default is not None:
839 # i18n: "_matchfiles" is a keyword
839 # i18n: "_matchfiles" is a keyword
840 raise error.ParseError(_('_matchfiles expected at most one '
840 raise error.ParseError(_('_matchfiles expected at most one '
841 'default mode'))
841 'default mode'))
842 default = value
842 default = value
843 else:
843 else:
844 # i18n: "_matchfiles" is a keyword
844 # i18n: "_matchfiles" is a keyword
845 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
845 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
846 if not hasset and matchmod.patkind(value) == 'set':
846 if not hasset and matchmod.patkind(value) == 'set':
847 hasset = True
847 hasset = True
848 if not default:
848 if not default:
849 default = 'glob'
849 default = 'glob'
850 m = None
850 m = None
851 s = []
851 s = []
852 for r in subset:
852 for r in subset:
853 c = repo[r]
853 c = repo[r]
854 if not m or (hasset and rev is None):
854 if not m or (hasset and rev is None):
855 ctx = c
855 ctx = c
856 if rev is not None:
856 if rev is not None:
857 ctx = repo[rev or None]
857 ctx = repo[rev or None]
858 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
858 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
859 exclude=exc, ctx=ctx, default=default)
859 exclude=exc, ctx=ctx, default=default)
860 for f in c.files():
860 for f in c.files():
861 if m(f):
861 if m(f):
862 s.append(r)
862 s.append(r)
863 break
863 break
864 return s
864 return s
865
865
866 def hasfile(repo, subset, x):
866 def hasfile(repo, subset, x):
867 """``file(pattern)``
867 """``file(pattern)``
868 Changesets affecting files matched by pattern.
868 Changesets affecting files matched by pattern.
869
869
870 For a faster but less accurate result, consider using ``filelog()``
870 For a faster but less accurate result, consider using ``filelog()``
871 instead.
871 instead.
872 """
872 """
873 # i18n: "file" is a keyword
873 # i18n: "file" is a keyword
874 pat = getstring(x, _("file requires a pattern"))
874 pat = getstring(x, _("file requires a pattern"))
875 return _matchfiles(repo, subset, ('string', 'p:' + pat))
875 return _matchfiles(repo, subset, ('string', 'p:' + pat))
876
876
877 def head(repo, subset, x):
877 def head(repo, subset, x):
878 """``head()``
878 """``head()``
879 Changeset is a named branch head.
879 Changeset is a named branch head.
880 """
880 """
881 # i18n: "head" is a keyword
881 # i18n: "head" is a keyword
882 getargs(x, 0, 0, _("head takes no arguments"))
882 getargs(x, 0, 0, _("head takes no arguments"))
883 hs = set()
883 hs = set()
884 for b, ls in repo.branchmap().iteritems():
884 for b, ls in repo.branchmap().iteritems():
885 hs.update(repo[h].rev() for h in ls)
885 hs.update(repo[h].rev() for h in ls)
886 return [r for r in subset if r in hs]
886 return [r for r in subset if r in hs]
887
887
888 def heads(repo, subset, x):
888 def heads(repo, subset, x):
889 """``heads(set)``
889 """``heads(set)``
890 Members of set with no children in set.
890 Members of set with no children in set.
891 """
891 """
892 s = getset(repo, subset, x)
892 s = getset(repo, subset, x)
893 ps = set(parents(repo, subset, x))
893 ps = set(parents(repo, subset, x))
894 return [r for r in s if r not in ps]
894 return [r for r in s if r not in ps]
895
895
896 def hidden(repo, subset, x):
896 def hidden(repo, subset, x):
897 """``hidden()``
897 """``hidden()``
898 Hidden changesets.
898 Hidden changesets.
899 """
899 """
900 # i18n: "hidden" is a keyword
900 # i18n: "hidden" is a keyword
901 getargs(x, 0, 0, _("hidden takes no arguments"))
901 getargs(x, 0, 0, _("hidden takes no arguments"))
902 hiddenrevs = repoview.filterrevs(repo, 'visible')
902 hiddenrevs = repoview.filterrevs(repo, 'visible')
903 return [r for r in subset if r in hiddenrevs]
903 return [r for r in subset if r in hiddenrevs]
904
904
905 def keyword(repo, subset, x):
905 def keyword(repo, subset, x):
906 """``keyword(string)``
906 """``keyword(string)``
907 Search commit message, user name, and names of changed files for
907 Search commit message, user name, and names of changed files for
908 string. The match is case-insensitive.
908 string. The match is case-insensitive.
909 """
909 """
910 # i18n: "keyword" is a keyword
910 # i18n: "keyword" is a keyword
911 kw = encoding.lower(getstring(x, _("keyword requires a string")))
911 kw = encoding.lower(getstring(x, _("keyword requires a string")))
912 l = []
912 l = []
913 for r in subset:
913 for r in subset:
914 c = repo[r]
914 c = repo[r]
915 t = " ".join(c.files() + [c.user(), c.description()])
915 t = " ".join(c.files() + [c.user(), c.description()])
916 if kw in encoding.lower(t):
916 if kw in encoding.lower(t):
917 l.append(r)
917 l.append(r)
918 return l
918 return l
919
919
920 def limit(repo, subset, x):
920 def limit(repo, subset, x):
921 """``limit(set, [n])``
921 """``limit(set, [n])``
922 First n members of set, defaulting to 1.
922 First n members of set, defaulting to 1.
923 """
923 """
924 # i18n: "limit" is a keyword
924 # i18n: "limit" is a keyword
925 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
925 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
926 try:
926 try:
927 lim = 1
927 lim = 1
928 if len(l) == 2:
928 if len(l) == 2:
929 # i18n: "limit" is a keyword
929 # i18n: "limit" is a keyword
930 lim = int(getstring(l[1], _("limit requires a number")))
930 lim = int(getstring(l[1], _("limit requires a number")))
931 except (TypeError, ValueError):
931 except (TypeError, ValueError):
932 # i18n: "limit" is a keyword
932 # i18n: "limit" is a keyword
933 raise error.ParseError(_("limit expects a number"))
933 raise error.ParseError(_("limit expects a number"))
934 ss = set(subset)
934 ss = set(subset)
935 os = getset(repo, list(repo), l[0])[:lim]
935 os = getset(repo, list(repo), l[0])[:lim]
936 return [r for r in os if r in ss]
936 return [r for r in os if r in ss]
937
937
938 def last(repo, subset, x):
938 def last(repo, subset, x):
939 """``last(set, [n])``
939 """``last(set, [n])``
940 Last n members of set, defaulting to 1.
940 Last n members of set, defaulting to 1.
941 """
941 """
942 # i18n: "last" is a keyword
942 # i18n: "last" is a keyword
943 l = getargs(x, 1, 2, _("last requires one or two arguments"))
943 l = getargs(x, 1, 2, _("last requires one or two arguments"))
944 try:
944 try:
945 lim = 1
945 lim = 1
946 if len(l) == 2:
946 if len(l) == 2:
947 # i18n: "last" is a keyword
947 # i18n: "last" is a keyword
948 lim = int(getstring(l[1], _("last requires a number")))
948 lim = int(getstring(l[1], _("last requires a number")))
949 except (TypeError, ValueError):
949 except (TypeError, ValueError):
950 # i18n: "last" is a keyword
950 # i18n: "last" is a keyword
951 raise error.ParseError(_("last expects a number"))
951 raise error.ParseError(_("last expects a number"))
952 ss = set(subset)
952 ss = set(subset)
953 os = getset(repo, list(repo), l[0])[-lim:]
953 os = getset(repo, list(repo), l[0])[-lim:]
954 return [r for r in os if r in ss]
954 return [r for r in os if r in ss]
955
955
956 def maxrev(repo, subset, x):
956 def maxrev(repo, subset, x):
957 """``max(set)``
957 """``max(set)``
958 Changeset with highest revision number in set.
958 Changeset with highest revision number in set.
959 """
959 """
960 os = getset(repo, list(repo), x)
960 os = getset(repo, list(repo), x)
961 if os:
961 if os:
962 m = max(os)
962 m = max(os)
963 if m in subset:
963 if m in subset:
964 return [m]
964 return [m]
965 return []
965 return []
966
966
967 def merge(repo, subset, x):
967 def merge(repo, subset, x):
968 """``merge()``
968 """``merge()``
969 Changeset is a merge changeset.
969 Changeset is a merge changeset.
970 """
970 """
971 # i18n: "merge" is a keyword
971 # i18n: "merge" is a keyword
972 getargs(x, 0, 0, _("merge takes no arguments"))
972 getargs(x, 0, 0, _("merge takes no arguments"))
973 cl = repo.changelog
973 cl = repo.changelog
974 return [r for r in subset if cl.parentrevs(r)[1] != -1]
974 return [r for r in subset if cl.parentrevs(r)[1] != -1]
975
975
976 def branchpoint(repo, subset, x):
976 def branchpoint(repo, subset, x):
977 """``branchpoint()``
977 """``branchpoint()``
978 Changesets with more than one child.
978 Changesets with more than one child.
979 """
979 """
980 # i18n: "branchpoint" is a keyword
980 # i18n: "branchpoint" is a keyword
981 getargs(x, 0, 0, _("branchpoint takes no arguments"))
981 getargs(x, 0, 0, _("branchpoint takes no arguments"))
982 cl = repo.changelog
982 cl = repo.changelog
983 if not subset:
983 if not subset:
984 return []
984 return []
985 baserev = min(subset)
985 baserev = min(subset)
986 parentscount = [0]*(len(repo) - baserev)
986 parentscount = [0]*(len(repo) - baserev)
987 for r in cl.revs(start=baserev + 1):
987 for r in cl.revs(start=baserev + 1):
988 for p in cl.parentrevs(r):
988 for p in cl.parentrevs(r):
989 if p >= baserev:
989 if p >= baserev:
990 parentscount[p - baserev] += 1
990 parentscount[p - baserev] += 1
991 return [r for r in subset if (parentscount[r - baserev] > 1)]
991 return [r for r in subset if (parentscount[r - baserev] > 1)]
992
992
993 def minrev(repo, subset, x):
993 def minrev(repo, subset, x):
994 """``min(set)``
994 """``min(set)``
995 Changeset with lowest revision number in set.
995 Changeset with lowest revision number in set.
996 """
996 """
997 os = getset(repo, list(repo), x)
997 os = getset(repo, list(repo), x)
998 if os:
998 if os:
999 m = min(os)
999 m = min(os)
1000 if m in subset:
1000 if m in subset:
1001 return [m]
1001 return [m]
1002 return []
1002 return []
1003
1003
1004 def modifies(repo, subset, x):
1004 def modifies(repo, subset, x):
1005 """``modifies(pattern)``
1005 """``modifies(pattern)``
1006 Changesets modifying files matched by pattern.
1006 Changesets modifying files matched by pattern.
1007 """
1007 """
1008 # i18n: "modifies" is a keyword
1008 # i18n: "modifies" is a keyword
1009 pat = getstring(x, _("modifies requires a pattern"))
1009 pat = getstring(x, _("modifies requires a pattern"))
1010 return checkstatus(repo, subset, pat, 0)
1010 return checkstatus(repo, subset, pat, 0)
1011
1011
1012 def node_(repo, subset, x):
1012 def node_(repo, subset, x):
1013 """``id(string)``
1013 """``id(string)``
1014 Revision non-ambiguously specified by the given hex string prefix.
1014 Revision non-ambiguously specified by the given hex string prefix.
1015 """
1015 """
1016 # i18n: "id" is a keyword
1016 # i18n: "id" is a keyword
1017 l = getargs(x, 1, 1, _("id requires one argument"))
1017 l = getargs(x, 1, 1, _("id requires one argument"))
1018 # i18n: "id" is a keyword
1018 # i18n: "id" is a keyword
1019 n = getstring(l[0], _("id requires a string"))
1019 n = getstring(l[0], _("id requires a string"))
1020 if len(n) == 40:
1020 if len(n) == 40:
1021 rn = repo[n].rev()
1021 rn = repo[n].rev()
1022 else:
1022 else:
1023 rn = None
1023 rn = None
1024 pm = repo.changelog._partialmatch(n)
1024 pm = repo.changelog._partialmatch(n)
1025 if pm is not None:
1025 if pm is not None:
1026 rn = repo.changelog.rev(pm)
1026 rn = repo.changelog.rev(pm)
1027
1027
1028 return [r for r in subset if r == rn]
1028 return [r for r in subset if r == rn]
1029
1029
1030 def obsolete(repo, subset, x):
1030 def obsolete(repo, subset, x):
1031 """``obsolete()``
1031 """``obsolete()``
1032 Mutable changeset with a newer version."""
1032 Mutable changeset with a newer version."""
1033 # i18n: "obsolete" is a keyword
1033 # i18n: "obsolete" is a keyword
1034 getargs(x, 0, 0, _("obsolete takes no arguments"))
1034 getargs(x, 0, 0, _("obsolete takes no arguments"))
1035 obsoletes = obsmod.getrevs(repo, 'obsolete')
1035 obsoletes = obsmod.getrevs(repo, 'obsolete')
1036 return [r for r in subset if r in obsoletes]
1036 return [r for r in subset if r in obsoletes]
1037
1037
1038 def origin(repo, subset, x):
1038 def origin(repo, subset, x):
1039 """``origin([set])``
1039 """``origin([set])``
1040 Changesets that were specified as a source for the grafts, transplants or
1040 Changesets that were specified as a source for the grafts, transplants or
1041 rebases that created the given revisions. Omitting the optional set is the
1041 rebases that created the given revisions. Omitting the optional set is the
1042 same as passing all(). If a changeset created by these operations is itself
1042 same as passing all(). If a changeset created by these operations is itself
1043 specified as a source for one of these operations, only the source changeset
1043 specified as a source for one of these operations, only the source changeset
1044 for the first operation is selected.
1044 for the first operation is selected.
1045 """
1045 """
1046 if x is not None:
1046 if x is not None:
1047 args = set(getset(repo, list(repo), x))
1047 args = set(getset(repo, list(repo), x))
1048 else:
1048 else:
1049 args = set(getall(repo, list(repo), x))
1049 args = set(getall(repo, list(repo), x))
1050
1050
1051 def _firstsrc(rev):
1051 def _firstsrc(rev):
1052 src = _getrevsource(repo, rev)
1052 src = _getrevsource(repo, rev)
1053 if src is None:
1053 if src is None:
1054 return None
1054 return None
1055
1055
1056 while True:
1056 while True:
1057 prev = _getrevsource(repo, src)
1057 prev = _getrevsource(repo, src)
1058
1058
1059 if prev is None:
1059 if prev is None:
1060 return src
1060 return src
1061 src = prev
1061 src = prev
1062
1062
1063 o = set([_firstsrc(r) for r in args])
1063 o = set([_firstsrc(r) for r in args])
1064 return [r for r in subset if r in o]
1064 return [r for r in subset if r in o]
1065
1065
1066 def outgoing(repo, subset, x):
1066 def outgoing(repo, subset, x):
1067 """``outgoing([path])``
1067 """``outgoing([path])``
1068 Changesets not found in the specified destination repository, or the
1068 Changesets not found in the specified destination repository, or the
1069 default push location.
1069 default push location.
1070 """
1070 """
1071 import hg # avoid start-up nasties
1071 import hg # avoid start-up nasties
1072 # i18n: "outgoing" is a keyword
1072 # i18n: "outgoing" is a keyword
1073 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1073 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1074 # i18n: "outgoing" is a keyword
1074 # i18n: "outgoing" is a keyword
1075 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1075 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1076 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1076 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1077 dest, branches = hg.parseurl(dest)
1077 dest, branches = hg.parseurl(dest)
1078 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1078 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1079 if revs:
1079 if revs:
1080 revs = [repo.lookup(rev) for rev in revs]
1080 revs = [repo.lookup(rev) for rev in revs]
1081 other = hg.peer(repo, {}, dest)
1081 other = hg.peer(repo, {}, dest)
1082 repo.ui.pushbuffer()
1082 repo.ui.pushbuffer()
1083 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1083 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1084 repo.ui.popbuffer()
1084 repo.ui.popbuffer()
1085 cl = repo.changelog
1085 cl = repo.changelog
1086 o = set([cl.rev(r) for r in outgoing.missing])
1086 o = set([cl.rev(r) for r in outgoing.missing])
1087 return [r for r in subset if r in o]
1087 return [r for r in subset if r in o]
1088
1088
1089 def p1(repo, subset, x):
1089 def p1(repo, subset, x):
1090 """``p1([set])``
1090 """``p1([set])``
1091 First parent of changesets in set, or the working directory.
1091 First parent of changesets in set, or the working directory.
1092 """
1092 """
1093 if x is None:
1093 if x is None:
1094 p = repo[x].p1().rev()
1094 p = repo[x].p1().rev()
1095 return [r for r in subset if r == p]
1095 return [r for r in subset if r == p]
1096
1096
1097 ps = set()
1097 ps = set()
1098 cl = repo.changelog
1098 cl = repo.changelog
1099 for r in getset(repo, list(repo), x):
1099 for r in getset(repo, list(repo), x):
1100 ps.add(cl.parentrevs(r)[0])
1100 ps.add(cl.parentrevs(r)[0])
1101 return [r for r in subset if r in ps]
1101 return [r for r in subset if r in ps]
1102
1102
1103 def p2(repo, subset, x):
1103 def p2(repo, subset, x):
1104 """``p2([set])``
1104 """``p2([set])``
1105 Second parent of changesets in set, or the working directory.
1105 Second parent of changesets in set, or the working directory.
1106 """
1106 """
1107 if x is None:
1107 if x is None:
1108 ps = repo[x].parents()
1108 ps = repo[x].parents()
1109 try:
1109 try:
1110 p = ps[1].rev()
1110 p = ps[1].rev()
1111 return [r for r in subset if r == p]
1111 return [r for r in subset if r == p]
1112 except IndexError:
1112 except IndexError:
1113 return []
1113 return []
1114
1114
1115 ps = set()
1115 ps = set()
1116 cl = repo.changelog
1116 cl = repo.changelog
1117 for r in getset(repo, list(repo), x):
1117 for r in getset(repo, list(repo), x):
1118 ps.add(cl.parentrevs(r)[1])
1118 ps.add(cl.parentrevs(r)[1])
1119 return [r for r in subset if r in ps]
1119 return [r for r in subset if r in ps]
1120
1120
1121 def parents(repo, subset, x):
1121 def parents(repo, subset, x):
1122 """``parents([set])``
1122 """``parents([set])``
1123 The set of all parents for all changesets in set, or the working directory.
1123 The set of all parents for all changesets in set, or the working directory.
1124 """
1124 """
1125 if x is None:
1125 if x is None:
1126 ps = tuple(p.rev() for p in repo[x].parents())
1126 ps = tuple(p.rev() for p in repo[x].parents())
1127 return [r for r in subset if r in ps]
1127 return [r for r in subset if r in ps]
1128
1128
1129 ps = set()
1129 ps = set()
1130 cl = repo.changelog
1130 cl = repo.changelog
1131 for r in getset(repo, list(repo), x):
1131 for r in getset(repo, list(repo), x):
1132 ps.update(cl.parentrevs(r))
1132 ps.update(cl.parentrevs(r))
1133 return [r for r in subset if r in ps]
1133 return [r for r in subset if r in ps]
1134
1134
1135 def parentspec(repo, subset, x, n):
1135 def parentspec(repo, subset, x, n):
1136 """``set^0``
1136 """``set^0``
1137 The set.
1137 The set.
1138 ``set^1`` (or ``set^``), ``set^2``
1138 ``set^1`` (or ``set^``), ``set^2``
1139 First or second parent, respectively, of all changesets in set.
1139 First or second parent, respectively, of all changesets in set.
1140 """
1140 """
1141 try:
1141 try:
1142 n = int(n[1])
1142 n = int(n[1])
1143 if n not in (0, 1, 2):
1143 if n not in (0, 1, 2):
1144 raise ValueError
1144 raise ValueError
1145 except (TypeError, ValueError):
1145 except (TypeError, ValueError):
1146 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1146 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1147 ps = set()
1147 ps = set()
1148 cl = repo.changelog
1148 cl = repo.changelog
1149 for r in getset(repo, cl, x):
1149 for r in getset(repo, cl, x):
1150 if n == 0:
1150 if n == 0:
1151 ps.add(r)
1151 ps.add(r)
1152 elif n == 1:
1152 elif n == 1:
1153 ps.add(cl.parentrevs(r)[0])
1153 ps.add(cl.parentrevs(r)[0])
1154 elif n == 2:
1154 elif n == 2:
1155 parents = cl.parentrevs(r)
1155 parents = cl.parentrevs(r)
1156 if len(parents) > 1:
1156 if len(parents) > 1:
1157 ps.add(parents[1])
1157 ps.add(parents[1])
1158 return [r for r in subset if r in ps]
1158 return [r for r in subset if r in ps]
1159
1159
1160 def present(repo, subset, x):
1160 def present(repo, subset, x):
1161 """``present(set)``
1161 """``present(set)``
1162 An empty set, if any revision in set isn't found; otherwise,
1162 An empty set, if any revision in set isn't found; otherwise,
1163 all revisions in set.
1163 all revisions in set.
1164
1164
1165 If any of specified revisions is not present in the local repository,
1165 If any of specified revisions is not present in the local repository,
1166 the query is normally aborted. But this predicate allows the query
1166 the query is normally aborted. But this predicate allows the query
1167 to continue even in such cases.
1167 to continue even in such cases.
1168 """
1168 """
1169 try:
1169 try:
1170 return getset(repo, subset, x)
1170 return getset(repo, subset, x)
1171 except error.RepoLookupError:
1171 except error.RepoLookupError:
1172 return []
1172 return []
1173
1173
1174 def public(repo, subset, x):
1174 def public(repo, subset, x):
1175 """``public()``
1175 """``public()``
1176 Changeset in public phase."""
1176 Changeset in public phase."""
1177 # i18n: "public" is a keyword
1177 # i18n: "public" is a keyword
1178 getargs(x, 0, 0, _("public takes no arguments"))
1178 getargs(x, 0, 0, _("public takes no arguments"))
1179 pc = repo._phasecache
1179 pc = repo._phasecache
1180 return [r for r in subset if pc.phase(repo, r) == phases.public]
1180 return [r for r in subset if pc.phase(repo, r) == phases.public]
1181
1181
1182 def remote(repo, subset, x):
1182 def remote(repo, subset, x):
1183 """``remote([id [,path]])``
1183 """``remote([id [,path]])``
1184 Local revision that corresponds to the given identifier in a
1184 Local revision that corresponds to the given identifier in a
1185 remote repository, if present. Here, the '.' identifier is a
1185 remote repository, if present. Here, the '.' identifier is a
1186 synonym for the current local branch.
1186 synonym for the current local branch.
1187 """
1187 """
1188
1188
1189 import hg # avoid start-up nasties
1189 import hg # avoid start-up nasties
1190 # i18n: "remote" is a keyword
1190 # i18n: "remote" is a keyword
1191 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1191 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1192
1192
1193 q = '.'
1193 q = '.'
1194 if len(l) > 0:
1194 if len(l) > 0:
1195 # i18n: "remote" is a keyword
1195 # i18n: "remote" is a keyword
1196 q = getstring(l[0], _("remote requires a string id"))
1196 q = getstring(l[0], _("remote requires a string id"))
1197 if q == '.':
1197 if q == '.':
1198 q = repo['.'].branch()
1198 q = repo['.'].branch()
1199
1199
1200 dest = ''
1200 dest = ''
1201 if len(l) > 1:
1201 if len(l) > 1:
1202 # i18n: "remote" is a keyword
1202 # i18n: "remote" is a keyword
1203 dest = getstring(l[1], _("remote requires a repository path"))
1203 dest = getstring(l[1], _("remote requires a repository path"))
1204 dest = repo.ui.expandpath(dest or 'default')
1204 dest = repo.ui.expandpath(dest or 'default')
1205 dest, branches = hg.parseurl(dest)
1205 dest, branches = hg.parseurl(dest)
1206 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1206 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1207 if revs:
1207 if revs:
1208 revs = [repo.lookup(rev) for rev in revs]
1208 revs = [repo.lookup(rev) for rev in revs]
1209 other = hg.peer(repo, {}, dest)
1209 other = hg.peer(repo, {}, dest)
1210 n = other.lookup(q)
1210 n = other.lookup(q)
1211 if n in repo:
1211 if n in repo:
1212 r = repo[n].rev()
1212 r = repo[n].rev()
1213 if r in subset:
1213 if r in subset:
1214 return [r]
1214 return [r]
1215 return []
1215 return []
1216
1216
1217 def removes(repo, subset, x):
1217 def removes(repo, subset, x):
1218 """``removes(pattern)``
1218 """``removes(pattern)``
1219 Changesets which remove files matching pattern.
1219 Changesets which remove files matching pattern.
1220 """
1220 """
1221 # i18n: "removes" is a keyword
1221 # i18n: "removes" is a keyword
1222 pat = getstring(x, _("removes requires a pattern"))
1222 pat = getstring(x, _("removes requires a pattern"))
1223 return checkstatus(repo, subset, pat, 2)
1223 return checkstatus(repo, subset, pat, 2)
1224
1224
1225 def rev(repo, subset, x):
1225 def rev(repo, subset, x):
1226 """``rev(number)``
1226 """``rev(number)``
1227 Revision with the given numeric identifier.
1227 Revision with the given numeric identifier.
1228 """
1228 """
1229 # i18n: "rev" is a keyword
1229 # i18n: "rev" is a keyword
1230 l = getargs(x, 1, 1, _("rev requires one argument"))
1230 l = getargs(x, 1, 1, _("rev requires one argument"))
1231 try:
1231 try:
1232 # i18n: "rev" is a keyword
1232 # i18n: "rev" is a keyword
1233 l = int(getstring(l[0], _("rev requires a number")))
1233 l = int(getstring(l[0], _("rev requires a number")))
1234 except (TypeError, ValueError):
1234 except (TypeError, ValueError):
1235 # i18n: "rev" is a keyword
1235 # i18n: "rev" is a keyword
1236 raise error.ParseError(_("rev expects a number"))
1236 raise error.ParseError(_("rev expects a number"))
1237 return [r for r in subset if r == l]
1237 return [r for r in subset if r == l]
1238
1238
1239 def matching(repo, subset, x):
1239 def matching(repo, subset, x):
1240 """``matching(revision [, field])``
1240 """``matching(revision [, field])``
1241 Changesets in which a given set of fields match the set of fields in the
1241 Changesets in which a given set of fields match the set of fields in the
1242 selected revision or set.
1242 selected revision or set.
1243
1243
1244 To match more than one field pass the list of fields to match separated
1244 To match more than one field pass the list of fields to match separated
1245 by spaces (e.g. ``author description``).
1245 by spaces (e.g. ``author description``).
1246
1246
1247 Valid fields are most regular revision fields and some special fields.
1247 Valid fields are most regular revision fields and some special fields.
1248
1248
1249 Regular revision fields are ``description``, ``author``, ``branch``,
1249 Regular revision fields are ``description``, ``author``, ``branch``,
1250 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1250 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1251 and ``diff``.
1251 and ``diff``.
1252 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1252 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1253 contents of the revision. Two revisions matching their ``diff`` will
1253 contents of the revision. Two revisions matching their ``diff`` will
1254 also match their ``files``.
1254 also match their ``files``.
1255
1255
1256 Special fields are ``summary`` and ``metadata``:
1256 Special fields are ``summary`` and ``metadata``:
1257 ``summary`` matches the first line of the description.
1257 ``summary`` matches the first line of the description.
1258 ``metadata`` is equivalent to matching ``description user date``
1258 ``metadata`` is equivalent to matching ``description user date``
1259 (i.e. it matches the main metadata fields).
1259 (i.e. it matches the main metadata fields).
1260
1260
1261 ``metadata`` is the default field which is used when no fields are
1261 ``metadata`` is the default field which is used when no fields are
1262 specified. You can match more than one field at a time.
1262 specified. You can match more than one field at a time.
1263 """
1263 """
1264 # i18n: "matching" is a keyword
1264 # i18n: "matching" is a keyword
1265 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1265 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1266
1266
1267 revs = getset(repo, repo.changelog, l[0])
1267 revs = getset(repo, repo.changelog, l[0])
1268
1268
1269 fieldlist = ['metadata']
1269 fieldlist = ['metadata']
1270 if len(l) > 1:
1270 if len(l) > 1:
1271 fieldlist = getstring(l[1],
1271 fieldlist = getstring(l[1],
1272 # i18n: "matching" is a keyword
1272 # i18n: "matching" is a keyword
1273 _("matching requires a string "
1273 _("matching requires a string "
1274 "as its second argument")).split()
1274 "as its second argument")).split()
1275
1275
1276 # Make sure that there are no repeated fields,
1276 # Make sure that there are no repeated fields,
1277 # expand the 'special' 'metadata' field type
1277 # expand the 'special' 'metadata' field type
1278 # and check the 'files' whenever we check the 'diff'
1278 # and check the 'files' whenever we check the 'diff'
1279 fields = []
1279 fields = []
1280 for field in fieldlist:
1280 for field in fieldlist:
1281 if field == 'metadata':
1281 if field == 'metadata':
1282 fields += ['user', 'description', 'date']
1282 fields += ['user', 'description', 'date']
1283 elif field == 'diff':
1283 elif field == 'diff':
1284 # a revision matching the diff must also match the files
1284 # a revision matching the diff must also match the files
1285 # since matching the diff is very costly, make sure to
1285 # since matching the diff is very costly, make sure to
1286 # also match the files first
1286 # also match the files first
1287 fields += ['files', 'diff']
1287 fields += ['files', 'diff']
1288 else:
1288 else:
1289 if field == 'author':
1289 if field == 'author':
1290 field = 'user'
1290 field = 'user'
1291 fields.append(field)
1291 fields.append(field)
1292 fields = set(fields)
1292 fields = set(fields)
1293 if 'summary' in fields and 'description' in fields:
1293 if 'summary' in fields and 'description' in fields:
1294 # If a revision matches its description it also matches its summary
1294 # If a revision matches its description it also matches its summary
1295 fields.discard('summary')
1295 fields.discard('summary')
1296
1296
1297 # We may want to match more than one field
1297 # We may want to match more than one field
1298 # Not all fields take the same amount of time to be matched
1298 # Not all fields take the same amount of time to be matched
1299 # Sort the selected fields in order of increasing matching cost
1299 # Sort the selected fields in order of increasing matching cost
1300 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1300 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1301 'files', 'description', 'substate', 'diff']
1301 'files', 'description', 'substate', 'diff']
1302 def fieldkeyfunc(f):
1302 def fieldkeyfunc(f):
1303 try:
1303 try:
1304 return fieldorder.index(f)
1304 return fieldorder.index(f)
1305 except ValueError:
1305 except ValueError:
1306 # assume an unknown field is very costly
1306 # assume an unknown field is very costly
1307 return len(fieldorder)
1307 return len(fieldorder)
1308 fields = list(fields)
1308 fields = list(fields)
1309 fields.sort(key=fieldkeyfunc)
1309 fields.sort(key=fieldkeyfunc)
1310
1310
1311 # Each field will be matched with its own "getfield" function
1311 # Each field will be matched with its own "getfield" function
1312 # which will be added to the getfieldfuncs array of functions
1312 # which will be added to the getfieldfuncs array of functions
1313 getfieldfuncs = []
1313 getfieldfuncs = []
1314 _funcs = {
1314 _funcs = {
1315 'user': lambda r: repo[r].user(),
1315 'user': lambda r: repo[r].user(),
1316 'branch': lambda r: repo[r].branch(),
1316 'branch': lambda r: repo[r].branch(),
1317 'date': lambda r: repo[r].date(),
1317 'date': lambda r: repo[r].date(),
1318 'description': lambda r: repo[r].description(),
1318 'description': lambda r: repo[r].description(),
1319 'files': lambda r: repo[r].files(),
1319 'files': lambda r: repo[r].files(),
1320 'parents': lambda r: repo[r].parents(),
1320 'parents': lambda r: repo[r].parents(),
1321 'phase': lambda r: repo[r].phase(),
1321 'phase': lambda r: repo[r].phase(),
1322 'substate': lambda r: repo[r].substate,
1322 'substate': lambda r: repo[r].substate,
1323 'summary': lambda r: repo[r].description().splitlines()[0],
1323 'summary': lambda r: repo[r].description().splitlines()[0],
1324 'diff': lambda r: list(repo[r].diff(git=True),)
1324 'diff': lambda r: list(repo[r].diff(git=True),)
1325 }
1325 }
1326 for info in fields:
1326 for info in fields:
1327 getfield = _funcs.get(info, None)
1327 getfield = _funcs.get(info, None)
1328 if getfield is None:
1328 if getfield is None:
1329 raise error.ParseError(
1329 raise error.ParseError(
1330 # i18n: "matching" is a keyword
1330 # i18n: "matching" is a keyword
1331 _("unexpected field name passed to matching: %s") % info)
1331 _("unexpected field name passed to matching: %s") % info)
1332 getfieldfuncs.append(getfield)
1332 getfieldfuncs.append(getfield)
1333 # convert the getfield array of functions into a "getinfo" function
1333 # convert the getfield array of functions into a "getinfo" function
1334 # which returns an array of field values (or a single value if there
1334 # which returns an array of field values (or a single value if there
1335 # is only one field to match)
1335 # is only one field to match)
1336 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1336 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1337
1337
1338 matches = set()
1338 matches = set()
1339 for rev in revs:
1339 for rev in revs:
1340 target = getinfo(rev)
1340 target = getinfo(rev)
1341 for r in subset:
1341 for r in subset:
1342 match = True
1342 match = True
1343 for n, f in enumerate(getfieldfuncs):
1343 for n, f in enumerate(getfieldfuncs):
1344 if target[n] != f(r):
1344 if target[n] != f(r):
1345 match = False
1345 match = False
1346 break
1346 break
1347 if match:
1347 if match:
1348 matches.add(r)
1348 matches.add(r)
1349 return [r for r in subset if r in matches]
1349 return [r for r in subset if r in matches]
1350
1350
1351 def reverse(repo, subset, x):
1351 def reverse(repo, subset, x):
1352 """``reverse(set)``
1352 """``reverse(set)``
1353 Reverse order of set.
1353 Reverse order of set.
1354 """
1354 """
1355 l = getset(repo, subset, x)
1355 l = getset(repo, subset, x)
1356 if not isinstance(l, list):
1356 if not isinstance(l, list):
1357 l = list(l)
1357 l = list(l)
1358 l.reverse()
1358 l.reverse()
1359 return l
1359 return l
1360
1360
1361 def roots(repo, subset, x):
1361 def roots(repo, subset, x):
1362 """``roots(set)``
1362 """``roots(set)``
1363 Changesets in set with no parent changeset in set.
1363 Changesets in set with no parent changeset in set.
1364 """
1364 """
1365 s = set(getset(repo, repo.changelog, x))
1365 s = set(getset(repo, repo.changelog, x))
1366 subset = [r for r in subset if r in s]
1366 subset = [r for r in subset if r in s]
1367 cs = _children(repo, subset, s)
1367 cs = _children(repo, subset, s)
1368 return [r for r in subset if r not in cs]
1368 return [r for r in subset if r not in cs]
1369
1369
1370 def secret(repo, subset, x):
1370 def secret(repo, subset, x):
1371 """``secret()``
1371 """``secret()``
1372 Changeset in secret phase."""
1372 Changeset in secret phase."""
1373 # i18n: "secret" is a keyword
1373 # i18n: "secret" is a keyword
1374 getargs(x, 0, 0, _("secret takes no arguments"))
1374 getargs(x, 0, 0, _("secret takes no arguments"))
1375 pc = repo._phasecache
1375 pc = repo._phasecache
1376 return [r for r in subset if pc.phase(repo, r) == phases.secret]
1376 return [r for r in subset if pc.phase(repo, r) == phases.secret]
1377
1377
1378 def sort(repo, subset, x):
1378 def sort(repo, subset, x):
1379 """``sort(set[, [-]key...])``
1379 """``sort(set[, [-]key...])``
1380 Sort set by keys. The default sort order is ascending, specify a key
1380 Sort set by keys. The default sort order is ascending, specify a key
1381 as ``-key`` to sort in descending order.
1381 as ``-key`` to sort in descending order.
1382
1382
1383 The keys can be:
1383 The keys can be:
1384
1384
1385 - ``rev`` for the revision number,
1385 - ``rev`` for the revision number,
1386 - ``branch`` for the branch name,
1386 - ``branch`` for the branch name,
1387 - ``desc`` for the commit message (description),
1387 - ``desc`` for the commit message (description),
1388 - ``user`` for user name (``author`` can be used as an alias),
1388 - ``user`` for user name (``author`` can be used as an alias),
1389 - ``date`` for the commit date
1389 - ``date`` for the commit date
1390 """
1390 """
1391 # i18n: "sort" is a keyword
1391 # i18n: "sort" is a keyword
1392 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1392 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1393 keys = "rev"
1393 keys = "rev"
1394 if len(l) == 2:
1394 if len(l) == 2:
1395 # i18n: "sort" is a keyword
1395 # i18n: "sort" is a keyword
1396 keys = getstring(l[1], _("sort spec must be a string"))
1396 keys = getstring(l[1], _("sort spec must be a string"))
1397
1397
1398 s = l[0]
1398 s = l[0]
1399 keys = keys.split()
1399 keys = keys.split()
1400 l = []
1400 l = []
1401 def invert(s):
1401 def invert(s):
1402 return "".join(chr(255 - ord(c)) for c in s)
1402 return "".join(chr(255 - ord(c)) for c in s)
1403 for r in getset(repo, subset, s):
1403 for r in getset(repo, subset, s):
1404 c = repo[r]
1404 c = repo[r]
1405 e = []
1405 e = []
1406 for k in keys:
1406 for k in keys:
1407 if k == 'rev':
1407 if k == 'rev':
1408 e.append(r)
1408 e.append(r)
1409 elif k == '-rev':
1409 elif k == '-rev':
1410 e.append(-r)
1410 e.append(-r)
1411 elif k == 'branch':
1411 elif k == 'branch':
1412 e.append(c.branch())
1412 e.append(c.branch())
1413 elif k == '-branch':
1413 elif k == '-branch':
1414 e.append(invert(c.branch()))
1414 e.append(invert(c.branch()))
1415 elif k == 'desc':
1415 elif k == 'desc':
1416 e.append(c.description())
1416 e.append(c.description())
1417 elif k == '-desc':
1417 elif k == '-desc':
1418 e.append(invert(c.description()))
1418 e.append(invert(c.description()))
1419 elif k in 'user author':
1419 elif k in 'user author':
1420 e.append(c.user())
1420 e.append(c.user())
1421 elif k in '-user -author':
1421 elif k in '-user -author':
1422 e.append(invert(c.user()))
1422 e.append(invert(c.user()))
1423 elif k == 'date':
1423 elif k == 'date':
1424 e.append(c.date()[0])
1424 e.append(c.date()[0])
1425 elif k == '-date':
1425 elif k == '-date':
1426 e.append(-c.date()[0])
1426 e.append(-c.date()[0])
1427 else:
1427 else:
1428 raise error.ParseError(_("unknown sort key %r") % k)
1428 raise error.ParseError(_("unknown sort key %r") % k)
1429 e.append(r)
1429 e.append(r)
1430 l.append(e)
1430 l.append(e)
1431 l.sort()
1431 l.sort()
1432 return [e[-1] for e in l]
1432 return [e[-1] for e in l]
1433
1433
1434 def _stringmatcher(pattern):
1434 def _stringmatcher(pattern):
1435 """
1435 """
1436 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1436 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1437 returns the matcher name, pattern, and matcher function.
1437 returns the matcher name, pattern, and matcher function.
1438 missing or unknown prefixes are treated as literal matches.
1438 missing or unknown prefixes are treated as literal matches.
1439
1439
1440 helper for tests:
1440 helper for tests:
1441 >>> def test(pattern, *tests):
1441 >>> def test(pattern, *tests):
1442 ... kind, pattern, matcher = _stringmatcher(pattern)
1442 ... kind, pattern, matcher = _stringmatcher(pattern)
1443 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1443 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1444
1444
1445 exact matching (no prefix):
1445 exact matching (no prefix):
1446 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1446 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1447 ('literal', 'abcdefg', [False, False, True])
1447 ('literal', 'abcdefg', [False, False, True])
1448
1448
1449 regex matching ('re:' prefix)
1449 regex matching ('re:' prefix)
1450 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1450 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1451 ('re', 'a.+b', [False, False, True])
1451 ('re', 'a.+b', [False, False, True])
1452
1452
1453 force exact matches ('literal:' prefix)
1453 force exact matches ('literal:' prefix)
1454 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1454 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1455 ('literal', 're:foobar', [False, True])
1455 ('literal', 're:foobar', [False, True])
1456
1456
1457 unknown prefixes are ignored and treated as literals
1457 unknown prefixes are ignored and treated as literals
1458 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1458 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1459 ('literal', 'foo:bar', [False, False, True])
1459 ('literal', 'foo:bar', [False, False, True])
1460 """
1460 """
1461 if pattern.startswith('re:'):
1461 if pattern.startswith('re:'):
1462 pattern = pattern[3:]
1462 pattern = pattern[3:]
1463 try:
1463 try:
1464 regex = re.compile(pattern)
1464 regex = re.compile(pattern)
1465 except re.error, e:
1465 except re.error, e:
1466 raise error.ParseError(_('invalid regular expression: %s')
1466 raise error.ParseError(_('invalid regular expression: %s')
1467 % e)
1467 % e)
1468 return 're', pattern, regex.search
1468 return 're', pattern, regex.search
1469 elif pattern.startswith('literal:'):
1469 elif pattern.startswith('literal:'):
1470 pattern = pattern[8:]
1470 pattern = pattern[8:]
1471 return 'literal', pattern, pattern.__eq__
1471 return 'literal', pattern, pattern.__eq__
1472
1472
1473 def _substringmatcher(pattern):
1473 def _substringmatcher(pattern):
1474 kind, pattern, matcher = _stringmatcher(pattern)
1474 kind, pattern, matcher = _stringmatcher(pattern)
1475 if kind == 'literal':
1475 if kind == 'literal':
1476 matcher = lambda s: pattern in s
1476 matcher = lambda s: pattern in s
1477 return kind, pattern, matcher
1477 return kind, pattern, matcher
1478
1478
1479 def tag(repo, subset, x):
1479 def tag(repo, subset, x):
1480 """``tag([name])``
1480 """``tag([name])``
1481 The specified tag by name, or all tagged revisions if no name is given.
1481 The specified tag by name, or all tagged revisions if no name is given.
1482 """
1482 """
1483 # i18n: "tag" is a keyword
1483 # i18n: "tag" is a keyword
1484 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1484 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1485 cl = repo.changelog
1485 cl = repo.changelog
1486 if args:
1486 if args:
1487 pattern = getstring(args[0],
1487 pattern = getstring(args[0],
1488 # i18n: "tag" is a keyword
1488 # i18n: "tag" is a keyword
1489 _('the argument to tag must be a string'))
1489 _('the argument to tag must be a string'))
1490 kind, pattern, matcher = _stringmatcher(pattern)
1490 kind, pattern, matcher = _stringmatcher(pattern)
1491 if kind == 'literal':
1491 if kind == 'literal':
1492 # avoid resolving all tags
1492 # avoid resolving all tags
1493 tn = repo._tagscache.tags.get(pattern, None)
1493 tn = repo._tagscache.tags.get(pattern, None)
1494 if tn is None:
1494 if tn is None:
1495 raise util.Abort(_("tag '%s' does not exist") % pattern)
1495 raise util.Abort(_("tag '%s' does not exist") % pattern)
1496 s = set([repo[tn].rev()])
1496 s = set([repo[tn].rev()])
1497 else:
1497 else:
1498 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1498 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1499 if not s:
1500 raise util.Abort(_("no tags exist that match '%s'") % pattern)
1501 else:
1499 else:
1502 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1500 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1503 return [r for r in subset if r in s]
1501 return [r for r in subset if r in s]
1504
1502
1505 def tagged(repo, subset, x):
1503 def tagged(repo, subset, x):
1506 return tag(repo, subset, x)
1504 return tag(repo, subset, x)
1507
1505
1508 def unstable(repo, subset, x):
1506 def unstable(repo, subset, x):
1509 """``unstable()``
1507 """``unstable()``
1510 Non-obsolete changesets with obsolete ancestors.
1508 Non-obsolete changesets with obsolete ancestors.
1511 """
1509 """
1512 # i18n: "unstable" is a keyword
1510 # i18n: "unstable" is a keyword
1513 getargs(x, 0, 0, _("unstable takes no arguments"))
1511 getargs(x, 0, 0, _("unstable takes no arguments"))
1514 unstables = obsmod.getrevs(repo, 'unstable')
1512 unstables = obsmod.getrevs(repo, 'unstable')
1515 return [r for r in subset if r in unstables]
1513 return [r for r in subset if r in unstables]
1516
1514
1517
1515
1518 def user(repo, subset, x):
1516 def user(repo, subset, x):
1519 """``user(string)``
1517 """``user(string)``
1520 User name contains string. The match is case-insensitive.
1518 User name contains string. The match is case-insensitive.
1521
1519
1522 If `string` starts with `re:`, the remainder of the string is treated as
1520 If `string` starts with `re:`, the remainder of the string is treated as
1523 a regular expression. To match a user that actually contains `re:`, use
1521 a regular expression. To match a user that actually contains `re:`, use
1524 the prefix `literal:`.
1522 the prefix `literal:`.
1525 """
1523 """
1526 return author(repo, subset, x)
1524 return author(repo, subset, x)
1527
1525
1528 # for internal use
1526 # for internal use
1529 def _list(repo, subset, x):
1527 def _list(repo, subset, x):
1530 s = getstring(x, "internal error")
1528 s = getstring(x, "internal error")
1531 if not s:
1529 if not s:
1532 return []
1530 return []
1533 if not isinstance(subset, set):
1531 if not isinstance(subset, set):
1534 subset = set(subset)
1532 subset = set(subset)
1535 ls = [repo[r].rev() for r in s.split('\0')]
1533 ls = [repo[r].rev() for r in s.split('\0')]
1536 return [r for r in ls if r in subset]
1534 return [r for r in ls if r in subset]
1537
1535
1538 symbols = {
1536 symbols = {
1539 "adds": adds,
1537 "adds": adds,
1540 "all": getall,
1538 "all": getall,
1541 "ancestor": ancestor,
1539 "ancestor": ancestor,
1542 "ancestors": ancestors,
1540 "ancestors": ancestors,
1543 "_firstancestors": _firstancestors,
1541 "_firstancestors": _firstancestors,
1544 "author": author,
1542 "author": author,
1545 "bisect": bisect,
1543 "bisect": bisect,
1546 "bisected": bisected,
1544 "bisected": bisected,
1547 "bookmark": bookmark,
1545 "bookmark": bookmark,
1548 "branch": branch,
1546 "branch": branch,
1549 "branchpoint": branchpoint,
1547 "branchpoint": branchpoint,
1550 "bumped": bumped,
1548 "bumped": bumped,
1551 "bundle": bundle,
1549 "bundle": bundle,
1552 "children": children,
1550 "children": children,
1553 "closed": closed,
1551 "closed": closed,
1554 "contains": contains,
1552 "contains": contains,
1555 "converted": converted,
1553 "converted": converted,
1556 "date": date,
1554 "date": date,
1557 "desc": desc,
1555 "desc": desc,
1558 "descendants": descendants,
1556 "descendants": descendants,
1559 "_firstdescendants": _firstdescendants,
1557 "_firstdescendants": _firstdescendants,
1560 "destination": destination,
1558 "destination": destination,
1561 "divergent": divergent,
1559 "divergent": divergent,
1562 "draft": draft,
1560 "draft": draft,
1563 "extinct": extinct,
1561 "extinct": extinct,
1564 "extra": extra,
1562 "extra": extra,
1565 "file": hasfile,
1563 "file": hasfile,
1566 "filelog": filelog,
1564 "filelog": filelog,
1567 "first": first,
1565 "first": first,
1568 "follow": follow,
1566 "follow": follow,
1569 "_followfirst": _followfirst,
1567 "_followfirst": _followfirst,
1570 "grep": grep,
1568 "grep": grep,
1571 "head": head,
1569 "head": head,
1572 "heads": heads,
1570 "heads": heads,
1573 "hidden": hidden,
1571 "hidden": hidden,
1574 "id": node_,
1572 "id": node_,
1575 "keyword": keyword,
1573 "keyword": keyword,
1576 "last": last,
1574 "last": last,
1577 "limit": limit,
1575 "limit": limit,
1578 "_matchfiles": _matchfiles,
1576 "_matchfiles": _matchfiles,
1579 "max": maxrev,
1577 "max": maxrev,
1580 "merge": merge,
1578 "merge": merge,
1581 "min": minrev,
1579 "min": minrev,
1582 "modifies": modifies,
1580 "modifies": modifies,
1583 "obsolete": obsolete,
1581 "obsolete": obsolete,
1584 "origin": origin,
1582 "origin": origin,
1585 "outgoing": outgoing,
1583 "outgoing": outgoing,
1586 "p1": p1,
1584 "p1": p1,
1587 "p2": p2,
1585 "p2": p2,
1588 "parents": parents,
1586 "parents": parents,
1589 "present": present,
1587 "present": present,
1590 "public": public,
1588 "public": public,
1591 "remote": remote,
1589 "remote": remote,
1592 "removes": removes,
1590 "removes": removes,
1593 "rev": rev,
1591 "rev": rev,
1594 "reverse": reverse,
1592 "reverse": reverse,
1595 "roots": roots,
1593 "roots": roots,
1596 "sort": sort,
1594 "sort": sort,
1597 "secret": secret,
1595 "secret": secret,
1598 "matching": matching,
1596 "matching": matching,
1599 "tag": tag,
1597 "tag": tag,
1600 "tagged": tagged,
1598 "tagged": tagged,
1601 "user": user,
1599 "user": user,
1602 "unstable": unstable,
1600 "unstable": unstable,
1603 "_list": _list,
1601 "_list": _list,
1604 }
1602 }
1605
1603
1606 methods = {
1604 methods = {
1607 "range": rangeset,
1605 "range": rangeset,
1608 "dagrange": dagrange,
1606 "dagrange": dagrange,
1609 "string": stringset,
1607 "string": stringset,
1610 "symbol": symbolset,
1608 "symbol": symbolset,
1611 "and": andset,
1609 "and": andset,
1612 "or": orset,
1610 "or": orset,
1613 "not": notset,
1611 "not": notset,
1614 "list": listset,
1612 "list": listset,
1615 "func": func,
1613 "func": func,
1616 "ancestor": ancestorspec,
1614 "ancestor": ancestorspec,
1617 "parent": parentspec,
1615 "parent": parentspec,
1618 "parentpost": p1,
1616 "parentpost": p1,
1619 }
1617 }
1620
1618
1621 def optimize(x, small):
1619 def optimize(x, small):
1622 if x is None:
1620 if x is None:
1623 return 0, x
1621 return 0, x
1624
1622
1625 smallbonus = 1
1623 smallbonus = 1
1626 if small:
1624 if small:
1627 smallbonus = .5
1625 smallbonus = .5
1628
1626
1629 op = x[0]
1627 op = x[0]
1630 if op == 'minus':
1628 if op == 'minus':
1631 return optimize(('and', x[1], ('not', x[2])), small)
1629 return optimize(('and', x[1], ('not', x[2])), small)
1632 elif op == 'dagrangepre':
1630 elif op == 'dagrangepre':
1633 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1631 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1634 elif op == 'dagrangepost':
1632 elif op == 'dagrangepost':
1635 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1633 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1636 elif op == 'rangepre':
1634 elif op == 'rangepre':
1637 return optimize(('range', ('string', '0'), x[1]), small)
1635 return optimize(('range', ('string', '0'), x[1]), small)
1638 elif op == 'rangepost':
1636 elif op == 'rangepost':
1639 return optimize(('range', x[1], ('string', 'tip')), small)
1637 return optimize(('range', x[1], ('string', 'tip')), small)
1640 elif op == 'negate':
1638 elif op == 'negate':
1641 return optimize(('string',
1639 return optimize(('string',
1642 '-' + getstring(x[1], _("can't negate that"))), small)
1640 '-' + getstring(x[1], _("can't negate that"))), small)
1643 elif op in 'string symbol negate':
1641 elif op in 'string symbol negate':
1644 return smallbonus, x # single revisions are small
1642 return smallbonus, x # single revisions are small
1645 elif op == 'and':
1643 elif op == 'and':
1646 wa, ta = optimize(x[1], True)
1644 wa, ta = optimize(x[1], True)
1647 wb, tb = optimize(x[2], True)
1645 wb, tb = optimize(x[2], True)
1648 w = min(wa, wb)
1646 w = min(wa, wb)
1649 if wa > wb:
1647 if wa > wb:
1650 return w, (op, tb, ta)
1648 return w, (op, tb, ta)
1651 return w, (op, ta, tb)
1649 return w, (op, ta, tb)
1652 elif op == 'or':
1650 elif op == 'or':
1653 wa, ta = optimize(x[1], False)
1651 wa, ta = optimize(x[1], False)
1654 wb, tb = optimize(x[2], False)
1652 wb, tb = optimize(x[2], False)
1655 if wb < wa:
1653 if wb < wa:
1656 wb, wa = wa, wb
1654 wb, wa = wa, wb
1657 return max(wa, wb), (op, ta, tb)
1655 return max(wa, wb), (op, ta, tb)
1658 elif op == 'not':
1656 elif op == 'not':
1659 o = optimize(x[1], not small)
1657 o = optimize(x[1], not small)
1660 return o[0], (op, o[1])
1658 return o[0], (op, o[1])
1661 elif op == 'parentpost':
1659 elif op == 'parentpost':
1662 o = optimize(x[1], small)
1660 o = optimize(x[1], small)
1663 return o[0], (op, o[1])
1661 return o[0], (op, o[1])
1664 elif op == 'group':
1662 elif op == 'group':
1665 return optimize(x[1], small)
1663 return optimize(x[1], small)
1666 elif op in 'dagrange range list parent ancestorspec':
1664 elif op in 'dagrange range list parent ancestorspec':
1667 if op == 'parent':
1665 if op == 'parent':
1668 # x^:y means (x^) : y, not x ^ (:y)
1666 # x^:y means (x^) : y, not x ^ (:y)
1669 post = ('parentpost', x[1])
1667 post = ('parentpost', x[1])
1670 if x[2][0] == 'dagrangepre':
1668 if x[2][0] == 'dagrangepre':
1671 return optimize(('dagrange', post, x[2][1]), small)
1669 return optimize(('dagrange', post, x[2][1]), small)
1672 elif x[2][0] == 'rangepre':
1670 elif x[2][0] == 'rangepre':
1673 return optimize(('range', post, x[2][1]), small)
1671 return optimize(('range', post, x[2][1]), small)
1674
1672
1675 wa, ta = optimize(x[1], small)
1673 wa, ta = optimize(x[1], small)
1676 wb, tb = optimize(x[2], small)
1674 wb, tb = optimize(x[2], small)
1677 return wa + wb, (op, ta, tb)
1675 return wa + wb, (op, ta, tb)
1678 elif op == 'func':
1676 elif op == 'func':
1679 f = getstring(x[1], _("not a symbol"))
1677 f = getstring(x[1], _("not a symbol"))
1680 wa, ta = optimize(x[2], small)
1678 wa, ta = optimize(x[2], small)
1681 if f in ("author branch closed date desc file grep keyword "
1679 if f in ("author branch closed date desc file grep keyword "
1682 "outgoing user"):
1680 "outgoing user"):
1683 w = 10 # slow
1681 w = 10 # slow
1684 elif f in "modifies adds removes":
1682 elif f in "modifies adds removes":
1685 w = 30 # slower
1683 w = 30 # slower
1686 elif f == "contains":
1684 elif f == "contains":
1687 w = 100 # very slow
1685 w = 100 # very slow
1688 elif f == "ancestor":
1686 elif f == "ancestor":
1689 w = 1 * smallbonus
1687 w = 1 * smallbonus
1690 elif f in "reverse limit first":
1688 elif f in "reverse limit first":
1691 w = 0
1689 w = 0
1692 elif f in "sort":
1690 elif f in "sort":
1693 w = 10 # assume most sorts look at changelog
1691 w = 10 # assume most sorts look at changelog
1694 else:
1692 else:
1695 w = 1
1693 w = 1
1696 return w + wa, (op, x[1], ta)
1694 return w + wa, (op, x[1], ta)
1697 return 1, x
1695 return 1, x
1698
1696
1699 _aliasarg = ('func', ('symbol', '_aliasarg'))
1697 _aliasarg = ('func', ('symbol', '_aliasarg'))
1700 def _getaliasarg(tree):
1698 def _getaliasarg(tree):
1701 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1699 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1702 return X, None otherwise.
1700 return X, None otherwise.
1703 """
1701 """
1704 if (len(tree) == 3 and tree[:2] == _aliasarg
1702 if (len(tree) == 3 and tree[:2] == _aliasarg
1705 and tree[2][0] == 'string'):
1703 and tree[2][0] == 'string'):
1706 return tree[2][1]
1704 return tree[2][1]
1707 return None
1705 return None
1708
1706
1709 def _checkaliasarg(tree, known=None):
1707 def _checkaliasarg(tree, known=None):
1710 """Check tree contains no _aliasarg construct or only ones which
1708 """Check tree contains no _aliasarg construct or only ones which
1711 value is in known. Used to avoid alias placeholders injection.
1709 value is in known. Used to avoid alias placeholders injection.
1712 """
1710 """
1713 if isinstance(tree, tuple):
1711 if isinstance(tree, tuple):
1714 arg = _getaliasarg(tree)
1712 arg = _getaliasarg(tree)
1715 if arg is not None and (not known or arg not in known):
1713 if arg is not None and (not known or arg not in known):
1716 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1714 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1717 for t in tree:
1715 for t in tree:
1718 _checkaliasarg(t, known)
1716 _checkaliasarg(t, known)
1719
1717
1720 class revsetalias(object):
1718 class revsetalias(object):
1721 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1719 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1722 args = None
1720 args = None
1723
1721
1724 def __init__(self, name, value):
1722 def __init__(self, name, value):
1725 '''Aliases like:
1723 '''Aliases like:
1726
1724
1727 h = heads(default)
1725 h = heads(default)
1728 b($1) = ancestors($1) - ancestors(default)
1726 b($1) = ancestors($1) - ancestors(default)
1729 '''
1727 '''
1730 m = self.funcre.search(name)
1728 m = self.funcre.search(name)
1731 if m:
1729 if m:
1732 self.name = m.group(1)
1730 self.name = m.group(1)
1733 self.tree = ('func', ('symbol', m.group(1)))
1731 self.tree = ('func', ('symbol', m.group(1)))
1734 self.args = [x.strip() for x in m.group(2).split(',')]
1732 self.args = [x.strip() for x in m.group(2).split(',')]
1735 for arg in self.args:
1733 for arg in self.args:
1736 # _aliasarg() is an unknown symbol only used separate
1734 # _aliasarg() is an unknown symbol only used separate
1737 # alias argument placeholders from regular strings.
1735 # alias argument placeholders from regular strings.
1738 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1736 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1739 else:
1737 else:
1740 self.name = name
1738 self.name = name
1741 self.tree = ('symbol', name)
1739 self.tree = ('symbol', name)
1742
1740
1743 self.replacement, pos = parse(value)
1741 self.replacement, pos = parse(value)
1744 if pos != len(value):
1742 if pos != len(value):
1745 raise error.ParseError(_('invalid token'), pos)
1743 raise error.ParseError(_('invalid token'), pos)
1746 # Check for placeholder injection
1744 # Check for placeholder injection
1747 _checkaliasarg(self.replacement, self.args)
1745 _checkaliasarg(self.replacement, self.args)
1748
1746
1749 def _getalias(aliases, tree):
1747 def _getalias(aliases, tree):
1750 """If tree looks like an unexpanded alias, return it. Return None
1748 """If tree looks like an unexpanded alias, return it. Return None
1751 otherwise.
1749 otherwise.
1752 """
1750 """
1753 if isinstance(tree, tuple) and tree:
1751 if isinstance(tree, tuple) and tree:
1754 if tree[0] == 'symbol' and len(tree) == 2:
1752 if tree[0] == 'symbol' and len(tree) == 2:
1755 name = tree[1]
1753 name = tree[1]
1756 alias = aliases.get(name)
1754 alias = aliases.get(name)
1757 if alias and alias.args is None and alias.tree == tree:
1755 if alias and alias.args is None and alias.tree == tree:
1758 return alias
1756 return alias
1759 if tree[0] == 'func' and len(tree) > 1:
1757 if tree[0] == 'func' and len(tree) > 1:
1760 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1758 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1761 name = tree[1][1]
1759 name = tree[1][1]
1762 alias = aliases.get(name)
1760 alias = aliases.get(name)
1763 if alias and alias.args is not None and alias.tree == tree[:2]:
1761 if alias and alias.args is not None and alias.tree == tree[:2]:
1764 return alias
1762 return alias
1765 return None
1763 return None
1766
1764
1767 def _expandargs(tree, args):
1765 def _expandargs(tree, args):
1768 """Replace _aliasarg instances with the substitution value of the
1766 """Replace _aliasarg instances with the substitution value of the
1769 same name in args, recursively.
1767 same name in args, recursively.
1770 """
1768 """
1771 if not tree or not isinstance(tree, tuple):
1769 if not tree or not isinstance(tree, tuple):
1772 return tree
1770 return tree
1773 arg = _getaliasarg(tree)
1771 arg = _getaliasarg(tree)
1774 if arg is not None:
1772 if arg is not None:
1775 return args[arg]
1773 return args[arg]
1776 return tuple(_expandargs(t, args) for t in tree)
1774 return tuple(_expandargs(t, args) for t in tree)
1777
1775
1778 def _expandaliases(aliases, tree, expanding, cache):
1776 def _expandaliases(aliases, tree, expanding, cache):
1779 """Expand aliases in tree, recursively.
1777 """Expand aliases in tree, recursively.
1780
1778
1781 'aliases' is a dictionary mapping user defined aliases to
1779 'aliases' is a dictionary mapping user defined aliases to
1782 revsetalias objects.
1780 revsetalias objects.
1783 """
1781 """
1784 if not isinstance(tree, tuple):
1782 if not isinstance(tree, tuple):
1785 # Do not expand raw strings
1783 # Do not expand raw strings
1786 return tree
1784 return tree
1787 alias = _getalias(aliases, tree)
1785 alias = _getalias(aliases, tree)
1788 if alias is not None:
1786 if alias is not None:
1789 if alias in expanding:
1787 if alias in expanding:
1790 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1788 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1791 'detected') % alias.name)
1789 'detected') % alias.name)
1792 expanding.append(alias)
1790 expanding.append(alias)
1793 if alias.name not in cache:
1791 if alias.name not in cache:
1794 cache[alias.name] = _expandaliases(aliases, alias.replacement,
1792 cache[alias.name] = _expandaliases(aliases, alias.replacement,
1795 expanding, cache)
1793 expanding, cache)
1796 result = cache[alias.name]
1794 result = cache[alias.name]
1797 expanding.pop()
1795 expanding.pop()
1798 if alias.args is not None:
1796 if alias.args is not None:
1799 l = getlist(tree[2])
1797 l = getlist(tree[2])
1800 if len(l) != len(alias.args):
1798 if len(l) != len(alias.args):
1801 raise error.ParseError(
1799 raise error.ParseError(
1802 _('invalid number of arguments: %s') % len(l))
1800 _('invalid number of arguments: %s') % len(l))
1803 l = [_expandaliases(aliases, a, [], cache) for a in l]
1801 l = [_expandaliases(aliases, a, [], cache) for a in l]
1804 result = _expandargs(result, dict(zip(alias.args, l)))
1802 result = _expandargs(result, dict(zip(alias.args, l)))
1805 else:
1803 else:
1806 result = tuple(_expandaliases(aliases, t, expanding, cache)
1804 result = tuple(_expandaliases(aliases, t, expanding, cache)
1807 for t in tree)
1805 for t in tree)
1808 return result
1806 return result
1809
1807
1810 def findaliases(ui, tree):
1808 def findaliases(ui, tree):
1811 _checkaliasarg(tree)
1809 _checkaliasarg(tree)
1812 aliases = {}
1810 aliases = {}
1813 for k, v in ui.configitems('revsetalias'):
1811 for k, v in ui.configitems('revsetalias'):
1814 alias = revsetalias(k, v)
1812 alias = revsetalias(k, v)
1815 aliases[alias.name] = alias
1813 aliases[alias.name] = alias
1816 return _expandaliases(aliases, tree, [], {})
1814 return _expandaliases(aliases, tree, [], {})
1817
1815
1818 parse = parser.parser(tokenize, elements).parse
1816 parse = parser.parser(tokenize, elements).parse
1819
1817
1820 def match(ui, spec):
1818 def match(ui, spec):
1821 if not spec:
1819 if not spec:
1822 raise error.ParseError(_("empty query"))
1820 raise error.ParseError(_("empty query"))
1823 tree, pos = parse(spec)
1821 tree, pos = parse(spec)
1824 if (pos != len(spec)):
1822 if (pos != len(spec)):
1825 raise error.ParseError(_("invalid token"), pos)
1823 raise error.ParseError(_("invalid token"), pos)
1826 if ui:
1824 if ui:
1827 tree = findaliases(ui, tree)
1825 tree = findaliases(ui, tree)
1828 weight, tree = optimize(tree, True)
1826 weight, tree = optimize(tree, True)
1829 def mfunc(repo, subset):
1827 def mfunc(repo, subset):
1830 return getset(repo, subset, tree)
1828 return getset(repo, subset, tree)
1831 return mfunc
1829 return mfunc
1832
1830
1833 def formatspec(expr, *args):
1831 def formatspec(expr, *args):
1834 '''
1832 '''
1835 This is a convenience function for using revsets internally, and
1833 This is a convenience function for using revsets internally, and
1836 escapes arguments appropriately. Aliases are intentionally ignored
1834 escapes arguments appropriately. Aliases are intentionally ignored
1837 so that intended expression behavior isn't accidentally subverted.
1835 so that intended expression behavior isn't accidentally subverted.
1838
1836
1839 Supported arguments:
1837 Supported arguments:
1840
1838
1841 %r = revset expression, parenthesized
1839 %r = revset expression, parenthesized
1842 %d = int(arg), no quoting
1840 %d = int(arg), no quoting
1843 %s = string(arg), escaped and single-quoted
1841 %s = string(arg), escaped and single-quoted
1844 %b = arg.branch(), escaped and single-quoted
1842 %b = arg.branch(), escaped and single-quoted
1845 %n = hex(arg), single-quoted
1843 %n = hex(arg), single-quoted
1846 %% = a literal '%'
1844 %% = a literal '%'
1847
1845
1848 Prefixing the type with 'l' specifies a parenthesized list of that type.
1846 Prefixing the type with 'l' specifies a parenthesized list of that type.
1849
1847
1850 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1848 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1851 '(10 or 11):: and ((this()) or (that()))'
1849 '(10 or 11):: and ((this()) or (that()))'
1852 >>> formatspec('%d:: and not %d::', 10, 20)
1850 >>> formatspec('%d:: and not %d::', 10, 20)
1853 '10:: and not 20::'
1851 '10:: and not 20::'
1854 >>> formatspec('%ld or %ld', [], [1])
1852 >>> formatspec('%ld or %ld', [], [1])
1855 "_list('') or 1"
1853 "_list('') or 1"
1856 >>> formatspec('keyword(%s)', 'foo\\xe9')
1854 >>> formatspec('keyword(%s)', 'foo\\xe9')
1857 "keyword('foo\\\\xe9')"
1855 "keyword('foo\\\\xe9')"
1858 >>> b = lambda: 'default'
1856 >>> b = lambda: 'default'
1859 >>> b.branch = b
1857 >>> b.branch = b
1860 >>> formatspec('branch(%b)', b)
1858 >>> formatspec('branch(%b)', b)
1861 "branch('default')"
1859 "branch('default')"
1862 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1860 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1863 "root(_list('a\\x00b\\x00c\\x00d'))"
1861 "root(_list('a\\x00b\\x00c\\x00d'))"
1864 '''
1862 '''
1865
1863
1866 def quote(s):
1864 def quote(s):
1867 return repr(str(s))
1865 return repr(str(s))
1868
1866
1869 def argtype(c, arg):
1867 def argtype(c, arg):
1870 if c == 'd':
1868 if c == 'd':
1871 return str(int(arg))
1869 return str(int(arg))
1872 elif c == 's':
1870 elif c == 's':
1873 return quote(arg)
1871 return quote(arg)
1874 elif c == 'r':
1872 elif c == 'r':
1875 parse(arg) # make sure syntax errors are confined
1873 parse(arg) # make sure syntax errors are confined
1876 return '(%s)' % arg
1874 return '(%s)' % arg
1877 elif c == 'n':
1875 elif c == 'n':
1878 return quote(node.hex(arg))
1876 return quote(node.hex(arg))
1879 elif c == 'b':
1877 elif c == 'b':
1880 return quote(arg.branch())
1878 return quote(arg.branch())
1881
1879
1882 def listexp(s, t):
1880 def listexp(s, t):
1883 l = len(s)
1881 l = len(s)
1884 if l == 0:
1882 if l == 0:
1885 return "_list('')"
1883 return "_list('')"
1886 elif l == 1:
1884 elif l == 1:
1887 return argtype(t, s[0])
1885 return argtype(t, s[0])
1888 elif t == 'd':
1886 elif t == 'd':
1889 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1887 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1890 elif t == 's':
1888 elif t == 's':
1891 return "_list('%s')" % "\0".join(s)
1889 return "_list('%s')" % "\0".join(s)
1892 elif t == 'n':
1890 elif t == 'n':
1893 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
1891 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
1894 elif t == 'b':
1892 elif t == 'b':
1895 return "_list('%s')" % "\0".join(a.branch() for a in s)
1893 return "_list('%s')" % "\0".join(a.branch() for a in s)
1896
1894
1897 m = l // 2
1895 m = l // 2
1898 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1896 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1899
1897
1900 ret = ''
1898 ret = ''
1901 pos = 0
1899 pos = 0
1902 arg = 0
1900 arg = 0
1903 while pos < len(expr):
1901 while pos < len(expr):
1904 c = expr[pos]
1902 c = expr[pos]
1905 if c == '%':
1903 if c == '%':
1906 pos += 1
1904 pos += 1
1907 d = expr[pos]
1905 d = expr[pos]
1908 if d == '%':
1906 if d == '%':
1909 ret += d
1907 ret += d
1910 elif d in 'dsnbr':
1908 elif d in 'dsnbr':
1911 ret += argtype(d, args[arg])
1909 ret += argtype(d, args[arg])
1912 arg += 1
1910 arg += 1
1913 elif d == 'l':
1911 elif d == 'l':
1914 # a list of some type
1912 # a list of some type
1915 pos += 1
1913 pos += 1
1916 d = expr[pos]
1914 d = expr[pos]
1917 ret += listexp(list(args[arg]), d)
1915 ret += listexp(list(args[arg]), d)
1918 arg += 1
1916 arg += 1
1919 else:
1917 else:
1920 raise util.Abort('unexpected revspec format character %s' % d)
1918 raise util.Abort('unexpected revspec format character %s' % d)
1921 else:
1919 else:
1922 ret += c
1920 ret += c
1923 pos += 1
1921 pos += 1
1924
1922
1925 return ret
1923 return ret
1926
1924
1927 def prettyformat(tree):
1925 def prettyformat(tree):
1928 def _prettyformat(tree, level, lines):
1926 def _prettyformat(tree, level, lines):
1929 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
1927 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
1930 lines.append((level, str(tree)))
1928 lines.append((level, str(tree)))
1931 else:
1929 else:
1932 lines.append((level, '(%s' % tree[0]))
1930 lines.append((level, '(%s' % tree[0]))
1933 for s in tree[1:]:
1931 for s in tree[1:]:
1934 _prettyformat(s, level + 1, lines)
1932 _prettyformat(s, level + 1, lines)
1935 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
1933 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
1936
1934
1937 lines = []
1935 lines = []
1938 _prettyformat(tree, 0, lines)
1936 _prettyformat(tree, 0, lines)
1939 output = '\n'.join((' '*l + s) for l, s in lines)
1937 output = '\n'.join((' '*l + s) for l, s in lines)
1940 return output
1938 return output
1941
1939
1942 # tell hggettext to extract docstrings from these functions:
1940 # tell hggettext to extract docstrings from these functions:
1943 i18nfunctions = symbols.values()
1941 i18nfunctions = symbols.values()
@@ -1,1921 +1,1927 b''
1 # util.py - Mercurial utility functions and platform specific implementations
1 # util.py - Mercurial utility functions and platform specific implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """Mercurial utility functions and platform specific implementations.
10 """Mercurial utility functions and platform specific implementations.
11
11
12 This contains helper routines that are independent of the SCM core and
12 This contains helper routines that are independent of the SCM core and
13 hide platform-specific details from the core.
13 hide platform-specific details from the core.
14 """
14 """
15
15
16 from i18n import _
16 from i18n import _
17 import error, osutil, encoding, collections
17 import error, osutil, encoding, collections
18 import errno, re, shutil, sys, tempfile, traceback
18 import errno, re, shutil, sys, tempfile, traceback
19 import os, time, datetime, calendar, textwrap, signal
19 import os, time, datetime, calendar, textwrap, signal
20 import imp, socket, urllib
20 import imp, socket, urllib
21
21
22 if os.name == 'nt':
22 if os.name == 'nt':
23 import windows as platform
23 import windows as platform
24 else:
24 else:
25 import posix as platform
25 import posix as platform
26
26
27 cachestat = platform.cachestat
27 cachestat = platform.cachestat
28 checkexec = platform.checkexec
28 checkexec = platform.checkexec
29 checklink = platform.checklink
29 checklink = platform.checklink
30 copymode = platform.copymode
30 copymode = platform.copymode
31 executablepath = platform.executablepath
31 executablepath = platform.executablepath
32 expandglobs = platform.expandglobs
32 expandglobs = platform.expandglobs
33 explainexit = platform.explainexit
33 explainexit = platform.explainexit
34 findexe = platform.findexe
34 findexe = platform.findexe
35 gethgcmd = platform.gethgcmd
35 gethgcmd = platform.gethgcmd
36 getuser = platform.getuser
36 getuser = platform.getuser
37 groupmembers = platform.groupmembers
37 groupmembers = platform.groupmembers
38 groupname = platform.groupname
38 groupname = platform.groupname
39 hidewindow = platform.hidewindow
39 hidewindow = platform.hidewindow
40 isexec = platform.isexec
40 isexec = platform.isexec
41 isowner = platform.isowner
41 isowner = platform.isowner
42 localpath = platform.localpath
42 localpath = platform.localpath
43 lookupreg = platform.lookupreg
43 lookupreg = platform.lookupreg
44 makedir = platform.makedir
44 makedir = platform.makedir
45 nlinks = platform.nlinks
45 nlinks = platform.nlinks
46 normpath = platform.normpath
46 normpath = platform.normpath
47 normcase = platform.normcase
47 normcase = platform.normcase
48 openhardlinks = platform.openhardlinks
48 openhardlinks = platform.openhardlinks
49 oslink = platform.oslink
49 oslink = platform.oslink
50 parsepatchoutput = platform.parsepatchoutput
50 parsepatchoutput = platform.parsepatchoutput
51 pconvert = platform.pconvert
51 pconvert = platform.pconvert
52 popen = platform.popen
52 popen = platform.popen
53 posixfile = platform.posixfile
53 posixfile = platform.posixfile
54 quotecommand = platform.quotecommand
54 quotecommand = platform.quotecommand
55 realpath = platform.realpath
55 realpath = platform.realpath
56 rename = platform.rename
56 rename = platform.rename
57 samedevice = platform.samedevice
57 samedevice = platform.samedevice
58 samefile = platform.samefile
58 samefile = platform.samefile
59 samestat = platform.samestat
59 samestat = platform.samestat
60 setbinary = platform.setbinary
60 setbinary = platform.setbinary
61 setflags = platform.setflags
61 setflags = platform.setflags
62 setsignalhandler = platform.setsignalhandler
62 setsignalhandler = platform.setsignalhandler
63 shellquote = platform.shellquote
63 shellquote = platform.shellquote
64 spawndetached = platform.spawndetached
64 spawndetached = platform.spawndetached
65 split = platform.split
65 split = platform.split
66 sshargs = platform.sshargs
66 sshargs = platform.sshargs
67 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
67 statfiles = getattr(osutil, 'statfiles', platform.statfiles)
68 termwidth = platform.termwidth
68 termwidth = platform.termwidth
69 testpid = platform.testpid
69 testpid = platform.testpid
70 umask = platform.umask
70 umask = platform.umask
71 unlink = platform.unlink
71 unlink = platform.unlink
72 unlinkpath = platform.unlinkpath
72 unlinkpath = platform.unlinkpath
73 username = platform.username
73 username = platform.username
74
74
75 # Python compatibility
75 # Python compatibility
76
76
77 _notset = object()
77 _notset = object()
78
78
79 def safehasattr(thing, attr):
79 def safehasattr(thing, attr):
80 return getattr(thing, attr, _notset) is not _notset
80 return getattr(thing, attr, _notset) is not _notset
81
81
82 def sha1(s=''):
82 def sha1(s=''):
83 '''
83 '''
84 Low-overhead wrapper around Python's SHA support
84 Low-overhead wrapper around Python's SHA support
85
85
86 >>> f = _fastsha1
86 >>> f = _fastsha1
87 >>> a = sha1()
87 >>> a = sha1()
88 >>> a = f()
88 >>> a = f()
89 >>> a.hexdigest()
89 >>> a.hexdigest()
90 'da39a3ee5e6b4b0d3255bfef95601890afd80709'
90 'da39a3ee5e6b4b0d3255bfef95601890afd80709'
91 '''
91 '''
92
92
93 return _fastsha1(s)
93 return _fastsha1(s)
94
94
95 def _fastsha1(s=''):
95 def _fastsha1(s=''):
96 # This function will import sha1 from hashlib or sha (whichever is
96 # This function will import sha1 from hashlib or sha (whichever is
97 # available) and overwrite itself with it on the first call.
97 # available) and overwrite itself with it on the first call.
98 # Subsequent calls will go directly to the imported function.
98 # Subsequent calls will go directly to the imported function.
99 if sys.version_info >= (2, 5):
99 if sys.version_info >= (2, 5):
100 from hashlib import sha1 as _sha1
100 from hashlib import sha1 as _sha1
101 else:
101 else:
102 from sha import sha as _sha1
102 from sha import sha as _sha1
103 global _fastsha1, sha1
103 global _fastsha1, sha1
104 _fastsha1 = sha1 = _sha1
104 _fastsha1 = sha1 = _sha1
105 return _sha1(s)
105 return _sha1(s)
106
106
107 try:
107 try:
108 buffer = buffer
108 buffer = buffer
109 except NameError:
109 except NameError:
110 if sys.version_info[0] < 3:
110 if sys.version_info[0] < 3:
111 def buffer(sliceable, offset=0):
111 def buffer(sliceable, offset=0):
112 return sliceable[offset:]
112 return sliceable[offset:]
113 else:
113 else:
114 def buffer(sliceable, offset=0):
114 def buffer(sliceable, offset=0):
115 return memoryview(sliceable)[offset:]
115 return memoryview(sliceable)[offset:]
116
116
117 import subprocess
117 import subprocess
118 closefds = os.name == 'posix'
118 closefds = os.name == 'posix'
119
119
120 def popen2(cmd, env=None, newlines=False):
120 def popen2(cmd, env=None, newlines=False):
121 # Setting bufsize to -1 lets the system decide the buffer size.
121 # Setting bufsize to -1 lets the system decide the buffer size.
122 # The default for bufsize is 0, meaning unbuffered. This leads to
122 # The default for bufsize is 0, meaning unbuffered. This leads to
123 # poor performance on Mac OS X: http://bugs.python.org/issue4194
123 # poor performance on Mac OS X: http://bugs.python.org/issue4194
124 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
124 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
125 close_fds=closefds,
125 close_fds=closefds,
126 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
126 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
127 universal_newlines=newlines,
127 universal_newlines=newlines,
128 env=env)
128 env=env)
129 return p.stdin, p.stdout
129 return p.stdin, p.stdout
130
130
131 def popen3(cmd, env=None, newlines=False):
131 def popen3(cmd, env=None, newlines=False):
132 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
132 stdin, stdout, stderr, p = popen4(cmd, env, newlines)
133 return stdin, stdout, stderr
133 return stdin, stdout, stderr
134
134
135 def popen4(cmd, env=None, newlines=False):
135 def popen4(cmd, env=None, newlines=False):
136 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
136 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
137 close_fds=closefds,
137 close_fds=closefds,
138 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
138 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
139 stderr=subprocess.PIPE,
139 stderr=subprocess.PIPE,
140 universal_newlines=newlines,
140 universal_newlines=newlines,
141 env=env)
141 env=env)
142 return p.stdin, p.stdout, p.stderr, p
142 return p.stdin, p.stdout, p.stderr, p
143
143
144 def version():
144 def version():
145 """Return version information if available."""
145 """Return version information if available."""
146 try:
146 try:
147 import __version__
147 import __version__
148 return __version__.version
148 return __version__.version
149 except ImportError:
149 except ImportError:
150 return 'unknown'
150 return 'unknown'
151
151
152 # used by parsedate
152 # used by parsedate
153 defaultdateformats = (
153 defaultdateformats = (
154 '%Y-%m-%d %H:%M:%S',
154 '%Y-%m-%d %H:%M:%S',
155 '%Y-%m-%d %I:%M:%S%p',
155 '%Y-%m-%d %I:%M:%S%p',
156 '%Y-%m-%d %H:%M',
156 '%Y-%m-%d %H:%M',
157 '%Y-%m-%d %I:%M%p',
157 '%Y-%m-%d %I:%M%p',
158 '%Y-%m-%d',
158 '%Y-%m-%d',
159 '%m-%d',
159 '%m-%d',
160 '%m/%d',
160 '%m/%d',
161 '%m/%d/%y',
161 '%m/%d/%y',
162 '%m/%d/%Y',
162 '%m/%d/%Y',
163 '%a %b %d %H:%M:%S %Y',
163 '%a %b %d %H:%M:%S %Y',
164 '%a %b %d %I:%M:%S%p %Y',
164 '%a %b %d %I:%M:%S%p %Y',
165 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
165 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
166 '%b %d %H:%M:%S %Y',
166 '%b %d %H:%M:%S %Y',
167 '%b %d %I:%M:%S%p %Y',
167 '%b %d %I:%M:%S%p %Y',
168 '%b %d %H:%M:%S',
168 '%b %d %H:%M:%S',
169 '%b %d %I:%M:%S%p',
169 '%b %d %I:%M:%S%p',
170 '%b %d %H:%M',
170 '%b %d %H:%M',
171 '%b %d %I:%M%p',
171 '%b %d %I:%M%p',
172 '%b %d %Y',
172 '%b %d %Y',
173 '%b %d',
173 '%b %d',
174 '%H:%M:%S',
174 '%H:%M:%S',
175 '%I:%M:%S%p',
175 '%I:%M:%S%p',
176 '%H:%M',
176 '%H:%M',
177 '%I:%M%p',
177 '%I:%M%p',
178 )
178 )
179
179
180 extendeddateformats = defaultdateformats + (
180 extendeddateformats = defaultdateformats + (
181 "%Y",
181 "%Y",
182 "%Y-%m",
182 "%Y-%m",
183 "%b",
183 "%b",
184 "%b %Y",
184 "%b %Y",
185 )
185 )
186
186
187 def cachefunc(func):
187 def cachefunc(func):
188 '''cache the result of function calls'''
188 '''cache the result of function calls'''
189 # XXX doesn't handle keywords args
189 # XXX doesn't handle keywords args
190 cache = {}
190 cache = {}
191 if func.func_code.co_argcount == 1:
191 if func.func_code.co_argcount == 1:
192 # we gain a small amount of time because
192 # we gain a small amount of time because
193 # we don't need to pack/unpack the list
193 # we don't need to pack/unpack the list
194 def f(arg):
194 def f(arg):
195 if arg not in cache:
195 if arg not in cache:
196 cache[arg] = func(arg)
196 cache[arg] = func(arg)
197 return cache[arg]
197 return cache[arg]
198 else:
198 else:
199 def f(*args):
199 def f(*args):
200 if args not in cache:
200 if args not in cache:
201 cache[args] = func(*args)
201 cache[args] = func(*args)
202 return cache[args]
202 return cache[args]
203
203
204 return f
204 return f
205
205
206 try:
206 try:
207 collections.deque.remove
207 collections.deque.remove
208 deque = collections.deque
208 deque = collections.deque
209 except AttributeError:
209 except AttributeError:
210 # python 2.4 lacks deque.remove
210 # python 2.4 lacks deque.remove
211 class deque(collections.deque):
211 class deque(collections.deque):
212 def remove(self, val):
212 def remove(self, val):
213 for i, v in enumerate(self):
213 for i, v in enumerate(self):
214 if v == val:
214 if v == val:
215 del self[i]
215 del self[i]
216 break
216 break
217
217
218 class lrucachedict(object):
218 class lrucachedict(object):
219 '''cache most recent gets from or sets to this dictionary'''
219 '''cache most recent gets from or sets to this dictionary'''
220 def __init__(self, maxsize):
220 def __init__(self, maxsize):
221 self._cache = {}
221 self._cache = {}
222 self._maxsize = maxsize
222 self._maxsize = maxsize
223 self._order = deque()
223 self._order = deque()
224
224
225 def __getitem__(self, key):
225 def __getitem__(self, key):
226 value = self._cache[key]
226 value = self._cache[key]
227 self._order.remove(key)
227 self._order.remove(key)
228 self._order.append(key)
228 self._order.append(key)
229 return value
229 return value
230
230
231 def __setitem__(self, key, value):
231 def __setitem__(self, key, value):
232 if key not in self._cache:
232 if key not in self._cache:
233 if len(self._cache) >= self._maxsize:
233 if len(self._cache) >= self._maxsize:
234 del self._cache[self._order.popleft()]
234 del self._cache[self._order.popleft()]
235 else:
235 else:
236 self._order.remove(key)
236 self._order.remove(key)
237 self._cache[key] = value
237 self._cache[key] = value
238 self._order.append(key)
238 self._order.append(key)
239
239
240 def __contains__(self, key):
240 def __contains__(self, key):
241 return key in self._cache
241 return key in self._cache
242
242
243 def lrucachefunc(func):
243 def lrucachefunc(func):
244 '''cache most recent results of function calls'''
244 '''cache most recent results of function calls'''
245 cache = {}
245 cache = {}
246 order = deque()
246 order = deque()
247 if func.func_code.co_argcount == 1:
247 if func.func_code.co_argcount == 1:
248 def f(arg):
248 def f(arg):
249 if arg not in cache:
249 if arg not in cache:
250 if len(cache) > 20:
250 if len(cache) > 20:
251 del cache[order.popleft()]
251 del cache[order.popleft()]
252 cache[arg] = func(arg)
252 cache[arg] = func(arg)
253 else:
253 else:
254 order.remove(arg)
254 order.remove(arg)
255 order.append(arg)
255 order.append(arg)
256 return cache[arg]
256 return cache[arg]
257 else:
257 else:
258 def f(*args):
258 def f(*args):
259 if args not in cache:
259 if args not in cache:
260 if len(cache) > 20:
260 if len(cache) > 20:
261 del cache[order.popleft()]
261 del cache[order.popleft()]
262 cache[args] = func(*args)
262 cache[args] = func(*args)
263 else:
263 else:
264 order.remove(args)
264 order.remove(args)
265 order.append(args)
265 order.append(args)
266 return cache[args]
266 return cache[args]
267
267
268 return f
268 return f
269
269
270 class propertycache(object):
270 class propertycache(object):
271 def __init__(self, func):
271 def __init__(self, func):
272 self.func = func
272 self.func = func
273 self.name = func.__name__
273 self.name = func.__name__
274 def __get__(self, obj, type=None):
274 def __get__(self, obj, type=None):
275 result = self.func(obj)
275 result = self.func(obj)
276 self.cachevalue(obj, result)
276 self.cachevalue(obj, result)
277 return result
277 return result
278
278
279 def cachevalue(self, obj, value):
279 def cachevalue(self, obj, value):
280 setattr(obj, self.name, value)
280 setattr(obj, self.name, value)
281
281
282 def pipefilter(s, cmd):
282 def pipefilter(s, cmd):
283 '''filter string S through command CMD, returning its output'''
283 '''filter string S through command CMD, returning its output'''
284 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
284 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
285 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
285 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
286 pout, perr = p.communicate(s)
286 pout, perr = p.communicate(s)
287 return pout
287 return pout
288
288
289 def tempfilter(s, cmd):
289 def tempfilter(s, cmd):
290 '''filter string S through a pair of temporary files with CMD.
290 '''filter string S through a pair of temporary files with CMD.
291 CMD is used as a template to create the real command to be run,
291 CMD is used as a template to create the real command to be run,
292 with the strings INFILE and OUTFILE replaced by the real names of
292 with the strings INFILE and OUTFILE replaced by the real names of
293 the temporary files generated.'''
293 the temporary files generated.'''
294 inname, outname = None, None
294 inname, outname = None, None
295 try:
295 try:
296 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
296 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
297 fp = os.fdopen(infd, 'wb')
297 fp = os.fdopen(infd, 'wb')
298 fp.write(s)
298 fp.write(s)
299 fp.close()
299 fp.close()
300 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
300 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
301 os.close(outfd)
301 os.close(outfd)
302 cmd = cmd.replace('INFILE', inname)
302 cmd = cmd.replace('INFILE', inname)
303 cmd = cmd.replace('OUTFILE', outname)
303 cmd = cmd.replace('OUTFILE', outname)
304 code = os.system(cmd)
304 code = os.system(cmd)
305 if sys.platform == 'OpenVMS' and code & 1:
305 if sys.platform == 'OpenVMS' and code & 1:
306 code = 0
306 code = 0
307 if code:
307 if code:
308 raise Abort(_("command '%s' failed: %s") %
308 raise Abort(_("command '%s' failed: %s") %
309 (cmd, explainexit(code)))
309 (cmd, explainexit(code)))
310 fp = open(outname, 'rb')
310 fp = open(outname, 'rb')
311 r = fp.read()
311 r = fp.read()
312 fp.close()
312 fp.close()
313 return r
313 return r
314 finally:
314 finally:
315 try:
315 try:
316 if inname:
316 if inname:
317 os.unlink(inname)
317 os.unlink(inname)
318 except OSError:
318 except OSError:
319 pass
319 pass
320 try:
320 try:
321 if outname:
321 if outname:
322 os.unlink(outname)
322 os.unlink(outname)
323 except OSError:
323 except OSError:
324 pass
324 pass
325
325
326 filtertable = {
326 filtertable = {
327 'tempfile:': tempfilter,
327 'tempfile:': tempfilter,
328 'pipe:': pipefilter,
328 'pipe:': pipefilter,
329 }
329 }
330
330
331 def filter(s, cmd):
331 def filter(s, cmd):
332 "filter a string through a command that transforms its input to its output"
332 "filter a string through a command that transforms its input to its output"
333 for name, fn in filtertable.iteritems():
333 for name, fn in filtertable.iteritems():
334 if cmd.startswith(name):
334 if cmd.startswith(name):
335 return fn(s, cmd[len(name):].lstrip())
335 return fn(s, cmd[len(name):].lstrip())
336 return pipefilter(s, cmd)
336 return pipefilter(s, cmd)
337
337
338 def binary(s):
338 def binary(s):
339 """return true if a string is binary data"""
339 """return true if a string is binary data"""
340 return bool(s and '\0' in s)
340 return bool(s and '\0' in s)
341
341
342 def increasingchunks(source, min=1024, max=65536):
342 def increasingchunks(source, min=1024, max=65536):
343 '''return no less than min bytes per chunk while data remains,
343 '''return no less than min bytes per chunk while data remains,
344 doubling min after each chunk until it reaches max'''
344 doubling min after each chunk until it reaches max'''
345 def log2(x):
345 def log2(x):
346 if not x:
346 if not x:
347 return 0
347 return 0
348 i = 0
348 i = 0
349 while x:
349 while x:
350 x >>= 1
350 x >>= 1
351 i += 1
351 i += 1
352 return i - 1
352 return i - 1
353
353
354 buf = []
354 buf = []
355 blen = 0
355 blen = 0
356 for chunk in source:
356 for chunk in source:
357 buf.append(chunk)
357 buf.append(chunk)
358 blen += len(chunk)
358 blen += len(chunk)
359 if blen >= min:
359 if blen >= min:
360 if min < max:
360 if min < max:
361 min = min << 1
361 min = min << 1
362 nmin = 1 << log2(blen)
362 nmin = 1 << log2(blen)
363 if nmin > min:
363 if nmin > min:
364 min = nmin
364 min = nmin
365 if min > max:
365 if min > max:
366 min = max
366 min = max
367 yield ''.join(buf)
367 yield ''.join(buf)
368 blen = 0
368 blen = 0
369 buf = []
369 buf = []
370 if buf:
370 if buf:
371 yield ''.join(buf)
371 yield ''.join(buf)
372
372
373 Abort = error.Abort
373 Abort = error.Abort
374
374
375 def always(fn):
375 def always(fn):
376 return True
376 return True
377
377
378 def never(fn):
378 def never(fn):
379 return False
379 return False
380
380
381 def pathto(root, n1, n2):
381 def pathto(root, n1, n2):
382 '''return the relative path from one place to another.
382 '''return the relative path from one place to another.
383 root should use os.sep to separate directories
383 root should use os.sep to separate directories
384 n1 should use os.sep to separate directories
384 n1 should use os.sep to separate directories
385 n2 should use "/" to separate directories
385 n2 should use "/" to separate directories
386 returns an os.sep-separated path.
386 returns an os.sep-separated path.
387
387
388 If n1 is a relative path, it's assumed it's
388 If n1 is a relative path, it's assumed it's
389 relative to root.
389 relative to root.
390 n2 should always be relative to root.
390 n2 should always be relative to root.
391 '''
391 '''
392 if not n1:
392 if not n1:
393 return localpath(n2)
393 return localpath(n2)
394 if os.path.isabs(n1):
394 if os.path.isabs(n1):
395 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
395 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
396 return os.path.join(root, localpath(n2))
396 return os.path.join(root, localpath(n2))
397 n2 = '/'.join((pconvert(root), n2))
397 n2 = '/'.join((pconvert(root), n2))
398 a, b = splitpath(n1), n2.split('/')
398 a, b = splitpath(n1), n2.split('/')
399 a.reverse()
399 a.reverse()
400 b.reverse()
400 b.reverse()
401 while a and b and a[-1] == b[-1]:
401 while a and b and a[-1] == b[-1]:
402 a.pop()
402 a.pop()
403 b.pop()
403 b.pop()
404 b.reverse()
404 b.reverse()
405 return os.sep.join((['..'] * len(a)) + b) or '.'
405 return os.sep.join((['..'] * len(a)) + b) or '.'
406
406
407 _hgexecutable = None
407 _hgexecutable = None
408
408
409 def mainfrozen():
409 def mainfrozen():
410 """return True if we are a frozen executable.
410 """return True if we are a frozen executable.
411
411
412 The code supports py2exe (most common, Windows only) and tools/freeze
412 The code supports py2exe (most common, Windows only) and tools/freeze
413 (portable, not much used).
413 (portable, not much used).
414 """
414 """
415 return (safehasattr(sys, "frozen") or # new py2exe
415 return (safehasattr(sys, "frozen") or # new py2exe
416 safehasattr(sys, "importers") or # old py2exe
416 safehasattr(sys, "importers") or # old py2exe
417 imp.is_frozen("__main__")) # tools/freeze
417 imp.is_frozen("__main__")) # tools/freeze
418
418
419 def hgexecutable():
419 def hgexecutable():
420 """return location of the 'hg' executable.
420 """return location of the 'hg' executable.
421
421
422 Defaults to $HG or 'hg' in the search path.
422 Defaults to $HG or 'hg' in the search path.
423 """
423 """
424 if _hgexecutable is None:
424 if _hgexecutable is None:
425 hg = os.environ.get('HG')
425 hg = os.environ.get('HG')
426 mainmod = sys.modules['__main__']
426 mainmod = sys.modules['__main__']
427 if hg:
427 if hg:
428 _sethgexecutable(hg)
428 _sethgexecutable(hg)
429 elif mainfrozen():
429 elif mainfrozen():
430 _sethgexecutable(sys.executable)
430 _sethgexecutable(sys.executable)
431 elif os.path.basename(getattr(mainmod, '__file__', '')) == 'hg':
431 elif os.path.basename(getattr(mainmod, '__file__', '')) == 'hg':
432 _sethgexecutable(mainmod.__file__)
432 _sethgexecutable(mainmod.__file__)
433 else:
433 else:
434 exe = findexe('hg') or os.path.basename(sys.argv[0])
434 exe = findexe('hg') or os.path.basename(sys.argv[0])
435 _sethgexecutable(exe)
435 _sethgexecutable(exe)
436 return _hgexecutable
436 return _hgexecutable
437
437
438 def _sethgexecutable(path):
438 def _sethgexecutable(path):
439 """set location of the 'hg' executable"""
439 """set location of the 'hg' executable"""
440 global _hgexecutable
440 global _hgexecutable
441 _hgexecutable = path
441 _hgexecutable = path
442
442
443 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
443 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
444 '''enhanced shell command execution.
444 '''enhanced shell command execution.
445 run with environment maybe modified, maybe in different dir.
445 run with environment maybe modified, maybe in different dir.
446
446
447 if command fails and onerr is None, return status. if ui object,
447 if command fails and onerr is None, return status. if ui object,
448 print error message and return status, else raise onerr object as
448 print error message and return status, else raise onerr object as
449 exception.
449 exception.
450
450
451 if out is specified, it is assumed to be a file-like object that has a
451 if out is specified, it is assumed to be a file-like object that has a
452 write() method. stdout and stderr will be redirected to out.'''
452 write() method. stdout and stderr will be redirected to out.'''
453 try:
453 try:
454 sys.stdout.flush()
454 sys.stdout.flush()
455 except Exception:
455 except Exception:
456 pass
456 pass
457 def py2shell(val):
457 def py2shell(val):
458 'convert python object into string that is useful to shell'
458 'convert python object into string that is useful to shell'
459 if val is None or val is False:
459 if val is None or val is False:
460 return '0'
460 return '0'
461 if val is True:
461 if val is True:
462 return '1'
462 return '1'
463 return str(val)
463 return str(val)
464 origcmd = cmd
464 origcmd = cmd
465 cmd = quotecommand(cmd)
465 cmd = quotecommand(cmd)
466 if sys.platform == 'plan9':
466 if sys.platform == 'plan9':
467 # subprocess kludge to work around issues in half-baked Python
467 # subprocess kludge to work around issues in half-baked Python
468 # ports, notably bichued/python:
468 # ports, notably bichued/python:
469 if not cwd is None:
469 if not cwd is None:
470 os.chdir(cwd)
470 os.chdir(cwd)
471 rc = os.system(cmd)
471 rc = os.system(cmd)
472 else:
472 else:
473 env = dict(os.environ)
473 env = dict(os.environ)
474 env.update((k, py2shell(v)) for k, v in environ.iteritems())
474 env.update((k, py2shell(v)) for k, v in environ.iteritems())
475 env['HG'] = hgexecutable()
475 env['HG'] = hgexecutable()
476 if out is None or out == sys.__stdout__:
476 if out is None or out == sys.__stdout__:
477 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
477 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
478 env=env, cwd=cwd)
478 env=env, cwd=cwd)
479 else:
479 else:
480 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
480 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
481 env=env, cwd=cwd, stdout=subprocess.PIPE,
481 env=env, cwd=cwd, stdout=subprocess.PIPE,
482 stderr=subprocess.STDOUT)
482 stderr=subprocess.STDOUT)
483 for line in proc.stdout:
483 for line in proc.stdout:
484 out.write(line)
484 out.write(line)
485 proc.wait()
485 proc.wait()
486 rc = proc.returncode
486 rc = proc.returncode
487 if sys.platform == 'OpenVMS' and rc & 1:
487 if sys.platform == 'OpenVMS' and rc & 1:
488 rc = 0
488 rc = 0
489 if rc and onerr:
489 if rc and onerr:
490 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
490 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
491 explainexit(rc)[0])
491 explainexit(rc)[0])
492 if errprefix:
492 if errprefix:
493 errmsg = '%s: %s' % (errprefix, errmsg)
493 errmsg = '%s: %s' % (errprefix, errmsg)
494 try:
494 try:
495 onerr.warn(errmsg + '\n')
495 onerr.warn(errmsg + '\n')
496 except AttributeError:
496 except AttributeError:
497 raise onerr(errmsg)
497 raise onerr(errmsg)
498 return rc
498 return rc
499
499
500 def checksignature(func):
500 def checksignature(func):
501 '''wrap a function with code to check for calling errors'''
501 '''wrap a function with code to check for calling errors'''
502 def check(*args, **kwargs):
502 def check(*args, **kwargs):
503 try:
503 try:
504 return func(*args, **kwargs)
504 return func(*args, **kwargs)
505 except TypeError:
505 except TypeError:
506 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
506 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
507 raise error.SignatureError
507 raise error.SignatureError
508 raise
508 raise
509
509
510 return check
510 return check
511
511
512 def copyfile(src, dest):
512 def copyfile(src, dest):
513 "copy a file, preserving mode and atime/mtime"
513 "copy a file, preserving mode and atime/mtime"
514 if os.path.lexists(dest):
514 if os.path.lexists(dest):
515 unlink(dest)
515 unlink(dest)
516 if os.path.islink(src):
516 if os.path.islink(src):
517 os.symlink(os.readlink(src), dest)
517 os.symlink(os.readlink(src), dest)
518 else:
518 else:
519 try:
519 try:
520 shutil.copyfile(src, dest)
520 shutil.copyfile(src, dest)
521 shutil.copymode(src, dest)
521 shutil.copymode(src, dest)
522 except shutil.Error, inst:
522 except shutil.Error, inst:
523 raise Abort(str(inst))
523 raise Abort(str(inst))
524
524
525 def copyfiles(src, dst, hardlink=None):
525 def copyfiles(src, dst, hardlink=None):
526 """Copy a directory tree using hardlinks if possible"""
526 """Copy a directory tree using hardlinks if possible"""
527
527
528 if hardlink is None:
528 if hardlink is None:
529 hardlink = (os.stat(src).st_dev ==
529 hardlink = (os.stat(src).st_dev ==
530 os.stat(os.path.dirname(dst)).st_dev)
530 os.stat(os.path.dirname(dst)).st_dev)
531
531
532 num = 0
532 num = 0
533 if os.path.isdir(src):
533 if os.path.isdir(src):
534 os.mkdir(dst)
534 os.mkdir(dst)
535 for name, kind in osutil.listdir(src):
535 for name, kind in osutil.listdir(src):
536 srcname = os.path.join(src, name)
536 srcname = os.path.join(src, name)
537 dstname = os.path.join(dst, name)
537 dstname = os.path.join(dst, name)
538 hardlink, n = copyfiles(srcname, dstname, hardlink)
538 hardlink, n = copyfiles(srcname, dstname, hardlink)
539 num += n
539 num += n
540 else:
540 else:
541 if hardlink:
541 if hardlink:
542 try:
542 try:
543 oslink(src, dst)
543 oslink(src, dst)
544 except (IOError, OSError):
544 except (IOError, OSError):
545 hardlink = False
545 hardlink = False
546 shutil.copy(src, dst)
546 shutil.copy(src, dst)
547 else:
547 else:
548 shutil.copy(src, dst)
548 shutil.copy(src, dst)
549 num += 1
549 num += 1
550
550
551 return hardlink, num
551 return hardlink, num
552
552
553 _winreservednames = '''con prn aux nul
553 _winreservednames = '''con prn aux nul
554 com1 com2 com3 com4 com5 com6 com7 com8 com9
554 com1 com2 com3 com4 com5 com6 com7 com8 com9
555 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
555 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
556 _winreservedchars = ':*?"<>|'
556 _winreservedchars = ':*?"<>|'
557 def checkwinfilename(path):
557 def checkwinfilename(path):
558 '''Check that the base-relative path is a valid filename on Windows.
558 '''Check that the base-relative path is a valid filename on Windows.
559 Returns None if the path is ok, or a UI string describing the problem.
559 Returns None if the path is ok, or a UI string describing the problem.
560
560
561 >>> checkwinfilename("just/a/normal/path")
561 >>> checkwinfilename("just/a/normal/path")
562 >>> checkwinfilename("foo/bar/con.xml")
562 >>> checkwinfilename("foo/bar/con.xml")
563 "filename contains 'con', which is reserved on Windows"
563 "filename contains 'con', which is reserved on Windows"
564 >>> checkwinfilename("foo/con.xml/bar")
564 >>> checkwinfilename("foo/con.xml/bar")
565 "filename contains 'con', which is reserved on Windows"
565 "filename contains 'con', which is reserved on Windows"
566 >>> checkwinfilename("foo/bar/xml.con")
566 >>> checkwinfilename("foo/bar/xml.con")
567 >>> checkwinfilename("foo/bar/AUX/bla.txt")
567 >>> checkwinfilename("foo/bar/AUX/bla.txt")
568 "filename contains 'AUX', which is reserved on Windows"
568 "filename contains 'AUX', which is reserved on Windows"
569 >>> checkwinfilename("foo/bar/bla:.txt")
569 >>> checkwinfilename("foo/bar/bla:.txt")
570 "filename contains ':', which is reserved on Windows"
570 "filename contains ':', which is reserved on Windows"
571 >>> checkwinfilename("foo/bar/b\07la.txt")
571 >>> checkwinfilename("foo/bar/b\07la.txt")
572 "filename contains '\\\\x07', which is invalid on Windows"
572 "filename contains '\\\\x07', which is invalid on Windows"
573 >>> checkwinfilename("foo/bar/bla ")
573 >>> checkwinfilename("foo/bar/bla ")
574 "filename ends with ' ', which is not allowed on Windows"
574 "filename ends with ' ', which is not allowed on Windows"
575 >>> checkwinfilename("../bar")
575 >>> checkwinfilename("../bar")
576 '''
576 '''
577 for n in path.replace('\\', '/').split('/'):
577 for n in path.replace('\\', '/').split('/'):
578 if not n:
578 if not n:
579 continue
579 continue
580 for c in n:
580 for c in n:
581 if c in _winreservedchars:
581 if c in _winreservedchars:
582 return _("filename contains '%s', which is reserved "
582 return _("filename contains '%s', which is reserved "
583 "on Windows") % c
583 "on Windows") % c
584 if ord(c) <= 31:
584 if ord(c) <= 31:
585 return _("filename contains %r, which is invalid "
585 return _("filename contains %r, which is invalid "
586 "on Windows") % c
586 "on Windows") % c
587 base = n.split('.')[0]
587 base = n.split('.')[0]
588 if base and base.lower() in _winreservednames:
588 if base and base.lower() in _winreservednames:
589 return _("filename contains '%s', which is reserved "
589 return _("filename contains '%s', which is reserved "
590 "on Windows") % base
590 "on Windows") % base
591 t = n[-1]
591 t = n[-1]
592 if t in '. ' and n not in '..':
592 if t in '. ' and n not in '..':
593 return _("filename ends with '%s', which is not allowed "
593 return _("filename ends with '%s', which is not allowed "
594 "on Windows") % t
594 "on Windows") % t
595
595
596 if os.name == 'nt':
596 if os.name == 'nt':
597 checkosfilename = checkwinfilename
597 checkosfilename = checkwinfilename
598 else:
598 else:
599 checkosfilename = platform.checkosfilename
599 checkosfilename = platform.checkosfilename
600
600
601 def makelock(info, pathname):
601 def makelock(info, pathname):
602 try:
602 try:
603 return os.symlink(info, pathname)
603 return os.symlink(info, pathname)
604 except OSError, why:
604 except OSError, why:
605 if why.errno == errno.EEXIST:
605 if why.errno == errno.EEXIST:
606 raise
606 raise
607 except AttributeError: # no symlink in os
607 except AttributeError: # no symlink in os
608 pass
608 pass
609
609
610 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
610 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
611 os.write(ld, info)
611 os.write(ld, info)
612 os.close(ld)
612 os.close(ld)
613
613
614 def readlock(pathname):
614 def readlock(pathname):
615 try:
615 try:
616 return os.readlink(pathname)
616 return os.readlink(pathname)
617 except OSError, why:
617 except OSError, why:
618 if why.errno not in (errno.EINVAL, errno.ENOSYS):
618 if why.errno not in (errno.EINVAL, errno.ENOSYS):
619 raise
619 raise
620 except AttributeError: # no symlink in os
620 except AttributeError: # no symlink in os
621 pass
621 pass
622 fp = posixfile(pathname)
622 fp = posixfile(pathname)
623 r = fp.read()
623 r = fp.read()
624 fp.close()
624 fp.close()
625 return r
625 return r
626
626
627 def fstat(fp):
627 def fstat(fp):
628 '''stat file object that may not have fileno method.'''
628 '''stat file object that may not have fileno method.'''
629 try:
629 try:
630 return os.fstat(fp.fileno())
630 return os.fstat(fp.fileno())
631 except AttributeError:
631 except AttributeError:
632 return os.stat(fp.name)
632 return os.stat(fp.name)
633
633
634 # File system features
634 # File system features
635
635
636 def checkcase(path):
636 def checkcase(path):
637 """
637 """
638 Check whether the given path is on a case-sensitive filesystem
638 Check whether the given path is on a case-sensitive filesystem
639
639
640 Requires a path (like /foo/.hg) ending with a foldable final
640 Requires a path (like /foo/.hg) ending with a foldable final
641 directory component.
641 directory component.
642 """
642 """
643 s1 = os.stat(path)
643 s1 = os.stat(path)
644 d, b = os.path.split(path)
644 d, b = os.path.split(path)
645 b2 = b.upper()
645 b2 = b.upper()
646 if b == b2:
646 if b == b2:
647 b2 = b.lower()
647 b2 = b.lower()
648 if b == b2:
648 if b == b2:
649 return True # no evidence against case sensitivity
649 return True # no evidence against case sensitivity
650 p2 = os.path.join(d, b2)
650 p2 = os.path.join(d, b2)
651 try:
651 try:
652 s2 = os.stat(p2)
652 s2 = os.stat(p2)
653 if s2 == s1:
653 if s2 == s1:
654 return False
654 return False
655 return True
655 return True
656 except OSError:
656 except OSError:
657 return True
657 return True
658
658
659 try:
659 try:
660 import re2
660 import re2
661 _re2 = None
661 _re2 = None
662 except ImportError:
662 except ImportError:
663 _re2 = False
663 _re2 = False
664
664
665 def compilere(pat):
665 def compilere(pat, flags=0):
666 '''Compile a regular expression, using re2 if possible
666 '''Compile a regular expression, using re2 if possible
667
667
668 For best performance, use only re2-compatible regexp features.'''
668 For best performance, use only re2-compatible regexp features. The
669 only flags from the re module that are re2-compatible are
670 IGNORECASE and MULTILINE.'''
669 global _re2
671 global _re2
670 if _re2 is None:
672 if _re2 is None:
671 try:
673 try:
672 re2.compile
674 re2.compile
673 _re2 = True
675 _re2 = True
674 except ImportError:
676 except ImportError:
675 _re2 = False
677 _re2 = False
676 if _re2:
678 if _re2 and (flags & ~(re.IGNORECASE | re.MULTILINE)) == 0:
679 if flags & re.IGNORECASE:
680 pat = '(?i)' + pat
681 if flags & re.MULTILINE:
682 pat = '(?m)' + pat
677 try:
683 try:
678 return re2.compile(pat)
684 return re2.compile(pat)
679 except re2.error:
685 except re2.error:
680 pass
686 pass
681 return re.compile(pat)
687 return re.compile(pat, flags)
682
688
683 _fspathcache = {}
689 _fspathcache = {}
684 def fspath(name, root):
690 def fspath(name, root):
685 '''Get name in the case stored in the filesystem
691 '''Get name in the case stored in the filesystem
686
692
687 The name should be relative to root, and be normcase-ed for efficiency.
693 The name should be relative to root, and be normcase-ed for efficiency.
688
694
689 Note that this function is unnecessary, and should not be
695 Note that this function is unnecessary, and should not be
690 called, for case-sensitive filesystems (simply because it's expensive).
696 called, for case-sensitive filesystems (simply because it's expensive).
691
697
692 The root should be normcase-ed, too.
698 The root should be normcase-ed, too.
693 '''
699 '''
694 def find(p, contents):
700 def find(p, contents):
695 for n in contents:
701 for n in contents:
696 if normcase(n) == p:
702 if normcase(n) == p:
697 return n
703 return n
698 return None
704 return None
699
705
700 seps = os.sep
706 seps = os.sep
701 if os.altsep:
707 if os.altsep:
702 seps = seps + os.altsep
708 seps = seps + os.altsep
703 # Protect backslashes. This gets silly very quickly.
709 # Protect backslashes. This gets silly very quickly.
704 seps.replace('\\','\\\\')
710 seps.replace('\\','\\\\')
705 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
711 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
706 dir = os.path.normpath(root)
712 dir = os.path.normpath(root)
707 result = []
713 result = []
708 for part, sep in pattern.findall(name):
714 for part, sep in pattern.findall(name):
709 if sep:
715 if sep:
710 result.append(sep)
716 result.append(sep)
711 continue
717 continue
712
718
713 if dir not in _fspathcache:
719 if dir not in _fspathcache:
714 _fspathcache[dir] = os.listdir(dir)
720 _fspathcache[dir] = os.listdir(dir)
715 contents = _fspathcache[dir]
721 contents = _fspathcache[dir]
716
722
717 found = find(part, contents)
723 found = find(part, contents)
718 if not found:
724 if not found:
719 # retry "once per directory" per "dirstate.walk" which
725 # retry "once per directory" per "dirstate.walk" which
720 # may take place for each patches of "hg qpush", for example
726 # may take place for each patches of "hg qpush", for example
721 contents = os.listdir(dir)
727 contents = os.listdir(dir)
722 _fspathcache[dir] = contents
728 _fspathcache[dir] = contents
723 found = find(part, contents)
729 found = find(part, contents)
724
730
725 result.append(found or part)
731 result.append(found or part)
726 dir = os.path.join(dir, part)
732 dir = os.path.join(dir, part)
727
733
728 return ''.join(result)
734 return ''.join(result)
729
735
730 def checknlink(testfile):
736 def checknlink(testfile):
731 '''check whether hardlink count reporting works properly'''
737 '''check whether hardlink count reporting works properly'''
732
738
733 # testfile may be open, so we need a separate file for checking to
739 # testfile may be open, so we need a separate file for checking to
734 # work around issue2543 (or testfile may get lost on Samba shares)
740 # work around issue2543 (or testfile may get lost on Samba shares)
735 f1 = testfile + ".hgtmp1"
741 f1 = testfile + ".hgtmp1"
736 if os.path.lexists(f1):
742 if os.path.lexists(f1):
737 return False
743 return False
738 try:
744 try:
739 posixfile(f1, 'w').close()
745 posixfile(f1, 'w').close()
740 except IOError:
746 except IOError:
741 return False
747 return False
742
748
743 f2 = testfile + ".hgtmp2"
749 f2 = testfile + ".hgtmp2"
744 fd = None
750 fd = None
745 try:
751 try:
746 try:
752 try:
747 oslink(f1, f2)
753 oslink(f1, f2)
748 except OSError:
754 except OSError:
749 return False
755 return False
750
756
751 # nlinks() may behave differently for files on Windows shares if
757 # nlinks() may behave differently for files on Windows shares if
752 # the file is open.
758 # the file is open.
753 fd = posixfile(f2)
759 fd = posixfile(f2)
754 return nlinks(f2) > 1
760 return nlinks(f2) > 1
755 finally:
761 finally:
756 if fd is not None:
762 if fd is not None:
757 fd.close()
763 fd.close()
758 for f in (f1, f2):
764 for f in (f1, f2):
759 try:
765 try:
760 os.unlink(f)
766 os.unlink(f)
761 except OSError:
767 except OSError:
762 pass
768 pass
763
769
764 return False
770 return False
765
771
766 def endswithsep(path):
772 def endswithsep(path):
767 '''Check path ends with os.sep or os.altsep.'''
773 '''Check path ends with os.sep or os.altsep.'''
768 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
774 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
769
775
770 def splitpath(path):
776 def splitpath(path):
771 '''Split path by os.sep.
777 '''Split path by os.sep.
772 Note that this function does not use os.altsep because this is
778 Note that this function does not use os.altsep because this is
773 an alternative of simple "xxx.split(os.sep)".
779 an alternative of simple "xxx.split(os.sep)".
774 It is recommended to use os.path.normpath() before using this
780 It is recommended to use os.path.normpath() before using this
775 function if need.'''
781 function if need.'''
776 return path.split(os.sep)
782 return path.split(os.sep)
777
783
778 def gui():
784 def gui():
779 '''Are we running in a GUI?'''
785 '''Are we running in a GUI?'''
780 if sys.platform == 'darwin':
786 if sys.platform == 'darwin':
781 if 'SSH_CONNECTION' in os.environ:
787 if 'SSH_CONNECTION' in os.environ:
782 # handle SSH access to a box where the user is logged in
788 # handle SSH access to a box where the user is logged in
783 return False
789 return False
784 elif getattr(osutil, 'isgui', None):
790 elif getattr(osutil, 'isgui', None):
785 # check if a CoreGraphics session is available
791 # check if a CoreGraphics session is available
786 return osutil.isgui()
792 return osutil.isgui()
787 else:
793 else:
788 # pure build; use a safe default
794 # pure build; use a safe default
789 return True
795 return True
790 else:
796 else:
791 return os.name == "nt" or os.environ.get("DISPLAY")
797 return os.name == "nt" or os.environ.get("DISPLAY")
792
798
793 def mktempcopy(name, emptyok=False, createmode=None):
799 def mktempcopy(name, emptyok=False, createmode=None):
794 """Create a temporary file with the same contents from name
800 """Create a temporary file with the same contents from name
795
801
796 The permission bits are copied from the original file.
802 The permission bits are copied from the original file.
797
803
798 If the temporary file is going to be truncated immediately, you
804 If the temporary file is going to be truncated immediately, you
799 can use emptyok=True as an optimization.
805 can use emptyok=True as an optimization.
800
806
801 Returns the name of the temporary file.
807 Returns the name of the temporary file.
802 """
808 """
803 d, fn = os.path.split(name)
809 d, fn = os.path.split(name)
804 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
810 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
805 os.close(fd)
811 os.close(fd)
806 # Temporary files are created with mode 0600, which is usually not
812 # Temporary files are created with mode 0600, which is usually not
807 # what we want. If the original file already exists, just copy
813 # what we want. If the original file already exists, just copy
808 # its mode. Otherwise, manually obey umask.
814 # its mode. Otherwise, manually obey umask.
809 copymode(name, temp, createmode)
815 copymode(name, temp, createmode)
810 if emptyok:
816 if emptyok:
811 return temp
817 return temp
812 try:
818 try:
813 try:
819 try:
814 ifp = posixfile(name, "rb")
820 ifp = posixfile(name, "rb")
815 except IOError, inst:
821 except IOError, inst:
816 if inst.errno == errno.ENOENT:
822 if inst.errno == errno.ENOENT:
817 return temp
823 return temp
818 if not getattr(inst, 'filename', None):
824 if not getattr(inst, 'filename', None):
819 inst.filename = name
825 inst.filename = name
820 raise
826 raise
821 ofp = posixfile(temp, "wb")
827 ofp = posixfile(temp, "wb")
822 for chunk in filechunkiter(ifp):
828 for chunk in filechunkiter(ifp):
823 ofp.write(chunk)
829 ofp.write(chunk)
824 ifp.close()
830 ifp.close()
825 ofp.close()
831 ofp.close()
826 except: # re-raises
832 except: # re-raises
827 try: os.unlink(temp)
833 try: os.unlink(temp)
828 except OSError: pass
834 except OSError: pass
829 raise
835 raise
830 return temp
836 return temp
831
837
832 class atomictempfile(object):
838 class atomictempfile(object):
833 '''writable file object that atomically updates a file
839 '''writable file object that atomically updates a file
834
840
835 All writes will go to a temporary copy of the original file. Call
841 All writes will go to a temporary copy of the original file. Call
836 close() when you are done writing, and atomictempfile will rename
842 close() when you are done writing, and atomictempfile will rename
837 the temporary copy to the original name, making the changes
843 the temporary copy to the original name, making the changes
838 visible. If the object is destroyed without being closed, all your
844 visible. If the object is destroyed without being closed, all your
839 writes are discarded.
845 writes are discarded.
840 '''
846 '''
841 def __init__(self, name, mode='w+b', createmode=None):
847 def __init__(self, name, mode='w+b', createmode=None):
842 self.__name = name # permanent name
848 self.__name = name # permanent name
843 self._tempname = mktempcopy(name, emptyok=('w' in mode),
849 self._tempname = mktempcopy(name, emptyok=('w' in mode),
844 createmode=createmode)
850 createmode=createmode)
845 self._fp = posixfile(self._tempname, mode)
851 self._fp = posixfile(self._tempname, mode)
846
852
847 # delegated methods
853 # delegated methods
848 self.write = self._fp.write
854 self.write = self._fp.write
849 self.seek = self._fp.seek
855 self.seek = self._fp.seek
850 self.tell = self._fp.tell
856 self.tell = self._fp.tell
851 self.fileno = self._fp.fileno
857 self.fileno = self._fp.fileno
852
858
853 def close(self):
859 def close(self):
854 if not self._fp.closed:
860 if not self._fp.closed:
855 self._fp.close()
861 self._fp.close()
856 rename(self._tempname, localpath(self.__name))
862 rename(self._tempname, localpath(self.__name))
857
863
858 def discard(self):
864 def discard(self):
859 if not self._fp.closed:
865 if not self._fp.closed:
860 try:
866 try:
861 os.unlink(self._tempname)
867 os.unlink(self._tempname)
862 except OSError:
868 except OSError:
863 pass
869 pass
864 self._fp.close()
870 self._fp.close()
865
871
866 def __del__(self):
872 def __del__(self):
867 if safehasattr(self, '_fp'): # constructor actually did something
873 if safehasattr(self, '_fp'): # constructor actually did something
868 self.discard()
874 self.discard()
869
875
870 def makedirs(name, mode=None):
876 def makedirs(name, mode=None):
871 """recursive directory creation with parent mode inheritance"""
877 """recursive directory creation with parent mode inheritance"""
872 try:
878 try:
873 os.mkdir(name)
879 os.mkdir(name)
874 except OSError, err:
880 except OSError, err:
875 if err.errno == errno.EEXIST:
881 if err.errno == errno.EEXIST:
876 return
882 return
877 if err.errno != errno.ENOENT or not name:
883 if err.errno != errno.ENOENT or not name:
878 raise
884 raise
879 parent = os.path.dirname(os.path.abspath(name))
885 parent = os.path.dirname(os.path.abspath(name))
880 if parent == name:
886 if parent == name:
881 raise
887 raise
882 makedirs(parent, mode)
888 makedirs(parent, mode)
883 os.mkdir(name)
889 os.mkdir(name)
884 if mode is not None:
890 if mode is not None:
885 os.chmod(name, mode)
891 os.chmod(name, mode)
886
892
887 def ensuredirs(name, mode=None):
893 def ensuredirs(name, mode=None):
888 """race-safe recursive directory creation"""
894 """race-safe recursive directory creation"""
889 if os.path.isdir(name):
895 if os.path.isdir(name):
890 return
896 return
891 parent = os.path.dirname(os.path.abspath(name))
897 parent = os.path.dirname(os.path.abspath(name))
892 if parent != name:
898 if parent != name:
893 ensuredirs(parent, mode)
899 ensuredirs(parent, mode)
894 try:
900 try:
895 os.mkdir(name)
901 os.mkdir(name)
896 except OSError, err:
902 except OSError, err:
897 if err.errno == errno.EEXIST and os.path.isdir(name):
903 if err.errno == errno.EEXIST and os.path.isdir(name):
898 # someone else seems to have won a directory creation race
904 # someone else seems to have won a directory creation race
899 return
905 return
900 raise
906 raise
901 if mode is not None:
907 if mode is not None:
902 os.chmod(name, mode)
908 os.chmod(name, mode)
903
909
904 def readfile(path):
910 def readfile(path):
905 fp = open(path, 'rb')
911 fp = open(path, 'rb')
906 try:
912 try:
907 return fp.read()
913 return fp.read()
908 finally:
914 finally:
909 fp.close()
915 fp.close()
910
916
911 def writefile(path, text):
917 def writefile(path, text):
912 fp = open(path, 'wb')
918 fp = open(path, 'wb')
913 try:
919 try:
914 fp.write(text)
920 fp.write(text)
915 finally:
921 finally:
916 fp.close()
922 fp.close()
917
923
918 def appendfile(path, text):
924 def appendfile(path, text):
919 fp = open(path, 'ab')
925 fp = open(path, 'ab')
920 try:
926 try:
921 fp.write(text)
927 fp.write(text)
922 finally:
928 finally:
923 fp.close()
929 fp.close()
924
930
925 class chunkbuffer(object):
931 class chunkbuffer(object):
926 """Allow arbitrary sized chunks of data to be efficiently read from an
932 """Allow arbitrary sized chunks of data to be efficiently read from an
927 iterator over chunks of arbitrary size."""
933 iterator over chunks of arbitrary size."""
928
934
929 def __init__(self, in_iter):
935 def __init__(self, in_iter):
930 """in_iter is the iterator that's iterating over the input chunks.
936 """in_iter is the iterator that's iterating over the input chunks.
931 targetsize is how big a buffer to try to maintain."""
937 targetsize is how big a buffer to try to maintain."""
932 def splitbig(chunks):
938 def splitbig(chunks):
933 for chunk in chunks:
939 for chunk in chunks:
934 if len(chunk) > 2**20:
940 if len(chunk) > 2**20:
935 pos = 0
941 pos = 0
936 while pos < len(chunk):
942 while pos < len(chunk):
937 end = pos + 2 ** 18
943 end = pos + 2 ** 18
938 yield chunk[pos:end]
944 yield chunk[pos:end]
939 pos = end
945 pos = end
940 else:
946 else:
941 yield chunk
947 yield chunk
942 self.iter = splitbig(in_iter)
948 self.iter = splitbig(in_iter)
943 self._queue = deque()
949 self._queue = deque()
944
950
945 def read(self, l):
951 def read(self, l):
946 """Read L bytes of data from the iterator of chunks of data.
952 """Read L bytes of data from the iterator of chunks of data.
947 Returns less than L bytes if the iterator runs dry."""
953 Returns less than L bytes if the iterator runs dry."""
948 left = l
954 left = l
949 buf = []
955 buf = []
950 queue = self._queue
956 queue = self._queue
951 while left > 0:
957 while left > 0:
952 # refill the queue
958 # refill the queue
953 if not queue:
959 if not queue:
954 target = 2**18
960 target = 2**18
955 for chunk in self.iter:
961 for chunk in self.iter:
956 queue.append(chunk)
962 queue.append(chunk)
957 target -= len(chunk)
963 target -= len(chunk)
958 if target <= 0:
964 if target <= 0:
959 break
965 break
960 if not queue:
966 if not queue:
961 break
967 break
962
968
963 chunk = queue.popleft()
969 chunk = queue.popleft()
964 left -= len(chunk)
970 left -= len(chunk)
965 if left < 0:
971 if left < 0:
966 queue.appendleft(chunk[left:])
972 queue.appendleft(chunk[left:])
967 buf.append(chunk[:left])
973 buf.append(chunk[:left])
968 else:
974 else:
969 buf.append(chunk)
975 buf.append(chunk)
970
976
971 return ''.join(buf)
977 return ''.join(buf)
972
978
973 def filechunkiter(f, size=65536, limit=None):
979 def filechunkiter(f, size=65536, limit=None):
974 """Create a generator that produces the data in the file size
980 """Create a generator that produces the data in the file size
975 (default 65536) bytes at a time, up to optional limit (default is
981 (default 65536) bytes at a time, up to optional limit (default is
976 to read all data). Chunks may be less than size bytes if the
982 to read all data). Chunks may be less than size bytes if the
977 chunk is the last chunk in the file, or the file is a socket or
983 chunk is the last chunk in the file, or the file is a socket or
978 some other type of file that sometimes reads less data than is
984 some other type of file that sometimes reads less data than is
979 requested."""
985 requested."""
980 assert size >= 0
986 assert size >= 0
981 assert limit is None or limit >= 0
987 assert limit is None or limit >= 0
982 while True:
988 while True:
983 if limit is None:
989 if limit is None:
984 nbytes = size
990 nbytes = size
985 else:
991 else:
986 nbytes = min(limit, size)
992 nbytes = min(limit, size)
987 s = nbytes and f.read(nbytes)
993 s = nbytes and f.read(nbytes)
988 if not s:
994 if not s:
989 break
995 break
990 if limit:
996 if limit:
991 limit -= len(s)
997 limit -= len(s)
992 yield s
998 yield s
993
999
994 def makedate():
1000 def makedate():
995 ct = time.time()
1001 ct = time.time()
996 if ct < 0:
1002 if ct < 0:
997 hint = _("check your clock")
1003 hint = _("check your clock")
998 raise Abort(_("negative timestamp: %d") % ct, hint=hint)
1004 raise Abort(_("negative timestamp: %d") % ct, hint=hint)
999 delta = (datetime.datetime.utcfromtimestamp(ct) -
1005 delta = (datetime.datetime.utcfromtimestamp(ct) -
1000 datetime.datetime.fromtimestamp(ct))
1006 datetime.datetime.fromtimestamp(ct))
1001 tz = delta.days * 86400 + delta.seconds
1007 tz = delta.days * 86400 + delta.seconds
1002 return ct, tz
1008 return ct, tz
1003
1009
1004 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1010 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1005 """represent a (unixtime, offset) tuple as a localized time.
1011 """represent a (unixtime, offset) tuple as a localized time.
1006 unixtime is seconds since the epoch, and offset is the time zone's
1012 unixtime is seconds since the epoch, and offset is the time zone's
1007 number of seconds away from UTC. if timezone is false, do not
1013 number of seconds away from UTC. if timezone is false, do not
1008 append time zone to string."""
1014 append time zone to string."""
1009 t, tz = date or makedate()
1015 t, tz = date or makedate()
1010 if t < 0:
1016 if t < 0:
1011 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1017 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1012 tz = 0
1018 tz = 0
1013 if "%1" in format or "%2" in format:
1019 if "%1" in format or "%2" in format:
1014 sign = (tz > 0) and "-" or "+"
1020 sign = (tz > 0) and "-" or "+"
1015 minutes = abs(tz) // 60
1021 minutes = abs(tz) // 60
1016 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1022 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1017 format = format.replace("%2", "%02d" % (minutes % 60))
1023 format = format.replace("%2", "%02d" % (minutes % 60))
1018 try:
1024 try:
1019 t = time.gmtime(float(t) - tz)
1025 t = time.gmtime(float(t) - tz)
1020 except ValueError:
1026 except ValueError:
1021 # time was out of range
1027 # time was out of range
1022 t = time.gmtime(sys.maxint)
1028 t = time.gmtime(sys.maxint)
1023 s = time.strftime(format, t)
1029 s = time.strftime(format, t)
1024 return s
1030 return s
1025
1031
1026 def shortdate(date=None):
1032 def shortdate(date=None):
1027 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1033 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1028 return datestr(date, format='%Y-%m-%d')
1034 return datestr(date, format='%Y-%m-%d')
1029
1035
1030 def strdate(string, format, defaults=[]):
1036 def strdate(string, format, defaults=[]):
1031 """parse a localized time string and return a (unixtime, offset) tuple.
1037 """parse a localized time string and return a (unixtime, offset) tuple.
1032 if the string cannot be parsed, ValueError is raised."""
1038 if the string cannot be parsed, ValueError is raised."""
1033 def timezone(string):
1039 def timezone(string):
1034 tz = string.split()[-1]
1040 tz = string.split()[-1]
1035 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1041 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1036 sign = (tz[0] == "+") and 1 or -1
1042 sign = (tz[0] == "+") and 1 or -1
1037 hours = int(tz[1:3])
1043 hours = int(tz[1:3])
1038 minutes = int(tz[3:5])
1044 minutes = int(tz[3:5])
1039 return -sign * (hours * 60 + minutes) * 60
1045 return -sign * (hours * 60 + minutes) * 60
1040 if tz == "GMT" or tz == "UTC":
1046 if tz == "GMT" or tz == "UTC":
1041 return 0
1047 return 0
1042 return None
1048 return None
1043
1049
1044 # NOTE: unixtime = localunixtime + offset
1050 # NOTE: unixtime = localunixtime + offset
1045 offset, date = timezone(string), string
1051 offset, date = timezone(string), string
1046 if offset is not None:
1052 if offset is not None:
1047 date = " ".join(string.split()[:-1])
1053 date = " ".join(string.split()[:-1])
1048
1054
1049 # add missing elements from defaults
1055 # add missing elements from defaults
1050 usenow = False # default to using biased defaults
1056 usenow = False # default to using biased defaults
1051 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1057 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1052 found = [True for p in part if ("%"+p) in format]
1058 found = [True for p in part if ("%"+p) in format]
1053 if not found:
1059 if not found:
1054 date += "@" + defaults[part][usenow]
1060 date += "@" + defaults[part][usenow]
1055 format += "@%" + part[0]
1061 format += "@%" + part[0]
1056 else:
1062 else:
1057 # We've found a specific time element, less specific time
1063 # We've found a specific time element, less specific time
1058 # elements are relative to today
1064 # elements are relative to today
1059 usenow = True
1065 usenow = True
1060
1066
1061 timetuple = time.strptime(date, format)
1067 timetuple = time.strptime(date, format)
1062 localunixtime = int(calendar.timegm(timetuple))
1068 localunixtime = int(calendar.timegm(timetuple))
1063 if offset is None:
1069 if offset is None:
1064 # local timezone
1070 # local timezone
1065 unixtime = int(time.mktime(timetuple))
1071 unixtime = int(time.mktime(timetuple))
1066 offset = unixtime - localunixtime
1072 offset = unixtime - localunixtime
1067 else:
1073 else:
1068 unixtime = localunixtime + offset
1074 unixtime = localunixtime + offset
1069 return unixtime, offset
1075 return unixtime, offset
1070
1076
1071 def parsedate(date, formats=None, bias={}):
1077 def parsedate(date, formats=None, bias={}):
1072 """parse a localized date/time and return a (unixtime, offset) tuple.
1078 """parse a localized date/time and return a (unixtime, offset) tuple.
1073
1079
1074 The date may be a "unixtime offset" string or in one of the specified
1080 The date may be a "unixtime offset" string or in one of the specified
1075 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1081 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1076
1082
1077 >>> parsedate(' today ') == parsedate(\
1083 >>> parsedate(' today ') == parsedate(\
1078 datetime.date.today().strftime('%b %d'))
1084 datetime.date.today().strftime('%b %d'))
1079 True
1085 True
1080 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1086 >>> parsedate( 'yesterday ') == parsedate((datetime.date.today() -\
1081 datetime.timedelta(days=1)\
1087 datetime.timedelta(days=1)\
1082 ).strftime('%b %d'))
1088 ).strftime('%b %d'))
1083 True
1089 True
1084 >>> now, tz = makedate()
1090 >>> now, tz = makedate()
1085 >>> strnow, strtz = parsedate('now')
1091 >>> strnow, strtz = parsedate('now')
1086 >>> (strnow - now) < 1
1092 >>> (strnow - now) < 1
1087 True
1093 True
1088 >>> tz == strtz
1094 >>> tz == strtz
1089 True
1095 True
1090 """
1096 """
1091 if not date:
1097 if not date:
1092 return 0, 0
1098 return 0, 0
1093 if isinstance(date, tuple) and len(date) == 2:
1099 if isinstance(date, tuple) and len(date) == 2:
1094 return date
1100 return date
1095 if not formats:
1101 if not formats:
1096 formats = defaultdateformats
1102 formats = defaultdateformats
1097 date = date.strip()
1103 date = date.strip()
1098
1104
1099 if date == _('now'):
1105 if date == _('now'):
1100 return makedate()
1106 return makedate()
1101 if date == _('today'):
1107 if date == _('today'):
1102 date = datetime.date.today().strftime('%b %d')
1108 date = datetime.date.today().strftime('%b %d')
1103 elif date == _('yesterday'):
1109 elif date == _('yesterday'):
1104 date = (datetime.date.today() -
1110 date = (datetime.date.today() -
1105 datetime.timedelta(days=1)).strftime('%b %d')
1111 datetime.timedelta(days=1)).strftime('%b %d')
1106
1112
1107 try:
1113 try:
1108 when, offset = map(int, date.split(' '))
1114 when, offset = map(int, date.split(' '))
1109 except ValueError:
1115 except ValueError:
1110 # fill out defaults
1116 # fill out defaults
1111 now = makedate()
1117 now = makedate()
1112 defaults = {}
1118 defaults = {}
1113 for part in ("d", "mb", "yY", "HI", "M", "S"):
1119 for part in ("d", "mb", "yY", "HI", "M", "S"):
1114 # this piece is for rounding the specific end of unknowns
1120 # this piece is for rounding the specific end of unknowns
1115 b = bias.get(part)
1121 b = bias.get(part)
1116 if b is None:
1122 if b is None:
1117 if part[0] in "HMS":
1123 if part[0] in "HMS":
1118 b = "00"
1124 b = "00"
1119 else:
1125 else:
1120 b = "0"
1126 b = "0"
1121
1127
1122 # this piece is for matching the generic end to today's date
1128 # this piece is for matching the generic end to today's date
1123 n = datestr(now, "%" + part[0])
1129 n = datestr(now, "%" + part[0])
1124
1130
1125 defaults[part] = (b, n)
1131 defaults[part] = (b, n)
1126
1132
1127 for format in formats:
1133 for format in formats:
1128 try:
1134 try:
1129 when, offset = strdate(date, format, defaults)
1135 when, offset = strdate(date, format, defaults)
1130 except (ValueError, OverflowError):
1136 except (ValueError, OverflowError):
1131 pass
1137 pass
1132 else:
1138 else:
1133 break
1139 break
1134 else:
1140 else:
1135 raise Abort(_('invalid date: %r') % date)
1141 raise Abort(_('invalid date: %r') % date)
1136 # validate explicit (probably user-specified) date and
1142 # validate explicit (probably user-specified) date and
1137 # time zone offset. values must fit in signed 32 bits for
1143 # time zone offset. values must fit in signed 32 bits for
1138 # current 32-bit linux runtimes. timezones go from UTC-12
1144 # current 32-bit linux runtimes. timezones go from UTC-12
1139 # to UTC+14
1145 # to UTC+14
1140 if abs(when) > 0x7fffffff:
1146 if abs(when) > 0x7fffffff:
1141 raise Abort(_('date exceeds 32 bits: %d') % when)
1147 raise Abort(_('date exceeds 32 bits: %d') % when)
1142 if when < 0:
1148 if when < 0:
1143 raise Abort(_('negative date value: %d') % when)
1149 raise Abort(_('negative date value: %d') % when)
1144 if offset < -50400 or offset > 43200:
1150 if offset < -50400 or offset > 43200:
1145 raise Abort(_('impossible time zone offset: %d') % offset)
1151 raise Abort(_('impossible time zone offset: %d') % offset)
1146 return when, offset
1152 return when, offset
1147
1153
1148 def matchdate(date):
1154 def matchdate(date):
1149 """Return a function that matches a given date match specifier
1155 """Return a function that matches a given date match specifier
1150
1156
1151 Formats include:
1157 Formats include:
1152
1158
1153 '{date}' match a given date to the accuracy provided
1159 '{date}' match a given date to the accuracy provided
1154
1160
1155 '<{date}' on or before a given date
1161 '<{date}' on or before a given date
1156
1162
1157 '>{date}' on or after a given date
1163 '>{date}' on or after a given date
1158
1164
1159 >>> p1 = parsedate("10:29:59")
1165 >>> p1 = parsedate("10:29:59")
1160 >>> p2 = parsedate("10:30:00")
1166 >>> p2 = parsedate("10:30:00")
1161 >>> p3 = parsedate("10:30:59")
1167 >>> p3 = parsedate("10:30:59")
1162 >>> p4 = parsedate("10:31:00")
1168 >>> p4 = parsedate("10:31:00")
1163 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1169 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1164 >>> f = matchdate("10:30")
1170 >>> f = matchdate("10:30")
1165 >>> f(p1[0])
1171 >>> f(p1[0])
1166 False
1172 False
1167 >>> f(p2[0])
1173 >>> f(p2[0])
1168 True
1174 True
1169 >>> f(p3[0])
1175 >>> f(p3[0])
1170 True
1176 True
1171 >>> f(p4[0])
1177 >>> f(p4[0])
1172 False
1178 False
1173 >>> f(p5[0])
1179 >>> f(p5[0])
1174 False
1180 False
1175 """
1181 """
1176
1182
1177 def lower(date):
1183 def lower(date):
1178 d = dict(mb="1", d="1")
1184 d = dict(mb="1", d="1")
1179 return parsedate(date, extendeddateformats, d)[0]
1185 return parsedate(date, extendeddateformats, d)[0]
1180
1186
1181 def upper(date):
1187 def upper(date):
1182 d = dict(mb="12", HI="23", M="59", S="59")
1188 d = dict(mb="12", HI="23", M="59", S="59")
1183 for days in ("31", "30", "29"):
1189 for days in ("31", "30", "29"):
1184 try:
1190 try:
1185 d["d"] = days
1191 d["d"] = days
1186 return parsedate(date, extendeddateformats, d)[0]
1192 return parsedate(date, extendeddateformats, d)[0]
1187 except Abort:
1193 except Abort:
1188 pass
1194 pass
1189 d["d"] = "28"
1195 d["d"] = "28"
1190 return parsedate(date, extendeddateformats, d)[0]
1196 return parsedate(date, extendeddateformats, d)[0]
1191
1197
1192 date = date.strip()
1198 date = date.strip()
1193
1199
1194 if not date:
1200 if not date:
1195 raise Abort(_("dates cannot consist entirely of whitespace"))
1201 raise Abort(_("dates cannot consist entirely of whitespace"))
1196 elif date[0] == "<":
1202 elif date[0] == "<":
1197 if not date[1:]:
1203 if not date[1:]:
1198 raise Abort(_("invalid day spec, use '<DATE'"))
1204 raise Abort(_("invalid day spec, use '<DATE'"))
1199 when = upper(date[1:])
1205 when = upper(date[1:])
1200 return lambda x: x <= when
1206 return lambda x: x <= when
1201 elif date[0] == ">":
1207 elif date[0] == ">":
1202 if not date[1:]:
1208 if not date[1:]:
1203 raise Abort(_("invalid day spec, use '>DATE'"))
1209 raise Abort(_("invalid day spec, use '>DATE'"))
1204 when = lower(date[1:])
1210 when = lower(date[1:])
1205 return lambda x: x >= when
1211 return lambda x: x >= when
1206 elif date[0] == "-":
1212 elif date[0] == "-":
1207 try:
1213 try:
1208 days = int(date[1:])
1214 days = int(date[1:])
1209 except ValueError:
1215 except ValueError:
1210 raise Abort(_("invalid day spec: %s") % date[1:])
1216 raise Abort(_("invalid day spec: %s") % date[1:])
1211 if days < 0:
1217 if days < 0:
1212 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
1218 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
1213 % date[1:])
1219 % date[1:])
1214 when = makedate()[0] - days * 3600 * 24
1220 when = makedate()[0] - days * 3600 * 24
1215 return lambda x: x >= when
1221 return lambda x: x >= when
1216 elif " to " in date:
1222 elif " to " in date:
1217 a, b = date.split(" to ")
1223 a, b = date.split(" to ")
1218 start, stop = lower(a), upper(b)
1224 start, stop = lower(a), upper(b)
1219 return lambda x: x >= start and x <= stop
1225 return lambda x: x >= start and x <= stop
1220 else:
1226 else:
1221 start, stop = lower(date), upper(date)
1227 start, stop = lower(date), upper(date)
1222 return lambda x: x >= start and x <= stop
1228 return lambda x: x >= start and x <= stop
1223
1229
1224 def shortuser(user):
1230 def shortuser(user):
1225 """Return a short representation of a user name or email address."""
1231 """Return a short representation of a user name or email address."""
1226 f = user.find('@')
1232 f = user.find('@')
1227 if f >= 0:
1233 if f >= 0:
1228 user = user[:f]
1234 user = user[:f]
1229 f = user.find('<')
1235 f = user.find('<')
1230 if f >= 0:
1236 if f >= 0:
1231 user = user[f + 1:]
1237 user = user[f + 1:]
1232 f = user.find(' ')
1238 f = user.find(' ')
1233 if f >= 0:
1239 if f >= 0:
1234 user = user[:f]
1240 user = user[:f]
1235 f = user.find('.')
1241 f = user.find('.')
1236 if f >= 0:
1242 if f >= 0:
1237 user = user[:f]
1243 user = user[:f]
1238 return user
1244 return user
1239
1245
1240 def emailuser(user):
1246 def emailuser(user):
1241 """Return the user portion of an email address."""
1247 """Return the user portion of an email address."""
1242 f = user.find('@')
1248 f = user.find('@')
1243 if f >= 0:
1249 if f >= 0:
1244 user = user[:f]
1250 user = user[:f]
1245 f = user.find('<')
1251 f = user.find('<')
1246 if f >= 0:
1252 if f >= 0:
1247 user = user[f + 1:]
1253 user = user[f + 1:]
1248 return user
1254 return user
1249
1255
1250 def email(author):
1256 def email(author):
1251 '''get email of author.'''
1257 '''get email of author.'''
1252 r = author.find('>')
1258 r = author.find('>')
1253 if r == -1:
1259 if r == -1:
1254 r = None
1260 r = None
1255 return author[author.find('<') + 1:r]
1261 return author[author.find('<') + 1:r]
1256
1262
1257 def _ellipsis(text, maxlength):
1263 def _ellipsis(text, maxlength):
1258 if len(text) <= maxlength:
1264 if len(text) <= maxlength:
1259 return text, False
1265 return text, False
1260 else:
1266 else:
1261 return "%s..." % (text[:maxlength - 3]), True
1267 return "%s..." % (text[:maxlength - 3]), True
1262
1268
1263 def ellipsis(text, maxlength=400):
1269 def ellipsis(text, maxlength=400):
1264 """Trim string to at most maxlength (default: 400) characters."""
1270 """Trim string to at most maxlength (default: 400) characters."""
1265 try:
1271 try:
1266 # use unicode not to split at intermediate multi-byte sequence
1272 # use unicode not to split at intermediate multi-byte sequence
1267 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1273 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1268 maxlength)
1274 maxlength)
1269 if not truncated:
1275 if not truncated:
1270 return text
1276 return text
1271 return utext.encode(encoding.encoding)
1277 return utext.encode(encoding.encoding)
1272 except (UnicodeDecodeError, UnicodeEncodeError):
1278 except (UnicodeDecodeError, UnicodeEncodeError):
1273 return _ellipsis(text, maxlength)[0]
1279 return _ellipsis(text, maxlength)[0]
1274
1280
1275 def unitcountfn(*unittable):
1281 def unitcountfn(*unittable):
1276 '''return a function that renders a readable count of some quantity'''
1282 '''return a function that renders a readable count of some quantity'''
1277
1283
1278 def go(count):
1284 def go(count):
1279 for multiplier, divisor, format in unittable:
1285 for multiplier, divisor, format in unittable:
1280 if count >= divisor * multiplier:
1286 if count >= divisor * multiplier:
1281 return format % (count / float(divisor))
1287 return format % (count / float(divisor))
1282 return unittable[-1][2] % count
1288 return unittable[-1][2] % count
1283
1289
1284 return go
1290 return go
1285
1291
1286 bytecount = unitcountfn(
1292 bytecount = unitcountfn(
1287 (100, 1 << 30, _('%.0f GB')),
1293 (100, 1 << 30, _('%.0f GB')),
1288 (10, 1 << 30, _('%.1f GB')),
1294 (10, 1 << 30, _('%.1f GB')),
1289 (1, 1 << 30, _('%.2f GB')),
1295 (1, 1 << 30, _('%.2f GB')),
1290 (100, 1 << 20, _('%.0f MB')),
1296 (100, 1 << 20, _('%.0f MB')),
1291 (10, 1 << 20, _('%.1f MB')),
1297 (10, 1 << 20, _('%.1f MB')),
1292 (1, 1 << 20, _('%.2f MB')),
1298 (1, 1 << 20, _('%.2f MB')),
1293 (100, 1 << 10, _('%.0f KB')),
1299 (100, 1 << 10, _('%.0f KB')),
1294 (10, 1 << 10, _('%.1f KB')),
1300 (10, 1 << 10, _('%.1f KB')),
1295 (1, 1 << 10, _('%.2f KB')),
1301 (1, 1 << 10, _('%.2f KB')),
1296 (1, 1, _('%.0f bytes')),
1302 (1, 1, _('%.0f bytes')),
1297 )
1303 )
1298
1304
1299 def uirepr(s):
1305 def uirepr(s):
1300 # Avoid double backslash in Windows path repr()
1306 # Avoid double backslash in Windows path repr()
1301 return repr(s).replace('\\\\', '\\')
1307 return repr(s).replace('\\\\', '\\')
1302
1308
1303 # delay import of textwrap
1309 # delay import of textwrap
1304 def MBTextWrapper(**kwargs):
1310 def MBTextWrapper(**kwargs):
1305 class tw(textwrap.TextWrapper):
1311 class tw(textwrap.TextWrapper):
1306 """
1312 """
1307 Extend TextWrapper for width-awareness.
1313 Extend TextWrapper for width-awareness.
1308
1314
1309 Neither number of 'bytes' in any encoding nor 'characters' is
1315 Neither number of 'bytes' in any encoding nor 'characters' is
1310 appropriate to calculate terminal columns for specified string.
1316 appropriate to calculate terminal columns for specified string.
1311
1317
1312 Original TextWrapper implementation uses built-in 'len()' directly,
1318 Original TextWrapper implementation uses built-in 'len()' directly,
1313 so overriding is needed to use width information of each characters.
1319 so overriding is needed to use width information of each characters.
1314
1320
1315 In addition, characters classified into 'ambiguous' width are
1321 In addition, characters classified into 'ambiguous' width are
1316 treated as wide in East Asian area, but as narrow in other.
1322 treated as wide in East Asian area, but as narrow in other.
1317
1323
1318 This requires use decision to determine width of such characters.
1324 This requires use decision to determine width of such characters.
1319 """
1325 """
1320 def __init__(self, **kwargs):
1326 def __init__(self, **kwargs):
1321 textwrap.TextWrapper.__init__(self, **kwargs)
1327 textwrap.TextWrapper.__init__(self, **kwargs)
1322
1328
1323 # for compatibility between 2.4 and 2.6
1329 # for compatibility between 2.4 and 2.6
1324 if getattr(self, 'drop_whitespace', None) is None:
1330 if getattr(self, 'drop_whitespace', None) is None:
1325 self.drop_whitespace = kwargs.get('drop_whitespace', True)
1331 self.drop_whitespace = kwargs.get('drop_whitespace', True)
1326
1332
1327 def _cutdown(self, ucstr, space_left):
1333 def _cutdown(self, ucstr, space_left):
1328 l = 0
1334 l = 0
1329 colwidth = encoding.ucolwidth
1335 colwidth = encoding.ucolwidth
1330 for i in xrange(len(ucstr)):
1336 for i in xrange(len(ucstr)):
1331 l += colwidth(ucstr[i])
1337 l += colwidth(ucstr[i])
1332 if space_left < l:
1338 if space_left < l:
1333 return (ucstr[:i], ucstr[i:])
1339 return (ucstr[:i], ucstr[i:])
1334 return ucstr, ''
1340 return ucstr, ''
1335
1341
1336 # overriding of base class
1342 # overriding of base class
1337 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1343 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1338 space_left = max(width - cur_len, 1)
1344 space_left = max(width - cur_len, 1)
1339
1345
1340 if self.break_long_words:
1346 if self.break_long_words:
1341 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1347 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1342 cur_line.append(cut)
1348 cur_line.append(cut)
1343 reversed_chunks[-1] = res
1349 reversed_chunks[-1] = res
1344 elif not cur_line:
1350 elif not cur_line:
1345 cur_line.append(reversed_chunks.pop())
1351 cur_line.append(reversed_chunks.pop())
1346
1352
1347 # this overriding code is imported from TextWrapper of python 2.6
1353 # this overriding code is imported from TextWrapper of python 2.6
1348 # to calculate columns of string by 'encoding.ucolwidth()'
1354 # to calculate columns of string by 'encoding.ucolwidth()'
1349 def _wrap_chunks(self, chunks):
1355 def _wrap_chunks(self, chunks):
1350 colwidth = encoding.ucolwidth
1356 colwidth = encoding.ucolwidth
1351
1357
1352 lines = []
1358 lines = []
1353 if self.width <= 0:
1359 if self.width <= 0:
1354 raise ValueError("invalid width %r (must be > 0)" % self.width)
1360 raise ValueError("invalid width %r (must be > 0)" % self.width)
1355
1361
1356 # Arrange in reverse order so items can be efficiently popped
1362 # Arrange in reverse order so items can be efficiently popped
1357 # from a stack of chucks.
1363 # from a stack of chucks.
1358 chunks.reverse()
1364 chunks.reverse()
1359
1365
1360 while chunks:
1366 while chunks:
1361
1367
1362 # Start the list of chunks that will make up the current line.
1368 # Start the list of chunks that will make up the current line.
1363 # cur_len is just the length of all the chunks in cur_line.
1369 # cur_len is just the length of all the chunks in cur_line.
1364 cur_line = []
1370 cur_line = []
1365 cur_len = 0
1371 cur_len = 0
1366
1372
1367 # Figure out which static string will prefix this line.
1373 # Figure out which static string will prefix this line.
1368 if lines:
1374 if lines:
1369 indent = self.subsequent_indent
1375 indent = self.subsequent_indent
1370 else:
1376 else:
1371 indent = self.initial_indent
1377 indent = self.initial_indent
1372
1378
1373 # Maximum width for this line.
1379 # Maximum width for this line.
1374 width = self.width - len(indent)
1380 width = self.width - len(indent)
1375
1381
1376 # First chunk on line is whitespace -- drop it, unless this
1382 # First chunk on line is whitespace -- drop it, unless this
1377 # is the very beginning of the text (i.e. no lines started yet).
1383 # is the very beginning of the text (i.e. no lines started yet).
1378 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
1384 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
1379 del chunks[-1]
1385 del chunks[-1]
1380
1386
1381 while chunks:
1387 while chunks:
1382 l = colwidth(chunks[-1])
1388 l = colwidth(chunks[-1])
1383
1389
1384 # Can at least squeeze this chunk onto the current line.
1390 # Can at least squeeze this chunk onto the current line.
1385 if cur_len + l <= width:
1391 if cur_len + l <= width:
1386 cur_line.append(chunks.pop())
1392 cur_line.append(chunks.pop())
1387 cur_len += l
1393 cur_len += l
1388
1394
1389 # Nope, this line is full.
1395 # Nope, this line is full.
1390 else:
1396 else:
1391 break
1397 break
1392
1398
1393 # The current line is full, and the next chunk is too big to
1399 # The current line is full, and the next chunk is too big to
1394 # fit on *any* line (not just this one).
1400 # fit on *any* line (not just this one).
1395 if chunks and colwidth(chunks[-1]) > width:
1401 if chunks and colwidth(chunks[-1]) > width:
1396 self._handle_long_word(chunks, cur_line, cur_len, width)
1402 self._handle_long_word(chunks, cur_line, cur_len, width)
1397
1403
1398 # If the last chunk on this line is all whitespace, drop it.
1404 # If the last chunk on this line is all whitespace, drop it.
1399 if (self.drop_whitespace and
1405 if (self.drop_whitespace and
1400 cur_line and cur_line[-1].strip() == ''):
1406 cur_line and cur_line[-1].strip() == ''):
1401 del cur_line[-1]
1407 del cur_line[-1]
1402
1408
1403 # Convert current line back to a string and store it in list
1409 # Convert current line back to a string and store it in list
1404 # of all lines (return value).
1410 # of all lines (return value).
1405 if cur_line:
1411 if cur_line:
1406 lines.append(indent + ''.join(cur_line))
1412 lines.append(indent + ''.join(cur_line))
1407
1413
1408 return lines
1414 return lines
1409
1415
1410 global MBTextWrapper
1416 global MBTextWrapper
1411 MBTextWrapper = tw
1417 MBTextWrapper = tw
1412 return tw(**kwargs)
1418 return tw(**kwargs)
1413
1419
1414 def wrap(line, width, initindent='', hangindent=''):
1420 def wrap(line, width, initindent='', hangindent=''):
1415 maxindent = max(len(hangindent), len(initindent))
1421 maxindent = max(len(hangindent), len(initindent))
1416 if width <= maxindent:
1422 if width <= maxindent:
1417 # adjust for weird terminal size
1423 # adjust for weird terminal size
1418 width = max(78, maxindent + 1)
1424 width = max(78, maxindent + 1)
1419 line = line.decode(encoding.encoding, encoding.encodingmode)
1425 line = line.decode(encoding.encoding, encoding.encodingmode)
1420 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
1426 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
1421 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
1427 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
1422 wrapper = MBTextWrapper(width=width,
1428 wrapper = MBTextWrapper(width=width,
1423 initial_indent=initindent,
1429 initial_indent=initindent,
1424 subsequent_indent=hangindent)
1430 subsequent_indent=hangindent)
1425 return wrapper.fill(line).encode(encoding.encoding)
1431 return wrapper.fill(line).encode(encoding.encoding)
1426
1432
1427 def iterlines(iterator):
1433 def iterlines(iterator):
1428 for chunk in iterator:
1434 for chunk in iterator:
1429 for line in chunk.splitlines():
1435 for line in chunk.splitlines():
1430 yield line
1436 yield line
1431
1437
1432 def expandpath(path):
1438 def expandpath(path):
1433 return os.path.expanduser(os.path.expandvars(path))
1439 return os.path.expanduser(os.path.expandvars(path))
1434
1440
1435 def hgcmd():
1441 def hgcmd():
1436 """Return the command used to execute current hg
1442 """Return the command used to execute current hg
1437
1443
1438 This is different from hgexecutable() because on Windows we want
1444 This is different from hgexecutable() because on Windows we want
1439 to avoid things opening new shell windows like batch files, so we
1445 to avoid things opening new shell windows like batch files, so we
1440 get either the python call or current executable.
1446 get either the python call or current executable.
1441 """
1447 """
1442 if mainfrozen():
1448 if mainfrozen():
1443 return [sys.executable]
1449 return [sys.executable]
1444 return gethgcmd()
1450 return gethgcmd()
1445
1451
1446 def rundetached(args, condfn):
1452 def rundetached(args, condfn):
1447 """Execute the argument list in a detached process.
1453 """Execute the argument list in a detached process.
1448
1454
1449 condfn is a callable which is called repeatedly and should return
1455 condfn is a callable which is called repeatedly and should return
1450 True once the child process is known to have started successfully.
1456 True once the child process is known to have started successfully.
1451 At this point, the child process PID is returned. If the child
1457 At this point, the child process PID is returned. If the child
1452 process fails to start or finishes before condfn() evaluates to
1458 process fails to start or finishes before condfn() evaluates to
1453 True, return -1.
1459 True, return -1.
1454 """
1460 """
1455 # Windows case is easier because the child process is either
1461 # Windows case is easier because the child process is either
1456 # successfully starting and validating the condition or exiting
1462 # successfully starting and validating the condition or exiting
1457 # on failure. We just poll on its PID. On Unix, if the child
1463 # on failure. We just poll on its PID. On Unix, if the child
1458 # process fails to start, it will be left in a zombie state until
1464 # process fails to start, it will be left in a zombie state until
1459 # the parent wait on it, which we cannot do since we expect a long
1465 # the parent wait on it, which we cannot do since we expect a long
1460 # running process on success. Instead we listen for SIGCHLD telling
1466 # running process on success. Instead we listen for SIGCHLD telling
1461 # us our child process terminated.
1467 # us our child process terminated.
1462 terminated = set()
1468 terminated = set()
1463 def handler(signum, frame):
1469 def handler(signum, frame):
1464 terminated.add(os.wait())
1470 terminated.add(os.wait())
1465 prevhandler = None
1471 prevhandler = None
1466 SIGCHLD = getattr(signal, 'SIGCHLD', None)
1472 SIGCHLD = getattr(signal, 'SIGCHLD', None)
1467 if SIGCHLD is not None:
1473 if SIGCHLD is not None:
1468 prevhandler = signal.signal(SIGCHLD, handler)
1474 prevhandler = signal.signal(SIGCHLD, handler)
1469 try:
1475 try:
1470 pid = spawndetached(args)
1476 pid = spawndetached(args)
1471 while not condfn():
1477 while not condfn():
1472 if ((pid in terminated or not testpid(pid))
1478 if ((pid in terminated or not testpid(pid))
1473 and not condfn()):
1479 and not condfn()):
1474 return -1
1480 return -1
1475 time.sleep(0.1)
1481 time.sleep(0.1)
1476 return pid
1482 return pid
1477 finally:
1483 finally:
1478 if prevhandler is not None:
1484 if prevhandler is not None:
1479 signal.signal(signal.SIGCHLD, prevhandler)
1485 signal.signal(signal.SIGCHLD, prevhandler)
1480
1486
1481 try:
1487 try:
1482 any, all = any, all
1488 any, all = any, all
1483 except NameError:
1489 except NameError:
1484 def any(iterable):
1490 def any(iterable):
1485 for i in iterable:
1491 for i in iterable:
1486 if i:
1492 if i:
1487 return True
1493 return True
1488 return False
1494 return False
1489
1495
1490 def all(iterable):
1496 def all(iterable):
1491 for i in iterable:
1497 for i in iterable:
1492 if not i:
1498 if not i:
1493 return False
1499 return False
1494 return True
1500 return True
1495
1501
1496 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1502 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1497 """Return the result of interpolating items in the mapping into string s.
1503 """Return the result of interpolating items in the mapping into string s.
1498
1504
1499 prefix is a single character string, or a two character string with
1505 prefix is a single character string, or a two character string with
1500 a backslash as the first character if the prefix needs to be escaped in
1506 a backslash as the first character if the prefix needs to be escaped in
1501 a regular expression.
1507 a regular expression.
1502
1508
1503 fn is an optional function that will be applied to the replacement text
1509 fn is an optional function that will be applied to the replacement text
1504 just before replacement.
1510 just before replacement.
1505
1511
1506 escape_prefix is an optional flag that allows using doubled prefix for
1512 escape_prefix is an optional flag that allows using doubled prefix for
1507 its escaping.
1513 its escaping.
1508 """
1514 """
1509 fn = fn or (lambda s: s)
1515 fn = fn or (lambda s: s)
1510 patterns = '|'.join(mapping.keys())
1516 patterns = '|'.join(mapping.keys())
1511 if escape_prefix:
1517 if escape_prefix:
1512 patterns += '|' + prefix
1518 patterns += '|' + prefix
1513 if len(prefix) > 1:
1519 if len(prefix) > 1:
1514 prefix_char = prefix[1:]
1520 prefix_char = prefix[1:]
1515 else:
1521 else:
1516 prefix_char = prefix
1522 prefix_char = prefix
1517 mapping[prefix_char] = prefix_char
1523 mapping[prefix_char] = prefix_char
1518 r = re.compile(r'%s(%s)' % (prefix, patterns))
1524 r = re.compile(r'%s(%s)' % (prefix, patterns))
1519 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1525 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1520
1526
1521 def getport(port):
1527 def getport(port):
1522 """Return the port for a given network service.
1528 """Return the port for a given network service.
1523
1529
1524 If port is an integer, it's returned as is. If it's a string, it's
1530 If port is an integer, it's returned as is. If it's a string, it's
1525 looked up using socket.getservbyname(). If there's no matching
1531 looked up using socket.getservbyname(). If there's no matching
1526 service, util.Abort is raised.
1532 service, util.Abort is raised.
1527 """
1533 """
1528 try:
1534 try:
1529 return int(port)
1535 return int(port)
1530 except ValueError:
1536 except ValueError:
1531 pass
1537 pass
1532
1538
1533 try:
1539 try:
1534 return socket.getservbyname(port)
1540 return socket.getservbyname(port)
1535 except socket.error:
1541 except socket.error:
1536 raise Abort(_("no port number associated with service '%s'") % port)
1542 raise Abort(_("no port number associated with service '%s'") % port)
1537
1543
1538 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1544 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1539 '0': False, 'no': False, 'false': False, 'off': False,
1545 '0': False, 'no': False, 'false': False, 'off': False,
1540 'never': False}
1546 'never': False}
1541
1547
1542 def parsebool(s):
1548 def parsebool(s):
1543 """Parse s into a boolean.
1549 """Parse s into a boolean.
1544
1550
1545 If s is not a valid boolean, returns None.
1551 If s is not a valid boolean, returns None.
1546 """
1552 """
1547 return _booleans.get(s.lower(), None)
1553 return _booleans.get(s.lower(), None)
1548
1554
1549 _hexdig = '0123456789ABCDEFabcdef'
1555 _hexdig = '0123456789ABCDEFabcdef'
1550 _hextochr = dict((a + b, chr(int(a + b, 16)))
1556 _hextochr = dict((a + b, chr(int(a + b, 16)))
1551 for a in _hexdig for b in _hexdig)
1557 for a in _hexdig for b in _hexdig)
1552
1558
1553 def _urlunquote(s):
1559 def _urlunquote(s):
1554 """Decode HTTP/HTML % encoding.
1560 """Decode HTTP/HTML % encoding.
1555
1561
1556 >>> _urlunquote('abc%20def')
1562 >>> _urlunquote('abc%20def')
1557 'abc def'
1563 'abc def'
1558 """
1564 """
1559 res = s.split('%')
1565 res = s.split('%')
1560 # fastpath
1566 # fastpath
1561 if len(res) == 1:
1567 if len(res) == 1:
1562 return s
1568 return s
1563 s = res[0]
1569 s = res[0]
1564 for item in res[1:]:
1570 for item in res[1:]:
1565 try:
1571 try:
1566 s += _hextochr[item[:2]] + item[2:]
1572 s += _hextochr[item[:2]] + item[2:]
1567 except KeyError:
1573 except KeyError:
1568 s += '%' + item
1574 s += '%' + item
1569 except UnicodeDecodeError:
1575 except UnicodeDecodeError:
1570 s += unichr(int(item[:2], 16)) + item[2:]
1576 s += unichr(int(item[:2], 16)) + item[2:]
1571 return s
1577 return s
1572
1578
1573 class url(object):
1579 class url(object):
1574 r"""Reliable URL parser.
1580 r"""Reliable URL parser.
1575
1581
1576 This parses URLs and provides attributes for the following
1582 This parses URLs and provides attributes for the following
1577 components:
1583 components:
1578
1584
1579 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
1585 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
1580
1586
1581 Missing components are set to None. The only exception is
1587 Missing components are set to None. The only exception is
1582 fragment, which is set to '' if present but empty.
1588 fragment, which is set to '' if present but empty.
1583
1589
1584 If parsefragment is False, fragment is included in query. If
1590 If parsefragment is False, fragment is included in query. If
1585 parsequery is False, query is included in path. If both are
1591 parsequery is False, query is included in path. If both are
1586 False, both fragment and query are included in path.
1592 False, both fragment and query are included in path.
1587
1593
1588 See http://www.ietf.org/rfc/rfc2396.txt for more information.
1594 See http://www.ietf.org/rfc/rfc2396.txt for more information.
1589
1595
1590 Note that for backward compatibility reasons, bundle URLs do not
1596 Note that for backward compatibility reasons, bundle URLs do not
1591 take host names. That means 'bundle://../' has a path of '../'.
1597 take host names. That means 'bundle://../' has a path of '../'.
1592
1598
1593 Examples:
1599 Examples:
1594
1600
1595 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
1601 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
1596 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
1602 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
1597 >>> url('ssh://[::1]:2200//home/joe/repo')
1603 >>> url('ssh://[::1]:2200//home/joe/repo')
1598 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
1604 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
1599 >>> url('file:///home/joe/repo')
1605 >>> url('file:///home/joe/repo')
1600 <url scheme: 'file', path: '/home/joe/repo'>
1606 <url scheme: 'file', path: '/home/joe/repo'>
1601 >>> url('file:///c:/temp/foo/')
1607 >>> url('file:///c:/temp/foo/')
1602 <url scheme: 'file', path: 'c:/temp/foo/'>
1608 <url scheme: 'file', path: 'c:/temp/foo/'>
1603 >>> url('bundle:foo')
1609 >>> url('bundle:foo')
1604 <url scheme: 'bundle', path: 'foo'>
1610 <url scheme: 'bundle', path: 'foo'>
1605 >>> url('bundle://../foo')
1611 >>> url('bundle://../foo')
1606 <url scheme: 'bundle', path: '../foo'>
1612 <url scheme: 'bundle', path: '../foo'>
1607 >>> url(r'c:\foo\bar')
1613 >>> url(r'c:\foo\bar')
1608 <url path: 'c:\\foo\\bar'>
1614 <url path: 'c:\\foo\\bar'>
1609 >>> url(r'\\blah\blah\blah')
1615 >>> url(r'\\blah\blah\blah')
1610 <url path: '\\\\blah\\blah\\blah'>
1616 <url path: '\\\\blah\\blah\\blah'>
1611 >>> url(r'\\blah\blah\blah#baz')
1617 >>> url(r'\\blah\blah\blah#baz')
1612 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
1618 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
1613
1619
1614 Authentication credentials:
1620 Authentication credentials:
1615
1621
1616 >>> url('ssh://joe:xyz@x/repo')
1622 >>> url('ssh://joe:xyz@x/repo')
1617 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
1623 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
1618 >>> url('ssh://joe@x/repo')
1624 >>> url('ssh://joe@x/repo')
1619 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
1625 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
1620
1626
1621 Query strings and fragments:
1627 Query strings and fragments:
1622
1628
1623 >>> url('http://host/a?b#c')
1629 >>> url('http://host/a?b#c')
1624 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
1630 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
1625 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
1631 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
1626 <url scheme: 'http', host: 'host', path: 'a?b#c'>
1632 <url scheme: 'http', host: 'host', path: 'a?b#c'>
1627 """
1633 """
1628
1634
1629 _safechars = "!~*'()+"
1635 _safechars = "!~*'()+"
1630 _safepchars = "/!~*'()+:"
1636 _safepchars = "/!~*'()+:"
1631 _matchscheme = re.compile(r'^[a-zA-Z0-9+.\-]+:').match
1637 _matchscheme = re.compile(r'^[a-zA-Z0-9+.\-]+:').match
1632
1638
1633 def __init__(self, path, parsequery=True, parsefragment=True):
1639 def __init__(self, path, parsequery=True, parsefragment=True):
1634 # We slowly chomp away at path until we have only the path left
1640 # We slowly chomp away at path until we have only the path left
1635 self.scheme = self.user = self.passwd = self.host = None
1641 self.scheme = self.user = self.passwd = self.host = None
1636 self.port = self.path = self.query = self.fragment = None
1642 self.port = self.path = self.query = self.fragment = None
1637 self._localpath = True
1643 self._localpath = True
1638 self._hostport = ''
1644 self._hostport = ''
1639 self._origpath = path
1645 self._origpath = path
1640
1646
1641 if parsefragment and '#' in path:
1647 if parsefragment and '#' in path:
1642 path, self.fragment = path.split('#', 1)
1648 path, self.fragment = path.split('#', 1)
1643 if not path:
1649 if not path:
1644 path = None
1650 path = None
1645
1651
1646 # special case for Windows drive letters and UNC paths
1652 # special case for Windows drive letters and UNC paths
1647 if hasdriveletter(path) or path.startswith(r'\\'):
1653 if hasdriveletter(path) or path.startswith(r'\\'):
1648 self.path = path
1654 self.path = path
1649 return
1655 return
1650
1656
1651 # For compatibility reasons, we can't handle bundle paths as
1657 # For compatibility reasons, we can't handle bundle paths as
1652 # normal URLS
1658 # normal URLS
1653 if path.startswith('bundle:'):
1659 if path.startswith('bundle:'):
1654 self.scheme = 'bundle'
1660 self.scheme = 'bundle'
1655 path = path[7:]
1661 path = path[7:]
1656 if path.startswith('//'):
1662 if path.startswith('//'):
1657 path = path[2:]
1663 path = path[2:]
1658 self.path = path
1664 self.path = path
1659 return
1665 return
1660
1666
1661 if self._matchscheme(path):
1667 if self._matchscheme(path):
1662 parts = path.split(':', 1)
1668 parts = path.split(':', 1)
1663 if parts[0]:
1669 if parts[0]:
1664 self.scheme, path = parts
1670 self.scheme, path = parts
1665 self._localpath = False
1671 self._localpath = False
1666
1672
1667 if not path:
1673 if not path:
1668 path = None
1674 path = None
1669 if self._localpath:
1675 if self._localpath:
1670 self.path = ''
1676 self.path = ''
1671 return
1677 return
1672 else:
1678 else:
1673 if self._localpath:
1679 if self._localpath:
1674 self.path = path
1680 self.path = path
1675 return
1681 return
1676
1682
1677 if parsequery and '?' in path:
1683 if parsequery and '?' in path:
1678 path, self.query = path.split('?', 1)
1684 path, self.query = path.split('?', 1)
1679 if not path:
1685 if not path:
1680 path = None
1686 path = None
1681 if not self.query:
1687 if not self.query:
1682 self.query = None
1688 self.query = None
1683
1689
1684 # // is required to specify a host/authority
1690 # // is required to specify a host/authority
1685 if path and path.startswith('//'):
1691 if path and path.startswith('//'):
1686 parts = path[2:].split('/', 1)
1692 parts = path[2:].split('/', 1)
1687 if len(parts) > 1:
1693 if len(parts) > 1:
1688 self.host, path = parts
1694 self.host, path = parts
1689 path = path
1695 path = path
1690 else:
1696 else:
1691 self.host = parts[0]
1697 self.host = parts[0]
1692 path = None
1698 path = None
1693 if not self.host:
1699 if not self.host:
1694 self.host = None
1700 self.host = None
1695 # path of file:///d is /d
1701 # path of file:///d is /d
1696 # path of file:///d:/ is d:/, not /d:/
1702 # path of file:///d:/ is d:/, not /d:/
1697 if path and not hasdriveletter(path):
1703 if path and not hasdriveletter(path):
1698 path = '/' + path
1704 path = '/' + path
1699
1705
1700 if self.host and '@' in self.host:
1706 if self.host and '@' in self.host:
1701 self.user, self.host = self.host.rsplit('@', 1)
1707 self.user, self.host = self.host.rsplit('@', 1)
1702 if ':' in self.user:
1708 if ':' in self.user:
1703 self.user, self.passwd = self.user.split(':', 1)
1709 self.user, self.passwd = self.user.split(':', 1)
1704 if not self.host:
1710 if not self.host:
1705 self.host = None
1711 self.host = None
1706
1712
1707 # Don't split on colons in IPv6 addresses without ports
1713 # Don't split on colons in IPv6 addresses without ports
1708 if (self.host and ':' in self.host and
1714 if (self.host and ':' in self.host and
1709 not (self.host.startswith('[') and self.host.endswith(']'))):
1715 not (self.host.startswith('[') and self.host.endswith(']'))):
1710 self._hostport = self.host
1716 self._hostport = self.host
1711 self.host, self.port = self.host.rsplit(':', 1)
1717 self.host, self.port = self.host.rsplit(':', 1)
1712 if not self.host:
1718 if not self.host:
1713 self.host = None
1719 self.host = None
1714
1720
1715 if (self.host and self.scheme == 'file' and
1721 if (self.host and self.scheme == 'file' and
1716 self.host not in ('localhost', '127.0.0.1', '[::1]')):
1722 self.host not in ('localhost', '127.0.0.1', '[::1]')):
1717 raise Abort(_('file:// URLs can only refer to localhost'))
1723 raise Abort(_('file:// URLs can only refer to localhost'))
1718
1724
1719 self.path = path
1725 self.path = path
1720
1726
1721 # leave the query string escaped
1727 # leave the query string escaped
1722 for a in ('user', 'passwd', 'host', 'port',
1728 for a in ('user', 'passwd', 'host', 'port',
1723 'path', 'fragment'):
1729 'path', 'fragment'):
1724 v = getattr(self, a)
1730 v = getattr(self, a)
1725 if v is not None:
1731 if v is not None:
1726 setattr(self, a, _urlunquote(v))
1732 setattr(self, a, _urlunquote(v))
1727
1733
1728 def __repr__(self):
1734 def __repr__(self):
1729 attrs = []
1735 attrs = []
1730 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
1736 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
1731 'query', 'fragment'):
1737 'query', 'fragment'):
1732 v = getattr(self, a)
1738 v = getattr(self, a)
1733 if v is not None:
1739 if v is not None:
1734 attrs.append('%s: %r' % (a, v))
1740 attrs.append('%s: %r' % (a, v))
1735 return '<url %s>' % ', '.join(attrs)
1741 return '<url %s>' % ', '.join(attrs)
1736
1742
1737 def __str__(self):
1743 def __str__(self):
1738 r"""Join the URL's components back into a URL string.
1744 r"""Join the URL's components back into a URL string.
1739
1745
1740 Examples:
1746 Examples:
1741
1747
1742 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
1748 >>> str(url('http://user:pw@host:80/c:/bob?fo:oo#ba:ar'))
1743 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
1749 'http://user:pw@host:80/c:/bob?fo:oo#ba:ar'
1744 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
1750 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
1745 'http://user:pw@host:80/?foo=bar&baz=42'
1751 'http://user:pw@host:80/?foo=bar&baz=42'
1746 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
1752 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
1747 'http://user:pw@host:80/?foo=bar%3dbaz'
1753 'http://user:pw@host:80/?foo=bar%3dbaz'
1748 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
1754 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
1749 'ssh://user:pw@[::1]:2200//home/joe#'
1755 'ssh://user:pw@[::1]:2200//home/joe#'
1750 >>> str(url('http://localhost:80//'))
1756 >>> str(url('http://localhost:80//'))
1751 'http://localhost:80//'
1757 'http://localhost:80//'
1752 >>> str(url('http://localhost:80/'))
1758 >>> str(url('http://localhost:80/'))
1753 'http://localhost:80/'
1759 'http://localhost:80/'
1754 >>> str(url('http://localhost:80'))
1760 >>> str(url('http://localhost:80'))
1755 'http://localhost:80/'
1761 'http://localhost:80/'
1756 >>> str(url('bundle:foo'))
1762 >>> str(url('bundle:foo'))
1757 'bundle:foo'
1763 'bundle:foo'
1758 >>> str(url('bundle://../foo'))
1764 >>> str(url('bundle://../foo'))
1759 'bundle:../foo'
1765 'bundle:../foo'
1760 >>> str(url('path'))
1766 >>> str(url('path'))
1761 'path'
1767 'path'
1762 >>> str(url('file:///tmp/foo/bar'))
1768 >>> str(url('file:///tmp/foo/bar'))
1763 'file:///tmp/foo/bar'
1769 'file:///tmp/foo/bar'
1764 >>> str(url('file:///c:/tmp/foo/bar'))
1770 >>> str(url('file:///c:/tmp/foo/bar'))
1765 'file:///c:/tmp/foo/bar'
1771 'file:///c:/tmp/foo/bar'
1766 >>> print url(r'bundle:foo\bar')
1772 >>> print url(r'bundle:foo\bar')
1767 bundle:foo\bar
1773 bundle:foo\bar
1768 """
1774 """
1769 if self._localpath:
1775 if self._localpath:
1770 s = self.path
1776 s = self.path
1771 if self.scheme == 'bundle':
1777 if self.scheme == 'bundle':
1772 s = 'bundle:' + s
1778 s = 'bundle:' + s
1773 if self.fragment:
1779 if self.fragment:
1774 s += '#' + self.fragment
1780 s += '#' + self.fragment
1775 return s
1781 return s
1776
1782
1777 s = self.scheme + ':'
1783 s = self.scheme + ':'
1778 if self.user or self.passwd or self.host:
1784 if self.user or self.passwd or self.host:
1779 s += '//'
1785 s += '//'
1780 elif self.scheme and (not self.path or self.path.startswith('/')
1786 elif self.scheme and (not self.path or self.path.startswith('/')
1781 or hasdriveletter(self.path)):
1787 or hasdriveletter(self.path)):
1782 s += '//'
1788 s += '//'
1783 if hasdriveletter(self.path):
1789 if hasdriveletter(self.path):
1784 s += '/'
1790 s += '/'
1785 if self.user:
1791 if self.user:
1786 s += urllib.quote(self.user, safe=self._safechars)
1792 s += urllib.quote(self.user, safe=self._safechars)
1787 if self.passwd:
1793 if self.passwd:
1788 s += ':' + urllib.quote(self.passwd, safe=self._safechars)
1794 s += ':' + urllib.quote(self.passwd, safe=self._safechars)
1789 if self.user or self.passwd:
1795 if self.user or self.passwd:
1790 s += '@'
1796 s += '@'
1791 if self.host:
1797 if self.host:
1792 if not (self.host.startswith('[') and self.host.endswith(']')):
1798 if not (self.host.startswith('[') and self.host.endswith(']')):
1793 s += urllib.quote(self.host)
1799 s += urllib.quote(self.host)
1794 else:
1800 else:
1795 s += self.host
1801 s += self.host
1796 if self.port:
1802 if self.port:
1797 s += ':' + urllib.quote(self.port)
1803 s += ':' + urllib.quote(self.port)
1798 if self.host:
1804 if self.host:
1799 s += '/'
1805 s += '/'
1800 if self.path:
1806 if self.path:
1801 # TODO: similar to the query string, we should not unescape the
1807 # TODO: similar to the query string, we should not unescape the
1802 # path when we store it, the path might contain '%2f' = '/',
1808 # path when we store it, the path might contain '%2f' = '/',
1803 # which we should *not* escape.
1809 # which we should *not* escape.
1804 s += urllib.quote(self.path, safe=self._safepchars)
1810 s += urllib.quote(self.path, safe=self._safepchars)
1805 if self.query:
1811 if self.query:
1806 # we store the query in escaped form.
1812 # we store the query in escaped form.
1807 s += '?' + self.query
1813 s += '?' + self.query
1808 if self.fragment is not None:
1814 if self.fragment is not None:
1809 s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
1815 s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
1810 return s
1816 return s
1811
1817
1812 def authinfo(self):
1818 def authinfo(self):
1813 user, passwd = self.user, self.passwd
1819 user, passwd = self.user, self.passwd
1814 try:
1820 try:
1815 self.user, self.passwd = None, None
1821 self.user, self.passwd = None, None
1816 s = str(self)
1822 s = str(self)
1817 finally:
1823 finally:
1818 self.user, self.passwd = user, passwd
1824 self.user, self.passwd = user, passwd
1819 if not self.user:
1825 if not self.user:
1820 return (s, None)
1826 return (s, None)
1821 # authinfo[1] is passed to urllib2 password manager, and its
1827 # authinfo[1] is passed to urllib2 password manager, and its
1822 # URIs must not contain credentials. The host is passed in the
1828 # URIs must not contain credentials. The host is passed in the
1823 # URIs list because Python < 2.4.3 uses only that to search for
1829 # URIs list because Python < 2.4.3 uses only that to search for
1824 # a password.
1830 # a password.
1825 return (s, (None, (s, self.host),
1831 return (s, (None, (s, self.host),
1826 self.user, self.passwd or ''))
1832 self.user, self.passwd or ''))
1827
1833
1828 def isabs(self):
1834 def isabs(self):
1829 if self.scheme and self.scheme != 'file':
1835 if self.scheme and self.scheme != 'file':
1830 return True # remote URL
1836 return True # remote URL
1831 if hasdriveletter(self.path):
1837 if hasdriveletter(self.path):
1832 return True # absolute for our purposes - can't be joined()
1838 return True # absolute for our purposes - can't be joined()
1833 if self.path.startswith(r'\\'):
1839 if self.path.startswith(r'\\'):
1834 return True # Windows UNC path
1840 return True # Windows UNC path
1835 if self.path.startswith('/'):
1841 if self.path.startswith('/'):
1836 return True # POSIX-style
1842 return True # POSIX-style
1837 return False
1843 return False
1838
1844
1839 def localpath(self):
1845 def localpath(self):
1840 if self.scheme == 'file' or self.scheme == 'bundle':
1846 if self.scheme == 'file' or self.scheme == 'bundle':
1841 path = self.path or '/'
1847 path = self.path or '/'
1842 # For Windows, we need to promote hosts containing drive
1848 # For Windows, we need to promote hosts containing drive
1843 # letters to paths with drive letters.
1849 # letters to paths with drive letters.
1844 if hasdriveletter(self._hostport):
1850 if hasdriveletter(self._hostport):
1845 path = self._hostport + '/' + self.path
1851 path = self._hostport + '/' + self.path
1846 elif (self.host is not None and self.path
1852 elif (self.host is not None and self.path
1847 and not hasdriveletter(path)):
1853 and not hasdriveletter(path)):
1848 path = '/' + path
1854 path = '/' + path
1849 return path
1855 return path
1850 return self._origpath
1856 return self._origpath
1851
1857
1852 def hasscheme(path):
1858 def hasscheme(path):
1853 return bool(url(path).scheme)
1859 return bool(url(path).scheme)
1854
1860
1855 def hasdriveletter(path):
1861 def hasdriveletter(path):
1856 return path and path[1:2] == ':' and path[0:1].isalpha()
1862 return path and path[1:2] == ':' and path[0:1].isalpha()
1857
1863
1858 def urllocalpath(path):
1864 def urllocalpath(path):
1859 return url(path, parsequery=False, parsefragment=False).localpath()
1865 return url(path, parsequery=False, parsefragment=False).localpath()
1860
1866
1861 def hidepassword(u):
1867 def hidepassword(u):
1862 '''hide user credential in a url string'''
1868 '''hide user credential in a url string'''
1863 u = url(u)
1869 u = url(u)
1864 if u.passwd:
1870 if u.passwd:
1865 u.passwd = '***'
1871 u.passwd = '***'
1866 return str(u)
1872 return str(u)
1867
1873
1868 def removeauth(u):
1874 def removeauth(u):
1869 '''remove all authentication information from a url string'''
1875 '''remove all authentication information from a url string'''
1870 u = url(u)
1876 u = url(u)
1871 u.user = u.passwd = None
1877 u.user = u.passwd = None
1872 return str(u)
1878 return str(u)
1873
1879
1874 def isatty(fd):
1880 def isatty(fd):
1875 try:
1881 try:
1876 return fd.isatty()
1882 return fd.isatty()
1877 except AttributeError:
1883 except AttributeError:
1878 return False
1884 return False
1879
1885
1880 timecount = unitcountfn(
1886 timecount = unitcountfn(
1881 (1, 1e3, _('%.0f s')),
1887 (1, 1e3, _('%.0f s')),
1882 (100, 1, _('%.1f s')),
1888 (100, 1, _('%.1f s')),
1883 (10, 1, _('%.2f s')),
1889 (10, 1, _('%.2f s')),
1884 (1, 1, _('%.3f s')),
1890 (1, 1, _('%.3f s')),
1885 (100, 0.001, _('%.1f ms')),
1891 (100, 0.001, _('%.1f ms')),
1886 (10, 0.001, _('%.2f ms')),
1892 (10, 0.001, _('%.2f ms')),
1887 (1, 0.001, _('%.3f ms')),
1893 (1, 0.001, _('%.3f ms')),
1888 (100, 0.000001, _('%.1f us')),
1894 (100, 0.000001, _('%.1f us')),
1889 (10, 0.000001, _('%.2f us')),
1895 (10, 0.000001, _('%.2f us')),
1890 (1, 0.000001, _('%.3f us')),
1896 (1, 0.000001, _('%.3f us')),
1891 (100, 0.000000001, _('%.1f ns')),
1897 (100, 0.000000001, _('%.1f ns')),
1892 (10, 0.000000001, _('%.2f ns')),
1898 (10, 0.000000001, _('%.2f ns')),
1893 (1, 0.000000001, _('%.3f ns')),
1899 (1, 0.000000001, _('%.3f ns')),
1894 )
1900 )
1895
1901
1896 _timenesting = [0]
1902 _timenesting = [0]
1897
1903
1898 def timed(func):
1904 def timed(func):
1899 '''Report the execution time of a function call to stderr.
1905 '''Report the execution time of a function call to stderr.
1900
1906
1901 During development, use as a decorator when you need to measure
1907 During development, use as a decorator when you need to measure
1902 the cost of a function, e.g. as follows:
1908 the cost of a function, e.g. as follows:
1903
1909
1904 @util.timed
1910 @util.timed
1905 def foo(a, b, c):
1911 def foo(a, b, c):
1906 pass
1912 pass
1907 '''
1913 '''
1908
1914
1909 def wrapper(*args, **kwargs):
1915 def wrapper(*args, **kwargs):
1910 start = time.time()
1916 start = time.time()
1911 indent = 2
1917 indent = 2
1912 _timenesting[0] += indent
1918 _timenesting[0] += indent
1913 try:
1919 try:
1914 return func(*args, **kwargs)
1920 return func(*args, **kwargs)
1915 finally:
1921 finally:
1916 elapsed = time.time() - start
1922 elapsed = time.time() - start
1917 _timenesting[0] -= indent
1923 _timenesting[0] -= indent
1918 sys.stderr.write('%s%s: %s\n' %
1924 sys.stderr.write('%s%s: %s\n' %
1919 (' ' * _timenesting[0], func.__name__,
1925 (' ' * _timenesting[0], func.__name__,
1920 timecount(elapsed)))
1926 timecount(elapsed)))
1921 return wrapper
1927 return wrapper
@@ -1,724 +1,748 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > graphlog=
3 > graphlog=
4 > rebase=
4 > rebase=
5 > mq=
5 > mq=
6 >
6 >
7 > [phases]
7 > [phases]
8 > publish=False
8 > publish=False
9 >
9 >
10 > [alias]
10 > [alias]
11 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
11 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
12 > tglogp = log -G --template "{rev}:{phase} '{desc}' {branches}\n"
12 > tglogp = log -G --template "{rev}:{phase} '{desc}' {branches}\n"
13 > EOF
13 > EOF
14
14
15 Create repo a:
15 Create repo a:
16
16
17 $ hg init a
17 $ hg init a
18 $ cd a
18 $ cd a
19 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
19 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
20 adding changesets
20 adding changesets
21 adding manifests
21 adding manifests
22 adding file changes
22 adding file changes
23 added 8 changesets with 7 changes to 7 files (+2 heads)
23 added 8 changesets with 7 changes to 7 files (+2 heads)
24 (run 'hg heads' to see heads, 'hg merge' to merge)
24 (run 'hg heads' to see heads, 'hg merge' to merge)
25 $ hg up tip
25 $ hg up tip
26 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
26 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
27
27
28 $ hg tglog
28 $ hg tglog
29 @ 7: 'H'
29 @ 7: 'H'
30 |
30 |
31 | o 6: 'G'
31 | o 6: 'G'
32 |/|
32 |/|
33 o | 5: 'F'
33 o | 5: 'F'
34 | |
34 | |
35 | o 4: 'E'
35 | o 4: 'E'
36 |/
36 |/
37 | o 3: 'D'
37 | o 3: 'D'
38 | |
38 | |
39 | o 2: 'C'
39 | o 2: 'C'
40 | |
40 | |
41 | o 1: 'B'
41 | o 1: 'B'
42 |/
42 |/
43 o 0: 'A'
43 o 0: 'A'
44
44
45 $ cd ..
45 $ cd ..
46
46
47
47
48 Rebasing B onto H and collapsing changesets with different phases:
48 Rebasing B onto H and collapsing changesets with different phases:
49
49
50
50
51 $ hg clone -q -u 3 a a1
51 $ hg clone -q -u 3 a a1
52 $ cd a1
52 $ cd a1
53
53
54 $ hg phase --force --secret 3
54 $ hg phase --force --secret 3
55
55
56 $ hg rebase --collapse --keepbranches
56 $ hg rebase --collapse --keepbranches
57 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
57 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
58
58
59 $ hg tglogp
59 $ hg tglogp
60 @ 5:secret 'Collapsed revision
60 @ 5:secret 'Collapsed revision
61 | * B
61 | * B
62 | * C
62 | * C
63 | * D'
63 | * D'
64 o 4:draft 'H'
64 o 4:draft 'H'
65 |
65 |
66 | o 3:draft 'G'
66 | o 3:draft 'G'
67 |/|
67 |/|
68 o | 2:draft 'F'
68 o | 2:draft 'F'
69 | |
69 | |
70 | o 1:draft 'E'
70 | o 1:draft 'E'
71 |/
71 |/
72 o 0:draft 'A'
72 o 0:draft 'A'
73
73
74 $ hg manifest
74 $ hg manifest
75 A
75 A
76 B
76 B
77 C
77 C
78 D
78 D
79 F
79 F
80 H
80 H
81
81
82 $ cd ..
82 $ cd ..
83
83
84
84
85 Rebasing E onto H:
85 Rebasing E onto H:
86
86
87 $ hg clone -q -u . a a2
87 $ hg clone -q -u . a a2
88 $ cd a2
88 $ cd a2
89
89
90 $ hg phase --force --secret 6
90 $ hg phase --force --secret 6
91 $ hg rebase --source 4 --collapse
91 $ hg rebase --source 4 --collapse
92 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
92 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
93
93
94 $ hg tglog
94 $ hg tglog
95 @ 6: 'Collapsed revision
95 @ 6: 'Collapsed revision
96 | * E
96 | * E
97 | * G'
97 | * G'
98 o 5: 'H'
98 o 5: 'H'
99 |
99 |
100 o 4: 'F'
100 o 4: 'F'
101 |
101 |
102 | o 3: 'D'
102 | o 3: 'D'
103 | |
103 | |
104 | o 2: 'C'
104 | o 2: 'C'
105 | |
105 | |
106 | o 1: 'B'
106 | o 1: 'B'
107 |/
107 |/
108 o 0: 'A'
108 o 0: 'A'
109
109
110 $ hg manifest
110 $ hg manifest
111 A
111 A
112 E
112 E
113 F
113 F
114 H
114 H
115
115
116 $ cd ..
116 $ cd ..
117
117
118 Rebasing G onto H with custom message:
118 Rebasing G onto H with custom message:
119
119
120 $ hg clone -q -u . a a3
120 $ hg clone -q -u . a a3
121 $ cd a3
121 $ cd a3
122
122
123 $ hg rebase --base 6 -m 'custom message'
123 $ hg rebase --base 6 -m 'custom message'
124 abort: message can only be specified with collapse
124 abort: message can only be specified with collapse
125 [255]
125 [255]
126
126
127 $ hg rebase --source 4 --collapse -m 'custom message'
127 $ hg rebase --source 4 --collapse -m 'custom message'
128 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
128 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
129
129
130 $ hg tglog
130 $ hg tglog
131 @ 6: 'custom message'
131 @ 6: 'custom message'
132 |
132 |
133 o 5: 'H'
133 o 5: 'H'
134 |
134 |
135 o 4: 'F'
135 o 4: 'F'
136 |
136 |
137 | o 3: 'D'
137 | o 3: 'D'
138 | |
138 | |
139 | o 2: 'C'
139 | o 2: 'C'
140 | |
140 | |
141 | o 1: 'B'
141 | o 1: 'B'
142 |/
142 |/
143 o 0: 'A'
143 o 0: 'A'
144
144
145 $ hg manifest
145 $ hg manifest
146 A
146 A
147 E
147 E
148 F
148 F
149 H
149 H
150
150
151 $ cd ..
151 $ cd ..
152
152
153 Create repo b:
153 Create repo b:
154
154
155 $ hg init b
155 $ hg init b
156 $ cd b
156 $ cd b
157
157
158 $ echo A > A
158 $ echo A > A
159 $ hg ci -Am A
159 $ hg ci -Am A
160 adding A
160 adding A
161 $ echo B > B
161 $ echo B > B
162 $ hg ci -Am B
162 $ hg ci -Am B
163 adding B
163 adding B
164
164
165 $ hg up -q 0
165 $ hg up -q 0
166
166
167 $ echo C > C
167 $ echo C > C
168 $ hg ci -Am C
168 $ hg ci -Am C
169 adding C
169 adding C
170 created new head
170 created new head
171
171
172 $ hg merge
172 $ hg merge
173 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
173 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 (branch merge, don't forget to commit)
174 (branch merge, don't forget to commit)
175
175
176 $ echo D > D
176 $ echo D > D
177 $ hg ci -Am D
177 $ hg ci -Am D
178 adding D
178 adding D
179
179
180 $ hg up -q 1
180 $ hg up -q 1
181
181
182 $ echo E > E
182 $ echo E > E
183 $ hg ci -Am E
183 $ hg ci -Am E
184 adding E
184 adding E
185 created new head
185 created new head
186
186
187 $ echo F > F
187 $ echo F > F
188 $ hg ci -Am F
188 $ hg ci -Am F
189 adding F
189 adding F
190
190
191 $ hg merge
191 $ hg merge
192 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
192 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
193 (branch merge, don't forget to commit)
193 (branch merge, don't forget to commit)
194 $ hg ci -m G
194 $ hg ci -m G
195
195
196 $ hg up -q 0
196 $ hg up -q 0
197
197
198 $ echo H > H
198 $ echo H > H
199 $ hg ci -Am H
199 $ hg ci -Am H
200 adding H
200 adding H
201 created new head
201 created new head
202
202
203 $ hg tglog
203 $ hg tglog
204 @ 7: 'H'
204 @ 7: 'H'
205 |
205 |
206 | o 6: 'G'
206 | o 6: 'G'
207 | |\
207 | |\
208 | | o 5: 'F'
208 | | o 5: 'F'
209 | | |
209 | | |
210 | | o 4: 'E'
210 | | o 4: 'E'
211 | | |
211 | | |
212 | o | 3: 'D'
212 | o | 3: 'D'
213 | |\|
213 | |\|
214 | o | 2: 'C'
214 | o | 2: 'C'
215 |/ /
215 |/ /
216 | o 1: 'B'
216 | o 1: 'B'
217 |/
217 |/
218 o 0: 'A'
218 o 0: 'A'
219
219
220 $ cd ..
220 $ cd ..
221
221
222
222
223 Rebase and collapse - more than one external (fail):
223 Rebase and collapse - more than one external (fail):
224
224
225 $ hg clone -q -u . b b1
225 $ hg clone -q -u . b b1
226 $ cd b1
226 $ cd b1
227
227
228 $ hg rebase -s 2 --collapse
228 $ hg rebase -s 2 --collapse
229 abort: unable to collapse, there is more than one external parent
229 abort: unable to collapse, there is more than one external parent
230 [255]
230 [255]
231
231
232 Rebase and collapse - E onto H:
232 Rebase and collapse - E onto H:
233
233
234 $ hg rebase -s 4 --collapse # root (4) is not a merge
234 $ hg rebase -s 4 --collapse # root (4) is not a merge
235 saved backup bundle to $TESTTMP/b1/.hg/strip-backup/*-backup.hg (glob)
235 saved backup bundle to $TESTTMP/b1/.hg/strip-backup/*-backup.hg (glob)
236
236
237 $ hg tglog
237 $ hg tglog
238 @ 5: 'Collapsed revision
238 @ 5: 'Collapsed revision
239 |\ * E
239 |\ * E
240 | | * F
240 | | * F
241 | | * G'
241 | | * G'
242 | o 4: 'H'
242 | o 4: 'H'
243 | |
243 | |
244 o | 3: 'D'
244 o | 3: 'D'
245 |\ \
245 |\ \
246 | o | 2: 'C'
246 | o | 2: 'C'
247 | |/
247 | |/
248 o / 1: 'B'
248 o / 1: 'B'
249 |/
249 |/
250 o 0: 'A'
250 o 0: 'A'
251
251
252 $ hg manifest
252 $ hg manifest
253 A
253 A
254 C
254 C
255 D
255 D
256 E
256 E
257 F
257 F
258 H
258 H
259
259
260 $ cd ..
260 $ cd ..
261
261
262
262
263
263
264
264
265 Test that branchheads cache is updated correctly when doing a strip in which
265 Test that branchheads cache is updated correctly when doing a strip in which
266 the parent of the ancestor node to be stripped does not become a head and also,
266 the parent of the ancestor node to be stripped does not become a head and also,
267 the parent of a node that is a child of the node stripped becomes a head (node
267 the parent of a node that is a child of the node stripped becomes a head (node
268 3). The code is now much simpler and we could just test a simpler scenario
268 3). The code is now much simpler and we could just test a simpler scenario
269 We keep it the test this way in case new complexity is injected.
269 We keep it the test this way in case new complexity is injected.
270
270
271 $ hg clone -q -u . b b2
271 $ hg clone -q -u . b b2
272 $ cd b2
272 $ cd b2
273
273
274 $ hg heads --template="{rev}:{node} {branch}\n"
274 $ hg heads --template="{rev}:{node} {branch}\n"
275 7:c65502d4178782309ce0574c5ae6ee9485a9bafa default
275 7:c65502d4178782309ce0574c5ae6ee9485a9bafa default
276 6:c772a8b2dc17629cec88a19d09c926c4814b12c7 default
276 6:c772a8b2dc17629cec88a19d09c926c4814b12c7 default
277
277
278 $ cat $TESTTMP/b2/.hg/cache/branchheads-served
278 $ cat $TESTTMP/b2/.hg/cache/branchheads-served
279 c65502d4178782309ce0574c5ae6ee9485a9bafa 7
279 c65502d4178782309ce0574c5ae6ee9485a9bafa 7
280 c772a8b2dc17629cec88a19d09c926c4814b12c7 default
280 c772a8b2dc17629cec88a19d09c926c4814b12c7 default
281 c65502d4178782309ce0574c5ae6ee9485a9bafa default
281 c65502d4178782309ce0574c5ae6ee9485a9bafa default
282
282
283 $ hg strip 4
283 $ hg strip 4
284 saved backup bundle to $TESTTMP/b2/.hg/strip-backup/8a5212ebc852-backup.hg (glob)
284 saved backup bundle to $TESTTMP/b2/.hg/strip-backup/8a5212ebc852-backup.hg (glob)
285
285
286 $ cat $TESTTMP/b2/.hg/cache/branchheads-served
286 $ cat $TESTTMP/b2/.hg/cache/branchheads-served
287 c65502d4178782309ce0574c5ae6ee9485a9bafa 4
287 c65502d4178782309ce0574c5ae6ee9485a9bafa 4
288 2870ad076e541e714f3c2bc32826b5c6a6e5b040 default
288 2870ad076e541e714f3c2bc32826b5c6a6e5b040 default
289 c65502d4178782309ce0574c5ae6ee9485a9bafa default
289 c65502d4178782309ce0574c5ae6ee9485a9bafa default
290
290
291 $ hg heads --template="{rev}:{node} {branch}\n"
291 $ hg heads --template="{rev}:{node} {branch}\n"
292 4:c65502d4178782309ce0574c5ae6ee9485a9bafa default
292 4:c65502d4178782309ce0574c5ae6ee9485a9bafa default
293 3:2870ad076e541e714f3c2bc32826b5c6a6e5b040 default
293 3:2870ad076e541e714f3c2bc32826b5c6a6e5b040 default
294
294
295 $ cd ..
295 $ cd ..
296
296
297
297
298
298
299
299
300
300
301
301
302 Create repo c:
302 Create repo c:
303
303
304 $ hg init c
304 $ hg init c
305 $ cd c
305 $ cd c
306
306
307 $ echo A > A
307 $ echo A > A
308 $ hg ci -Am A
308 $ hg ci -Am A
309 adding A
309 adding A
310 $ echo B > B
310 $ echo B > B
311 $ hg ci -Am B
311 $ hg ci -Am B
312 adding B
312 adding B
313
313
314 $ hg up -q 0
314 $ hg up -q 0
315
315
316 $ echo C > C
316 $ echo C > C
317 $ hg ci -Am C
317 $ hg ci -Am C
318 adding C
318 adding C
319 created new head
319 created new head
320
320
321 $ hg merge
321 $ hg merge
322 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
322 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
323 (branch merge, don't forget to commit)
323 (branch merge, don't forget to commit)
324
324
325 $ echo D > D
325 $ echo D > D
326 $ hg ci -Am D
326 $ hg ci -Am D
327 adding D
327 adding D
328
328
329 $ hg up -q 1
329 $ hg up -q 1
330
330
331 $ echo E > E
331 $ echo E > E
332 $ hg ci -Am E
332 $ hg ci -Am E
333 adding E
333 adding E
334 created new head
334 created new head
335 $ echo F > E
335 $ echo F > E
336 $ hg ci -m 'F'
336 $ hg ci -m 'F'
337
337
338 $ echo G > G
338 $ echo G > G
339 $ hg ci -Am G
339 $ hg ci -Am G
340 adding G
340 adding G
341
341
342 $ hg merge
342 $ hg merge
343 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
343 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
344 (branch merge, don't forget to commit)
344 (branch merge, don't forget to commit)
345
345
346 $ hg ci -m H
346 $ hg ci -m H
347
347
348 $ hg up -q 0
348 $ hg up -q 0
349
349
350 $ echo I > I
350 $ echo I > I
351 $ hg ci -Am I
351 $ hg ci -Am I
352 adding I
352 adding I
353 created new head
353 created new head
354
354
355 $ hg tglog
355 $ hg tglog
356 @ 8: 'I'
356 @ 8: 'I'
357 |
357 |
358 | o 7: 'H'
358 | o 7: 'H'
359 | |\
359 | |\
360 | | o 6: 'G'
360 | | o 6: 'G'
361 | | |
361 | | |
362 | | o 5: 'F'
362 | | o 5: 'F'
363 | | |
363 | | |
364 | | o 4: 'E'
364 | | o 4: 'E'
365 | | |
365 | | |
366 | o | 3: 'D'
366 | o | 3: 'D'
367 | |\|
367 | |\|
368 | o | 2: 'C'
368 | o | 2: 'C'
369 |/ /
369 |/ /
370 | o 1: 'B'
370 | o 1: 'B'
371 |/
371 |/
372 o 0: 'A'
372 o 0: 'A'
373
373
374 $ cd ..
374 $ cd ..
375
375
376
376
377 Rebase and collapse - E onto I:
377 Rebase and collapse - E onto I:
378
378
379 $ hg clone -q -u . c c1
379 $ hg clone -q -u . c c1
380 $ cd c1
380 $ cd c1
381
381
382 $ hg rebase -s 4 --collapse # root (4) is not a merge
382 $ hg rebase -s 4 --collapse # root (4) is not a merge
383 merging E
383 merging E
384 saved backup bundle to $TESTTMP/c1/.hg/strip-backup/*-backup.hg (glob)
384 saved backup bundle to $TESTTMP/c1/.hg/strip-backup/*-backup.hg (glob)
385
385
386 $ hg tglog
386 $ hg tglog
387 @ 5: 'Collapsed revision
387 @ 5: 'Collapsed revision
388 |\ * E
388 |\ * E
389 | | * F
389 | | * F
390 | | * G
390 | | * G
391 | | * H'
391 | | * H'
392 | o 4: 'I'
392 | o 4: 'I'
393 | |
393 | |
394 o | 3: 'D'
394 o | 3: 'D'
395 |\ \
395 |\ \
396 | o | 2: 'C'
396 | o | 2: 'C'
397 | |/
397 | |/
398 o / 1: 'B'
398 o / 1: 'B'
399 |/
399 |/
400 o 0: 'A'
400 o 0: 'A'
401
401
402 $ hg manifest
402 $ hg manifest
403 A
403 A
404 C
404 C
405 D
405 D
406 E
406 E
407 G
407 G
408 I
408 I
409
409
410 $ cat E
410 $ cat E
411 F
411 F
412
412
413 $ cd ..
413 $ cd ..
414
414
415
415
416 Create repo d:
416 Create repo d:
417
417
418 $ hg init d
418 $ hg init d
419 $ cd d
419 $ cd d
420
420
421 $ echo A > A
421 $ echo A > A
422 $ hg ci -Am A
422 $ hg ci -Am A
423 adding A
423 adding A
424 $ echo B > B
424 $ echo B > B
425 $ hg ci -Am B
425 $ hg ci -Am B
426 adding B
426 adding B
427 $ echo C > C
427 $ echo C > C
428 $ hg ci -Am C
428 $ hg ci -Am C
429 adding C
429 adding C
430
430
431 $ hg up -q 1
431 $ hg up -q 1
432
432
433 $ echo D > D
433 $ echo D > D
434 $ hg ci -Am D
434 $ hg ci -Am D
435 adding D
435 adding D
436 created new head
436 created new head
437 $ hg merge
437 $ hg merge
438 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
438 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
439 (branch merge, don't forget to commit)
439 (branch merge, don't forget to commit)
440
440
441 $ hg ci -m E
441 $ hg ci -m E
442
442
443 $ hg up -q 0
443 $ hg up -q 0
444
444
445 $ echo F > F
445 $ echo F > F
446 $ hg ci -Am F
446 $ hg ci -Am F
447 adding F
447 adding F
448 created new head
448 created new head
449
449
450 $ hg tglog
450 $ hg tglog
451 @ 5: 'F'
451 @ 5: 'F'
452 |
452 |
453 | o 4: 'E'
453 | o 4: 'E'
454 | |\
454 | |\
455 | | o 3: 'D'
455 | | o 3: 'D'
456 | | |
456 | | |
457 | o | 2: 'C'
457 | o | 2: 'C'
458 | |/
458 | |/
459 | o 1: 'B'
459 | o 1: 'B'
460 |/
460 |/
461 o 0: 'A'
461 o 0: 'A'
462
462
463 $ cd ..
463 $ cd ..
464
464
465
465
466 Rebase and collapse - B onto F:
466 Rebase and collapse - B onto F:
467
467
468 $ hg clone -q -u . d d1
468 $ hg clone -q -u . d d1
469 $ cd d1
469 $ cd d1
470
470
471 $ hg rebase -s 1 --collapse
471 $ hg rebase -s 1 --collapse
472 saved backup bundle to $TESTTMP/d1/.hg/strip-backup/*-backup.hg (glob)
472 saved backup bundle to $TESTTMP/d1/.hg/strip-backup/*-backup.hg (glob)
473
473
474 $ hg tglog
474 $ hg tglog
475 @ 2: 'Collapsed revision
475 @ 2: 'Collapsed revision
476 | * B
476 | * B
477 | * C
477 | * C
478 | * D
478 | * D
479 | * E'
479 | * E'
480 o 1: 'F'
480 o 1: 'F'
481 |
481 |
482 o 0: 'A'
482 o 0: 'A'
483
483
484 $ hg manifest
484 $ hg manifest
485 A
485 A
486 B
486 B
487 C
487 C
488 D
488 D
489 F
489 F
490
490
491 Interactions between collapse and keepbranches
491 Interactions between collapse and keepbranches
492 $ cd ..
492 $ cd ..
493 $ hg init e
493 $ hg init e
494 $ cd e
494 $ cd e
495 $ echo 'a' > a
495 $ echo 'a' > a
496 $ hg ci -Am 'A'
496 $ hg ci -Am 'A'
497 adding a
497 adding a
498
498
499 $ hg branch 'one'
499 $ hg branch 'one'
500 marked working directory as branch one
500 marked working directory as branch one
501 (branches are permanent and global, did you want a bookmark?)
501 (branches are permanent and global, did you want a bookmark?)
502 $ echo 'b' > b
502 $ echo 'b' > b
503 $ hg ci -Am 'B'
503 $ hg ci -Am 'B'
504 adding b
504 adding b
505
505
506 $ hg branch 'two'
506 $ hg branch 'two'
507 marked working directory as branch two
507 marked working directory as branch two
508 (branches are permanent and global, did you want a bookmark?)
508 (branches are permanent and global, did you want a bookmark?)
509 $ echo 'c' > c
509 $ echo 'c' > c
510 $ hg ci -Am 'C'
510 $ hg ci -Am 'C'
511 adding c
511 adding c
512
512
513 $ hg up -q 0
513 $ hg up -q 0
514 $ echo 'd' > d
514 $ echo 'd' > d
515 $ hg ci -Am 'D'
515 $ hg ci -Am 'D'
516 adding d
516 adding d
517
517
518 $ hg tglog
518 $ hg tglog
519 @ 3: 'D'
519 @ 3: 'D'
520 |
520 |
521 | o 2: 'C' two
521 | o 2: 'C' two
522 | |
522 | |
523 | o 1: 'B' one
523 | o 1: 'B' one
524 |/
524 |/
525 o 0: 'A'
525 o 0: 'A'
526
526
527 $ hg rebase --keepbranches --collapse -s 1 -d 3
527 $ hg rebase --keepbranches --collapse -s 1 -d 3
528 abort: cannot collapse multiple named branches
528 abort: cannot collapse multiple named branches
529 [255]
529 [255]
530
530
531 $ repeatchange() {
531 $ repeatchange() {
532 > hg checkout $1
532 > hg checkout $1
533 > hg cp d z
533 > hg cp d z
534 > echo blah >> z
534 > echo blah >> z
535 > hg commit -Am "$2" --user "$3"
535 > hg commit -Am "$2" --user "$3"
536 > }
536 > }
537 $ repeatchange 3 "E" "user1"
537 $ repeatchange 3 "E" "user1"
538 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
538 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
539 $ repeatchange 3 "E" "user2"
539 $ repeatchange 3 "E" "user2"
540 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
540 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
541 created new head
541 created new head
542 $ hg tglog
542 $ hg tglog
543 @ 5: 'E'
543 @ 5: 'E'
544 |
544 |
545 | o 4: 'E'
545 | o 4: 'E'
546 |/
546 |/
547 o 3: 'D'
547 o 3: 'D'
548 |
548 |
549 | o 2: 'C' two
549 | o 2: 'C' two
550 | |
550 | |
551 | o 1: 'B' one
551 | o 1: 'B' one
552 |/
552 |/
553 o 0: 'A'
553 o 0: 'A'
554
554
555 $ hg rebase -s 5 -d 4
555 $ hg rebase -s 5 -d 4
556 saved backup bundle to $TESTTMP/e/.hg/strip-backup/*-backup.hg (glob)
556 saved backup bundle to $TESTTMP/e/.hg/strip-backup/*-backup.hg (glob)
557 $ hg tglog
557 $ hg tglog
558 @ 4: 'E'
558 @ 4: 'E'
559 |
559 |
560 o 3: 'D'
560 o 3: 'D'
561 |
561 |
562 | o 2: 'C' two
562 | o 2: 'C' two
563 | |
563 | |
564 | o 1: 'B' one
564 | o 1: 'B' one
565 |/
565 |/
566 o 0: 'A'
566 o 0: 'A'
567
567
568 $ hg export tip
568 $ hg export tip
569 # HG changeset patch
569 # HG changeset patch
570 # User user1
570 # User user1
571 # Date 0 0
571 # Date 0 0
572 # Thu Jan 01 00:00:00 1970 +0000
572 # Thu Jan 01 00:00:00 1970 +0000
573 # Node ID f338eb3c2c7cc5b5915676a2376ba7ac558c5213
573 # Node ID f338eb3c2c7cc5b5915676a2376ba7ac558c5213
574 # Parent 41acb9dca9eb976e84cd21fcb756b4afa5a35c09
574 # Parent 41acb9dca9eb976e84cd21fcb756b4afa5a35c09
575 E
575 E
576
576
577 diff -r 41acb9dca9eb -r f338eb3c2c7c z
577 diff -r 41acb9dca9eb -r f338eb3c2c7c z
578 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
578 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
579 +++ b/z Thu Jan 01 00:00:00 1970 +0000
579 +++ b/z Thu Jan 01 00:00:00 1970 +0000
580 @@ -0,0 +1,2 @@
580 @@ -0,0 +1,2 @@
581 +d
581 +d
582 +blah
582 +blah
583
583
584 $ cd ..
584 $ cd ..
585
585
586 Rebase, collapse and copies
586 Rebase, collapse and copies
587
587
588 $ hg init copies
588 $ hg init copies
589 $ cd copies
589 $ cd copies
590 $ hg unbundle "$TESTDIR/bundles/renames.hg"
590 $ hg unbundle "$TESTDIR/bundles/renames.hg"
591 adding changesets
591 adding changesets
592 adding manifests
592 adding manifests
593 adding file changes
593 adding file changes
594 added 4 changesets with 11 changes to 7 files (+1 heads)
594 added 4 changesets with 11 changes to 7 files (+1 heads)
595 (run 'hg heads' to see heads, 'hg merge' to merge)
595 (run 'hg heads' to see heads, 'hg merge' to merge)
596 $ hg up -q tip
596 $ hg up -q tip
597 $ hg tglog
597 $ hg tglog
598 @ 3: 'move2'
598 @ 3: 'move2'
599 |
599 |
600 o 2: 'move1'
600 o 2: 'move1'
601 |
601 |
602 | o 1: 'change'
602 | o 1: 'change'
603 |/
603 |/
604 o 0: 'add'
604 o 0: 'add'
605
605
606 $ hg rebase --collapse -d 1
606 $ hg rebase --collapse -d 1
607 merging a and d to d
607 merging a and d to d
608 merging b and e to e
608 merging b and e to e
609 merging c and f to f
609 merging c and f to f
610 merging e and g to g
610 merging e and g to g
611 merging f and c to c
611 merging f and c to c
612 saved backup bundle to $TESTTMP/copies/.hg/strip-backup/*-backup.hg (glob)
612 saved backup bundle to $TESTTMP/copies/.hg/strip-backup/*-backup.hg (glob)
613 $ hg st
613 $ hg st
614 $ hg st --copies --change .
614 $ hg st --copies --change .
615 A d
615 A d
616 a
616 a
617 A g
617 A g
618 b
618 b
619 R b
619 R b
620 $ cat c
620 $ cat c
621 c
621 c
622 c
622 c
623 $ cat d
623 $ cat d
624 a
624 a
625 a
625 a
626 $ cat g
626 $ cat g
627 b
627 b
628 b
628 b
629 $ hg log -r . --template "{file_copies}\n"
629 $ hg log -r . --template "{file_copies}\n"
630 d (a)g (b)
630 d (a)g (b)
631
631
632 Test collapsing a middle revision in-place
632 Test collapsing a middle revision in-place
633
633
634 $ hg tglog
634 $ hg tglog
635 @ 2: 'Collapsed revision
635 @ 2: 'Collapsed revision
636 | * move1
636 | * move1
637 | * move2'
637 | * move2'
638 o 1: 'change'
638 o 1: 'change'
639 |
639 |
640 o 0: 'add'
640 o 0: 'add'
641
641
642 $ hg rebase --collapse -r 1 -d 0
642 $ hg rebase --collapse -r 1 -d 0
643 abort: can't remove original changesets with unrebased descendants
643 abort: can't remove original changesets with unrebased descendants
644 (use --keep to keep original changesets)
644 (use --keep to keep original changesets)
645 [255]
645 [255]
646
646
647 Test collapsing in place
647 Test collapsing in place
648
648
649 $ hg rebase --collapse -b . -d 0
649 $ hg rebase --collapse -b . -d 0
650 saved backup bundle to $TESTTMP/copies/.hg/strip-backup/*-backup.hg (glob)
650 saved backup bundle to $TESTTMP/copies/.hg/strip-backup/*-backup.hg (glob)
651 $ hg st --change . --copies
651 $ hg st --change . --copies
652 M a
652 M a
653 M c
653 M c
654 A d
654 A d
655 a
655 a
656 A g
656 A g
657 b
657 b
658 R b
658 R b
659 $ cat a
659 $ cat a
660 a
660 a
661 a
661 a
662 $ cat c
662 $ cat c
663 c
663 c
664 c
664 c
665 $ cat d
665 $ cat d
666 a
666 a
667 a
667 a
668 $ cat g
668 $ cat g
669 b
669 b
670 b
670 b
671 $ cd ..
671 $ cd ..
672
672
673
673
674 Test stripping a revision with another child
674 Test stripping a revision with another child
675
675
676 $ hg init f
676 $ hg init f
677 $ cd f
677 $ cd f
678
678
679 $ echo A > A
679 $ echo A > A
680 $ hg ci -Am A
680 $ hg ci -Am A
681 adding A
681 adding A
682 $ echo B > B
682 $ echo B > B
683 $ hg ci -Am B
683 $ hg ci -Am B
684 adding B
684 adding B
685
685
686 $ hg up -q 0
686 $ hg up -q 0
687
687
688 $ echo C > C
688 $ echo C > C
689 $ hg ci -Am C
689 $ hg ci -Am C
690 adding C
690 adding C
691 created new head
691 created new head
692
692
693 $ hg tglog
693 $ hg tglog
694 @ 2: 'C'
694 @ 2: 'C'
695 |
695 |
696 | o 1: 'B'
696 | o 1: 'B'
697 |/
697 |/
698 o 0: 'A'
698 o 0: 'A'
699
699
700
700
701
701
702 $ hg heads --template="{rev}:{node} {branch}: {desc}\n"
702 $ hg heads --template="{rev}:{node} {branch}: {desc}\n"
703 2:c5cefa58fd557f84b72b87f970135984337acbc5 default: C
703 2:c5cefa58fd557f84b72b87f970135984337acbc5 default: C
704 1:27547f69f25460a52fff66ad004e58da7ad3fb56 default: B
704 1:27547f69f25460a52fff66ad004e58da7ad3fb56 default: B
705
705
706 $ hg strip 2
706 $ hg strip 2
707 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
707 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
708 saved backup bundle to $TESTTMP/f/.hg/strip-backup/*-backup.hg (glob)
708 saved backup bundle to $TESTTMP/f/.hg/strip-backup/*-backup.hg (glob)
709
709
710 $ hg tglog
710 $ hg tglog
711 o 1: 'B'
711 o 1: 'B'
712 |
712 |
713 @ 0: 'A'
713 @ 0: 'A'
714
714
715
715
716
716
717 $ hg heads --template="{rev}:{node} {branch}: {desc}\n"
717 $ hg heads --template="{rev}:{node} {branch}: {desc}\n"
718 1:27547f69f25460a52fff66ad004e58da7ad3fb56 default: B
718 1:27547f69f25460a52fff66ad004e58da7ad3fb56 default: B
719
719
720 $ cd ..
720 $ cd ..
721
721
722 Test collapsing changes that add then remove a file
722
723
724 $ hg init collapseaddremove
725 $ cd collapseaddremove
723
726
727 $ touch base
728 $ hg commit -Am base
729 adding base
730 $ touch a
731 $ hg commit -Am a
732 adding a
733 $ hg rm a
734 $ touch b
735 $ hg commit -Am b
736 adding b
737 $ hg rebase -d 0 -r "1::2" --collapse -m collapsed
738 saved backup bundle to $TESTTMP/collapseaddremove/.hg/strip-backup/*-backup.hg (glob)
739 $ hg tglog
740 @ 1: 'collapsed'
741 |
742 o 0: 'base'
743
744 $ hg manifest
745 b
746 base
724
747
748 $ cd ..
@@ -1,400 +1,398 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > graphlog=
3 > graphlog=
4 > rebase=
4 > rebase=
5 >
5 >
6 > [phases]
6 > [phases]
7 > publish=False
7 > publish=False
8 >
8 >
9 > [alias]
9 > [alias]
10 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
10 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
11 > EOF
11 > EOF
12
12
13
13
14 $ hg init a
14 $ hg init a
15 $ cd a
15 $ cd a
16 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
16 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
17 adding changesets
17 adding changesets
18 adding manifests
18 adding manifests
19 adding file changes
19 adding file changes
20 added 8 changesets with 7 changes to 7 files (+2 heads)
20 added 8 changesets with 7 changes to 7 files (+2 heads)
21 (run 'hg heads' to see heads, 'hg merge' to merge)
21 (run 'hg heads' to see heads, 'hg merge' to merge)
22 $ hg up tip
22 $ hg up tip
23 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
23 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
24
24
25 $ cd ..
25 $ cd ..
26
26
27
27
28 Rebasing D onto H detaching from C:
28 Rebasing D onto H detaching from C:
29
29
30 $ hg clone -q -u . a a1
30 $ hg clone -q -u . a a1
31 $ cd a1
31 $ cd a1
32
32
33 $ hg tglog
33 $ hg tglog
34 @ 7: 'H'
34 @ 7: 'H'
35 |
35 |
36 | o 6: 'G'
36 | o 6: 'G'
37 |/|
37 |/|
38 o | 5: 'F'
38 o | 5: 'F'
39 | |
39 | |
40 | o 4: 'E'
40 | o 4: 'E'
41 |/
41 |/
42 | o 3: 'D'
42 | o 3: 'D'
43 | |
43 | |
44 | o 2: 'C'
44 | o 2: 'C'
45 | |
45 | |
46 | o 1: 'B'
46 | o 1: 'B'
47 |/
47 |/
48 o 0: 'A'
48 o 0: 'A'
49
49
50 $ hg phase --force --secret 3
50 $ hg phase --force --secret 3
51 $ hg rebase -s 3 -d 7
51 $ hg rebase -s 3 -d 7
52 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
52 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
53
53
54 $ hg log -G --template "{rev}:{phase} '{desc}' {branches}\n"
54 $ hg log -G --template "{rev}:{phase} '{desc}' {branches}\n"
55 @ 7:secret 'D'
55 @ 7:secret 'D'
56 |
56 |
57 o 6:draft 'H'
57 o 6:draft 'H'
58 |
58 |
59 | o 5:draft 'G'
59 | o 5:draft 'G'
60 |/|
60 |/|
61 o | 4:draft 'F'
61 o | 4:draft 'F'
62 | |
62 | |
63 | o 3:draft 'E'
63 | o 3:draft 'E'
64 |/
64 |/
65 | o 2:draft 'C'
65 | o 2:draft 'C'
66 | |
66 | |
67 | o 1:draft 'B'
67 | o 1:draft 'B'
68 |/
68 |/
69 o 0:draft 'A'
69 o 0:draft 'A'
70
70
71 $ hg manifest
71 $ hg manifest
72 A
72 A
73 D
73 D
74 F
74 F
75 H
75 H
76
76
77 $ cd ..
77 $ cd ..
78
78
79
79
80 Rebasing C onto H detaching from B:
80 Rebasing C onto H detaching from B:
81
81
82 $ hg clone -q -u . a a2
82 $ hg clone -q -u . a a2
83 $ cd a2
83 $ cd a2
84
84
85 $ hg tglog
85 $ hg tglog
86 @ 7: 'H'
86 @ 7: 'H'
87 |
87 |
88 | o 6: 'G'
88 | o 6: 'G'
89 |/|
89 |/|
90 o | 5: 'F'
90 o | 5: 'F'
91 | |
91 | |
92 | o 4: 'E'
92 | o 4: 'E'
93 |/
93 |/
94 | o 3: 'D'
94 | o 3: 'D'
95 | |
95 | |
96 | o 2: 'C'
96 | o 2: 'C'
97 | |
97 | |
98 | o 1: 'B'
98 | o 1: 'B'
99 |/
99 |/
100 o 0: 'A'
100 o 0: 'A'
101
101
102 $ hg rebase -s 2 -d 7
102 $ hg rebase -s 2 -d 7
103 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
103 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
104
104
105 $ hg tglog
105 $ hg tglog
106 @ 7: 'D'
106 @ 7: 'D'
107 |
107 |
108 o 6: 'C'
108 o 6: 'C'
109 |
109 |
110 o 5: 'H'
110 o 5: 'H'
111 |
111 |
112 | o 4: 'G'
112 | o 4: 'G'
113 |/|
113 |/|
114 o | 3: 'F'
114 o | 3: 'F'
115 | |
115 | |
116 | o 2: 'E'
116 | o 2: 'E'
117 |/
117 |/
118 | o 1: 'B'
118 | o 1: 'B'
119 |/
119 |/
120 o 0: 'A'
120 o 0: 'A'
121
121
122 $ hg manifest
122 $ hg manifest
123 A
123 A
124 C
124 C
125 D
125 D
126 F
126 F
127 H
127 H
128
128
129 $ cd ..
129 $ cd ..
130
130
131
131
132 Rebasing B onto H using detach (same as not using it):
132 Rebasing B onto H using detach (same as not using it):
133
133
134 $ hg clone -q -u . a a3
134 $ hg clone -q -u . a a3
135 $ cd a3
135 $ cd a3
136
136
137 $ hg tglog
137 $ hg tglog
138 @ 7: 'H'
138 @ 7: 'H'
139 |
139 |
140 | o 6: 'G'
140 | o 6: 'G'
141 |/|
141 |/|
142 o | 5: 'F'
142 o | 5: 'F'
143 | |
143 | |
144 | o 4: 'E'
144 | o 4: 'E'
145 |/
145 |/
146 | o 3: 'D'
146 | o 3: 'D'
147 | |
147 | |
148 | o 2: 'C'
148 | o 2: 'C'
149 | |
149 | |
150 | o 1: 'B'
150 | o 1: 'B'
151 |/
151 |/
152 o 0: 'A'
152 o 0: 'A'
153
153
154 $ hg rebase -s 1 -d 7
154 $ hg rebase -s 1 -d 7
155 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
155 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
156
156
157 $ hg tglog
157 $ hg tglog
158 @ 7: 'D'
158 @ 7: 'D'
159 |
159 |
160 o 6: 'C'
160 o 6: 'C'
161 |
161 |
162 o 5: 'B'
162 o 5: 'B'
163 |
163 |
164 o 4: 'H'
164 o 4: 'H'
165 |
165 |
166 | o 3: 'G'
166 | o 3: 'G'
167 |/|
167 |/|
168 o | 2: 'F'
168 o | 2: 'F'
169 | |
169 | |
170 | o 1: 'E'
170 | o 1: 'E'
171 |/
171 |/
172 o 0: 'A'
172 o 0: 'A'
173
173
174 $ hg manifest
174 $ hg manifest
175 A
175 A
176 B
176 B
177 C
177 C
178 D
178 D
179 F
179 F
180 H
180 H
181
181
182 $ cd ..
182 $ cd ..
183
183
184
184
185 Rebasing C onto H detaching from B and collapsing:
185 Rebasing C onto H detaching from B and collapsing:
186
186
187 $ hg clone -q -u . a a4
187 $ hg clone -q -u . a a4
188 $ cd a4
188 $ cd a4
189 $ hg phase --force --secret 3
189 $ hg phase --force --secret 3
190
190
191 $ hg tglog
191 $ hg tglog
192 @ 7: 'H'
192 @ 7: 'H'
193 |
193 |
194 | o 6: 'G'
194 | o 6: 'G'
195 |/|
195 |/|
196 o | 5: 'F'
196 o | 5: 'F'
197 | |
197 | |
198 | o 4: 'E'
198 | o 4: 'E'
199 |/
199 |/
200 | o 3: 'D'
200 | o 3: 'D'
201 | |
201 | |
202 | o 2: 'C'
202 | o 2: 'C'
203 | |
203 | |
204 | o 1: 'B'
204 | o 1: 'B'
205 |/
205 |/
206 o 0: 'A'
206 o 0: 'A'
207
207
208 $ hg rebase --collapse -s 2 -d 7
208 $ hg rebase --collapse -s 2 -d 7
209 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/*-backup.hg (glob)
209 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/*-backup.hg (glob)
210
210
211 $ hg log -G --template "{rev}:{phase} '{desc}' {branches}\n"
211 $ hg log -G --template "{rev}:{phase} '{desc}' {branches}\n"
212 @ 6:secret 'Collapsed revision
212 @ 6:secret 'Collapsed revision
213 | * C
213 | * C
214 | * D'
214 | * D'
215 o 5:draft 'H'
215 o 5:draft 'H'
216 |
216 |
217 | o 4:draft 'G'
217 | o 4:draft 'G'
218 |/|
218 |/|
219 o | 3:draft 'F'
219 o | 3:draft 'F'
220 | |
220 | |
221 | o 2:draft 'E'
221 | o 2:draft 'E'
222 |/
222 |/
223 | o 1:draft 'B'
223 | o 1:draft 'B'
224 |/
224 |/
225 o 0:draft 'A'
225 o 0:draft 'A'
226
226
227 $ hg manifest
227 $ hg manifest
228 A
228 A
229 C
229 C
230 D
230 D
231 F
231 F
232 H
232 H
233
233
234 $ cd ..
234 $ cd ..
235
235
236 Rebasing across null as ancestor
236 Rebasing across null as ancestor
237 $ hg clone -q -U a a5
237 $ hg clone -q -U a a5
238
238
239 $ cd a5
239 $ cd a5
240
240
241 $ echo x > x
241 $ echo x > x
242
242
243 $ hg add x
243 $ hg add x
244
244
245 $ hg ci -m "extra branch"
245 $ hg ci -m "extra branch"
246 created new head
246 created new head
247
247
248 $ hg tglog
248 $ hg tglog
249 @ 8: 'extra branch'
249 @ 8: 'extra branch'
250
250
251 o 7: 'H'
251 o 7: 'H'
252 |
252 |
253 | o 6: 'G'
253 | o 6: 'G'
254 |/|
254 |/|
255 o | 5: 'F'
255 o | 5: 'F'
256 | |
256 | |
257 | o 4: 'E'
257 | o 4: 'E'
258 |/
258 |/
259 | o 3: 'D'
259 | o 3: 'D'
260 | |
260 | |
261 | o 2: 'C'
261 | o 2: 'C'
262 | |
262 | |
263 | o 1: 'B'
263 | o 1: 'B'
264 |/
264 |/
265 o 0: 'A'
265 o 0: 'A'
266
266
267 $ hg rebase -s 1 -d tip
267 $ hg rebase -s 1 -d tip
268 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/*-backup.hg (glob)
268 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/*-backup.hg (glob)
269
269
270 $ hg tglog
270 $ hg tglog
271 @ 8: 'D'
271 @ 8: 'D'
272 |
272 |
273 o 7: 'C'
273 o 7: 'C'
274 |
274 |
275 o 6: 'B'
275 o 6: 'B'
276 |
276 |
277 o 5: 'extra branch'
277 o 5: 'extra branch'
278
278
279 o 4: 'H'
279 o 4: 'H'
280 |
280 |
281 | o 3: 'G'
281 | o 3: 'G'
282 |/|
282 |/|
283 o | 2: 'F'
283 o | 2: 'F'
284 | |
284 | |
285 | o 1: 'E'
285 | o 1: 'E'
286 |/
286 |/
287 o 0: 'A'
287 o 0: 'A'
288
288
289
289
290 $ hg rebase -d 5 -s 7
290 $ hg rebase -d 5 -s 7
291 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/13547172c9c0-backup.hg (glob)
291 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/13547172c9c0-backup.hg (glob)
292 $ hg tglog
292 $ hg tglog
293 @ 8: 'D'
293 @ 8: 'D'
294 |
294 |
295 o 7: 'C'
295 o 7: 'C'
296 |
296 |
297 | o 6: 'B'
297 | o 6: 'B'
298 |/
298 |/
299 o 5: 'extra branch'
299 o 5: 'extra branch'
300
300
301 o 4: 'H'
301 o 4: 'H'
302 |
302 |
303 | o 3: 'G'
303 | o 3: 'G'
304 |/|
304 |/|
305 o | 2: 'F'
305 o | 2: 'F'
306 | |
306 | |
307 | o 1: 'E'
307 | o 1: 'E'
308 |/
308 |/
309 o 0: 'A'
309 o 0: 'A'
310
310
311 $ cd ..
311 $ cd ..
312
312
313 Verify that target is not selected as external rev (issue3085)
313 Verify that target is not selected as external rev (issue3085)
314
314
315 $ hg clone -q -U a a6
315 $ hg clone -q -U a a6
316 $ cd a6
316 $ cd a6
317 $ hg up -q 6
317 $ hg up -q 6
318
318
319 $ echo "I" >> E
319 $ echo "I" >> E
320 $ hg ci -m "I"
320 $ hg ci -m "I"
321 $ hg merge 7
321 $ hg merge 7
322 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
322 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
323 (branch merge, don't forget to commit)
323 (branch merge, don't forget to commit)
324 $ hg ci -m "Merge"
324 $ hg ci -m "Merge"
325 $ echo "J" >> F
325 $ echo "J" >> F
326 $ hg ci -m "J"
326 $ hg ci -m "J"
327
327
328 $ hg rebase -s 8 -d 7 --collapse --config ui.merge=internal:other
328 $ hg rebase -s 8 -d 7 --collapse --config ui.merge=internal:other
329 remote changed E which local deleted
330 use (c)hanged version or leave (d)eleted? c
331 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob)
329 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob)
332
330
333 $ hg tglog
331 $ hg tglog
334 @ 8: 'Collapsed revision
332 @ 8: 'Collapsed revision
335 | * I
333 | * I
336 | * Merge
334 | * Merge
337 | * J'
335 | * J'
338 o 7: 'H'
336 o 7: 'H'
339 |
337 |
340 | o 6: 'G'
338 | o 6: 'G'
341 |/|
339 |/|
342 o | 5: 'F'
340 o | 5: 'F'
343 | |
341 | |
344 | o 4: 'E'
342 | o 4: 'E'
345 |/
343 |/
346 | o 3: 'D'
344 | o 3: 'D'
347 | |
345 | |
348 | o 2: 'C'
346 | o 2: 'C'
349 | |
347 | |
350 | o 1: 'B'
348 | o 1: 'B'
351 |/
349 |/
352 o 0: 'A'
350 o 0: 'A'
353
351
354
352
355 $ hg parents
353 $ hg parents
356 changeset: 8:9472f4b1d736
354 changeset: 8:9472f4b1d736
357 tag: tip
355 tag: tip
358 user: test
356 user: test
359 date: Thu Jan 01 00:00:00 1970 +0000
357 date: Thu Jan 01 00:00:00 1970 +0000
360 summary: Collapsed revision
358 summary: Collapsed revision
361
359
362
360
363 $ cd ..
361 $ cd ..
364
362
365 Ensure --continue restores a correct state (issue3046) and phase:
363 Ensure --continue restores a correct state (issue3046) and phase:
366 $ hg clone -q a a7
364 $ hg clone -q a a7
367 $ cd a7
365 $ cd a7
368 $ hg up -q 3
366 $ hg up -q 3
369 $ echo 'H2' > H
367 $ echo 'H2' > H
370 $ hg ci -A -m 'H2'
368 $ hg ci -A -m 'H2'
371 adding H
369 adding H
372 $ hg phase --force --secret 8
370 $ hg phase --force --secret 8
373 $ hg rebase -s 8 -d 7 --config ui.merge=internal:fail
371 $ hg rebase -s 8 -d 7 --config ui.merge=internal:fail
374 merging H
372 merging H
375 warning: conflicts during merge.
373 warning: conflicts during merge.
376 merging H incomplete! (edit conflicts, then use 'hg resolve --mark')
374 merging H incomplete! (edit conflicts, then use 'hg resolve --mark')
377 abort: unresolved conflicts (see hg resolve, then hg rebase --continue)
375 abort: unresolved conflicts (see hg resolve, then hg rebase --continue)
378 [255]
376 [255]
379 $ hg resolve --all -t internal:local
377 $ hg resolve --all -t internal:local
380 $ hg rebase -c
378 $ hg rebase -c
381 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/6215fafa5447-backup.hg (glob)
379 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/6215fafa5447-backup.hg (glob)
382 $ hg log -G --template "{rev}:{phase} '{desc}' {branches}\n"
380 $ hg log -G --template "{rev}:{phase} '{desc}' {branches}\n"
383 @ 7:draft 'H'
381 @ 7:draft 'H'
384 |
382 |
385 | o 6:draft 'G'
383 | o 6:draft 'G'
386 |/|
384 |/|
387 o | 5:draft 'F'
385 o | 5:draft 'F'
388 | |
386 | |
389 | o 4:draft 'E'
387 | o 4:draft 'E'
390 |/
388 |/
391 | o 3:draft 'D'
389 | o 3:draft 'D'
392 | |
390 | |
393 | o 2:draft 'C'
391 | o 2:draft 'C'
394 | |
392 | |
395 | o 1:draft 'B'
393 | o 1:draft 'B'
396 |/
394 |/
397 o 0:draft 'A'
395 o 0:draft 'A'
398
396
399
397
400 $ cd ..
398 $ cd ..
@@ -1,878 +1,876 b''
1 $ HGENCODING=utf-8
1 $ HGENCODING=utf-8
2 $ export HGENCODING
2 $ export HGENCODING
3
3
4 $ try() {
4 $ try() {
5 > hg debugrevspec --debug "$@"
5 > hg debugrevspec --debug "$@"
6 > }
6 > }
7
7
8 $ log() {
8 $ log() {
9 > hg log --template '{rev}\n' -r "$1"
9 > hg log --template '{rev}\n' -r "$1"
10 > }
10 > }
11
11
12 $ hg init repo
12 $ hg init repo
13 $ cd repo
13 $ cd repo
14
14
15 $ echo a > a
15 $ echo a > a
16 $ hg branch a
16 $ hg branch a
17 marked working directory as branch a
17 marked working directory as branch a
18 (branches are permanent and global, did you want a bookmark?)
18 (branches are permanent and global, did you want a bookmark?)
19 $ hg ci -Aqm0
19 $ hg ci -Aqm0
20
20
21 $ echo b > b
21 $ echo b > b
22 $ hg branch b
22 $ hg branch b
23 marked working directory as branch b
23 marked working directory as branch b
24 (branches are permanent and global, did you want a bookmark?)
24 (branches are permanent and global, did you want a bookmark?)
25 $ hg ci -Aqm1
25 $ hg ci -Aqm1
26
26
27 $ rm a
27 $ rm a
28 $ hg branch a-b-c-
28 $ hg branch a-b-c-
29 marked working directory as branch a-b-c-
29 marked working directory as branch a-b-c-
30 (branches are permanent and global, did you want a bookmark?)
30 (branches are permanent and global, did you want a bookmark?)
31 $ hg ci -Aqm2 -u Bob
31 $ hg ci -Aqm2 -u Bob
32
32
33 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
33 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
34 2
34 2
35 $ hg log -r "extra('branch')" --template '{rev}\n'
35 $ hg log -r "extra('branch')" --template '{rev}\n'
36 0
36 0
37 1
37 1
38 2
38 2
39 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
39 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
40 0 a
40 0 a
41 2 a-b-c-
41 2 a-b-c-
42
42
43 $ hg co 1
43 $ hg co 1
44 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
44 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 $ hg branch +a+b+c+
45 $ hg branch +a+b+c+
46 marked working directory as branch +a+b+c+
46 marked working directory as branch +a+b+c+
47 (branches are permanent and global, did you want a bookmark?)
47 (branches are permanent and global, did you want a bookmark?)
48 $ hg ci -Aqm3
48 $ hg ci -Aqm3
49
49
50 $ hg co 2 # interleave
50 $ hg co 2 # interleave
51 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
51 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
52 $ echo bb > b
52 $ echo bb > b
53 $ hg branch -- -a-b-c-
53 $ hg branch -- -a-b-c-
54 marked working directory as branch -a-b-c-
54 marked working directory as branch -a-b-c-
55 (branches are permanent and global, did you want a bookmark?)
55 (branches are permanent and global, did you want a bookmark?)
56 $ hg ci -Aqm4 -d "May 12 2005"
56 $ hg ci -Aqm4 -d "May 12 2005"
57
57
58 $ hg co 3
58 $ hg co 3
59 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
59 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 $ hg branch !a/b/c/
60 $ hg branch !a/b/c/
61 marked working directory as branch !a/b/c/
61 marked working directory as branch !a/b/c/
62 (branches are permanent and global, did you want a bookmark?)
62 (branches are permanent and global, did you want a bookmark?)
63 $ hg ci -Aqm"5 bug"
63 $ hg ci -Aqm"5 bug"
64
64
65 $ hg merge 4
65 $ hg merge 4
66 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
66 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
67 (branch merge, don't forget to commit)
67 (branch merge, don't forget to commit)
68 $ hg branch _a_b_c_
68 $ hg branch _a_b_c_
69 marked working directory as branch _a_b_c_
69 marked working directory as branch _a_b_c_
70 (branches are permanent and global, did you want a bookmark?)
70 (branches are permanent and global, did you want a bookmark?)
71 $ hg ci -Aqm"6 issue619"
71 $ hg ci -Aqm"6 issue619"
72
72
73 $ hg branch .a.b.c.
73 $ hg branch .a.b.c.
74 marked working directory as branch .a.b.c.
74 marked working directory as branch .a.b.c.
75 (branches are permanent and global, did you want a bookmark?)
75 (branches are permanent and global, did you want a bookmark?)
76 $ hg ci -Aqm7
76 $ hg ci -Aqm7
77
77
78 $ hg branch all
78 $ hg branch all
79 marked working directory as branch all
79 marked working directory as branch all
80 (branches are permanent and global, did you want a bookmark?)
80 (branches are permanent and global, did you want a bookmark?)
81 $ hg ci --close-branch -Aqm8
81 $ hg ci --close-branch -Aqm8
82 abort: can only close branch heads
82 abort: can only close branch heads
83 [255]
83 [255]
84
84
85 $ hg co 4
85 $ hg co 4
86 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
86 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 $ hg branch Γ©
87 $ hg branch Γ©
88 marked working directory as branch \xc3\xa9 (esc)
88 marked working directory as branch \xc3\xa9 (esc)
89 (branches are permanent and global, did you want a bookmark?)
89 (branches are permanent and global, did you want a bookmark?)
90 $ hg ci -Aqm9
90 $ hg ci -Aqm9
91
91
92 $ hg tag -r6 1.0
92 $ hg tag -r6 1.0
93
93
94 $ hg clone --quiet -U -r 7 . ../remote1
94 $ hg clone --quiet -U -r 7 . ../remote1
95 $ hg clone --quiet -U -r 8 . ../remote2
95 $ hg clone --quiet -U -r 8 . ../remote2
96 $ echo "[paths]" >> .hg/hgrc
96 $ echo "[paths]" >> .hg/hgrc
97 $ echo "default = ../remote1" >> .hg/hgrc
97 $ echo "default = ../remote1" >> .hg/hgrc
98
98
99 names that should work without quoting
99 names that should work without quoting
100
100
101 $ try a
101 $ try a
102 ('symbol', 'a')
102 ('symbol', 'a')
103 0
103 0
104 $ try b-a
104 $ try b-a
105 (minus
105 (minus
106 ('symbol', 'b')
106 ('symbol', 'b')
107 ('symbol', 'a'))
107 ('symbol', 'a'))
108 1
108 1
109 $ try _a_b_c_
109 $ try _a_b_c_
110 ('symbol', '_a_b_c_')
110 ('symbol', '_a_b_c_')
111 6
111 6
112 $ try _a_b_c_-a
112 $ try _a_b_c_-a
113 (minus
113 (minus
114 ('symbol', '_a_b_c_')
114 ('symbol', '_a_b_c_')
115 ('symbol', 'a'))
115 ('symbol', 'a'))
116 6
116 6
117 $ try .a.b.c.
117 $ try .a.b.c.
118 ('symbol', '.a.b.c.')
118 ('symbol', '.a.b.c.')
119 7
119 7
120 $ try .a.b.c.-a
120 $ try .a.b.c.-a
121 (minus
121 (minus
122 ('symbol', '.a.b.c.')
122 ('symbol', '.a.b.c.')
123 ('symbol', 'a'))
123 ('symbol', 'a'))
124 7
124 7
125 $ try -- '-a-b-c-' # complains
125 $ try -- '-a-b-c-' # complains
126 hg: parse error at 7: not a prefix: end
126 hg: parse error at 7: not a prefix: end
127 [255]
127 [255]
128 $ log -a-b-c- # succeeds with fallback
128 $ log -a-b-c- # succeeds with fallback
129 4
129 4
130 $ try -- -a-b-c--a # complains
130 $ try -- -a-b-c--a # complains
131 (minus
131 (minus
132 (minus
132 (minus
133 (minus
133 (minus
134 (negate
134 (negate
135 ('symbol', 'a'))
135 ('symbol', 'a'))
136 ('symbol', 'b'))
136 ('symbol', 'b'))
137 ('symbol', 'c'))
137 ('symbol', 'c'))
138 (negate
138 (negate
139 ('symbol', 'a')))
139 ('symbol', 'a')))
140 abort: unknown revision '-a'!
140 abort: unknown revision '-a'!
141 [255]
141 [255]
142 $ try Γ©
142 $ try Γ©
143 ('symbol', '\xc3\xa9')
143 ('symbol', '\xc3\xa9')
144 9
144 9
145
145
146 quoting needed
146 quoting needed
147
147
148 $ try '"-a-b-c-"-a'
148 $ try '"-a-b-c-"-a'
149 (minus
149 (minus
150 ('string', '-a-b-c-')
150 ('string', '-a-b-c-')
151 ('symbol', 'a'))
151 ('symbol', 'a'))
152 4
152 4
153
153
154 $ log '1 or 2'
154 $ log '1 or 2'
155 1
155 1
156 2
156 2
157 $ log '1|2'
157 $ log '1|2'
158 1
158 1
159 2
159 2
160 $ log '1 and 2'
160 $ log '1 and 2'
161 $ log '1&2'
161 $ log '1&2'
162 $ try '1&2|3' # precedence - and is higher
162 $ try '1&2|3' # precedence - and is higher
163 (or
163 (or
164 (and
164 (and
165 ('symbol', '1')
165 ('symbol', '1')
166 ('symbol', '2'))
166 ('symbol', '2'))
167 ('symbol', '3'))
167 ('symbol', '3'))
168 3
168 3
169 $ try '1|2&3'
169 $ try '1|2&3'
170 (or
170 (or
171 ('symbol', '1')
171 ('symbol', '1')
172 (and
172 (and
173 ('symbol', '2')
173 ('symbol', '2')
174 ('symbol', '3')))
174 ('symbol', '3')))
175 1
175 1
176 $ try '1&2&3' # associativity
176 $ try '1&2&3' # associativity
177 (and
177 (and
178 (and
178 (and
179 ('symbol', '1')
179 ('symbol', '1')
180 ('symbol', '2'))
180 ('symbol', '2'))
181 ('symbol', '3'))
181 ('symbol', '3'))
182 $ try '1|(2|3)'
182 $ try '1|(2|3)'
183 (or
183 (or
184 ('symbol', '1')
184 ('symbol', '1')
185 (group
185 (group
186 (or
186 (or
187 ('symbol', '2')
187 ('symbol', '2')
188 ('symbol', '3'))))
188 ('symbol', '3'))))
189 1
189 1
190 2
190 2
191 3
191 3
192 $ log '1.0' # tag
192 $ log '1.0' # tag
193 6
193 6
194 $ log 'a' # branch
194 $ log 'a' # branch
195 0
195 0
196 $ log '2785f51ee'
196 $ log '2785f51ee'
197 0
197 0
198 $ log 'date(2005)'
198 $ log 'date(2005)'
199 4
199 4
200 $ log 'date(this is a test)'
200 $ log 'date(this is a test)'
201 hg: parse error at 10: unexpected token: symbol
201 hg: parse error at 10: unexpected token: symbol
202 [255]
202 [255]
203 $ log 'date()'
203 $ log 'date()'
204 hg: parse error: date requires a string
204 hg: parse error: date requires a string
205 [255]
205 [255]
206 $ log 'date'
206 $ log 'date'
207 hg: parse error: can't use date here
207 hg: parse error: can't use date here
208 [255]
208 [255]
209 $ log 'date('
209 $ log 'date('
210 hg: parse error at 5: not a prefix: end
210 hg: parse error at 5: not a prefix: end
211 [255]
211 [255]
212 $ log 'date(tip)'
212 $ log 'date(tip)'
213 abort: invalid date: 'tip'
213 abort: invalid date: 'tip'
214 [255]
214 [255]
215 $ log '"date"'
215 $ log '"date"'
216 abort: unknown revision 'date'!
216 abort: unknown revision 'date'!
217 [255]
217 [255]
218 $ log 'date(2005) and 1::'
218 $ log 'date(2005) and 1::'
219 4
219 4
220
220
221 ancestor can accept 0 or more arguments
221 ancestor can accept 0 or more arguments
222
222
223 $ log 'ancestor()'
223 $ log 'ancestor()'
224 $ log 'ancestor(1)'
224 $ log 'ancestor(1)'
225 1
225 1
226 $ log 'ancestor(4,5)'
226 $ log 'ancestor(4,5)'
227 1
227 1
228 $ log 'ancestor(4,5) and 4'
228 $ log 'ancestor(4,5) and 4'
229 $ log 'ancestor(0,0,1,3)'
229 $ log 'ancestor(0,0,1,3)'
230 0
230 0
231 $ log 'ancestor(3,1,5,3,5,1)'
231 $ log 'ancestor(3,1,5,3,5,1)'
232 1
232 1
233 $ log 'ancestor(0,1,3,5)'
233 $ log 'ancestor(0,1,3,5)'
234 0
234 0
235 $ log 'ancestor(1,2,3,4,5)'
235 $ log 'ancestor(1,2,3,4,5)'
236 1
236 1
237 $ log 'ancestors(5)'
237 $ log 'ancestors(5)'
238 0
238 0
239 1
239 1
240 3
240 3
241 5
241 5
242 $ log 'ancestor(ancestors(5))'
242 $ log 'ancestor(ancestors(5))'
243 0
243 0
244 $ log 'author(bob)'
244 $ log 'author(bob)'
245 2
245 2
246 $ log 'author("re:bob|test")'
246 $ log 'author("re:bob|test")'
247 0
247 0
248 1
248 1
249 2
249 2
250 3
250 3
251 4
251 4
252 5
252 5
253 6
253 6
254 7
254 7
255 8
255 8
256 9
256 9
257 $ log 'branch(Γ©)'
257 $ log 'branch(Γ©)'
258 8
258 8
259 9
259 9
260 $ log 'branch(a)'
260 $ log 'branch(a)'
261 0
261 0
262 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
262 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
263 0 a
263 0 a
264 2 a-b-c-
264 2 a-b-c-
265 3 +a+b+c+
265 3 +a+b+c+
266 4 -a-b-c-
266 4 -a-b-c-
267 5 !a/b/c/
267 5 !a/b/c/
268 6 _a_b_c_
268 6 _a_b_c_
269 7 .a.b.c.
269 7 .a.b.c.
270 $ log 'children(ancestor(4,5))'
270 $ log 'children(ancestor(4,5))'
271 2
271 2
272 3
272 3
273 $ log 'closed()'
273 $ log 'closed()'
274 $ log 'contains(a)'
274 $ log 'contains(a)'
275 0
275 0
276 1
276 1
277 3
277 3
278 5
278 5
279 $ log 'desc(B)'
279 $ log 'desc(B)'
280 5
280 5
281 $ log 'descendants(2 or 3)'
281 $ log 'descendants(2 or 3)'
282 2
282 2
283 3
283 3
284 4
284 4
285 5
285 5
286 6
286 6
287 7
287 7
288 8
288 8
289 9
289 9
290 $ log 'file("b*")'
290 $ log 'file("b*")'
291 1
291 1
292 4
292 4
293 $ log 'follow()'
293 $ log 'follow()'
294 0
294 0
295 1
295 1
296 2
296 2
297 4
297 4
298 8
298 8
299 9
299 9
300 $ log 'grep("issue\d+")'
300 $ log 'grep("issue\d+")'
301 6
301 6
302 $ try 'grep("(")' # invalid regular expression
302 $ try 'grep("(")' # invalid regular expression
303 (func
303 (func
304 ('symbol', 'grep')
304 ('symbol', 'grep')
305 ('string', '('))
305 ('string', '('))
306 hg: parse error: invalid match pattern: unbalanced parenthesis
306 hg: parse error: invalid match pattern: unbalanced parenthesis
307 [255]
307 [255]
308 $ try 'grep("\bissue\d+")'
308 $ try 'grep("\bissue\d+")'
309 (func
309 (func
310 ('symbol', 'grep')
310 ('symbol', 'grep')
311 ('string', '\x08issue\\d+'))
311 ('string', '\x08issue\\d+'))
312 $ try 'grep(r"\bissue\d+")'
312 $ try 'grep(r"\bissue\d+")'
313 (func
313 (func
314 ('symbol', 'grep')
314 ('symbol', 'grep')
315 ('string', '\\bissue\\d+'))
315 ('string', '\\bissue\\d+'))
316 6
316 6
317 $ try 'grep(r"\")'
317 $ try 'grep(r"\")'
318 hg: parse error at 7: unterminated string
318 hg: parse error at 7: unterminated string
319 [255]
319 [255]
320 $ log 'head()'
320 $ log 'head()'
321 0
321 0
322 1
322 1
323 2
323 2
324 3
324 3
325 4
325 4
326 5
326 5
327 6
327 6
328 7
328 7
329 9
329 9
330 $ log 'heads(6::)'
330 $ log 'heads(6::)'
331 7
331 7
332 $ log 'keyword(issue)'
332 $ log 'keyword(issue)'
333 6
333 6
334 $ log 'limit(head(), 1)'
334 $ log 'limit(head(), 1)'
335 0
335 0
336 $ log 'matching(6)'
336 $ log 'matching(6)'
337 6
337 6
338 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
338 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
339 6
339 6
340 7
340 7
341 $ log 'max(contains(a))'
341 $ log 'max(contains(a))'
342 5
342 5
343 $ log 'min(contains(a))'
343 $ log 'min(contains(a))'
344 0
344 0
345 $ log 'merge()'
345 $ log 'merge()'
346 6
346 6
347 $ log 'branchpoint()'
347 $ log 'branchpoint()'
348 1
348 1
349 4
349 4
350 $ log 'modifies(b)'
350 $ log 'modifies(b)'
351 4
351 4
352 $ log 'modifies("path:b")'
352 $ log 'modifies("path:b")'
353 4
353 4
354 $ log 'modifies("*")'
354 $ log 'modifies("*")'
355 4
355 4
356 6
356 6
357 $ log 'modifies("set:modified()")'
357 $ log 'modifies("set:modified()")'
358 4
358 4
359 $ log 'id(5)'
359 $ log 'id(5)'
360 2
360 2
361 $ log 'outgoing()'
361 $ log 'outgoing()'
362 8
362 8
363 9
363 9
364 $ log 'outgoing("../remote1")'
364 $ log 'outgoing("../remote1")'
365 8
365 8
366 9
366 9
367 $ log 'outgoing("../remote2")'
367 $ log 'outgoing("../remote2")'
368 3
368 3
369 5
369 5
370 6
370 6
371 7
371 7
372 9
372 9
373 $ log 'p1(merge())'
373 $ log 'p1(merge())'
374 5
374 5
375 $ log 'p2(merge())'
375 $ log 'p2(merge())'
376 4
376 4
377 $ log 'parents(merge())'
377 $ log 'parents(merge())'
378 4
378 4
379 5
379 5
380 $ log 'p1(branchpoint())'
380 $ log 'p1(branchpoint())'
381 0
381 0
382 2
382 2
383 $ log 'p2(branchpoint())'
383 $ log 'p2(branchpoint())'
384 $ log 'parents(branchpoint())'
384 $ log 'parents(branchpoint())'
385 0
385 0
386 2
386 2
387 $ log 'removes(a)'
387 $ log 'removes(a)'
388 2
388 2
389 6
389 6
390 $ log 'roots(all())'
390 $ log 'roots(all())'
391 0
391 0
392 $ log 'reverse(2 or 3 or 4 or 5)'
392 $ log 'reverse(2 or 3 or 4 or 5)'
393 5
393 5
394 4
394 4
395 3
395 3
396 2
396 2
397 $ log 'reverse(all())'
397 $ log 'reverse(all())'
398 9
398 9
399 8
399 8
400 7
400 7
401 6
401 6
402 5
402 5
403 4
403 4
404 3
404 3
405 2
405 2
406 1
406 1
407 0
407 0
408 $ log 'rev(5)'
408 $ log 'rev(5)'
409 5
409 5
410 $ log 'sort(limit(reverse(all()), 3))'
410 $ log 'sort(limit(reverse(all()), 3))'
411 7
411 7
412 8
412 8
413 9
413 9
414 $ log 'sort(2 or 3 or 4 or 5, date)'
414 $ log 'sort(2 or 3 or 4 or 5, date)'
415 2
415 2
416 3
416 3
417 5
417 5
418 4
418 4
419 $ log 'tagged()'
419 $ log 'tagged()'
420 6
420 6
421 $ log 'tag()'
421 $ log 'tag()'
422 6
422 6
423 $ log 'tag(1.0)'
423 $ log 'tag(1.0)'
424 6
424 6
425 $ log 'tag(tip)'
425 $ log 'tag(tip)'
426 9
426 9
427
427
428 we can use patterns when searching for tags
428 we can use patterns when searching for tags
429
429
430 $ log 'tag("1..*")'
430 $ log 'tag("1..*")'
431 abort: tag '1..*' does not exist
431 abort: tag '1..*' does not exist
432 [255]
432 [255]
433 $ log 'tag("re:1..*")'
433 $ log 'tag("re:1..*")'
434 6
434 6
435 $ log 'tag("re:[0-9].[0-9]")'
435 $ log 'tag("re:[0-9].[0-9]")'
436 6
436 6
437 $ log 'tag("literal:1.0")'
437 $ log 'tag("literal:1.0")'
438 6
438 6
439 $ log 'tag("re:0..*")'
439 $ log 'tag("re:0..*")'
440 abort: no tags exist that match '0..*'
441 [255]
442
440
443 $ log 'tag(unknown)'
441 $ log 'tag(unknown)'
444 abort: tag 'unknown' does not exist
442 abort: tag 'unknown' does not exist
445 [255]
443 [255]
446 $ log 'branch(unknown)'
444 $ log 'branch(unknown)'
447 abort: unknown revision 'unknown'!
445 abort: unknown revision 'unknown'!
448 [255]
446 [255]
449 $ log 'user(bob)'
447 $ log 'user(bob)'
450 2
448 2
451
449
452 $ log '4::8'
450 $ log '4::8'
453 4
451 4
454 8
452 8
455 $ log '4:8'
453 $ log '4:8'
456 4
454 4
457 5
455 5
458 6
456 6
459 7
457 7
460 8
458 8
461
459
462 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
460 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
463 4
461 4
464 2
462 2
465 5
463 5
466
464
467 $ log 'not 0 and 0:2'
465 $ log 'not 0 and 0:2'
468 1
466 1
469 2
467 2
470 $ log 'not 1 and 0:2'
468 $ log 'not 1 and 0:2'
471 0
469 0
472 2
470 2
473 $ log 'not 2 and 0:2'
471 $ log 'not 2 and 0:2'
474 0
472 0
475 1
473 1
476 $ log '(1 and 2)::'
474 $ log '(1 and 2)::'
477 $ log '(1 and 2):'
475 $ log '(1 and 2):'
478 $ log '(1 and 2):3'
476 $ log '(1 and 2):3'
479 $ log 'sort(head(), -rev)'
477 $ log 'sort(head(), -rev)'
480 9
478 9
481 7
479 7
482 6
480 6
483 5
481 5
484 4
482 4
485 3
483 3
486 2
484 2
487 1
485 1
488 0
486 0
489 $ log '4::8 - 8'
487 $ log '4::8 - 8'
490 4
488 4
491 $ log 'matching(1 or 2 or 3) and (2 or 3 or 1)'
489 $ log 'matching(1 or 2 or 3) and (2 or 3 or 1)'
492 2
490 2
493 3
491 3
494 1
492 1
495
493
496 issue2437
494 issue2437
497
495
498 $ log '3 and p1(5)'
496 $ log '3 and p1(5)'
499 3
497 3
500 $ log '4 and p2(6)'
498 $ log '4 and p2(6)'
501 4
499 4
502 $ log '1 and parents(:2)'
500 $ log '1 and parents(:2)'
503 1
501 1
504 $ log '2 and children(1:)'
502 $ log '2 and children(1:)'
505 2
503 2
506 $ log 'roots(all()) or roots(all())'
504 $ log 'roots(all()) or roots(all())'
507 0
505 0
508 $ hg debugrevspec 'roots(all()) or roots(all())'
506 $ hg debugrevspec 'roots(all()) or roots(all())'
509 0
507 0
510 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
508 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
511 9
509 9
512 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
510 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
513 4
511 4
514
512
515 issue2654: report a parse error if the revset was not completely parsed
513 issue2654: report a parse error if the revset was not completely parsed
516
514
517 $ log '1 OR 2'
515 $ log '1 OR 2'
518 hg: parse error at 2: invalid token
516 hg: parse error at 2: invalid token
519 [255]
517 [255]
520
518
521 or operator should preserve ordering:
519 or operator should preserve ordering:
522 $ log 'reverse(2::4) or tip'
520 $ log 'reverse(2::4) or tip'
523 4
521 4
524 2
522 2
525 9
523 9
526
524
527 parentrevspec
525 parentrevspec
528
526
529 $ log 'merge()^0'
527 $ log 'merge()^0'
530 6
528 6
531 $ log 'merge()^'
529 $ log 'merge()^'
532 5
530 5
533 $ log 'merge()^1'
531 $ log 'merge()^1'
534 5
532 5
535 $ log 'merge()^2'
533 $ log 'merge()^2'
536 4
534 4
537 $ log 'merge()^^'
535 $ log 'merge()^^'
538 3
536 3
539 $ log 'merge()^1^'
537 $ log 'merge()^1^'
540 3
538 3
541 $ log 'merge()^^^'
539 $ log 'merge()^^^'
542 1
540 1
543
541
544 $ log 'merge()~0'
542 $ log 'merge()~0'
545 6
543 6
546 $ log 'merge()~1'
544 $ log 'merge()~1'
547 5
545 5
548 $ log 'merge()~2'
546 $ log 'merge()~2'
549 3
547 3
550 $ log 'merge()~2^1'
548 $ log 'merge()~2^1'
551 1
549 1
552 $ log 'merge()~3'
550 $ log 'merge()~3'
553 1
551 1
554
552
555 $ log '(-3:tip)^'
553 $ log '(-3:tip)^'
556 4
554 4
557 6
555 6
558 8
556 8
559
557
560 $ log 'tip^foo'
558 $ log 'tip^foo'
561 hg: parse error: ^ expects a number 0, 1, or 2
559 hg: parse error: ^ expects a number 0, 1, or 2
562 [255]
560 [255]
563
561
564 aliases:
562 aliases:
565
563
566 $ echo '[revsetalias]' >> .hg/hgrc
564 $ echo '[revsetalias]' >> .hg/hgrc
567 $ echo 'm = merge()' >> .hg/hgrc
565 $ echo 'm = merge()' >> .hg/hgrc
568 $ echo 'sincem = descendants(m)' >> .hg/hgrc
566 $ echo 'sincem = descendants(m)' >> .hg/hgrc
569 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
567 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
570 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
568 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
571 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
569 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
572
570
573 $ try m
571 $ try m
574 ('symbol', 'm')
572 ('symbol', 'm')
575 (func
573 (func
576 ('symbol', 'merge')
574 ('symbol', 'merge')
577 None)
575 None)
578 6
576 6
579
577
580 test alias recursion
578 test alias recursion
581
579
582 $ try sincem
580 $ try sincem
583 ('symbol', 'sincem')
581 ('symbol', 'sincem')
584 (func
582 (func
585 ('symbol', 'descendants')
583 ('symbol', 'descendants')
586 (func
584 (func
587 ('symbol', 'merge')
585 ('symbol', 'merge')
588 None))
586 None))
589 6
587 6
590 7
588 7
591
589
592 test infinite recursion
590 test infinite recursion
593
591
594 $ echo 'recurse1 = recurse2' >> .hg/hgrc
592 $ echo 'recurse1 = recurse2' >> .hg/hgrc
595 $ echo 'recurse2 = recurse1' >> .hg/hgrc
593 $ echo 'recurse2 = recurse1' >> .hg/hgrc
596 $ try recurse1
594 $ try recurse1
597 ('symbol', 'recurse1')
595 ('symbol', 'recurse1')
598 hg: parse error: infinite expansion of revset alias "recurse1" detected
596 hg: parse error: infinite expansion of revset alias "recurse1" detected
599 [255]
597 [255]
600
598
601 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
599 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
602 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
600 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
603 $ try "level2(level1(1, 2), 3)"
601 $ try "level2(level1(1, 2), 3)"
604 (func
602 (func
605 ('symbol', 'level2')
603 ('symbol', 'level2')
606 (list
604 (list
607 (func
605 (func
608 ('symbol', 'level1')
606 ('symbol', 'level1')
609 (list
607 (list
610 ('symbol', '1')
608 ('symbol', '1')
611 ('symbol', '2')))
609 ('symbol', '2')))
612 ('symbol', '3')))
610 ('symbol', '3')))
613 (or
611 (or
614 ('symbol', '3')
612 ('symbol', '3')
615 (or
613 (or
616 ('symbol', '1')
614 ('symbol', '1')
617 ('symbol', '2')))
615 ('symbol', '2')))
618 3
616 3
619 1
617 1
620 2
618 2
621
619
622 test nesting and variable passing
620 test nesting and variable passing
623
621
624 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
622 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
625 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
623 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
626 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
624 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
627 $ try 'nested(2:5)'
625 $ try 'nested(2:5)'
628 (func
626 (func
629 ('symbol', 'nested')
627 ('symbol', 'nested')
630 (range
628 (range
631 ('symbol', '2')
629 ('symbol', '2')
632 ('symbol', '5')))
630 ('symbol', '5')))
633 (func
631 (func
634 ('symbol', 'max')
632 ('symbol', 'max')
635 (range
633 (range
636 ('symbol', '2')
634 ('symbol', '2')
637 ('symbol', '5')))
635 ('symbol', '5')))
638 5
636 5
639
637
640 test variable isolation, variable placeholders are rewritten as string
638 test variable isolation, variable placeholders are rewritten as string
641 then parsed and matched again as string. Check they do not leak too
639 then parsed and matched again as string. Check they do not leak too
642 far away.
640 far away.
643
641
644 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
642 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
645 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
643 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
646 $ try 'callinjection(2:5)'
644 $ try 'callinjection(2:5)'
647 (func
645 (func
648 ('symbol', 'callinjection')
646 ('symbol', 'callinjection')
649 (range
647 (range
650 ('symbol', '2')
648 ('symbol', '2')
651 ('symbol', '5')))
649 ('symbol', '5')))
652 (func
650 (func
653 ('symbol', 'descendants')
651 ('symbol', 'descendants')
654 (func
652 (func
655 ('symbol', 'max')
653 ('symbol', 'max')
656 ('string', '$1')))
654 ('string', '$1')))
657 abort: unknown revision '$1'!
655 abort: unknown revision '$1'!
658 [255]
656 [255]
659
657
660 $ echo 'injectparamasstring2 = max(_aliasarg("$1"))' >> .hg/hgrc
658 $ echo 'injectparamasstring2 = max(_aliasarg("$1"))' >> .hg/hgrc
661 $ echo 'callinjection2($1) = descendants(injectparamasstring2)' >> .hg/hgrc
659 $ echo 'callinjection2($1) = descendants(injectparamasstring2)' >> .hg/hgrc
662 $ try 'callinjection2(2:5)'
660 $ try 'callinjection2(2:5)'
663 (func
661 (func
664 ('symbol', 'callinjection2')
662 ('symbol', 'callinjection2')
665 (range
663 (range
666 ('symbol', '2')
664 ('symbol', '2')
667 ('symbol', '5')))
665 ('symbol', '5')))
668 hg: parse error: not a function: _aliasarg
666 hg: parse error: not a function: _aliasarg
669 [255]
667 [255]
670 >>> data = file('.hg/hgrc', 'rb').read()
668 >>> data = file('.hg/hgrc', 'rb').read()
671 >>> file('.hg/hgrc', 'wb').write(data.replace('_aliasarg', ''))
669 >>> file('.hg/hgrc', 'wb').write(data.replace('_aliasarg', ''))
672
670
673 $ try 'd(2:5)'
671 $ try 'd(2:5)'
674 (func
672 (func
675 ('symbol', 'd')
673 ('symbol', 'd')
676 (range
674 (range
677 ('symbol', '2')
675 ('symbol', '2')
678 ('symbol', '5')))
676 ('symbol', '5')))
679 (func
677 (func
680 ('symbol', 'reverse')
678 ('symbol', 'reverse')
681 (func
679 (func
682 ('symbol', 'sort')
680 ('symbol', 'sort')
683 (list
681 (list
684 (range
682 (range
685 ('symbol', '2')
683 ('symbol', '2')
686 ('symbol', '5'))
684 ('symbol', '5'))
687 ('symbol', 'date'))))
685 ('symbol', 'date'))))
688 4
686 4
689 5
687 5
690 3
688 3
691 2
689 2
692 $ try 'rs(2 or 3, date)'
690 $ try 'rs(2 or 3, date)'
693 (func
691 (func
694 ('symbol', 'rs')
692 ('symbol', 'rs')
695 (list
693 (list
696 (or
694 (or
697 ('symbol', '2')
695 ('symbol', '2')
698 ('symbol', '3'))
696 ('symbol', '3'))
699 ('symbol', 'date')))
697 ('symbol', 'date')))
700 (func
698 (func
701 ('symbol', 'reverse')
699 ('symbol', 'reverse')
702 (func
700 (func
703 ('symbol', 'sort')
701 ('symbol', 'sort')
704 (list
702 (list
705 (or
703 (or
706 ('symbol', '2')
704 ('symbol', '2')
707 ('symbol', '3'))
705 ('symbol', '3'))
708 ('symbol', 'date'))))
706 ('symbol', 'date'))))
709 3
707 3
710 2
708 2
711 $ try 'rs()'
709 $ try 'rs()'
712 (func
710 (func
713 ('symbol', 'rs')
711 ('symbol', 'rs')
714 None)
712 None)
715 hg: parse error: invalid number of arguments: 0
713 hg: parse error: invalid number of arguments: 0
716 [255]
714 [255]
717 $ try 'rs(2)'
715 $ try 'rs(2)'
718 (func
716 (func
719 ('symbol', 'rs')
717 ('symbol', 'rs')
720 ('symbol', '2'))
718 ('symbol', '2'))
721 hg: parse error: invalid number of arguments: 1
719 hg: parse error: invalid number of arguments: 1
722 [255]
720 [255]
723 $ try 'rs(2, data, 7)'
721 $ try 'rs(2, data, 7)'
724 (func
722 (func
725 ('symbol', 'rs')
723 ('symbol', 'rs')
726 (list
724 (list
727 (list
725 (list
728 ('symbol', '2')
726 ('symbol', '2')
729 ('symbol', 'data'))
727 ('symbol', 'data'))
730 ('symbol', '7')))
728 ('symbol', '7')))
731 hg: parse error: invalid number of arguments: 3
729 hg: parse error: invalid number of arguments: 3
732 [255]
730 [255]
733 $ try 'rs4(2 or 3, x, x, date)'
731 $ try 'rs4(2 or 3, x, x, date)'
734 (func
732 (func
735 ('symbol', 'rs4')
733 ('symbol', 'rs4')
736 (list
734 (list
737 (list
735 (list
738 (list
736 (list
739 (or
737 (or
740 ('symbol', '2')
738 ('symbol', '2')
741 ('symbol', '3'))
739 ('symbol', '3'))
742 ('symbol', 'x'))
740 ('symbol', 'x'))
743 ('symbol', 'x'))
741 ('symbol', 'x'))
744 ('symbol', 'date')))
742 ('symbol', 'date')))
745 (func
743 (func
746 ('symbol', 'reverse')
744 ('symbol', 'reverse')
747 (func
745 (func
748 ('symbol', 'sort')
746 ('symbol', 'sort')
749 (list
747 (list
750 (or
748 (or
751 ('symbol', '2')
749 ('symbol', '2')
752 ('symbol', '3'))
750 ('symbol', '3'))
753 ('symbol', 'date'))))
751 ('symbol', 'date'))))
754 3
752 3
755 2
753 2
756
754
757 issue2549 - correct optimizations
755 issue2549 - correct optimizations
758
756
759 $ log 'limit(1 or 2 or 3, 2) and not 2'
757 $ log 'limit(1 or 2 or 3, 2) and not 2'
760 1
758 1
761 $ log 'max(1 or 2) and not 2'
759 $ log 'max(1 or 2) and not 2'
762 $ log 'min(1 or 2) and not 1'
760 $ log 'min(1 or 2) and not 1'
763 $ log 'last(1 or 2, 1) and not 2'
761 $ log 'last(1 or 2, 1) and not 2'
764
762
765 test revsets started with 40-chars hash (issue3669)
763 test revsets started with 40-chars hash (issue3669)
766
764
767 $ ISSUE3669_TIP=`hg tip --template '{node}'`
765 $ ISSUE3669_TIP=`hg tip --template '{node}'`
768 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
766 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
769 9
767 9
770 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
768 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
771 8
769 8
772
770
773 test or-ed indirect predicates (issue3775)
771 test or-ed indirect predicates (issue3775)
774
772
775 $ log '6 or 6^1' | sort
773 $ log '6 or 6^1' | sort
776 5
774 5
777 6
775 6
778 $ log '6^1 or 6' | sort
776 $ log '6^1 or 6' | sort
779 5
777 5
780 6
778 6
781 $ log '4 or 4~1' | sort
779 $ log '4 or 4~1' | sort
782 2
780 2
783 4
781 4
784 $ log '4~1 or 4' | sort
782 $ log '4~1 or 4' | sort
785 2
783 2
786 4
784 4
787 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
785 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
788 0
786 0
789 1
787 1
790 2
788 2
791 3
789 3
792 4
790 4
793 5
791 5
794 6
792 6
795 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
793 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
796 0
794 0
797 1
795 1
798 2
796 2
799 3
797 3
800 4
798 4
801 5
799 5
802 6
800 6
803
801
804 tests for 'remote()' predicate:
802 tests for 'remote()' predicate:
805 #. (csets in remote) (id) (remote)
803 #. (csets in remote) (id) (remote)
806 1. less than local current branch "default"
804 1. less than local current branch "default"
807 2. same with local specified "default"
805 2. same with local specified "default"
808 3. more than local specified specified
806 3. more than local specified specified
809
807
810 $ hg clone --quiet -U . ../remote3
808 $ hg clone --quiet -U . ../remote3
811 $ cd ../remote3
809 $ cd ../remote3
812 $ hg update -q 7
810 $ hg update -q 7
813 $ echo r > r
811 $ echo r > r
814 $ hg ci -Aqm 10
812 $ hg ci -Aqm 10
815 $ log 'remote()'
813 $ log 'remote()'
816 7
814 7
817 $ log 'remote("a-b-c-")'
815 $ log 'remote("a-b-c-")'
818 2
816 2
819 $ cd ../repo
817 $ cd ../repo
820 $ log 'remote(".a.b.c.", "../remote3")'
818 $ log 'remote(".a.b.c.", "../remote3")'
821
819
822 $ cd ..
820 $ cd ..
823
821
824 test author/desc/keyword in problematic encoding
822 test author/desc/keyword in problematic encoding
825 # unicode: cp932:
823 # unicode: cp932:
826 # u30A2 0x83 0x41(= 'A')
824 # u30A2 0x83 0x41(= 'A')
827 # u30C2 0x83 0x61(= 'a')
825 # u30C2 0x83 0x61(= 'a')
828
826
829 $ hg init problematicencoding
827 $ hg init problematicencoding
830 $ cd problematicencoding
828 $ cd problematicencoding
831
829
832 $ python > setup.sh <<EOF
830 $ python > setup.sh <<EOF
833 > print u'''
831 > print u'''
834 > echo a > text
832 > echo a > text
835 > hg add text
833 > hg add text
836 > hg --encoding utf-8 commit -u '\u30A2' -m none
834 > hg --encoding utf-8 commit -u '\u30A2' -m none
837 > echo b > text
835 > echo b > text
838 > hg --encoding utf-8 commit -u '\u30C2' -m none
836 > hg --encoding utf-8 commit -u '\u30C2' -m none
839 > echo c > text
837 > echo c > text
840 > hg --encoding utf-8 commit -u none -m '\u30A2'
838 > hg --encoding utf-8 commit -u none -m '\u30A2'
841 > echo d > text
839 > echo d > text
842 > hg --encoding utf-8 commit -u none -m '\u30C2'
840 > hg --encoding utf-8 commit -u none -m '\u30C2'
843 > '''.encode('utf-8')
841 > '''.encode('utf-8')
844 > EOF
842 > EOF
845 $ sh < setup.sh
843 $ sh < setup.sh
846
844
847 test in problematic encoding
845 test in problematic encoding
848 $ python > test.sh <<EOF
846 $ python > test.sh <<EOF
849 > print u'''
847 > print u'''
850 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
848 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
851 > echo ====
849 > echo ====
852 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
850 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
853 > echo ====
851 > echo ====
854 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
852 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
855 > echo ====
853 > echo ====
856 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
854 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
857 > echo ====
855 > echo ====
858 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
856 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
859 > echo ====
857 > echo ====
860 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
858 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
861 > '''.encode('cp932')
859 > '''.encode('cp932')
862 > EOF
860 > EOF
863 $ sh < test.sh
861 $ sh < test.sh
864 0
862 0
865 ====
863 ====
866 1
864 1
867 ====
865 ====
868 2
866 2
869 ====
867 ====
870 3
868 3
871 ====
869 ====
872 0
870 0
873 2
871 2
874 ====
872 ====
875 1
873 1
876 3
874 3
877
875
878 $ cd ..
876 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now