##// END OF EJS Templates
commit: add option to amend the working dir parent...
Idan Kamara -
r16458:55982f62 default
parent child Browse files
Show More
@@ -0,0 +1,292 b''
1 $ hg init
2
3 Setup:
4
5 $ echo a >> a
6 $ hg ci -Am 'base'
7 adding a
8
9 Refuse to amend public csets:
10
11 $ hg phase -r . -p
12 $ hg ci --amend
13 abort: cannot amend public changesets
14 [255]
15 $ hg phase -r . -f -d
16
17 $ echo a >> a
18 $ hg ci -Am 'base1'
19
20 Nothing to amend:
21
22 $ hg ci --amend
23 nothing changed
24 [1]
25
26 Amending changeset with changes in working dir:
27
28 $ echo a >> a
29 $ hg ci --amend -m 'amend base1'
30 saved backup bundle to $TESTTMP/.hg/strip-backup/489edb5b847d-amend-backup.hg
31 $ hg diff -c .
32 diff -r ad120869acf0 -r 9cd25b479c51 a
33 --- a/a Thu Jan 01 00:00:00 1970 +0000
34 +++ b/a Thu Jan 01 00:00:00 1970 +0000
35 @@ -1,1 +1,3 @@
36 a
37 +a
38 +a
39 $ hg log
40 changeset: 1:9cd25b479c51
41 tag: tip
42 user: test
43 date: Thu Jan 01 00:00:00 1970 +0000
44 summary: amend base1
45
46 changeset: 0:ad120869acf0
47 user: test
48 date: Thu Jan 01 00:00:00 1970 +0000
49 summary: base
50
51
52 Add new file:
53
54 $ echo b > b
55 $ hg ci --amend -Am 'amend base1 new file'
56 adding b
57 saved backup bundle to $TESTTMP/.hg/strip-backup/9cd25b479c51-amend-backup.hg
58
59 Remove file that was added in amended commit:
60
61 $ hg rm b
62 $ hg ci --amend -m 'amend base1 remove new file'
63 saved backup bundle to $TESTTMP/.hg/strip-backup/e2bb3ecffd2f-amend-backup.hg
64
65 $ hg cat b
66 b: no such file in rev 664a9b2d60cd
67 [1]
68
69 No changes, just a different message:
70
71 $ hg ci -v --amend -m 'no changes, new message'
72 amending changeset 664a9b2d60cd
73 copying changeset 664a9b2d60cd to ad120869acf0
74 a
75 stripping amended changeset 664a9b2d60cd
76 1 changesets found
77 saved backup bundle to $TESTTMP/.hg/strip-backup/664a9b2d60cd-amend-backup.hg
78 1 changesets found
79 adding branch
80 adding changesets
81 adding manifests
82 adding file changes
83 added 1 changesets with 1 changes to 1 files
84 committed changeset 1:ea6e356ff2ad
85 $ hg diff -c .
86 diff -r ad120869acf0 -r ea6e356ff2ad a
87 --- a/a Thu Jan 01 00:00:00 1970 +0000
88 +++ b/a Thu Jan 01 00:00:00 1970 +0000
89 @@ -1,1 +1,3 @@
90 a
91 +a
92 +a
93 $ hg log
94 changeset: 1:ea6e356ff2ad
95 tag: tip
96 user: test
97 date: Thu Jan 01 00:00:00 1970 +0000
98 summary: no changes, new message
99
100 changeset: 0:ad120869acf0
101 user: test
102 date: Thu Jan 01 00:00:00 1970 +0000
103 summary: base
104
105
106 Disable default date on commit so when -d isn't given, the old date is preserved:
107
108 $ echo '[defaults]' >> $HGRCPATH
109 $ echo 'commit=' >> $HGRCPATH
110
111 Test -u/-d:
112
113 $ hg ci --amend -u foo -d '1 0'
114 saved backup bundle to $TESTTMP/.hg/strip-backup/ea6e356ff2ad-amend-backup.hg
115 $ echo a >> a
116 $ hg ci --amend -u foo -d '1 0'
117 saved backup bundle to $TESTTMP/.hg/strip-backup/377b91ce8b56-amend-backup.hg
118 $ hg log -r .
119 changeset: 1:2c94e4a5756f
120 tag: tip
121 user: foo
122 date: Thu Jan 01 00:00:01 1970 +0000
123 summary: no changes, new message
124
125
126 Open editor with old commit message if a message isn't given otherwise:
127
128 $ cat > editor << '__EOF__'
129 > #!/bin/sh
130 > cat $1
131 > echo "another precious commit message" > "$1"
132 > __EOF__
133 $ chmod +x editor
134 $ HGEDITOR="'`pwd`'"/editor hg commit --amend -v
135 amending changeset 2c94e4a5756f
136 copying changeset 2c94e4a5756f to ad120869acf0
137 no changes, new message
138
139
140 HG: Enter commit message. Lines beginning with 'HG:' are removed.
141 HG: Leave message empty to abort commit.
142 HG: --
143 HG: user: foo
144 HG: branch 'default'
145 HG: changed a
146 a
147 stripping amended changeset 2c94e4a5756f
148 1 changesets found
149 saved backup bundle to $TESTTMP/.hg/strip-backup/2c94e4a5756f-amend-backup.hg
150 1 changesets found
151 adding branch
152 adding changesets
153 adding manifests
154 adding file changes
155 added 1 changesets with 1 changes to 1 files
156 committed changeset 1:ffb49186f961
157
158 Same, but with changes in working dir (different code path):
159
160 $ echo a >> a
161 $ HGEDITOR="'`pwd`'"/editor hg commit --amend -v
162 amending changeset ffb49186f961
163 another precious commit message
164
165
166 HG: Enter commit message. Lines beginning with 'HG:' are removed.
167 HG: Leave message empty to abort commit.
168 HG: --
169 HG: user: foo
170 HG: branch 'default'
171 HG: changed a
172 a
173 copying changeset 27f3aacd3011 to ad120869acf0
174 a
175 stripping intermediate changeset 27f3aacd3011
176 stripping amended changeset ffb49186f961
177 2 changesets found
178 saved backup bundle to $TESTTMP/.hg/strip-backup/ffb49186f961-amend-backup.hg
179 1 changesets found
180 adding branch
181 adding changesets
182 adding manifests
183 adding file changes
184 added 1 changesets with 1 changes to 1 files
185 committed changeset 1:fb6cca43446f
186
187 $ rm editor
188 $ hg log -r .
189 changeset: 1:fb6cca43446f
190 tag: tip
191 user: foo
192 date: Thu Jan 01 00:00:01 1970 +0000
193 summary: another precious commit message
194
195
196 Moving bookmarks, preserve active bookmark:
197
198 $ hg book book1
199 $ hg book book2
200 $ hg ci --amend -m 'move bookmarks'
201 saved backup bundle to $TESTTMP/.hg/strip-backup/fb6cca43446f-amend-backup.hg
202 $ hg book
203 book1 1:0cf1c7a51bcf
204 * book2 1:0cf1c7a51bcf
205 $ echo a >> a
206 $ hg ci --amend -m 'move bookmarks'
207 saved backup bundle to $TESTTMP/.hg/strip-backup/0cf1c7a51bcf-amend-backup.hg
208 $ hg book
209 book1 1:7344472bd951
210 * book2 1:7344472bd951
211
212 $ echo '[defaults]' >> $HGRCPATH
213 $ echo "commit=-d '0 0'" >> $HGRCPATH
214
215 Moving branches:
216
217 $ hg branch foo
218 marked working directory as branch foo
219 (branches are permanent and global, did you want a bookmark?)
220 $ echo a >> a
221 $ hg ci -m 'branch foo'
222 $ hg branch default -f
223 marked working directory as branch default
224 (branches are permanent and global, did you want a bookmark?)
225 $ hg ci --amend -m 'back to default'
226 saved backup bundle to $TESTTMP/.hg/strip-backup/1661ca36a2db-amend-backup.hg
227 $ hg branches
228 default 2:f24ee5961967
229
230 Close branch:
231
232 $ hg up -q 0
233 $ echo b >> b
234 $ hg branch foo
235 marked working directory as branch foo
236 (branches are permanent and global, did you want a bookmark?)
237 $ hg ci -Am 'fork'
238 adding b
239 $ echo b >> b
240 $ hg ci -mb
241 $ hg ci --amend --close-branch -m 'closing branch foo'
242 saved backup bundle to $TESTTMP/.hg/strip-backup/c962248fa264-amend-backup.hg
243
244 Same thing, different code path:
245
246 $ echo b >> b
247 $ hg ci -m 'reopen branch'
248 reopening closed branch head 4
249 $ echo b >> b
250 $ hg ci --amend --close-branch
251 saved backup bundle to $TESTTMP/.hg/strip-backup/5e302dcc12b8-amend-backup.hg
252 $ hg branches
253 default 2:f24ee5961967
254
255 Refuse to amend merges:
256
257 $ hg up -q default
258 $ hg merge foo
259 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
260 (branch merge, don't forget to commit)
261 $ hg ci --amend
262 abort: cannot amend while merging
263 [255]
264 $ hg ci -m 'merge'
265 $ hg ci --amend
266 abort: cannot amend merge changesets
267 [255]
268
269 Follow copies/renames:
270
271 $ hg mv b c
272 $ hg ci -m 'b -> c'
273 $ hg mv c d
274 $ hg ci --amend -m 'b -> d'
275 saved backup bundle to $TESTTMP/.hg/strip-backup/9c207120aa98-amend-backup.hg
276 $ hg st --rev .^ --copies d
277 A d
278 b
279 $ hg cp d e
280 $ hg ci -m 'e = d'
281 $ hg cp e f
282 $ hg ci --amend -m 'f = d'
283 saved backup bundle to $TESTTMP/.hg/strip-backup/fda2b3b27b22-amend-backup.hg
284 $ hg st --rev .^ --copies f
285 A f
286 d
287
288 Can't rollback an amend:
289
290 $ hg rollback
291 no rollback information available
292 [1]
@@ -1,1522 +1,1639 b''
1 1 # cmdutil.py - help for command processing in mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import hex, nullid, nullrev, short
9 9 from i18n import _
10 10 import os, sys, errno, re, tempfile
11 11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
12 12 import match as matchmod
13 import subrepo
13 import subrepo, context, repair, bookmarks
14 14
15 15 def parsealiases(cmd):
16 16 return cmd.lstrip("^").split("|")
17 17
18 18 def findpossible(cmd, table, strict=False):
19 19 """
20 20 Return cmd -> (aliases, command table entry)
21 21 for each matching command.
22 22 Return debug commands (or their aliases) only if no normal command matches.
23 23 """
24 24 choice = {}
25 25 debugchoice = {}
26 26
27 27 if cmd in table:
28 28 # short-circuit exact matches, "log" alias beats "^log|history"
29 29 keys = [cmd]
30 30 else:
31 31 keys = table.keys()
32 32
33 33 for e in keys:
34 34 aliases = parsealiases(e)
35 35 found = None
36 36 if cmd in aliases:
37 37 found = cmd
38 38 elif not strict:
39 39 for a in aliases:
40 40 if a.startswith(cmd):
41 41 found = a
42 42 break
43 43 if found is not None:
44 44 if aliases[0].startswith("debug") or found.startswith("debug"):
45 45 debugchoice[found] = (aliases, table[e])
46 46 else:
47 47 choice[found] = (aliases, table[e])
48 48
49 49 if not choice and debugchoice:
50 50 choice = debugchoice
51 51
52 52 return choice
53 53
54 54 def findcmd(cmd, table, strict=True):
55 55 """Return (aliases, command table entry) for command string."""
56 56 choice = findpossible(cmd, table, strict)
57 57
58 58 if cmd in choice:
59 59 return choice[cmd]
60 60
61 61 if len(choice) > 1:
62 62 clist = choice.keys()
63 63 clist.sort()
64 64 raise error.AmbiguousCommand(cmd, clist)
65 65
66 66 if choice:
67 67 return choice.values()[0]
68 68
69 69 raise error.UnknownCommand(cmd)
70 70
71 71 def findrepo(p):
72 72 while not os.path.isdir(os.path.join(p, ".hg")):
73 73 oldp, p = p, os.path.dirname(p)
74 74 if p == oldp:
75 75 return None
76 76
77 77 return p
78 78
79 79 def bailifchanged(repo):
80 80 if repo.dirstate.p2() != nullid:
81 81 raise util.Abort(_('outstanding uncommitted merge'))
82 82 modified, added, removed, deleted = repo.status()[:4]
83 83 if modified or added or removed or deleted:
84 84 raise util.Abort(_("outstanding uncommitted changes"))
85 85 ctx = repo[None]
86 86 for s in ctx.substate:
87 87 if ctx.sub(s).dirty():
88 88 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
89 89
90 90 def logmessage(ui, opts):
91 91 """ get the log message according to -m and -l option """
92 92 message = opts.get('message')
93 93 logfile = opts.get('logfile')
94 94
95 95 if message and logfile:
96 96 raise util.Abort(_('options --message and --logfile are mutually '
97 97 'exclusive'))
98 98 if not message and logfile:
99 99 try:
100 100 if logfile == '-':
101 101 message = ui.fin.read()
102 102 else:
103 103 message = '\n'.join(util.readfile(logfile).splitlines())
104 104 except IOError, inst:
105 105 raise util.Abort(_("can't read commit message '%s': %s") %
106 106 (logfile, inst.strerror))
107 107 return message
108 108
109 109 def loglimit(opts):
110 110 """get the log limit according to option -l/--limit"""
111 111 limit = opts.get('limit')
112 112 if limit:
113 113 try:
114 114 limit = int(limit)
115 115 except ValueError:
116 116 raise util.Abort(_('limit must be a positive integer'))
117 117 if limit <= 0:
118 118 raise util.Abort(_('limit must be positive'))
119 119 else:
120 120 limit = None
121 121 return limit
122 122
123 123 def makefilename(repo, pat, node, desc=None,
124 124 total=None, seqno=None, revwidth=None, pathname=None):
125 125 node_expander = {
126 126 'H': lambda: hex(node),
127 127 'R': lambda: str(repo.changelog.rev(node)),
128 128 'h': lambda: short(node),
129 129 'm': lambda: re.sub('[^\w]', '_', str(desc))
130 130 }
131 131 expander = {
132 132 '%': lambda: '%',
133 133 'b': lambda: os.path.basename(repo.root),
134 134 }
135 135
136 136 try:
137 137 if node:
138 138 expander.update(node_expander)
139 139 if node:
140 140 expander['r'] = (lambda:
141 141 str(repo.changelog.rev(node)).zfill(revwidth or 0))
142 142 if total is not None:
143 143 expander['N'] = lambda: str(total)
144 144 if seqno is not None:
145 145 expander['n'] = lambda: str(seqno)
146 146 if total is not None and seqno is not None:
147 147 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
148 148 if pathname is not None:
149 149 expander['s'] = lambda: os.path.basename(pathname)
150 150 expander['d'] = lambda: os.path.dirname(pathname) or '.'
151 151 expander['p'] = lambda: pathname
152 152
153 153 newname = []
154 154 patlen = len(pat)
155 155 i = 0
156 156 while i < patlen:
157 157 c = pat[i]
158 158 if c == '%':
159 159 i += 1
160 160 c = pat[i]
161 161 c = expander[c]()
162 162 newname.append(c)
163 163 i += 1
164 164 return ''.join(newname)
165 165 except KeyError, inst:
166 166 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
167 167 inst.args[0])
168 168
169 169 def makefileobj(repo, pat, node=None, desc=None, total=None,
170 170 seqno=None, revwidth=None, mode='wb', pathname=None):
171 171
172 172 writable = mode not in ('r', 'rb')
173 173
174 174 if not pat or pat == '-':
175 175 fp = writable and repo.ui.fout or repo.ui.fin
176 176 if util.safehasattr(fp, 'fileno'):
177 177 return os.fdopen(os.dup(fp.fileno()), mode)
178 178 else:
179 179 # if this fp can't be duped properly, return
180 180 # a dummy object that can be closed
181 181 class wrappedfileobj(object):
182 182 noop = lambda x: None
183 183 def __init__(self, f):
184 184 self.f = f
185 185 def __getattr__(self, attr):
186 186 if attr == 'close':
187 187 return self.noop
188 188 else:
189 189 return getattr(self.f, attr)
190 190
191 191 return wrappedfileobj(fp)
192 192 if util.safehasattr(pat, 'write') and writable:
193 193 return pat
194 194 if util.safehasattr(pat, 'read') and 'r' in mode:
195 195 return pat
196 196 return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
197 197 pathname),
198 198 mode)
199 199
200 200 def openrevlog(repo, cmd, file_, opts):
201 201 """opens the changelog, manifest, a filelog or a given revlog"""
202 202 cl = opts['changelog']
203 203 mf = opts['manifest']
204 204 msg = None
205 205 if cl and mf:
206 206 msg = _('cannot specify --changelog and --manifest at the same time')
207 207 elif cl or mf:
208 208 if file_:
209 209 msg = _('cannot specify filename with --changelog or --manifest')
210 210 elif not repo:
211 211 msg = _('cannot specify --changelog or --manifest '
212 212 'without a repository')
213 213 if msg:
214 214 raise util.Abort(msg)
215 215
216 216 r = None
217 217 if repo:
218 218 if cl:
219 219 r = repo.changelog
220 220 elif mf:
221 221 r = repo.manifest
222 222 elif file_:
223 223 filelog = repo.file(file_)
224 224 if len(filelog):
225 225 r = filelog
226 226 if not r:
227 227 if not file_:
228 228 raise error.CommandError(cmd, _('invalid arguments'))
229 229 if not os.path.isfile(file_):
230 230 raise util.Abort(_("revlog '%s' not found") % file_)
231 231 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
232 232 file_[:-2] + ".i")
233 233 return r
234 234
235 235 def copy(ui, repo, pats, opts, rename=False):
236 236 # called with the repo lock held
237 237 #
238 238 # hgsep => pathname that uses "/" to separate directories
239 239 # ossep => pathname that uses os.sep to separate directories
240 240 cwd = repo.getcwd()
241 241 targets = {}
242 242 after = opts.get("after")
243 243 dryrun = opts.get("dry_run")
244 244 wctx = repo[None]
245 245
246 246 def walkpat(pat):
247 247 srcs = []
248 248 badstates = after and '?' or '?r'
249 249 m = scmutil.match(repo[None], [pat], opts, globbed=True)
250 250 for abs in repo.walk(m):
251 251 state = repo.dirstate[abs]
252 252 rel = m.rel(abs)
253 253 exact = m.exact(abs)
254 254 if state in badstates:
255 255 if exact and state == '?':
256 256 ui.warn(_('%s: not copying - file is not managed\n') % rel)
257 257 if exact and state == 'r':
258 258 ui.warn(_('%s: not copying - file has been marked for'
259 259 ' remove\n') % rel)
260 260 continue
261 261 # abs: hgsep
262 262 # rel: ossep
263 263 srcs.append((abs, rel, exact))
264 264 return srcs
265 265
266 266 # abssrc: hgsep
267 267 # relsrc: ossep
268 268 # otarget: ossep
269 269 def copyfile(abssrc, relsrc, otarget, exact):
270 270 abstarget = scmutil.canonpath(repo.root, cwd, otarget)
271 271 reltarget = repo.pathto(abstarget, cwd)
272 272 target = repo.wjoin(abstarget)
273 273 src = repo.wjoin(abssrc)
274 274 state = repo.dirstate[abstarget]
275 275
276 276 scmutil.checkportable(ui, abstarget)
277 277
278 278 # check for collisions
279 279 prevsrc = targets.get(abstarget)
280 280 if prevsrc is not None:
281 281 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
282 282 (reltarget, repo.pathto(abssrc, cwd),
283 283 repo.pathto(prevsrc, cwd)))
284 284 return
285 285
286 286 # check for overwrites
287 287 exists = os.path.lexists(target)
288 288 samefile = False
289 289 if exists and abssrc != abstarget:
290 290 if (repo.dirstate.normalize(abssrc) ==
291 291 repo.dirstate.normalize(abstarget)):
292 292 if not rename:
293 293 ui.warn(_("%s: can't copy - same file\n") % reltarget)
294 294 return
295 295 exists = False
296 296 samefile = True
297 297
298 298 if not after and exists or after and state in 'mn':
299 299 if not opts['force']:
300 300 ui.warn(_('%s: not overwriting - file exists\n') %
301 301 reltarget)
302 302 return
303 303
304 304 if after:
305 305 if not exists:
306 306 if rename:
307 307 ui.warn(_('%s: not recording move - %s does not exist\n') %
308 308 (relsrc, reltarget))
309 309 else:
310 310 ui.warn(_('%s: not recording copy - %s does not exist\n') %
311 311 (relsrc, reltarget))
312 312 return
313 313 elif not dryrun:
314 314 try:
315 315 if exists:
316 316 os.unlink(target)
317 317 targetdir = os.path.dirname(target) or '.'
318 318 if not os.path.isdir(targetdir):
319 319 os.makedirs(targetdir)
320 320 if samefile:
321 321 tmp = target + "~hgrename"
322 322 os.rename(src, tmp)
323 323 os.rename(tmp, target)
324 324 else:
325 325 util.copyfile(src, target)
326 326 srcexists = True
327 327 except IOError, inst:
328 328 if inst.errno == errno.ENOENT:
329 329 ui.warn(_('%s: deleted in working copy\n') % relsrc)
330 330 srcexists = False
331 331 else:
332 332 ui.warn(_('%s: cannot copy - %s\n') %
333 333 (relsrc, inst.strerror))
334 334 return True # report a failure
335 335
336 336 if ui.verbose or not exact:
337 337 if rename:
338 338 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
339 339 else:
340 340 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
341 341
342 342 targets[abstarget] = abssrc
343 343
344 344 # fix up dirstate
345 345 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
346 346 dryrun=dryrun, cwd=cwd)
347 347 if rename and not dryrun:
348 348 if not after and srcexists and not samefile:
349 349 util.unlinkpath(repo.wjoin(abssrc))
350 350 wctx.forget([abssrc])
351 351
352 352 # pat: ossep
353 353 # dest ossep
354 354 # srcs: list of (hgsep, hgsep, ossep, bool)
355 355 # return: function that takes hgsep and returns ossep
356 356 def targetpathfn(pat, dest, srcs):
357 357 if os.path.isdir(pat):
358 358 abspfx = scmutil.canonpath(repo.root, cwd, pat)
359 359 abspfx = util.localpath(abspfx)
360 360 if destdirexists:
361 361 striplen = len(os.path.split(abspfx)[0])
362 362 else:
363 363 striplen = len(abspfx)
364 364 if striplen:
365 365 striplen += len(os.sep)
366 366 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
367 367 elif destdirexists:
368 368 res = lambda p: os.path.join(dest,
369 369 os.path.basename(util.localpath(p)))
370 370 else:
371 371 res = lambda p: dest
372 372 return res
373 373
374 374 # pat: ossep
375 375 # dest ossep
376 376 # srcs: list of (hgsep, hgsep, ossep, bool)
377 377 # return: function that takes hgsep and returns ossep
378 378 def targetpathafterfn(pat, dest, srcs):
379 379 if matchmod.patkind(pat):
380 380 # a mercurial pattern
381 381 res = lambda p: os.path.join(dest,
382 382 os.path.basename(util.localpath(p)))
383 383 else:
384 384 abspfx = scmutil.canonpath(repo.root, cwd, pat)
385 385 if len(abspfx) < len(srcs[0][0]):
386 386 # A directory. Either the target path contains the last
387 387 # component of the source path or it does not.
388 388 def evalpath(striplen):
389 389 score = 0
390 390 for s in srcs:
391 391 t = os.path.join(dest, util.localpath(s[0])[striplen:])
392 392 if os.path.lexists(t):
393 393 score += 1
394 394 return score
395 395
396 396 abspfx = util.localpath(abspfx)
397 397 striplen = len(abspfx)
398 398 if striplen:
399 399 striplen += len(os.sep)
400 400 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
401 401 score = evalpath(striplen)
402 402 striplen1 = len(os.path.split(abspfx)[0])
403 403 if striplen1:
404 404 striplen1 += len(os.sep)
405 405 if evalpath(striplen1) > score:
406 406 striplen = striplen1
407 407 res = lambda p: os.path.join(dest,
408 408 util.localpath(p)[striplen:])
409 409 else:
410 410 # a file
411 411 if destdirexists:
412 412 res = lambda p: os.path.join(dest,
413 413 os.path.basename(util.localpath(p)))
414 414 else:
415 415 res = lambda p: dest
416 416 return res
417 417
418 418
419 419 pats = scmutil.expandpats(pats)
420 420 if not pats:
421 421 raise util.Abort(_('no source or destination specified'))
422 422 if len(pats) == 1:
423 423 raise util.Abort(_('no destination specified'))
424 424 dest = pats.pop()
425 425 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
426 426 if not destdirexists:
427 427 if len(pats) > 1 or matchmod.patkind(pats[0]):
428 428 raise util.Abort(_('with multiple sources, destination must be an '
429 429 'existing directory'))
430 430 if util.endswithsep(dest):
431 431 raise util.Abort(_('destination %s is not a directory') % dest)
432 432
433 433 tfn = targetpathfn
434 434 if after:
435 435 tfn = targetpathafterfn
436 436 copylist = []
437 437 for pat in pats:
438 438 srcs = walkpat(pat)
439 439 if not srcs:
440 440 continue
441 441 copylist.append((tfn(pat, dest, srcs), srcs))
442 442 if not copylist:
443 443 raise util.Abort(_('no files to copy'))
444 444
445 445 errors = 0
446 446 for targetpath, srcs in copylist:
447 447 for abssrc, relsrc, exact in srcs:
448 448 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
449 449 errors += 1
450 450
451 451 if errors:
452 452 ui.warn(_('(consider using --after)\n'))
453 453
454 454 return errors != 0
455 455
456 456 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
457 457 runargs=None, appendpid=False):
458 458 '''Run a command as a service.'''
459 459
460 460 if opts['daemon'] and not opts['daemon_pipefds']:
461 461 # Signal child process startup with file removal
462 462 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
463 463 os.close(lockfd)
464 464 try:
465 465 if not runargs:
466 466 runargs = util.hgcmd() + sys.argv[1:]
467 467 runargs.append('--daemon-pipefds=%s' % lockpath)
468 468 # Don't pass --cwd to the child process, because we've already
469 469 # changed directory.
470 470 for i in xrange(1, len(runargs)):
471 471 if runargs[i].startswith('--cwd='):
472 472 del runargs[i]
473 473 break
474 474 elif runargs[i].startswith('--cwd'):
475 475 del runargs[i:i + 2]
476 476 break
477 477 def condfn():
478 478 return not os.path.exists(lockpath)
479 479 pid = util.rundetached(runargs, condfn)
480 480 if pid < 0:
481 481 raise util.Abort(_('child process failed to start'))
482 482 finally:
483 483 try:
484 484 os.unlink(lockpath)
485 485 except OSError, e:
486 486 if e.errno != errno.ENOENT:
487 487 raise
488 488 if parentfn:
489 489 return parentfn(pid)
490 490 else:
491 491 return
492 492
493 493 if initfn:
494 494 initfn()
495 495
496 496 if opts['pid_file']:
497 497 mode = appendpid and 'a' or 'w'
498 498 fp = open(opts['pid_file'], mode)
499 499 fp.write(str(os.getpid()) + '\n')
500 500 fp.close()
501 501
502 502 if opts['daemon_pipefds']:
503 503 lockpath = opts['daemon_pipefds']
504 504 try:
505 505 os.setsid()
506 506 except AttributeError:
507 507 pass
508 508 os.unlink(lockpath)
509 509 util.hidewindow()
510 510 sys.stdout.flush()
511 511 sys.stderr.flush()
512 512
513 513 nullfd = os.open(util.nulldev, os.O_RDWR)
514 514 logfilefd = nullfd
515 515 if logfile:
516 516 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
517 517 os.dup2(nullfd, 0)
518 518 os.dup2(logfilefd, 1)
519 519 os.dup2(logfilefd, 2)
520 520 if nullfd not in (0, 1, 2):
521 521 os.close(nullfd)
522 522 if logfile and logfilefd not in (0, 1, 2):
523 523 os.close(logfilefd)
524 524
525 525 if runfn:
526 526 return runfn()
527 527
528 528 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
529 529 opts=None):
530 530 '''export changesets as hg patches.'''
531 531
532 532 total = len(revs)
533 533 revwidth = max([len(str(rev)) for rev in revs])
534 534
535 535 def single(rev, seqno, fp):
536 536 ctx = repo[rev]
537 537 node = ctx.node()
538 538 parents = [p.node() for p in ctx.parents() if p]
539 539 branch = ctx.branch()
540 540 if switch_parent:
541 541 parents.reverse()
542 542 prev = (parents and parents[0]) or nullid
543 543
544 544 shouldclose = False
545 545 if not fp:
546 546 desc_lines = ctx.description().rstrip().split('\n')
547 547 desc = desc_lines[0] #Commit always has a first line.
548 548 fp = makefileobj(repo, template, node, desc=desc, total=total,
549 549 seqno=seqno, revwidth=revwidth, mode='ab')
550 550 if fp != template:
551 551 shouldclose = True
552 552 if fp != sys.stdout and util.safehasattr(fp, 'name'):
553 553 repo.ui.note("%s\n" % fp.name)
554 554
555 555 fp.write("# HG changeset patch\n")
556 556 fp.write("# User %s\n" % ctx.user())
557 557 fp.write("# Date %d %d\n" % ctx.date())
558 558 if branch and branch != 'default':
559 559 fp.write("# Branch %s\n" % branch)
560 560 fp.write("# Node ID %s\n" % hex(node))
561 561 fp.write("# Parent %s\n" % hex(prev))
562 562 if len(parents) > 1:
563 563 fp.write("# Parent %s\n" % hex(parents[1]))
564 564 fp.write(ctx.description().rstrip())
565 565 fp.write("\n\n")
566 566
567 567 for chunk in patch.diff(repo, prev, node, opts=opts):
568 568 fp.write(chunk)
569 569
570 570 if shouldclose:
571 571 fp.close()
572 572
573 573 for seqno, rev in enumerate(revs):
574 574 single(rev, seqno + 1, fp)
575 575
576 576 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
577 577 changes=None, stat=False, fp=None, prefix='',
578 578 listsubrepos=False):
579 579 '''show diff or diffstat.'''
580 580 if fp is None:
581 581 write = ui.write
582 582 else:
583 583 def write(s, **kw):
584 584 fp.write(s)
585 585
586 586 if stat:
587 587 diffopts = diffopts.copy(context=0)
588 588 width = 80
589 589 if not ui.plain():
590 590 width = ui.termwidth()
591 591 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
592 592 prefix=prefix)
593 593 for chunk, label in patch.diffstatui(util.iterlines(chunks),
594 594 width=width,
595 595 git=diffopts.git):
596 596 write(chunk, label=label)
597 597 else:
598 598 for chunk, label in patch.diffui(repo, node1, node2, match,
599 599 changes, diffopts, prefix=prefix):
600 600 write(chunk, label=label)
601 601
602 602 if listsubrepos:
603 603 ctx1 = repo[node1]
604 604 ctx2 = repo[node2]
605 605 for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
606 606 tempnode2 = node2
607 607 try:
608 608 if node2 is not None:
609 609 tempnode2 = ctx2.substate[subpath][1]
610 610 except KeyError:
611 611 # A subrepo that existed in node1 was deleted between node1 and
612 612 # node2 (inclusive). Thus, ctx2's substate won't contain that
613 613 # subpath. The best we can do is to ignore it.
614 614 tempnode2 = None
615 615 submatch = matchmod.narrowmatcher(subpath, match)
616 616 sub.diff(diffopts, tempnode2, submatch, changes=changes,
617 617 stat=stat, fp=fp, prefix=prefix)
618 618
619 619 class changeset_printer(object):
620 620 '''show changeset information when templating not requested.'''
621 621
622 622 def __init__(self, ui, repo, patch, diffopts, buffered):
623 623 self.ui = ui
624 624 self.repo = repo
625 625 self.buffered = buffered
626 626 self.patch = patch
627 627 self.diffopts = diffopts
628 628 self.header = {}
629 629 self.hunk = {}
630 630 self.lastheader = None
631 631 self.footer = None
632 632
633 633 def flush(self, rev):
634 634 if rev in self.header:
635 635 h = self.header[rev]
636 636 if h != self.lastheader:
637 637 self.lastheader = h
638 638 self.ui.write(h)
639 639 del self.header[rev]
640 640 if rev in self.hunk:
641 641 self.ui.write(self.hunk[rev])
642 642 del self.hunk[rev]
643 643 return 1
644 644 return 0
645 645
646 646 def close(self):
647 647 if self.footer:
648 648 self.ui.write(self.footer)
649 649
650 650 def show(self, ctx, copies=None, matchfn=None, **props):
651 651 if self.buffered:
652 652 self.ui.pushbuffer()
653 653 self._show(ctx, copies, matchfn, props)
654 654 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
655 655 else:
656 656 self._show(ctx, copies, matchfn, props)
657 657
658 658 def _show(self, ctx, copies, matchfn, props):
659 659 '''show a single changeset or file revision'''
660 660 changenode = ctx.node()
661 661 rev = ctx.rev()
662 662
663 663 if self.ui.quiet:
664 664 self.ui.write("%d:%s\n" % (rev, short(changenode)),
665 665 label='log.node')
666 666 return
667 667
668 668 log = self.repo.changelog
669 669 date = util.datestr(ctx.date())
670 670
671 671 hexfunc = self.ui.debugflag and hex or short
672 672
673 673 parents = [(p, hexfunc(log.node(p)))
674 674 for p in self._meaningful_parentrevs(log, rev)]
675 675
676 676 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
677 677 label='log.changeset')
678 678
679 679 branch = ctx.branch()
680 680 # don't show the default branch name
681 681 if branch != 'default':
682 682 self.ui.write(_("branch: %s\n") % branch,
683 683 label='log.branch')
684 684 for bookmark in self.repo.nodebookmarks(changenode):
685 685 self.ui.write(_("bookmark: %s\n") % bookmark,
686 686 label='log.bookmark')
687 687 for tag in self.repo.nodetags(changenode):
688 688 self.ui.write(_("tag: %s\n") % tag,
689 689 label='log.tag')
690 690 if self.ui.debugflag and ctx.phase():
691 691 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
692 692 label='log.phase')
693 693 for parent in parents:
694 694 self.ui.write(_("parent: %d:%s\n") % parent,
695 695 label='log.parent')
696 696
697 697 if self.ui.debugflag:
698 698 mnode = ctx.manifestnode()
699 699 self.ui.write(_("manifest: %d:%s\n") %
700 700 (self.repo.manifest.rev(mnode), hex(mnode)),
701 701 label='ui.debug log.manifest')
702 702 self.ui.write(_("user: %s\n") % ctx.user(),
703 703 label='log.user')
704 704 self.ui.write(_("date: %s\n") % date,
705 705 label='log.date')
706 706
707 707 if self.ui.debugflag:
708 708 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
709 709 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
710 710 files):
711 711 if value:
712 712 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
713 713 label='ui.debug log.files')
714 714 elif ctx.files() and self.ui.verbose:
715 715 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
716 716 label='ui.note log.files')
717 717 if copies and self.ui.verbose:
718 718 copies = ['%s (%s)' % c for c in copies]
719 719 self.ui.write(_("copies: %s\n") % ' '.join(copies),
720 720 label='ui.note log.copies')
721 721
722 722 extra = ctx.extra()
723 723 if extra and self.ui.debugflag:
724 724 for key, value in sorted(extra.items()):
725 725 self.ui.write(_("extra: %s=%s\n")
726 726 % (key, value.encode('string_escape')),
727 727 label='ui.debug log.extra')
728 728
729 729 description = ctx.description().strip()
730 730 if description:
731 731 if self.ui.verbose:
732 732 self.ui.write(_("description:\n"),
733 733 label='ui.note log.description')
734 734 self.ui.write(description,
735 735 label='ui.note log.description')
736 736 self.ui.write("\n\n")
737 737 else:
738 738 self.ui.write(_("summary: %s\n") %
739 739 description.splitlines()[0],
740 740 label='log.summary')
741 741 self.ui.write("\n")
742 742
743 743 self.showpatch(changenode, matchfn)
744 744
745 745 def showpatch(self, node, matchfn):
746 746 if not matchfn:
747 747 matchfn = self.patch
748 748 if matchfn:
749 749 stat = self.diffopts.get('stat')
750 750 diff = self.diffopts.get('patch')
751 751 diffopts = patch.diffopts(self.ui, self.diffopts)
752 752 prev = self.repo.changelog.parents(node)[0]
753 753 if stat:
754 754 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
755 755 match=matchfn, stat=True)
756 756 if diff:
757 757 if stat:
758 758 self.ui.write("\n")
759 759 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
760 760 match=matchfn, stat=False)
761 761 self.ui.write("\n")
762 762
763 763 def _meaningful_parentrevs(self, log, rev):
764 764 """Return list of meaningful (or all if debug) parentrevs for rev.
765 765
766 766 For merges (two non-nullrev revisions) both parents are meaningful.
767 767 Otherwise the first parent revision is considered meaningful if it
768 768 is not the preceding revision.
769 769 """
770 770 parents = log.parentrevs(rev)
771 771 if not self.ui.debugflag and parents[1] == nullrev:
772 772 if parents[0] >= rev - 1:
773 773 parents = []
774 774 else:
775 775 parents = [parents[0]]
776 776 return parents
777 777
778 778
779 779 class changeset_templater(changeset_printer):
780 780 '''format changeset information.'''
781 781
782 782 def __init__(self, ui, repo, patch, diffopts, mapfile, buffered):
783 783 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
784 784 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
785 785 defaulttempl = {
786 786 'parent': '{rev}:{node|formatnode} ',
787 787 'manifest': '{rev}:{node|formatnode}',
788 788 'file_copy': '{name} ({source})',
789 789 'extra': '{key}={value|stringescape}'
790 790 }
791 791 # filecopy is preserved for compatibility reasons
792 792 defaulttempl['filecopy'] = defaulttempl['file_copy']
793 793 self.t = templater.templater(mapfile, {'formatnode': formatnode},
794 794 cache=defaulttempl)
795 795 self.cache = {}
796 796
797 797 def use_template(self, t):
798 798 '''set template string to use'''
799 799 self.t.cache['changeset'] = t
800 800
801 801 def _meaningful_parentrevs(self, ctx):
802 802 """Return list of meaningful (or all if debug) parentrevs for rev.
803 803 """
804 804 parents = ctx.parents()
805 805 if len(parents) > 1:
806 806 return parents
807 807 if self.ui.debugflag:
808 808 return [parents[0], self.repo['null']]
809 809 if parents[0].rev() >= ctx.rev() - 1:
810 810 return []
811 811 return parents
812 812
813 813 def _show(self, ctx, copies, matchfn, props):
814 814 '''show a single changeset or file revision'''
815 815
816 816 showlist = templatekw.showlist
817 817
818 818 # showparents() behaviour depends on ui trace level which
819 819 # causes unexpected behaviours at templating level and makes
820 820 # it harder to extract it in a standalone function. Its
821 821 # behaviour cannot be changed so leave it here for now.
822 822 def showparents(**args):
823 823 ctx = args['ctx']
824 824 parents = [[('rev', p.rev()), ('node', p.hex())]
825 825 for p in self._meaningful_parentrevs(ctx)]
826 826 return showlist('parent', parents, **args)
827 827
828 828 props = props.copy()
829 829 props.update(templatekw.keywords)
830 830 props['parents'] = showparents
831 831 props['templ'] = self.t
832 832 props['ctx'] = ctx
833 833 props['repo'] = self.repo
834 834 props['revcache'] = {'copies': copies}
835 835 props['cache'] = self.cache
836 836
837 837 # find correct templates for current mode
838 838
839 839 tmplmodes = [
840 840 (True, None),
841 841 (self.ui.verbose, 'verbose'),
842 842 (self.ui.quiet, 'quiet'),
843 843 (self.ui.debugflag, 'debug'),
844 844 ]
845 845
846 846 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
847 847 for mode, postfix in tmplmodes:
848 848 for type in types:
849 849 cur = postfix and ('%s_%s' % (type, postfix)) or type
850 850 if mode and cur in self.t:
851 851 types[type] = cur
852 852
853 853 try:
854 854
855 855 # write header
856 856 if types['header']:
857 857 h = templater.stringify(self.t(types['header'], **props))
858 858 if self.buffered:
859 859 self.header[ctx.rev()] = h
860 860 else:
861 861 if self.lastheader != h:
862 862 self.lastheader = h
863 863 self.ui.write(h)
864 864
865 865 # write changeset metadata, then patch if requested
866 866 key = types['changeset']
867 867 self.ui.write(templater.stringify(self.t(key, **props)))
868 868 self.showpatch(ctx.node(), matchfn)
869 869
870 870 if types['footer']:
871 871 if not self.footer:
872 872 self.footer = templater.stringify(self.t(types['footer'],
873 873 **props))
874 874
875 875 except KeyError, inst:
876 876 msg = _("%s: no key named '%s'")
877 877 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
878 878 except SyntaxError, inst:
879 879 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
880 880
881 881 def show_changeset(ui, repo, opts, buffered=False):
882 882 """show one changeset using template or regular display.
883 883
884 884 Display format will be the first non-empty hit of:
885 885 1. option 'template'
886 886 2. option 'style'
887 887 3. [ui] setting 'logtemplate'
888 888 4. [ui] setting 'style'
889 889 If all of these values are either the unset or the empty string,
890 890 regular display via changeset_printer() is done.
891 891 """
892 892 # options
893 893 patch = False
894 894 if opts.get('patch') or opts.get('stat'):
895 895 patch = scmutil.matchall(repo)
896 896
897 897 tmpl = opts.get('template')
898 898 style = None
899 899 if tmpl:
900 900 tmpl = templater.parsestring(tmpl, quoted=False)
901 901 else:
902 902 style = opts.get('style')
903 903
904 904 # ui settings
905 905 if not (tmpl or style):
906 906 tmpl = ui.config('ui', 'logtemplate')
907 907 if tmpl:
908 908 tmpl = templater.parsestring(tmpl)
909 909 else:
910 910 style = util.expandpath(ui.config('ui', 'style', ''))
911 911
912 912 if not (tmpl or style):
913 913 return changeset_printer(ui, repo, patch, opts, buffered)
914 914
915 915 mapfile = None
916 916 if style and not tmpl:
917 917 mapfile = style
918 918 if not os.path.split(mapfile)[0]:
919 919 mapname = (templater.templatepath('map-cmdline.' + mapfile)
920 920 or templater.templatepath(mapfile))
921 921 if mapname:
922 922 mapfile = mapname
923 923
924 924 try:
925 925 t = changeset_templater(ui, repo, patch, opts, mapfile, buffered)
926 926 except SyntaxError, inst:
927 927 raise util.Abort(inst.args[0])
928 928 if tmpl:
929 929 t.use_template(tmpl)
930 930 return t
931 931
932 932 def finddate(ui, repo, date):
933 933 """Find the tipmost changeset that matches the given date spec"""
934 934
935 935 df = util.matchdate(date)
936 936 m = scmutil.matchall(repo)
937 937 results = {}
938 938
939 939 def prep(ctx, fns):
940 940 d = ctx.date()
941 941 if df(d[0]):
942 942 results[ctx.rev()] = d
943 943
944 944 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
945 945 rev = ctx.rev()
946 946 if rev in results:
947 947 ui.status(_("Found revision %s from %s\n") %
948 948 (rev, util.datestr(results[rev])))
949 949 return str(rev)
950 950
951 951 raise util.Abort(_("revision matching date not found"))
952 952
953 953 def walkchangerevs(repo, match, opts, prepare):
954 954 '''Iterate over files and the revs in which they changed.
955 955
956 956 Callers most commonly need to iterate backwards over the history
957 957 in which they are interested. Doing so has awful (quadratic-looking)
958 958 performance, so we use iterators in a "windowed" way.
959 959
960 960 We walk a window of revisions in the desired order. Within the
961 961 window, we first walk forwards to gather data, then in the desired
962 962 order (usually backwards) to display it.
963 963
964 964 This function returns an iterator yielding contexts. Before
965 965 yielding each context, the iterator will first call the prepare
966 966 function on each context in the window in forward order.'''
967 967
968 968 def increasing_windows(start, end, windowsize=8, sizelimit=512):
969 969 if start < end:
970 970 while start < end:
971 971 yield start, min(windowsize, end - start)
972 972 start += windowsize
973 973 if windowsize < sizelimit:
974 974 windowsize *= 2
975 975 else:
976 976 while start > end:
977 977 yield start, min(windowsize, start - end - 1)
978 978 start -= windowsize
979 979 if windowsize < sizelimit:
980 980 windowsize *= 2
981 981
982 982 follow = opts.get('follow') or opts.get('follow_first')
983 983
984 984 if not len(repo):
985 985 return []
986 986
987 987 if follow:
988 988 defrange = '%s:0' % repo['.'].rev()
989 989 else:
990 990 defrange = '-1:0'
991 991 revs = scmutil.revrange(repo, opts['rev'] or [defrange])
992 992 if not revs:
993 993 return []
994 994 wanted = set()
995 995 slowpath = match.anypats() or (match.files() and opts.get('removed'))
996 996 fncache = {}
997 997 change = repo.changectx
998 998
999 999 # First step is to fill wanted, the set of revisions that we want to yield.
1000 1000 # When it does not induce extra cost, we also fill fncache for revisions in
1001 1001 # wanted: a cache of filenames that were changed (ctx.files()) and that
1002 1002 # match the file filtering conditions.
1003 1003
1004 1004 if not slowpath and not match.files():
1005 1005 # No files, no patterns. Display all revs.
1006 1006 wanted = set(revs)
1007 1007 copies = []
1008 1008
1009 1009 if not slowpath and match.files():
1010 1010 # We only have to read through the filelog to find wanted revisions
1011 1011
1012 1012 minrev, maxrev = min(revs), max(revs)
1013 1013 def filerevgen(filelog, last):
1014 1014 """
1015 1015 Only files, no patterns. Check the history of each file.
1016 1016
1017 1017 Examines filelog entries within minrev, maxrev linkrev range
1018 1018 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1019 1019 tuples in backwards order
1020 1020 """
1021 1021 cl_count = len(repo)
1022 1022 revs = []
1023 1023 for j in xrange(0, last + 1):
1024 1024 linkrev = filelog.linkrev(j)
1025 1025 if linkrev < minrev:
1026 1026 continue
1027 1027 # only yield rev for which we have the changelog, it can
1028 1028 # happen while doing "hg log" during a pull or commit
1029 1029 if linkrev >= cl_count:
1030 1030 break
1031 1031
1032 1032 parentlinkrevs = []
1033 1033 for p in filelog.parentrevs(j):
1034 1034 if p != nullrev:
1035 1035 parentlinkrevs.append(filelog.linkrev(p))
1036 1036 n = filelog.node(j)
1037 1037 revs.append((linkrev, parentlinkrevs,
1038 1038 follow and filelog.renamed(n)))
1039 1039
1040 1040 return reversed(revs)
1041 1041 def iterfiles():
1042 1042 pctx = repo['.']
1043 1043 for filename in match.files():
1044 1044 if follow:
1045 1045 if filename not in pctx:
1046 1046 raise util.Abort(_('cannot follow file not in parent '
1047 1047 'revision: "%s"') % filename)
1048 1048 yield filename, pctx[filename].filenode()
1049 1049 else:
1050 1050 yield filename, None
1051 1051 for filename_node in copies:
1052 1052 yield filename_node
1053 1053 for file_, node in iterfiles():
1054 1054 filelog = repo.file(file_)
1055 1055 if not len(filelog):
1056 1056 if node is None:
1057 1057 # A zero count may be a directory or deleted file, so
1058 1058 # try to find matching entries on the slow path.
1059 1059 if follow:
1060 1060 raise util.Abort(
1061 1061 _('cannot follow nonexistent file: "%s"') % file_)
1062 1062 slowpath = True
1063 1063 break
1064 1064 else:
1065 1065 continue
1066 1066
1067 1067 if node is None:
1068 1068 last = len(filelog) - 1
1069 1069 else:
1070 1070 last = filelog.rev(node)
1071 1071
1072 1072
1073 1073 # keep track of all ancestors of the file
1074 1074 ancestors = set([filelog.linkrev(last)])
1075 1075
1076 1076 # iterate from latest to oldest revision
1077 1077 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1078 1078 if not follow:
1079 1079 if rev > maxrev:
1080 1080 continue
1081 1081 else:
1082 1082 # Note that last might not be the first interesting
1083 1083 # rev to us:
1084 1084 # if the file has been changed after maxrev, we'll
1085 1085 # have linkrev(last) > maxrev, and we still need
1086 1086 # to explore the file graph
1087 1087 if rev not in ancestors:
1088 1088 continue
1089 1089 # XXX insert 1327 fix here
1090 1090 if flparentlinkrevs:
1091 1091 ancestors.update(flparentlinkrevs)
1092 1092
1093 1093 fncache.setdefault(rev, []).append(file_)
1094 1094 wanted.add(rev)
1095 1095 if copied:
1096 1096 copies.append(copied)
1097 1097 if slowpath:
1098 1098 # We have to read the changelog to match filenames against
1099 1099 # changed files
1100 1100
1101 1101 if follow:
1102 1102 raise util.Abort(_('can only follow copies/renames for explicit '
1103 1103 'filenames'))
1104 1104
1105 1105 # The slow path checks files modified in every changeset.
1106 1106 for i in sorted(revs):
1107 1107 ctx = change(i)
1108 1108 matches = filter(match, ctx.files())
1109 1109 if matches:
1110 1110 fncache[i] = matches
1111 1111 wanted.add(i)
1112 1112
1113 1113 class followfilter(object):
1114 1114 def __init__(self, onlyfirst=False):
1115 1115 self.startrev = nullrev
1116 1116 self.roots = set()
1117 1117 self.onlyfirst = onlyfirst
1118 1118
1119 1119 def match(self, rev):
1120 1120 def realparents(rev):
1121 1121 if self.onlyfirst:
1122 1122 return repo.changelog.parentrevs(rev)[0:1]
1123 1123 else:
1124 1124 return filter(lambda x: x != nullrev,
1125 1125 repo.changelog.parentrevs(rev))
1126 1126
1127 1127 if self.startrev == nullrev:
1128 1128 self.startrev = rev
1129 1129 return True
1130 1130
1131 1131 if rev > self.startrev:
1132 1132 # forward: all descendants
1133 1133 if not self.roots:
1134 1134 self.roots.add(self.startrev)
1135 1135 for parent in realparents(rev):
1136 1136 if parent in self.roots:
1137 1137 self.roots.add(rev)
1138 1138 return True
1139 1139 else:
1140 1140 # backwards: all parents
1141 1141 if not self.roots:
1142 1142 self.roots.update(realparents(self.startrev))
1143 1143 if rev in self.roots:
1144 1144 self.roots.remove(rev)
1145 1145 self.roots.update(realparents(rev))
1146 1146 return True
1147 1147
1148 1148 return False
1149 1149
1150 1150 # it might be worthwhile to do this in the iterator if the rev range
1151 1151 # is descending and the prune args are all within that range
1152 1152 for rev in opts.get('prune', ()):
1153 1153 rev = repo[rev].rev()
1154 1154 ff = followfilter()
1155 1155 stop = min(revs[0], revs[-1])
1156 1156 for x in xrange(rev, stop - 1, -1):
1157 1157 if ff.match(x):
1158 1158 wanted.discard(x)
1159 1159
1160 1160 # Now that wanted is correctly initialized, we can iterate over the
1161 1161 # revision range, yielding only revisions in wanted.
1162 1162 def iterate():
1163 1163 if follow and not match.files():
1164 1164 ff = followfilter(onlyfirst=opts.get('follow_first'))
1165 1165 def want(rev):
1166 1166 return ff.match(rev) and rev in wanted
1167 1167 else:
1168 1168 def want(rev):
1169 1169 return rev in wanted
1170 1170
1171 1171 for i, window in increasing_windows(0, len(revs)):
1172 1172 nrevs = [rev for rev in revs[i:i + window] if want(rev)]
1173 1173 for rev in sorted(nrevs):
1174 1174 fns = fncache.get(rev)
1175 1175 ctx = change(rev)
1176 1176 if not fns:
1177 1177 def fns_generator():
1178 1178 for f in ctx.files():
1179 1179 if match(f):
1180 1180 yield f
1181 1181 fns = fns_generator()
1182 1182 prepare(ctx, fns)
1183 1183 for rev in nrevs:
1184 1184 yield change(rev)
1185 1185 return iterate()
1186 1186
1187 1187 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1188 1188 join = lambda f: os.path.join(prefix, f)
1189 1189 bad = []
1190 1190 oldbad = match.bad
1191 1191 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1192 1192 names = []
1193 1193 wctx = repo[None]
1194 1194 cca = None
1195 1195 abort, warn = scmutil.checkportabilityalert(ui)
1196 1196 if abort or warn:
1197 1197 cca = scmutil.casecollisionauditor(ui, abort, wctx)
1198 1198 for f in repo.walk(match):
1199 1199 exact = match.exact(f)
1200 1200 if exact or not explicitonly and f not in repo.dirstate:
1201 1201 if cca:
1202 1202 cca(f)
1203 1203 names.append(f)
1204 1204 if ui.verbose or not exact:
1205 1205 ui.status(_('adding %s\n') % match.rel(join(f)))
1206 1206
1207 1207 for subpath in wctx.substate:
1208 1208 sub = wctx.sub(subpath)
1209 1209 try:
1210 1210 submatch = matchmod.narrowmatcher(subpath, match)
1211 1211 if listsubrepos:
1212 1212 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1213 1213 False))
1214 1214 else:
1215 1215 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1216 1216 True))
1217 1217 except error.LookupError:
1218 1218 ui.status(_("skipping missing subrepository: %s\n")
1219 1219 % join(subpath))
1220 1220
1221 1221 if not dryrun:
1222 1222 rejected = wctx.add(names, prefix)
1223 1223 bad.extend(f for f in rejected if f in match.files())
1224 1224 return bad
1225 1225
1226 1226 def forget(ui, repo, match, prefix, explicitonly):
1227 1227 join = lambda f: os.path.join(prefix, f)
1228 1228 bad = []
1229 1229 oldbad = match.bad
1230 1230 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1231 1231 wctx = repo[None]
1232 1232 forgot = []
1233 1233 s = repo.status(match=match, clean=True)
1234 1234 forget = sorted(s[0] + s[1] + s[3] + s[6])
1235 1235 if explicitonly:
1236 1236 forget = [f for f in forget if match.exact(f)]
1237 1237
1238 1238 for subpath in wctx.substate:
1239 1239 sub = wctx.sub(subpath)
1240 1240 try:
1241 1241 submatch = matchmod.narrowmatcher(subpath, match)
1242 1242 subbad, subforgot = sub.forget(ui, submatch, prefix)
1243 1243 bad.extend([subpath + '/' + f for f in subbad])
1244 1244 forgot.extend([subpath + '/' + f for f in subforgot])
1245 1245 except error.LookupError:
1246 1246 ui.status(_("skipping missing subrepository: %s\n")
1247 1247 % join(subpath))
1248 1248
1249 1249 if not explicitonly:
1250 1250 for f in match.files():
1251 1251 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
1252 1252 if f not in forgot:
1253 1253 if os.path.exists(match.rel(join(f))):
1254 1254 ui.warn(_('not removing %s: '
1255 1255 'file is already untracked\n')
1256 1256 % match.rel(join(f)))
1257 1257 bad.append(f)
1258 1258
1259 1259 for f in forget:
1260 1260 if ui.verbose or not match.exact(f):
1261 1261 ui.status(_('removing %s\n') % match.rel(join(f)))
1262 1262
1263 1263 rejected = wctx.forget(forget, prefix)
1264 1264 bad.extend(f for f in rejected if f in match.files())
1265 1265 forgot.extend(forget)
1266 1266 return bad, forgot
1267 1267
1268 1268 def duplicatecopies(repo, rev, p1):
1269 1269 "Reproduce copies found in the source revision in the dirstate for grafts"
1270 1270 for dst, src in copies.pathcopies(repo[p1], repo[rev]).iteritems():
1271 1271 repo.dirstate.copy(src, dst)
1272 1272
1273 1273 def commit(ui, repo, commitfunc, pats, opts):
1274 1274 '''commit the specified files or all outstanding changes'''
1275 1275 date = opts.get('date')
1276 1276 if date:
1277 1277 opts['date'] = util.parsedate(date)
1278 1278 message = logmessage(ui, opts)
1279 1279
1280 1280 # extract addremove carefully -- this function can be called from a command
1281 1281 # that doesn't support addremove
1282 1282 if opts.get('addremove'):
1283 1283 scmutil.addremove(repo, pats, opts)
1284 1284
1285 1285 return commitfunc(ui, repo, message,
1286 1286 scmutil.match(repo[None], pats, opts), opts)
1287 1287
1288 def amend(ui, repo, commitfunc, old, extra, pats, opts):
1289 ui.note(_('amending changeset %s\n') % old)
1290 base = old.p1()
1291
1292 wlock = repo.wlock()
1293 try:
1294 # Fix up dirstate for copies and renames
1295 duplicatecopies(repo, None, base.node())
1296
1297 # First, do a regular commit to record all changes in the working
1298 # directory (if there are any)
1299 node = commit(ui, repo, commitfunc, pats, opts)
1300 ctx = repo[node]
1301
1302 # Participating changesets:
1303 #
1304 # node/ctx o - new (intermediate) commit that contains changes from
1305 # | working dir to go into amending commit (or a workingctx
1306 # | if there were no changes)
1307 # |
1308 # old o - changeset to amend
1309 # |
1310 # base o - parent of amending changeset
1311
1312 files = set(old.files())
1313
1314 # Second, we use either the commit we just did, or if there were no
1315 # changes the parent of the working directory as the version of the
1316 # files in the final amend commit
1317 if node:
1318 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
1319
1320 user = ctx.user()
1321 date = ctx.date()
1322 message = ctx.description()
1323 extra = ctx.extra()
1324
1325 # Prune files which were reverted by the updates: if old introduced
1326 # file X and our intermediate commit, node, renamed that file, then
1327 # those two files are the same and we can discard X from our list
1328 # of files. Likewise if X was deleted, it's no longer relevant
1329 files.update(ctx.files())
1330
1331 def samefile(f):
1332 if f in ctx.manifest():
1333 a = ctx.filectx(f)
1334 if f in base.manifest():
1335 b = base.filectx(f)
1336 return (a.data() == b.data()
1337 and a.flags() == b.flags()
1338 and a.renamed() == b.renamed())
1339 else:
1340 return False
1341 else:
1342 return f not in base.manifest()
1343 files = [f for f in files if not samefile(f)]
1344
1345 def filectxfn(repo, ctx_, path):
1346 try:
1347 return ctx.filectx(path)
1348 except KeyError:
1349 raise IOError()
1350 else:
1351 ui.note(_('copying changeset %s to %s\n') % (old, base))
1352
1353 # Use version of files as in the old cset
1354 def filectxfn(repo, ctx_, path):
1355 try:
1356 return old.filectx(path)
1357 except KeyError:
1358 raise IOError()
1359
1360 # See if we got a message from -m or -l, if not, open the editor
1361 # with the message of the changeset to amend
1362 user = opts.get('user') or old.user()
1363 date = opts.get('date') or old.date()
1364 message = logmessage(ui, opts)
1365 if not message:
1366 cctx = context.workingctx(repo, old.description(), user, date,
1367 extra,
1368 repo.status(base.node(), old.node()))
1369 message = commitforceeditor(repo, cctx, [])
1370
1371 new = context.memctx(repo,
1372 parents=[base.node(), nullid],
1373 text=message,
1374 files=files,
1375 filectxfn=filectxfn,
1376 user=user,
1377 date=date,
1378 extra=extra)
1379 newid = repo.commitctx(new)
1380 if newid != old.node():
1381 # Reroute the working copy parent to the new changeset
1382 repo.dirstate.setparents(newid, nullid)
1383
1384 # Move bookmarks from old parent to amend commit
1385 bms = repo.nodebookmarks(old.node())
1386 if bms:
1387 for bm in bms:
1388 repo._bookmarks[bm] = newid
1389 bookmarks.write(repo)
1390
1391 # Strip the intermediate commit (if there was one) and the amended
1392 # commit
1393 lock = repo.lock()
1394 try:
1395 if node:
1396 ui.note(_('stripping intermediate changeset %s\n') % ctx)
1397 ui.note(_('stripping amended changeset %s\n') % old)
1398 repair.strip(ui, repo, old.node(), topic='amend-backup')
1399 finally:
1400 lock.release()
1401 finally:
1402 wlock.release()
1403 return newid
1404
1288 1405 def commiteditor(repo, ctx, subs):
1289 1406 if ctx.description():
1290 1407 return ctx.description()
1291 1408 return commitforceeditor(repo, ctx, subs)
1292 1409
1293 1410 def commitforceeditor(repo, ctx, subs):
1294 1411 edittext = []
1295 1412 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
1296 1413 if ctx.description():
1297 1414 edittext.append(ctx.description())
1298 1415 edittext.append("")
1299 1416 edittext.append("") # Empty line between message and comments.
1300 1417 edittext.append(_("HG: Enter commit message."
1301 1418 " Lines beginning with 'HG:' are removed."))
1302 1419 edittext.append(_("HG: Leave message empty to abort commit."))
1303 1420 edittext.append("HG: --")
1304 1421 edittext.append(_("HG: user: %s") % ctx.user())
1305 1422 if ctx.p2():
1306 1423 edittext.append(_("HG: branch merge"))
1307 1424 if ctx.branch():
1308 1425 edittext.append(_("HG: branch '%s'") % ctx.branch())
1309 1426 edittext.extend([_("HG: subrepo %s") % s for s in subs])
1310 1427 edittext.extend([_("HG: added %s") % f for f in added])
1311 1428 edittext.extend([_("HG: changed %s") % f for f in modified])
1312 1429 edittext.extend([_("HG: removed %s") % f for f in removed])
1313 1430 if not added and not modified and not removed:
1314 1431 edittext.append(_("HG: no files changed"))
1315 1432 edittext.append("")
1316 1433 # run editor in the repository root
1317 1434 olddir = os.getcwd()
1318 1435 os.chdir(repo.root)
1319 1436 text = repo.ui.edit("\n".join(edittext), ctx.user())
1320 1437 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
1321 1438 os.chdir(olddir)
1322 1439
1323 1440 if not text.strip():
1324 1441 raise util.Abort(_("empty commit message"))
1325 1442
1326 1443 return text
1327 1444
1328 1445 def revert(ui, repo, ctx, parents, *pats, **opts):
1329 1446 parent, p2 = parents
1330 1447 node = ctx.node()
1331 1448
1332 1449 mf = ctx.manifest()
1333 1450 if node == parent:
1334 1451 pmf = mf
1335 1452 else:
1336 1453 pmf = None
1337 1454
1338 1455 # need all matching names in dirstate and manifest of target rev,
1339 1456 # so have to walk both. do not print errors if files exist in one
1340 1457 # but not other.
1341 1458
1342 1459 names = {}
1343 1460
1344 1461 wlock = repo.wlock()
1345 1462 try:
1346 1463 # walk dirstate.
1347 1464
1348 1465 m = scmutil.match(repo[None], pats, opts)
1349 1466 m.bad = lambda x, y: False
1350 1467 for abs in repo.walk(m):
1351 1468 names[abs] = m.rel(abs), m.exact(abs)
1352 1469
1353 1470 # walk target manifest.
1354 1471
1355 1472 def badfn(path, msg):
1356 1473 if path in names:
1357 1474 return
1358 1475 if path in repo[node].substate:
1359 1476 return
1360 1477 path_ = path + '/'
1361 1478 for f in names:
1362 1479 if f.startswith(path_):
1363 1480 return
1364 1481 ui.warn("%s: %s\n" % (m.rel(path), msg))
1365 1482
1366 1483 m = scmutil.match(repo[node], pats, opts)
1367 1484 m.bad = badfn
1368 1485 for abs in repo[node].walk(m):
1369 1486 if abs not in names:
1370 1487 names[abs] = m.rel(abs), m.exact(abs)
1371 1488
1372 1489 # get the list of subrepos that must be reverted
1373 1490 targetsubs = [s for s in repo[node].substate if m(s)]
1374 1491 m = scmutil.matchfiles(repo, names)
1375 1492 changes = repo.status(match=m)[:4]
1376 1493 modified, added, removed, deleted = map(set, changes)
1377 1494
1378 1495 # if f is a rename, also revert the source
1379 1496 cwd = repo.getcwd()
1380 1497 for f in added:
1381 1498 src = repo.dirstate.copied(f)
1382 1499 if src and src not in names and repo.dirstate[src] == 'r':
1383 1500 removed.add(src)
1384 1501 names[src] = (repo.pathto(src, cwd), True)
1385 1502
1386 1503 def removeforget(abs):
1387 1504 if repo.dirstate[abs] == 'a':
1388 1505 return _('forgetting %s\n')
1389 1506 return _('removing %s\n')
1390 1507
1391 1508 revert = ([], _('reverting %s\n'))
1392 1509 add = ([], _('adding %s\n'))
1393 1510 remove = ([], removeforget)
1394 1511 undelete = ([], _('undeleting %s\n'))
1395 1512
1396 1513 disptable = (
1397 1514 # dispatch table:
1398 1515 # file state
1399 1516 # action if in target manifest
1400 1517 # action if not in target manifest
1401 1518 # make backup if in target manifest
1402 1519 # make backup if not in target manifest
1403 1520 (modified, revert, remove, True, True),
1404 1521 (added, revert, remove, True, False),
1405 1522 (removed, undelete, None, False, False),
1406 1523 (deleted, revert, remove, False, False),
1407 1524 )
1408 1525
1409 1526 for abs, (rel, exact) in sorted(names.items()):
1410 1527 mfentry = mf.get(abs)
1411 1528 target = repo.wjoin(abs)
1412 1529 def handle(xlist, dobackup):
1413 1530 xlist[0].append(abs)
1414 1531 if (dobackup and not opts.get('no_backup') and
1415 1532 os.path.lexists(target)):
1416 1533 bakname = "%s.orig" % rel
1417 1534 ui.note(_('saving current version of %s as %s\n') %
1418 1535 (rel, bakname))
1419 1536 if not opts.get('dry_run'):
1420 1537 util.rename(target, bakname)
1421 1538 if ui.verbose or not exact:
1422 1539 msg = xlist[1]
1423 1540 if not isinstance(msg, basestring):
1424 1541 msg = msg(abs)
1425 1542 ui.status(msg % rel)
1426 1543 for table, hitlist, misslist, backuphit, backupmiss in disptable:
1427 1544 if abs not in table:
1428 1545 continue
1429 1546 # file has changed in dirstate
1430 1547 if mfentry:
1431 1548 handle(hitlist, backuphit)
1432 1549 elif misslist is not None:
1433 1550 handle(misslist, backupmiss)
1434 1551 break
1435 1552 else:
1436 1553 if abs not in repo.dirstate:
1437 1554 if mfentry:
1438 1555 handle(add, True)
1439 1556 elif exact:
1440 1557 ui.warn(_('file not managed: %s\n') % rel)
1441 1558 continue
1442 1559 # file has not changed in dirstate
1443 1560 if node == parent:
1444 1561 if exact:
1445 1562 ui.warn(_('no changes needed to %s\n') % rel)
1446 1563 continue
1447 1564 if pmf is None:
1448 1565 # only need parent manifest in this unlikely case,
1449 1566 # so do not read by default
1450 1567 pmf = repo[parent].manifest()
1451 1568 if abs in pmf and mfentry:
1452 1569 # if version of file is same in parent and target
1453 1570 # manifests, do nothing
1454 1571 if (pmf[abs] != mfentry or
1455 1572 pmf.flags(abs) != mf.flags(abs)):
1456 1573 handle(revert, False)
1457 1574 else:
1458 1575 handle(remove, False)
1459 1576
1460 1577 if not opts.get('dry_run'):
1461 1578 def checkout(f):
1462 1579 fc = ctx[f]
1463 1580 repo.wwrite(f, fc.data(), fc.flags())
1464 1581
1465 1582 audit_path = scmutil.pathauditor(repo.root)
1466 1583 for f in remove[0]:
1467 1584 if repo.dirstate[f] == 'a':
1468 1585 repo.dirstate.drop(f)
1469 1586 continue
1470 1587 audit_path(f)
1471 1588 try:
1472 1589 util.unlinkpath(repo.wjoin(f))
1473 1590 except OSError:
1474 1591 pass
1475 1592 repo.dirstate.remove(f)
1476 1593
1477 1594 normal = None
1478 1595 if node == parent:
1479 1596 # We're reverting to our parent. If possible, we'd like status
1480 1597 # to report the file as clean. We have to use normallookup for
1481 1598 # merges to avoid losing information about merged/dirty files.
1482 1599 if p2 != nullid:
1483 1600 normal = repo.dirstate.normallookup
1484 1601 else:
1485 1602 normal = repo.dirstate.normal
1486 1603 for f in revert[0]:
1487 1604 checkout(f)
1488 1605 if normal:
1489 1606 normal(f)
1490 1607
1491 1608 for f in add[0]:
1492 1609 checkout(f)
1493 1610 repo.dirstate.add(f)
1494 1611
1495 1612 normal = repo.dirstate.normallookup
1496 1613 if node == parent and p2 == nullid:
1497 1614 normal = repo.dirstate.normal
1498 1615 for f in undelete[0]:
1499 1616 checkout(f)
1500 1617 normal(f)
1501 1618
1502 1619 if targetsubs:
1503 1620 # Revert the subrepos on the revert list
1504 1621 for sub in targetsubs:
1505 1622 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
1506 1623 finally:
1507 1624 wlock.release()
1508 1625
1509 1626 def command(table):
1510 1627 '''returns a function object bound to table which can be used as
1511 1628 a decorator for populating table as a command table'''
1512 1629
1513 1630 def cmd(name, options, synopsis=None):
1514 1631 def decorator(func):
1515 1632 if synopsis:
1516 1633 table[name] = func, options[:], synopsis
1517 1634 else:
1518 1635 table[name] = func, options[:]
1519 1636 return func
1520 1637 return decorator
1521 1638
1522 1639 return cmd
@@ -1,5650 +1,5704 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import hex, bin, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _, gettext
11 11 import os, re, difflib, time, tempfile, errno
12 12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13 13 import patch, help, url, encoding, templatekw, discovery
14 14 import archival, changegroup, cmdutil, hbisect
15 15 import sshserver, hgweb, hgweb.server, commandserver
16 16 import merge as mergemod
17 17 import minirst, revset, fileset
18 18 import dagparser, context, simplemerge
19 19 import random, setdiscovery, treediscovery, dagutil, pvec
20 20 import phases
21 21
22 22 table = {}
23 23
24 24 command = cmdutil.command(table)
25 25
26 26 # common command options
27 27
28 28 globalopts = [
29 29 ('R', 'repository', '',
30 30 _('repository root directory or name of overlay bundle file'),
31 31 _('REPO')),
32 32 ('', 'cwd', '',
33 33 _('change working directory'), _('DIR')),
34 34 ('y', 'noninteractive', None,
35 35 _('do not prompt, automatically pick the first choice for all prompts')),
36 36 ('q', 'quiet', None, _('suppress output')),
37 37 ('v', 'verbose', None, _('enable additional output')),
38 38 ('', 'config', [],
39 39 _('set/override config option (use \'section.name=value\')'),
40 40 _('CONFIG')),
41 41 ('', 'debug', None, _('enable debugging output')),
42 42 ('', 'debugger', None, _('start debugger')),
43 43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
44 44 _('ENCODE')),
45 45 ('', 'encodingmode', encoding.encodingmode,
46 46 _('set the charset encoding mode'), _('MODE')),
47 47 ('', 'traceback', None, _('always print a traceback on exception')),
48 48 ('', 'time', None, _('time how long the command takes')),
49 49 ('', 'profile', None, _('print command execution profile')),
50 50 ('', 'version', None, _('output version information and exit')),
51 51 ('h', 'help', None, _('display help and exit')),
52 52 ]
53 53
54 54 dryrunopts = [('n', 'dry-run', None,
55 55 _('do not perform actions, just print output'))]
56 56
57 57 remoteopts = [
58 58 ('e', 'ssh', '',
59 59 _('specify ssh command to use'), _('CMD')),
60 60 ('', 'remotecmd', '',
61 61 _('specify hg command to run on the remote side'), _('CMD')),
62 62 ('', 'insecure', None,
63 63 _('do not verify server certificate (ignoring web.cacerts config)')),
64 64 ]
65 65
66 66 walkopts = [
67 67 ('I', 'include', [],
68 68 _('include names matching the given patterns'), _('PATTERN')),
69 69 ('X', 'exclude', [],
70 70 _('exclude names matching the given patterns'), _('PATTERN')),
71 71 ]
72 72
73 73 commitopts = [
74 74 ('m', 'message', '',
75 75 _('use text as commit message'), _('TEXT')),
76 76 ('l', 'logfile', '',
77 77 _('read commit message from file'), _('FILE')),
78 78 ]
79 79
80 80 commitopts2 = [
81 81 ('d', 'date', '',
82 82 _('record the specified date as commit date'), _('DATE')),
83 83 ('u', 'user', '',
84 84 _('record the specified user as committer'), _('USER')),
85 85 ]
86 86
87 87 templateopts = [
88 88 ('', 'style', '',
89 89 _('display using template map file'), _('STYLE')),
90 90 ('', 'template', '',
91 91 _('display with template'), _('TEMPLATE')),
92 92 ]
93 93
94 94 logopts = [
95 95 ('p', 'patch', None, _('show patch')),
96 96 ('g', 'git', None, _('use git extended diff format')),
97 97 ('l', 'limit', '',
98 98 _('limit number of changes displayed'), _('NUM')),
99 99 ('M', 'no-merges', None, _('do not show merges')),
100 100 ('', 'stat', None, _('output diffstat-style summary of changes')),
101 101 ] + templateopts
102 102
103 103 diffopts = [
104 104 ('a', 'text', None, _('treat all files as text')),
105 105 ('g', 'git', None, _('use git extended diff format')),
106 106 ('', 'nodates', None, _('omit dates from diff headers'))
107 107 ]
108 108
109 109 diffwsopts = [
110 110 ('w', 'ignore-all-space', None,
111 111 _('ignore white space when comparing lines')),
112 112 ('b', 'ignore-space-change', None,
113 113 _('ignore changes in the amount of white space')),
114 114 ('B', 'ignore-blank-lines', None,
115 115 _('ignore changes whose lines are all blank')),
116 116 ]
117 117
118 118 diffopts2 = [
119 119 ('p', 'show-function', None, _('show which function each change is in')),
120 120 ('', 'reverse', None, _('produce a diff that undoes the changes')),
121 121 ] + diffwsopts + [
122 122 ('U', 'unified', '',
123 123 _('number of lines of context to show'), _('NUM')),
124 124 ('', 'stat', None, _('output diffstat-style summary of changes')),
125 125 ]
126 126
127 127 mergetoolopts = [
128 128 ('t', 'tool', '', _('specify merge tool')),
129 129 ]
130 130
131 131 similarityopts = [
132 132 ('s', 'similarity', '',
133 133 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
134 134 ]
135 135
136 136 subrepoopts = [
137 137 ('S', 'subrepos', None,
138 138 _('recurse into subrepositories'))
139 139 ]
140 140
141 141 # Commands start here, listed alphabetically
142 142
143 143 @command('^add',
144 144 walkopts + subrepoopts + dryrunopts,
145 145 _('[OPTION]... [FILE]...'))
146 146 def add(ui, repo, *pats, **opts):
147 147 """add the specified files on the next commit
148 148
149 149 Schedule files to be version controlled and added to the
150 150 repository.
151 151
152 152 The files will be added to the repository at the next commit. To
153 153 undo an add before that, see :hg:`forget`.
154 154
155 155 If no names are given, add all files to the repository.
156 156
157 157 .. container:: verbose
158 158
159 159 An example showing how new (unknown) files are added
160 160 automatically by :hg:`add`::
161 161
162 162 $ ls
163 163 foo.c
164 164 $ hg status
165 165 ? foo.c
166 166 $ hg add
167 167 adding foo.c
168 168 $ hg status
169 169 A foo.c
170 170
171 171 Returns 0 if all files are successfully added.
172 172 """
173 173
174 174 m = scmutil.match(repo[None], pats, opts)
175 175 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
176 176 opts.get('subrepos'), prefix="", explicitonly=False)
177 177 return rejected and 1 or 0
178 178
179 179 @command('addremove',
180 180 similarityopts + walkopts + dryrunopts,
181 181 _('[OPTION]... [FILE]...'))
182 182 def addremove(ui, repo, *pats, **opts):
183 183 """add all new files, delete all missing files
184 184
185 185 Add all new files and remove all missing files from the
186 186 repository.
187 187
188 188 New files are ignored if they match any of the patterns in
189 189 ``.hgignore``. As with add, these changes take effect at the next
190 190 commit.
191 191
192 192 Use the -s/--similarity option to detect renamed files. With a
193 193 parameter greater than 0, this compares every removed file with
194 194 every added file and records those similar enough as renames. This
195 195 option takes a percentage between 0 (disabled) and 100 (files must
196 196 be identical) as its parameter. Detecting renamed files this way
197 197 can be expensive. After using this option, :hg:`status -C` can be
198 198 used to check which files were identified as moved or renamed.
199 199
200 200 Returns 0 if all files are successfully added.
201 201 """
202 202 try:
203 203 sim = float(opts.get('similarity') or 100)
204 204 except ValueError:
205 205 raise util.Abort(_('similarity must be a number'))
206 206 if sim < 0 or sim > 100:
207 207 raise util.Abort(_('similarity must be between 0 and 100'))
208 208 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
209 209
210 210 @command('^annotate|blame',
211 211 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
212 212 ('', 'follow', None,
213 213 _('follow copies/renames and list the filename (DEPRECATED)')),
214 214 ('', 'no-follow', None, _("don't follow copies and renames")),
215 215 ('a', 'text', None, _('treat all files as text')),
216 216 ('u', 'user', None, _('list the author (long with -v)')),
217 217 ('f', 'file', None, _('list the filename')),
218 218 ('d', 'date', None, _('list the date (short with -q)')),
219 219 ('n', 'number', None, _('list the revision number (default)')),
220 220 ('c', 'changeset', None, _('list the changeset')),
221 221 ('l', 'line-number', None, _('show line number at the first appearance'))
222 222 ] + diffwsopts + walkopts,
223 223 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
224 224 def annotate(ui, repo, *pats, **opts):
225 225 """show changeset information by line for each file
226 226
227 227 List changes in files, showing the revision id responsible for
228 228 each line
229 229
230 230 This command is useful for discovering when a change was made and
231 231 by whom.
232 232
233 233 Without the -a/--text option, annotate will avoid processing files
234 234 it detects as binary. With -a, annotate will annotate the file
235 235 anyway, although the results will probably be neither useful
236 236 nor desirable.
237 237
238 238 Returns 0 on success.
239 239 """
240 240 if opts.get('follow'):
241 241 # --follow is deprecated and now just an alias for -f/--file
242 242 # to mimic the behavior of Mercurial before version 1.5
243 243 opts['file'] = True
244 244
245 245 datefunc = ui.quiet and util.shortdate or util.datestr
246 246 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
247 247
248 248 if not pats:
249 249 raise util.Abort(_('at least one filename or pattern is required'))
250 250
251 251 hexfn = ui.debugflag and hex or short
252 252
253 253 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
254 254 ('number', ' ', lambda x: str(x[0].rev())),
255 255 ('changeset', ' ', lambda x: hexfn(x[0].node())),
256 256 ('date', ' ', getdate),
257 257 ('file', ' ', lambda x: x[0].path()),
258 258 ('line_number', ':', lambda x: str(x[1])),
259 259 ]
260 260
261 261 if (not opts.get('user') and not opts.get('changeset')
262 262 and not opts.get('date') and not opts.get('file')):
263 263 opts['number'] = True
264 264
265 265 linenumber = opts.get('line_number') is not None
266 266 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
267 267 raise util.Abort(_('at least one of -n/-c is required for -l'))
268 268
269 269 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
270 270 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
271 271
272 272 def bad(x, y):
273 273 raise util.Abort("%s: %s" % (x, y))
274 274
275 275 ctx = scmutil.revsingle(repo, opts.get('rev'))
276 276 m = scmutil.match(ctx, pats, opts)
277 277 m.bad = bad
278 278 follow = not opts.get('no_follow')
279 279 diffopts = patch.diffopts(ui, opts, section='annotate')
280 280 for abs in ctx.walk(m):
281 281 fctx = ctx[abs]
282 282 if not opts.get('text') and util.binary(fctx.data()):
283 283 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
284 284 continue
285 285
286 286 lines = fctx.annotate(follow=follow, linenumber=linenumber,
287 287 diffopts=diffopts)
288 288 pieces = []
289 289
290 290 for f, sep in funcmap:
291 291 l = [f(n) for n, dummy in lines]
292 292 if l:
293 293 sized = [(x, encoding.colwidth(x)) for x in l]
294 294 ml = max([w for x, w in sized])
295 295 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
296 296 for x, w in sized])
297 297
298 298 if pieces:
299 299 for p, l in zip(zip(*pieces), lines):
300 300 ui.write("%s: %s" % ("".join(p), l[1]))
301 301
302 302 if lines and not lines[-1][1].endswith('\n'):
303 303 ui.write('\n')
304 304
305 305 @command('archive',
306 306 [('', 'no-decode', None, _('do not pass files through decoders')),
307 307 ('p', 'prefix', '', _('directory prefix for files in archive'),
308 308 _('PREFIX')),
309 309 ('r', 'rev', '', _('revision to distribute'), _('REV')),
310 310 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
311 311 ] + subrepoopts + walkopts,
312 312 _('[OPTION]... DEST'))
313 313 def archive(ui, repo, dest, **opts):
314 314 '''create an unversioned archive of a repository revision
315 315
316 316 By default, the revision used is the parent of the working
317 317 directory; use -r/--rev to specify a different revision.
318 318
319 319 The archive type is automatically detected based on file
320 320 extension (or override using -t/--type).
321 321
322 322 .. container:: verbose
323 323
324 324 Examples:
325 325
326 326 - create a zip file containing the 1.0 release::
327 327
328 328 hg archive -r 1.0 project-1.0.zip
329 329
330 330 - create a tarball excluding .hg files::
331 331
332 332 hg archive project.tar.gz -X ".hg*"
333 333
334 334 Valid types are:
335 335
336 336 :``files``: a directory full of files (default)
337 337 :``tar``: tar archive, uncompressed
338 338 :``tbz2``: tar archive, compressed using bzip2
339 339 :``tgz``: tar archive, compressed using gzip
340 340 :``uzip``: zip archive, uncompressed
341 341 :``zip``: zip archive, compressed using deflate
342 342
343 343 The exact name of the destination archive or directory is given
344 344 using a format string; see :hg:`help export` for details.
345 345
346 346 Each member added to an archive file has a directory prefix
347 347 prepended. Use -p/--prefix to specify a format string for the
348 348 prefix. The default is the basename of the archive, with suffixes
349 349 removed.
350 350
351 351 Returns 0 on success.
352 352 '''
353 353
354 354 ctx = scmutil.revsingle(repo, opts.get('rev'))
355 355 if not ctx:
356 356 raise util.Abort(_('no working directory: please specify a revision'))
357 357 node = ctx.node()
358 358 dest = cmdutil.makefilename(repo, dest, node)
359 359 if os.path.realpath(dest) == repo.root:
360 360 raise util.Abort(_('repository root cannot be destination'))
361 361
362 362 kind = opts.get('type') or archival.guesskind(dest) or 'files'
363 363 prefix = opts.get('prefix')
364 364
365 365 if dest == '-':
366 366 if kind == 'files':
367 367 raise util.Abort(_('cannot archive plain files to stdout'))
368 368 dest = cmdutil.makefileobj(repo, dest)
369 369 if not prefix:
370 370 prefix = os.path.basename(repo.root) + '-%h'
371 371
372 372 prefix = cmdutil.makefilename(repo, prefix, node)
373 373 matchfn = scmutil.match(ctx, [], opts)
374 374 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
375 375 matchfn, prefix, subrepos=opts.get('subrepos'))
376 376
377 377 @command('backout',
378 378 [('', 'merge', None, _('merge with old dirstate parent after backout')),
379 379 ('', 'parent', '',
380 380 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
381 381 ('r', 'rev', '', _('revision to backout'), _('REV')),
382 382 ] + mergetoolopts + walkopts + commitopts + commitopts2,
383 383 _('[OPTION]... [-r] REV'))
384 384 def backout(ui, repo, node=None, rev=None, **opts):
385 385 '''reverse effect of earlier changeset
386 386
387 387 Prepare a new changeset with the effect of REV undone in the
388 388 current working directory.
389 389
390 390 If REV is the parent of the working directory, then this new changeset
391 391 is committed automatically. Otherwise, hg needs to merge the
392 392 changes and the merged result is left uncommitted.
393 393
394 394 .. note::
395 395 backout cannot be used to fix either an unwanted or
396 396 incorrect merge.
397 397
398 398 .. container:: verbose
399 399
400 400 By default, the pending changeset will have one parent,
401 401 maintaining a linear history. With --merge, the pending
402 402 changeset will instead have two parents: the old parent of the
403 403 working directory and a new child of REV that simply undoes REV.
404 404
405 405 Before version 1.7, the behavior without --merge was equivalent
406 406 to specifying --merge followed by :hg:`update --clean .` to
407 407 cancel the merge and leave the child of REV as a head to be
408 408 merged separately.
409 409
410 410 See :hg:`help dates` for a list of formats valid for -d/--date.
411 411
412 412 Returns 0 on success.
413 413 '''
414 414 if rev and node:
415 415 raise util.Abort(_("please specify just one revision"))
416 416
417 417 if not rev:
418 418 rev = node
419 419
420 420 if not rev:
421 421 raise util.Abort(_("please specify a revision to backout"))
422 422
423 423 date = opts.get('date')
424 424 if date:
425 425 opts['date'] = util.parsedate(date)
426 426
427 427 cmdutil.bailifchanged(repo)
428 428 node = scmutil.revsingle(repo, rev).node()
429 429
430 430 op1, op2 = repo.dirstate.parents()
431 431 a = repo.changelog.ancestor(op1, node)
432 432 if a != node:
433 433 raise util.Abort(_('cannot backout change on a different branch'))
434 434
435 435 p1, p2 = repo.changelog.parents(node)
436 436 if p1 == nullid:
437 437 raise util.Abort(_('cannot backout a change with no parents'))
438 438 if p2 != nullid:
439 439 if not opts.get('parent'):
440 440 raise util.Abort(_('cannot backout a merge changeset'))
441 441 p = repo.lookup(opts['parent'])
442 442 if p not in (p1, p2):
443 443 raise util.Abort(_('%s is not a parent of %s') %
444 444 (short(p), short(node)))
445 445 parent = p
446 446 else:
447 447 if opts.get('parent'):
448 448 raise util.Abort(_('cannot use --parent on non-merge changeset'))
449 449 parent = p1
450 450
451 451 # the backout should appear on the same branch
452 452 branch = repo.dirstate.branch()
453 453 hg.clean(repo, node, show_stats=False)
454 454 repo.dirstate.setbranch(branch)
455 455 revert_opts = opts.copy()
456 456 revert_opts['date'] = None
457 457 revert_opts['all'] = True
458 458 revert_opts['rev'] = hex(parent)
459 459 revert_opts['no_backup'] = None
460 460 revert(ui, repo, **revert_opts)
461 461 if not opts.get('merge') and op1 != node:
462 462 try:
463 463 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
464 464 return hg.update(repo, op1)
465 465 finally:
466 466 ui.setconfig('ui', 'forcemerge', '')
467 467
468 468 commit_opts = opts.copy()
469 469 commit_opts['addremove'] = False
470 470 if not commit_opts['message'] and not commit_opts['logfile']:
471 471 # we don't translate commit messages
472 472 commit_opts['message'] = "Backed out changeset %s" % short(node)
473 473 commit_opts['force_editor'] = True
474 474 commit(ui, repo, **commit_opts)
475 475 def nice(node):
476 476 return '%d:%s' % (repo.changelog.rev(node), short(node))
477 477 ui.status(_('changeset %s backs out changeset %s\n') %
478 478 (nice(repo.changelog.tip()), nice(node)))
479 479 if opts.get('merge') and op1 != node:
480 480 hg.clean(repo, op1, show_stats=False)
481 481 ui.status(_('merging with changeset %s\n')
482 482 % nice(repo.changelog.tip()))
483 483 try:
484 484 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
485 485 return hg.merge(repo, hex(repo.changelog.tip()))
486 486 finally:
487 487 ui.setconfig('ui', 'forcemerge', '')
488 488 return 0
489 489
490 490 @command('bisect',
491 491 [('r', 'reset', False, _('reset bisect state')),
492 492 ('g', 'good', False, _('mark changeset good')),
493 493 ('b', 'bad', False, _('mark changeset bad')),
494 494 ('s', 'skip', False, _('skip testing changeset')),
495 495 ('e', 'extend', False, _('extend the bisect range')),
496 496 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
497 497 ('U', 'noupdate', False, _('do not update to target'))],
498 498 _("[-gbsr] [-U] [-c CMD] [REV]"))
499 499 def bisect(ui, repo, rev=None, extra=None, command=None,
500 500 reset=None, good=None, bad=None, skip=None, extend=None,
501 501 noupdate=None):
502 502 """subdivision search of changesets
503 503
504 504 This command helps to find changesets which introduce problems. To
505 505 use, mark the earliest changeset you know exhibits the problem as
506 506 bad, then mark the latest changeset which is free from the problem
507 507 as good. Bisect will update your working directory to a revision
508 508 for testing (unless the -U/--noupdate option is specified). Once
509 509 you have performed tests, mark the working directory as good or
510 510 bad, and bisect will either update to another candidate changeset
511 511 or announce that it has found the bad revision.
512 512
513 513 As a shortcut, you can also use the revision argument to mark a
514 514 revision as good or bad without checking it out first.
515 515
516 516 If you supply a command, it will be used for automatic bisection.
517 517 Its exit status will be used to mark revisions as good or bad:
518 518 status 0 means good, 125 means to skip the revision, 127
519 519 (command not found) will abort the bisection, and any other
520 520 non-zero exit status means the revision is bad.
521 521
522 522 .. container:: verbose
523 523
524 524 Some examples:
525 525
526 526 - start a bisection with known bad revision 12, and good revision 34::
527 527
528 528 hg bisect --bad 34
529 529 hg bisect --good 12
530 530
531 531 - advance the current bisection by marking current revision as good or
532 532 bad::
533 533
534 534 hg bisect --good
535 535 hg bisect --bad
536 536
537 537 - mark the current revision, or a known revision, to be skipped (eg. if
538 538 that revision is not usable because of another issue)::
539 539
540 540 hg bisect --skip
541 541 hg bisect --skip 23
542 542
543 543 - forget the current bisection::
544 544
545 545 hg bisect --reset
546 546
547 547 - use 'make && make tests' to automatically find the first broken
548 548 revision::
549 549
550 550 hg bisect --reset
551 551 hg bisect --bad 34
552 552 hg bisect --good 12
553 553 hg bisect --command 'make && make tests'
554 554
555 555 - see all changesets whose states are already known in the current
556 556 bisection::
557 557
558 558 hg log -r "bisect(pruned)"
559 559
560 560 - see all changesets that took part in the current bisection::
561 561
562 562 hg log -r "bisect(range)"
563 563
564 564 - with the graphlog extension, you can even get a nice graph::
565 565
566 566 hg log --graph -r "bisect(range)"
567 567
568 568 See :hg:`help revsets` for more about the `bisect()` keyword.
569 569
570 570 Returns 0 on success.
571 571 """
572 572 def extendbisectrange(nodes, good):
573 573 # bisect is incomplete when it ends on a merge node and
574 574 # one of the parent was not checked.
575 575 parents = repo[nodes[0]].parents()
576 576 if len(parents) > 1:
577 577 side = good and state['bad'] or state['good']
578 578 num = len(set(i.node() for i in parents) & set(side))
579 579 if num == 1:
580 580 return parents[0].ancestor(parents[1])
581 581 return None
582 582
583 583 def print_result(nodes, good):
584 584 displayer = cmdutil.show_changeset(ui, repo, {})
585 585 if len(nodes) == 1:
586 586 # narrowed it down to a single revision
587 587 if good:
588 588 ui.write(_("The first good revision is:\n"))
589 589 else:
590 590 ui.write(_("The first bad revision is:\n"))
591 591 displayer.show(repo[nodes[0]])
592 592 extendnode = extendbisectrange(nodes, good)
593 593 if extendnode is not None:
594 594 ui.write(_('Not all ancestors of this changeset have been'
595 595 ' checked.\nUse bisect --extend to continue the '
596 596 'bisection from\nthe common ancestor, %s.\n')
597 597 % extendnode)
598 598 else:
599 599 # multiple possible revisions
600 600 if good:
601 601 ui.write(_("Due to skipped revisions, the first "
602 602 "good revision could be any of:\n"))
603 603 else:
604 604 ui.write(_("Due to skipped revisions, the first "
605 605 "bad revision could be any of:\n"))
606 606 for n in nodes:
607 607 displayer.show(repo[n])
608 608 displayer.close()
609 609
610 610 def check_state(state, interactive=True):
611 611 if not state['good'] or not state['bad']:
612 612 if (good or bad or skip or reset) and interactive:
613 613 return
614 614 if not state['good']:
615 615 raise util.Abort(_('cannot bisect (no known good revisions)'))
616 616 else:
617 617 raise util.Abort(_('cannot bisect (no known bad revisions)'))
618 618 return True
619 619
620 620 # backward compatibility
621 621 if rev in "good bad reset init".split():
622 622 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
623 623 cmd, rev, extra = rev, extra, None
624 624 if cmd == "good":
625 625 good = True
626 626 elif cmd == "bad":
627 627 bad = True
628 628 else:
629 629 reset = True
630 630 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
631 631 raise util.Abort(_('incompatible arguments'))
632 632
633 633 if reset:
634 634 p = repo.join("bisect.state")
635 635 if os.path.exists(p):
636 636 os.unlink(p)
637 637 return
638 638
639 639 state = hbisect.load_state(repo)
640 640
641 641 if command:
642 642 changesets = 1
643 643 try:
644 644 while changesets:
645 645 # update state
646 646 status = util.system(command, out=ui.fout)
647 647 if status == 125:
648 648 transition = "skip"
649 649 elif status == 0:
650 650 transition = "good"
651 651 # status < 0 means process was killed
652 652 elif status == 127:
653 653 raise util.Abort(_("failed to execute %s") % command)
654 654 elif status < 0:
655 655 raise util.Abort(_("%s killed") % command)
656 656 else:
657 657 transition = "bad"
658 658 ctx = scmutil.revsingle(repo, rev)
659 659 rev = None # clear for future iterations
660 660 state[transition].append(ctx.node())
661 661 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
662 662 check_state(state, interactive=False)
663 663 # bisect
664 664 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
665 665 # update to next check
666 666 cmdutil.bailifchanged(repo)
667 667 hg.clean(repo, nodes[0], show_stats=False)
668 668 finally:
669 669 hbisect.save_state(repo, state)
670 670 print_result(nodes, good)
671 671 return
672 672
673 673 # update state
674 674
675 675 if rev:
676 676 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
677 677 else:
678 678 nodes = [repo.lookup('.')]
679 679
680 680 if good or bad or skip:
681 681 if good:
682 682 state['good'] += nodes
683 683 elif bad:
684 684 state['bad'] += nodes
685 685 elif skip:
686 686 state['skip'] += nodes
687 687 hbisect.save_state(repo, state)
688 688
689 689 if not check_state(state):
690 690 return
691 691
692 692 # actually bisect
693 693 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
694 694 if extend:
695 695 if not changesets:
696 696 extendnode = extendbisectrange(nodes, good)
697 697 if extendnode is not None:
698 698 ui.write(_("Extending search to changeset %d:%s\n"
699 699 % (extendnode.rev(), extendnode)))
700 700 if noupdate:
701 701 return
702 702 cmdutil.bailifchanged(repo)
703 703 return hg.clean(repo, extendnode.node())
704 704 raise util.Abort(_("nothing to extend"))
705 705
706 706 if changesets == 0:
707 707 print_result(nodes, good)
708 708 else:
709 709 assert len(nodes) == 1 # only a single node can be tested next
710 710 node = nodes[0]
711 711 # compute the approximate number of remaining tests
712 712 tests, size = 0, 2
713 713 while size <= changesets:
714 714 tests, size = tests + 1, size * 2
715 715 rev = repo.changelog.rev(node)
716 716 ui.write(_("Testing changeset %d:%s "
717 717 "(%d changesets remaining, ~%d tests)\n")
718 718 % (rev, short(node), changesets, tests))
719 719 if not noupdate:
720 720 cmdutil.bailifchanged(repo)
721 721 return hg.clean(repo, node)
722 722
723 723 @command('bookmarks',
724 724 [('f', 'force', False, _('force')),
725 725 ('r', 'rev', '', _('revision'), _('REV')),
726 726 ('d', 'delete', False, _('delete a given bookmark')),
727 727 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
728 728 ('i', 'inactive', False, _('mark a bookmark inactive'))],
729 729 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
730 730 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
731 731 rename=None, inactive=False):
732 732 '''track a line of development with movable markers
733 733
734 734 Bookmarks are pointers to certain commits that move when committing.
735 735 Bookmarks are local. They can be renamed, copied and deleted. It is
736 736 possible to use :hg:`merge NAME` to merge from a given bookmark, and
737 737 :hg:`update NAME` to update to a given bookmark.
738 738
739 739 You can use :hg:`bookmark NAME` to set a bookmark on the working
740 740 directory's parent revision with the given name. If you specify
741 741 a revision using -r REV (where REV may be an existing bookmark),
742 742 the bookmark is assigned to that revision.
743 743
744 744 Bookmarks can be pushed and pulled between repositories (see :hg:`help
745 745 push` and :hg:`help pull`). This requires both the local and remote
746 746 repositories to support bookmarks. For versions prior to 1.8, this means
747 747 the bookmarks extension must be enabled.
748 748
749 749 With -i/--inactive, the new bookmark will not be made the active
750 750 bookmark. If -r/--rev is given, the new bookmark will not be made
751 751 active even if -i/--inactive is not given. If no NAME is given, the
752 752 current active bookmark will be marked inactive.
753 753 '''
754 754 hexfn = ui.debugflag and hex or short
755 755 marks = repo._bookmarks
756 756 cur = repo.changectx('.').node()
757 757
758 758 if delete:
759 759 if mark is None:
760 760 raise util.Abort(_("bookmark name required"))
761 761 if mark not in marks:
762 762 raise util.Abort(_("bookmark '%s' does not exist") % mark)
763 763 if mark == repo._bookmarkcurrent:
764 764 bookmarks.setcurrent(repo, None)
765 765 del marks[mark]
766 766 bookmarks.write(repo)
767 767 return
768 768
769 769 if rename:
770 770 if rename not in marks:
771 771 raise util.Abort(_("bookmark '%s' does not exist") % rename)
772 772 if mark in marks and not force:
773 773 raise util.Abort(_("bookmark '%s' already exists "
774 774 "(use -f to force)") % mark)
775 775 if mark is None:
776 776 raise util.Abort(_("new bookmark name required"))
777 777 marks[mark] = marks[rename]
778 778 if repo._bookmarkcurrent == rename and not inactive:
779 779 bookmarks.setcurrent(repo, mark)
780 780 del marks[rename]
781 781 bookmarks.write(repo)
782 782 return
783 783
784 784 if mark is not None:
785 785 if "\n" in mark:
786 786 raise util.Abort(_("bookmark name cannot contain newlines"))
787 787 mark = mark.strip()
788 788 if not mark:
789 789 raise util.Abort(_("bookmark names cannot consist entirely of "
790 790 "whitespace"))
791 791 if inactive and mark == repo._bookmarkcurrent:
792 792 bookmarks.setcurrent(repo, None)
793 793 return
794 794 if mark in marks and not force:
795 795 raise util.Abort(_("bookmark '%s' already exists "
796 796 "(use -f to force)") % mark)
797 797 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
798 798 and not force):
799 799 raise util.Abort(
800 800 _("a bookmark cannot have the name of an existing branch"))
801 801 if rev:
802 802 marks[mark] = repo.lookup(rev)
803 803 else:
804 804 marks[mark] = cur
805 805 if not inactive and cur == marks[mark]:
806 806 bookmarks.setcurrent(repo, mark)
807 807 bookmarks.write(repo)
808 808 return
809 809
810 810 if mark is None:
811 811 if rev:
812 812 raise util.Abort(_("bookmark name required"))
813 813 if len(marks) == 0:
814 814 ui.status(_("no bookmarks set\n"))
815 815 else:
816 816 for bmark, n in sorted(marks.iteritems()):
817 817 current = repo._bookmarkcurrent
818 818 if bmark == current and n == cur:
819 819 prefix, label = '*', 'bookmarks.current'
820 820 else:
821 821 prefix, label = ' ', ''
822 822
823 823 if ui.quiet:
824 824 ui.write("%s\n" % bmark, label=label)
825 825 else:
826 826 ui.write(" %s %-25s %d:%s\n" % (
827 827 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
828 828 label=label)
829 829 return
830 830
831 831 @command('branch',
832 832 [('f', 'force', None,
833 833 _('set branch name even if it shadows an existing branch')),
834 834 ('C', 'clean', None, _('reset branch name to parent branch name'))],
835 835 _('[-fC] [NAME]'))
836 836 def branch(ui, repo, label=None, **opts):
837 837 """set or show the current branch name
838 838
839 839 .. note::
840 840 Branch names are permanent and global. Use :hg:`bookmark` to create a
841 841 light-weight bookmark instead. See :hg:`help glossary` for more
842 842 information about named branches and bookmarks.
843 843
844 844 With no argument, show the current branch name. With one argument,
845 845 set the working directory branch name (the branch will not exist
846 846 in the repository until the next commit). Standard practice
847 847 recommends that primary development take place on the 'default'
848 848 branch.
849 849
850 850 Unless -f/--force is specified, branch will not let you set a
851 851 branch name that already exists, even if it's inactive.
852 852
853 853 Use -C/--clean to reset the working directory branch to that of
854 854 the parent of the working directory, negating a previous branch
855 855 change.
856 856
857 857 Use the command :hg:`update` to switch to an existing branch. Use
858 858 :hg:`commit --close-branch` to mark this branch as closed.
859 859
860 860 Returns 0 on success.
861 861 """
862 862
863 863 if opts.get('clean'):
864 864 label = repo[None].p1().branch()
865 865 repo.dirstate.setbranch(label)
866 866 ui.status(_('reset working directory to branch %s\n') % label)
867 867 elif label:
868 868 if not opts.get('force') and label in repo.branchtags():
869 869 if label not in [p.branch() for p in repo.parents()]:
870 870 raise util.Abort(_('a branch of the same name already exists'),
871 871 # i18n: "it" refers to an existing branch
872 872 hint=_("use 'hg update' to switch to it"))
873 873 repo.dirstate.setbranch(label)
874 874 ui.status(_('marked working directory as branch %s\n') % label)
875 875 ui.status(_('(branches are permanent and global, '
876 876 'did you want a bookmark?)\n'))
877 877 else:
878 878 ui.write("%s\n" % repo.dirstate.branch())
879 879
880 880 @command('branches',
881 881 [('a', 'active', False, _('show only branches that have unmerged heads')),
882 882 ('c', 'closed', False, _('show normal and closed branches'))],
883 883 _('[-ac]'))
884 884 def branches(ui, repo, active=False, closed=False):
885 885 """list repository named branches
886 886
887 887 List the repository's named branches, indicating which ones are
888 888 inactive. If -c/--closed is specified, also list branches which have
889 889 been marked closed (see :hg:`commit --close-branch`).
890 890
891 891 If -a/--active is specified, only show active branches. A branch
892 892 is considered active if it contains repository heads.
893 893
894 894 Use the command :hg:`update` to switch to an existing branch.
895 895
896 896 Returns 0.
897 897 """
898 898
899 899 hexfunc = ui.debugflag and hex or short
900 900 activebranches = [repo[n].branch() for n in repo.heads()]
901 901 def testactive(tag, node):
902 902 realhead = tag in activebranches
903 903 open = node in repo.branchheads(tag, closed=False)
904 904 return realhead and open
905 905 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
906 906 for tag, node in repo.branchtags().items()],
907 907 reverse=True)
908 908
909 909 for isactive, node, tag in branches:
910 910 if (not active) or isactive:
911 911 if ui.quiet:
912 912 ui.write("%s\n" % tag)
913 913 else:
914 914 hn = repo.lookup(node)
915 915 if isactive:
916 916 label = 'branches.active'
917 917 notice = ''
918 918 elif hn not in repo.branchheads(tag, closed=False):
919 919 if not closed:
920 920 continue
921 921 label = 'branches.closed'
922 922 notice = _(' (closed)')
923 923 else:
924 924 label = 'branches.inactive'
925 925 notice = _(' (inactive)')
926 926 if tag == repo.dirstate.branch():
927 927 label = 'branches.current'
928 928 rev = str(node).rjust(31 - encoding.colwidth(tag))
929 929 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
930 930 tag = ui.label(tag, label)
931 931 ui.write("%s %s%s\n" % (tag, rev, notice))
932 932
933 933 @command('bundle',
934 934 [('f', 'force', None, _('run even when the destination is unrelated')),
935 935 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
936 936 _('REV')),
937 937 ('b', 'branch', [], _('a specific branch you would like to bundle'),
938 938 _('BRANCH')),
939 939 ('', 'base', [],
940 940 _('a base changeset assumed to be available at the destination'),
941 941 _('REV')),
942 942 ('a', 'all', None, _('bundle all changesets in the repository')),
943 943 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
944 944 ] + remoteopts,
945 945 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
946 946 def bundle(ui, repo, fname, dest=None, **opts):
947 947 """create a changegroup file
948 948
949 949 Generate a compressed changegroup file collecting changesets not
950 950 known to be in another repository.
951 951
952 952 If you omit the destination repository, then hg assumes the
953 953 destination will have all the nodes you specify with --base
954 954 parameters. To create a bundle containing all changesets, use
955 955 -a/--all (or --base null).
956 956
957 957 You can change compression method with the -t/--type option.
958 958 The available compression methods are: none, bzip2, and
959 959 gzip (by default, bundles are compressed using bzip2).
960 960
961 961 The bundle file can then be transferred using conventional means
962 962 and applied to another repository with the unbundle or pull
963 963 command. This is useful when direct push and pull are not
964 964 available or when exporting an entire repository is undesirable.
965 965
966 966 Applying bundles preserves all changeset contents including
967 967 permissions, copy/rename information, and revision history.
968 968
969 969 Returns 0 on success, 1 if no changes found.
970 970 """
971 971 revs = None
972 972 if 'rev' in opts:
973 973 revs = scmutil.revrange(repo, opts['rev'])
974 974
975 975 bundletype = opts.get('type', 'bzip2').lower()
976 976 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
977 977 bundletype = btypes.get(bundletype)
978 978 if bundletype not in changegroup.bundletypes:
979 979 raise util.Abort(_('unknown bundle type specified with --type'))
980 980
981 981 if opts.get('all'):
982 982 base = ['null']
983 983 else:
984 984 base = scmutil.revrange(repo, opts.get('base'))
985 985 if base:
986 986 if dest:
987 987 raise util.Abort(_("--base is incompatible with specifying "
988 988 "a destination"))
989 989 common = [repo.lookup(rev) for rev in base]
990 990 heads = revs and map(repo.lookup, revs) or revs
991 991 cg = repo.getbundle('bundle', heads=heads, common=common)
992 992 outgoing = None
993 993 else:
994 994 dest = ui.expandpath(dest or 'default-push', dest or 'default')
995 995 dest, branches = hg.parseurl(dest, opts.get('branch'))
996 996 other = hg.peer(repo, opts, dest)
997 997 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
998 998 heads = revs and map(repo.lookup, revs) or revs
999 999 outgoing = discovery.findcommonoutgoing(repo, other,
1000 1000 onlyheads=heads,
1001 1001 force=opts.get('force'))
1002 1002 cg = repo.getlocalbundle('bundle', outgoing)
1003 1003 if not cg:
1004 1004 scmutil.nochangesfound(ui, outgoing and outgoing.excluded)
1005 1005 return 1
1006 1006
1007 1007 changegroup.writebundle(cg, fname, bundletype)
1008 1008
1009 1009 @command('cat',
1010 1010 [('o', 'output', '',
1011 1011 _('print output to file with formatted name'), _('FORMAT')),
1012 1012 ('r', 'rev', '', _('print the given revision'), _('REV')),
1013 1013 ('', 'decode', None, _('apply any matching decode filter')),
1014 1014 ] + walkopts,
1015 1015 _('[OPTION]... FILE...'))
1016 1016 def cat(ui, repo, file1, *pats, **opts):
1017 1017 """output the current or given revision of files
1018 1018
1019 1019 Print the specified files as they were at the given revision. If
1020 1020 no revision is given, the parent of the working directory is used,
1021 1021 or tip if no revision is checked out.
1022 1022
1023 1023 Output may be to a file, in which case the name of the file is
1024 1024 given using a format string. The formatting rules are the same as
1025 1025 for the export command, with the following additions:
1026 1026
1027 1027 :``%s``: basename of file being printed
1028 1028 :``%d``: dirname of file being printed, or '.' if in repository root
1029 1029 :``%p``: root-relative path name of file being printed
1030 1030
1031 1031 Returns 0 on success.
1032 1032 """
1033 1033 ctx = scmutil.revsingle(repo, opts.get('rev'))
1034 1034 err = 1
1035 1035 m = scmutil.match(ctx, (file1,) + pats, opts)
1036 1036 for abs in ctx.walk(m):
1037 1037 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1038 1038 pathname=abs)
1039 1039 data = ctx[abs].data()
1040 1040 if opts.get('decode'):
1041 1041 data = repo.wwritedata(abs, data)
1042 1042 fp.write(data)
1043 1043 fp.close()
1044 1044 err = 0
1045 1045 return err
1046 1046
1047 1047 @command('^clone',
1048 1048 [('U', 'noupdate', None,
1049 1049 _('the clone will include an empty working copy (only a repository)')),
1050 1050 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1051 1051 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1052 1052 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1053 1053 ('', 'pull', None, _('use pull protocol to copy metadata')),
1054 1054 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1055 1055 ] + remoteopts,
1056 1056 _('[OPTION]... SOURCE [DEST]'))
1057 1057 def clone(ui, source, dest=None, **opts):
1058 1058 """make a copy of an existing repository
1059 1059
1060 1060 Create a copy of an existing repository in a new directory.
1061 1061
1062 1062 If no destination directory name is specified, it defaults to the
1063 1063 basename of the source.
1064 1064
1065 1065 The location of the source is added to the new repository's
1066 1066 ``.hg/hgrc`` file, as the default to be used for future pulls.
1067 1067
1068 1068 Only local paths and ``ssh://`` URLs are supported as
1069 1069 destinations. For ``ssh://`` destinations, no working directory or
1070 1070 ``.hg/hgrc`` will be created on the remote side.
1071 1071
1072 1072 To pull only a subset of changesets, specify one or more revisions
1073 1073 identifiers with -r/--rev or branches with -b/--branch. The
1074 1074 resulting clone will contain only the specified changesets and
1075 1075 their ancestors. These options (or 'clone src#rev dest') imply
1076 1076 --pull, even for local source repositories. Note that specifying a
1077 1077 tag will include the tagged changeset but not the changeset
1078 1078 containing the tag.
1079 1079
1080 1080 To check out a particular version, use -u/--update, or
1081 1081 -U/--noupdate to create a clone with no working directory.
1082 1082
1083 1083 .. container:: verbose
1084 1084
1085 1085 For efficiency, hardlinks are used for cloning whenever the
1086 1086 source and destination are on the same filesystem (note this
1087 1087 applies only to the repository data, not to the working
1088 1088 directory). Some filesystems, such as AFS, implement hardlinking
1089 1089 incorrectly, but do not report errors. In these cases, use the
1090 1090 --pull option to avoid hardlinking.
1091 1091
1092 1092 In some cases, you can clone repositories and the working
1093 1093 directory using full hardlinks with ::
1094 1094
1095 1095 $ cp -al REPO REPOCLONE
1096 1096
1097 1097 This is the fastest way to clone, but it is not always safe. The
1098 1098 operation is not atomic (making sure REPO is not modified during
1099 1099 the operation is up to you) and you have to make sure your
1100 1100 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1101 1101 so). Also, this is not compatible with certain extensions that
1102 1102 place their metadata under the .hg directory, such as mq.
1103 1103
1104 1104 Mercurial will update the working directory to the first applicable
1105 1105 revision from this list:
1106 1106
1107 1107 a) null if -U or the source repository has no changesets
1108 1108 b) if -u . and the source repository is local, the first parent of
1109 1109 the source repository's working directory
1110 1110 c) the changeset specified with -u (if a branch name, this means the
1111 1111 latest head of that branch)
1112 1112 d) the changeset specified with -r
1113 1113 e) the tipmost head specified with -b
1114 1114 f) the tipmost head specified with the url#branch source syntax
1115 1115 g) the tipmost head of the default branch
1116 1116 h) tip
1117 1117
1118 1118 Examples:
1119 1119
1120 1120 - clone a remote repository to a new directory named hg/::
1121 1121
1122 1122 hg clone http://selenic.com/hg
1123 1123
1124 1124 - create a lightweight local clone::
1125 1125
1126 1126 hg clone project/ project-feature/
1127 1127
1128 1128 - clone from an absolute path on an ssh server (note double-slash)::
1129 1129
1130 1130 hg clone ssh://user@server//home/projects/alpha/
1131 1131
1132 1132 - do a high-speed clone over a LAN while checking out a
1133 1133 specified version::
1134 1134
1135 1135 hg clone --uncompressed http://server/repo -u 1.5
1136 1136
1137 1137 - create a repository without changesets after a particular revision::
1138 1138
1139 1139 hg clone -r 04e544 experimental/ good/
1140 1140
1141 1141 - clone (and track) a particular named branch::
1142 1142
1143 1143 hg clone http://selenic.com/hg#stable
1144 1144
1145 1145 See :hg:`help urls` for details on specifying URLs.
1146 1146
1147 1147 Returns 0 on success.
1148 1148 """
1149 1149 if opts.get('noupdate') and opts.get('updaterev'):
1150 1150 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1151 1151
1152 1152 r = hg.clone(ui, opts, source, dest,
1153 1153 pull=opts.get('pull'),
1154 1154 stream=opts.get('uncompressed'),
1155 1155 rev=opts.get('rev'),
1156 1156 update=opts.get('updaterev') or not opts.get('noupdate'),
1157 1157 branch=opts.get('branch'))
1158 1158
1159 1159 return r is None
1160 1160
1161 1161 @command('^commit|ci',
1162 1162 [('A', 'addremove', None,
1163 1163 _('mark new/missing files as added/removed before committing')),
1164 1164 ('', 'close-branch', None,
1165 1165 _('mark a branch as closed, hiding it from the branch list')),
1166 ('', 'amend', None, _('amend the parent of the working dir')),
1166 1167 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1167 1168 _('[OPTION]... [FILE]...'))
1168 1169 def commit(ui, repo, *pats, **opts):
1169 1170 """commit the specified files or all outstanding changes
1170 1171
1171 1172 Commit changes to the given files into the repository. Unlike a
1172 1173 centralized SCM, this operation is a local operation. See
1173 1174 :hg:`push` for a way to actively distribute your changes.
1174 1175
1175 1176 If a list of files is omitted, all changes reported by :hg:`status`
1176 1177 will be committed.
1177 1178
1178 1179 If you are committing the result of a merge, do not provide any
1179 1180 filenames or -I/-X filters.
1180 1181
1181 1182 If no commit message is specified, Mercurial starts your
1182 1183 configured editor where you can enter a message. In case your
1183 1184 commit fails, you will find a backup of your message in
1184 1185 ``.hg/last-message.txt``.
1185 1186
1187 The --amend flag can be used to amend the parent of the
1188 working directory with a new commit that contains the changes
1189 in the parent in addition to those currently reported by :hg:`status`,
1190 if there are any. The old commit is stored in a backup bundle in
1191 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1192 on how to restore it).
1193
1194 Message, user and date are taken from the amended commit unless
1195 specified. When a message isn't specified on the command line,
1196 the editor will open with the message of the amended commit.
1197
1198 It is not possible to amend public changesets (see :hg:`help phases`)
1199 or changesets that have children.
1200
1186 1201 See :hg:`help dates` for a list of formats valid for -d/--date.
1187 1202
1188 1203 Returns 0 on success, 1 if nothing changed.
1189 1204 """
1190 1205 if opts.get('subrepos'):
1191 1206 # Let --subrepos on the command line overide config setting.
1192 1207 ui.setconfig('ui', 'commitsubrepos', True)
1193 1208
1194 1209 extra = {}
1195 1210 if opts.get('close_branch'):
1196 1211 if repo['.'].node() not in repo.branchheads():
1197 1212 # The topo heads set is included in the branch heads set of the
1198 1213 # current branch, so it's sufficient to test branchheads
1199 1214 raise util.Abort(_('can only close branch heads'))
1200 1215 extra['close'] = 1
1201 e = cmdutil.commiteditor
1202 if opts.get('force_editor'):
1203 e = cmdutil.commitforceeditor
1204
1205 def commitfunc(ui, repo, message, match, opts):
1206 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1207 editor=e, extra=extra)
1208 1216
1209 1217 branch = repo[None].branch()
1210 1218 bheads = repo.branchheads(branch)
1211 1219
1212 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1213 if not node:
1214 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1215 if stat[3]:
1216 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1217 % len(stat[3]))
1218 else:
1220 if opts.get('amend'):
1221 if ui.config('ui', 'commitsubrepos'):
1222 raise util.Abort(_('cannot amend recursively'))
1223
1224 old = repo['.']
1225 if old.phase() == phases.public:
1226 raise util.Abort(_('cannot amend public changesets'))
1227 if len(old.parents()) > 1:
1228 raise util.Abort(_('cannot amend merge changesets'))
1229 if len(repo[None].parents()) > 1:
1230 raise util.Abort(_('cannot amend while merging'))
1231 if old.children():
1232 raise util.Abort(_('cannot amend changeset with children'))
1233
1234 e = cmdutil.commiteditor
1235 if opts.get('force_editor'):
1236 e = cmdutil.commitforceeditor
1237
1238 def commitfunc(ui, repo, message, match, opts):
1239 editor = e
1240 # message contains text from -m or -l, if it's empty,
1241 # open the editor with the old message
1242 if not message:
1243 message = old.description()
1244 editor = cmdutil.commitforceeditor
1245 return repo.commit(message,
1246 opts.get('user') or old.user(),
1247 opts.get('date') or old.date(),
1248 match,
1249 editor=editor,
1250 extra=extra)
1251
1252 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1253 if node == old.node():
1219 1254 ui.status(_("nothing changed\n"))
1220 return 1
1255 return 1
1256 else:
1257 e = cmdutil.commiteditor
1258 if opts.get('force_editor'):
1259 e = cmdutil.commitforceeditor
1260
1261 def commitfunc(ui, repo, message, match, opts):
1262 return repo.commit(message, opts.get('user'), opts.get('date'),
1263 match, editor=e, extra=extra)
1264
1265 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1266
1267 if not node:
1268 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1269 if stat[3]:
1270 ui.status(_("nothing changed (%d missing files, see "
1271 "'hg status')\n") % len(stat[3]))
1272 else:
1273 ui.status(_("nothing changed\n"))
1274 return 1
1221 1275
1222 1276 ctx = repo[node]
1223 1277 parents = ctx.parents()
1224 1278
1225 if (bheads and node not in bheads and not
1279 if (not opts.get('amend') and bheads and node not in bheads and not
1226 1280 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1227 1281 ui.status(_('created new head\n'))
1228 1282 # The message is not printed for initial roots. For the other
1229 1283 # changesets, it is printed in the following situations:
1230 1284 #
1231 1285 # Par column: for the 2 parents with ...
1232 1286 # N: null or no parent
1233 1287 # B: parent is on another named branch
1234 1288 # C: parent is a regular non head changeset
1235 1289 # H: parent was a branch head of the current branch
1236 1290 # Msg column: whether we print "created new head" message
1237 1291 # In the following, it is assumed that there already exists some
1238 1292 # initial branch heads of the current branch, otherwise nothing is
1239 1293 # printed anyway.
1240 1294 #
1241 1295 # Par Msg Comment
1242 1296 # NN y additional topo root
1243 1297 #
1244 1298 # BN y additional branch root
1245 1299 # CN y additional topo head
1246 1300 # HN n usual case
1247 1301 #
1248 1302 # BB y weird additional branch root
1249 1303 # CB y branch merge
1250 1304 # HB n merge with named branch
1251 1305 #
1252 1306 # CC y additional head from merge
1253 1307 # CH n merge with a head
1254 1308 #
1255 1309 # HH n head merge: head count decreases
1256 1310
1257 1311 if not opts.get('close_branch'):
1258 1312 for r in parents:
1259 1313 if r.extra().get('close') and r.branch() == branch:
1260 1314 ui.status(_('reopening closed branch head %d\n') % r)
1261 1315
1262 1316 if ui.debugflag:
1263 1317 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1264 1318 elif ui.verbose:
1265 1319 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1266 1320
1267 1321 @command('copy|cp',
1268 1322 [('A', 'after', None, _('record a copy that has already occurred')),
1269 1323 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1270 1324 ] + walkopts + dryrunopts,
1271 1325 _('[OPTION]... [SOURCE]... DEST'))
1272 1326 def copy(ui, repo, *pats, **opts):
1273 1327 """mark files as copied for the next commit
1274 1328
1275 1329 Mark dest as having copies of source files. If dest is a
1276 1330 directory, copies are put in that directory. If dest is a file,
1277 1331 the source must be a single file.
1278 1332
1279 1333 By default, this command copies the contents of files as they
1280 1334 exist in the working directory. If invoked with -A/--after, the
1281 1335 operation is recorded, but no copying is performed.
1282 1336
1283 1337 This command takes effect with the next commit. To undo a copy
1284 1338 before that, see :hg:`revert`.
1285 1339
1286 1340 Returns 0 on success, 1 if errors are encountered.
1287 1341 """
1288 1342 wlock = repo.wlock(False)
1289 1343 try:
1290 1344 return cmdutil.copy(ui, repo, pats, opts)
1291 1345 finally:
1292 1346 wlock.release()
1293 1347
1294 1348 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1295 1349 def debugancestor(ui, repo, *args):
1296 1350 """find the ancestor revision of two revisions in a given index"""
1297 1351 if len(args) == 3:
1298 1352 index, rev1, rev2 = args
1299 1353 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1300 1354 lookup = r.lookup
1301 1355 elif len(args) == 2:
1302 1356 if not repo:
1303 1357 raise util.Abort(_("there is no Mercurial repository here "
1304 1358 "(.hg not found)"))
1305 1359 rev1, rev2 = args
1306 1360 r = repo.changelog
1307 1361 lookup = repo.lookup
1308 1362 else:
1309 1363 raise util.Abort(_('either two or three arguments required'))
1310 1364 a = r.ancestor(lookup(rev1), lookup(rev2))
1311 1365 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1312 1366
1313 1367 @command('debugbuilddag',
1314 1368 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1315 1369 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1316 1370 ('n', 'new-file', None, _('add new file at each rev'))],
1317 1371 _('[OPTION]... [TEXT]'))
1318 1372 def debugbuilddag(ui, repo, text=None,
1319 1373 mergeable_file=False,
1320 1374 overwritten_file=False,
1321 1375 new_file=False):
1322 1376 """builds a repo with a given DAG from scratch in the current empty repo
1323 1377
1324 1378 The description of the DAG is read from stdin if not given on the
1325 1379 command line.
1326 1380
1327 1381 Elements:
1328 1382
1329 1383 - "+n" is a linear run of n nodes based on the current default parent
1330 1384 - "." is a single node based on the current default parent
1331 1385 - "$" resets the default parent to null (implied at the start);
1332 1386 otherwise the default parent is always the last node created
1333 1387 - "<p" sets the default parent to the backref p
1334 1388 - "*p" is a fork at parent p, which is a backref
1335 1389 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1336 1390 - "/p2" is a merge of the preceding node and p2
1337 1391 - ":tag" defines a local tag for the preceding node
1338 1392 - "@branch" sets the named branch for subsequent nodes
1339 1393 - "#...\\n" is a comment up to the end of the line
1340 1394
1341 1395 Whitespace between the above elements is ignored.
1342 1396
1343 1397 A backref is either
1344 1398
1345 1399 - a number n, which references the node curr-n, where curr is the current
1346 1400 node, or
1347 1401 - the name of a local tag you placed earlier using ":tag", or
1348 1402 - empty to denote the default parent.
1349 1403
1350 1404 All string valued-elements are either strictly alphanumeric, or must
1351 1405 be enclosed in double quotes ("..."), with "\\" as escape character.
1352 1406 """
1353 1407
1354 1408 if text is None:
1355 1409 ui.status(_("reading DAG from stdin\n"))
1356 1410 text = ui.fin.read()
1357 1411
1358 1412 cl = repo.changelog
1359 1413 if len(cl) > 0:
1360 1414 raise util.Abort(_('repository is not empty'))
1361 1415
1362 1416 # determine number of revs in DAG
1363 1417 total = 0
1364 1418 for type, data in dagparser.parsedag(text):
1365 1419 if type == 'n':
1366 1420 total += 1
1367 1421
1368 1422 if mergeable_file:
1369 1423 linesperrev = 2
1370 1424 # make a file with k lines per rev
1371 1425 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1372 1426 initialmergedlines.append("")
1373 1427
1374 1428 tags = []
1375 1429
1376 1430 lock = tr = None
1377 1431 try:
1378 1432 lock = repo.lock()
1379 1433 tr = repo.transaction("builddag")
1380 1434
1381 1435 at = -1
1382 1436 atbranch = 'default'
1383 1437 nodeids = []
1384 1438 id = 0
1385 1439 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1386 1440 for type, data in dagparser.parsedag(text):
1387 1441 if type == 'n':
1388 1442 ui.note('node %s\n' % str(data))
1389 1443 id, ps = data
1390 1444
1391 1445 files = []
1392 1446 fctxs = {}
1393 1447
1394 1448 p2 = None
1395 1449 if mergeable_file:
1396 1450 fn = "mf"
1397 1451 p1 = repo[ps[0]]
1398 1452 if len(ps) > 1:
1399 1453 p2 = repo[ps[1]]
1400 1454 pa = p1.ancestor(p2)
1401 1455 base, local, other = [x[fn].data() for x in pa, p1, p2]
1402 1456 m3 = simplemerge.Merge3Text(base, local, other)
1403 1457 ml = [l.strip() for l in m3.merge_lines()]
1404 1458 ml.append("")
1405 1459 elif at > 0:
1406 1460 ml = p1[fn].data().split("\n")
1407 1461 else:
1408 1462 ml = initialmergedlines
1409 1463 ml[id * linesperrev] += " r%i" % id
1410 1464 mergedtext = "\n".join(ml)
1411 1465 files.append(fn)
1412 1466 fctxs[fn] = context.memfilectx(fn, mergedtext)
1413 1467
1414 1468 if overwritten_file:
1415 1469 fn = "of"
1416 1470 files.append(fn)
1417 1471 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1418 1472
1419 1473 if new_file:
1420 1474 fn = "nf%i" % id
1421 1475 files.append(fn)
1422 1476 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1423 1477 if len(ps) > 1:
1424 1478 if not p2:
1425 1479 p2 = repo[ps[1]]
1426 1480 for fn in p2:
1427 1481 if fn.startswith("nf"):
1428 1482 files.append(fn)
1429 1483 fctxs[fn] = p2[fn]
1430 1484
1431 1485 def fctxfn(repo, cx, path):
1432 1486 return fctxs.get(path)
1433 1487
1434 1488 if len(ps) == 0 or ps[0] < 0:
1435 1489 pars = [None, None]
1436 1490 elif len(ps) == 1:
1437 1491 pars = [nodeids[ps[0]], None]
1438 1492 else:
1439 1493 pars = [nodeids[p] for p in ps]
1440 1494 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1441 1495 date=(id, 0),
1442 1496 user="debugbuilddag",
1443 1497 extra={'branch': atbranch})
1444 1498 nodeid = repo.commitctx(cx)
1445 1499 nodeids.append(nodeid)
1446 1500 at = id
1447 1501 elif type == 'l':
1448 1502 id, name = data
1449 1503 ui.note('tag %s\n' % name)
1450 1504 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1451 1505 elif type == 'a':
1452 1506 ui.note('branch %s\n' % data)
1453 1507 atbranch = data
1454 1508 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1455 1509 tr.close()
1456 1510
1457 1511 if tags:
1458 1512 repo.opener.write("localtags", "".join(tags))
1459 1513 finally:
1460 1514 ui.progress(_('building'), None)
1461 1515 release(tr, lock)
1462 1516
1463 1517 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1464 1518 def debugbundle(ui, bundlepath, all=None, **opts):
1465 1519 """lists the contents of a bundle"""
1466 1520 f = url.open(ui, bundlepath)
1467 1521 try:
1468 1522 gen = changegroup.readbundle(f, bundlepath)
1469 1523 if all:
1470 1524 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1471 1525
1472 1526 def showchunks(named):
1473 1527 ui.write("\n%s\n" % named)
1474 1528 chain = None
1475 1529 while True:
1476 1530 chunkdata = gen.deltachunk(chain)
1477 1531 if not chunkdata:
1478 1532 break
1479 1533 node = chunkdata['node']
1480 1534 p1 = chunkdata['p1']
1481 1535 p2 = chunkdata['p2']
1482 1536 cs = chunkdata['cs']
1483 1537 deltabase = chunkdata['deltabase']
1484 1538 delta = chunkdata['delta']
1485 1539 ui.write("%s %s %s %s %s %s\n" %
1486 1540 (hex(node), hex(p1), hex(p2),
1487 1541 hex(cs), hex(deltabase), len(delta)))
1488 1542 chain = node
1489 1543
1490 1544 chunkdata = gen.changelogheader()
1491 1545 showchunks("changelog")
1492 1546 chunkdata = gen.manifestheader()
1493 1547 showchunks("manifest")
1494 1548 while True:
1495 1549 chunkdata = gen.filelogheader()
1496 1550 if not chunkdata:
1497 1551 break
1498 1552 fname = chunkdata['filename']
1499 1553 showchunks(fname)
1500 1554 else:
1501 1555 chunkdata = gen.changelogheader()
1502 1556 chain = None
1503 1557 while True:
1504 1558 chunkdata = gen.deltachunk(chain)
1505 1559 if not chunkdata:
1506 1560 break
1507 1561 node = chunkdata['node']
1508 1562 ui.write("%s\n" % hex(node))
1509 1563 chain = node
1510 1564 finally:
1511 1565 f.close()
1512 1566
1513 1567 @command('debugcheckstate', [], '')
1514 1568 def debugcheckstate(ui, repo):
1515 1569 """validate the correctness of the current dirstate"""
1516 1570 parent1, parent2 = repo.dirstate.parents()
1517 1571 m1 = repo[parent1].manifest()
1518 1572 m2 = repo[parent2].manifest()
1519 1573 errors = 0
1520 1574 for f in repo.dirstate:
1521 1575 state = repo.dirstate[f]
1522 1576 if state in "nr" and f not in m1:
1523 1577 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1524 1578 errors += 1
1525 1579 if state in "a" and f in m1:
1526 1580 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1527 1581 errors += 1
1528 1582 if state in "m" and f not in m1 and f not in m2:
1529 1583 ui.warn(_("%s in state %s, but not in either manifest\n") %
1530 1584 (f, state))
1531 1585 errors += 1
1532 1586 for f in m1:
1533 1587 state = repo.dirstate[f]
1534 1588 if state not in "nrm":
1535 1589 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1536 1590 errors += 1
1537 1591 if errors:
1538 1592 error = _(".hg/dirstate inconsistent with current parent's manifest")
1539 1593 raise util.Abort(error)
1540 1594
1541 1595 @command('debugcommands', [], _('[COMMAND]'))
1542 1596 def debugcommands(ui, cmd='', *args):
1543 1597 """list all available commands and options"""
1544 1598 for cmd, vals in sorted(table.iteritems()):
1545 1599 cmd = cmd.split('|')[0].strip('^')
1546 1600 opts = ', '.join([i[1] for i in vals[1]])
1547 1601 ui.write('%s: %s\n' % (cmd, opts))
1548 1602
1549 1603 @command('debugcomplete',
1550 1604 [('o', 'options', None, _('show the command options'))],
1551 1605 _('[-o] CMD'))
1552 1606 def debugcomplete(ui, cmd='', **opts):
1553 1607 """returns the completion list associated with the given command"""
1554 1608
1555 1609 if opts.get('options'):
1556 1610 options = []
1557 1611 otables = [globalopts]
1558 1612 if cmd:
1559 1613 aliases, entry = cmdutil.findcmd(cmd, table, False)
1560 1614 otables.append(entry[1])
1561 1615 for t in otables:
1562 1616 for o in t:
1563 1617 if "(DEPRECATED)" in o[3]:
1564 1618 continue
1565 1619 if o[0]:
1566 1620 options.append('-%s' % o[0])
1567 1621 options.append('--%s' % o[1])
1568 1622 ui.write("%s\n" % "\n".join(options))
1569 1623 return
1570 1624
1571 1625 cmdlist = cmdutil.findpossible(cmd, table)
1572 1626 if ui.verbose:
1573 1627 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1574 1628 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1575 1629
1576 1630 @command('debugdag',
1577 1631 [('t', 'tags', None, _('use tags as labels')),
1578 1632 ('b', 'branches', None, _('annotate with branch names')),
1579 1633 ('', 'dots', None, _('use dots for runs')),
1580 1634 ('s', 'spaces', None, _('separate elements by spaces'))],
1581 1635 _('[OPTION]... [FILE [REV]...]'))
1582 1636 def debugdag(ui, repo, file_=None, *revs, **opts):
1583 1637 """format the changelog or an index DAG as a concise textual description
1584 1638
1585 1639 If you pass a revlog index, the revlog's DAG is emitted. If you list
1586 1640 revision numbers, they get labelled in the output as rN.
1587 1641
1588 1642 Otherwise, the changelog DAG of the current repo is emitted.
1589 1643 """
1590 1644 spaces = opts.get('spaces')
1591 1645 dots = opts.get('dots')
1592 1646 if file_:
1593 1647 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1594 1648 revs = set((int(r) for r in revs))
1595 1649 def events():
1596 1650 for r in rlog:
1597 1651 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1598 1652 if r in revs:
1599 1653 yield 'l', (r, "r%i" % r)
1600 1654 elif repo:
1601 1655 cl = repo.changelog
1602 1656 tags = opts.get('tags')
1603 1657 branches = opts.get('branches')
1604 1658 if tags:
1605 1659 labels = {}
1606 1660 for l, n in repo.tags().items():
1607 1661 labels.setdefault(cl.rev(n), []).append(l)
1608 1662 def events():
1609 1663 b = "default"
1610 1664 for r in cl:
1611 1665 if branches:
1612 1666 newb = cl.read(cl.node(r))[5]['branch']
1613 1667 if newb != b:
1614 1668 yield 'a', newb
1615 1669 b = newb
1616 1670 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1617 1671 if tags:
1618 1672 ls = labels.get(r)
1619 1673 if ls:
1620 1674 for l in ls:
1621 1675 yield 'l', (r, l)
1622 1676 else:
1623 1677 raise util.Abort(_('need repo for changelog dag'))
1624 1678
1625 1679 for line in dagparser.dagtextlines(events(),
1626 1680 addspaces=spaces,
1627 1681 wraplabels=True,
1628 1682 wrapannotations=True,
1629 1683 wrapnonlinear=dots,
1630 1684 usedots=dots,
1631 1685 maxlinewidth=70):
1632 1686 ui.write(line)
1633 1687 ui.write("\n")
1634 1688
1635 1689 @command('debugdata',
1636 1690 [('c', 'changelog', False, _('open changelog')),
1637 1691 ('m', 'manifest', False, _('open manifest'))],
1638 1692 _('-c|-m|FILE REV'))
1639 1693 def debugdata(ui, repo, file_, rev = None, **opts):
1640 1694 """dump the contents of a data file revision"""
1641 1695 if opts.get('changelog') or opts.get('manifest'):
1642 1696 file_, rev = None, file_
1643 1697 elif rev is None:
1644 1698 raise error.CommandError('debugdata', _('invalid arguments'))
1645 1699 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1646 1700 try:
1647 1701 ui.write(r.revision(r.lookup(rev)))
1648 1702 except KeyError:
1649 1703 raise util.Abort(_('invalid revision identifier %s') % rev)
1650 1704
1651 1705 @command('debugdate',
1652 1706 [('e', 'extended', None, _('try extended date formats'))],
1653 1707 _('[-e] DATE [RANGE]'))
1654 1708 def debugdate(ui, date, range=None, **opts):
1655 1709 """parse and display a date"""
1656 1710 if opts["extended"]:
1657 1711 d = util.parsedate(date, util.extendeddateformats)
1658 1712 else:
1659 1713 d = util.parsedate(date)
1660 1714 ui.write("internal: %s %s\n" % d)
1661 1715 ui.write("standard: %s\n" % util.datestr(d))
1662 1716 if range:
1663 1717 m = util.matchdate(range)
1664 1718 ui.write("match: %s\n" % m(d[0]))
1665 1719
1666 1720 @command('debugdiscovery',
1667 1721 [('', 'old', None, _('use old-style discovery')),
1668 1722 ('', 'nonheads', None,
1669 1723 _('use old-style discovery with non-heads included')),
1670 1724 ] + remoteopts,
1671 1725 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1672 1726 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1673 1727 """runs the changeset discovery protocol in isolation"""
1674 1728 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1675 1729 remote = hg.peer(repo, opts, remoteurl)
1676 1730 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1677 1731
1678 1732 # make sure tests are repeatable
1679 1733 random.seed(12323)
1680 1734
1681 1735 def doit(localheads, remoteheads):
1682 1736 if opts.get('old'):
1683 1737 if localheads:
1684 1738 raise util.Abort('cannot use localheads with old style discovery')
1685 1739 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1686 1740 force=True)
1687 1741 common = set(common)
1688 1742 if not opts.get('nonheads'):
1689 1743 ui.write("unpruned common: %s\n" % " ".join([short(n)
1690 1744 for n in common]))
1691 1745 dag = dagutil.revlogdag(repo.changelog)
1692 1746 all = dag.ancestorset(dag.internalizeall(common))
1693 1747 common = dag.externalizeall(dag.headsetofconnecteds(all))
1694 1748 else:
1695 1749 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1696 1750 common = set(common)
1697 1751 rheads = set(hds)
1698 1752 lheads = set(repo.heads())
1699 1753 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1700 1754 if lheads <= common:
1701 1755 ui.write("local is subset\n")
1702 1756 elif rheads <= common:
1703 1757 ui.write("remote is subset\n")
1704 1758
1705 1759 serverlogs = opts.get('serverlog')
1706 1760 if serverlogs:
1707 1761 for filename in serverlogs:
1708 1762 logfile = open(filename, 'r')
1709 1763 try:
1710 1764 line = logfile.readline()
1711 1765 while line:
1712 1766 parts = line.strip().split(';')
1713 1767 op = parts[1]
1714 1768 if op == 'cg':
1715 1769 pass
1716 1770 elif op == 'cgss':
1717 1771 doit(parts[2].split(' '), parts[3].split(' '))
1718 1772 elif op == 'unb':
1719 1773 doit(parts[3].split(' '), parts[2].split(' '))
1720 1774 line = logfile.readline()
1721 1775 finally:
1722 1776 logfile.close()
1723 1777
1724 1778 else:
1725 1779 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1726 1780 opts.get('remote_head'))
1727 1781 localrevs = opts.get('local_head')
1728 1782 doit(localrevs, remoterevs)
1729 1783
1730 1784 @command('debugfileset', [], ('REVSPEC'))
1731 1785 def debugfileset(ui, repo, expr):
1732 1786 '''parse and apply a fileset specification'''
1733 1787 if ui.verbose:
1734 1788 tree = fileset.parse(expr)[0]
1735 1789 ui.note(tree, "\n")
1736 1790
1737 1791 for f in fileset.getfileset(repo[None], expr):
1738 1792 ui.write("%s\n" % f)
1739 1793
1740 1794 @command('debugfsinfo', [], _('[PATH]'))
1741 1795 def debugfsinfo(ui, path = "."):
1742 1796 """show information detected about current filesystem"""
1743 1797 util.writefile('.debugfsinfo', '')
1744 1798 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1745 1799 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1746 1800 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1747 1801 and 'yes' or 'no'))
1748 1802 os.unlink('.debugfsinfo')
1749 1803
1750 1804 @command('debuggetbundle',
1751 1805 [('H', 'head', [], _('id of head node'), _('ID')),
1752 1806 ('C', 'common', [], _('id of common node'), _('ID')),
1753 1807 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1754 1808 _('REPO FILE [-H|-C ID]...'))
1755 1809 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1756 1810 """retrieves a bundle from a repo
1757 1811
1758 1812 Every ID must be a full-length hex node id string. Saves the bundle to the
1759 1813 given file.
1760 1814 """
1761 1815 repo = hg.peer(ui, opts, repopath)
1762 1816 if not repo.capable('getbundle'):
1763 1817 raise util.Abort("getbundle() not supported by target repository")
1764 1818 args = {}
1765 1819 if common:
1766 1820 args['common'] = [bin(s) for s in common]
1767 1821 if head:
1768 1822 args['heads'] = [bin(s) for s in head]
1769 1823 bundle = repo.getbundle('debug', **args)
1770 1824
1771 1825 bundletype = opts.get('type', 'bzip2').lower()
1772 1826 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1773 1827 bundletype = btypes.get(bundletype)
1774 1828 if bundletype not in changegroup.bundletypes:
1775 1829 raise util.Abort(_('unknown bundle type specified with --type'))
1776 1830 changegroup.writebundle(bundle, bundlepath, bundletype)
1777 1831
1778 1832 @command('debugignore', [], '')
1779 1833 def debugignore(ui, repo, *values, **opts):
1780 1834 """display the combined ignore pattern"""
1781 1835 ignore = repo.dirstate._ignore
1782 1836 includepat = getattr(ignore, 'includepat', None)
1783 1837 if includepat is not None:
1784 1838 ui.write("%s\n" % includepat)
1785 1839 else:
1786 1840 raise util.Abort(_("no ignore patterns found"))
1787 1841
1788 1842 @command('debugindex',
1789 1843 [('c', 'changelog', False, _('open changelog')),
1790 1844 ('m', 'manifest', False, _('open manifest')),
1791 1845 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1792 1846 _('[-f FORMAT] -c|-m|FILE'))
1793 1847 def debugindex(ui, repo, file_ = None, **opts):
1794 1848 """dump the contents of an index file"""
1795 1849 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1796 1850 format = opts.get('format', 0)
1797 1851 if format not in (0, 1):
1798 1852 raise util.Abort(_("unknown format %d") % format)
1799 1853
1800 1854 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1801 1855 if generaldelta:
1802 1856 basehdr = ' delta'
1803 1857 else:
1804 1858 basehdr = ' base'
1805 1859
1806 1860 if format == 0:
1807 1861 ui.write(" rev offset length " + basehdr + " linkrev"
1808 1862 " nodeid p1 p2\n")
1809 1863 elif format == 1:
1810 1864 ui.write(" rev flag offset length"
1811 1865 " size " + basehdr + " link p1 p2 nodeid\n")
1812 1866
1813 1867 for i in r:
1814 1868 node = r.node(i)
1815 1869 if generaldelta:
1816 1870 base = r.deltaparent(i)
1817 1871 else:
1818 1872 base = r.chainbase(i)
1819 1873 if format == 0:
1820 1874 try:
1821 1875 pp = r.parents(node)
1822 1876 except:
1823 1877 pp = [nullid, nullid]
1824 1878 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1825 1879 i, r.start(i), r.length(i), base, r.linkrev(i),
1826 1880 short(node), short(pp[0]), short(pp[1])))
1827 1881 elif format == 1:
1828 1882 pr = r.parentrevs(i)
1829 1883 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1830 1884 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1831 1885 base, r.linkrev(i), pr[0], pr[1], short(node)))
1832 1886
1833 1887 @command('debugindexdot', [], _('FILE'))
1834 1888 def debugindexdot(ui, repo, file_):
1835 1889 """dump an index DAG as a graphviz dot file"""
1836 1890 r = None
1837 1891 if repo:
1838 1892 filelog = repo.file(file_)
1839 1893 if len(filelog):
1840 1894 r = filelog
1841 1895 if not r:
1842 1896 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1843 1897 ui.write("digraph G {\n")
1844 1898 for i in r:
1845 1899 node = r.node(i)
1846 1900 pp = r.parents(node)
1847 1901 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1848 1902 if pp[1] != nullid:
1849 1903 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1850 1904 ui.write("}\n")
1851 1905
1852 1906 @command('debuginstall', [], '')
1853 1907 def debuginstall(ui):
1854 1908 '''test Mercurial installation
1855 1909
1856 1910 Returns 0 on success.
1857 1911 '''
1858 1912
1859 1913 def writetemp(contents):
1860 1914 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1861 1915 f = os.fdopen(fd, "wb")
1862 1916 f.write(contents)
1863 1917 f.close()
1864 1918 return name
1865 1919
1866 1920 problems = 0
1867 1921
1868 1922 # encoding
1869 1923 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1870 1924 try:
1871 1925 encoding.fromlocal("test")
1872 1926 except util.Abort, inst:
1873 1927 ui.write(" %s\n" % inst)
1874 1928 ui.write(_(" (check that your locale is properly set)\n"))
1875 1929 problems += 1
1876 1930
1877 1931 # compiled modules
1878 1932 ui.status(_("Checking installed modules (%s)...\n")
1879 1933 % os.path.dirname(__file__))
1880 1934 try:
1881 1935 import bdiff, mpatch, base85, osutil
1882 1936 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1883 1937 except Exception, inst:
1884 1938 ui.write(" %s\n" % inst)
1885 1939 ui.write(_(" One or more extensions could not be found"))
1886 1940 ui.write(_(" (check that you compiled the extensions)\n"))
1887 1941 problems += 1
1888 1942
1889 1943 # templates
1890 1944 import templater
1891 1945 p = templater.templatepath()
1892 1946 ui.status(_("Checking templates (%s)...\n") % ' '.join(p))
1893 1947 try:
1894 1948 templater.templater(templater.templatepath("map-cmdline.default"))
1895 1949 except Exception, inst:
1896 1950 ui.write(" %s\n" % inst)
1897 1951 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1898 1952 problems += 1
1899 1953
1900 1954 # editor
1901 1955 ui.status(_("Checking commit editor...\n"))
1902 1956 editor = ui.geteditor()
1903 1957 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1904 1958 if not cmdpath:
1905 1959 if editor == 'vi':
1906 1960 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1907 1961 ui.write(_(" (specify a commit editor in your configuration"
1908 1962 " file)\n"))
1909 1963 else:
1910 1964 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1911 1965 ui.write(_(" (specify a commit editor in your configuration"
1912 1966 " file)\n"))
1913 1967 problems += 1
1914 1968
1915 1969 # check username
1916 1970 ui.status(_("Checking username...\n"))
1917 1971 try:
1918 1972 ui.username()
1919 1973 except util.Abort, e:
1920 1974 ui.write(" %s\n" % e)
1921 1975 ui.write(_(" (specify a username in your configuration file)\n"))
1922 1976 problems += 1
1923 1977
1924 1978 if not problems:
1925 1979 ui.status(_("No problems detected\n"))
1926 1980 else:
1927 1981 ui.write(_("%s problems detected,"
1928 1982 " please check your install!\n") % problems)
1929 1983
1930 1984 return problems
1931 1985
1932 1986 @command('debugknown', [], _('REPO ID...'))
1933 1987 def debugknown(ui, repopath, *ids, **opts):
1934 1988 """test whether node ids are known to a repo
1935 1989
1936 1990 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1937 1991 indicating unknown/known.
1938 1992 """
1939 1993 repo = hg.peer(ui, opts, repopath)
1940 1994 if not repo.capable('known'):
1941 1995 raise util.Abort("known() not supported by target repository")
1942 1996 flags = repo.known([bin(s) for s in ids])
1943 1997 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1944 1998
1945 1999 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1946 2000 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1947 2001 '''access the pushkey key/value protocol
1948 2002
1949 2003 With two args, list the keys in the given namespace.
1950 2004
1951 2005 With five args, set a key to new if it currently is set to old.
1952 2006 Reports success or failure.
1953 2007 '''
1954 2008
1955 2009 target = hg.peer(ui, {}, repopath)
1956 2010 if keyinfo:
1957 2011 key, old, new = keyinfo
1958 2012 r = target.pushkey(namespace, key, old, new)
1959 2013 ui.status(str(r) + '\n')
1960 2014 return not r
1961 2015 else:
1962 2016 for k, v in target.listkeys(namespace).iteritems():
1963 2017 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1964 2018 v.encode('string-escape')))
1965 2019
1966 2020 @command('debugpvec', [], _('A B'))
1967 2021 def debugpvec(ui, repo, a, b=None):
1968 2022 ca = scmutil.revsingle(repo, a)
1969 2023 cb = scmutil.revsingle(repo, b)
1970 2024 pa = pvec.ctxpvec(ca)
1971 2025 pb = pvec.ctxpvec(cb)
1972 2026 if pa == pb:
1973 2027 rel = "="
1974 2028 elif pa > pb:
1975 2029 rel = ">"
1976 2030 elif pa < pb:
1977 2031 rel = "<"
1978 2032 elif pa | pb:
1979 2033 rel = "|"
1980 2034 ui.write(_("a: %s\n") % pa)
1981 2035 ui.write(_("b: %s\n") % pb)
1982 2036 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
1983 2037 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
1984 2038 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
1985 2039 pa.distance(pb), rel))
1986 2040
1987 2041 @command('debugrebuildstate',
1988 2042 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1989 2043 _('[-r REV] [REV]'))
1990 2044 def debugrebuildstate(ui, repo, rev="tip"):
1991 2045 """rebuild the dirstate as it would look like for the given revision"""
1992 2046 ctx = scmutil.revsingle(repo, rev)
1993 2047 wlock = repo.wlock()
1994 2048 try:
1995 2049 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1996 2050 finally:
1997 2051 wlock.release()
1998 2052
1999 2053 @command('debugrename',
2000 2054 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2001 2055 _('[-r REV] FILE'))
2002 2056 def debugrename(ui, repo, file1, *pats, **opts):
2003 2057 """dump rename information"""
2004 2058
2005 2059 ctx = scmutil.revsingle(repo, opts.get('rev'))
2006 2060 m = scmutil.match(ctx, (file1,) + pats, opts)
2007 2061 for abs in ctx.walk(m):
2008 2062 fctx = ctx[abs]
2009 2063 o = fctx.filelog().renamed(fctx.filenode())
2010 2064 rel = m.rel(abs)
2011 2065 if o:
2012 2066 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2013 2067 else:
2014 2068 ui.write(_("%s not renamed\n") % rel)
2015 2069
2016 2070 @command('debugrevlog',
2017 2071 [('c', 'changelog', False, _('open changelog')),
2018 2072 ('m', 'manifest', False, _('open manifest')),
2019 2073 ('d', 'dump', False, _('dump index data'))],
2020 2074 _('-c|-m|FILE'))
2021 2075 def debugrevlog(ui, repo, file_ = None, **opts):
2022 2076 """show data and statistics about a revlog"""
2023 2077 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2024 2078
2025 2079 if opts.get("dump"):
2026 2080 numrevs = len(r)
2027 2081 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2028 2082 " rawsize totalsize compression heads\n")
2029 2083 ts = 0
2030 2084 heads = set()
2031 2085 for rev in xrange(numrevs):
2032 2086 dbase = r.deltaparent(rev)
2033 2087 if dbase == -1:
2034 2088 dbase = rev
2035 2089 cbase = r.chainbase(rev)
2036 2090 p1, p2 = r.parentrevs(rev)
2037 2091 rs = r.rawsize(rev)
2038 2092 ts = ts + rs
2039 2093 heads -= set(r.parentrevs(rev))
2040 2094 heads.add(rev)
2041 2095 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2042 2096 (rev, p1, p2, r.start(rev), r.end(rev),
2043 2097 r.start(dbase), r.start(cbase),
2044 2098 r.start(p1), r.start(p2),
2045 2099 rs, ts, ts / r.end(rev), len(heads)))
2046 2100 return 0
2047 2101
2048 2102 v = r.version
2049 2103 format = v & 0xFFFF
2050 2104 flags = []
2051 2105 gdelta = False
2052 2106 if v & revlog.REVLOGNGINLINEDATA:
2053 2107 flags.append('inline')
2054 2108 if v & revlog.REVLOGGENERALDELTA:
2055 2109 gdelta = True
2056 2110 flags.append('generaldelta')
2057 2111 if not flags:
2058 2112 flags = ['(none)']
2059 2113
2060 2114 nummerges = 0
2061 2115 numfull = 0
2062 2116 numprev = 0
2063 2117 nump1 = 0
2064 2118 nump2 = 0
2065 2119 numother = 0
2066 2120 nump1prev = 0
2067 2121 nump2prev = 0
2068 2122 chainlengths = []
2069 2123
2070 2124 datasize = [None, 0, 0L]
2071 2125 fullsize = [None, 0, 0L]
2072 2126 deltasize = [None, 0, 0L]
2073 2127
2074 2128 def addsize(size, l):
2075 2129 if l[0] is None or size < l[0]:
2076 2130 l[0] = size
2077 2131 if size > l[1]:
2078 2132 l[1] = size
2079 2133 l[2] += size
2080 2134
2081 2135 numrevs = len(r)
2082 2136 for rev in xrange(numrevs):
2083 2137 p1, p2 = r.parentrevs(rev)
2084 2138 delta = r.deltaparent(rev)
2085 2139 if format > 0:
2086 2140 addsize(r.rawsize(rev), datasize)
2087 2141 if p2 != nullrev:
2088 2142 nummerges += 1
2089 2143 size = r.length(rev)
2090 2144 if delta == nullrev:
2091 2145 chainlengths.append(0)
2092 2146 numfull += 1
2093 2147 addsize(size, fullsize)
2094 2148 else:
2095 2149 chainlengths.append(chainlengths[delta] + 1)
2096 2150 addsize(size, deltasize)
2097 2151 if delta == rev - 1:
2098 2152 numprev += 1
2099 2153 if delta == p1:
2100 2154 nump1prev += 1
2101 2155 elif delta == p2:
2102 2156 nump2prev += 1
2103 2157 elif delta == p1:
2104 2158 nump1 += 1
2105 2159 elif delta == p2:
2106 2160 nump2 += 1
2107 2161 elif delta != nullrev:
2108 2162 numother += 1
2109 2163
2110 2164 numdeltas = numrevs - numfull
2111 2165 numoprev = numprev - nump1prev - nump2prev
2112 2166 totalrawsize = datasize[2]
2113 2167 datasize[2] /= numrevs
2114 2168 fulltotal = fullsize[2]
2115 2169 fullsize[2] /= numfull
2116 2170 deltatotal = deltasize[2]
2117 2171 deltasize[2] /= numrevs - numfull
2118 2172 totalsize = fulltotal + deltatotal
2119 2173 avgchainlen = sum(chainlengths) / numrevs
2120 2174 compratio = totalrawsize / totalsize
2121 2175
2122 2176 basedfmtstr = '%%%dd\n'
2123 2177 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2124 2178
2125 2179 def dfmtstr(max):
2126 2180 return basedfmtstr % len(str(max))
2127 2181 def pcfmtstr(max, padding=0):
2128 2182 return basepcfmtstr % (len(str(max)), ' ' * padding)
2129 2183
2130 2184 def pcfmt(value, total):
2131 2185 return (value, 100 * float(value) / total)
2132 2186
2133 2187 ui.write('format : %d\n' % format)
2134 2188 ui.write('flags : %s\n' % ', '.join(flags))
2135 2189
2136 2190 ui.write('\n')
2137 2191 fmt = pcfmtstr(totalsize)
2138 2192 fmt2 = dfmtstr(totalsize)
2139 2193 ui.write('revisions : ' + fmt2 % numrevs)
2140 2194 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2141 2195 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2142 2196 ui.write('revisions : ' + fmt2 % numrevs)
2143 2197 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2144 2198 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2145 2199 ui.write('revision size : ' + fmt2 % totalsize)
2146 2200 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2147 2201 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2148 2202
2149 2203 ui.write('\n')
2150 2204 fmt = dfmtstr(max(avgchainlen, compratio))
2151 2205 ui.write('avg chain length : ' + fmt % avgchainlen)
2152 2206 ui.write('compression ratio : ' + fmt % compratio)
2153 2207
2154 2208 if format > 0:
2155 2209 ui.write('\n')
2156 2210 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2157 2211 % tuple(datasize))
2158 2212 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2159 2213 % tuple(fullsize))
2160 2214 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2161 2215 % tuple(deltasize))
2162 2216
2163 2217 if numdeltas > 0:
2164 2218 ui.write('\n')
2165 2219 fmt = pcfmtstr(numdeltas)
2166 2220 fmt2 = pcfmtstr(numdeltas, 4)
2167 2221 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2168 2222 if numprev > 0:
2169 2223 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2170 2224 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2171 2225 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2172 2226 if gdelta:
2173 2227 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2174 2228 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2175 2229 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2176 2230
2177 2231 @command('debugrevspec', [], ('REVSPEC'))
2178 2232 def debugrevspec(ui, repo, expr):
2179 2233 """parse and apply a revision specification
2180 2234
2181 2235 Use --verbose to print the parsed tree before and after aliases
2182 2236 expansion.
2183 2237 """
2184 2238 if ui.verbose:
2185 2239 tree = revset.parse(expr)[0]
2186 2240 ui.note(revset.prettyformat(tree), "\n")
2187 2241 newtree = revset.findaliases(ui, tree)
2188 2242 if newtree != tree:
2189 2243 ui.note(revset.prettyformat(newtree), "\n")
2190 2244 func = revset.match(ui, expr)
2191 2245 for c in func(repo, range(len(repo))):
2192 2246 ui.write("%s\n" % c)
2193 2247
2194 2248 @command('debugsetparents', [], _('REV1 [REV2]'))
2195 2249 def debugsetparents(ui, repo, rev1, rev2=None):
2196 2250 """manually set the parents of the current working directory
2197 2251
2198 2252 This is useful for writing repository conversion tools, but should
2199 2253 be used with care.
2200 2254
2201 2255 Returns 0 on success.
2202 2256 """
2203 2257
2204 2258 r1 = scmutil.revsingle(repo, rev1).node()
2205 2259 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2206 2260
2207 2261 wlock = repo.wlock()
2208 2262 try:
2209 2263 repo.dirstate.setparents(r1, r2)
2210 2264 finally:
2211 2265 wlock.release()
2212 2266
2213 2267 @command('debugstate',
2214 2268 [('', 'nodates', None, _('do not display the saved mtime')),
2215 2269 ('', 'datesort', None, _('sort by saved mtime'))],
2216 2270 _('[OPTION]...'))
2217 2271 def debugstate(ui, repo, nodates=None, datesort=None):
2218 2272 """show the contents of the current dirstate"""
2219 2273 timestr = ""
2220 2274 showdate = not nodates
2221 2275 if datesort:
2222 2276 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2223 2277 else:
2224 2278 keyfunc = None # sort by filename
2225 2279 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2226 2280 if showdate:
2227 2281 if ent[3] == -1:
2228 2282 # Pad or slice to locale representation
2229 2283 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2230 2284 time.localtime(0)))
2231 2285 timestr = 'unset'
2232 2286 timestr = (timestr[:locale_len] +
2233 2287 ' ' * (locale_len - len(timestr)))
2234 2288 else:
2235 2289 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2236 2290 time.localtime(ent[3]))
2237 2291 if ent[1] & 020000:
2238 2292 mode = 'lnk'
2239 2293 else:
2240 2294 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2241 2295 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2242 2296 for f in repo.dirstate.copies():
2243 2297 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2244 2298
2245 2299 @command('debugsub',
2246 2300 [('r', 'rev', '',
2247 2301 _('revision to check'), _('REV'))],
2248 2302 _('[-r REV] [REV]'))
2249 2303 def debugsub(ui, repo, rev=None):
2250 2304 ctx = scmutil.revsingle(repo, rev, None)
2251 2305 for k, v in sorted(ctx.substate.items()):
2252 2306 ui.write('path %s\n' % k)
2253 2307 ui.write(' source %s\n' % v[0])
2254 2308 ui.write(' revision %s\n' % v[1])
2255 2309
2256 2310 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2257 2311 def debugwalk(ui, repo, *pats, **opts):
2258 2312 """show how files match on given patterns"""
2259 2313 m = scmutil.match(repo[None], pats, opts)
2260 2314 items = list(repo.walk(m))
2261 2315 if not items:
2262 2316 return
2263 2317 fmt = 'f %%-%ds %%-%ds %%s' % (
2264 2318 max([len(abs) for abs in items]),
2265 2319 max([len(m.rel(abs)) for abs in items]))
2266 2320 for abs in items:
2267 2321 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2268 2322 ui.write("%s\n" % line.rstrip())
2269 2323
2270 2324 @command('debugwireargs',
2271 2325 [('', 'three', '', 'three'),
2272 2326 ('', 'four', '', 'four'),
2273 2327 ('', 'five', '', 'five'),
2274 2328 ] + remoteopts,
2275 2329 _('REPO [OPTIONS]... [ONE [TWO]]'))
2276 2330 def debugwireargs(ui, repopath, *vals, **opts):
2277 2331 repo = hg.peer(ui, opts, repopath)
2278 2332 for opt in remoteopts:
2279 2333 del opts[opt[1]]
2280 2334 args = {}
2281 2335 for k, v in opts.iteritems():
2282 2336 if v:
2283 2337 args[k] = v
2284 2338 # run twice to check that we don't mess up the stream for the next command
2285 2339 res1 = repo.debugwireargs(*vals, **args)
2286 2340 res2 = repo.debugwireargs(*vals, **args)
2287 2341 ui.write("%s\n" % res1)
2288 2342 if res1 != res2:
2289 2343 ui.warn("%s\n" % res2)
2290 2344
2291 2345 @command('^diff',
2292 2346 [('r', 'rev', [], _('revision'), _('REV')),
2293 2347 ('c', 'change', '', _('change made by revision'), _('REV'))
2294 2348 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2295 2349 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2296 2350 def diff(ui, repo, *pats, **opts):
2297 2351 """diff repository (or selected files)
2298 2352
2299 2353 Show differences between revisions for the specified files.
2300 2354
2301 2355 Differences between files are shown using the unified diff format.
2302 2356
2303 2357 .. note::
2304 2358 diff may generate unexpected results for merges, as it will
2305 2359 default to comparing against the working directory's first
2306 2360 parent changeset if no revisions are specified.
2307 2361
2308 2362 When two revision arguments are given, then changes are shown
2309 2363 between those revisions. If only one revision is specified then
2310 2364 that revision is compared to the working directory, and, when no
2311 2365 revisions are specified, the working directory files are compared
2312 2366 to its parent.
2313 2367
2314 2368 Alternatively you can specify -c/--change with a revision to see
2315 2369 the changes in that changeset relative to its first parent.
2316 2370
2317 2371 Without the -a/--text option, diff will avoid generating diffs of
2318 2372 files it detects as binary. With -a, diff will generate a diff
2319 2373 anyway, probably with undesirable results.
2320 2374
2321 2375 Use the -g/--git option to generate diffs in the git extended diff
2322 2376 format. For more information, read :hg:`help diffs`.
2323 2377
2324 2378 .. container:: verbose
2325 2379
2326 2380 Examples:
2327 2381
2328 2382 - compare a file in the current working directory to its parent::
2329 2383
2330 2384 hg diff foo.c
2331 2385
2332 2386 - compare two historical versions of a directory, with rename info::
2333 2387
2334 2388 hg diff --git -r 1.0:1.2 lib/
2335 2389
2336 2390 - get change stats relative to the last change on some date::
2337 2391
2338 2392 hg diff --stat -r "date('may 2')"
2339 2393
2340 2394 - diff all newly-added files that contain a keyword::
2341 2395
2342 2396 hg diff "set:added() and grep(GNU)"
2343 2397
2344 2398 - compare a revision and its parents::
2345 2399
2346 2400 hg diff -c 9353 # compare against first parent
2347 2401 hg diff -r 9353^:9353 # same using revset syntax
2348 2402 hg diff -r 9353^2:9353 # compare against the second parent
2349 2403
2350 2404 Returns 0 on success.
2351 2405 """
2352 2406
2353 2407 revs = opts.get('rev')
2354 2408 change = opts.get('change')
2355 2409 stat = opts.get('stat')
2356 2410 reverse = opts.get('reverse')
2357 2411
2358 2412 if revs and change:
2359 2413 msg = _('cannot specify --rev and --change at the same time')
2360 2414 raise util.Abort(msg)
2361 2415 elif change:
2362 2416 node2 = scmutil.revsingle(repo, change, None).node()
2363 2417 node1 = repo[node2].p1().node()
2364 2418 else:
2365 2419 node1, node2 = scmutil.revpair(repo, revs)
2366 2420
2367 2421 if reverse:
2368 2422 node1, node2 = node2, node1
2369 2423
2370 2424 diffopts = patch.diffopts(ui, opts)
2371 2425 m = scmutil.match(repo[node2], pats, opts)
2372 2426 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2373 2427 listsubrepos=opts.get('subrepos'))
2374 2428
2375 2429 @command('^export',
2376 2430 [('o', 'output', '',
2377 2431 _('print output to file with formatted name'), _('FORMAT')),
2378 2432 ('', 'switch-parent', None, _('diff against the second parent')),
2379 2433 ('r', 'rev', [], _('revisions to export'), _('REV')),
2380 2434 ] + diffopts,
2381 2435 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2382 2436 def export(ui, repo, *changesets, **opts):
2383 2437 """dump the header and diffs for one or more changesets
2384 2438
2385 2439 Print the changeset header and diffs for one or more revisions.
2386 2440
2387 2441 The information shown in the changeset header is: author, date,
2388 2442 branch name (if non-default), changeset hash, parent(s) and commit
2389 2443 comment.
2390 2444
2391 2445 .. note::
2392 2446 export may generate unexpected diff output for merge
2393 2447 changesets, as it will compare the merge changeset against its
2394 2448 first parent only.
2395 2449
2396 2450 Output may be to a file, in which case the name of the file is
2397 2451 given using a format string. The formatting rules are as follows:
2398 2452
2399 2453 :``%%``: literal "%" character
2400 2454 :``%H``: changeset hash (40 hexadecimal digits)
2401 2455 :``%N``: number of patches being generated
2402 2456 :``%R``: changeset revision number
2403 2457 :``%b``: basename of the exporting repository
2404 2458 :``%h``: short-form changeset hash (12 hexadecimal digits)
2405 2459 :``%m``: first line of the commit message (only alphanumeric characters)
2406 2460 :``%n``: zero-padded sequence number, starting at 1
2407 2461 :``%r``: zero-padded changeset revision number
2408 2462
2409 2463 Without the -a/--text option, export will avoid generating diffs
2410 2464 of files it detects as binary. With -a, export will generate a
2411 2465 diff anyway, probably with undesirable results.
2412 2466
2413 2467 Use the -g/--git option to generate diffs in the git extended diff
2414 2468 format. See :hg:`help diffs` for more information.
2415 2469
2416 2470 With the --switch-parent option, the diff will be against the
2417 2471 second parent. It can be useful to review a merge.
2418 2472
2419 2473 .. container:: verbose
2420 2474
2421 2475 Examples:
2422 2476
2423 2477 - use export and import to transplant a bugfix to the current
2424 2478 branch::
2425 2479
2426 2480 hg export -r 9353 | hg import -
2427 2481
2428 2482 - export all the changesets between two revisions to a file with
2429 2483 rename information::
2430 2484
2431 2485 hg export --git -r 123:150 > changes.txt
2432 2486
2433 2487 - split outgoing changes into a series of patches with
2434 2488 descriptive names::
2435 2489
2436 2490 hg export -r "outgoing()" -o "%n-%m.patch"
2437 2491
2438 2492 Returns 0 on success.
2439 2493 """
2440 2494 changesets += tuple(opts.get('rev', []))
2441 2495 revs = scmutil.revrange(repo, changesets)
2442 2496 if not revs:
2443 2497 raise util.Abort(_("export requires at least one changeset"))
2444 2498 if len(revs) > 1:
2445 2499 ui.note(_('exporting patches:\n'))
2446 2500 else:
2447 2501 ui.note(_('exporting patch:\n'))
2448 2502 cmdutil.export(repo, revs, template=opts.get('output'),
2449 2503 switch_parent=opts.get('switch_parent'),
2450 2504 opts=patch.diffopts(ui, opts))
2451 2505
2452 2506 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2453 2507 def forget(ui, repo, *pats, **opts):
2454 2508 """forget the specified files on the next commit
2455 2509
2456 2510 Mark the specified files so they will no longer be tracked
2457 2511 after the next commit.
2458 2512
2459 2513 This only removes files from the current branch, not from the
2460 2514 entire project history, and it does not delete them from the
2461 2515 working directory.
2462 2516
2463 2517 To undo a forget before the next commit, see :hg:`add`.
2464 2518
2465 2519 .. container:: verbose
2466 2520
2467 2521 Examples:
2468 2522
2469 2523 - forget newly-added binary files::
2470 2524
2471 2525 hg forget "set:added() and binary()"
2472 2526
2473 2527 - forget files that would be excluded by .hgignore::
2474 2528
2475 2529 hg forget "set:hgignore()"
2476 2530
2477 2531 Returns 0 on success.
2478 2532 """
2479 2533
2480 2534 if not pats:
2481 2535 raise util.Abort(_('no files specified'))
2482 2536
2483 2537 m = scmutil.match(repo[None], pats, opts)
2484 2538 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2485 2539 return rejected and 1 or 0
2486 2540
2487 2541 @command(
2488 2542 'graft',
2489 2543 [('c', 'continue', False, _('resume interrupted graft')),
2490 2544 ('e', 'edit', False, _('invoke editor on commit messages')),
2491 2545 ('D', 'currentdate', False,
2492 2546 _('record the current date as commit date')),
2493 2547 ('U', 'currentuser', False,
2494 2548 _('record the current user as committer'), _('DATE'))]
2495 2549 + commitopts2 + mergetoolopts + dryrunopts,
2496 2550 _('[OPTION]... REVISION...'))
2497 2551 def graft(ui, repo, *revs, **opts):
2498 2552 '''copy changes from other branches onto the current branch
2499 2553
2500 2554 This command uses Mercurial's merge logic to copy individual
2501 2555 changes from other branches without merging branches in the
2502 2556 history graph. This is sometimes known as 'backporting' or
2503 2557 'cherry-picking'. By default, graft will copy user, date, and
2504 2558 description from the source changesets.
2505 2559
2506 2560 Changesets that are ancestors of the current revision, that have
2507 2561 already been grafted, or that are merges will be skipped.
2508 2562
2509 2563 If a graft merge results in conflicts, the graft process is
2510 2564 interrupted so that the current merge can be manually resolved.
2511 2565 Once all conflicts are addressed, the graft process can be
2512 2566 continued with the -c/--continue option.
2513 2567
2514 2568 .. note::
2515 2569 The -c/--continue option does not reapply earlier options.
2516 2570
2517 2571 .. container:: verbose
2518 2572
2519 2573 Examples:
2520 2574
2521 2575 - copy a single change to the stable branch and edit its description::
2522 2576
2523 2577 hg update stable
2524 2578 hg graft --edit 9393
2525 2579
2526 2580 - graft a range of changesets with one exception, updating dates::
2527 2581
2528 2582 hg graft -D "2085::2093 and not 2091"
2529 2583
2530 2584 - continue a graft after resolving conflicts::
2531 2585
2532 2586 hg graft -c
2533 2587
2534 2588 - show the source of a grafted changeset::
2535 2589
2536 2590 hg log --debug -r tip
2537 2591
2538 2592 Returns 0 on successful completion.
2539 2593 '''
2540 2594
2541 2595 if not opts.get('user') and opts.get('currentuser'):
2542 2596 opts['user'] = ui.username()
2543 2597 if not opts.get('date') and opts.get('currentdate'):
2544 2598 opts['date'] = "%d %d" % util.makedate()
2545 2599
2546 2600 editor = None
2547 2601 if opts.get('edit'):
2548 2602 editor = cmdutil.commitforceeditor
2549 2603
2550 2604 cont = False
2551 2605 if opts['continue']:
2552 2606 cont = True
2553 2607 if revs:
2554 2608 raise util.Abort(_("can't specify --continue and revisions"))
2555 2609 # read in unfinished revisions
2556 2610 try:
2557 2611 nodes = repo.opener.read('graftstate').splitlines()
2558 2612 revs = [repo[node].rev() for node in nodes]
2559 2613 except IOError, inst:
2560 2614 if inst.errno != errno.ENOENT:
2561 2615 raise
2562 2616 raise util.Abort(_("no graft state found, can't continue"))
2563 2617 else:
2564 2618 cmdutil.bailifchanged(repo)
2565 2619 if not revs:
2566 2620 raise util.Abort(_('no revisions specified'))
2567 2621 revs = scmutil.revrange(repo, revs)
2568 2622
2569 2623 # check for merges
2570 2624 for rev in repo.revs('%ld and merge()', revs):
2571 2625 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2572 2626 revs.remove(rev)
2573 2627 if not revs:
2574 2628 return -1
2575 2629
2576 2630 # check for ancestors of dest branch
2577 2631 for rev in repo.revs('::. and %ld', revs):
2578 2632 ui.warn(_('skipping ancestor revision %s\n') % rev)
2579 2633 revs.remove(rev)
2580 2634 if not revs:
2581 2635 return -1
2582 2636
2583 2637 # analyze revs for earlier grafts
2584 2638 ids = {}
2585 2639 for ctx in repo.set("%ld", revs):
2586 2640 ids[ctx.hex()] = ctx.rev()
2587 2641 n = ctx.extra().get('source')
2588 2642 if n:
2589 2643 ids[n] = ctx.rev()
2590 2644
2591 2645 # check ancestors for earlier grafts
2592 2646 ui.debug('scanning for duplicate grafts\n')
2593 2647 for ctx in repo.set("::. - ::%ld", revs):
2594 2648 n = ctx.extra().get('source')
2595 2649 if n in ids:
2596 2650 r = repo[n].rev()
2597 2651 if r in revs:
2598 2652 ui.warn(_('skipping already grafted revision %s\n') % r)
2599 2653 revs.remove(r)
2600 2654 elif ids[n] in revs:
2601 2655 ui.warn(_('skipping already grafted revision %s '
2602 2656 '(same origin %d)\n') % (ids[n], r))
2603 2657 revs.remove(ids[n])
2604 2658 elif ctx.hex() in ids:
2605 2659 r = ids[ctx.hex()]
2606 2660 ui.warn(_('skipping already grafted revision %s '
2607 2661 '(was grafted from %d)\n') % (r, ctx.rev()))
2608 2662 revs.remove(r)
2609 2663 if not revs:
2610 2664 return -1
2611 2665
2612 2666 for pos, ctx in enumerate(repo.set("%ld", revs)):
2613 2667 current = repo['.']
2614 2668
2615 2669 ui.status(_('grafting revision %s\n') % ctx.rev())
2616 2670 if opts.get('dry_run'):
2617 2671 continue
2618 2672
2619 2673 # we don't merge the first commit when continuing
2620 2674 if not cont:
2621 2675 # perform the graft merge with p1(rev) as 'ancestor'
2622 2676 try:
2623 2677 # ui.forcemerge is an internal variable, do not document
2624 2678 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2625 2679 stats = mergemod.update(repo, ctx.node(), True, True, False,
2626 2680 ctx.p1().node())
2627 2681 finally:
2628 2682 ui.setconfig('ui', 'forcemerge', '')
2629 2683 # drop the second merge parent
2630 2684 repo.dirstate.setparents(current.node(), nullid)
2631 2685 repo.dirstate.write()
2632 2686 # fix up dirstate for copies and renames
2633 2687 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
2634 2688 # report any conflicts
2635 2689 if stats and stats[3] > 0:
2636 2690 # write out state for --continue
2637 2691 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2638 2692 repo.opener.write('graftstate', ''.join(nodelines))
2639 2693 raise util.Abort(
2640 2694 _("unresolved conflicts, can't continue"),
2641 2695 hint=_('use hg resolve and hg graft --continue'))
2642 2696 else:
2643 2697 cont = False
2644 2698
2645 2699 # commit
2646 2700 source = ctx.extra().get('source')
2647 2701 if not source:
2648 2702 source = ctx.hex()
2649 2703 extra = {'source': source}
2650 2704 user = ctx.user()
2651 2705 if opts.get('user'):
2652 2706 user = opts['user']
2653 2707 date = ctx.date()
2654 2708 if opts.get('date'):
2655 2709 date = opts['date']
2656 2710 repo.commit(text=ctx.description(), user=user,
2657 2711 date=date, extra=extra, editor=editor)
2658 2712
2659 2713 # remove state when we complete successfully
2660 2714 if not opts.get('dry_run') and os.path.exists(repo.join('graftstate')):
2661 2715 util.unlinkpath(repo.join('graftstate'))
2662 2716
2663 2717 return 0
2664 2718
2665 2719 @command('grep',
2666 2720 [('0', 'print0', None, _('end fields with NUL')),
2667 2721 ('', 'all', None, _('print all revisions that match')),
2668 2722 ('a', 'text', None, _('treat all files as text')),
2669 2723 ('f', 'follow', None,
2670 2724 _('follow changeset history,'
2671 2725 ' or file history across copies and renames')),
2672 2726 ('i', 'ignore-case', None, _('ignore case when matching')),
2673 2727 ('l', 'files-with-matches', None,
2674 2728 _('print only filenames and revisions that match')),
2675 2729 ('n', 'line-number', None, _('print matching line numbers')),
2676 2730 ('r', 'rev', [],
2677 2731 _('only search files changed within revision range'), _('REV')),
2678 2732 ('u', 'user', None, _('list the author (long with -v)')),
2679 2733 ('d', 'date', None, _('list the date (short with -q)')),
2680 2734 ] + walkopts,
2681 2735 _('[OPTION]... PATTERN [FILE]...'))
2682 2736 def grep(ui, repo, pattern, *pats, **opts):
2683 2737 """search for a pattern in specified files and revisions
2684 2738
2685 2739 Search revisions of files for a regular expression.
2686 2740
2687 2741 This command behaves differently than Unix grep. It only accepts
2688 2742 Python/Perl regexps. It searches repository history, not the
2689 2743 working directory. It always prints the revision number in which a
2690 2744 match appears.
2691 2745
2692 2746 By default, grep only prints output for the first revision of a
2693 2747 file in which it finds a match. To get it to print every revision
2694 2748 that contains a change in match status ("-" for a match that
2695 2749 becomes a non-match, or "+" for a non-match that becomes a match),
2696 2750 use the --all flag.
2697 2751
2698 2752 Returns 0 if a match is found, 1 otherwise.
2699 2753 """
2700 2754 reflags = re.M
2701 2755 if opts.get('ignore_case'):
2702 2756 reflags |= re.I
2703 2757 try:
2704 2758 regexp = re.compile(pattern, reflags)
2705 2759 except re.error, inst:
2706 2760 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2707 2761 return 1
2708 2762 sep, eol = ':', '\n'
2709 2763 if opts.get('print0'):
2710 2764 sep = eol = '\0'
2711 2765
2712 2766 getfile = util.lrucachefunc(repo.file)
2713 2767
2714 2768 def matchlines(body):
2715 2769 begin = 0
2716 2770 linenum = 0
2717 2771 while True:
2718 2772 match = regexp.search(body, begin)
2719 2773 if not match:
2720 2774 break
2721 2775 mstart, mend = match.span()
2722 2776 linenum += body.count('\n', begin, mstart) + 1
2723 2777 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2724 2778 begin = body.find('\n', mend) + 1 or len(body) + 1
2725 2779 lend = begin - 1
2726 2780 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2727 2781
2728 2782 class linestate(object):
2729 2783 def __init__(self, line, linenum, colstart, colend):
2730 2784 self.line = line
2731 2785 self.linenum = linenum
2732 2786 self.colstart = colstart
2733 2787 self.colend = colend
2734 2788
2735 2789 def __hash__(self):
2736 2790 return hash((self.linenum, self.line))
2737 2791
2738 2792 def __eq__(self, other):
2739 2793 return self.line == other.line
2740 2794
2741 2795 matches = {}
2742 2796 copies = {}
2743 2797 def grepbody(fn, rev, body):
2744 2798 matches[rev].setdefault(fn, [])
2745 2799 m = matches[rev][fn]
2746 2800 for lnum, cstart, cend, line in matchlines(body):
2747 2801 s = linestate(line, lnum, cstart, cend)
2748 2802 m.append(s)
2749 2803
2750 2804 def difflinestates(a, b):
2751 2805 sm = difflib.SequenceMatcher(None, a, b)
2752 2806 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2753 2807 if tag == 'insert':
2754 2808 for i in xrange(blo, bhi):
2755 2809 yield ('+', b[i])
2756 2810 elif tag == 'delete':
2757 2811 for i in xrange(alo, ahi):
2758 2812 yield ('-', a[i])
2759 2813 elif tag == 'replace':
2760 2814 for i in xrange(alo, ahi):
2761 2815 yield ('-', a[i])
2762 2816 for i in xrange(blo, bhi):
2763 2817 yield ('+', b[i])
2764 2818
2765 2819 def display(fn, ctx, pstates, states):
2766 2820 rev = ctx.rev()
2767 2821 datefunc = ui.quiet and util.shortdate or util.datestr
2768 2822 found = False
2769 2823 filerevmatches = {}
2770 2824 def binary():
2771 2825 flog = getfile(fn)
2772 2826 return util.binary(flog.read(ctx.filenode(fn)))
2773 2827
2774 2828 if opts.get('all'):
2775 2829 iter = difflinestates(pstates, states)
2776 2830 else:
2777 2831 iter = [('', l) for l in states]
2778 2832 for change, l in iter:
2779 2833 cols = [fn, str(rev)]
2780 2834 before, match, after = None, None, None
2781 2835 if opts.get('line_number'):
2782 2836 cols.append(str(l.linenum))
2783 2837 if opts.get('all'):
2784 2838 cols.append(change)
2785 2839 if opts.get('user'):
2786 2840 cols.append(ui.shortuser(ctx.user()))
2787 2841 if opts.get('date'):
2788 2842 cols.append(datefunc(ctx.date()))
2789 2843 if opts.get('files_with_matches'):
2790 2844 c = (fn, rev)
2791 2845 if c in filerevmatches:
2792 2846 continue
2793 2847 filerevmatches[c] = 1
2794 2848 else:
2795 2849 before = l.line[:l.colstart]
2796 2850 match = l.line[l.colstart:l.colend]
2797 2851 after = l.line[l.colend:]
2798 2852 ui.write(sep.join(cols))
2799 2853 if before is not None:
2800 2854 if not opts.get('text') and binary():
2801 2855 ui.write(sep + " Binary file matches")
2802 2856 else:
2803 2857 ui.write(sep + before)
2804 2858 ui.write(match, label='grep.match')
2805 2859 ui.write(after)
2806 2860 ui.write(eol)
2807 2861 found = True
2808 2862 return found
2809 2863
2810 2864 skip = {}
2811 2865 revfiles = {}
2812 2866 matchfn = scmutil.match(repo[None], pats, opts)
2813 2867 found = False
2814 2868 follow = opts.get('follow')
2815 2869
2816 2870 def prep(ctx, fns):
2817 2871 rev = ctx.rev()
2818 2872 pctx = ctx.p1()
2819 2873 parent = pctx.rev()
2820 2874 matches.setdefault(rev, {})
2821 2875 matches.setdefault(parent, {})
2822 2876 files = revfiles.setdefault(rev, [])
2823 2877 for fn in fns:
2824 2878 flog = getfile(fn)
2825 2879 try:
2826 2880 fnode = ctx.filenode(fn)
2827 2881 except error.LookupError:
2828 2882 continue
2829 2883
2830 2884 copied = flog.renamed(fnode)
2831 2885 copy = follow and copied and copied[0]
2832 2886 if copy:
2833 2887 copies.setdefault(rev, {})[fn] = copy
2834 2888 if fn in skip:
2835 2889 if copy:
2836 2890 skip[copy] = True
2837 2891 continue
2838 2892 files.append(fn)
2839 2893
2840 2894 if fn not in matches[rev]:
2841 2895 grepbody(fn, rev, flog.read(fnode))
2842 2896
2843 2897 pfn = copy or fn
2844 2898 if pfn not in matches[parent]:
2845 2899 try:
2846 2900 fnode = pctx.filenode(pfn)
2847 2901 grepbody(pfn, parent, flog.read(fnode))
2848 2902 except error.LookupError:
2849 2903 pass
2850 2904
2851 2905 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2852 2906 rev = ctx.rev()
2853 2907 parent = ctx.p1().rev()
2854 2908 for fn in sorted(revfiles.get(rev, [])):
2855 2909 states = matches[rev][fn]
2856 2910 copy = copies.get(rev, {}).get(fn)
2857 2911 if fn in skip:
2858 2912 if copy:
2859 2913 skip[copy] = True
2860 2914 continue
2861 2915 pstates = matches.get(parent, {}).get(copy or fn, [])
2862 2916 if pstates or states:
2863 2917 r = display(fn, ctx, pstates, states)
2864 2918 found = found or r
2865 2919 if r and not opts.get('all'):
2866 2920 skip[fn] = True
2867 2921 if copy:
2868 2922 skip[copy] = True
2869 2923 del matches[rev]
2870 2924 del revfiles[rev]
2871 2925
2872 2926 return not found
2873 2927
2874 2928 @command('heads',
2875 2929 [('r', 'rev', '',
2876 2930 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2877 2931 ('t', 'topo', False, _('show topological heads only')),
2878 2932 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2879 2933 ('c', 'closed', False, _('show normal and closed branch heads')),
2880 2934 ] + templateopts,
2881 2935 _('[-ac] [-r STARTREV] [REV]...'))
2882 2936 def heads(ui, repo, *branchrevs, **opts):
2883 2937 """show current repository heads or show branch heads
2884 2938
2885 2939 With no arguments, show all repository branch heads.
2886 2940
2887 2941 Repository "heads" are changesets with no child changesets. They are
2888 2942 where development generally takes place and are the usual targets
2889 2943 for update and merge operations. Branch heads are changesets that have
2890 2944 no child changeset on the same branch.
2891 2945
2892 2946 If one or more REVs are given, only branch heads on the branches
2893 2947 associated with the specified changesets are shown. This means
2894 2948 that you can use :hg:`heads foo` to see the heads on a branch
2895 2949 named ``foo``.
2896 2950
2897 2951 If -c/--closed is specified, also show branch heads marked closed
2898 2952 (see :hg:`commit --close-branch`).
2899 2953
2900 2954 If STARTREV is specified, only those heads that are descendants of
2901 2955 STARTREV will be displayed.
2902 2956
2903 2957 If -t/--topo is specified, named branch mechanics will be ignored and only
2904 2958 changesets without children will be shown.
2905 2959
2906 2960 Returns 0 if matching heads are found, 1 if not.
2907 2961 """
2908 2962
2909 2963 start = None
2910 2964 if 'rev' in opts:
2911 2965 start = scmutil.revsingle(repo, opts['rev'], None).node()
2912 2966
2913 2967 if opts.get('topo'):
2914 2968 heads = [repo[h] for h in repo.heads(start)]
2915 2969 else:
2916 2970 heads = []
2917 2971 for branch in repo.branchmap():
2918 2972 heads += repo.branchheads(branch, start, opts.get('closed'))
2919 2973 heads = [repo[h] for h in heads]
2920 2974
2921 2975 if branchrevs:
2922 2976 branches = set(repo[br].branch() for br in branchrevs)
2923 2977 heads = [h for h in heads if h.branch() in branches]
2924 2978
2925 2979 if opts.get('active') and branchrevs:
2926 2980 dagheads = repo.heads(start)
2927 2981 heads = [h for h in heads if h.node() in dagheads]
2928 2982
2929 2983 if branchrevs:
2930 2984 haveheads = set(h.branch() for h in heads)
2931 2985 if branches - haveheads:
2932 2986 headless = ', '.join(b for b in branches - haveheads)
2933 2987 msg = _('no open branch heads found on branches %s')
2934 2988 if opts.get('rev'):
2935 2989 msg += _(' (started at %s)') % opts['rev']
2936 2990 ui.warn((msg + '\n') % headless)
2937 2991
2938 2992 if not heads:
2939 2993 return 1
2940 2994
2941 2995 heads = sorted(heads, key=lambda x: -x.rev())
2942 2996 displayer = cmdutil.show_changeset(ui, repo, opts)
2943 2997 for ctx in heads:
2944 2998 displayer.show(ctx)
2945 2999 displayer.close()
2946 3000
2947 3001 @command('help',
2948 3002 [('e', 'extension', None, _('show only help for extensions')),
2949 3003 ('c', 'command', None, _('show only help for commands'))],
2950 3004 _('[-ec] [TOPIC]'))
2951 3005 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
2952 3006 """show help for a given topic or a help overview
2953 3007
2954 3008 With no arguments, print a list of commands with short help messages.
2955 3009
2956 3010 Given a topic, extension, or command name, print help for that
2957 3011 topic.
2958 3012
2959 3013 Returns 0 if successful.
2960 3014 """
2961 3015
2962 3016 textwidth = min(ui.termwidth(), 80) - 2
2963 3017
2964 3018 def optrst(options):
2965 3019 data = []
2966 3020 multioccur = False
2967 3021 for option in options:
2968 3022 if len(option) == 5:
2969 3023 shortopt, longopt, default, desc, optlabel = option
2970 3024 else:
2971 3025 shortopt, longopt, default, desc = option
2972 3026 optlabel = _("VALUE") # default label
2973 3027
2974 3028 if _("DEPRECATED") in desc and not ui.verbose:
2975 3029 continue
2976 3030
2977 3031 so = ''
2978 3032 if shortopt:
2979 3033 so = '-' + shortopt
2980 3034 lo = '--' + longopt
2981 3035 if default:
2982 3036 desc += _(" (default: %s)") % default
2983 3037
2984 3038 if isinstance(default, list):
2985 3039 lo += " %s [+]" % optlabel
2986 3040 multioccur = True
2987 3041 elif (default is not None) and not isinstance(default, bool):
2988 3042 lo += " %s" % optlabel
2989 3043
2990 3044 data.append((so, lo, desc))
2991 3045
2992 3046 rst = minirst.maketable(data, 1)
2993 3047
2994 3048 if multioccur:
2995 3049 rst += _("\n[+] marked option can be specified multiple times\n")
2996 3050
2997 3051 return rst
2998 3052
2999 3053 # list all option lists
3000 3054 def opttext(optlist, width):
3001 3055 rst = ''
3002 3056 if not optlist:
3003 3057 return ''
3004 3058
3005 3059 for title, options in optlist:
3006 3060 rst += '\n%s\n' % title
3007 3061 if options:
3008 3062 rst += "\n"
3009 3063 rst += optrst(options)
3010 3064 rst += '\n'
3011 3065
3012 3066 return '\n' + minirst.format(rst, width)
3013 3067
3014 3068 def addglobalopts(optlist, aliases):
3015 3069 if ui.quiet:
3016 3070 return []
3017 3071
3018 3072 if ui.verbose:
3019 3073 optlist.append((_("global options:"), globalopts))
3020 3074 if name == 'shortlist':
3021 3075 optlist.append((_('use "hg help" for the full list '
3022 3076 'of commands'), ()))
3023 3077 else:
3024 3078 if name == 'shortlist':
3025 3079 msg = _('use "hg help" for the full list of commands '
3026 3080 'or "hg -v" for details')
3027 3081 elif name and not full:
3028 3082 msg = _('use "hg help %s" to show the full help text') % name
3029 3083 elif aliases:
3030 3084 msg = _('use "hg -v help%s" to show builtin aliases and '
3031 3085 'global options') % (name and " " + name or "")
3032 3086 else:
3033 3087 msg = _('use "hg -v help %s" to show more info') % name
3034 3088 optlist.append((msg, ()))
3035 3089
3036 3090 def helpcmd(name):
3037 3091 try:
3038 3092 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3039 3093 except error.AmbiguousCommand, inst:
3040 3094 # py3k fix: except vars can't be used outside the scope of the
3041 3095 # except block, nor can be used inside a lambda. python issue4617
3042 3096 prefix = inst.args[0]
3043 3097 select = lambda c: c.lstrip('^').startswith(prefix)
3044 3098 helplist(select)
3045 3099 return
3046 3100
3047 3101 # check if it's an invalid alias and display its error if it is
3048 3102 if getattr(entry[0], 'badalias', False):
3049 3103 if not unknowncmd:
3050 3104 entry[0](ui)
3051 3105 return
3052 3106
3053 3107 rst = ""
3054 3108
3055 3109 # synopsis
3056 3110 if len(entry) > 2:
3057 3111 if entry[2].startswith('hg'):
3058 3112 rst += "%s\n" % entry[2]
3059 3113 else:
3060 3114 rst += 'hg %s %s\n' % (aliases[0], entry[2])
3061 3115 else:
3062 3116 rst += 'hg %s\n' % aliases[0]
3063 3117
3064 3118 # aliases
3065 3119 if full and not ui.quiet and len(aliases) > 1:
3066 3120 rst += _("\naliases: %s\n") % ', '.join(aliases[1:])
3067 3121
3068 3122 # description
3069 3123 doc = gettext(entry[0].__doc__)
3070 3124 if not doc:
3071 3125 doc = _("(no help text available)")
3072 3126 if util.safehasattr(entry[0], 'definition'): # aliased command
3073 3127 if entry[0].definition.startswith('!'): # shell alias
3074 3128 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3075 3129 else:
3076 3130 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3077 3131 if ui.quiet or not full:
3078 3132 doc = doc.splitlines()[0]
3079 3133 rst += "\n" + doc + "\n"
3080 3134
3081 3135 # check if this command shadows a non-trivial (multi-line)
3082 3136 # extension help text
3083 3137 try:
3084 3138 mod = extensions.find(name)
3085 3139 doc = gettext(mod.__doc__) or ''
3086 3140 if '\n' in doc.strip():
3087 3141 msg = _('use "hg help -e %s" to show help for '
3088 3142 'the %s extension') % (name, name)
3089 3143 rst += '\n%s\n' % msg
3090 3144 except KeyError:
3091 3145 pass
3092 3146
3093 3147 # options
3094 3148 if not ui.quiet and entry[1]:
3095 3149 rst += '\n'
3096 3150 rst += _("options:")
3097 3151 rst += '\n\n'
3098 3152 rst += optrst(entry[1])
3099 3153
3100 3154 if ui.verbose:
3101 3155 rst += '\n'
3102 3156 rst += _("global options:")
3103 3157 rst += '\n\n'
3104 3158 rst += optrst(globalopts)
3105 3159
3106 3160 keep = ui.verbose and ['verbose'] or []
3107 3161 formatted, pruned = minirst.format(rst, textwidth, keep=keep)
3108 3162 ui.write(formatted)
3109 3163
3110 3164 if not ui.verbose:
3111 3165 if not full:
3112 3166 ui.write(_('\nuse "hg help %s" to show the full help text\n')
3113 3167 % name)
3114 3168 elif not ui.quiet:
3115 3169 ui.write(_('\nuse "hg -v help %s" to show more info\n') % name)
3116 3170
3117 3171
3118 3172 def helplist(select=None):
3119 3173 # list of commands
3120 3174 if name == "shortlist":
3121 3175 header = _('basic commands:\n\n')
3122 3176 else:
3123 3177 header = _('list of commands:\n\n')
3124 3178
3125 3179 h = {}
3126 3180 cmds = {}
3127 3181 for c, e in table.iteritems():
3128 3182 f = c.split("|", 1)[0]
3129 3183 if select and not select(f):
3130 3184 continue
3131 3185 if (not select and name != 'shortlist' and
3132 3186 e[0].__module__ != __name__):
3133 3187 continue
3134 3188 if name == "shortlist" and not f.startswith("^"):
3135 3189 continue
3136 3190 f = f.lstrip("^")
3137 3191 if not ui.debugflag and f.startswith("debug"):
3138 3192 continue
3139 3193 doc = e[0].__doc__
3140 3194 if doc and 'DEPRECATED' in doc and not ui.verbose:
3141 3195 continue
3142 3196 doc = gettext(doc)
3143 3197 if not doc:
3144 3198 doc = _("(no help text available)")
3145 3199 h[f] = doc.splitlines()[0].rstrip()
3146 3200 cmds[f] = c.lstrip("^")
3147 3201
3148 3202 if not h:
3149 3203 ui.status(_('no commands defined\n'))
3150 3204 return
3151 3205
3152 3206 ui.status(header)
3153 3207 fns = sorted(h)
3154 3208 m = max(map(len, fns))
3155 3209 for f in fns:
3156 3210 if ui.verbose:
3157 3211 commands = cmds[f].replace("|",", ")
3158 3212 ui.write(" %s:\n %s\n"%(commands, h[f]))
3159 3213 else:
3160 3214 ui.write('%s\n' % (util.wrap(h[f], textwidth,
3161 3215 initindent=' %-*s ' % (m, f),
3162 3216 hangindent=' ' * (m + 4))))
3163 3217
3164 3218 if not name:
3165 3219 text = help.listexts(_('enabled extensions:'), extensions.enabled())
3166 3220 if text:
3167 3221 ui.write("\n%s" % minirst.format(text, textwidth))
3168 3222
3169 3223 ui.write(_("\nadditional help topics:\n\n"))
3170 3224 topics = []
3171 3225 for names, header, doc in help.helptable:
3172 3226 topics.append((sorted(names, key=len, reverse=True)[0], header))
3173 3227 topics_len = max([len(s[0]) for s in topics])
3174 3228 for t, desc in topics:
3175 3229 ui.write(" %-*s %s\n" % (topics_len, t, desc))
3176 3230
3177 3231 optlist = []
3178 3232 addglobalopts(optlist, True)
3179 3233 ui.write(opttext(optlist, textwidth))
3180 3234
3181 3235 def helptopic(name):
3182 3236 for names, header, doc in help.helptable:
3183 3237 if name in names:
3184 3238 break
3185 3239 else:
3186 3240 raise error.UnknownCommand(name)
3187 3241
3188 3242 # description
3189 3243 if not doc:
3190 3244 doc = _("(no help text available)")
3191 3245 if util.safehasattr(doc, '__call__'):
3192 3246 doc = doc()
3193 3247
3194 3248 ui.write("%s\n\n" % header)
3195 3249 ui.write("%s" % minirst.format(doc, textwidth, indent=4))
3196 3250 try:
3197 3251 cmdutil.findcmd(name, table)
3198 3252 ui.write(_('\nuse "hg help -c %s" to see help for '
3199 3253 'the %s command\n') % (name, name))
3200 3254 except error.UnknownCommand:
3201 3255 pass
3202 3256
3203 3257 def helpext(name):
3204 3258 try:
3205 3259 mod = extensions.find(name)
3206 3260 doc = gettext(mod.__doc__) or _('no help text available')
3207 3261 except KeyError:
3208 3262 mod = None
3209 3263 doc = extensions.disabledext(name)
3210 3264 if not doc:
3211 3265 raise error.UnknownCommand(name)
3212 3266
3213 3267 if '\n' not in doc:
3214 3268 head, tail = doc, ""
3215 3269 else:
3216 3270 head, tail = doc.split('\n', 1)
3217 3271 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
3218 3272 if tail:
3219 3273 ui.write(minirst.format(tail, textwidth))
3220 3274 ui.status('\n')
3221 3275
3222 3276 if mod:
3223 3277 try:
3224 3278 ct = mod.cmdtable
3225 3279 except AttributeError:
3226 3280 ct = {}
3227 3281 modcmds = set([c.split('|', 1)[0] for c in ct])
3228 3282 helplist(modcmds.__contains__)
3229 3283 else:
3230 3284 ui.write(_('use "hg help extensions" for information on enabling '
3231 3285 'extensions\n'))
3232 3286
3233 3287 def helpextcmd(name):
3234 3288 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
3235 3289 doc = gettext(mod.__doc__).splitlines()[0]
3236 3290
3237 3291 msg = help.listexts(_("'%s' is provided by the following "
3238 3292 "extension:") % cmd, {ext: doc}, indent=4)
3239 3293 ui.write(minirst.format(msg, textwidth))
3240 3294 ui.write('\n')
3241 3295 ui.write(_('use "hg help extensions" for information on enabling '
3242 3296 'extensions\n'))
3243 3297
3244 3298 if name and name != 'shortlist':
3245 3299 i = None
3246 3300 if unknowncmd:
3247 3301 queries = (helpextcmd,)
3248 3302 elif opts.get('extension'):
3249 3303 queries = (helpext,)
3250 3304 elif opts.get('command'):
3251 3305 queries = (helpcmd,)
3252 3306 else:
3253 3307 queries = (helptopic, helpcmd, helpext, helpextcmd)
3254 3308 for f in queries:
3255 3309 try:
3256 3310 f(name)
3257 3311 i = None
3258 3312 break
3259 3313 except error.UnknownCommand, inst:
3260 3314 i = inst
3261 3315 if i:
3262 3316 raise i
3263 3317 else:
3264 3318 # program name
3265 3319 ui.status(_("Mercurial Distributed SCM\n"))
3266 3320 ui.status('\n')
3267 3321 helplist()
3268 3322
3269 3323
3270 3324 @command('identify|id',
3271 3325 [('r', 'rev', '',
3272 3326 _('identify the specified revision'), _('REV')),
3273 3327 ('n', 'num', None, _('show local revision number')),
3274 3328 ('i', 'id', None, _('show global revision id')),
3275 3329 ('b', 'branch', None, _('show branch')),
3276 3330 ('t', 'tags', None, _('show tags')),
3277 3331 ('B', 'bookmarks', None, _('show bookmarks')),
3278 3332 ] + remoteopts,
3279 3333 _('[-nibtB] [-r REV] [SOURCE]'))
3280 3334 def identify(ui, repo, source=None, rev=None,
3281 3335 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3282 3336 """identify the working copy or specified revision
3283 3337
3284 3338 Print a summary identifying the repository state at REV using one or
3285 3339 two parent hash identifiers, followed by a "+" if the working
3286 3340 directory has uncommitted changes, the branch name (if not default),
3287 3341 a list of tags, and a list of bookmarks.
3288 3342
3289 3343 When REV is not given, print a summary of the current state of the
3290 3344 repository.
3291 3345
3292 3346 Specifying a path to a repository root or Mercurial bundle will
3293 3347 cause lookup to operate on that repository/bundle.
3294 3348
3295 3349 .. container:: verbose
3296 3350
3297 3351 Examples:
3298 3352
3299 3353 - generate a build identifier for the working directory::
3300 3354
3301 3355 hg id --id > build-id.dat
3302 3356
3303 3357 - find the revision corresponding to a tag::
3304 3358
3305 3359 hg id -n -r 1.3
3306 3360
3307 3361 - check the most recent revision of a remote repository::
3308 3362
3309 3363 hg id -r tip http://selenic.com/hg/
3310 3364
3311 3365 Returns 0 if successful.
3312 3366 """
3313 3367
3314 3368 if not repo and not source:
3315 3369 raise util.Abort(_("there is no Mercurial repository here "
3316 3370 "(.hg not found)"))
3317 3371
3318 3372 hexfunc = ui.debugflag and hex or short
3319 3373 default = not (num or id or branch or tags or bookmarks)
3320 3374 output = []
3321 3375 revs = []
3322 3376
3323 3377 if source:
3324 3378 source, branches = hg.parseurl(ui.expandpath(source))
3325 3379 repo = hg.peer(ui, opts, source)
3326 3380 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3327 3381
3328 3382 if not repo.local():
3329 3383 if num or branch or tags:
3330 3384 raise util.Abort(
3331 3385 _("can't query remote revision number, branch, or tags"))
3332 3386 if not rev and revs:
3333 3387 rev = revs[0]
3334 3388 if not rev:
3335 3389 rev = "tip"
3336 3390
3337 3391 remoterev = repo.lookup(rev)
3338 3392 if default or id:
3339 3393 output = [hexfunc(remoterev)]
3340 3394
3341 3395 def getbms():
3342 3396 bms = []
3343 3397
3344 3398 if 'bookmarks' in repo.listkeys('namespaces'):
3345 3399 hexremoterev = hex(remoterev)
3346 3400 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
3347 3401 if bmr == hexremoterev]
3348 3402
3349 3403 return bms
3350 3404
3351 3405 if bookmarks:
3352 3406 output.extend(getbms())
3353 3407 elif default and not ui.quiet:
3354 3408 # multiple bookmarks for a single parent separated by '/'
3355 3409 bm = '/'.join(getbms())
3356 3410 if bm:
3357 3411 output.append(bm)
3358 3412 else:
3359 3413 if not rev:
3360 3414 ctx = repo[None]
3361 3415 parents = ctx.parents()
3362 3416 changed = ""
3363 3417 if default or id or num:
3364 3418 changed = util.any(repo.status()) and "+" or ""
3365 3419 if default or id:
3366 3420 output = ["%s%s" %
3367 3421 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3368 3422 if num:
3369 3423 output.append("%s%s" %
3370 3424 ('+'.join([str(p.rev()) for p in parents]), changed))
3371 3425 else:
3372 3426 ctx = scmutil.revsingle(repo, rev)
3373 3427 if default or id:
3374 3428 output = [hexfunc(ctx.node())]
3375 3429 if num:
3376 3430 output.append(str(ctx.rev()))
3377 3431
3378 3432 if default and not ui.quiet:
3379 3433 b = ctx.branch()
3380 3434 if b != 'default':
3381 3435 output.append("(%s)" % b)
3382 3436
3383 3437 # multiple tags for a single parent separated by '/'
3384 3438 t = '/'.join(ctx.tags())
3385 3439 if t:
3386 3440 output.append(t)
3387 3441
3388 3442 # multiple bookmarks for a single parent separated by '/'
3389 3443 bm = '/'.join(ctx.bookmarks())
3390 3444 if bm:
3391 3445 output.append(bm)
3392 3446 else:
3393 3447 if branch:
3394 3448 output.append(ctx.branch())
3395 3449
3396 3450 if tags:
3397 3451 output.extend(ctx.tags())
3398 3452
3399 3453 if bookmarks:
3400 3454 output.extend(ctx.bookmarks())
3401 3455
3402 3456 ui.write("%s\n" % ' '.join(output))
3403 3457
3404 3458 @command('import|patch',
3405 3459 [('p', 'strip', 1,
3406 3460 _('directory strip option for patch. This has the same '
3407 3461 'meaning as the corresponding patch option'), _('NUM')),
3408 3462 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3409 3463 ('e', 'edit', False, _('invoke editor on commit messages')),
3410 3464 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3411 3465 ('', 'no-commit', None,
3412 3466 _("don't commit, just update the working directory")),
3413 3467 ('', 'bypass', None,
3414 3468 _("apply patch without touching the working directory")),
3415 3469 ('', 'exact', None,
3416 3470 _('apply patch to the nodes from which it was generated')),
3417 3471 ('', 'import-branch', None,
3418 3472 _('use any branch information in patch (implied by --exact)'))] +
3419 3473 commitopts + commitopts2 + similarityopts,
3420 3474 _('[OPTION]... PATCH...'))
3421 3475 def import_(ui, repo, patch1=None, *patches, **opts):
3422 3476 """import an ordered set of patches
3423 3477
3424 3478 Import a list of patches and commit them individually (unless
3425 3479 --no-commit is specified).
3426 3480
3427 3481 If there are outstanding changes in the working directory, import
3428 3482 will abort unless given the -f/--force flag.
3429 3483
3430 3484 You can import a patch straight from a mail message. Even patches
3431 3485 as attachments work (to use the body part, it must have type
3432 3486 text/plain or text/x-patch). From and Subject headers of email
3433 3487 message are used as default committer and commit message. All
3434 3488 text/plain body parts before first diff are added to commit
3435 3489 message.
3436 3490
3437 3491 If the imported patch was generated by :hg:`export`, user and
3438 3492 description from patch override values from message headers and
3439 3493 body. Values given on command line with -m/--message and -u/--user
3440 3494 override these.
3441 3495
3442 3496 If --exact is specified, import will set the working directory to
3443 3497 the parent of each patch before applying it, and will abort if the
3444 3498 resulting changeset has a different ID than the one recorded in
3445 3499 the patch. This may happen due to character set problems or other
3446 3500 deficiencies in the text patch format.
3447 3501
3448 3502 Use --bypass to apply and commit patches directly to the
3449 3503 repository, not touching the working directory. Without --exact,
3450 3504 patches will be applied on top of the working directory parent
3451 3505 revision.
3452 3506
3453 3507 With -s/--similarity, hg will attempt to discover renames and
3454 3508 copies in the patch in the same way as :hg:`addremove`.
3455 3509
3456 3510 To read a patch from standard input, use "-" as the patch name. If
3457 3511 a URL is specified, the patch will be downloaded from it.
3458 3512 See :hg:`help dates` for a list of formats valid for -d/--date.
3459 3513
3460 3514 .. container:: verbose
3461 3515
3462 3516 Examples:
3463 3517
3464 3518 - import a traditional patch from a website and detect renames::
3465 3519
3466 3520 hg import -s 80 http://example.com/bugfix.patch
3467 3521
3468 3522 - import a changeset from an hgweb server::
3469 3523
3470 3524 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3471 3525
3472 3526 - import all the patches in an Unix-style mbox::
3473 3527
3474 3528 hg import incoming-patches.mbox
3475 3529
3476 3530 - attempt to exactly restore an exported changeset (not always
3477 3531 possible)::
3478 3532
3479 3533 hg import --exact proposed-fix.patch
3480 3534
3481 3535 Returns 0 on success.
3482 3536 """
3483 3537
3484 3538 if not patch1:
3485 3539 raise util.Abort(_('need at least one patch to import'))
3486 3540
3487 3541 patches = (patch1,) + patches
3488 3542
3489 3543 date = opts.get('date')
3490 3544 if date:
3491 3545 opts['date'] = util.parsedate(date)
3492 3546
3493 3547 editor = cmdutil.commiteditor
3494 3548 if opts.get('edit'):
3495 3549 editor = cmdutil.commitforceeditor
3496 3550
3497 3551 update = not opts.get('bypass')
3498 3552 if not update and opts.get('no_commit'):
3499 3553 raise util.Abort(_('cannot use --no-commit with --bypass'))
3500 3554 try:
3501 3555 sim = float(opts.get('similarity') or 0)
3502 3556 except ValueError:
3503 3557 raise util.Abort(_('similarity must be a number'))
3504 3558 if sim < 0 or sim > 100:
3505 3559 raise util.Abort(_('similarity must be between 0 and 100'))
3506 3560 if sim and not update:
3507 3561 raise util.Abort(_('cannot use --similarity with --bypass'))
3508 3562
3509 3563 if (opts.get('exact') or not opts.get('force')) and update:
3510 3564 cmdutil.bailifchanged(repo)
3511 3565
3512 3566 base = opts["base"]
3513 3567 strip = opts["strip"]
3514 3568 wlock = lock = tr = None
3515 3569 msgs = []
3516 3570
3517 3571 def checkexact(repo, n, nodeid):
3518 3572 if opts.get('exact') and hex(n) != nodeid:
3519 3573 repo.rollback()
3520 3574 raise util.Abort(_('patch is damaged or loses information'))
3521 3575
3522 3576 def tryone(ui, hunk, parents):
3523 3577 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3524 3578 patch.extract(ui, hunk)
3525 3579
3526 3580 if not tmpname:
3527 3581 return (None, None)
3528 3582 msg = _('applied to working directory')
3529 3583
3530 3584 try:
3531 3585 cmdline_message = cmdutil.logmessage(ui, opts)
3532 3586 if cmdline_message:
3533 3587 # pickup the cmdline msg
3534 3588 message = cmdline_message
3535 3589 elif message:
3536 3590 # pickup the patch msg
3537 3591 message = message.strip()
3538 3592 else:
3539 3593 # launch the editor
3540 3594 message = None
3541 3595 ui.debug('message:\n%s\n' % message)
3542 3596
3543 3597 if len(parents) == 1:
3544 3598 parents.append(repo[nullid])
3545 3599 if opts.get('exact'):
3546 3600 if not nodeid or not p1:
3547 3601 raise util.Abort(_('not a Mercurial patch'))
3548 3602 p1 = repo[p1]
3549 3603 p2 = repo[p2 or nullid]
3550 3604 elif p2:
3551 3605 try:
3552 3606 p1 = repo[p1]
3553 3607 p2 = repo[p2]
3554 3608 # Without any options, consider p2 only if the
3555 3609 # patch is being applied on top of the recorded
3556 3610 # first parent.
3557 3611 if p1 != parents[0]:
3558 3612 p1 = parents[0]
3559 3613 p2 = repo[nullid]
3560 3614 except error.RepoError:
3561 3615 p1, p2 = parents
3562 3616 else:
3563 3617 p1, p2 = parents
3564 3618
3565 3619 n = None
3566 3620 if update:
3567 3621 if p1 != parents[0]:
3568 3622 hg.clean(repo, p1.node())
3569 3623 if p2 != parents[1]:
3570 3624 repo.dirstate.setparents(p1.node(), p2.node())
3571 3625
3572 3626 if opts.get('exact') or opts.get('import_branch'):
3573 3627 repo.dirstate.setbranch(branch or 'default')
3574 3628
3575 3629 files = set()
3576 3630 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3577 3631 eolmode=None, similarity=sim / 100.0)
3578 3632 files = list(files)
3579 3633 if opts.get('no_commit'):
3580 3634 if message:
3581 3635 msgs.append(message)
3582 3636 else:
3583 3637 if opts.get('exact') or p2:
3584 3638 # If you got here, you either use --force and know what
3585 3639 # you are doing or used --exact or a merge patch while
3586 3640 # being updated to its first parent.
3587 3641 m = None
3588 3642 else:
3589 3643 m = scmutil.matchfiles(repo, files or [])
3590 3644 n = repo.commit(message, opts.get('user') or user,
3591 3645 opts.get('date') or date, match=m,
3592 3646 editor=editor)
3593 3647 checkexact(repo, n, nodeid)
3594 3648 else:
3595 3649 if opts.get('exact') or opts.get('import_branch'):
3596 3650 branch = branch or 'default'
3597 3651 else:
3598 3652 branch = p1.branch()
3599 3653 store = patch.filestore()
3600 3654 try:
3601 3655 files = set()
3602 3656 try:
3603 3657 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3604 3658 files, eolmode=None)
3605 3659 except patch.PatchError, e:
3606 3660 raise util.Abort(str(e))
3607 3661 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3608 3662 message,
3609 3663 opts.get('user') or user,
3610 3664 opts.get('date') or date,
3611 3665 branch, files, store,
3612 3666 editor=cmdutil.commiteditor)
3613 3667 repo.savecommitmessage(memctx.description())
3614 3668 n = memctx.commit()
3615 3669 checkexact(repo, n, nodeid)
3616 3670 finally:
3617 3671 store.close()
3618 3672 if n:
3619 3673 # i18n: refers to a short changeset id
3620 3674 msg = _('created %s') % short(n)
3621 3675 return (msg, n)
3622 3676 finally:
3623 3677 os.unlink(tmpname)
3624 3678
3625 3679 try:
3626 3680 try:
3627 3681 wlock = repo.wlock()
3628 3682 if not opts.get('no_commit'):
3629 3683 lock = repo.lock()
3630 3684 tr = repo.transaction('import')
3631 3685 parents = repo.parents()
3632 3686 for patchurl in patches:
3633 3687 if patchurl == '-':
3634 3688 ui.status(_('applying patch from stdin\n'))
3635 3689 patchfile = ui.fin
3636 3690 patchurl = 'stdin' # for error message
3637 3691 else:
3638 3692 patchurl = os.path.join(base, patchurl)
3639 3693 ui.status(_('applying %s\n') % patchurl)
3640 3694 patchfile = url.open(ui, patchurl)
3641 3695
3642 3696 haspatch = False
3643 3697 for hunk in patch.split(patchfile):
3644 3698 (msg, node) = tryone(ui, hunk, parents)
3645 3699 if msg:
3646 3700 haspatch = True
3647 3701 ui.note(msg + '\n')
3648 3702 if update or opts.get('exact'):
3649 3703 parents = repo.parents()
3650 3704 else:
3651 3705 parents = [repo[node]]
3652 3706
3653 3707 if not haspatch:
3654 3708 raise util.Abort(_('%s: no diffs found') % patchurl)
3655 3709
3656 3710 if tr:
3657 3711 tr.close()
3658 3712 if msgs:
3659 3713 repo.savecommitmessage('\n* * *\n'.join(msgs))
3660 3714 except:
3661 3715 # wlock.release() indirectly calls dirstate.write(): since
3662 3716 # we're crashing, we do not want to change the working dir
3663 3717 # parent after all, so make sure it writes nothing
3664 3718 repo.dirstate.invalidate()
3665 3719 raise
3666 3720 finally:
3667 3721 if tr:
3668 3722 tr.release()
3669 3723 release(lock, wlock)
3670 3724
3671 3725 @command('incoming|in',
3672 3726 [('f', 'force', None,
3673 3727 _('run even if remote repository is unrelated')),
3674 3728 ('n', 'newest-first', None, _('show newest record first')),
3675 3729 ('', 'bundle', '',
3676 3730 _('file to store the bundles into'), _('FILE')),
3677 3731 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3678 3732 ('B', 'bookmarks', False, _("compare bookmarks")),
3679 3733 ('b', 'branch', [],
3680 3734 _('a specific branch you would like to pull'), _('BRANCH')),
3681 3735 ] + logopts + remoteopts + subrepoopts,
3682 3736 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3683 3737 def incoming(ui, repo, source="default", **opts):
3684 3738 """show new changesets found in source
3685 3739
3686 3740 Show new changesets found in the specified path/URL or the default
3687 3741 pull location. These are the changesets that would have been pulled
3688 3742 if a pull at the time you issued this command.
3689 3743
3690 3744 For remote repository, using --bundle avoids downloading the
3691 3745 changesets twice if the incoming is followed by a pull.
3692 3746
3693 3747 See pull for valid source format details.
3694 3748
3695 3749 Returns 0 if there are incoming changes, 1 otherwise.
3696 3750 """
3697 3751 if opts.get('bundle') and opts.get('subrepos'):
3698 3752 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3699 3753
3700 3754 if opts.get('bookmarks'):
3701 3755 source, branches = hg.parseurl(ui.expandpath(source),
3702 3756 opts.get('branch'))
3703 3757 other = hg.peer(repo, opts, source)
3704 3758 if 'bookmarks' not in other.listkeys('namespaces'):
3705 3759 ui.warn(_("remote doesn't support bookmarks\n"))
3706 3760 return 0
3707 3761 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3708 3762 return bookmarks.diff(ui, repo, other)
3709 3763
3710 3764 repo._subtoppath = ui.expandpath(source)
3711 3765 try:
3712 3766 return hg.incoming(ui, repo, source, opts)
3713 3767 finally:
3714 3768 del repo._subtoppath
3715 3769
3716 3770
3717 3771 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3718 3772 def init(ui, dest=".", **opts):
3719 3773 """create a new repository in the given directory
3720 3774
3721 3775 Initialize a new repository in the given directory. If the given
3722 3776 directory does not exist, it will be created.
3723 3777
3724 3778 If no directory is given, the current directory is used.
3725 3779
3726 3780 It is possible to specify an ``ssh://`` URL as the destination.
3727 3781 See :hg:`help urls` for more information.
3728 3782
3729 3783 Returns 0 on success.
3730 3784 """
3731 3785 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3732 3786
3733 3787 @command('locate',
3734 3788 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3735 3789 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3736 3790 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3737 3791 ] + walkopts,
3738 3792 _('[OPTION]... [PATTERN]...'))
3739 3793 def locate(ui, repo, *pats, **opts):
3740 3794 """locate files matching specific patterns
3741 3795
3742 3796 Print files under Mercurial control in the working directory whose
3743 3797 names match the given patterns.
3744 3798
3745 3799 By default, this command searches all directories in the working
3746 3800 directory. To search just the current directory and its
3747 3801 subdirectories, use "--include .".
3748 3802
3749 3803 If no patterns are given to match, this command prints the names
3750 3804 of all files under Mercurial control in the working directory.
3751 3805
3752 3806 If you want to feed the output of this command into the "xargs"
3753 3807 command, use the -0 option to both this command and "xargs". This
3754 3808 will avoid the problem of "xargs" treating single filenames that
3755 3809 contain whitespace as multiple filenames.
3756 3810
3757 3811 Returns 0 if a match is found, 1 otherwise.
3758 3812 """
3759 3813 end = opts.get('print0') and '\0' or '\n'
3760 3814 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3761 3815
3762 3816 ret = 1
3763 3817 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3764 3818 m.bad = lambda x, y: False
3765 3819 for abs in repo[rev].walk(m):
3766 3820 if not rev and abs not in repo.dirstate:
3767 3821 continue
3768 3822 if opts.get('fullpath'):
3769 3823 ui.write(repo.wjoin(abs), end)
3770 3824 else:
3771 3825 ui.write(((pats and m.rel(abs)) or abs), end)
3772 3826 ret = 0
3773 3827
3774 3828 return ret
3775 3829
3776 3830 @command('^log|history',
3777 3831 [('f', 'follow', None,
3778 3832 _('follow changeset history, or file history across copies and renames')),
3779 3833 ('', 'follow-first', None,
3780 3834 _('only follow the first parent of merge changesets (DEPRECATED)')),
3781 3835 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3782 3836 ('C', 'copies', None, _('show copied files')),
3783 3837 ('k', 'keyword', [],
3784 3838 _('do case-insensitive search for a given text'), _('TEXT')),
3785 3839 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3786 3840 ('', 'removed', None, _('include revisions where files were removed')),
3787 3841 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
3788 3842 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3789 3843 ('', 'only-branch', [],
3790 3844 _('show only changesets within the given named branch (DEPRECATED)'),
3791 3845 _('BRANCH')),
3792 3846 ('b', 'branch', [],
3793 3847 _('show changesets within the given named branch'), _('BRANCH')),
3794 3848 ('P', 'prune', [],
3795 3849 _('do not display revision or any of its ancestors'), _('REV')),
3796 3850 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
3797 3851 ] + logopts + walkopts,
3798 3852 _('[OPTION]... [FILE]'))
3799 3853 def log(ui, repo, *pats, **opts):
3800 3854 """show revision history of entire repository or files
3801 3855
3802 3856 Print the revision history of the specified files or the entire
3803 3857 project.
3804 3858
3805 3859 If no revision range is specified, the default is ``tip:0`` unless
3806 3860 --follow is set, in which case the working directory parent is
3807 3861 used as the starting revision.
3808 3862
3809 3863 File history is shown without following rename or copy history of
3810 3864 files. Use -f/--follow with a filename to follow history across
3811 3865 renames and copies. --follow without a filename will only show
3812 3866 ancestors or descendants of the starting revision.
3813 3867
3814 3868 By default this command prints revision number and changeset id,
3815 3869 tags, non-trivial parents, user, date and time, and a summary for
3816 3870 each commit. When the -v/--verbose switch is used, the list of
3817 3871 changed files and full commit message are shown.
3818 3872
3819 3873 .. note::
3820 3874 log -p/--patch may generate unexpected diff output for merge
3821 3875 changesets, as it will only compare the merge changeset against
3822 3876 its first parent. Also, only files different from BOTH parents
3823 3877 will appear in files:.
3824 3878
3825 3879 .. note::
3826 3880 for performance reasons, log FILE may omit duplicate changes
3827 3881 made on branches and will not show deletions. To see all
3828 3882 changes including duplicates and deletions, use the --removed
3829 3883 switch.
3830 3884
3831 3885 .. container:: verbose
3832 3886
3833 3887 Some examples:
3834 3888
3835 3889 - changesets with full descriptions and file lists::
3836 3890
3837 3891 hg log -v
3838 3892
3839 3893 - changesets ancestral to the working directory::
3840 3894
3841 3895 hg log -f
3842 3896
3843 3897 - last 10 commits on the current branch::
3844 3898
3845 3899 hg log -l 10 -b .
3846 3900
3847 3901 - changesets showing all modifications of a file, including removals::
3848 3902
3849 3903 hg log --removed file.c
3850 3904
3851 3905 - all changesets that touch a directory, with diffs, excluding merges::
3852 3906
3853 3907 hg log -Mp lib/
3854 3908
3855 3909 - all revision numbers that match a keyword::
3856 3910
3857 3911 hg log -k bug --template "{rev}\\n"
3858 3912
3859 3913 - check if a given changeset is included is a tagged release::
3860 3914
3861 3915 hg log -r "a21ccf and ancestor(1.9)"
3862 3916
3863 3917 - find all changesets by some user in a date range::
3864 3918
3865 3919 hg log -k alice -d "may 2008 to jul 2008"
3866 3920
3867 3921 - summary of all changesets after the last tag::
3868 3922
3869 3923 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
3870 3924
3871 3925 See :hg:`help dates` for a list of formats valid for -d/--date.
3872 3926
3873 3927 See :hg:`help revisions` and :hg:`help revsets` for more about
3874 3928 specifying revisions.
3875 3929
3876 3930 Returns 0 on success.
3877 3931 """
3878 3932
3879 3933 matchfn = scmutil.match(repo[None], pats, opts)
3880 3934 limit = cmdutil.loglimit(opts)
3881 3935 count = 0
3882 3936
3883 3937 getrenamed, endrev = None, None
3884 3938 if opts.get('copies'):
3885 3939 if opts.get('rev'):
3886 3940 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3887 3941 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3888 3942
3889 3943 df = False
3890 3944 if opts["date"]:
3891 3945 df = util.matchdate(opts["date"])
3892 3946
3893 3947 branches = opts.get('branch', []) + opts.get('only_branch', [])
3894 3948 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3895 3949
3896 3950 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3897 3951 def prep(ctx, fns):
3898 3952 rev = ctx.rev()
3899 3953 parents = [p for p in repo.changelog.parentrevs(rev)
3900 3954 if p != nullrev]
3901 3955 if opts.get('no_merges') and len(parents) == 2:
3902 3956 return
3903 3957 if opts.get('only_merges') and len(parents) != 2:
3904 3958 return
3905 3959 if opts.get('branch') and ctx.branch() not in opts['branch']:
3906 3960 return
3907 3961 if not opts.get('hidden') and ctx.hidden():
3908 3962 return
3909 3963 if df and not df(ctx.date()[0]):
3910 3964 return
3911 3965
3912 3966 lower = encoding.lower
3913 3967 if opts.get('user'):
3914 3968 luser = lower(ctx.user())
3915 3969 for k in [lower(x) for x in opts['user']]:
3916 3970 if (k in luser):
3917 3971 break
3918 3972 else:
3919 3973 return
3920 3974 if opts.get('keyword'):
3921 3975 luser = lower(ctx.user())
3922 3976 ldesc = lower(ctx.description())
3923 3977 lfiles = lower(" ".join(ctx.files()))
3924 3978 for k in [lower(x) for x in opts['keyword']]:
3925 3979 if (k in luser or k in ldesc or k in lfiles):
3926 3980 break
3927 3981 else:
3928 3982 return
3929 3983
3930 3984 copies = None
3931 3985 if getrenamed is not None and rev:
3932 3986 copies = []
3933 3987 for fn in ctx.files():
3934 3988 rename = getrenamed(fn, rev)
3935 3989 if rename:
3936 3990 copies.append((fn, rename[0]))
3937 3991
3938 3992 revmatchfn = None
3939 3993 if opts.get('patch') or opts.get('stat'):
3940 3994 if opts.get('follow') or opts.get('follow_first'):
3941 3995 # note: this might be wrong when following through merges
3942 3996 revmatchfn = scmutil.match(repo[None], fns, default='path')
3943 3997 else:
3944 3998 revmatchfn = matchfn
3945 3999
3946 4000 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3947 4001
3948 4002 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3949 4003 if count == limit:
3950 4004 break
3951 4005 if displayer.flush(ctx.rev()):
3952 4006 count += 1
3953 4007 displayer.close()
3954 4008
3955 4009 @command('manifest',
3956 4010 [('r', 'rev', '', _('revision to display'), _('REV')),
3957 4011 ('', 'all', False, _("list files from all revisions"))],
3958 4012 _('[-r REV]'))
3959 4013 def manifest(ui, repo, node=None, rev=None, **opts):
3960 4014 """output the current or given revision of the project manifest
3961 4015
3962 4016 Print a list of version controlled files for the given revision.
3963 4017 If no revision is given, the first parent of the working directory
3964 4018 is used, or the null revision if no revision is checked out.
3965 4019
3966 4020 With -v, print file permissions, symlink and executable bits.
3967 4021 With --debug, print file revision hashes.
3968 4022
3969 4023 If option --all is specified, the list of all files from all revisions
3970 4024 is printed. This includes deleted and renamed files.
3971 4025
3972 4026 Returns 0 on success.
3973 4027 """
3974 4028 if opts.get('all'):
3975 4029 if rev or node:
3976 4030 raise util.Abort(_("can't specify a revision with --all"))
3977 4031
3978 4032 res = []
3979 4033 prefix = "data/"
3980 4034 suffix = ".i"
3981 4035 plen = len(prefix)
3982 4036 slen = len(suffix)
3983 4037 lock = repo.lock()
3984 4038 try:
3985 4039 for fn, b, size in repo.store.datafiles():
3986 4040 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3987 4041 res.append(fn[plen:-slen])
3988 4042 finally:
3989 4043 lock.release()
3990 4044 for f in sorted(res):
3991 4045 ui.write("%s\n" % f)
3992 4046 return
3993 4047
3994 4048 if rev and node:
3995 4049 raise util.Abort(_("please specify just one revision"))
3996 4050
3997 4051 if not node:
3998 4052 node = rev
3999 4053
4000 4054 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
4001 4055 ctx = scmutil.revsingle(repo, node)
4002 4056 for f in ctx:
4003 4057 if ui.debugflag:
4004 4058 ui.write("%40s " % hex(ctx.manifest()[f]))
4005 4059 if ui.verbose:
4006 4060 ui.write(decor[ctx.flags(f)])
4007 4061 ui.write("%s\n" % f)
4008 4062
4009 4063 @command('^merge',
4010 4064 [('f', 'force', None, _('force a merge with outstanding changes')),
4011 4065 ('r', 'rev', '', _('revision to merge'), _('REV')),
4012 4066 ('P', 'preview', None,
4013 4067 _('review revisions to merge (no merge is performed)'))
4014 4068 ] + mergetoolopts,
4015 4069 _('[-P] [-f] [[-r] REV]'))
4016 4070 def merge(ui, repo, node=None, **opts):
4017 4071 """merge working directory with another revision
4018 4072
4019 4073 The current working directory is updated with all changes made in
4020 4074 the requested revision since the last common predecessor revision.
4021 4075
4022 4076 Files that changed between either parent are marked as changed for
4023 4077 the next commit and a commit must be performed before any further
4024 4078 updates to the repository are allowed. The next commit will have
4025 4079 two parents.
4026 4080
4027 4081 ``--tool`` can be used to specify the merge tool used for file
4028 4082 merges. It overrides the HGMERGE environment variable and your
4029 4083 configuration files. See :hg:`help merge-tools` for options.
4030 4084
4031 4085 If no revision is specified, the working directory's parent is a
4032 4086 head revision, and the current branch contains exactly one other
4033 4087 head, the other head is merged with by default. Otherwise, an
4034 4088 explicit revision with which to merge with must be provided.
4035 4089
4036 4090 :hg:`resolve` must be used to resolve unresolved files.
4037 4091
4038 4092 To undo an uncommitted merge, use :hg:`update --clean .` which
4039 4093 will check out a clean copy of the original merge parent, losing
4040 4094 all changes.
4041 4095
4042 4096 Returns 0 on success, 1 if there are unresolved files.
4043 4097 """
4044 4098
4045 4099 if opts.get('rev') and node:
4046 4100 raise util.Abort(_("please specify just one revision"))
4047 4101 if not node:
4048 4102 node = opts.get('rev')
4049 4103
4050 4104 if not node:
4051 4105 branch = repo[None].branch()
4052 4106 bheads = repo.branchheads(branch)
4053 4107 if len(bheads) > 2:
4054 4108 raise util.Abort(_("branch '%s' has %d heads - "
4055 4109 "please merge with an explicit rev")
4056 4110 % (branch, len(bheads)),
4057 4111 hint=_("run 'hg heads .' to see heads"))
4058 4112
4059 4113 parent = repo.dirstate.p1()
4060 4114 if len(bheads) == 1:
4061 4115 if len(repo.heads()) > 1:
4062 4116 raise util.Abort(_("branch '%s' has one head - "
4063 4117 "please merge with an explicit rev")
4064 4118 % branch,
4065 4119 hint=_("run 'hg heads' to see all heads"))
4066 4120 msg, hint = _('nothing to merge'), None
4067 4121 if parent != repo.lookup(branch):
4068 4122 hint = _("use 'hg update' instead")
4069 4123 raise util.Abort(msg, hint=hint)
4070 4124
4071 4125 if parent not in bheads:
4072 4126 raise util.Abort(_('working directory not at a head revision'),
4073 4127 hint=_("use 'hg update' or merge with an "
4074 4128 "explicit revision"))
4075 4129 node = parent == bheads[0] and bheads[-1] or bheads[0]
4076 4130 else:
4077 4131 node = scmutil.revsingle(repo, node).node()
4078 4132
4079 4133 if opts.get('preview'):
4080 4134 # find nodes that are ancestors of p2 but not of p1
4081 4135 p1 = repo.lookup('.')
4082 4136 p2 = repo.lookup(node)
4083 4137 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4084 4138
4085 4139 displayer = cmdutil.show_changeset(ui, repo, opts)
4086 4140 for node in nodes:
4087 4141 displayer.show(repo[node])
4088 4142 displayer.close()
4089 4143 return 0
4090 4144
4091 4145 try:
4092 4146 # ui.forcemerge is an internal variable, do not document
4093 4147 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4094 4148 return hg.merge(repo, node, force=opts.get('force'))
4095 4149 finally:
4096 4150 ui.setconfig('ui', 'forcemerge', '')
4097 4151
4098 4152 @command('outgoing|out',
4099 4153 [('f', 'force', None, _('run even when the destination is unrelated')),
4100 4154 ('r', 'rev', [],
4101 4155 _('a changeset intended to be included in the destination'), _('REV')),
4102 4156 ('n', 'newest-first', None, _('show newest record first')),
4103 4157 ('B', 'bookmarks', False, _('compare bookmarks')),
4104 4158 ('b', 'branch', [], _('a specific branch you would like to push'),
4105 4159 _('BRANCH')),
4106 4160 ] + logopts + remoteopts + subrepoopts,
4107 4161 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4108 4162 def outgoing(ui, repo, dest=None, **opts):
4109 4163 """show changesets not found in the destination
4110 4164
4111 4165 Show changesets not found in the specified destination repository
4112 4166 or the default push location. These are the changesets that would
4113 4167 be pushed if a push was requested.
4114 4168
4115 4169 See pull for details of valid destination formats.
4116 4170
4117 4171 Returns 0 if there are outgoing changes, 1 otherwise.
4118 4172 """
4119 4173
4120 4174 if opts.get('bookmarks'):
4121 4175 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4122 4176 dest, branches = hg.parseurl(dest, opts.get('branch'))
4123 4177 other = hg.peer(repo, opts, dest)
4124 4178 if 'bookmarks' not in other.listkeys('namespaces'):
4125 4179 ui.warn(_("remote doesn't support bookmarks\n"))
4126 4180 return 0
4127 4181 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4128 4182 return bookmarks.diff(ui, other, repo)
4129 4183
4130 4184 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4131 4185 try:
4132 4186 return hg.outgoing(ui, repo, dest, opts)
4133 4187 finally:
4134 4188 del repo._subtoppath
4135 4189
4136 4190 @command('parents',
4137 4191 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4138 4192 ] + templateopts,
4139 4193 _('[-r REV] [FILE]'))
4140 4194 def parents(ui, repo, file_=None, **opts):
4141 4195 """show the parents of the working directory or revision
4142 4196
4143 4197 Print the working directory's parent revisions. If a revision is
4144 4198 given via -r/--rev, the parent of that revision will be printed.
4145 4199 If a file argument is given, the revision in which the file was
4146 4200 last changed (before the working directory revision or the
4147 4201 argument to --rev if given) is printed.
4148 4202
4149 4203 Returns 0 on success.
4150 4204 """
4151 4205
4152 4206 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4153 4207
4154 4208 if file_:
4155 4209 m = scmutil.match(ctx, (file_,), opts)
4156 4210 if m.anypats() or len(m.files()) != 1:
4157 4211 raise util.Abort(_('can only specify an explicit filename'))
4158 4212 file_ = m.files()[0]
4159 4213 filenodes = []
4160 4214 for cp in ctx.parents():
4161 4215 if not cp:
4162 4216 continue
4163 4217 try:
4164 4218 filenodes.append(cp.filenode(file_))
4165 4219 except error.LookupError:
4166 4220 pass
4167 4221 if not filenodes:
4168 4222 raise util.Abort(_("'%s' not found in manifest!") % file_)
4169 4223 fl = repo.file(file_)
4170 4224 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4171 4225 else:
4172 4226 p = [cp.node() for cp in ctx.parents()]
4173 4227
4174 4228 displayer = cmdutil.show_changeset(ui, repo, opts)
4175 4229 for n in p:
4176 4230 if n != nullid:
4177 4231 displayer.show(repo[n])
4178 4232 displayer.close()
4179 4233
4180 4234 @command('paths', [], _('[NAME]'))
4181 4235 def paths(ui, repo, search=None):
4182 4236 """show aliases for remote repositories
4183 4237
4184 4238 Show definition of symbolic path name NAME. If no name is given,
4185 4239 show definition of all available names.
4186 4240
4187 4241 Option -q/--quiet suppresses all output when searching for NAME
4188 4242 and shows only the path names when listing all definitions.
4189 4243
4190 4244 Path names are defined in the [paths] section of your
4191 4245 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4192 4246 repository, ``.hg/hgrc`` is used, too.
4193 4247
4194 4248 The path names ``default`` and ``default-push`` have a special
4195 4249 meaning. When performing a push or pull operation, they are used
4196 4250 as fallbacks if no location is specified on the command-line.
4197 4251 When ``default-push`` is set, it will be used for push and
4198 4252 ``default`` will be used for pull; otherwise ``default`` is used
4199 4253 as the fallback for both. When cloning a repository, the clone
4200 4254 source is written as ``default`` in ``.hg/hgrc``. Note that
4201 4255 ``default`` and ``default-push`` apply to all inbound (e.g.
4202 4256 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4203 4257 :hg:`bundle`) operations.
4204 4258
4205 4259 See :hg:`help urls` for more information.
4206 4260
4207 4261 Returns 0 on success.
4208 4262 """
4209 4263 if search:
4210 4264 for name, path in ui.configitems("paths"):
4211 4265 if name == search:
4212 4266 ui.status("%s\n" % util.hidepassword(path))
4213 4267 return
4214 4268 if not ui.quiet:
4215 4269 ui.warn(_("not found!\n"))
4216 4270 return 1
4217 4271 else:
4218 4272 for name, path in ui.configitems("paths"):
4219 4273 if ui.quiet:
4220 4274 ui.write("%s\n" % name)
4221 4275 else:
4222 4276 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4223 4277
4224 4278 @command('^phase',
4225 4279 [('p', 'public', False, _('set changeset phase to public')),
4226 4280 ('d', 'draft', False, _('set changeset phase to draft')),
4227 4281 ('s', 'secret', False, _('set changeset phase to secret')),
4228 4282 ('f', 'force', False, _('allow to move boundary backward')),
4229 4283 ('r', 'rev', [], _('target revision'), _('REV')),
4230 4284 ],
4231 4285 _('[-p|-d|-s] [-f] [-r] REV...'))
4232 4286 def phase(ui, repo, *revs, **opts):
4233 4287 """set or show the current phase name
4234 4288
4235 4289 With no argument, show the phase name of specified revisions.
4236 4290
4237 4291 With one of -p/--public, -d/--draft or -s/--secret, change the
4238 4292 phase value of the specified revisions.
4239 4293
4240 4294 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4241 4295 lower phase to an higher phase. Phases are ordered as follows::
4242 4296
4243 4297 public < draft < secret
4244 4298
4245 4299 Return 0 on success, 1 if no phases were changed or some could not
4246 4300 be changed.
4247 4301 """
4248 4302 # search for a unique phase argument
4249 4303 targetphase = None
4250 4304 for idx, name in enumerate(phases.phasenames):
4251 4305 if opts[name]:
4252 4306 if targetphase is not None:
4253 4307 raise util.Abort(_('only one phase can be specified'))
4254 4308 targetphase = idx
4255 4309
4256 4310 # look for specified revision
4257 4311 revs = list(revs)
4258 4312 revs.extend(opts['rev'])
4259 4313 if not revs:
4260 4314 raise util.Abort(_('no revisions specified'))
4261 4315
4262 4316 revs = scmutil.revrange(repo, revs)
4263 4317
4264 4318 lock = None
4265 4319 ret = 0
4266 4320 if targetphase is None:
4267 4321 # display
4268 4322 for r in revs:
4269 4323 ctx = repo[r]
4270 4324 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4271 4325 else:
4272 4326 lock = repo.lock()
4273 4327 try:
4274 4328 # set phase
4275 4329 nodes = [ctx.node() for ctx in repo.set('%ld', revs)]
4276 4330 if not nodes:
4277 4331 raise util.Abort(_('empty revision set'))
4278 4332 olddata = repo._phaserev[:]
4279 4333 phases.advanceboundary(repo, targetphase, nodes)
4280 4334 if opts['force']:
4281 4335 phases.retractboundary(repo, targetphase, nodes)
4282 4336 finally:
4283 4337 lock.release()
4284 4338 if olddata is not None:
4285 4339 changes = 0
4286 4340 newdata = repo._phaserev
4287 4341 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4288 4342 rejected = [n for n in nodes
4289 4343 if newdata[repo[n].rev()] < targetphase]
4290 4344 if rejected:
4291 4345 ui.warn(_('cannot move %i changesets to a more permissive '
4292 4346 'phase, use --force\n') % len(rejected))
4293 4347 ret = 1
4294 4348 if changes:
4295 4349 msg = _('phase changed for %i changesets\n') % changes
4296 4350 if ret:
4297 4351 ui.status(msg)
4298 4352 else:
4299 4353 ui.note(msg)
4300 4354 else:
4301 4355 ui.warn(_('no phases changed\n'))
4302 4356 ret = 1
4303 4357 return ret
4304 4358
4305 4359 def postincoming(ui, repo, modheads, optupdate, checkout):
4306 4360 if modheads == 0:
4307 4361 return
4308 4362 if optupdate:
4309 4363 movemarkfrom = repo['.'].node()
4310 4364 try:
4311 4365 ret = hg.update(repo, checkout)
4312 4366 except util.Abort, inst:
4313 4367 ui.warn(_("not updating: %s\n") % str(inst))
4314 4368 return 0
4315 4369 if not ret and not checkout:
4316 4370 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4317 4371 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4318 4372 return ret
4319 4373 if modheads > 1:
4320 4374 currentbranchheads = len(repo.branchheads())
4321 4375 if currentbranchheads == modheads:
4322 4376 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4323 4377 elif currentbranchheads > 1:
4324 4378 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
4325 4379 else:
4326 4380 ui.status(_("(run 'hg heads' to see heads)\n"))
4327 4381 else:
4328 4382 ui.status(_("(run 'hg update' to get a working copy)\n"))
4329 4383
4330 4384 @command('^pull',
4331 4385 [('u', 'update', None,
4332 4386 _('update to new branch head if changesets were pulled')),
4333 4387 ('f', 'force', None, _('run even when remote repository is unrelated')),
4334 4388 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4335 4389 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4336 4390 ('b', 'branch', [], _('a specific branch you would like to pull'),
4337 4391 _('BRANCH')),
4338 4392 ] + remoteopts,
4339 4393 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4340 4394 def pull(ui, repo, source="default", **opts):
4341 4395 """pull changes from the specified source
4342 4396
4343 4397 Pull changes from a remote repository to a local one.
4344 4398
4345 4399 This finds all changes from the repository at the specified path
4346 4400 or URL and adds them to a local repository (the current one unless
4347 4401 -R is specified). By default, this does not update the copy of the
4348 4402 project in the working directory.
4349 4403
4350 4404 Use :hg:`incoming` if you want to see what would have been added
4351 4405 by a pull at the time you issued this command. If you then decide
4352 4406 to add those changes to the repository, you should use :hg:`pull
4353 4407 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4354 4408
4355 4409 If SOURCE is omitted, the 'default' path will be used.
4356 4410 See :hg:`help urls` for more information.
4357 4411
4358 4412 Returns 0 on success, 1 if an update had unresolved files.
4359 4413 """
4360 4414 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4361 4415 other = hg.peer(repo, opts, source)
4362 4416 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4363 4417 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4364 4418
4365 4419 if opts.get('bookmark'):
4366 4420 if not revs:
4367 4421 revs = []
4368 4422 rb = other.listkeys('bookmarks')
4369 4423 for b in opts['bookmark']:
4370 4424 if b not in rb:
4371 4425 raise util.Abort(_('remote bookmark %s not found!') % b)
4372 4426 revs.append(rb[b])
4373 4427
4374 4428 if revs:
4375 4429 try:
4376 4430 revs = [other.lookup(rev) for rev in revs]
4377 4431 except error.CapabilityError:
4378 4432 err = _("other repository doesn't support revision lookup, "
4379 4433 "so a rev cannot be specified.")
4380 4434 raise util.Abort(err)
4381 4435
4382 4436 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4383 4437 bookmarks.updatefromremote(ui, repo, other, source)
4384 4438 if checkout:
4385 4439 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4386 4440 repo._subtoppath = source
4387 4441 try:
4388 4442 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4389 4443
4390 4444 finally:
4391 4445 del repo._subtoppath
4392 4446
4393 4447 # update specified bookmarks
4394 4448 if opts.get('bookmark'):
4395 4449 for b in opts['bookmark']:
4396 4450 # explicit pull overrides local bookmark if any
4397 4451 ui.status(_("importing bookmark %s\n") % b)
4398 4452 repo._bookmarks[b] = repo[rb[b]].node()
4399 4453 bookmarks.write(repo)
4400 4454
4401 4455 return ret
4402 4456
4403 4457 @command('^push',
4404 4458 [('f', 'force', None, _('force push')),
4405 4459 ('r', 'rev', [],
4406 4460 _('a changeset intended to be included in the destination'),
4407 4461 _('REV')),
4408 4462 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4409 4463 ('b', 'branch', [],
4410 4464 _('a specific branch you would like to push'), _('BRANCH')),
4411 4465 ('', 'new-branch', False, _('allow pushing a new branch')),
4412 4466 ] + remoteopts,
4413 4467 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4414 4468 def push(ui, repo, dest=None, **opts):
4415 4469 """push changes to the specified destination
4416 4470
4417 4471 Push changesets from the local repository to the specified
4418 4472 destination.
4419 4473
4420 4474 This operation is symmetrical to pull: it is identical to a pull
4421 4475 in the destination repository from the current one.
4422 4476
4423 4477 By default, push will not allow creation of new heads at the
4424 4478 destination, since multiple heads would make it unclear which head
4425 4479 to use. In this situation, it is recommended to pull and merge
4426 4480 before pushing.
4427 4481
4428 4482 Use --new-branch if you want to allow push to create a new named
4429 4483 branch that is not present at the destination. This allows you to
4430 4484 only create a new branch without forcing other changes.
4431 4485
4432 4486 Use -f/--force to override the default behavior and push all
4433 4487 changesets on all branches.
4434 4488
4435 4489 If -r/--rev is used, the specified revision and all its ancestors
4436 4490 will be pushed to the remote repository.
4437 4491
4438 4492 Please see :hg:`help urls` for important details about ``ssh://``
4439 4493 URLs. If DESTINATION is omitted, a default path will be used.
4440 4494
4441 4495 Returns 0 if push was successful, 1 if nothing to push.
4442 4496 """
4443 4497
4444 4498 if opts.get('bookmark'):
4445 4499 for b in opts['bookmark']:
4446 4500 # translate -B options to -r so changesets get pushed
4447 4501 if b in repo._bookmarks:
4448 4502 opts.setdefault('rev', []).append(b)
4449 4503 else:
4450 4504 # if we try to push a deleted bookmark, translate it to null
4451 4505 # this lets simultaneous -r, -b options continue working
4452 4506 opts.setdefault('rev', []).append("null")
4453 4507
4454 4508 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4455 4509 dest, branches = hg.parseurl(dest, opts.get('branch'))
4456 4510 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4457 4511 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4458 4512 other = hg.peer(repo, opts, dest)
4459 4513 if revs:
4460 4514 revs = [repo.lookup(rev) for rev in revs]
4461 4515
4462 4516 repo._subtoppath = dest
4463 4517 try:
4464 4518 # push subrepos depth-first for coherent ordering
4465 4519 c = repo['']
4466 4520 subs = c.substate # only repos that are committed
4467 4521 for s in sorted(subs):
4468 4522 if c.sub(s).push(opts) == 0:
4469 4523 return False
4470 4524 finally:
4471 4525 del repo._subtoppath
4472 4526 result = repo.push(other, opts.get('force'), revs=revs,
4473 4527 newbranch=opts.get('new_branch'))
4474 4528
4475 4529 result = not result
4476 4530
4477 4531 if opts.get('bookmark'):
4478 4532 rb = other.listkeys('bookmarks')
4479 4533 for b in opts['bookmark']:
4480 4534 # explicit push overrides remote bookmark if any
4481 4535 if b in repo._bookmarks:
4482 4536 ui.status(_("exporting bookmark %s\n") % b)
4483 4537 new = repo[b].hex()
4484 4538 elif b in rb:
4485 4539 ui.status(_("deleting remote bookmark %s\n") % b)
4486 4540 new = '' # delete
4487 4541 else:
4488 4542 ui.warn(_('bookmark %s does not exist on the local '
4489 4543 'or remote repository!\n') % b)
4490 4544 return 2
4491 4545 old = rb.get(b, '')
4492 4546 r = other.pushkey('bookmarks', b, old, new)
4493 4547 if not r:
4494 4548 ui.warn(_('updating bookmark %s failed!\n') % b)
4495 4549 if not result:
4496 4550 result = 2
4497 4551
4498 4552 return result
4499 4553
4500 4554 @command('recover', [])
4501 4555 def recover(ui, repo):
4502 4556 """roll back an interrupted transaction
4503 4557
4504 4558 Recover from an interrupted commit or pull.
4505 4559
4506 4560 This command tries to fix the repository status after an
4507 4561 interrupted operation. It should only be necessary when Mercurial
4508 4562 suggests it.
4509 4563
4510 4564 Returns 0 if successful, 1 if nothing to recover or verify fails.
4511 4565 """
4512 4566 if repo.recover():
4513 4567 return hg.verify(repo)
4514 4568 return 1
4515 4569
4516 4570 @command('^remove|rm',
4517 4571 [('A', 'after', None, _('record delete for missing files')),
4518 4572 ('f', 'force', None,
4519 4573 _('remove (and delete) file even if added or modified')),
4520 4574 ] + walkopts,
4521 4575 _('[OPTION]... FILE...'))
4522 4576 def remove(ui, repo, *pats, **opts):
4523 4577 """remove the specified files on the next commit
4524 4578
4525 4579 Schedule the indicated files for removal from the current branch.
4526 4580
4527 4581 This command schedules the files to be removed at the next commit.
4528 4582 To undo a remove before that, see :hg:`revert`. To undo added
4529 4583 files, see :hg:`forget`.
4530 4584
4531 4585 .. container:: verbose
4532 4586
4533 4587 -A/--after can be used to remove only files that have already
4534 4588 been deleted, -f/--force can be used to force deletion, and -Af
4535 4589 can be used to remove files from the next revision without
4536 4590 deleting them from the working directory.
4537 4591
4538 4592 The following table details the behavior of remove for different
4539 4593 file states (columns) and option combinations (rows). The file
4540 4594 states are Added [A], Clean [C], Modified [M] and Missing [!]
4541 4595 (as reported by :hg:`status`). The actions are Warn, Remove
4542 4596 (from branch) and Delete (from disk):
4543 4597
4544 4598 ======= == == == ==
4545 4599 A C M !
4546 4600 ======= == == == ==
4547 4601 none W RD W R
4548 4602 -f R RD RD R
4549 4603 -A W W W R
4550 4604 -Af R R R R
4551 4605 ======= == == == ==
4552 4606
4553 4607 Note that remove never deletes files in Added [A] state from the
4554 4608 working directory, not even if option --force is specified.
4555 4609
4556 4610 Returns 0 on success, 1 if any warnings encountered.
4557 4611 """
4558 4612
4559 4613 ret = 0
4560 4614 after, force = opts.get('after'), opts.get('force')
4561 4615 if not pats and not after:
4562 4616 raise util.Abort(_('no files specified'))
4563 4617
4564 4618 m = scmutil.match(repo[None], pats, opts)
4565 4619 s = repo.status(match=m, clean=True)
4566 4620 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4567 4621
4568 4622 for f in m.files():
4569 4623 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
4570 4624 if os.path.exists(m.rel(f)):
4571 4625 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4572 4626 ret = 1
4573 4627
4574 4628 if force:
4575 4629 list = modified + deleted + clean + added
4576 4630 elif after:
4577 4631 list = deleted
4578 4632 for f in modified + added + clean:
4579 4633 ui.warn(_('not removing %s: file still exists (use -f'
4580 4634 ' to force removal)\n') % m.rel(f))
4581 4635 ret = 1
4582 4636 else:
4583 4637 list = deleted + clean
4584 4638 for f in modified:
4585 4639 ui.warn(_('not removing %s: file is modified (use -f'
4586 4640 ' to force removal)\n') % m.rel(f))
4587 4641 ret = 1
4588 4642 for f in added:
4589 4643 ui.warn(_('not removing %s: file has been marked for add'
4590 4644 ' (use forget to undo)\n') % m.rel(f))
4591 4645 ret = 1
4592 4646
4593 4647 for f in sorted(list):
4594 4648 if ui.verbose or not m.exact(f):
4595 4649 ui.status(_('removing %s\n') % m.rel(f))
4596 4650
4597 4651 wlock = repo.wlock()
4598 4652 try:
4599 4653 if not after:
4600 4654 for f in list:
4601 4655 if f in added:
4602 4656 continue # we never unlink added files on remove
4603 4657 try:
4604 4658 util.unlinkpath(repo.wjoin(f))
4605 4659 except OSError, inst:
4606 4660 if inst.errno != errno.ENOENT:
4607 4661 raise
4608 4662 repo[None].forget(list)
4609 4663 finally:
4610 4664 wlock.release()
4611 4665
4612 4666 return ret
4613 4667
4614 4668 @command('rename|move|mv',
4615 4669 [('A', 'after', None, _('record a rename that has already occurred')),
4616 4670 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4617 4671 ] + walkopts + dryrunopts,
4618 4672 _('[OPTION]... SOURCE... DEST'))
4619 4673 def rename(ui, repo, *pats, **opts):
4620 4674 """rename files; equivalent of copy + remove
4621 4675
4622 4676 Mark dest as copies of sources; mark sources for deletion. If dest
4623 4677 is a directory, copies are put in that directory. If dest is a
4624 4678 file, there can only be one source.
4625 4679
4626 4680 By default, this command copies the contents of files as they
4627 4681 exist in the working directory. If invoked with -A/--after, the
4628 4682 operation is recorded, but no copying is performed.
4629 4683
4630 4684 This command takes effect at the next commit. To undo a rename
4631 4685 before that, see :hg:`revert`.
4632 4686
4633 4687 Returns 0 on success, 1 if errors are encountered.
4634 4688 """
4635 4689 wlock = repo.wlock(False)
4636 4690 try:
4637 4691 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4638 4692 finally:
4639 4693 wlock.release()
4640 4694
4641 4695 @command('resolve',
4642 4696 [('a', 'all', None, _('select all unresolved files')),
4643 4697 ('l', 'list', None, _('list state of files needing merge')),
4644 4698 ('m', 'mark', None, _('mark files as resolved')),
4645 4699 ('u', 'unmark', None, _('mark files as unresolved')),
4646 4700 ('n', 'no-status', None, _('hide status prefix'))]
4647 4701 + mergetoolopts + walkopts,
4648 4702 _('[OPTION]... [FILE]...'))
4649 4703 def resolve(ui, repo, *pats, **opts):
4650 4704 """redo merges or set/view the merge status of files
4651 4705
4652 4706 Merges with unresolved conflicts are often the result of
4653 4707 non-interactive merging using the ``internal:merge`` configuration
4654 4708 setting, or a command-line merge tool like ``diff3``. The resolve
4655 4709 command is used to manage the files involved in a merge, after
4656 4710 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4657 4711 working directory must have two parents). See :hg:`help
4658 4712 merge-tools` for information on configuring merge tools.
4659 4713
4660 4714 The resolve command can be used in the following ways:
4661 4715
4662 4716 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4663 4717 files, discarding any previous merge attempts. Re-merging is not
4664 4718 performed for files already marked as resolved. Use ``--all/-a``
4665 4719 to select all unresolved files. ``--tool`` can be used to specify
4666 4720 the merge tool used for the given files. It overrides the HGMERGE
4667 4721 environment variable and your configuration files. Previous file
4668 4722 contents are saved with a ``.orig`` suffix.
4669 4723
4670 4724 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4671 4725 (e.g. after having manually fixed-up the files). The default is
4672 4726 to mark all unresolved files.
4673 4727
4674 4728 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4675 4729 default is to mark all resolved files.
4676 4730
4677 4731 - :hg:`resolve -l`: list files which had or still have conflicts.
4678 4732 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4679 4733
4680 4734 Note that Mercurial will not let you commit files with unresolved
4681 4735 merge conflicts. You must use :hg:`resolve -m ...` before you can
4682 4736 commit after a conflicting merge.
4683 4737
4684 4738 Returns 0 on success, 1 if any files fail a resolve attempt.
4685 4739 """
4686 4740
4687 4741 all, mark, unmark, show, nostatus = \
4688 4742 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4689 4743
4690 4744 if (show and (mark or unmark)) or (mark and unmark):
4691 4745 raise util.Abort(_("too many options specified"))
4692 4746 if pats and all:
4693 4747 raise util.Abort(_("can't specify --all and patterns"))
4694 4748 if not (all or pats or show or mark or unmark):
4695 4749 raise util.Abort(_('no files or directories specified; '
4696 4750 'use --all to remerge all files'))
4697 4751
4698 4752 ms = mergemod.mergestate(repo)
4699 4753 m = scmutil.match(repo[None], pats, opts)
4700 4754 ret = 0
4701 4755
4702 4756 for f in ms:
4703 4757 if m(f):
4704 4758 if show:
4705 4759 if nostatus:
4706 4760 ui.write("%s\n" % f)
4707 4761 else:
4708 4762 ui.write("%s %s\n" % (ms[f].upper(), f),
4709 4763 label='resolve.' +
4710 4764 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4711 4765 elif mark:
4712 4766 ms.mark(f, "r")
4713 4767 elif unmark:
4714 4768 ms.mark(f, "u")
4715 4769 else:
4716 4770 wctx = repo[None]
4717 4771 mctx = wctx.parents()[-1]
4718 4772
4719 4773 # backup pre-resolve (merge uses .orig for its own purposes)
4720 4774 a = repo.wjoin(f)
4721 4775 util.copyfile(a, a + ".resolve")
4722 4776
4723 4777 try:
4724 4778 # resolve file
4725 4779 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4726 4780 if ms.resolve(f, wctx, mctx):
4727 4781 ret = 1
4728 4782 finally:
4729 4783 ui.setconfig('ui', 'forcemerge', '')
4730 4784
4731 4785 # replace filemerge's .orig file with our resolve file
4732 4786 util.rename(a + ".resolve", a + ".orig")
4733 4787
4734 4788 ms.commit()
4735 4789 return ret
4736 4790
4737 4791 @command('revert',
4738 4792 [('a', 'all', None, _('revert all changes when no arguments given')),
4739 4793 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4740 4794 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4741 4795 ('C', 'no-backup', None, _('do not save backup copies of files')),
4742 4796 ] + walkopts + dryrunopts,
4743 4797 _('[OPTION]... [-r REV] [NAME]...'))
4744 4798 def revert(ui, repo, *pats, **opts):
4745 4799 """restore files to their checkout state
4746 4800
4747 4801 .. note::
4748 4802 To check out earlier revisions, you should use :hg:`update REV`.
4749 4803 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4750 4804
4751 4805 With no revision specified, revert the specified files or directories
4752 4806 to the contents they had in the parent of the working directory.
4753 4807 This restores the contents of files to an unmodified
4754 4808 state and unschedules adds, removes, copies, and renames. If the
4755 4809 working directory has two parents, you must explicitly specify a
4756 4810 revision.
4757 4811
4758 4812 Using the -r/--rev or -d/--date options, revert the given files or
4759 4813 directories to their states as of a specific revision. Because
4760 4814 revert does not change the working directory parents, this will
4761 4815 cause these files to appear modified. This can be helpful to "back
4762 4816 out" some or all of an earlier change. See :hg:`backout` for a
4763 4817 related method.
4764 4818
4765 4819 Modified files are saved with a .orig suffix before reverting.
4766 4820 To disable these backups, use --no-backup.
4767 4821
4768 4822 See :hg:`help dates` for a list of formats valid for -d/--date.
4769 4823
4770 4824 Returns 0 on success.
4771 4825 """
4772 4826
4773 4827 if opts.get("date"):
4774 4828 if opts.get("rev"):
4775 4829 raise util.Abort(_("you can't specify a revision and a date"))
4776 4830 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4777 4831
4778 4832 parent, p2 = repo.dirstate.parents()
4779 4833 if not opts.get('rev') and p2 != nullid:
4780 4834 # revert after merge is a trap for new users (issue2915)
4781 4835 raise util.Abort(_('uncommitted merge with no revision specified'),
4782 4836 hint=_('use "hg update" or see "hg help revert"'))
4783 4837
4784 4838 ctx = scmutil.revsingle(repo, opts.get('rev'))
4785 4839
4786 4840 if not pats and not opts.get('all'):
4787 4841 msg = _("no files or directories specified")
4788 4842 if p2 != nullid:
4789 4843 hint = _("uncommitted merge, use --all to discard all changes,"
4790 4844 " or 'hg update -C .' to abort the merge")
4791 4845 raise util.Abort(msg, hint=hint)
4792 4846 dirty = util.any(repo.status())
4793 4847 node = ctx.node()
4794 4848 if node != parent:
4795 4849 if dirty:
4796 4850 hint = _("uncommitted changes, use --all to discard all"
4797 4851 " changes, or 'hg update %s' to update") % ctx.rev()
4798 4852 else:
4799 4853 hint = _("use --all to revert all files,"
4800 4854 " or 'hg update %s' to update") % ctx.rev()
4801 4855 elif dirty:
4802 4856 hint = _("uncommitted changes, use --all to discard all changes")
4803 4857 else:
4804 4858 hint = _("use --all to revert all files")
4805 4859 raise util.Abort(msg, hint=hint)
4806 4860
4807 4861 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
4808 4862
4809 4863 @command('rollback', dryrunopts +
4810 4864 [('f', 'force', False, _('ignore safety measures'))])
4811 4865 def rollback(ui, repo, **opts):
4812 4866 """roll back the last transaction (dangerous)
4813 4867
4814 4868 This command should be used with care. There is only one level of
4815 4869 rollback, and there is no way to undo a rollback. It will also
4816 4870 restore the dirstate at the time of the last transaction, losing
4817 4871 any dirstate changes since that time. This command does not alter
4818 4872 the working directory.
4819 4873
4820 4874 Transactions are used to encapsulate the effects of all commands
4821 4875 that create new changesets or propagate existing changesets into a
4822 4876 repository. For example, the following commands are transactional,
4823 4877 and their effects can be rolled back:
4824 4878
4825 4879 - commit
4826 4880 - import
4827 4881 - pull
4828 4882 - push (with this repository as the destination)
4829 4883 - unbundle
4830 4884
4831 4885 To avoid permanent data loss, rollback will refuse to rollback a
4832 4886 commit transaction if it isn't checked out. Use --force to
4833 4887 override this protection.
4834 4888
4835 4889 This command is not intended for use on public repositories. Once
4836 4890 changes are visible for pull by other users, rolling a transaction
4837 4891 back locally is ineffective (someone else may already have pulled
4838 4892 the changes). Furthermore, a race is possible with readers of the
4839 4893 repository; for example an in-progress pull from the repository
4840 4894 may fail if a rollback is performed.
4841 4895
4842 4896 Returns 0 on success, 1 if no rollback data is available.
4843 4897 """
4844 4898 return repo.rollback(dryrun=opts.get('dry_run'),
4845 4899 force=opts.get('force'))
4846 4900
4847 4901 @command('root', [])
4848 4902 def root(ui, repo):
4849 4903 """print the root (top) of the current working directory
4850 4904
4851 4905 Print the root directory of the current repository.
4852 4906
4853 4907 Returns 0 on success.
4854 4908 """
4855 4909 ui.write(repo.root + "\n")
4856 4910
4857 4911 @command('^serve',
4858 4912 [('A', 'accesslog', '', _('name of access log file to write to'),
4859 4913 _('FILE')),
4860 4914 ('d', 'daemon', None, _('run server in background')),
4861 4915 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4862 4916 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4863 4917 # use string type, then we can check if something was passed
4864 4918 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4865 4919 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4866 4920 _('ADDR')),
4867 4921 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4868 4922 _('PREFIX')),
4869 4923 ('n', 'name', '',
4870 4924 _('name to show in web pages (default: working directory)'), _('NAME')),
4871 4925 ('', 'web-conf', '',
4872 4926 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4873 4927 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4874 4928 _('FILE')),
4875 4929 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4876 4930 ('', 'stdio', None, _('for remote clients')),
4877 4931 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
4878 4932 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4879 4933 ('', 'style', '', _('template style to use'), _('STYLE')),
4880 4934 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4881 4935 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4882 4936 _('[OPTION]...'))
4883 4937 def serve(ui, repo, **opts):
4884 4938 """start stand-alone webserver
4885 4939
4886 4940 Start a local HTTP repository browser and pull server. You can use
4887 4941 this for ad-hoc sharing and browsing of repositories. It is
4888 4942 recommended to use a real web server to serve a repository for
4889 4943 longer periods of time.
4890 4944
4891 4945 Please note that the server does not implement access control.
4892 4946 This means that, by default, anybody can read from the server and
4893 4947 nobody can write to it by default. Set the ``web.allow_push``
4894 4948 option to ``*`` to allow everybody to push to the server. You
4895 4949 should use a real web server if you need to authenticate users.
4896 4950
4897 4951 By default, the server logs accesses to stdout and errors to
4898 4952 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4899 4953 files.
4900 4954
4901 4955 To have the server choose a free port number to listen on, specify
4902 4956 a port number of 0; in this case, the server will print the port
4903 4957 number it uses.
4904 4958
4905 4959 Returns 0 on success.
4906 4960 """
4907 4961
4908 4962 if opts["stdio"] and opts["cmdserver"]:
4909 4963 raise util.Abort(_("cannot use --stdio with --cmdserver"))
4910 4964
4911 4965 def checkrepo():
4912 4966 if repo is None:
4913 4967 raise error.RepoError(_("There is no Mercurial repository here"
4914 4968 " (.hg not found)"))
4915 4969
4916 4970 if opts["stdio"]:
4917 4971 checkrepo()
4918 4972 s = sshserver.sshserver(ui, repo)
4919 4973 s.serve_forever()
4920 4974
4921 4975 if opts["cmdserver"]:
4922 4976 checkrepo()
4923 4977 s = commandserver.server(ui, repo, opts["cmdserver"])
4924 4978 return s.serve()
4925 4979
4926 4980 # this way we can check if something was given in the command-line
4927 4981 if opts.get('port'):
4928 4982 opts['port'] = util.getport(opts.get('port'))
4929 4983
4930 4984 baseui = repo and repo.baseui or ui
4931 4985 optlist = ("name templates style address port prefix ipv6"
4932 4986 " accesslog errorlog certificate encoding")
4933 4987 for o in optlist.split():
4934 4988 val = opts.get(o, '')
4935 4989 if val in (None, ''): # should check against default options instead
4936 4990 continue
4937 4991 baseui.setconfig("web", o, val)
4938 4992 if repo and repo.ui != baseui:
4939 4993 repo.ui.setconfig("web", o, val)
4940 4994
4941 4995 o = opts.get('web_conf') or opts.get('webdir_conf')
4942 4996 if not o:
4943 4997 if not repo:
4944 4998 raise error.RepoError(_("There is no Mercurial repository"
4945 4999 " here (.hg not found)"))
4946 5000 o = repo.root
4947 5001
4948 5002 app = hgweb.hgweb(o, baseui=ui)
4949 5003
4950 5004 class service(object):
4951 5005 def init(self):
4952 5006 util.setsignalhandler()
4953 5007 self.httpd = hgweb.server.create_server(ui, app)
4954 5008
4955 5009 if opts['port'] and not ui.verbose:
4956 5010 return
4957 5011
4958 5012 if self.httpd.prefix:
4959 5013 prefix = self.httpd.prefix.strip('/') + '/'
4960 5014 else:
4961 5015 prefix = ''
4962 5016
4963 5017 port = ':%d' % self.httpd.port
4964 5018 if port == ':80':
4965 5019 port = ''
4966 5020
4967 5021 bindaddr = self.httpd.addr
4968 5022 if bindaddr == '0.0.0.0':
4969 5023 bindaddr = '*'
4970 5024 elif ':' in bindaddr: # IPv6
4971 5025 bindaddr = '[%s]' % bindaddr
4972 5026
4973 5027 fqaddr = self.httpd.fqaddr
4974 5028 if ':' in fqaddr:
4975 5029 fqaddr = '[%s]' % fqaddr
4976 5030 if opts['port']:
4977 5031 write = ui.status
4978 5032 else:
4979 5033 write = ui.write
4980 5034 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
4981 5035 (fqaddr, port, prefix, bindaddr, self.httpd.port))
4982 5036
4983 5037 def run(self):
4984 5038 self.httpd.serve_forever()
4985 5039
4986 5040 service = service()
4987 5041
4988 5042 cmdutil.service(opts, initfn=service.init, runfn=service.run)
4989 5043
4990 5044 @command('showconfig|debugconfig',
4991 5045 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4992 5046 _('[-u] [NAME]...'))
4993 5047 def showconfig(ui, repo, *values, **opts):
4994 5048 """show combined config settings from all hgrc files
4995 5049
4996 5050 With no arguments, print names and values of all config items.
4997 5051
4998 5052 With one argument of the form section.name, print just the value
4999 5053 of that config item.
5000 5054
5001 5055 With multiple arguments, print names and values of all config
5002 5056 items with matching section names.
5003 5057
5004 5058 With --debug, the source (filename and line number) is printed
5005 5059 for each config item.
5006 5060
5007 5061 Returns 0 on success.
5008 5062 """
5009 5063
5010 5064 for f in scmutil.rcpath():
5011 5065 ui.debug('read config from: %s\n' % f)
5012 5066 untrusted = bool(opts.get('untrusted'))
5013 5067 if values:
5014 5068 sections = [v for v in values if '.' not in v]
5015 5069 items = [v for v in values if '.' in v]
5016 5070 if len(items) > 1 or items and sections:
5017 5071 raise util.Abort(_('only one config item permitted'))
5018 5072 for section, name, value in ui.walkconfig(untrusted=untrusted):
5019 5073 value = str(value).replace('\n', '\\n')
5020 5074 sectname = section + '.' + name
5021 5075 if values:
5022 5076 for v in values:
5023 5077 if v == section:
5024 5078 ui.debug('%s: ' %
5025 5079 ui.configsource(section, name, untrusted))
5026 5080 ui.write('%s=%s\n' % (sectname, value))
5027 5081 elif v == sectname:
5028 5082 ui.debug('%s: ' %
5029 5083 ui.configsource(section, name, untrusted))
5030 5084 ui.write(value, '\n')
5031 5085 else:
5032 5086 ui.debug('%s: ' %
5033 5087 ui.configsource(section, name, untrusted))
5034 5088 ui.write('%s=%s\n' % (sectname, value))
5035 5089
5036 5090 @command('^status|st',
5037 5091 [('A', 'all', None, _('show status of all files')),
5038 5092 ('m', 'modified', None, _('show only modified files')),
5039 5093 ('a', 'added', None, _('show only added files')),
5040 5094 ('r', 'removed', None, _('show only removed files')),
5041 5095 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5042 5096 ('c', 'clean', None, _('show only files without changes')),
5043 5097 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5044 5098 ('i', 'ignored', None, _('show only ignored files')),
5045 5099 ('n', 'no-status', None, _('hide status prefix')),
5046 5100 ('C', 'copies', None, _('show source of copied files')),
5047 5101 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5048 5102 ('', 'rev', [], _('show difference from revision'), _('REV')),
5049 5103 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5050 5104 ] + walkopts + subrepoopts,
5051 5105 _('[OPTION]... [FILE]...'))
5052 5106 def status(ui, repo, *pats, **opts):
5053 5107 """show changed files in the working directory
5054 5108
5055 5109 Show status of files in the repository. If names are given, only
5056 5110 files that match are shown. Files that are clean or ignored or
5057 5111 the source of a copy/move operation, are not listed unless
5058 5112 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5059 5113 Unless options described with "show only ..." are given, the
5060 5114 options -mardu are used.
5061 5115
5062 5116 Option -q/--quiet hides untracked (unknown and ignored) files
5063 5117 unless explicitly requested with -u/--unknown or -i/--ignored.
5064 5118
5065 5119 .. note::
5066 5120 status may appear to disagree with diff if permissions have
5067 5121 changed or a merge has occurred. The standard diff format does
5068 5122 not report permission changes and diff only reports changes
5069 5123 relative to one merge parent.
5070 5124
5071 5125 If one revision is given, it is used as the base revision.
5072 5126 If two revisions are given, the differences between them are
5073 5127 shown. The --change option can also be used as a shortcut to list
5074 5128 the changed files of a revision from its first parent.
5075 5129
5076 5130 The codes used to show the status of files are::
5077 5131
5078 5132 M = modified
5079 5133 A = added
5080 5134 R = removed
5081 5135 C = clean
5082 5136 ! = missing (deleted by non-hg command, but still tracked)
5083 5137 ? = not tracked
5084 5138 I = ignored
5085 5139 = origin of the previous file listed as A (added)
5086 5140
5087 5141 .. container:: verbose
5088 5142
5089 5143 Examples:
5090 5144
5091 5145 - show changes in the working directory relative to a
5092 5146 changeset::
5093 5147
5094 5148 hg status --rev 9353
5095 5149
5096 5150 - show all changes including copies in an existing changeset::
5097 5151
5098 5152 hg status --copies --change 9353
5099 5153
5100 5154 - get a NUL separated list of added files, suitable for xargs::
5101 5155
5102 5156 hg status -an0
5103 5157
5104 5158 Returns 0 on success.
5105 5159 """
5106 5160
5107 5161 revs = opts.get('rev')
5108 5162 change = opts.get('change')
5109 5163
5110 5164 if revs and change:
5111 5165 msg = _('cannot specify --rev and --change at the same time')
5112 5166 raise util.Abort(msg)
5113 5167 elif change:
5114 5168 node2 = scmutil.revsingle(repo, change, None).node()
5115 5169 node1 = repo[node2].p1().node()
5116 5170 else:
5117 5171 node1, node2 = scmutil.revpair(repo, revs)
5118 5172
5119 5173 cwd = (pats and repo.getcwd()) or ''
5120 5174 end = opts.get('print0') and '\0' or '\n'
5121 5175 copy = {}
5122 5176 states = 'modified added removed deleted unknown ignored clean'.split()
5123 5177 show = [k for k in states if opts.get(k)]
5124 5178 if opts.get('all'):
5125 5179 show += ui.quiet and (states[:4] + ['clean']) or states
5126 5180 if not show:
5127 5181 show = ui.quiet and states[:4] or states[:5]
5128 5182
5129 5183 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5130 5184 'ignored' in show, 'clean' in show, 'unknown' in show,
5131 5185 opts.get('subrepos'))
5132 5186 changestates = zip(states, 'MAR!?IC', stat)
5133 5187
5134 5188 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5135 5189 copy = copies.pathcopies(repo[node1], repo[node2])
5136 5190
5137 5191 fm = ui.formatter('status', opts)
5138 5192 format = '%s %s' + end
5139 5193 if opts.get('no_status'):
5140 5194 format = '%.0s%s' + end
5141 5195
5142 5196 for state, char, files in changestates:
5143 5197 if state in show:
5144 5198 label = 'status.' + state
5145 5199 for f in files:
5146 5200 fm.startitem()
5147 5201 fm.write("status path", format, char,
5148 5202 repo.pathto(f, cwd), label=label)
5149 5203 if f in copy:
5150 5204 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5151 5205 label='status.copied')
5152 5206 fm.end()
5153 5207
5154 5208 @command('^summary|sum',
5155 5209 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5156 5210 def summary(ui, repo, **opts):
5157 5211 """summarize working directory state
5158 5212
5159 5213 This generates a brief summary of the working directory state,
5160 5214 including parents, branch, commit status, and available updates.
5161 5215
5162 5216 With the --remote option, this will check the default paths for
5163 5217 incoming and outgoing changes. This can be time-consuming.
5164 5218
5165 5219 Returns 0 on success.
5166 5220 """
5167 5221
5168 5222 ctx = repo[None]
5169 5223 parents = ctx.parents()
5170 5224 pnode = parents[0].node()
5171 5225 marks = []
5172 5226
5173 5227 for p in parents:
5174 5228 # label with log.changeset (instead of log.parent) since this
5175 5229 # shows a working directory parent *changeset*:
5176 5230 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5177 5231 label='log.changeset')
5178 5232 ui.write(' '.join(p.tags()), label='log.tag')
5179 5233 if p.bookmarks():
5180 5234 marks.extend(p.bookmarks())
5181 5235 if p.rev() == -1:
5182 5236 if not len(repo):
5183 5237 ui.write(_(' (empty repository)'))
5184 5238 else:
5185 5239 ui.write(_(' (no revision checked out)'))
5186 5240 ui.write('\n')
5187 5241 if p.description():
5188 5242 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5189 5243 label='log.summary')
5190 5244
5191 5245 branch = ctx.branch()
5192 5246 bheads = repo.branchheads(branch)
5193 5247 m = _('branch: %s\n') % branch
5194 5248 if branch != 'default':
5195 5249 ui.write(m, label='log.branch')
5196 5250 else:
5197 5251 ui.status(m, label='log.branch')
5198 5252
5199 5253 if marks:
5200 5254 current = repo._bookmarkcurrent
5201 5255 ui.write(_('bookmarks:'), label='log.bookmark')
5202 5256 if current is not None:
5203 5257 try:
5204 5258 marks.remove(current)
5205 5259 ui.write(' *' + current, label='bookmarks.current')
5206 5260 except ValueError:
5207 5261 # current bookmark not in parent ctx marks
5208 5262 pass
5209 5263 for m in marks:
5210 5264 ui.write(' ' + m, label='log.bookmark')
5211 5265 ui.write('\n', label='log.bookmark')
5212 5266
5213 5267 st = list(repo.status(unknown=True))[:6]
5214 5268
5215 5269 c = repo.dirstate.copies()
5216 5270 copied, renamed = [], []
5217 5271 for d, s in c.iteritems():
5218 5272 if s in st[2]:
5219 5273 st[2].remove(s)
5220 5274 renamed.append(d)
5221 5275 else:
5222 5276 copied.append(d)
5223 5277 if d in st[1]:
5224 5278 st[1].remove(d)
5225 5279 st.insert(3, renamed)
5226 5280 st.insert(4, copied)
5227 5281
5228 5282 ms = mergemod.mergestate(repo)
5229 5283 st.append([f for f in ms if ms[f] == 'u'])
5230 5284
5231 5285 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5232 5286 st.append(subs)
5233 5287
5234 5288 labels = [ui.label(_('%d modified'), 'status.modified'),
5235 5289 ui.label(_('%d added'), 'status.added'),
5236 5290 ui.label(_('%d removed'), 'status.removed'),
5237 5291 ui.label(_('%d renamed'), 'status.copied'),
5238 5292 ui.label(_('%d copied'), 'status.copied'),
5239 5293 ui.label(_('%d deleted'), 'status.deleted'),
5240 5294 ui.label(_('%d unknown'), 'status.unknown'),
5241 5295 ui.label(_('%d ignored'), 'status.ignored'),
5242 5296 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5243 5297 ui.label(_('%d subrepos'), 'status.modified')]
5244 5298 t = []
5245 5299 for s, l in zip(st, labels):
5246 5300 if s:
5247 5301 t.append(l % len(s))
5248 5302
5249 5303 t = ', '.join(t)
5250 5304 cleanworkdir = False
5251 5305
5252 5306 if len(parents) > 1:
5253 5307 t += _(' (merge)')
5254 5308 elif branch != parents[0].branch():
5255 5309 t += _(' (new branch)')
5256 5310 elif (parents[0].extra().get('close') and
5257 5311 pnode in repo.branchheads(branch, closed=True)):
5258 5312 t += _(' (head closed)')
5259 5313 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5260 5314 t += _(' (clean)')
5261 5315 cleanworkdir = True
5262 5316 elif pnode not in bheads:
5263 5317 t += _(' (new branch head)')
5264 5318
5265 5319 if cleanworkdir:
5266 5320 ui.status(_('commit: %s\n') % t.strip())
5267 5321 else:
5268 5322 ui.write(_('commit: %s\n') % t.strip())
5269 5323
5270 5324 # all ancestors of branch heads - all ancestors of parent = new csets
5271 5325 new = [0] * len(repo)
5272 5326 cl = repo.changelog
5273 5327 for a in [cl.rev(n) for n in bheads]:
5274 5328 new[a] = 1
5275 5329 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
5276 5330 new[a] = 1
5277 5331 for a in [p.rev() for p in parents]:
5278 5332 if a >= 0:
5279 5333 new[a] = 0
5280 5334 for a in cl.ancestors(*[p.rev() for p in parents]):
5281 5335 new[a] = 0
5282 5336 new = sum(new)
5283 5337
5284 5338 if new == 0:
5285 5339 ui.status(_('update: (current)\n'))
5286 5340 elif pnode not in bheads:
5287 5341 ui.write(_('update: %d new changesets (update)\n') % new)
5288 5342 else:
5289 5343 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5290 5344 (new, len(bheads)))
5291 5345
5292 5346 if opts.get('remote'):
5293 5347 t = []
5294 5348 source, branches = hg.parseurl(ui.expandpath('default'))
5295 5349 other = hg.peer(repo, {}, source)
5296 5350 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
5297 5351 ui.debug('comparing with %s\n' % util.hidepassword(source))
5298 5352 repo.ui.pushbuffer()
5299 5353 commoninc = discovery.findcommonincoming(repo, other)
5300 5354 _common, incoming, _rheads = commoninc
5301 5355 repo.ui.popbuffer()
5302 5356 if incoming:
5303 5357 t.append(_('1 or more incoming'))
5304 5358
5305 5359 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5306 5360 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5307 5361 if source != dest:
5308 5362 other = hg.peer(repo, {}, dest)
5309 5363 commoninc = None
5310 5364 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5311 5365 repo.ui.pushbuffer()
5312 5366 outgoing = discovery.findcommonoutgoing(repo, other,
5313 5367 commoninc=commoninc)
5314 5368 repo.ui.popbuffer()
5315 5369 o = outgoing.missing
5316 5370 if o:
5317 5371 t.append(_('%d outgoing') % len(o))
5318 5372 if 'bookmarks' in other.listkeys('namespaces'):
5319 5373 lmarks = repo.listkeys('bookmarks')
5320 5374 rmarks = other.listkeys('bookmarks')
5321 5375 diff = set(rmarks) - set(lmarks)
5322 5376 if len(diff) > 0:
5323 5377 t.append(_('%d incoming bookmarks') % len(diff))
5324 5378 diff = set(lmarks) - set(rmarks)
5325 5379 if len(diff) > 0:
5326 5380 t.append(_('%d outgoing bookmarks') % len(diff))
5327 5381
5328 5382 if t:
5329 5383 ui.write(_('remote: %s\n') % (', '.join(t)))
5330 5384 else:
5331 5385 ui.status(_('remote: (synced)\n'))
5332 5386
5333 5387 @command('tag',
5334 5388 [('f', 'force', None, _('force tag')),
5335 5389 ('l', 'local', None, _('make the tag local')),
5336 5390 ('r', 'rev', '', _('revision to tag'), _('REV')),
5337 5391 ('', 'remove', None, _('remove a tag')),
5338 5392 # -l/--local is already there, commitopts cannot be used
5339 5393 ('e', 'edit', None, _('edit commit message')),
5340 5394 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5341 5395 ] + commitopts2,
5342 5396 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5343 5397 def tag(ui, repo, name1, *names, **opts):
5344 5398 """add one or more tags for the current or given revision
5345 5399
5346 5400 Name a particular revision using <name>.
5347 5401
5348 5402 Tags are used to name particular revisions of the repository and are
5349 5403 very useful to compare different revisions, to go back to significant
5350 5404 earlier versions or to mark branch points as releases, etc. Changing
5351 5405 an existing tag is normally disallowed; use -f/--force to override.
5352 5406
5353 5407 If no revision is given, the parent of the working directory is
5354 5408 used, or tip if no revision is checked out.
5355 5409
5356 5410 To facilitate version control, distribution, and merging of tags,
5357 5411 they are stored as a file named ".hgtags" which is managed similarly
5358 5412 to other project files and can be hand-edited if necessary. This
5359 5413 also means that tagging creates a new commit. The file
5360 5414 ".hg/localtags" is used for local tags (not shared among
5361 5415 repositories).
5362 5416
5363 5417 Tag commits are usually made at the head of a branch. If the parent
5364 5418 of the working directory is not a branch head, :hg:`tag` aborts; use
5365 5419 -f/--force to force the tag commit to be based on a non-head
5366 5420 changeset.
5367 5421
5368 5422 See :hg:`help dates` for a list of formats valid for -d/--date.
5369 5423
5370 5424 Since tag names have priority over branch names during revision
5371 5425 lookup, using an existing branch name as a tag name is discouraged.
5372 5426
5373 5427 Returns 0 on success.
5374 5428 """
5375 5429 wlock = lock = None
5376 5430 try:
5377 5431 wlock = repo.wlock()
5378 5432 lock = repo.lock()
5379 5433 rev_ = "."
5380 5434 names = [t.strip() for t in (name1,) + names]
5381 5435 if len(names) != len(set(names)):
5382 5436 raise util.Abort(_('tag names must be unique'))
5383 5437 for n in names:
5384 5438 if n in ['tip', '.', 'null']:
5385 5439 raise util.Abort(_("the name '%s' is reserved") % n)
5386 5440 if not n:
5387 5441 raise util.Abort(_('tag names cannot consist entirely of '
5388 5442 'whitespace'))
5389 5443 if opts.get('rev') and opts.get('remove'):
5390 5444 raise util.Abort(_("--rev and --remove are incompatible"))
5391 5445 if opts.get('rev'):
5392 5446 rev_ = opts['rev']
5393 5447 message = opts.get('message')
5394 5448 if opts.get('remove'):
5395 5449 expectedtype = opts.get('local') and 'local' or 'global'
5396 5450 for n in names:
5397 5451 if not repo.tagtype(n):
5398 5452 raise util.Abort(_("tag '%s' does not exist") % n)
5399 5453 if repo.tagtype(n) != expectedtype:
5400 5454 if expectedtype == 'global':
5401 5455 raise util.Abort(_("tag '%s' is not a global tag") % n)
5402 5456 else:
5403 5457 raise util.Abort(_("tag '%s' is not a local tag") % n)
5404 5458 rev_ = nullid
5405 5459 if not message:
5406 5460 # we don't translate commit messages
5407 5461 message = 'Removed tag %s' % ', '.join(names)
5408 5462 elif not opts.get('force'):
5409 5463 for n in names:
5410 5464 if n in repo.tags():
5411 5465 raise util.Abort(_("tag '%s' already exists "
5412 5466 "(use -f to force)") % n)
5413 5467 if not opts.get('local'):
5414 5468 p1, p2 = repo.dirstate.parents()
5415 5469 if p2 != nullid:
5416 5470 raise util.Abort(_('uncommitted merge'))
5417 5471 bheads = repo.branchheads()
5418 5472 if not opts.get('force') and bheads and p1 not in bheads:
5419 5473 raise util.Abort(_('not at a branch head (use -f to force)'))
5420 5474 r = scmutil.revsingle(repo, rev_).node()
5421 5475
5422 5476 if not message:
5423 5477 # we don't translate commit messages
5424 5478 message = ('Added tag %s for changeset %s' %
5425 5479 (', '.join(names), short(r)))
5426 5480
5427 5481 date = opts.get('date')
5428 5482 if date:
5429 5483 date = util.parsedate(date)
5430 5484
5431 5485 if opts.get('edit'):
5432 5486 message = ui.edit(message, ui.username())
5433 5487
5434 5488 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5435 5489 finally:
5436 5490 release(lock, wlock)
5437 5491
5438 5492 @command('tags', [], '')
5439 5493 def tags(ui, repo):
5440 5494 """list repository tags
5441 5495
5442 5496 This lists both regular and local tags. When the -v/--verbose
5443 5497 switch is used, a third column "local" is printed for local tags.
5444 5498
5445 5499 Returns 0 on success.
5446 5500 """
5447 5501
5448 5502 hexfunc = ui.debugflag and hex or short
5449 5503 tagtype = ""
5450 5504
5451 5505 for t, n in reversed(repo.tagslist()):
5452 5506 if ui.quiet:
5453 5507 ui.write("%s\n" % t, label='tags.normal')
5454 5508 continue
5455 5509
5456 5510 hn = hexfunc(n)
5457 5511 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5458 5512 rev = ui.label(r, 'log.changeset')
5459 5513 spaces = " " * (30 - encoding.colwidth(t))
5460 5514
5461 5515 tag = ui.label(t, 'tags.normal')
5462 5516 if ui.verbose:
5463 5517 if repo.tagtype(t) == 'local':
5464 5518 tagtype = " local"
5465 5519 tag = ui.label(t, 'tags.local')
5466 5520 else:
5467 5521 tagtype = ""
5468 5522 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5469 5523
5470 5524 @command('tip',
5471 5525 [('p', 'patch', None, _('show patch')),
5472 5526 ('g', 'git', None, _('use git extended diff format')),
5473 5527 ] + templateopts,
5474 5528 _('[-p] [-g]'))
5475 5529 def tip(ui, repo, **opts):
5476 5530 """show the tip revision
5477 5531
5478 5532 The tip revision (usually just called the tip) is the changeset
5479 5533 most recently added to the repository (and therefore the most
5480 5534 recently changed head).
5481 5535
5482 5536 If you have just made a commit, that commit will be the tip. If
5483 5537 you have just pulled changes from another repository, the tip of
5484 5538 that repository becomes the current tip. The "tip" tag is special
5485 5539 and cannot be renamed or assigned to a different changeset.
5486 5540
5487 5541 Returns 0 on success.
5488 5542 """
5489 5543 displayer = cmdutil.show_changeset(ui, repo, opts)
5490 5544 displayer.show(repo[len(repo) - 1])
5491 5545 displayer.close()
5492 5546
5493 5547 @command('unbundle',
5494 5548 [('u', 'update', None,
5495 5549 _('update to new branch head if changesets were unbundled'))],
5496 5550 _('[-u] FILE...'))
5497 5551 def unbundle(ui, repo, fname1, *fnames, **opts):
5498 5552 """apply one or more changegroup files
5499 5553
5500 5554 Apply one or more compressed changegroup files generated by the
5501 5555 bundle command.
5502 5556
5503 5557 Returns 0 on success, 1 if an update has unresolved files.
5504 5558 """
5505 5559 fnames = (fname1,) + fnames
5506 5560
5507 5561 lock = repo.lock()
5508 5562 wc = repo['.']
5509 5563 try:
5510 5564 for fname in fnames:
5511 5565 f = url.open(ui, fname)
5512 5566 gen = changegroup.readbundle(f, fname)
5513 5567 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5514 5568 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5515 5569 finally:
5516 5570 lock.release()
5517 5571 return postincoming(ui, repo, modheads, opts.get('update'), None)
5518 5572
5519 5573 @command('^update|up|checkout|co',
5520 5574 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5521 5575 ('c', 'check', None,
5522 5576 _('update across branches if no uncommitted changes')),
5523 5577 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5524 5578 ('r', 'rev', '', _('revision'), _('REV'))],
5525 5579 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5526 5580 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5527 5581 """update working directory (or switch revisions)
5528 5582
5529 5583 Update the repository's working directory to the specified
5530 5584 changeset. If no changeset is specified, update to the tip of the
5531 5585 current named branch and move the current bookmark (see :hg:`help
5532 5586 bookmarks`).
5533 5587
5534 5588 If the changeset is not a descendant of the working directory's
5535 5589 parent, the update is aborted. With the -c/--check option, the
5536 5590 working directory is checked for uncommitted changes; if none are
5537 5591 found, the working directory is updated to the specified
5538 5592 changeset.
5539 5593
5540 5594 Update sets the working directory's parent revison to the specified
5541 5595 changeset (see :hg:`help parents`).
5542 5596
5543 5597 The following rules apply when the working directory contains
5544 5598 uncommitted changes:
5545 5599
5546 5600 1. If neither -c/--check nor -C/--clean is specified, and if
5547 5601 the requested changeset is an ancestor or descendant of
5548 5602 the working directory's parent, the uncommitted changes
5549 5603 are merged into the requested changeset and the merged
5550 5604 result is left uncommitted. If the requested changeset is
5551 5605 not an ancestor or descendant (that is, it is on another
5552 5606 branch), the update is aborted and the uncommitted changes
5553 5607 are preserved.
5554 5608
5555 5609 2. With the -c/--check option, the update is aborted and the
5556 5610 uncommitted changes are preserved.
5557 5611
5558 5612 3. With the -C/--clean option, uncommitted changes are discarded and
5559 5613 the working directory is updated to the requested changeset.
5560 5614
5561 5615 Use null as the changeset to remove the working directory (like
5562 5616 :hg:`clone -U`).
5563 5617
5564 5618 If you want to revert just one file to an older revision, use
5565 5619 :hg:`revert [-r REV] NAME`.
5566 5620
5567 5621 See :hg:`help dates` for a list of formats valid for -d/--date.
5568 5622
5569 5623 Returns 0 on success, 1 if there are unresolved files.
5570 5624 """
5571 5625 if rev and node:
5572 5626 raise util.Abort(_("please specify just one revision"))
5573 5627
5574 5628 if rev is None or rev == '':
5575 5629 rev = node
5576 5630
5577 5631 # with no argument, we also move the current bookmark, if any
5578 5632 movemarkfrom = None
5579 5633 if rev is None or node == '':
5580 5634 movemarkfrom = repo['.'].node()
5581 5635
5582 5636 # if we defined a bookmark, we have to remember the original bookmark name
5583 5637 brev = rev
5584 5638 rev = scmutil.revsingle(repo, rev, rev).rev()
5585 5639
5586 5640 if check and clean:
5587 5641 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5588 5642
5589 5643 if date:
5590 5644 if rev is not None:
5591 5645 raise util.Abort(_("you can't specify a revision and a date"))
5592 5646 rev = cmdutil.finddate(ui, repo, date)
5593 5647
5594 5648 if check:
5595 5649 # we could use dirty() but we can ignore merge and branch trivia
5596 5650 c = repo[None]
5597 5651 if c.modified() or c.added() or c.removed():
5598 5652 raise util.Abort(_("uncommitted local changes"))
5599 5653 if not rev:
5600 5654 rev = repo[repo[None].branch()].rev()
5601 5655 mergemod._checkunknown(repo, repo[None], repo[rev])
5602 5656
5603 5657 if clean:
5604 5658 ret = hg.clean(repo, rev)
5605 5659 else:
5606 5660 ret = hg.update(repo, rev)
5607 5661
5608 5662 if not ret and movemarkfrom:
5609 5663 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5610 5664 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5611 5665 elif brev in repo._bookmarks:
5612 5666 bookmarks.setcurrent(repo, brev)
5613 5667 elif brev:
5614 5668 bookmarks.unsetcurrent(repo)
5615 5669
5616 5670 return ret
5617 5671
5618 5672 @command('verify', [])
5619 5673 def verify(ui, repo):
5620 5674 """verify the integrity of the repository
5621 5675
5622 5676 Verify the integrity of the current repository.
5623 5677
5624 5678 This will perform an extensive check of the repository's
5625 5679 integrity, validating the hashes and checksums of each entry in
5626 5680 the changelog, manifest, and tracked files, as well as the
5627 5681 integrity of their crosslinks and indices.
5628 5682
5629 5683 Returns 0 on success, 1 if errors are encountered.
5630 5684 """
5631 5685 return hg.verify(repo)
5632 5686
5633 5687 @command('version', [])
5634 5688 def version_(ui):
5635 5689 """output version and copyright information"""
5636 5690 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5637 5691 % util.version())
5638 5692 ui.status(_(
5639 5693 "(see http://mercurial.selenic.com for more information)\n"
5640 5694 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
5641 5695 "This is free software; see the source for copying conditions. "
5642 5696 "There is NO\nwarranty; "
5643 5697 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5644 5698 ))
5645 5699
5646 5700 norepo = ("clone init version help debugcommands debugcomplete"
5647 5701 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5648 5702 " debugknown debuggetbundle debugbundle")
5649 5703 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5650 5704 " debugdata debugindex debugindexdot debugrevlog")
@@ -1,274 +1,274 b''
1 1 Show all commands except debug commands
2 2 $ hg debugcomplete
3 3 add
4 4 addremove
5 5 annotate
6 6 archive
7 7 backout
8 8 bisect
9 9 bookmarks
10 10 branch
11 11 branches
12 12 bundle
13 13 cat
14 14 clone
15 15 commit
16 16 copy
17 17 diff
18 18 export
19 19 forget
20 20 graft
21 21 grep
22 22 heads
23 23 help
24 24 identify
25 25 import
26 26 incoming
27 27 init
28 28 locate
29 29 log
30 30 manifest
31 31 merge
32 32 outgoing
33 33 parents
34 34 paths
35 35 phase
36 36 pull
37 37 push
38 38 recover
39 39 remove
40 40 rename
41 41 resolve
42 42 revert
43 43 rollback
44 44 root
45 45 serve
46 46 showconfig
47 47 status
48 48 summary
49 49 tag
50 50 tags
51 51 tip
52 52 unbundle
53 53 update
54 54 verify
55 55 version
56 56
57 57 Show all commands that start with "a"
58 58 $ hg debugcomplete a
59 59 add
60 60 addremove
61 61 annotate
62 62 archive
63 63
64 64 Do not show debug commands if there are other candidates
65 65 $ hg debugcomplete d
66 66 diff
67 67
68 68 Show debug commands if there are no other candidates
69 69 $ hg debugcomplete debug
70 70 debugancestor
71 71 debugbuilddag
72 72 debugbundle
73 73 debugcheckstate
74 74 debugcommands
75 75 debugcomplete
76 76 debugconfig
77 77 debugdag
78 78 debugdata
79 79 debugdate
80 80 debugdiscovery
81 81 debugfileset
82 82 debugfsinfo
83 83 debuggetbundle
84 84 debugignore
85 85 debugindex
86 86 debugindexdot
87 87 debuginstall
88 88 debugknown
89 89 debugpushkey
90 90 debugpvec
91 91 debugrebuildstate
92 92 debugrename
93 93 debugrevlog
94 94 debugrevspec
95 95 debugsetparents
96 96 debugstate
97 97 debugsub
98 98 debugwalk
99 99 debugwireargs
100 100
101 101 Do not show the alias of a debug command if there are other candidates
102 102 (this should hide rawcommit)
103 103 $ hg debugcomplete r
104 104 recover
105 105 remove
106 106 rename
107 107 resolve
108 108 revert
109 109 rollback
110 110 root
111 111 Show the alias of a debug command if there are no other candidates
112 112 $ hg debugcomplete rawc
113 113
114 114
115 115 Show the global options
116 116 $ hg debugcomplete --options | sort
117 117 --config
118 118 --cwd
119 119 --debug
120 120 --debugger
121 121 --encoding
122 122 --encodingmode
123 123 --help
124 124 --noninteractive
125 125 --profile
126 126 --quiet
127 127 --repository
128 128 --time
129 129 --traceback
130 130 --verbose
131 131 --version
132 132 -R
133 133 -h
134 134 -q
135 135 -v
136 136 -y
137 137
138 138 Show the options for the "serve" command
139 139 $ hg debugcomplete --options serve | sort
140 140 --accesslog
141 141 --address
142 142 --certificate
143 143 --cmdserver
144 144 --config
145 145 --cwd
146 146 --daemon
147 147 --daemon-pipefds
148 148 --debug
149 149 --debugger
150 150 --encoding
151 151 --encodingmode
152 152 --errorlog
153 153 --help
154 154 --ipv6
155 155 --name
156 156 --noninteractive
157 157 --pid-file
158 158 --port
159 159 --prefix
160 160 --profile
161 161 --quiet
162 162 --repository
163 163 --stdio
164 164 --style
165 165 --templates
166 166 --time
167 167 --traceback
168 168 --verbose
169 169 --version
170 170 --web-conf
171 171 -6
172 172 -A
173 173 -E
174 174 -R
175 175 -a
176 176 -d
177 177 -h
178 178 -n
179 179 -p
180 180 -q
181 181 -t
182 182 -v
183 183 -y
184 184
185 185 Show an error if we use --options with an ambiguous abbreviation
186 186 $ hg debugcomplete --options s
187 187 hg: command 's' is ambiguous:
188 188 serve showconfig status summary
189 189 [255]
190 190
191 191 Show all commands + options
192 192 $ hg debugcommands
193 193 add: include, exclude, subrepos, dry-run
194 194 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, ignore-all-space, ignore-space-change, ignore-blank-lines, include, exclude
195 195 clone: noupdate, updaterev, rev, branch, pull, uncompressed, ssh, remotecmd, insecure
196 commit: addremove, close-branch, include, exclude, message, logfile, date, user, subrepos
196 commit: addremove, close-branch, amend, include, exclude, message, logfile, date, user, subrepos
197 197 diff: rev, change, text, git, nodates, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, unified, stat, include, exclude, subrepos
198 198 export: output, switch-parent, rev, text, git, nodates
199 199 forget: include, exclude
200 200 init: ssh, remotecmd, insecure
201 201 log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, hidden, patch, git, limit, no-merges, stat, style, template, include, exclude
202 202 merge: force, rev, preview, tool
203 203 phase: public, draft, secret, force, rev
204 204 pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
205 205 push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
206 206 remove: after, force, include, exclude
207 207 serve: accesslog, daemon, daemon-pipefds, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate
208 208 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, copies, print0, rev, change, include, exclude, subrepos
209 209 summary: remote
210 210 update: clean, check, date, rev
211 211 addremove: similarity, include, exclude, dry-run
212 212 archive: no-decode, prefix, rev, type, subrepos, include, exclude
213 213 backout: merge, parent, rev, tool, include, exclude, message, logfile, date, user
214 214 bisect: reset, good, bad, skip, extend, command, noupdate
215 215 bookmarks: force, rev, delete, rename, inactive
216 216 branch: force, clean
217 217 branches: active, closed
218 218 bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
219 219 cat: output, rev, decode, include, exclude
220 220 copy: after, force, include, exclude, dry-run
221 221 debugancestor:
222 222 debugbuilddag: mergeable-file, overwritten-file, new-file
223 223 debugbundle: all
224 224 debugcheckstate:
225 225 debugcommands:
226 226 debugcomplete: options
227 227 debugdag: tags, branches, dots, spaces
228 228 debugdata: changelog, manifest
229 229 debugdate: extended
230 230 debugdiscovery: old, nonheads, ssh, remotecmd, insecure
231 231 debugfileset:
232 232 debugfsinfo:
233 233 debuggetbundle: head, common, type
234 234 debugignore:
235 235 debugindex: changelog, manifest, format
236 236 debugindexdot:
237 237 debuginstall:
238 238 debugknown:
239 239 debugpushkey:
240 240 debugpvec:
241 241 debugrebuildstate: rev
242 242 debugrename: rev
243 243 debugrevlog: changelog, manifest, dump
244 244 debugrevspec:
245 245 debugsetparents:
246 246 debugstate: nodates, datesort
247 247 debugsub: rev
248 248 debugwalk: include, exclude
249 249 debugwireargs: three, four, five, ssh, remotecmd, insecure
250 250 graft: continue, edit, currentdate, currentuser, date, user, tool, dry-run
251 251 grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
252 252 heads: rev, topo, active, closed, style, template
253 253 help: extension, command
254 254 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure
255 255 import: strip, base, edit, force, no-commit, bypass, exact, import-branch, message, logfile, date, user, similarity
256 256 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
257 257 locate: rev, print0, fullpath, include, exclude
258 258 manifest: rev, all
259 259 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, style, template, ssh, remotecmd, insecure, subrepos
260 260 parents: rev, style, template
261 261 paths:
262 262 recover:
263 263 rename: after, force, include, exclude, dry-run
264 264 resolve: all, list, mark, unmark, no-status, tool, include, exclude
265 265 revert: all, date, rev, no-backup, include, exclude, dry-run
266 266 rollback: dry-run, force
267 267 root:
268 268 showconfig: untrusted
269 269 tag: force, local, rev, remove, edit, message, date, user
270 270 tags:
271 271 tip: patch, git, style, template
272 272 unbundle: update
273 273 verify:
274 274 version:
@@ -1,398 +1,399 b''
1 1 Create configuration
2 2
3 3 $ echo "[ui]" >> $HGRCPATH
4 4 $ echo "interactive=true" >> $HGRCPATH
5 5
6 6 help record (no record)
7 7
8 8 $ hg help record
9 9 record extension - commands to interactively select changes for commit/qrefresh
10 10
11 11 use "hg help extensions" for information on enabling extensions
12 12
13 13 help qrecord (no record)
14 14
15 15 $ hg help qrecord
16 16 'qrecord' is provided by the following extension:
17 17
18 18 record commands to interactively select changes for commit/qrefresh
19 19
20 20 use "hg help extensions" for information on enabling extensions
21 21
22 22 $ echo "[extensions]" >> $HGRCPATH
23 23 $ echo "record=" >> $HGRCPATH
24 24
25 25 help record (record)
26 26
27 27 $ hg help record
28 28 hg record [OPTION]... [FILE]...
29 29
30 30 interactively select changes to commit
31 31
32 32 If a list of files is omitted, all changes reported by "hg status" will be
33 33 candidates for recording.
34 34
35 35 See "hg help dates" for a list of formats valid for -d/--date.
36 36
37 37 You will be prompted for whether to record changes to each modified file,
38 38 and for files with multiple changes, for each change to use. For each
39 39 query, the following responses are possible:
40 40
41 41 y - record this change
42 42 n - skip this change
43 43 e - edit this change manually
44 44
45 45 s - skip remaining changes to this file
46 46 f - record remaining changes to this file
47 47
48 48 d - done, skip remaining changes and files
49 49 a - record all changes to all remaining files
50 50 q - quit, recording no changes
51 51
52 52 ? - display help
53 53
54 54 This command is not available when committing a merge.
55 55
56 56 options:
57 57
58 58 -A --addremove mark new/missing files as added/removed before
59 59 committing
60 60 --close-branch mark a branch as closed, hiding it from the branch
61 61 list
62 --amend amend the parent of the working dir
62 63 -I --include PATTERN [+] include names matching the given patterns
63 64 -X --exclude PATTERN [+] exclude names matching the given patterns
64 65 -m --message TEXT use text as commit message
65 66 -l --logfile FILE read commit message from file
66 67 -d --date DATE record the specified date as commit date
67 68 -u --user USER record the specified user as committer
68 69 -S --subrepos recurse into subrepositories
69 70 -w --ignore-all-space ignore white space when comparing lines
70 71 -b --ignore-space-change ignore changes in the amount of white space
71 72 -B --ignore-blank-lines ignore changes whose lines are all blank
72 73
73 74 [+] marked option can be specified multiple times
74 75
75 76 use "hg -v help record" to show more info
76 77
77 78 help (no mq, so no qrecord)
78 79
79 80 $ hg help qrecord
80 81 hg qrecord [OPTION]... PATCH [FILE]...
81 82
82 83 interactively record a new patch
83 84
84 85 See "hg help qnew" & "hg help record" for more information and usage.
85 86
86 87 use "hg -v help qrecord" to show more info
87 88
88 89 $ hg init a
89 90
90 91 qrecord (mq not present)
91 92
92 93 $ hg -R a qrecord
93 94 hg qrecord: invalid arguments
94 95 hg qrecord [OPTION]... PATCH [FILE]...
95 96
96 97 interactively record a new patch
97 98
98 99 use "hg help qrecord" to show the full help text
99 100 [255]
100 101
101 102 qrecord patch (mq not present)
102 103
103 104 $ hg -R a qrecord patch
104 105 abort: 'mq' extension not loaded
105 106 [255]
106 107
107 108 help (bad mq)
108 109
109 110 $ echo "mq=nonexistant" >> $HGRCPATH
110 111 $ hg help qrecord
111 112 *** failed to import extension mq from nonexistant: [Errno 2] * (glob)
112 113 hg qrecord [OPTION]... PATCH [FILE]...
113 114
114 115 interactively record a new patch
115 116
116 117 See "hg help qnew" & "hg help record" for more information and usage.
117 118
118 119 use "hg -v help qrecord" to show more info
119 120
120 121 help (mq present)
121 122
122 123 $ sed 's/mq=nonexistant/mq=/' $HGRCPATH > hgrc.tmp
123 124 $ mv hgrc.tmp $HGRCPATH
124 125
125 126 $ hg help qrecord
126 127 hg qrecord [OPTION]... PATCH [FILE]...
127 128
128 129 interactively record a new patch
129 130
130 131 See "hg help qnew" & "hg help record" for more information and usage.
131 132
132 133 options:
133 134
134 135 -e --edit edit commit message
135 136 -g --git use git extended diff format
136 137 -U --currentuser add "From: <current user>" to patch
137 138 -u --user USER add "From: <USER>" to patch
138 139 -D --currentdate add "Date: <current date>" to patch
139 140 -d --date DATE add "Date: <DATE>" to patch
140 141 -I --include PATTERN [+] include names matching the given patterns
141 142 -X --exclude PATTERN [+] exclude names matching the given patterns
142 143 -m --message TEXT use text as commit message
143 144 -l --logfile FILE read commit message from file
144 145 -w --ignore-all-space ignore white space when comparing lines
145 146 -b --ignore-space-change ignore changes in the amount of white space
146 147 -B --ignore-blank-lines ignore changes whose lines are all blank
147 148 --mq operate on patch repository
148 149
149 150 [+] marked option can be specified multiple times
150 151
151 152 use "hg -v help qrecord" to show more info
152 153
153 154 $ cd a
154 155
155 156 Base commit
156 157
157 158 $ cat > 1.txt <<EOF
158 159 > 1
159 160 > 2
160 161 > 3
161 162 > 4
162 163 > 5
163 164 > EOF
164 165 $ cat > 2.txt <<EOF
165 166 > a
166 167 > b
167 168 > c
168 169 > d
169 170 > e
170 171 > f
171 172 > EOF
172 173
173 174 $ mkdir dir
174 175 $ cat > dir/a.txt <<EOF
175 176 > hello world
176 177 >
177 178 > someone
178 179 > up
179 180 > there
180 181 > loves
181 182 > me
182 183 > EOF
183 184
184 185 $ hg add 1.txt 2.txt dir/a.txt
185 186 $ hg commit -m 'initial checkin'
186 187
187 188 Changing files
188 189
189 190 $ sed -e 's/2/2 2/;s/4/4 4/' 1.txt > 1.txt.new
190 191 $ sed -e 's/b/b b/' 2.txt > 2.txt.new
191 192 $ sed -e 's/hello world/hello world!/' dir/a.txt > dir/a.txt.new
192 193
193 194 $ mv -f 1.txt.new 1.txt
194 195 $ mv -f 2.txt.new 2.txt
195 196 $ mv -f dir/a.txt.new dir/a.txt
196 197
197 198 Whole diff
198 199
199 200 $ hg diff --nodates
200 201 diff -r 1057167b20ef 1.txt
201 202 --- a/1.txt
202 203 +++ b/1.txt
203 204 @@ -1,5 +1,5 @@
204 205 1
205 206 -2
206 207 +2 2
207 208 3
208 209 -4
209 210 +4 4
210 211 5
211 212 diff -r 1057167b20ef 2.txt
212 213 --- a/2.txt
213 214 +++ b/2.txt
214 215 @@ -1,5 +1,5 @@
215 216 a
216 217 -b
217 218 +b b
218 219 c
219 220 d
220 221 e
221 222 diff -r 1057167b20ef dir/a.txt
222 223 --- a/dir/a.txt
223 224 +++ b/dir/a.txt
224 225 @@ -1,4 +1,4 @@
225 226 -hello world
226 227 +hello world!
227 228
228 229 someone
229 230 up
230 231
231 232 qrecord with bad patch name, should abort before prompting
232 233
233 234 $ hg qrecord .hg
234 235 abort: patch name cannot begin with ".hg"
235 236 [255]
236 237
237 238 qrecord a.patch
238 239
239 240 $ hg qrecord -d '0 0' -m aaa a.patch <<EOF
240 241 > y
241 242 > y
242 243 > n
243 244 > y
244 245 > y
245 246 > n
246 247 > EOF
247 248 diff --git a/1.txt b/1.txt
248 249 2 hunks, 2 lines changed
249 250 examine changes to '1.txt'? [Ynesfdaq?]
250 251 @@ -1,3 +1,3 @@
251 252 1
252 253 -2
253 254 +2 2
254 255 3
255 256 record change 1/4 to '1.txt'? [Ynesfdaq?]
256 257 @@ -3,3 +3,3 @@
257 258 3
258 259 -4
259 260 +4 4
260 261 5
261 262 record change 2/4 to '1.txt'? [Ynesfdaq?]
262 263 diff --git a/2.txt b/2.txt
263 264 1 hunks, 1 lines changed
264 265 examine changes to '2.txt'? [Ynesfdaq?]
265 266 @@ -1,5 +1,5 @@
266 267 a
267 268 -b
268 269 +b b
269 270 c
270 271 d
271 272 e
272 273 record change 3/4 to '2.txt'? [Ynesfdaq?]
273 274 diff --git a/dir/a.txt b/dir/a.txt
274 275 1 hunks, 1 lines changed
275 276 examine changes to 'dir/a.txt'? [Ynesfdaq?]
276 277
277 278 After qrecord a.patch 'tip'"
278 279
279 280 $ hg tip -p
280 281 changeset: 1:5d1ca63427ee
281 282 tag: a.patch
282 283 tag: qbase
283 284 tag: qtip
284 285 tag: tip
285 286 user: test
286 287 date: Thu Jan 01 00:00:00 1970 +0000
287 288 summary: aaa
288 289
289 290 diff -r 1057167b20ef -r 5d1ca63427ee 1.txt
290 291 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
291 292 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
292 293 @@ -1,5 +1,5 @@
293 294 1
294 295 -2
295 296 +2 2
296 297 3
297 298 4
298 299 5
299 300 diff -r 1057167b20ef -r 5d1ca63427ee 2.txt
300 301 --- a/2.txt Thu Jan 01 00:00:00 1970 +0000
301 302 +++ b/2.txt Thu Jan 01 00:00:00 1970 +0000
302 303 @@ -1,5 +1,5 @@
303 304 a
304 305 -b
305 306 +b b
306 307 c
307 308 d
308 309 e
309 310
310 311
311 312 After qrecord a.patch 'diff'"
312 313
313 314 $ hg diff --nodates
314 315 diff -r 5d1ca63427ee 1.txt
315 316 --- a/1.txt
316 317 +++ b/1.txt
317 318 @@ -1,5 +1,5 @@
318 319 1
319 320 2 2
320 321 3
321 322 -4
322 323 +4 4
323 324 5
324 325 diff -r 5d1ca63427ee dir/a.txt
325 326 --- a/dir/a.txt
326 327 +++ b/dir/a.txt
327 328 @@ -1,4 +1,4 @@
328 329 -hello world
329 330 +hello world!
330 331
331 332 someone
332 333 up
333 334
334 335 qrecord b.patch
335 336
336 337 $ hg qrecord -d '0 0' -m bbb b.patch <<EOF
337 338 > y
338 339 > y
339 340 > y
340 341 > y
341 342 > EOF
342 343 diff --git a/1.txt b/1.txt
343 344 1 hunks, 1 lines changed
344 345 examine changes to '1.txt'? [Ynesfdaq?]
345 346 @@ -1,5 +1,5 @@
346 347 1
347 348 2 2
348 349 3
349 350 -4
350 351 +4 4
351 352 5
352 353 record change 1/2 to '1.txt'? [Ynesfdaq?]
353 354 diff --git a/dir/a.txt b/dir/a.txt
354 355 1 hunks, 1 lines changed
355 356 examine changes to 'dir/a.txt'? [Ynesfdaq?]
356 357 @@ -1,4 +1,4 @@
357 358 -hello world
358 359 +hello world!
359 360
360 361 someone
361 362 up
362 363 record change 2/2 to 'dir/a.txt'? [Ynesfdaq?]
363 364
364 365 After qrecord b.patch 'tip'
365 366
366 367 $ hg tip -p
367 368 changeset: 2:b056198bf878
368 369 tag: b.patch
369 370 tag: qtip
370 371 tag: tip
371 372 user: test
372 373 date: Thu Jan 01 00:00:00 1970 +0000
373 374 summary: bbb
374 375
375 376 diff -r 5d1ca63427ee -r b056198bf878 1.txt
376 377 --- a/1.txt Thu Jan 01 00:00:00 1970 +0000
377 378 +++ b/1.txt Thu Jan 01 00:00:00 1970 +0000
378 379 @@ -1,5 +1,5 @@
379 380 1
380 381 2 2
381 382 3
382 383 -4
383 384 +4 4
384 385 5
385 386 diff -r 5d1ca63427ee -r b056198bf878 dir/a.txt
386 387 --- a/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
387 388 +++ b/dir/a.txt Thu Jan 01 00:00:00 1970 +0000
388 389 @@ -1,4 +1,4 @@
389 390 -hello world
390 391 +hello world!
391 392
392 393 someone
393 394 up
394 395
395 396
396 397 After qrecord b.patch 'diff'
397 398
398 399 $ hg diff --nodates
General Comments 0
You need to be logged in to leave comments. Login now