##// END OF EJS Templates
changegroup.writebundle: provide ui...
Eric Sumner -
r23895:cda18ded default
parent child Browse files
Show More
@@ -1,721 +1,722 b''
1 # shelve.py - save/restore working directory state
1 # shelve.py - save/restore working directory state
2 #
2 #
3 # Copyright 2013 Facebook, Inc.
3 # Copyright 2013 Facebook, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 """save and restore changes to the working directory
8 """save and restore changes to the working directory
9
9
10 The "hg shelve" command saves changes made to the working directory
10 The "hg shelve" command saves changes made to the working directory
11 and reverts those changes, resetting the working directory to a clean
11 and reverts those changes, resetting the working directory to a clean
12 state.
12 state.
13
13
14 Later on, the "hg unshelve" command restores the changes saved by "hg
14 Later on, the "hg unshelve" command restores the changes saved by "hg
15 shelve". Changes can be restored even after updating to a different
15 shelve". Changes can be restored even after updating to a different
16 parent, in which case Mercurial's merge machinery will resolve any
16 parent, in which case Mercurial's merge machinery will resolve any
17 conflicts if necessary.
17 conflicts if necessary.
18
18
19 You can have more than one shelved change outstanding at a time; each
19 You can have more than one shelved change outstanding at a time; each
20 shelved change has a distinct name. For details, see the help for "hg
20 shelved change has a distinct name. For details, see the help for "hg
21 shelve".
21 shelve".
22 """
22 """
23
23
24 from mercurial.i18n import _
24 from mercurial.i18n import _
25 from mercurial.node import nullid, nullrev, bin, hex
25 from mercurial.node import nullid, nullrev, bin, hex
26 from mercurial import changegroup, cmdutil, scmutil, phases, commands
26 from mercurial import changegroup, cmdutil, scmutil, phases, commands
27 from mercurial import error, hg, mdiff, merge, patch, repair, util
27 from mercurial import error, hg, mdiff, merge, patch, repair, util
28 from mercurial import templatefilters, exchange, bundlerepo
28 from mercurial import templatefilters, exchange, bundlerepo
29 from mercurial import lock as lockmod
29 from mercurial import lock as lockmod
30 from hgext import rebase
30 from hgext import rebase
31 import errno
31 import errno
32
32
33 cmdtable = {}
33 cmdtable = {}
34 command = cmdutil.command(cmdtable)
34 command = cmdutil.command(cmdtable)
35 testedwith = 'internal'
35 testedwith = 'internal'
36
36
37 class shelvedfile(object):
37 class shelvedfile(object):
38 """Helper for the file storing a single shelve
38 """Helper for the file storing a single shelve
39
39
40 Handles common functions on shelve files (.hg/.patch) using
40 Handles common functions on shelve files (.hg/.patch) using
41 the vfs layer"""
41 the vfs layer"""
42 def __init__(self, repo, name, filetype=None):
42 def __init__(self, repo, name, filetype=None):
43 self.repo = repo
43 self.repo = repo
44 self.name = name
44 self.name = name
45 self.vfs = scmutil.vfs(repo.join('shelved'))
45 self.vfs = scmutil.vfs(repo.join('shelved'))
46 self.ui = self.repo.ui
46 if filetype:
47 if filetype:
47 self.fname = name + '.' + filetype
48 self.fname = name + '.' + filetype
48 else:
49 else:
49 self.fname = name
50 self.fname = name
50
51
51 def exists(self):
52 def exists(self):
52 return self.vfs.exists(self.fname)
53 return self.vfs.exists(self.fname)
53
54
54 def filename(self):
55 def filename(self):
55 return self.vfs.join(self.fname)
56 return self.vfs.join(self.fname)
56
57
57 def unlink(self):
58 def unlink(self):
58 util.unlink(self.filename())
59 util.unlink(self.filename())
59
60
60 def stat(self):
61 def stat(self):
61 return self.vfs.stat(self.fname)
62 return self.vfs.stat(self.fname)
62
63
63 def opener(self, mode='rb'):
64 def opener(self, mode='rb'):
64 try:
65 try:
65 return self.vfs(self.fname, mode)
66 return self.vfs(self.fname, mode)
66 except IOError, err:
67 except IOError, err:
67 if err.errno != errno.ENOENT:
68 if err.errno != errno.ENOENT:
68 raise
69 raise
69 raise util.Abort(_("shelved change '%s' not found") % self.name)
70 raise util.Abort(_("shelved change '%s' not found") % self.name)
70
71
71 def applybundle(self):
72 def applybundle(self):
72 fp = self.opener()
73 fp = self.opener()
73 try:
74 try:
74 gen = exchange.readbundle(self.repo.ui, fp, self.fname, self.vfs)
75 gen = exchange.readbundle(self.repo.ui, fp, self.fname, self.vfs)
75 changegroup.addchangegroup(self.repo, gen, 'unshelve',
76 changegroup.addchangegroup(self.repo, gen, 'unshelve',
76 'bundle:' + self.vfs.join(self.fname),
77 'bundle:' + self.vfs.join(self.fname),
77 targetphase=phases.secret)
78 targetphase=phases.secret)
78 finally:
79 finally:
79 fp.close()
80 fp.close()
80
81
81 def bundlerepo(self):
82 def bundlerepo(self):
82 return bundlerepo.bundlerepository(self.repo.baseui, self.repo.root,
83 return bundlerepo.bundlerepository(self.repo.baseui, self.repo.root,
83 self.vfs.join(self.fname))
84 self.vfs.join(self.fname))
84 def writebundle(self, cg):
85 def writebundle(self, cg):
85 changegroup.writebundle(cg, self.fname, 'HG10UN', self.vfs)
86 changegroup.writebundle(self.ui, cg, self.fname, 'HG10UN', self.vfs)
86
87
87 class shelvedstate(object):
88 class shelvedstate(object):
88 """Handle persistence during unshelving operations.
89 """Handle persistence during unshelving operations.
89
90
90 Handles saving and restoring a shelved state. Ensures that different
91 Handles saving and restoring a shelved state. Ensures that different
91 versions of a shelved state are possible and handles them appropriately.
92 versions of a shelved state are possible and handles them appropriately.
92 """
93 """
93 _version = 1
94 _version = 1
94 _filename = 'shelvedstate'
95 _filename = 'shelvedstate'
95
96
96 @classmethod
97 @classmethod
97 def load(cls, repo):
98 def load(cls, repo):
98 fp = repo.vfs(cls._filename)
99 fp = repo.vfs(cls._filename)
99 try:
100 try:
100 version = int(fp.readline().strip())
101 version = int(fp.readline().strip())
101
102
102 if version != cls._version:
103 if version != cls._version:
103 raise util.Abort(_('this version of shelve is incompatible '
104 raise util.Abort(_('this version of shelve is incompatible '
104 'with the version used in this repo'))
105 'with the version used in this repo'))
105 name = fp.readline().strip()
106 name = fp.readline().strip()
106 wctx = fp.readline().strip()
107 wctx = fp.readline().strip()
107 pendingctx = fp.readline().strip()
108 pendingctx = fp.readline().strip()
108 parents = [bin(h) for h in fp.readline().split()]
109 parents = [bin(h) for h in fp.readline().split()]
109 stripnodes = [bin(h) for h in fp.readline().split()]
110 stripnodes = [bin(h) for h in fp.readline().split()]
110 finally:
111 finally:
111 fp.close()
112 fp.close()
112
113
113 obj = cls()
114 obj = cls()
114 obj.name = name
115 obj.name = name
115 obj.wctx = repo[bin(wctx)]
116 obj.wctx = repo[bin(wctx)]
116 obj.pendingctx = repo[bin(pendingctx)]
117 obj.pendingctx = repo[bin(pendingctx)]
117 obj.parents = parents
118 obj.parents = parents
118 obj.stripnodes = stripnodes
119 obj.stripnodes = stripnodes
119
120
120 return obj
121 return obj
121
122
122 @classmethod
123 @classmethod
123 def save(cls, repo, name, originalwctx, pendingctx, stripnodes):
124 def save(cls, repo, name, originalwctx, pendingctx, stripnodes):
124 fp = repo.vfs(cls._filename, 'wb')
125 fp = repo.vfs(cls._filename, 'wb')
125 fp.write('%i\n' % cls._version)
126 fp.write('%i\n' % cls._version)
126 fp.write('%s\n' % name)
127 fp.write('%s\n' % name)
127 fp.write('%s\n' % hex(originalwctx.node()))
128 fp.write('%s\n' % hex(originalwctx.node()))
128 fp.write('%s\n' % hex(pendingctx.node()))
129 fp.write('%s\n' % hex(pendingctx.node()))
129 fp.write('%s\n' % ' '.join([hex(p) for p in repo.dirstate.parents()]))
130 fp.write('%s\n' % ' '.join([hex(p) for p in repo.dirstate.parents()]))
130 fp.write('%s\n' % ' '.join([hex(n) for n in stripnodes]))
131 fp.write('%s\n' % ' '.join([hex(n) for n in stripnodes]))
131 fp.close()
132 fp.close()
132
133
133 @classmethod
134 @classmethod
134 def clear(cls, repo):
135 def clear(cls, repo):
135 util.unlinkpath(repo.join(cls._filename), ignoremissing=True)
136 util.unlinkpath(repo.join(cls._filename), ignoremissing=True)
136
137
137 def createcmd(ui, repo, pats, opts):
138 def createcmd(ui, repo, pats, opts):
138 """subcommand that creates a new shelve"""
139 """subcommand that creates a new shelve"""
139
140
140 def publicancestors(ctx):
141 def publicancestors(ctx):
141 """Compute the public ancestors of a commit.
142 """Compute the public ancestors of a commit.
142
143
143 Much faster than the revset ancestors(ctx) & draft()"""
144 Much faster than the revset ancestors(ctx) & draft()"""
144 seen = set([nullrev])
145 seen = set([nullrev])
145 visit = util.deque()
146 visit = util.deque()
146 visit.append(ctx)
147 visit.append(ctx)
147 while visit:
148 while visit:
148 ctx = visit.popleft()
149 ctx = visit.popleft()
149 yield ctx.node()
150 yield ctx.node()
150 for parent in ctx.parents():
151 for parent in ctx.parents():
151 rev = parent.rev()
152 rev = parent.rev()
152 if rev not in seen:
153 if rev not in seen:
153 seen.add(rev)
154 seen.add(rev)
154 if parent.mutable():
155 if parent.mutable():
155 visit.append(parent)
156 visit.append(parent)
156
157
157 wctx = repo[None]
158 wctx = repo[None]
158 parents = wctx.parents()
159 parents = wctx.parents()
159 if len(parents) > 1:
160 if len(parents) > 1:
160 raise util.Abort(_('cannot shelve while merging'))
161 raise util.Abort(_('cannot shelve while merging'))
161 parent = parents[0]
162 parent = parents[0]
162
163
163 # we never need the user, so we use a generic user for all shelve operations
164 # we never need the user, so we use a generic user for all shelve operations
164 user = 'shelve@localhost'
165 user = 'shelve@localhost'
165 label = repo._bookmarkcurrent or parent.branch() or 'default'
166 label = repo._bookmarkcurrent or parent.branch() or 'default'
166
167
167 # slashes aren't allowed in filenames, therefore we rename it
168 # slashes aren't allowed in filenames, therefore we rename it
168 label = label.replace('/', '_')
169 label = label.replace('/', '_')
169
170
170 def gennames():
171 def gennames():
171 yield label
172 yield label
172 for i in xrange(1, 100):
173 for i in xrange(1, 100):
173 yield '%s-%02d' % (label, i)
174 yield '%s-%02d' % (label, i)
174
175
175 def commitfunc(ui, repo, message, match, opts):
176 def commitfunc(ui, repo, message, match, opts):
176 hasmq = util.safehasattr(repo, 'mq')
177 hasmq = util.safehasattr(repo, 'mq')
177 if hasmq:
178 if hasmq:
178 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
179 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
179 backup = repo.ui.backupconfig('phases', 'new-commit')
180 backup = repo.ui.backupconfig('phases', 'new-commit')
180 try:
181 try:
181 repo.ui. setconfig('phases', 'new-commit', phases.secret)
182 repo.ui. setconfig('phases', 'new-commit', phases.secret)
182 editor = cmdutil.getcommiteditor(editform='shelve.shelve', **opts)
183 editor = cmdutil.getcommiteditor(editform='shelve.shelve', **opts)
183 return repo.commit(message, user, opts.get('date'), match,
184 return repo.commit(message, user, opts.get('date'), match,
184 editor=editor)
185 editor=editor)
185 finally:
186 finally:
186 repo.ui.restoreconfig(backup)
187 repo.ui.restoreconfig(backup)
187 if hasmq:
188 if hasmq:
188 repo.mq.checkapplied = saved
189 repo.mq.checkapplied = saved
189
190
190 if parent.node() != nullid:
191 if parent.node() != nullid:
191 desc = "changes to '%s'" % parent.description().split('\n', 1)[0]
192 desc = "changes to '%s'" % parent.description().split('\n', 1)[0]
192 else:
193 else:
193 desc = '(changes in empty repository)'
194 desc = '(changes in empty repository)'
194
195
195 if not opts['message']:
196 if not opts['message']:
196 opts['message'] = desc
197 opts['message'] = desc
197
198
198 name = opts['name']
199 name = opts['name']
199
200
200 wlock = lock = tr = bms = None
201 wlock = lock = tr = bms = None
201 try:
202 try:
202 wlock = repo.wlock()
203 wlock = repo.wlock()
203 lock = repo.lock()
204 lock = repo.lock()
204
205
205 bms = repo._bookmarks.copy()
206 bms = repo._bookmarks.copy()
206 # use an uncommitted transaction to generate the bundle to avoid
207 # use an uncommitted transaction to generate the bundle to avoid
207 # pull races. ensure we don't print the abort message to stderr.
208 # pull races. ensure we don't print the abort message to stderr.
208 tr = repo.transaction('commit', report=lambda x: None)
209 tr = repo.transaction('commit', report=lambda x: None)
209
210
210 if name:
211 if name:
211 if shelvedfile(repo, name, 'hg').exists():
212 if shelvedfile(repo, name, 'hg').exists():
212 raise util.Abort(_("a shelved change named '%s' already exists")
213 raise util.Abort(_("a shelved change named '%s' already exists")
213 % name)
214 % name)
214 else:
215 else:
215 for n in gennames():
216 for n in gennames():
216 if not shelvedfile(repo, n, 'hg').exists():
217 if not shelvedfile(repo, n, 'hg').exists():
217 name = n
218 name = n
218 break
219 break
219 else:
220 else:
220 raise util.Abort(_("too many shelved changes named '%s'") %
221 raise util.Abort(_("too many shelved changes named '%s'") %
221 label)
222 label)
222
223
223 # ensure we are not creating a subdirectory or a hidden file
224 # ensure we are not creating a subdirectory or a hidden file
224 if '/' in name or '\\' in name:
225 if '/' in name or '\\' in name:
225 raise util.Abort(_('shelved change names may not contain slashes'))
226 raise util.Abort(_('shelved change names may not contain slashes'))
226 if name.startswith('.'):
227 if name.startswith('.'):
227 raise util.Abort(_("shelved change names may not start with '.'"))
228 raise util.Abort(_("shelved change names may not start with '.'"))
228
229
229 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
230 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
230
231
231 if not node:
232 if not node:
232 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
233 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
233 if stat.deleted:
234 if stat.deleted:
234 ui.status(_("nothing changed (%d missing files, see "
235 ui.status(_("nothing changed (%d missing files, see "
235 "'hg status')\n") % len(stat.deleted))
236 "'hg status')\n") % len(stat.deleted))
236 else:
237 else:
237 ui.status(_("nothing changed\n"))
238 ui.status(_("nothing changed\n"))
238 return 1
239 return 1
239
240
240 bases = list(publicancestors(repo[node]))
241 bases = list(publicancestors(repo[node]))
241 cg = changegroup.changegroupsubset(repo, bases, [node], 'shelve')
242 cg = changegroup.changegroupsubset(repo, bases, [node], 'shelve')
242 shelvedfile(repo, name, 'hg').writebundle(cg)
243 shelvedfile(repo, name, 'hg').writebundle(cg)
243 cmdutil.export(repo, [node],
244 cmdutil.export(repo, [node],
244 fp=shelvedfile(repo, name, 'patch').opener('wb'),
245 fp=shelvedfile(repo, name, 'patch').opener('wb'),
245 opts=mdiff.diffopts(git=True))
246 opts=mdiff.diffopts(git=True))
246
247
247
248
248 if ui.formatted():
249 if ui.formatted():
249 desc = util.ellipsis(desc, ui.termwidth())
250 desc = util.ellipsis(desc, ui.termwidth())
250 ui.status(_('shelved as %s\n') % name)
251 ui.status(_('shelved as %s\n') % name)
251 hg.update(repo, parent.node())
252 hg.update(repo, parent.node())
252 finally:
253 finally:
253 if bms:
254 if bms:
254 # restore old bookmarks
255 # restore old bookmarks
255 repo._bookmarks.update(bms)
256 repo._bookmarks.update(bms)
256 repo._bookmarks.write()
257 repo._bookmarks.write()
257 if tr:
258 if tr:
258 tr.abort()
259 tr.abort()
259 lockmod.release(lock, wlock)
260 lockmod.release(lock, wlock)
260
261
261 def cleanupcmd(ui, repo):
262 def cleanupcmd(ui, repo):
262 """subcommand that deletes all shelves"""
263 """subcommand that deletes all shelves"""
263
264
264 wlock = None
265 wlock = None
265 try:
266 try:
266 wlock = repo.wlock()
267 wlock = repo.wlock()
267 for (name, _type) in repo.vfs.readdir('shelved'):
268 for (name, _type) in repo.vfs.readdir('shelved'):
268 suffix = name.rsplit('.', 1)[-1]
269 suffix = name.rsplit('.', 1)[-1]
269 if suffix in ('hg', 'patch'):
270 if suffix in ('hg', 'patch'):
270 shelvedfile(repo, name).unlink()
271 shelvedfile(repo, name).unlink()
271 finally:
272 finally:
272 lockmod.release(wlock)
273 lockmod.release(wlock)
273
274
274 def deletecmd(ui, repo, pats):
275 def deletecmd(ui, repo, pats):
275 """subcommand that deletes a specific shelve"""
276 """subcommand that deletes a specific shelve"""
276 if not pats:
277 if not pats:
277 raise util.Abort(_('no shelved changes specified!'))
278 raise util.Abort(_('no shelved changes specified!'))
278 wlock = None
279 wlock = None
279 try:
280 try:
280 wlock = repo.wlock()
281 wlock = repo.wlock()
281 try:
282 try:
282 for name in pats:
283 for name in pats:
283 for suffix in 'hg patch'.split():
284 for suffix in 'hg patch'.split():
284 shelvedfile(repo, name, suffix).unlink()
285 shelvedfile(repo, name, suffix).unlink()
285 except OSError, err:
286 except OSError, err:
286 if err.errno != errno.ENOENT:
287 if err.errno != errno.ENOENT:
287 raise
288 raise
288 raise util.Abort(_("shelved change '%s' not found") % name)
289 raise util.Abort(_("shelved change '%s' not found") % name)
289 finally:
290 finally:
290 lockmod.release(wlock)
291 lockmod.release(wlock)
291
292
292 def listshelves(repo):
293 def listshelves(repo):
293 """return all shelves in repo as list of (time, filename)"""
294 """return all shelves in repo as list of (time, filename)"""
294 try:
295 try:
295 names = repo.vfs.readdir('shelved')
296 names = repo.vfs.readdir('shelved')
296 except OSError, err:
297 except OSError, err:
297 if err.errno != errno.ENOENT:
298 if err.errno != errno.ENOENT:
298 raise
299 raise
299 return []
300 return []
300 info = []
301 info = []
301 for (name, _type) in names:
302 for (name, _type) in names:
302 pfx, sfx = name.rsplit('.', 1)
303 pfx, sfx = name.rsplit('.', 1)
303 if not pfx or sfx != 'patch':
304 if not pfx or sfx != 'patch':
304 continue
305 continue
305 st = shelvedfile(repo, name).stat()
306 st = shelvedfile(repo, name).stat()
306 info.append((st.st_mtime, shelvedfile(repo, pfx).filename()))
307 info.append((st.st_mtime, shelvedfile(repo, pfx).filename()))
307 return sorted(info, reverse=True)
308 return sorted(info, reverse=True)
308
309
309 def listcmd(ui, repo, pats, opts):
310 def listcmd(ui, repo, pats, opts):
310 """subcommand that displays the list of shelves"""
311 """subcommand that displays the list of shelves"""
311 pats = set(pats)
312 pats = set(pats)
312 width = 80
313 width = 80
313 if not ui.plain():
314 if not ui.plain():
314 width = ui.termwidth()
315 width = ui.termwidth()
315 namelabel = 'shelve.newest'
316 namelabel = 'shelve.newest'
316 for mtime, name in listshelves(repo):
317 for mtime, name in listshelves(repo):
317 sname = util.split(name)[1]
318 sname = util.split(name)[1]
318 if pats and sname not in pats:
319 if pats and sname not in pats:
319 continue
320 continue
320 ui.write(sname, label=namelabel)
321 ui.write(sname, label=namelabel)
321 namelabel = 'shelve.name'
322 namelabel = 'shelve.name'
322 if ui.quiet:
323 if ui.quiet:
323 ui.write('\n')
324 ui.write('\n')
324 continue
325 continue
325 ui.write(' ' * (16 - len(sname)))
326 ui.write(' ' * (16 - len(sname)))
326 used = 16
327 used = 16
327 age = '(%s)' % templatefilters.age(util.makedate(mtime), abbrev=True)
328 age = '(%s)' % templatefilters.age(util.makedate(mtime), abbrev=True)
328 ui.write(age, label='shelve.age')
329 ui.write(age, label='shelve.age')
329 ui.write(' ' * (12 - len(age)))
330 ui.write(' ' * (12 - len(age)))
330 used += 12
331 used += 12
331 fp = open(name + '.patch', 'rb')
332 fp = open(name + '.patch', 'rb')
332 try:
333 try:
333 while True:
334 while True:
334 line = fp.readline()
335 line = fp.readline()
335 if not line:
336 if not line:
336 break
337 break
337 if not line.startswith('#'):
338 if not line.startswith('#'):
338 desc = line.rstrip()
339 desc = line.rstrip()
339 if ui.formatted():
340 if ui.formatted():
340 desc = util.ellipsis(desc, width - used)
341 desc = util.ellipsis(desc, width - used)
341 ui.write(desc)
342 ui.write(desc)
342 break
343 break
343 ui.write('\n')
344 ui.write('\n')
344 if not (opts['patch'] or opts['stat']):
345 if not (opts['patch'] or opts['stat']):
345 continue
346 continue
346 difflines = fp.readlines()
347 difflines = fp.readlines()
347 if opts['patch']:
348 if opts['patch']:
348 for chunk, label in patch.difflabel(iter, difflines):
349 for chunk, label in patch.difflabel(iter, difflines):
349 ui.write(chunk, label=label)
350 ui.write(chunk, label=label)
350 if opts['stat']:
351 if opts['stat']:
351 for chunk, label in patch.diffstatui(difflines, width=width,
352 for chunk, label in patch.diffstatui(difflines, width=width,
352 git=True):
353 git=True):
353 ui.write(chunk, label=label)
354 ui.write(chunk, label=label)
354 finally:
355 finally:
355 fp.close()
356 fp.close()
356
357
357 def checkparents(repo, state):
358 def checkparents(repo, state):
358 """check parent while resuming an unshelve"""
359 """check parent while resuming an unshelve"""
359 if state.parents != repo.dirstate.parents():
360 if state.parents != repo.dirstate.parents():
360 raise util.Abort(_('working directory parents do not match unshelve '
361 raise util.Abort(_('working directory parents do not match unshelve '
361 'state'))
362 'state'))
362
363
363 def pathtofiles(repo, files):
364 def pathtofiles(repo, files):
364 cwd = repo.getcwd()
365 cwd = repo.getcwd()
365 return [repo.pathto(f, cwd) for f in files]
366 return [repo.pathto(f, cwd) for f in files]
366
367
367 def unshelveabort(ui, repo, state, opts):
368 def unshelveabort(ui, repo, state, opts):
368 """subcommand that abort an in-progress unshelve"""
369 """subcommand that abort an in-progress unshelve"""
369 wlock = repo.wlock()
370 wlock = repo.wlock()
370 lock = None
371 lock = None
371 try:
372 try:
372 checkparents(repo, state)
373 checkparents(repo, state)
373
374
374 util.rename(repo.join('unshelverebasestate'),
375 util.rename(repo.join('unshelverebasestate'),
375 repo.join('rebasestate'))
376 repo.join('rebasestate'))
376 try:
377 try:
377 rebase.rebase(ui, repo, **{
378 rebase.rebase(ui, repo, **{
378 'abort' : True
379 'abort' : True
379 })
380 })
380 except Exception:
381 except Exception:
381 util.rename(repo.join('rebasestate'),
382 util.rename(repo.join('rebasestate'),
382 repo.join('unshelverebasestate'))
383 repo.join('unshelverebasestate'))
383 raise
384 raise
384
385
385 lock = repo.lock()
386 lock = repo.lock()
386
387
387 mergefiles(ui, repo, state.wctx, state.pendingctx)
388 mergefiles(ui, repo, state.wctx, state.pendingctx)
388
389
389 repair.strip(ui, repo, state.stripnodes, backup=False, topic='shelve')
390 repair.strip(ui, repo, state.stripnodes, backup=False, topic='shelve')
390 shelvedstate.clear(repo)
391 shelvedstate.clear(repo)
391 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
392 ui.warn(_("unshelve of '%s' aborted\n") % state.name)
392 finally:
393 finally:
393 lockmod.release(lock, wlock)
394 lockmod.release(lock, wlock)
394
395
395 def mergefiles(ui, repo, wctx, shelvectx):
396 def mergefiles(ui, repo, wctx, shelvectx):
396 """updates to wctx and merges the changes from shelvectx into the
397 """updates to wctx and merges the changes from shelvectx into the
397 dirstate."""
398 dirstate."""
398 oldquiet = ui.quiet
399 oldquiet = ui.quiet
399 try:
400 try:
400 ui.quiet = True
401 ui.quiet = True
401 hg.update(repo, wctx.node())
402 hg.update(repo, wctx.node())
402 files = []
403 files = []
403 files.extend(shelvectx.files())
404 files.extend(shelvectx.files())
404 files.extend(shelvectx.parents()[0].files())
405 files.extend(shelvectx.parents()[0].files())
405
406
406 # revert will overwrite unknown files, so move them out of the way
407 # revert will overwrite unknown files, so move them out of the way
407 for file in repo.status(unknown=True).unknown:
408 for file in repo.status(unknown=True).unknown:
408 if file in files:
409 if file in files:
409 util.rename(file, file + ".orig")
410 util.rename(file, file + ".orig")
410 ui.pushbuffer(True)
411 ui.pushbuffer(True)
411 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
412 cmdutil.revert(ui, repo, shelvectx, repo.dirstate.parents(),
412 *pathtofiles(repo, files),
413 *pathtofiles(repo, files),
413 **{'no_backup': True})
414 **{'no_backup': True})
414 ui.popbuffer()
415 ui.popbuffer()
415 finally:
416 finally:
416 ui.quiet = oldquiet
417 ui.quiet = oldquiet
417
418
418 def unshelvecleanup(ui, repo, name, opts):
419 def unshelvecleanup(ui, repo, name, opts):
419 """remove related files after an unshelve"""
420 """remove related files after an unshelve"""
420 if not opts['keep']:
421 if not opts['keep']:
421 for filetype in 'hg patch'.split():
422 for filetype in 'hg patch'.split():
422 shelvedfile(repo, name, filetype).unlink()
423 shelvedfile(repo, name, filetype).unlink()
423
424
424 def unshelvecontinue(ui, repo, state, opts):
425 def unshelvecontinue(ui, repo, state, opts):
425 """subcommand to continue an in-progress unshelve"""
426 """subcommand to continue an in-progress unshelve"""
426 # We're finishing off a merge. First parent is our original
427 # We're finishing off a merge. First parent is our original
427 # parent, second is the temporary "fake" commit we're unshelving.
428 # parent, second is the temporary "fake" commit we're unshelving.
428 wlock = repo.wlock()
429 wlock = repo.wlock()
429 lock = None
430 lock = None
430 try:
431 try:
431 checkparents(repo, state)
432 checkparents(repo, state)
432 ms = merge.mergestate(repo)
433 ms = merge.mergestate(repo)
433 if [f for f in ms if ms[f] == 'u']:
434 if [f for f in ms if ms[f] == 'u']:
434 raise util.Abort(
435 raise util.Abort(
435 _("unresolved conflicts, can't continue"),
436 _("unresolved conflicts, can't continue"),
436 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
437 hint=_("see 'hg resolve', then 'hg unshelve --continue'"))
437
438
438 lock = repo.lock()
439 lock = repo.lock()
439
440
440 util.rename(repo.join('unshelverebasestate'),
441 util.rename(repo.join('unshelverebasestate'),
441 repo.join('rebasestate'))
442 repo.join('rebasestate'))
442 try:
443 try:
443 rebase.rebase(ui, repo, **{
444 rebase.rebase(ui, repo, **{
444 'continue' : True
445 'continue' : True
445 })
446 })
446 except Exception:
447 except Exception:
447 util.rename(repo.join('rebasestate'),
448 util.rename(repo.join('rebasestate'),
448 repo.join('unshelverebasestate'))
449 repo.join('unshelverebasestate'))
449 raise
450 raise
450
451
451 shelvectx = repo['tip']
452 shelvectx = repo['tip']
452 if not shelvectx in state.pendingctx.children():
453 if not shelvectx in state.pendingctx.children():
453 # rebase was a no-op, so it produced no child commit
454 # rebase was a no-op, so it produced no child commit
454 shelvectx = state.pendingctx
455 shelvectx = state.pendingctx
455 else:
456 else:
456 # only strip the shelvectx if the rebase produced it
457 # only strip the shelvectx if the rebase produced it
457 state.stripnodes.append(shelvectx.node())
458 state.stripnodes.append(shelvectx.node())
458
459
459 mergefiles(ui, repo, state.wctx, shelvectx)
460 mergefiles(ui, repo, state.wctx, shelvectx)
460
461
461 repair.strip(ui, repo, state.stripnodes, backup=False, topic='shelve')
462 repair.strip(ui, repo, state.stripnodes, backup=False, topic='shelve')
462 shelvedstate.clear(repo)
463 shelvedstate.clear(repo)
463 unshelvecleanup(ui, repo, state.name, opts)
464 unshelvecleanup(ui, repo, state.name, opts)
464 ui.status(_("unshelve of '%s' complete\n") % state.name)
465 ui.status(_("unshelve of '%s' complete\n") % state.name)
465 finally:
466 finally:
466 lockmod.release(lock, wlock)
467 lockmod.release(lock, wlock)
467
468
468 @command('unshelve',
469 @command('unshelve',
469 [('a', 'abort', None,
470 [('a', 'abort', None,
470 _('abort an incomplete unshelve operation')),
471 _('abort an incomplete unshelve operation')),
471 ('c', 'continue', None,
472 ('c', 'continue', None,
472 _('continue an incomplete unshelve operation')),
473 _('continue an incomplete unshelve operation')),
473 ('', 'keep', None,
474 ('', 'keep', None,
474 _('keep shelve after unshelving')),
475 _('keep shelve after unshelving')),
475 ('', 'date', '',
476 ('', 'date', '',
476 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
477 _('set date for temporary commits (DEPRECATED)'), _('DATE'))],
477 _('hg unshelve [SHELVED]'))
478 _('hg unshelve [SHELVED]'))
478 def unshelve(ui, repo, *shelved, **opts):
479 def unshelve(ui, repo, *shelved, **opts):
479 """restore a shelved change to the working directory
480 """restore a shelved change to the working directory
480
481
481 This command accepts an optional name of a shelved change to
482 This command accepts an optional name of a shelved change to
482 restore. If none is given, the most recent shelved change is used.
483 restore. If none is given, the most recent shelved change is used.
483
484
484 If a shelved change is applied successfully, the bundle that
485 If a shelved change is applied successfully, the bundle that
485 contains the shelved changes is deleted afterwards.
486 contains the shelved changes is deleted afterwards.
486
487
487 Since you can restore a shelved change on top of an arbitrary
488 Since you can restore a shelved change on top of an arbitrary
488 commit, it is possible that unshelving will result in a conflict
489 commit, it is possible that unshelving will result in a conflict
489 between your changes and the commits you are unshelving onto. If
490 between your changes and the commits you are unshelving onto. If
490 this occurs, you must resolve the conflict, then use
491 this occurs, you must resolve the conflict, then use
491 ``--continue`` to complete the unshelve operation. (The bundle
492 ``--continue`` to complete the unshelve operation. (The bundle
492 will not be deleted until you successfully complete the unshelve.)
493 will not be deleted until you successfully complete the unshelve.)
493
494
494 (Alternatively, you can use ``--abort`` to abandon an unshelve
495 (Alternatively, you can use ``--abort`` to abandon an unshelve
495 that causes a conflict. This reverts the unshelved changes, and
496 that causes a conflict. This reverts the unshelved changes, and
496 does not delete the bundle.)
497 does not delete the bundle.)
497 """
498 """
498 abortf = opts['abort']
499 abortf = opts['abort']
499 continuef = opts['continue']
500 continuef = opts['continue']
500 if not abortf and not continuef:
501 if not abortf and not continuef:
501 cmdutil.checkunfinished(repo)
502 cmdutil.checkunfinished(repo)
502
503
503 if abortf or continuef:
504 if abortf or continuef:
504 if abortf and continuef:
505 if abortf and continuef:
505 raise util.Abort(_('cannot use both abort and continue'))
506 raise util.Abort(_('cannot use both abort and continue'))
506 if shelved:
507 if shelved:
507 raise util.Abort(_('cannot combine abort/continue with '
508 raise util.Abort(_('cannot combine abort/continue with '
508 'naming a shelved change'))
509 'naming a shelved change'))
509
510
510 try:
511 try:
511 state = shelvedstate.load(repo)
512 state = shelvedstate.load(repo)
512 except IOError, err:
513 except IOError, err:
513 if err.errno != errno.ENOENT:
514 if err.errno != errno.ENOENT:
514 raise
515 raise
515 raise util.Abort(_('no unshelve operation underway'))
516 raise util.Abort(_('no unshelve operation underway'))
516
517
517 if abortf:
518 if abortf:
518 return unshelveabort(ui, repo, state, opts)
519 return unshelveabort(ui, repo, state, opts)
519 elif continuef:
520 elif continuef:
520 return unshelvecontinue(ui, repo, state, opts)
521 return unshelvecontinue(ui, repo, state, opts)
521 elif len(shelved) > 1:
522 elif len(shelved) > 1:
522 raise util.Abort(_('can only unshelve one change at a time'))
523 raise util.Abort(_('can only unshelve one change at a time'))
523 elif not shelved:
524 elif not shelved:
524 shelved = listshelves(repo)
525 shelved = listshelves(repo)
525 if not shelved:
526 if not shelved:
526 raise util.Abort(_('no shelved changes to apply!'))
527 raise util.Abort(_('no shelved changes to apply!'))
527 basename = util.split(shelved[0][1])[1]
528 basename = util.split(shelved[0][1])[1]
528 ui.status(_("unshelving change '%s'\n") % basename)
529 ui.status(_("unshelving change '%s'\n") % basename)
529 else:
530 else:
530 basename = shelved[0]
531 basename = shelved[0]
531
532
532 if not shelvedfile(repo, basename, 'patch').exists():
533 if not shelvedfile(repo, basename, 'patch').exists():
533 raise util.Abort(_("shelved change '%s' not found") % basename)
534 raise util.Abort(_("shelved change '%s' not found") % basename)
534
535
535 oldquiet = ui.quiet
536 oldquiet = ui.quiet
536 wlock = lock = tr = None
537 wlock = lock = tr = None
537 try:
538 try:
538 lock = repo.lock()
539 lock = repo.lock()
539 wlock = repo.wlock()
540 wlock = repo.wlock()
540
541
541 tr = repo.transaction('unshelve', report=lambda x: None)
542 tr = repo.transaction('unshelve', report=lambda x: None)
542 oldtiprev = len(repo)
543 oldtiprev = len(repo)
543
544
544 pctx = repo['.']
545 pctx = repo['.']
545 tmpwctx = pctx
546 tmpwctx = pctx
546 # The goal is to have a commit structure like so:
547 # The goal is to have a commit structure like so:
547 # ...-> pctx -> tmpwctx -> shelvectx
548 # ...-> pctx -> tmpwctx -> shelvectx
548 # where tmpwctx is an optional commit with the user's pending changes
549 # where tmpwctx is an optional commit with the user's pending changes
549 # and shelvectx is the unshelved changes. Then we merge it all down
550 # and shelvectx is the unshelved changes. Then we merge it all down
550 # to the original pctx.
551 # to the original pctx.
551
552
552 # Store pending changes in a commit
553 # Store pending changes in a commit
553 s = repo.status()
554 s = repo.status()
554 if s.modified or s.added or s.removed or s.deleted:
555 if s.modified or s.added or s.removed or s.deleted:
555 ui.status(_("temporarily committing pending changes "
556 ui.status(_("temporarily committing pending changes "
556 "(restore with 'hg unshelve --abort')\n"))
557 "(restore with 'hg unshelve --abort')\n"))
557 def commitfunc(ui, repo, message, match, opts):
558 def commitfunc(ui, repo, message, match, opts):
558 hasmq = util.safehasattr(repo, 'mq')
559 hasmq = util.safehasattr(repo, 'mq')
559 if hasmq:
560 if hasmq:
560 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
561 saved, repo.mq.checkapplied = repo.mq.checkapplied, False
561
562
562 backup = repo.ui.backupconfig('phases', 'new-commit')
563 backup = repo.ui.backupconfig('phases', 'new-commit')
563 try:
564 try:
564 repo.ui. setconfig('phases', 'new-commit', phases.secret)
565 repo.ui. setconfig('phases', 'new-commit', phases.secret)
565 return repo.commit(message, 'shelve@localhost',
566 return repo.commit(message, 'shelve@localhost',
566 opts.get('date'), match)
567 opts.get('date'), match)
567 finally:
568 finally:
568 repo.ui.restoreconfig(backup)
569 repo.ui.restoreconfig(backup)
569 if hasmq:
570 if hasmq:
570 repo.mq.checkapplied = saved
571 repo.mq.checkapplied = saved
571
572
572 tempopts = {}
573 tempopts = {}
573 tempopts['message'] = "pending changes temporary commit"
574 tempopts['message'] = "pending changes temporary commit"
574 tempopts['date'] = opts.get('date')
575 tempopts['date'] = opts.get('date')
575 ui.quiet = True
576 ui.quiet = True
576 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
577 node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
577 tmpwctx = repo[node]
578 tmpwctx = repo[node]
578
579
579 ui.quiet = True
580 ui.quiet = True
580 shelvedfile(repo, basename, 'hg').applybundle()
581 shelvedfile(repo, basename, 'hg').applybundle()
581
582
582 ui.quiet = oldquiet
583 ui.quiet = oldquiet
583
584
584 shelvectx = repo['tip']
585 shelvectx = repo['tip']
585
586
586 # If the shelve is not immediately on top of the commit
587 # If the shelve is not immediately on top of the commit
587 # we'll be merging with, rebase it to be on top.
588 # we'll be merging with, rebase it to be on top.
588 if tmpwctx.node() != shelvectx.parents()[0].node():
589 if tmpwctx.node() != shelvectx.parents()[0].node():
589 ui.status(_('rebasing shelved changes\n'))
590 ui.status(_('rebasing shelved changes\n'))
590 try:
591 try:
591 rebase.rebase(ui, repo, **{
592 rebase.rebase(ui, repo, **{
592 'rev' : [shelvectx.rev()],
593 'rev' : [shelvectx.rev()],
593 'dest' : str(tmpwctx.rev()),
594 'dest' : str(tmpwctx.rev()),
594 'keep' : True,
595 'keep' : True,
595 })
596 })
596 except error.InterventionRequired:
597 except error.InterventionRequired:
597 tr.close()
598 tr.close()
598
599
599 stripnodes = [repo.changelog.node(rev)
600 stripnodes = [repo.changelog.node(rev)
600 for rev in xrange(oldtiprev, len(repo))]
601 for rev in xrange(oldtiprev, len(repo))]
601 shelvedstate.save(repo, basename, pctx, tmpwctx, stripnodes)
602 shelvedstate.save(repo, basename, pctx, tmpwctx, stripnodes)
602
603
603 util.rename(repo.join('rebasestate'),
604 util.rename(repo.join('rebasestate'),
604 repo.join('unshelverebasestate'))
605 repo.join('unshelverebasestate'))
605 raise error.InterventionRequired(
606 raise error.InterventionRequired(
606 _("unresolved conflicts (see 'hg resolve', then "
607 _("unresolved conflicts (see 'hg resolve', then "
607 "'hg unshelve --continue')"))
608 "'hg unshelve --continue')"))
608
609
609 # refresh ctx after rebase completes
610 # refresh ctx after rebase completes
610 shelvectx = repo['tip']
611 shelvectx = repo['tip']
611
612
612 if not shelvectx in tmpwctx.children():
613 if not shelvectx in tmpwctx.children():
613 # rebase was a no-op, so it produced no child commit
614 # rebase was a no-op, so it produced no child commit
614 shelvectx = tmpwctx
615 shelvectx = tmpwctx
615
616
616 mergefiles(ui, repo, pctx, shelvectx)
617 mergefiles(ui, repo, pctx, shelvectx)
617 shelvedstate.clear(repo)
618 shelvedstate.clear(repo)
618
619
619 # The transaction aborting will strip all the commits for us,
620 # The transaction aborting will strip all the commits for us,
620 # but it doesn't update the inmemory structures, so addchangegroup
621 # but it doesn't update the inmemory structures, so addchangegroup
621 # hooks still fire and try to operate on the missing commits.
622 # hooks still fire and try to operate on the missing commits.
622 # Clean up manually to prevent this.
623 # Clean up manually to prevent this.
623 repo.unfiltered().changelog.strip(oldtiprev, tr)
624 repo.unfiltered().changelog.strip(oldtiprev, tr)
624
625
625 unshelvecleanup(ui, repo, basename, opts)
626 unshelvecleanup(ui, repo, basename, opts)
626 finally:
627 finally:
627 ui.quiet = oldquiet
628 ui.quiet = oldquiet
628 if tr:
629 if tr:
629 tr.release()
630 tr.release()
630 lockmod.release(lock, wlock)
631 lockmod.release(lock, wlock)
631
632
632 @command('shelve',
633 @command('shelve',
633 [('A', 'addremove', None,
634 [('A', 'addremove', None,
634 _('mark new/missing files as added/removed before shelving')),
635 _('mark new/missing files as added/removed before shelving')),
635 ('', 'cleanup', None,
636 ('', 'cleanup', None,
636 _('delete all shelved changes')),
637 _('delete all shelved changes')),
637 ('', 'date', '',
638 ('', 'date', '',
638 _('shelve with the specified commit date'), _('DATE')),
639 _('shelve with the specified commit date'), _('DATE')),
639 ('d', 'delete', None,
640 ('d', 'delete', None,
640 _('delete the named shelved change(s)')),
641 _('delete the named shelved change(s)')),
641 ('e', 'edit', False,
642 ('e', 'edit', False,
642 _('invoke editor on commit messages')),
643 _('invoke editor on commit messages')),
643 ('l', 'list', None,
644 ('l', 'list', None,
644 _('list current shelves')),
645 _('list current shelves')),
645 ('m', 'message', '',
646 ('m', 'message', '',
646 _('use text as shelve message'), _('TEXT')),
647 _('use text as shelve message'), _('TEXT')),
647 ('n', 'name', '',
648 ('n', 'name', '',
648 _('use the given name for the shelved commit'), _('NAME')),
649 _('use the given name for the shelved commit'), _('NAME')),
649 ('p', 'patch', None,
650 ('p', 'patch', None,
650 _('show patch')),
651 _('show patch')),
651 ('', 'stat', None,
652 ('', 'stat', None,
652 _('output diffstat-style summary of changes'))] + commands.walkopts,
653 _('output diffstat-style summary of changes'))] + commands.walkopts,
653 _('hg shelve [OPTION]... [FILE]...'))
654 _('hg shelve [OPTION]... [FILE]...'))
654 def shelvecmd(ui, repo, *pats, **opts):
655 def shelvecmd(ui, repo, *pats, **opts):
655 '''save and set aside changes from the working directory
656 '''save and set aside changes from the working directory
656
657
657 Shelving takes files that "hg status" reports as not clean, saves
658 Shelving takes files that "hg status" reports as not clean, saves
658 the modifications to a bundle (a shelved change), and reverts the
659 the modifications to a bundle (a shelved change), and reverts the
659 files so that their state in the working directory becomes clean.
660 files so that their state in the working directory becomes clean.
660
661
661 To restore these changes to the working directory, using "hg
662 To restore these changes to the working directory, using "hg
662 unshelve"; this will work even if you switch to a different
663 unshelve"; this will work even if you switch to a different
663 commit.
664 commit.
664
665
665 When no files are specified, "hg shelve" saves all not-clean
666 When no files are specified, "hg shelve" saves all not-clean
666 files. If specific files or directories are named, only changes to
667 files. If specific files or directories are named, only changes to
667 those files are shelved.
668 those files are shelved.
668
669
669 Each shelved change has a name that makes it easier to find later.
670 Each shelved change has a name that makes it easier to find later.
670 The name of a shelved change defaults to being based on the active
671 The name of a shelved change defaults to being based on the active
671 bookmark, or if there is no active bookmark, the current named
672 bookmark, or if there is no active bookmark, the current named
672 branch. To specify a different name, use ``--name``.
673 branch. To specify a different name, use ``--name``.
673
674
674 To see a list of existing shelved changes, use the ``--list``
675 To see a list of existing shelved changes, use the ``--list``
675 option. For each shelved change, this will print its name, age,
676 option. For each shelved change, this will print its name, age,
676 and description; use ``--patch`` or ``--stat`` for more details.
677 and description; use ``--patch`` or ``--stat`` for more details.
677
678
678 To delete specific shelved changes, use ``--delete``. To delete
679 To delete specific shelved changes, use ``--delete``. To delete
679 all shelved changes, use ``--cleanup``.
680 all shelved changes, use ``--cleanup``.
680 '''
681 '''
681 cmdutil.checkunfinished(repo)
682 cmdutil.checkunfinished(repo)
682
683
683 allowables = [
684 allowables = [
684 ('addremove', 'create'), # 'create' is pseudo action
685 ('addremove', 'create'), # 'create' is pseudo action
685 ('cleanup', 'cleanup'),
686 ('cleanup', 'cleanup'),
686 # ('date', 'create'), # ignored for passing '--date "0 0"' in tests
687 # ('date', 'create'), # ignored for passing '--date "0 0"' in tests
687 ('delete', 'delete'),
688 ('delete', 'delete'),
688 ('edit', 'create'),
689 ('edit', 'create'),
689 ('list', 'list'),
690 ('list', 'list'),
690 ('message', 'create'),
691 ('message', 'create'),
691 ('name', 'create'),
692 ('name', 'create'),
692 ('patch', 'list'),
693 ('patch', 'list'),
693 ('stat', 'list'),
694 ('stat', 'list'),
694 ]
695 ]
695 def checkopt(opt):
696 def checkopt(opt):
696 if opts[opt]:
697 if opts[opt]:
697 for i, allowable in allowables:
698 for i, allowable in allowables:
698 if opts[i] and opt != allowable:
699 if opts[i] and opt != allowable:
699 raise util.Abort(_("options '--%s' and '--%s' may not be "
700 raise util.Abort(_("options '--%s' and '--%s' may not be "
700 "used together") % (opt, i))
701 "used together") % (opt, i))
701 return True
702 return True
702 if checkopt('cleanup'):
703 if checkopt('cleanup'):
703 if pats:
704 if pats:
704 raise util.Abort(_("cannot specify names when using '--cleanup'"))
705 raise util.Abort(_("cannot specify names when using '--cleanup'"))
705 return cleanupcmd(ui, repo)
706 return cleanupcmd(ui, repo)
706 elif checkopt('delete'):
707 elif checkopt('delete'):
707 return deletecmd(ui, repo, pats)
708 return deletecmd(ui, repo, pats)
708 elif checkopt('list'):
709 elif checkopt('list'):
709 return listcmd(ui, repo, pats, opts)
710 return listcmd(ui, repo, pats, opts)
710 else:
711 else:
711 for i in ('patch', 'stat'):
712 for i in ('patch', 'stat'):
712 if opts[i]:
713 if opts[i]:
713 raise util.Abort(_("option '--%s' may not be "
714 raise util.Abort(_("option '--%s' may not be "
714 "used when shelving a change") % (i,))
715 "used when shelving a change") % (i,))
715 return createcmd(ui, repo, pats, opts)
716 return createcmd(ui, repo, pats, opts)
716
717
717 def extsetup(ui):
718 def extsetup(ui):
718 cmdutil.unfinishedstates.append(
719 cmdutil.unfinishedstates.append(
719 [shelvedstate._filename, False, False,
720 [shelvedstate._filename, False, False,
720 _('unshelve already in progress'),
721 _('unshelve already in progress'),
721 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
722 _("use 'hg unshelve --continue' or 'hg unshelve --abort'")])
@@ -1,446 +1,446 b''
1 # bundlerepo.py - repository class for viewing uncompressed bundles
1 # bundlerepo.py - repository class for viewing uncompressed bundles
2 #
2 #
3 # Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
3 # Copyright 2006, 2007 Benoit Boissinot <bboissin@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 """Repository class for viewing uncompressed bundles.
8 """Repository class for viewing uncompressed bundles.
9
9
10 This provides a read-only repository interface to bundles as if they
10 This provides a read-only repository interface to bundles as if they
11 were part of the actual repository.
11 were part of the actual repository.
12 """
12 """
13
13
14 from node import nullid
14 from node import nullid
15 from i18n import _
15 from i18n import _
16 import os, tempfile, shutil
16 import os, tempfile, shutil
17 import changegroup, util, mdiff, discovery, cmdutil, scmutil, exchange
17 import changegroup, util, mdiff, discovery, cmdutil, scmutil, exchange
18 import localrepo, changelog, manifest, filelog, revlog, error, phases
18 import localrepo, changelog, manifest, filelog, revlog, error, phases
19
19
20 class bundlerevlog(revlog.revlog):
20 class bundlerevlog(revlog.revlog):
21 def __init__(self, opener, indexfile, bundle, linkmapper):
21 def __init__(self, opener, indexfile, bundle, linkmapper):
22 # How it works:
22 # How it works:
23 # To retrieve a revision, we need to know the offset of the revision in
23 # To retrieve a revision, we need to know the offset of the revision in
24 # the bundle (an unbundle object). We store this offset in the index
24 # the bundle (an unbundle object). We store this offset in the index
25 # (start). The base of the delta is stored in the base field.
25 # (start). The base of the delta is stored in the base field.
26 #
26 #
27 # To differentiate a rev in the bundle from a rev in the revlog, we
27 # To differentiate a rev in the bundle from a rev in the revlog, we
28 # check revision against repotiprev.
28 # check revision against repotiprev.
29 opener = scmutil.readonlyvfs(opener)
29 opener = scmutil.readonlyvfs(opener)
30 revlog.revlog.__init__(self, opener, indexfile)
30 revlog.revlog.__init__(self, opener, indexfile)
31 self.bundle = bundle
31 self.bundle = bundle
32 n = len(self)
32 n = len(self)
33 self.repotiprev = n - 1
33 self.repotiprev = n - 1
34 chain = None
34 chain = None
35 self.bundlerevs = set() # used by 'bundle()' revset expression
35 self.bundlerevs = set() # used by 'bundle()' revset expression
36 while True:
36 while True:
37 chunkdata = bundle.deltachunk(chain)
37 chunkdata = bundle.deltachunk(chain)
38 if not chunkdata:
38 if not chunkdata:
39 break
39 break
40 node = chunkdata['node']
40 node = chunkdata['node']
41 p1 = chunkdata['p1']
41 p1 = chunkdata['p1']
42 p2 = chunkdata['p2']
42 p2 = chunkdata['p2']
43 cs = chunkdata['cs']
43 cs = chunkdata['cs']
44 deltabase = chunkdata['deltabase']
44 deltabase = chunkdata['deltabase']
45 delta = chunkdata['delta']
45 delta = chunkdata['delta']
46
46
47 size = len(delta)
47 size = len(delta)
48 start = bundle.tell() - size
48 start = bundle.tell() - size
49
49
50 link = linkmapper(cs)
50 link = linkmapper(cs)
51 if node in self.nodemap:
51 if node in self.nodemap:
52 # this can happen if two branches make the same change
52 # this can happen if two branches make the same change
53 chain = node
53 chain = node
54 self.bundlerevs.add(self.nodemap[node])
54 self.bundlerevs.add(self.nodemap[node])
55 continue
55 continue
56
56
57 for p in (p1, p2):
57 for p in (p1, p2):
58 if p not in self.nodemap:
58 if p not in self.nodemap:
59 raise error.LookupError(p, self.indexfile,
59 raise error.LookupError(p, self.indexfile,
60 _("unknown parent"))
60 _("unknown parent"))
61
61
62 if deltabase not in self.nodemap:
62 if deltabase not in self.nodemap:
63 raise LookupError(deltabase, self.indexfile,
63 raise LookupError(deltabase, self.indexfile,
64 _('unknown delta base'))
64 _('unknown delta base'))
65
65
66 baserev = self.rev(deltabase)
66 baserev = self.rev(deltabase)
67 # start, size, full unc. size, base (unused), link, p1, p2, node
67 # start, size, full unc. size, base (unused), link, p1, p2, node
68 e = (revlog.offset_type(start, 0), size, -1, baserev, link,
68 e = (revlog.offset_type(start, 0), size, -1, baserev, link,
69 self.rev(p1), self.rev(p2), node)
69 self.rev(p1), self.rev(p2), node)
70 self.index.insert(-1, e)
70 self.index.insert(-1, e)
71 self.nodemap[node] = n
71 self.nodemap[node] = n
72 self.bundlerevs.add(n)
72 self.bundlerevs.add(n)
73 chain = node
73 chain = node
74 n += 1
74 n += 1
75
75
76 def _chunk(self, rev):
76 def _chunk(self, rev):
77 # Warning: in case of bundle, the diff is against what we stored as
77 # Warning: in case of bundle, the diff is against what we stored as
78 # delta base, not against rev - 1
78 # delta base, not against rev - 1
79 # XXX: could use some caching
79 # XXX: could use some caching
80 if rev <= self.repotiprev:
80 if rev <= self.repotiprev:
81 return revlog.revlog._chunk(self, rev)
81 return revlog.revlog._chunk(self, rev)
82 self.bundle.seek(self.start(rev))
82 self.bundle.seek(self.start(rev))
83 return self.bundle.read(self.length(rev))
83 return self.bundle.read(self.length(rev))
84
84
85 def revdiff(self, rev1, rev2):
85 def revdiff(self, rev1, rev2):
86 """return or calculate a delta between two revisions"""
86 """return or calculate a delta between two revisions"""
87 if rev1 > self.repotiprev and rev2 > self.repotiprev:
87 if rev1 > self.repotiprev and rev2 > self.repotiprev:
88 # hot path for bundle
88 # hot path for bundle
89 revb = self.index[rev2][3]
89 revb = self.index[rev2][3]
90 if revb == rev1:
90 if revb == rev1:
91 return self._chunk(rev2)
91 return self._chunk(rev2)
92 elif rev1 <= self.repotiprev and rev2 <= self.repotiprev:
92 elif rev1 <= self.repotiprev and rev2 <= self.repotiprev:
93 return revlog.revlog.revdiff(self, rev1, rev2)
93 return revlog.revlog.revdiff(self, rev1, rev2)
94
94
95 return mdiff.textdiff(self.revision(self.node(rev1)),
95 return mdiff.textdiff(self.revision(self.node(rev1)),
96 self.revision(self.node(rev2)))
96 self.revision(self.node(rev2)))
97
97
98 def revision(self, nodeorrev):
98 def revision(self, nodeorrev):
99 """return an uncompressed revision of a given node or revision
99 """return an uncompressed revision of a given node or revision
100 number.
100 number.
101 """
101 """
102 if isinstance(nodeorrev, int):
102 if isinstance(nodeorrev, int):
103 rev = nodeorrev
103 rev = nodeorrev
104 node = self.node(rev)
104 node = self.node(rev)
105 else:
105 else:
106 node = nodeorrev
106 node = nodeorrev
107 rev = self.rev(node)
107 rev = self.rev(node)
108
108
109 if node == nullid:
109 if node == nullid:
110 return ""
110 return ""
111
111
112 text = None
112 text = None
113 chain = []
113 chain = []
114 iterrev = rev
114 iterrev = rev
115 # reconstruct the revision if it is from a changegroup
115 # reconstruct the revision if it is from a changegroup
116 while iterrev > self.repotiprev:
116 while iterrev > self.repotiprev:
117 if self._cache and self._cache[1] == iterrev:
117 if self._cache and self._cache[1] == iterrev:
118 text = self._cache[2]
118 text = self._cache[2]
119 break
119 break
120 chain.append(iterrev)
120 chain.append(iterrev)
121 iterrev = self.index[iterrev][3]
121 iterrev = self.index[iterrev][3]
122 if text is None:
122 if text is None:
123 text = self.baserevision(iterrev)
123 text = self.baserevision(iterrev)
124
124
125 while chain:
125 while chain:
126 delta = self._chunk(chain.pop())
126 delta = self._chunk(chain.pop())
127 text = mdiff.patches(text, [delta])
127 text = mdiff.patches(text, [delta])
128
128
129 self._checkhash(text, node, rev)
129 self._checkhash(text, node, rev)
130 self._cache = (node, rev, text)
130 self._cache = (node, rev, text)
131 return text
131 return text
132
132
133 def baserevision(self, nodeorrev):
133 def baserevision(self, nodeorrev):
134 # Revlog subclasses may override 'revision' method to modify format of
134 # Revlog subclasses may override 'revision' method to modify format of
135 # content retrieved from revlog. To use bundlerevlog with such class one
135 # content retrieved from revlog. To use bundlerevlog with such class one
136 # needs to override 'baserevision' and make more specific call here.
136 # needs to override 'baserevision' and make more specific call here.
137 return revlog.revlog.revision(self, nodeorrev)
137 return revlog.revlog.revision(self, nodeorrev)
138
138
139 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
139 def addrevision(self, text, transaction, link, p1=None, p2=None, d=None):
140 raise NotImplementedError
140 raise NotImplementedError
141 def addgroup(self, revs, linkmapper, transaction):
141 def addgroup(self, revs, linkmapper, transaction):
142 raise NotImplementedError
142 raise NotImplementedError
143 def strip(self, rev, minlink):
143 def strip(self, rev, minlink):
144 raise NotImplementedError
144 raise NotImplementedError
145 def checksize(self):
145 def checksize(self):
146 raise NotImplementedError
146 raise NotImplementedError
147
147
148 class bundlechangelog(bundlerevlog, changelog.changelog):
148 class bundlechangelog(bundlerevlog, changelog.changelog):
149 def __init__(self, opener, bundle):
149 def __init__(self, opener, bundle):
150 changelog.changelog.__init__(self, opener)
150 changelog.changelog.__init__(self, opener)
151 linkmapper = lambda x: x
151 linkmapper = lambda x: x
152 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
152 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
153 linkmapper)
153 linkmapper)
154
154
155 def baserevision(self, nodeorrev):
155 def baserevision(self, nodeorrev):
156 # Although changelog doesn't override 'revision' method, some extensions
156 # Although changelog doesn't override 'revision' method, some extensions
157 # may replace this class with another that does. Same story with
157 # may replace this class with another that does. Same story with
158 # manifest and filelog classes.
158 # manifest and filelog classes.
159 return changelog.changelog.revision(self, nodeorrev)
159 return changelog.changelog.revision(self, nodeorrev)
160
160
161 class bundlemanifest(bundlerevlog, manifest.manifest):
161 class bundlemanifest(bundlerevlog, manifest.manifest):
162 def __init__(self, opener, bundle, linkmapper):
162 def __init__(self, opener, bundle, linkmapper):
163 manifest.manifest.__init__(self, opener)
163 manifest.manifest.__init__(self, opener)
164 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
164 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
165 linkmapper)
165 linkmapper)
166
166
167 def baserevision(self, nodeorrev):
167 def baserevision(self, nodeorrev):
168 return manifest.manifest.revision(self, nodeorrev)
168 return manifest.manifest.revision(self, nodeorrev)
169
169
170 class bundlefilelog(bundlerevlog, filelog.filelog):
170 class bundlefilelog(bundlerevlog, filelog.filelog):
171 def __init__(self, opener, path, bundle, linkmapper, repo):
171 def __init__(self, opener, path, bundle, linkmapper, repo):
172 filelog.filelog.__init__(self, opener, path)
172 filelog.filelog.__init__(self, opener, path)
173 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
173 bundlerevlog.__init__(self, opener, self.indexfile, bundle,
174 linkmapper)
174 linkmapper)
175 self._repo = repo
175 self._repo = repo
176
176
177 def baserevision(self, nodeorrev):
177 def baserevision(self, nodeorrev):
178 return filelog.filelog.revision(self, nodeorrev)
178 return filelog.filelog.revision(self, nodeorrev)
179
179
180 def _file(self, f):
180 def _file(self, f):
181 self._repo.file(f)
181 self._repo.file(f)
182
182
183 class bundlepeer(localrepo.localpeer):
183 class bundlepeer(localrepo.localpeer):
184 def canpush(self):
184 def canpush(self):
185 return False
185 return False
186
186
187 class bundlephasecache(phases.phasecache):
187 class bundlephasecache(phases.phasecache):
188 def __init__(self, *args, **kwargs):
188 def __init__(self, *args, **kwargs):
189 super(bundlephasecache, self).__init__(*args, **kwargs)
189 super(bundlephasecache, self).__init__(*args, **kwargs)
190 if util.safehasattr(self, 'opener'):
190 if util.safehasattr(self, 'opener'):
191 self.opener = scmutil.readonlyvfs(self.opener)
191 self.opener = scmutil.readonlyvfs(self.opener)
192
192
193 def write(self):
193 def write(self):
194 raise NotImplementedError
194 raise NotImplementedError
195
195
196 def _write(self, fp):
196 def _write(self, fp):
197 raise NotImplementedError
197 raise NotImplementedError
198
198
199 def _updateroots(self, phase, newroots, tr):
199 def _updateroots(self, phase, newroots, tr):
200 self.phaseroots[phase] = newroots
200 self.phaseroots[phase] = newroots
201 self.invalidate()
201 self.invalidate()
202 self.dirty = True
202 self.dirty = True
203
203
204 class bundlerepository(localrepo.localrepository):
204 class bundlerepository(localrepo.localrepository):
205 def __init__(self, ui, path, bundlename):
205 def __init__(self, ui, path, bundlename):
206 self._tempparent = None
206 self._tempparent = None
207 try:
207 try:
208 localrepo.localrepository.__init__(self, ui, path)
208 localrepo.localrepository.__init__(self, ui, path)
209 except error.RepoError:
209 except error.RepoError:
210 self._tempparent = tempfile.mkdtemp()
210 self._tempparent = tempfile.mkdtemp()
211 localrepo.instance(ui, self._tempparent, 1)
211 localrepo.instance(ui, self._tempparent, 1)
212 localrepo.localrepository.__init__(self, ui, self._tempparent)
212 localrepo.localrepository.__init__(self, ui, self._tempparent)
213 self.ui.setconfig('phases', 'publish', False, 'bundlerepo')
213 self.ui.setconfig('phases', 'publish', False, 'bundlerepo')
214
214
215 if path:
215 if path:
216 self._url = 'bundle:' + util.expandpath(path) + '+' + bundlename
216 self._url = 'bundle:' + util.expandpath(path) + '+' + bundlename
217 else:
217 else:
218 self._url = 'bundle:' + bundlename
218 self._url = 'bundle:' + bundlename
219
219
220 self.tempfile = None
220 self.tempfile = None
221 f = util.posixfile(bundlename, "rb")
221 f = util.posixfile(bundlename, "rb")
222 self.bundle = exchange.readbundle(ui, f, bundlename)
222 self.bundle = exchange.readbundle(ui, f, bundlename)
223 if self.bundle.compressed():
223 if self.bundle.compressed():
224 fdtemp, temp = self.vfs.mkstemp(prefix="hg-bundle-",
224 fdtemp, temp = self.vfs.mkstemp(prefix="hg-bundle-",
225 suffix=".hg10un")
225 suffix=".hg10un")
226 self.tempfile = temp
226 self.tempfile = temp
227 fptemp = os.fdopen(fdtemp, 'wb')
227 fptemp = os.fdopen(fdtemp, 'wb')
228
228
229 try:
229 try:
230 fptemp.write("HG10UN")
230 fptemp.write("HG10UN")
231 while True:
231 while True:
232 chunk = self.bundle.read(2**18)
232 chunk = self.bundle.read(2**18)
233 if not chunk:
233 if not chunk:
234 break
234 break
235 fptemp.write(chunk)
235 fptemp.write(chunk)
236 finally:
236 finally:
237 fptemp.close()
237 fptemp.close()
238
238
239 f = self.vfs.open(self.tempfile, mode="rb")
239 f = self.vfs.open(self.tempfile, mode="rb")
240 self.bundle = exchange.readbundle(ui, f, bundlename, self.vfs)
240 self.bundle = exchange.readbundle(ui, f, bundlename, self.vfs)
241
241
242 # dict with the mapping 'filename' -> position in the bundle
242 # dict with the mapping 'filename' -> position in the bundle
243 self.bundlefilespos = {}
243 self.bundlefilespos = {}
244
244
245 self.firstnewrev = self.changelog.repotiprev + 1
245 self.firstnewrev = self.changelog.repotiprev + 1
246 phases.retractboundary(self, None, phases.draft,
246 phases.retractboundary(self, None, phases.draft,
247 [ctx.node() for ctx in self[self.firstnewrev:]])
247 [ctx.node() for ctx in self[self.firstnewrev:]])
248
248
249 @localrepo.unfilteredpropertycache
249 @localrepo.unfilteredpropertycache
250 def _phasecache(self):
250 def _phasecache(self):
251 return bundlephasecache(self, self._phasedefaults)
251 return bundlephasecache(self, self._phasedefaults)
252
252
253 @localrepo.unfilteredpropertycache
253 @localrepo.unfilteredpropertycache
254 def changelog(self):
254 def changelog(self):
255 # consume the header if it exists
255 # consume the header if it exists
256 self.bundle.changelogheader()
256 self.bundle.changelogheader()
257 c = bundlechangelog(self.svfs, self.bundle)
257 c = bundlechangelog(self.svfs, self.bundle)
258 self.manstart = self.bundle.tell()
258 self.manstart = self.bundle.tell()
259 return c
259 return c
260
260
261 @localrepo.unfilteredpropertycache
261 @localrepo.unfilteredpropertycache
262 def manifest(self):
262 def manifest(self):
263 self.bundle.seek(self.manstart)
263 self.bundle.seek(self.manstart)
264 # consume the header if it exists
264 # consume the header if it exists
265 self.bundle.manifestheader()
265 self.bundle.manifestheader()
266 m = bundlemanifest(self.svfs, self.bundle, self.changelog.rev)
266 m = bundlemanifest(self.svfs, self.bundle, self.changelog.rev)
267 self.filestart = self.bundle.tell()
267 self.filestart = self.bundle.tell()
268 return m
268 return m
269
269
270 @localrepo.unfilteredpropertycache
270 @localrepo.unfilteredpropertycache
271 def manstart(self):
271 def manstart(self):
272 self.changelog
272 self.changelog
273 return self.manstart
273 return self.manstart
274
274
275 @localrepo.unfilteredpropertycache
275 @localrepo.unfilteredpropertycache
276 def filestart(self):
276 def filestart(self):
277 self.manifest
277 self.manifest
278 return self.filestart
278 return self.filestart
279
279
280 def url(self):
280 def url(self):
281 return self._url
281 return self._url
282
282
283 def file(self, f):
283 def file(self, f):
284 if not self.bundlefilespos:
284 if not self.bundlefilespos:
285 self.bundle.seek(self.filestart)
285 self.bundle.seek(self.filestart)
286 while True:
286 while True:
287 chunkdata = self.bundle.filelogheader()
287 chunkdata = self.bundle.filelogheader()
288 if not chunkdata:
288 if not chunkdata:
289 break
289 break
290 fname = chunkdata['filename']
290 fname = chunkdata['filename']
291 self.bundlefilespos[fname] = self.bundle.tell()
291 self.bundlefilespos[fname] = self.bundle.tell()
292 while True:
292 while True:
293 c = self.bundle.deltachunk(None)
293 c = self.bundle.deltachunk(None)
294 if not c:
294 if not c:
295 break
295 break
296
296
297 if f in self.bundlefilespos:
297 if f in self.bundlefilespos:
298 self.bundle.seek(self.bundlefilespos[f])
298 self.bundle.seek(self.bundlefilespos[f])
299 return bundlefilelog(self.svfs, f, self.bundle,
299 return bundlefilelog(self.svfs, f, self.bundle,
300 self.changelog.rev, self)
300 self.changelog.rev, self)
301 else:
301 else:
302 return filelog.filelog(self.svfs, f)
302 return filelog.filelog(self.svfs, f)
303
303
304 def close(self):
304 def close(self):
305 """Close assigned bundle file immediately."""
305 """Close assigned bundle file immediately."""
306 self.bundle.close()
306 self.bundle.close()
307 if self.tempfile is not None:
307 if self.tempfile is not None:
308 self.vfs.unlink(self.tempfile)
308 self.vfs.unlink(self.tempfile)
309 if self._tempparent:
309 if self._tempparent:
310 shutil.rmtree(self._tempparent, True)
310 shutil.rmtree(self._tempparent, True)
311
311
312 def cancopy(self):
312 def cancopy(self):
313 return False
313 return False
314
314
315 def peer(self):
315 def peer(self):
316 return bundlepeer(self)
316 return bundlepeer(self)
317
317
318 def getcwd(self):
318 def getcwd(self):
319 return os.getcwd() # always outside the repo
319 return os.getcwd() # always outside the repo
320
320
321
321
322 def instance(ui, path, create):
322 def instance(ui, path, create):
323 if create:
323 if create:
324 raise util.Abort(_('cannot create new bundle repository'))
324 raise util.Abort(_('cannot create new bundle repository'))
325 parentpath = ui.config("bundle", "mainreporoot", "")
325 parentpath = ui.config("bundle", "mainreporoot", "")
326 if not parentpath:
326 if not parentpath:
327 # try to find the correct path to the working directory repo
327 # try to find the correct path to the working directory repo
328 parentpath = cmdutil.findrepo(os.getcwd())
328 parentpath = cmdutil.findrepo(os.getcwd())
329 if parentpath is None:
329 if parentpath is None:
330 parentpath = ''
330 parentpath = ''
331 if parentpath:
331 if parentpath:
332 # Try to make the full path relative so we get a nice, short URL.
332 # Try to make the full path relative so we get a nice, short URL.
333 # In particular, we don't want temp dir names in test outputs.
333 # In particular, we don't want temp dir names in test outputs.
334 cwd = os.getcwd()
334 cwd = os.getcwd()
335 if parentpath == cwd:
335 if parentpath == cwd:
336 parentpath = ''
336 parentpath = ''
337 else:
337 else:
338 cwd = os.path.join(cwd,'')
338 cwd = os.path.join(cwd,'')
339 if parentpath.startswith(cwd):
339 if parentpath.startswith(cwd):
340 parentpath = parentpath[len(cwd):]
340 parentpath = parentpath[len(cwd):]
341 u = util.url(path)
341 u = util.url(path)
342 path = u.localpath()
342 path = u.localpath()
343 if u.scheme == 'bundle':
343 if u.scheme == 'bundle':
344 s = path.split("+", 1)
344 s = path.split("+", 1)
345 if len(s) == 1:
345 if len(s) == 1:
346 repopath, bundlename = parentpath, s[0]
346 repopath, bundlename = parentpath, s[0]
347 else:
347 else:
348 repopath, bundlename = s
348 repopath, bundlename = s
349 else:
349 else:
350 repopath, bundlename = parentpath, path
350 repopath, bundlename = parentpath, path
351 return bundlerepository(ui, repopath, bundlename)
351 return bundlerepository(ui, repopath, bundlename)
352
352
353 class bundletransactionmanager(object):
353 class bundletransactionmanager(object):
354 def transaction(self):
354 def transaction(self):
355 return None
355 return None
356
356
357 def close(self):
357 def close(self):
358 raise NotImplementedError
358 raise NotImplementedError
359
359
360 def release(self):
360 def release(self):
361 raise NotImplementedError
361 raise NotImplementedError
362
362
363 def getremotechanges(ui, repo, other, onlyheads=None, bundlename=None,
363 def getremotechanges(ui, repo, other, onlyheads=None, bundlename=None,
364 force=False):
364 force=False):
365 '''obtains a bundle of changes incoming from other
365 '''obtains a bundle of changes incoming from other
366
366
367 "onlyheads" restricts the returned changes to those reachable from the
367 "onlyheads" restricts the returned changes to those reachable from the
368 specified heads.
368 specified heads.
369 "bundlename", if given, stores the bundle to this file path permanently;
369 "bundlename", if given, stores the bundle to this file path permanently;
370 otherwise it's stored to a temp file and gets deleted again when you call
370 otherwise it's stored to a temp file and gets deleted again when you call
371 the returned "cleanupfn".
371 the returned "cleanupfn".
372 "force" indicates whether to proceed on unrelated repos.
372 "force" indicates whether to proceed on unrelated repos.
373
373
374 Returns a tuple (local, csets, cleanupfn):
374 Returns a tuple (local, csets, cleanupfn):
375
375
376 "local" is a local repo from which to obtain the actual incoming
376 "local" is a local repo from which to obtain the actual incoming
377 changesets; it is a bundlerepo for the obtained bundle when the
377 changesets; it is a bundlerepo for the obtained bundle when the
378 original "other" is remote.
378 original "other" is remote.
379 "csets" lists the incoming changeset node ids.
379 "csets" lists the incoming changeset node ids.
380 "cleanupfn" must be called without arguments when you're done processing
380 "cleanupfn" must be called without arguments when you're done processing
381 the changes; it closes both the original "other" and the one returned
381 the changes; it closes both the original "other" and the one returned
382 here.
382 here.
383 '''
383 '''
384 tmp = discovery.findcommonincoming(repo, other, heads=onlyheads,
384 tmp = discovery.findcommonincoming(repo, other, heads=onlyheads,
385 force=force)
385 force=force)
386 common, incoming, rheads = tmp
386 common, incoming, rheads = tmp
387 if not incoming:
387 if not incoming:
388 try:
388 try:
389 if bundlename:
389 if bundlename:
390 os.unlink(bundlename)
390 os.unlink(bundlename)
391 except OSError:
391 except OSError:
392 pass
392 pass
393 return repo, [], other.close
393 return repo, [], other.close
394
394
395 commonset = set(common)
395 commonset = set(common)
396 rheads = [x for x in rheads if x not in commonset]
396 rheads = [x for x in rheads if x not in commonset]
397
397
398 bundle = None
398 bundle = None
399 bundlerepo = None
399 bundlerepo = None
400 localrepo = other.local()
400 localrepo = other.local()
401 if bundlename or not localrepo:
401 if bundlename or not localrepo:
402 # create a bundle (uncompressed if other repo is not local)
402 # create a bundle (uncompressed if other repo is not local)
403
403
404 if other.capable('getbundle'):
404 if other.capable('getbundle'):
405 cg = other.getbundle('incoming', common=common, heads=rheads)
405 cg = other.getbundle('incoming', common=common, heads=rheads)
406 elif onlyheads is None and not other.capable('changegroupsubset'):
406 elif onlyheads is None and not other.capable('changegroupsubset'):
407 # compat with older servers when pulling all remote heads
407 # compat with older servers when pulling all remote heads
408 cg = other.changegroup(incoming, "incoming")
408 cg = other.changegroup(incoming, "incoming")
409 rheads = None
409 rheads = None
410 else:
410 else:
411 cg = other.changegroupsubset(incoming, rheads, 'incoming')
411 cg = other.changegroupsubset(incoming, rheads, 'incoming')
412 bundletype = localrepo and "HG10BZ" or "HG10UN"
412 bundletype = localrepo and "HG10BZ" or "HG10UN"
413 fname = bundle = changegroup.writebundle(cg, bundlename, bundletype)
413 fname = bundle = changegroup.writebundle(ui, cg, bundlename, bundletype)
414 # keep written bundle?
414 # keep written bundle?
415 if bundlename:
415 if bundlename:
416 bundle = None
416 bundle = None
417 if not localrepo:
417 if not localrepo:
418 # use the created uncompressed bundlerepo
418 # use the created uncompressed bundlerepo
419 localrepo = bundlerepo = bundlerepository(repo.baseui, repo.root,
419 localrepo = bundlerepo = bundlerepository(repo.baseui, repo.root,
420 fname)
420 fname)
421 # this repo contains local and other now, so filter out local again
421 # this repo contains local and other now, so filter out local again
422 common = repo.heads()
422 common = repo.heads()
423 if localrepo:
423 if localrepo:
424 # Part of common may be remotely filtered
424 # Part of common may be remotely filtered
425 # So use an unfiltered version
425 # So use an unfiltered version
426 # The discovery process probably need cleanup to avoid that
426 # The discovery process probably need cleanup to avoid that
427 localrepo = localrepo.unfiltered()
427 localrepo = localrepo.unfiltered()
428
428
429 csets = localrepo.changelog.findmissing(common, rheads)
429 csets = localrepo.changelog.findmissing(common, rheads)
430
430
431 if bundlerepo:
431 if bundlerepo:
432 reponodes = [ctx.node() for ctx in bundlerepo[bundlerepo.firstnewrev:]]
432 reponodes = [ctx.node() for ctx in bundlerepo[bundlerepo.firstnewrev:]]
433 remotephases = other.listkeys('phases')
433 remotephases = other.listkeys('phases')
434
434
435 pullop = exchange.pulloperation(bundlerepo, other, heads=reponodes)
435 pullop = exchange.pulloperation(bundlerepo, other, heads=reponodes)
436 pullop.trmanager = bundletransactionmanager()
436 pullop.trmanager = bundletransactionmanager()
437 exchange._pullapplyphases(pullop, remotephases)
437 exchange._pullapplyphases(pullop, remotephases)
438
438
439 def cleanup():
439 def cleanup():
440 if bundlerepo:
440 if bundlerepo:
441 bundlerepo.close()
441 bundlerepo.close()
442 if bundle:
442 if bundle:
443 os.unlink(bundle)
443 os.unlink(bundle)
444 other.close()
444 other.close()
445
445
446 return (localrepo, csets, cleanup)
446 return (localrepo, csets, cleanup)
@@ -1,869 +1,869 b''
1 # changegroup.py - Mercurial changegroup manipulation functions
1 # changegroup.py - Mercurial changegroup manipulation functions
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import weakref
8 import weakref
9 from i18n import _
9 from i18n import _
10 from node import nullrev, nullid, hex, short
10 from node import nullrev, nullid, hex, short
11 import mdiff, util, dagutil
11 import mdiff, util, dagutil
12 import struct, os, bz2, zlib, tempfile
12 import struct, os, bz2, zlib, tempfile
13 import discovery, error, phases, branchmap
13 import discovery, error, phases, branchmap
14
14
15 _CHANGEGROUPV1_DELTA_HEADER = "20s20s20s20s"
15 _CHANGEGROUPV1_DELTA_HEADER = "20s20s20s20s"
16 _CHANGEGROUPV2_DELTA_HEADER = "20s20s20s20s20s"
16 _CHANGEGROUPV2_DELTA_HEADER = "20s20s20s20s20s"
17
17
18 def readexactly(stream, n):
18 def readexactly(stream, n):
19 '''read n bytes from stream.read and abort if less was available'''
19 '''read n bytes from stream.read and abort if less was available'''
20 s = stream.read(n)
20 s = stream.read(n)
21 if len(s) < n:
21 if len(s) < n:
22 raise util.Abort(_("stream ended unexpectedly"
22 raise util.Abort(_("stream ended unexpectedly"
23 " (got %d bytes, expected %d)")
23 " (got %d bytes, expected %d)")
24 % (len(s), n))
24 % (len(s), n))
25 return s
25 return s
26
26
27 def getchunk(stream):
27 def getchunk(stream):
28 """return the next chunk from stream as a string"""
28 """return the next chunk from stream as a string"""
29 d = readexactly(stream, 4)
29 d = readexactly(stream, 4)
30 l = struct.unpack(">l", d)[0]
30 l = struct.unpack(">l", d)[0]
31 if l <= 4:
31 if l <= 4:
32 if l:
32 if l:
33 raise util.Abort(_("invalid chunk length %d") % l)
33 raise util.Abort(_("invalid chunk length %d") % l)
34 return ""
34 return ""
35 return readexactly(stream, l - 4)
35 return readexactly(stream, l - 4)
36
36
37 def chunkheader(length):
37 def chunkheader(length):
38 """return a changegroup chunk header (string)"""
38 """return a changegroup chunk header (string)"""
39 return struct.pack(">l", length + 4)
39 return struct.pack(">l", length + 4)
40
40
41 def closechunk():
41 def closechunk():
42 """return a changegroup chunk header (string) for a zero-length chunk"""
42 """return a changegroup chunk header (string) for a zero-length chunk"""
43 return struct.pack(">l", 0)
43 return struct.pack(">l", 0)
44
44
45 def combineresults(results):
45 def combineresults(results):
46 """logic to combine 0 or more addchangegroup results into one"""
46 """logic to combine 0 or more addchangegroup results into one"""
47 changedheads = 0
47 changedheads = 0
48 result = 1
48 result = 1
49 for ret in results:
49 for ret in results:
50 # If any changegroup result is 0, return 0
50 # If any changegroup result is 0, return 0
51 if ret == 0:
51 if ret == 0:
52 result = 0
52 result = 0
53 break
53 break
54 if ret < -1:
54 if ret < -1:
55 changedheads += ret + 1
55 changedheads += ret + 1
56 elif ret > 1:
56 elif ret > 1:
57 changedheads += ret - 1
57 changedheads += ret - 1
58 if changedheads > 0:
58 if changedheads > 0:
59 result = 1 + changedheads
59 result = 1 + changedheads
60 elif changedheads < 0:
60 elif changedheads < 0:
61 result = -1 + changedheads
61 result = -1 + changedheads
62 return result
62 return result
63
63
64 class nocompress(object):
64 class nocompress(object):
65 def compress(self, x):
65 def compress(self, x):
66 return x
66 return x
67 def flush(self):
67 def flush(self):
68 return ""
68 return ""
69
69
70 bundletypes = {
70 bundletypes = {
71 "": ("", nocompress), # only when using unbundle on ssh and old http servers
71 "": ("", nocompress), # only when using unbundle on ssh and old http servers
72 # since the unification ssh accepts a header but there
72 # since the unification ssh accepts a header but there
73 # is no capability signaling it.
73 # is no capability signaling it.
74 "HG10UN": ("HG10UN", nocompress),
74 "HG10UN": ("HG10UN", nocompress),
75 "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()),
75 "HG10BZ": ("HG10", lambda: bz2.BZ2Compressor()),
76 "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
76 "HG10GZ": ("HG10GZ", lambda: zlib.compressobj()),
77 }
77 }
78
78
79 # hgweb uses this list to communicate its preferred type
79 # hgweb uses this list to communicate its preferred type
80 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
80 bundlepriority = ['HG10GZ', 'HG10BZ', 'HG10UN']
81
81
82 def writebundle(cg, filename, bundletype, vfs=None):
82 def writebundle(ui, cg, filename, bundletype, vfs=None):
83 """Write a bundle file and return its filename.
83 """Write a bundle file and return its filename.
84
84
85 Existing files will not be overwritten.
85 Existing files will not be overwritten.
86 If no filename is specified, a temporary file is created.
86 If no filename is specified, a temporary file is created.
87 bz2 compression can be turned off.
87 bz2 compression can be turned off.
88 The bundle file will be deleted in case of errors.
88 The bundle file will be deleted in case of errors.
89 """
89 """
90
90
91 fh = None
91 fh = None
92 cleanup = None
92 cleanup = None
93 try:
93 try:
94 if filename:
94 if filename:
95 if vfs:
95 if vfs:
96 fh = vfs.open(filename, "wb")
96 fh = vfs.open(filename, "wb")
97 else:
97 else:
98 fh = open(filename, "wb")
98 fh = open(filename, "wb")
99 else:
99 else:
100 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
100 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
101 fh = os.fdopen(fd, "wb")
101 fh = os.fdopen(fd, "wb")
102 cleanup = filename
102 cleanup = filename
103
103
104 header, compressor = bundletypes[bundletype]
104 header, compressor = bundletypes[bundletype]
105 fh.write(header)
105 fh.write(header)
106 z = compressor()
106 z = compressor()
107
107
108 # parse the changegroup data, otherwise we will block
108 # parse the changegroup data, otherwise we will block
109 # in case of sshrepo because we don't know the end of the stream
109 # in case of sshrepo because we don't know the end of the stream
110
110
111 # an empty chunkgroup is the end of the changegroup
111 # an empty chunkgroup is the end of the changegroup
112 # a changegroup has at least 2 chunkgroups (changelog and manifest).
112 # a changegroup has at least 2 chunkgroups (changelog and manifest).
113 # after that, an empty chunkgroup is the end of the changegroup
113 # after that, an empty chunkgroup is the end of the changegroup
114 for chunk in cg.getchunks():
114 for chunk in cg.getchunks():
115 fh.write(z.compress(chunk))
115 fh.write(z.compress(chunk))
116 fh.write(z.flush())
116 fh.write(z.flush())
117 cleanup = None
117 cleanup = None
118 return filename
118 return filename
119 finally:
119 finally:
120 if fh is not None:
120 if fh is not None:
121 fh.close()
121 fh.close()
122 if cleanup is not None:
122 if cleanup is not None:
123 if filename and vfs:
123 if filename and vfs:
124 vfs.unlink(cleanup)
124 vfs.unlink(cleanup)
125 else:
125 else:
126 os.unlink(cleanup)
126 os.unlink(cleanup)
127
127
128 def decompressor(fh, alg):
128 def decompressor(fh, alg):
129 if alg == 'UN':
129 if alg == 'UN':
130 return fh
130 return fh
131 elif alg == 'GZ':
131 elif alg == 'GZ':
132 def generator(f):
132 def generator(f):
133 zd = zlib.decompressobj()
133 zd = zlib.decompressobj()
134 for chunk in util.filechunkiter(f):
134 for chunk in util.filechunkiter(f):
135 yield zd.decompress(chunk)
135 yield zd.decompress(chunk)
136 elif alg == 'BZ':
136 elif alg == 'BZ':
137 def generator(f):
137 def generator(f):
138 zd = bz2.BZ2Decompressor()
138 zd = bz2.BZ2Decompressor()
139 zd.decompress("BZ")
139 zd.decompress("BZ")
140 for chunk in util.filechunkiter(f, 4096):
140 for chunk in util.filechunkiter(f, 4096):
141 yield zd.decompress(chunk)
141 yield zd.decompress(chunk)
142 else:
142 else:
143 raise util.Abort("unknown bundle compression '%s'" % alg)
143 raise util.Abort("unknown bundle compression '%s'" % alg)
144 return util.chunkbuffer(generator(fh))
144 return util.chunkbuffer(generator(fh))
145
145
146 class cg1unpacker(object):
146 class cg1unpacker(object):
147 deltaheader = _CHANGEGROUPV1_DELTA_HEADER
147 deltaheader = _CHANGEGROUPV1_DELTA_HEADER
148 deltaheadersize = struct.calcsize(deltaheader)
148 deltaheadersize = struct.calcsize(deltaheader)
149 def __init__(self, fh, alg):
149 def __init__(self, fh, alg):
150 self._stream = decompressor(fh, alg)
150 self._stream = decompressor(fh, alg)
151 self._type = alg
151 self._type = alg
152 self.callback = None
152 self.callback = None
153 def compressed(self):
153 def compressed(self):
154 return self._type != 'UN'
154 return self._type != 'UN'
155 def read(self, l):
155 def read(self, l):
156 return self._stream.read(l)
156 return self._stream.read(l)
157 def seek(self, pos):
157 def seek(self, pos):
158 return self._stream.seek(pos)
158 return self._stream.seek(pos)
159 def tell(self):
159 def tell(self):
160 return self._stream.tell()
160 return self._stream.tell()
161 def close(self):
161 def close(self):
162 return self._stream.close()
162 return self._stream.close()
163
163
164 def chunklength(self):
164 def chunklength(self):
165 d = readexactly(self._stream, 4)
165 d = readexactly(self._stream, 4)
166 l = struct.unpack(">l", d)[0]
166 l = struct.unpack(">l", d)[0]
167 if l <= 4:
167 if l <= 4:
168 if l:
168 if l:
169 raise util.Abort(_("invalid chunk length %d") % l)
169 raise util.Abort(_("invalid chunk length %d") % l)
170 return 0
170 return 0
171 if self.callback:
171 if self.callback:
172 self.callback()
172 self.callback()
173 return l - 4
173 return l - 4
174
174
175 def changelogheader(self):
175 def changelogheader(self):
176 """v10 does not have a changelog header chunk"""
176 """v10 does not have a changelog header chunk"""
177 return {}
177 return {}
178
178
179 def manifestheader(self):
179 def manifestheader(self):
180 """v10 does not have a manifest header chunk"""
180 """v10 does not have a manifest header chunk"""
181 return {}
181 return {}
182
182
183 def filelogheader(self):
183 def filelogheader(self):
184 """return the header of the filelogs chunk, v10 only has the filename"""
184 """return the header of the filelogs chunk, v10 only has the filename"""
185 l = self.chunklength()
185 l = self.chunklength()
186 if not l:
186 if not l:
187 return {}
187 return {}
188 fname = readexactly(self._stream, l)
188 fname = readexactly(self._stream, l)
189 return {'filename': fname}
189 return {'filename': fname}
190
190
191 def _deltaheader(self, headertuple, prevnode):
191 def _deltaheader(self, headertuple, prevnode):
192 node, p1, p2, cs = headertuple
192 node, p1, p2, cs = headertuple
193 if prevnode is None:
193 if prevnode is None:
194 deltabase = p1
194 deltabase = p1
195 else:
195 else:
196 deltabase = prevnode
196 deltabase = prevnode
197 return node, p1, p2, deltabase, cs
197 return node, p1, p2, deltabase, cs
198
198
199 def deltachunk(self, prevnode):
199 def deltachunk(self, prevnode):
200 l = self.chunklength()
200 l = self.chunklength()
201 if not l:
201 if not l:
202 return {}
202 return {}
203 headerdata = readexactly(self._stream, self.deltaheadersize)
203 headerdata = readexactly(self._stream, self.deltaheadersize)
204 header = struct.unpack(self.deltaheader, headerdata)
204 header = struct.unpack(self.deltaheader, headerdata)
205 delta = readexactly(self._stream, l - self.deltaheadersize)
205 delta = readexactly(self._stream, l - self.deltaheadersize)
206 node, p1, p2, deltabase, cs = self._deltaheader(header, prevnode)
206 node, p1, p2, deltabase, cs = self._deltaheader(header, prevnode)
207 return {'node': node, 'p1': p1, 'p2': p2, 'cs': cs,
207 return {'node': node, 'p1': p1, 'p2': p2, 'cs': cs,
208 'deltabase': deltabase, 'delta': delta}
208 'deltabase': deltabase, 'delta': delta}
209
209
210 def getchunks(self):
210 def getchunks(self):
211 """returns all the chunks contains in the bundle
211 """returns all the chunks contains in the bundle
212
212
213 Used when you need to forward the binary stream to a file or another
213 Used when you need to forward the binary stream to a file or another
214 network API. To do so, it parse the changegroup data, otherwise it will
214 network API. To do so, it parse the changegroup data, otherwise it will
215 block in case of sshrepo because it don't know the end of the stream.
215 block in case of sshrepo because it don't know the end of the stream.
216 """
216 """
217 # an empty chunkgroup is the end of the changegroup
217 # an empty chunkgroup is the end of the changegroup
218 # a changegroup has at least 2 chunkgroups (changelog and manifest).
218 # a changegroup has at least 2 chunkgroups (changelog and manifest).
219 # after that, an empty chunkgroup is the end of the changegroup
219 # after that, an empty chunkgroup is the end of the changegroup
220 empty = False
220 empty = False
221 count = 0
221 count = 0
222 while not empty or count <= 2:
222 while not empty or count <= 2:
223 empty = True
223 empty = True
224 count += 1
224 count += 1
225 while True:
225 while True:
226 chunk = getchunk(self)
226 chunk = getchunk(self)
227 if not chunk:
227 if not chunk:
228 break
228 break
229 empty = False
229 empty = False
230 yield chunkheader(len(chunk))
230 yield chunkheader(len(chunk))
231 pos = 0
231 pos = 0
232 while pos < len(chunk):
232 while pos < len(chunk):
233 next = pos + 2**20
233 next = pos + 2**20
234 yield chunk[pos:next]
234 yield chunk[pos:next]
235 pos = next
235 pos = next
236 yield closechunk()
236 yield closechunk()
237
237
238 class cg2unpacker(cg1unpacker):
238 class cg2unpacker(cg1unpacker):
239 deltaheader = _CHANGEGROUPV2_DELTA_HEADER
239 deltaheader = _CHANGEGROUPV2_DELTA_HEADER
240 deltaheadersize = struct.calcsize(deltaheader)
240 deltaheadersize = struct.calcsize(deltaheader)
241
241
242 def _deltaheader(self, headertuple, prevnode):
242 def _deltaheader(self, headertuple, prevnode):
243 node, p1, p2, deltabase, cs = headertuple
243 node, p1, p2, deltabase, cs = headertuple
244 return node, p1, p2, deltabase, cs
244 return node, p1, p2, deltabase, cs
245
245
246 class headerlessfixup(object):
246 class headerlessfixup(object):
247 def __init__(self, fh, h):
247 def __init__(self, fh, h):
248 self._h = h
248 self._h = h
249 self._fh = fh
249 self._fh = fh
250 def read(self, n):
250 def read(self, n):
251 if self._h:
251 if self._h:
252 d, self._h = self._h[:n], self._h[n:]
252 d, self._h = self._h[:n], self._h[n:]
253 if len(d) < n:
253 if len(d) < n:
254 d += readexactly(self._fh, n - len(d))
254 d += readexactly(self._fh, n - len(d))
255 return d
255 return d
256 return readexactly(self._fh, n)
256 return readexactly(self._fh, n)
257
257
258 class cg1packer(object):
258 class cg1packer(object):
259 deltaheader = _CHANGEGROUPV1_DELTA_HEADER
259 deltaheader = _CHANGEGROUPV1_DELTA_HEADER
260 def __init__(self, repo, bundlecaps=None):
260 def __init__(self, repo, bundlecaps=None):
261 """Given a source repo, construct a bundler.
261 """Given a source repo, construct a bundler.
262
262
263 bundlecaps is optional and can be used to specify the set of
263 bundlecaps is optional and can be used to specify the set of
264 capabilities which can be used to build the bundle.
264 capabilities which can be used to build the bundle.
265 """
265 """
266 # Set of capabilities we can use to build the bundle.
266 # Set of capabilities we can use to build the bundle.
267 if bundlecaps is None:
267 if bundlecaps is None:
268 bundlecaps = set()
268 bundlecaps = set()
269 self._bundlecaps = bundlecaps
269 self._bundlecaps = bundlecaps
270 self._changelog = repo.changelog
270 self._changelog = repo.changelog
271 self._manifest = repo.manifest
271 self._manifest = repo.manifest
272 reorder = repo.ui.config('bundle', 'reorder', 'auto')
272 reorder = repo.ui.config('bundle', 'reorder', 'auto')
273 if reorder == 'auto':
273 if reorder == 'auto':
274 reorder = None
274 reorder = None
275 else:
275 else:
276 reorder = util.parsebool(reorder)
276 reorder = util.parsebool(reorder)
277 self._repo = repo
277 self._repo = repo
278 self._reorder = reorder
278 self._reorder = reorder
279 self._progress = repo.ui.progress
279 self._progress = repo.ui.progress
280 if self._repo.ui.verbose and not self._repo.ui.debugflag:
280 if self._repo.ui.verbose and not self._repo.ui.debugflag:
281 self._verbosenote = self._repo.ui.note
281 self._verbosenote = self._repo.ui.note
282 else:
282 else:
283 self._verbosenote = lambda s: None
283 self._verbosenote = lambda s: None
284
284
285 def close(self):
285 def close(self):
286 return closechunk()
286 return closechunk()
287
287
288 def fileheader(self, fname):
288 def fileheader(self, fname):
289 return chunkheader(len(fname)) + fname
289 return chunkheader(len(fname)) + fname
290
290
291 def group(self, nodelist, revlog, lookup, units=None, reorder=None):
291 def group(self, nodelist, revlog, lookup, units=None, reorder=None):
292 """Calculate a delta group, yielding a sequence of changegroup chunks
292 """Calculate a delta group, yielding a sequence of changegroup chunks
293 (strings).
293 (strings).
294
294
295 Given a list of changeset revs, return a set of deltas and
295 Given a list of changeset revs, return a set of deltas and
296 metadata corresponding to nodes. The first delta is
296 metadata corresponding to nodes. The first delta is
297 first parent(nodelist[0]) -> nodelist[0], the receiver is
297 first parent(nodelist[0]) -> nodelist[0], the receiver is
298 guaranteed to have this parent as it has all history before
298 guaranteed to have this parent as it has all history before
299 these changesets. In the case firstparent is nullrev the
299 these changesets. In the case firstparent is nullrev the
300 changegroup starts with a full revision.
300 changegroup starts with a full revision.
301
301
302 If units is not None, progress detail will be generated, units specifies
302 If units is not None, progress detail will be generated, units specifies
303 the type of revlog that is touched (changelog, manifest, etc.).
303 the type of revlog that is touched (changelog, manifest, etc.).
304 """
304 """
305 # if we don't have any revisions touched by these changesets, bail
305 # if we don't have any revisions touched by these changesets, bail
306 if len(nodelist) == 0:
306 if len(nodelist) == 0:
307 yield self.close()
307 yield self.close()
308 return
308 return
309
309
310 # for generaldelta revlogs, we linearize the revs; this will both be
310 # for generaldelta revlogs, we linearize the revs; this will both be
311 # much quicker and generate a much smaller bundle
311 # much quicker and generate a much smaller bundle
312 if (revlog._generaldelta and reorder is not False) or reorder:
312 if (revlog._generaldelta and reorder is not False) or reorder:
313 dag = dagutil.revlogdag(revlog)
313 dag = dagutil.revlogdag(revlog)
314 revs = set(revlog.rev(n) for n in nodelist)
314 revs = set(revlog.rev(n) for n in nodelist)
315 revs = dag.linearize(revs)
315 revs = dag.linearize(revs)
316 else:
316 else:
317 revs = sorted([revlog.rev(n) for n in nodelist])
317 revs = sorted([revlog.rev(n) for n in nodelist])
318
318
319 # add the parent of the first rev
319 # add the parent of the first rev
320 p = revlog.parentrevs(revs[0])[0]
320 p = revlog.parentrevs(revs[0])[0]
321 revs.insert(0, p)
321 revs.insert(0, p)
322
322
323 # build deltas
323 # build deltas
324 total = len(revs) - 1
324 total = len(revs) - 1
325 msgbundling = _('bundling')
325 msgbundling = _('bundling')
326 for r in xrange(len(revs) - 1):
326 for r in xrange(len(revs) - 1):
327 if units is not None:
327 if units is not None:
328 self._progress(msgbundling, r + 1, unit=units, total=total)
328 self._progress(msgbundling, r + 1, unit=units, total=total)
329 prev, curr = revs[r], revs[r + 1]
329 prev, curr = revs[r], revs[r + 1]
330 linknode = lookup(revlog.node(curr))
330 linknode = lookup(revlog.node(curr))
331 for c in self.revchunk(revlog, curr, prev, linknode):
331 for c in self.revchunk(revlog, curr, prev, linknode):
332 yield c
332 yield c
333
333
334 yield self.close()
334 yield self.close()
335
335
336 # filter any nodes that claim to be part of the known set
336 # filter any nodes that claim to be part of the known set
337 def prune(self, revlog, missing, commonrevs, source):
337 def prune(self, revlog, missing, commonrevs, source):
338 rr, rl = revlog.rev, revlog.linkrev
338 rr, rl = revlog.rev, revlog.linkrev
339 return [n for n in missing if rl(rr(n)) not in commonrevs]
339 return [n for n in missing if rl(rr(n)) not in commonrevs]
340
340
341 def generate(self, commonrevs, clnodes, fastpathlinkrev, source):
341 def generate(self, commonrevs, clnodes, fastpathlinkrev, source):
342 '''yield a sequence of changegroup chunks (strings)'''
342 '''yield a sequence of changegroup chunks (strings)'''
343 repo = self._repo
343 repo = self._repo
344 cl = self._changelog
344 cl = self._changelog
345 mf = self._manifest
345 mf = self._manifest
346 reorder = self._reorder
346 reorder = self._reorder
347 progress = self._progress
347 progress = self._progress
348
348
349 # for progress output
349 # for progress output
350 msgbundling = _('bundling')
350 msgbundling = _('bundling')
351
351
352 clrevorder = {}
352 clrevorder = {}
353 mfs = {} # needed manifests
353 mfs = {} # needed manifests
354 fnodes = {} # needed file nodes
354 fnodes = {} # needed file nodes
355 changedfiles = set()
355 changedfiles = set()
356
356
357 # Callback for the changelog, used to collect changed files and manifest
357 # Callback for the changelog, used to collect changed files and manifest
358 # nodes.
358 # nodes.
359 # Returns the linkrev node (identity in the changelog case).
359 # Returns the linkrev node (identity in the changelog case).
360 def lookupcl(x):
360 def lookupcl(x):
361 c = cl.read(x)
361 c = cl.read(x)
362 clrevorder[x] = len(clrevorder)
362 clrevorder[x] = len(clrevorder)
363 changedfiles.update(c[3])
363 changedfiles.update(c[3])
364 # record the first changeset introducing this manifest version
364 # record the first changeset introducing this manifest version
365 mfs.setdefault(c[0], x)
365 mfs.setdefault(c[0], x)
366 return x
366 return x
367
367
368 self._verbosenote(_('uncompressed size of bundle content:\n'))
368 self._verbosenote(_('uncompressed size of bundle content:\n'))
369 size = 0
369 size = 0
370 for chunk in self.group(clnodes, cl, lookupcl, units=_('changesets'),
370 for chunk in self.group(clnodes, cl, lookupcl, units=_('changesets'),
371 reorder=reorder):
371 reorder=reorder):
372 size += len(chunk)
372 size += len(chunk)
373 yield chunk
373 yield chunk
374 self._verbosenote(_('%8.i (changelog)\n') % size)
374 self._verbosenote(_('%8.i (changelog)\n') % size)
375 progress(msgbundling, None)
375 progress(msgbundling, None)
376
376
377 # Callback for the manifest, used to collect linkrevs for filelog
377 # Callback for the manifest, used to collect linkrevs for filelog
378 # revisions.
378 # revisions.
379 # Returns the linkrev node (collected in lookupcl).
379 # Returns the linkrev node (collected in lookupcl).
380 def lookupmf(x):
380 def lookupmf(x):
381 clnode = mfs[x]
381 clnode = mfs[x]
382 if not fastpathlinkrev or reorder:
382 if not fastpathlinkrev or reorder:
383 mdata = mf.readfast(x)
383 mdata = mf.readfast(x)
384 for f, n in mdata.iteritems():
384 for f, n in mdata.iteritems():
385 if f in changedfiles:
385 if f in changedfiles:
386 # record the first changeset introducing this filelog
386 # record the first changeset introducing this filelog
387 # version
387 # version
388 fclnodes = fnodes.setdefault(f, {})
388 fclnodes = fnodes.setdefault(f, {})
389 fclnode = fclnodes.setdefault(n, clnode)
389 fclnode = fclnodes.setdefault(n, clnode)
390 if clrevorder[clnode] < clrevorder[fclnode]:
390 if clrevorder[clnode] < clrevorder[fclnode]:
391 fclnodes[n] = clnode
391 fclnodes[n] = clnode
392 return clnode
392 return clnode
393
393
394 mfnodes = self.prune(mf, mfs, commonrevs, source)
394 mfnodes = self.prune(mf, mfs, commonrevs, source)
395 size = 0
395 size = 0
396 for chunk in self.group(mfnodes, mf, lookupmf, units=_('manifests'),
396 for chunk in self.group(mfnodes, mf, lookupmf, units=_('manifests'),
397 reorder=reorder):
397 reorder=reorder):
398 size += len(chunk)
398 size += len(chunk)
399 yield chunk
399 yield chunk
400 self._verbosenote(_('%8.i (manifests)\n') % size)
400 self._verbosenote(_('%8.i (manifests)\n') % size)
401 progress(msgbundling, None)
401 progress(msgbundling, None)
402
402
403 mfs.clear()
403 mfs.clear()
404 needed = set(cl.rev(x) for x in clnodes)
404 needed = set(cl.rev(x) for x in clnodes)
405
405
406 def linknodes(filerevlog, fname):
406 def linknodes(filerevlog, fname):
407 if fastpathlinkrev and not reorder:
407 if fastpathlinkrev and not reorder:
408 llr = filerevlog.linkrev
408 llr = filerevlog.linkrev
409 def genfilenodes():
409 def genfilenodes():
410 for r in filerevlog:
410 for r in filerevlog:
411 linkrev = llr(r)
411 linkrev = llr(r)
412 if linkrev in needed:
412 if linkrev in needed:
413 yield filerevlog.node(r), cl.node(linkrev)
413 yield filerevlog.node(r), cl.node(linkrev)
414 return dict(genfilenodes())
414 return dict(genfilenodes())
415 return fnodes.get(fname, {})
415 return fnodes.get(fname, {})
416
416
417 for chunk in self.generatefiles(changedfiles, linknodes, commonrevs,
417 for chunk in self.generatefiles(changedfiles, linknodes, commonrevs,
418 source):
418 source):
419 yield chunk
419 yield chunk
420
420
421 yield self.close()
421 yield self.close()
422 progress(msgbundling, None)
422 progress(msgbundling, None)
423
423
424 if clnodes:
424 if clnodes:
425 repo.hook('outgoing', node=hex(clnodes[0]), source=source)
425 repo.hook('outgoing', node=hex(clnodes[0]), source=source)
426
426
427 def generatefiles(self, changedfiles, linknodes, commonrevs, source):
427 def generatefiles(self, changedfiles, linknodes, commonrevs, source):
428 repo = self._repo
428 repo = self._repo
429 progress = self._progress
429 progress = self._progress
430 reorder = self._reorder
430 reorder = self._reorder
431 msgbundling = _('bundling')
431 msgbundling = _('bundling')
432
432
433 total = len(changedfiles)
433 total = len(changedfiles)
434 # for progress output
434 # for progress output
435 msgfiles = _('files')
435 msgfiles = _('files')
436 for i, fname in enumerate(sorted(changedfiles)):
436 for i, fname in enumerate(sorted(changedfiles)):
437 filerevlog = repo.file(fname)
437 filerevlog = repo.file(fname)
438 if not filerevlog:
438 if not filerevlog:
439 raise util.Abort(_("empty or missing revlog for %s") % fname)
439 raise util.Abort(_("empty or missing revlog for %s") % fname)
440
440
441 linkrevnodes = linknodes(filerevlog, fname)
441 linkrevnodes = linknodes(filerevlog, fname)
442 # Lookup for filenodes, we collected the linkrev nodes above in the
442 # Lookup for filenodes, we collected the linkrev nodes above in the
443 # fastpath case and with lookupmf in the slowpath case.
443 # fastpath case and with lookupmf in the slowpath case.
444 def lookupfilelog(x):
444 def lookupfilelog(x):
445 return linkrevnodes[x]
445 return linkrevnodes[x]
446
446
447 filenodes = self.prune(filerevlog, linkrevnodes, commonrevs, source)
447 filenodes = self.prune(filerevlog, linkrevnodes, commonrevs, source)
448 if filenodes:
448 if filenodes:
449 progress(msgbundling, i + 1, item=fname, unit=msgfiles,
449 progress(msgbundling, i + 1, item=fname, unit=msgfiles,
450 total=total)
450 total=total)
451 h = self.fileheader(fname)
451 h = self.fileheader(fname)
452 size = len(h)
452 size = len(h)
453 yield h
453 yield h
454 for chunk in self.group(filenodes, filerevlog, lookupfilelog,
454 for chunk in self.group(filenodes, filerevlog, lookupfilelog,
455 reorder=reorder):
455 reorder=reorder):
456 size += len(chunk)
456 size += len(chunk)
457 yield chunk
457 yield chunk
458 self._verbosenote(_('%8.i %s\n') % (size, fname))
458 self._verbosenote(_('%8.i %s\n') % (size, fname))
459
459
460 def deltaparent(self, revlog, rev, p1, p2, prev):
460 def deltaparent(self, revlog, rev, p1, p2, prev):
461 return prev
461 return prev
462
462
463 def revchunk(self, revlog, rev, prev, linknode):
463 def revchunk(self, revlog, rev, prev, linknode):
464 node = revlog.node(rev)
464 node = revlog.node(rev)
465 p1, p2 = revlog.parentrevs(rev)
465 p1, p2 = revlog.parentrevs(rev)
466 base = self.deltaparent(revlog, rev, p1, p2, prev)
466 base = self.deltaparent(revlog, rev, p1, p2, prev)
467
467
468 prefix = ''
468 prefix = ''
469 if base == nullrev:
469 if base == nullrev:
470 delta = revlog.revision(node)
470 delta = revlog.revision(node)
471 prefix = mdiff.trivialdiffheader(len(delta))
471 prefix = mdiff.trivialdiffheader(len(delta))
472 else:
472 else:
473 delta = revlog.revdiff(base, rev)
473 delta = revlog.revdiff(base, rev)
474 p1n, p2n = revlog.parents(node)
474 p1n, p2n = revlog.parents(node)
475 basenode = revlog.node(base)
475 basenode = revlog.node(base)
476 meta = self.builddeltaheader(node, p1n, p2n, basenode, linknode)
476 meta = self.builddeltaheader(node, p1n, p2n, basenode, linknode)
477 meta += prefix
477 meta += prefix
478 l = len(meta) + len(delta)
478 l = len(meta) + len(delta)
479 yield chunkheader(l)
479 yield chunkheader(l)
480 yield meta
480 yield meta
481 yield delta
481 yield delta
482 def builddeltaheader(self, node, p1n, p2n, basenode, linknode):
482 def builddeltaheader(self, node, p1n, p2n, basenode, linknode):
483 # do nothing with basenode, it is implicitly the previous one in HG10
483 # do nothing with basenode, it is implicitly the previous one in HG10
484 return struct.pack(self.deltaheader, node, p1n, p2n, linknode)
484 return struct.pack(self.deltaheader, node, p1n, p2n, linknode)
485
485
486 class cg2packer(cg1packer):
486 class cg2packer(cg1packer):
487
487
488 deltaheader = _CHANGEGROUPV2_DELTA_HEADER
488 deltaheader = _CHANGEGROUPV2_DELTA_HEADER
489
489
490 def group(self, nodelist, revlog, lookup, units=None, reorder=None):
490 def group(self, nodelist, revlog, lookup, units=None, reorder=None):
491 if (revlog._generaldelta and reorder is not True):
491 if (revlog._generaldelta and reorder is not True):
492 reorder = False
492 reorder = False
493 return super(cg2packer, self).group(nodelist, revlog, lookup,
493 return super(cg2packer, self).group(nodelist, revlog, lookup,
494 units=units, reorder=reorder)
494 units=units, reorder=reorder)
495
495
496 def deltaparent(self, revlog, rev, p1, p2, prev):
496 def deltaparent(self, revlog, rev, p1, p2, prev):
497 dp = revlog.deltaparent(rev)
497 dp = revlog.deltaparent(rev)
498 # avoid storing full revisions; pick prev in those cases
498 # avoid storing full revisions; pick prev in those cases
499 # also pick prev when we can't be sure remote has dp
499 # also pick prev when we can't be sure remote has dp
500 if dp == nullrev or (dp != p1 and dp != p2 and dp != prev):
500 if dp == nullrev or (dp != p1 and dp != p2 and dp != prev):
501 return prev
501 return prev
502 return dp
502 return dp
503
503
504 def builddeltaheader(self, node, p1n, p2n, basenode, linknode):
504 def builddeltaheader(self, node, p1n, p2n, basenode, linknode):
505 return struct.pack(self.deltaheader, node, p1n, p2n, basenode, linknode)
505 return struct.pack(self.deltaheader, node, p1n, p2n, basenode, linknode)
506
506
507 packermap = {'01': (cg1packer, cg1unpacker),
507 packermap = {'01': (cg1packer, cg1unpacker),
508 '02': (cg2packer, cg2unpacker)}
508 '02': (cg2packer, cg2unpacker)}
509
509
510 def _changegroupinfo(repo, nodes, source):
510 def _changegroupinfo(repo, nodes, source):
511 if repo.ui.verbose or source == 'bundle':
511 if repo.ui.verbose or source == 'bundle':
512 repo.ui.status(_("%d changesets found\n") % len(nodes))
512 repo.ui.status(_("%d changesets found\n") % len(nodes))
513 if repo.ui.debugflag:
513 if repo.ui.debugflag:
514 repo.ui.debug("list of changesets:\n")
514 repo.ui.debug("list of changesets:\n")
515 for node in nodes:
515 for node in nodes:
516 repo.ui.debug("%s\n" % hex(node))
516 repo.ui.debug("%s\n" % hex(node))
517
517
518 def getsubsetraw(repo, outgoing, bundler, source, fastpath=False):
518 def getsubsetraw(repo, outgoing, bundler, source, fastpath=False):
519 repo = repo.unfiltered()
519 repo = repo.unfiltered()
520 commonrevs = outgoing.common
520 commonrevs = outgoing.common
521 csets = outgoing.missing
521 csets = outgoing.missing
522 heads = outgoing.missingheads
522 heads = outgoing.missingheads
523 # We go through the fast path if we get told to, or if all (unfiltered
523 # We go through the fast path if we get told to, or if all (unfiltered
524 # heads have been requested (since we then know there all linkrevs will
524 # heads have been requested (since we then know there all linkrevs will
525 # be pulled by the client).
525 # be pulled by the client).
526 heads.sort()
526 heads.sort()
527 fastpathlinkrev = fastpath or (
527 fastpathlinkrev = fastpath or (
528 repo.filtername is None and heads == sorted(repo.heads()))
528 repo.filtername is None and heads == sorted(repo.heads()))
529
529
530 repo.hook('preoutgoing', throw=True, source=source)
530 repo.hook('preoutgoing', throw=True, source=source)
531 _changegroupinfo(repo, csets, source)
531 _changegroupinfo(repo, csets, source)
532 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
532 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
533
533
534 def getsubset(repo, outgoing, bundler, source, fastpath=False):
534 def getsubset(repo, outgoing, bundler, source, fastpath=False):
535 gengroup = getsubsetraw(repo, outgoing, bundler, source, fastpath)
535 gengroup = getsubsetraw(repo, outgoing, bundler, source, fastpath)
536 return cg1unpacker(util.chunkbuffer(gengroup), 'UN')
536 return cg1unpacker(util.chunkbuffer(gengroup), 'UN')
537
537
538 def changegroupsubset(repo, roots, heads, source):
538 def changegroupsubset(repo, roots, heads, source):
539 """Compute a changegroup consisting of all the nodes that are
539 """Compute a changegroup consisting of all the nodes that are
540 descendants of any of the roots and ancestors of any of the heads.
540 descendants of any of the roots and ancestors of any of the heads.
541 Return a chunkbuffer object whose read() method will return
541 Return a chunkbuffer object whose read() method will return
542 successive changegroup chunks.
542 successive changegroup chunks.
543
543
544 It is fairly complex as determining which filenodes and which
544 It is fairly complex as determining which filenodes and which
545 manifest nodes need to be included for the changeset to be complete
545 manifest nodes need to be included for the changeset to be complete
546 is non-trivial.
546 is non-trivial.
547
547
548 Another wrinkle is doing the reverse, figuring out which changeset in
548 Another wrinkle is doing the reverse, figuring out which changeset in
549 the changegroup a particular filenode or manifestnode belongs to.
549 the changegroup a particular filenode or manifestnode belongs to.
550 """
550 """
551 cl = repo.changelog
551 cl = repo.changelog
552 if not roots:
552 if not roots:
553 roots = [nullid]
553 roots = [nullid]
554 # TODO: remove call to nodesbetween.
554 # TODO: remove call to nodesbetween.
555 csets, roots, heads = cl.nodesbetween(roots, heads)
555 csets, roots, heads = cl.nodesbetween(roots, heads)
556 discbases = []
556 discbases = []
557 for n in roots:
557 for n in roots:
558 discbases.extend([p for p in cl.parents(n) if p != nullid])
558 discbases.extend([p for p in cl.parents(n) if p != nullid])
559 outgoing = discovery.outgoing(cl, discbases, heads)
559 outgoing = discovery.outgoing(cl, discbases, heads)
560 bundler = cg1packer(repo)
560 bundler = cg1packer(repo)
561 return getsubset(repo, outgoing, bundler, source)
561 return getsubset(repo, outgoing, bundler, source)
562
562
563 def getlocalchangegroupraw(repo, source, outgoing, bundlecaps=None,
563 def getlocalchangegroupraw(repo, source, outgoing, bundlecaps=None,
564 version='01'):
564 version='01'):
565 """Like getbundle, but taking a discovery.outgoing as an argument.
565 """Like getbundle, but taking a discovery.outgoing as an argument.
566
566
567 This is only implemented for local repos and reuses potentially
567 This is only implemented for local repos and reuses potentially
568 precomputed sets in outgoing. Returns a raw changegroup generator."""
568 precomputed sets in outgoing. Returns a raw changegroup generator."""
569 if not outgoing.missing:
569 if not outgoing.missing:
570 return None
570 return None
571 bundler = packermap[version][0](repo, bundlecaps)
571 bundler = packermap[version][0](repo, bundlecaps)
572 return getsubsetraw(repo, outgoing, bundler, source)
572 return getsubsetraw(repo, outgoing, bundler, source)
573
573
574 def getlocalchangegroup(repo, source, outgoing, bundlecaps=None):
574 def getlocalchangegroup(repo, source, outgoing, bundlecaps=None):
575 """Like getbundle, but taking a discovery.outgoing as an argument.
575 """Like getbundle, but taking a discovery.outgoing as an argument.
576
576
577 This is only implemented for local repos and reuses potentially
577 This is only implemented for local repos and reuses potentially
578 precomputed sets in outgoing."""
578 precomputed sets in outgoing."""
579 if not outgoing.missing:
579 if not outgoing.missing:
580 return None
580 return None
581 bundler = cg1packer(repo, bundlecaps)
581 bundler = cg1packer(repo, bundlecaps)
582 return getsubset(repo, outgoing, bundler, source)
582 return getsubset(repo, outgoing, bundler, source)
583
583
584 def _computeoutgoing(repo, heads, common):
584 def _computeoutgoing(repo, heads, common):
585 """Computes which revs are outgoing given a set of common
585 """Computes which revs are outgoing given a set of common
586 and a set of heads.
586 and a set of heads.
587
587
588 This is a separate function so extensions can have access to
588 This is a separate function so extensions can have access to
589 the logic.
589 the logic.
590
590
591 Returns a discovery.outgoing object.
591 Returns a discovery.outgoing object.
592 """
592 """
593 cl = repo.changelog
593 cl = repo.changelog
594 if common:
594 if common:
595 hasnode = cl.hasnode
595 hasnode = cl.hasnode
596 common = [n for n in common if hasnode(n)]
596 common = [n for n in common if hasnode(n)]
597 else:
597 else:
598 common = [nullid]
598 common = [nullid]
599 if not heads:
599 if not heads:
600 heads = cl.heads()
600 heads = cl.heads()
601 return discovery.outgoing(cl, common, heads)
601 return discovery.outgoing(cl, common, heads)
602
602
603 def getchangegroupraw(repo, source, heads=None, common=None, bundlecaps=None,
603 def getchangegroupraw(repo, source, heads=None, common=None, bundlecaps=None,
604 version='01'):
604 version='01'):
605 """Like changegroupsubset, but returns the set difference between the
605 """Like changegroupsubset, but returns the set difference between the
606 ancestors of heads and the ancestors common.
606 ancestors of heads and the ancestors common.
607
607
608 If heads is None, use the local heads. If common is None, use [nullid].
608 If heads is None, use the local heads. If common is None, use [nullid].
609
609
610 If version is None, use a version '1' changegroup.
610 If version is None, use a version '1' changegroup.
611
611
612 The nodes in common might not all be known locally due to the way the
612 The nodes in common might not all be known locally due to the way the
613 current discovery protocol works. Returns a raw changegroup generator.
613 current discovery protocol works. Returns a raw changegroup generator.
614 """
614 """
615 outgoing = _computeoutgoing(repo, heads, common)
615 outgoing = _computeoutgoing(repo, heads, common)
616 return getlocalchangegroupraw(repo, source, outgoing, bundlecaps=bundlecaps,
616 return getlocalchangegroupraw(repo, source, outgoing, bundlecaps=bundlecaps,
617 version=version)
617 version=version)
618
618
619 def getchangegroup(repo, source, heads=None, common=None, bundlecaps=None):
619 def getchangegroup(repo, source, heads=None, common=None, bundlecaps=None):
620 """Like changegroupsubset, but returns the set difference between the
620 """Like changegroupsubset, but returns the set difference between the
621 ancestors of heads and the ancestors common.
621 ancestors of heads and the ancestors common.
622
622
623 If heads is None, use the local heads. If common is None, use [nullid].
623 If heads is None, use the local heads. If common is None, use [nullid].
624
624
625 The nodes in common might not all be known locally due to the way the
625 The nodes in common might not all be known locally due to the way the
626 current discovery protocol works.
626 current discovery protocol works.
627 """
627 """
628 outgoing = _computeoutgoing(repo, heads, common)
628 outgoing = _computeoutgoing(repo, heads, common)
629 return getlocalchangegroup(repo, source, outgoing, bundlecaps=bundlecaps)
629 return getlocalchangegroup(repo, source, outgoing, bundlecaps=bundlecaps)
630
630
631 def changegroup(repo, basenodes, source):
631 def changegroup(repo, basenodes, source):
632 # to avoid a race we use changegroupsubset() (issue1320)
632 # to avoid a race we use changegroupsubset() (issue1320)
633 return changegroupsubset(repo, basenodes, repo.heads(), source)
633 return changegroupsubset(repo, basenodes, repo.heads(), source)
634
634
635 def addchangegroupfiles(repo, source, revmap, trp, pr, needfiles):
635 def addchangegroupfiles(repo, source, revmap, trp, pr, needfiles):
636 revisions = 0
636 revisions = 0
637 files = 0
637 files = 0
638 while True:
638 while True:
639 chunkdata = source.filelogheader()
639 chunkdata = source.filelogheader()
640 if not chunkdata:
640 if not chunkdata:
641 break
641 break
642 f = chunkdata["filename"]
642 f = chunkdata["filename"]
643 repo.ui.debug("adding %s revisions\n" % f)
643 repo.ui.debug("adding %s revisions\n" % f)
644 pr()
644 pr()
645 fl = repo.file(f)
645 fl = repo.file(f)
646 o = len(fl)
646 o = len(fl)
647 if not fl.addgroup(source, revmap, trp):
647 if not fl.addgroup(source, revmap, trp):
648 raise util.Abort(_("received file revlog group is empty"))
648 raise util.Abort(_("received file revlog group is empty"))
649 revisions += len(fl) - o
649 revisions += len(fl) - o
650 files += 1
650 files += 1
651 if f in needfiles:
651 if f in needfiles:
652 needs = needfiles[f]
652 needs = needfiles[f]
653 for new in xrange(o, len(fl)):
653 for new in xrange(o, len(fl)):
654 n = fl.node(new)
654 n = fl.node(new)
655 if n in needs:
655 if n in needs:
656 needs.remove(n)
656 needs.remove(n)
657 else:
657 else:
658 raise util.Abort(
658 raise util.Abort(
659 _("received spurious file revlog entry"))
659 _("received spurious file revlog entry"))
660 if not needs:
660 if not needs:
661 del needfiles[f]
661 del needfiles[f]
662 repo.ui.progress(_('files'), None)
662 repo.ui.progress(_('files'), None)
663
663
664 for f, needs in needfiles.iteritems():
664 for f, needs in needfiles.iteritems():
665 fl = repo.file(f)
665 fl = repo.file(f)
666 for n in needs:
666 for n in needs:
667 try:
667 try:
668 fl.rev(n)
668 fl.rev(n)
669 except error.LookupError:
669 except error.LookupError:
670 raise util.Abort(
670 raise util.Abort(
671 _('missing file data for %s:%s - run hg verify') %
671 _('missing file data for %s:%s - run hg verify') %
672 (f, hex(n)))
672 (f, hex(n)))
673
673
674 return revisions, files
674 return revisions, files
675
675
676 def addchangegroup(repo, source, srctype, url, emptyok=False,
676 def addchangegroup(repo, source, srctype, url, emptyok=False,
677 targetphase=phases.draft):
677 targetphase=phases.draft):
678 """Add the changegroup returned by source.read() to this repo.
678 """Add the changegroup returned by source.read() to this repo.
679 srctype is a string like 'push', 'pull', or 'unbundle'. url is
679 srctype is a string like 'push', 'pull', or 'unbundle'. url is
680 the URL of the repo where this changegroup is coming from.
680 the URL of the repo where this changegroup is coming from.
681
681
682 Return an integer summarizing the change to this repo:
682 Return an integer summarizing the change to this repo:
683 - nothing changed or no source: 0
683 - nothing changed or no source: 0
684 - more heads than before: 1+added heads (2..n)
684 - more heads than before: 1+added heads (2..n)
685 - fewer heads than before: -1-removed heads (-2..-n)
685 - fewer heads than before: -1-removed heads (-2..-n)
686 - number of heads stays the same: 1
686 - number of heads stays the same: 1
687 """
687 """
688 repo = repo.unfiltered()
688 repo = repo.unfiltered()
689 def csmap(x):
689 def csmap(x):
690 repo.ui.debug("add changeset %s\n" % short(x))
690 repo.ui.debug("add changeset %s\n" % short(x))
691 return len(cl)
691 return len(cl)
692
692
693 def revmap(x):
693 def revmap(x):
694 return cl.rev(x)
694 return cl.rev(x)
695
695
696 if not source:
696 if not source:
697 return 0
697 return 0
698
698
699 changesets = files = revisions = 0
699 changesets = files = revisions = 0
700 efiles = set()
700 efiles = set()
701
701
702 tr = repo.transaction("\n".join([srctype, util.hidepassword(url)]))
702 tr = repo.transaction("\n".join([srctype, util.hidepassword(url)]))
703 # The transaction could have been created before and already carries source
703 # The transaction could have been created before and already carries source
704 # information. In this case we use the top level data. We overwrite the
704 # information. In this case we use the top level data. We overwrite the
705 # argument because we need to use the top level value (if they exist) in
705 # argument because we need to use the top level value (if they exist) in
706 # this function.
706 # this function.
707 srctype = tr.hookargs.setdefault('source', srctype)
707 srctype = tr.hookargs.setdefault('source', srctype)
708 url = tr.hookargs.setdefault('url', url)
708 url = tr.hookargs.setdefault('url', url)
709
709
710 # write changelog data to temp files so concurrent readers will not see
710 # write changelog data to temp files so concurrent readers will not see
711 # inconsistent view
711 # inconsistent view
712 cl = repo.changelog
712 cl = repo.changelog
713 cl.delayupdate(tr)
713 cl.delayupdate(tr)
714 oldheads = cl.heads()
714 oldheads = cl.heads()
715 try:
715 try:
716 repo.hook('prechangegroup', throw=True, **tr.hookargs)
716 repo.hook('prechangegroup', throw=True, **tr.hookargs)
717
717
718 trp = weakref.proxy(tr)
718 trp = weakref.proxy(tr)
719 # pull off the changeset group
719 # pull off the changeset group
720 repo.ui.status(_("adding changesets\n"))
720 repo.ui.status(_("adding changesets\n"))
721 clstart = len(cl)
721 clstart = len(cl)
722 class prog(object):
722 class prog(object):
723 step = _('changesets')
723 step = _('changesets')
724 count = 1
724 count = 1
725 ui = repo.ui
725 ui = repo.ui
726 total = None
726 total = None
727 def __call__(repo):
727 def __call__(repo):
728 repo.ui.progress(repo.step, repo.count, unit=_('chunks'),
728 repo.ui.progress(repo.step, repo.count, unit=_('chunks'),
729 total=repo.total)
729 total=repo.total)
730 repo.count += 1
730 repo.count += 1
731 pr = prog()
731 pr = prog()
732 source.callback = pr
732 source.callback = pr
733
733
734 source.changelogheader()
734 source.changelogheader()
735 srccontent = cl.addgroup(source, csmap, trp)
735 srccontent = cl.addgroup(source, csmap, trp)
736 if not (srccontent or emptyok):
736 if not (srccontent or emptyok):
737 raise util.Abort(_("received changelog group is empty"))
737 raise util.Abort(_("received changelog group is empty"))
738 clend = len(cl)
738 clend = len(cl)
739 changesets = clend - clstart
739 changesets = clend - clstart
740 for c in xrange(clstart, clend):
740 for c in xrange(clstart, clend):
741 efiles.update(repo[c].files())
741 efiles.update(repo[c].files())
742 efiles = len(efiles)
742 efiles = len(efiles)
743 repo.ui.progress(_('changesets'), None)
743 repo.ui.progress(_('changesets'), None)
744
744
745 # pull off the manifest group
745 # pull off the manifest group
746 repo.ui.status(_("adding manifests\n"))
746 repo.ui.status(_("adding manifests\n"))
747 pr.step = _('manifests')
747 pr.step = _('manifests')
748 pr.count = 1
748 pr.count = 1
749 pr.total = changesets # manifests <= changesets
749 pr.total = changesets # manifests <= changesets
750 # no need to check for empty manifest group here:
750 # no need to check for empty manifest group here:
751 # if the result of the merge of 1 and 2 is the same in 3 and 4,
751 # if the result of the merge of 1 and 2 is the same in 3 and 4,
752 # no new manifest will be created and the manifest group will
752 # no new manifest will be created and the manifest group will
753 # be empty during the pull
753 # be empty during the pull
754 source.manifestheader()
754 source.manifestheader()
755 repo.manifest.addgroup(source, revmap, trp)
755 repo.manifest.addgroup(source, revmap, trp)
756 repo.ui.progress(_('manifests'), None)
756 repo.ui.progress(_('manifests'), None)
757
757
758 needfiles = {}
758 needfiles = {}
759 if repo.ui.configbool('server', 'validate', default=False):
759 if repo.ui.configbool('server', 'validate', default=False):
760 # validate incoming csets have their manifests
760 # validate incoming csets have their manifests
761 for cset in xrange(clstart, clend):
761 for cset in xrange(clstart, clend):
762 mfest = repo.changelog.read(repo.changelog.node(cset))[0]
762 mfest = repo.changelog.read(repo.changelog.node(cset))[0]
763 mfest = repo.manifest.readdelta(mfest)
763 mfest = repo.manifest.readdelta(mfest)
764 # store file nodes we must see
764 # store file nodes we must see
765 for f, n in mfest.iteritems():
765 for f, n in mfest.iteritems():
766 needfiles.setdefault(f, set()).add(n)
766 needfiles.setdefault(f, set()).add(n)
767
767
768 # process the files
768 # process the files
769 repo.ui.status(_("adding file changes\n"))
769 repo.ui.status(_("adding file changes\n"))
770 pr.step = _('files')
770 pr.step = _('files')
771 pr.count = 1
771 pr.count = 1
772 pr.total = efiles
772 pr.total = efiles
773 source.callback = None
773 source.callback = None
774
774
775 newrevs, newfiles = addchangegroupfiles(repo, source, revmap, trp, pr,
775 newrevs, newfiles = addchangegroupfiles(repo, source, revmap, trp, pr,
776 needfiles)
776 needfiles)
777 revisions += newrevs
777 revisions += newrevs
778 files += newfiles
778 files += newfiles
779
779
780 dh = 0
780 dh = 0
781 if oldheads:
781 if oldheads:
782 heads = cl.heads()
782 heads = cl.heads()
783 dh = len(heads) - len(oldheads)
783 dh = len(heads) - len(oldheads)
784 for h in heads:
784 for h in heads:
785 if h not in oldheads and repo[h].closesbranch():
785 if h not in oldheads and repo[h].closesbranch():
786 dh -= 1
786 dh -= 1
787 htext = ""
787 htext = ""
788 if dh:
788 if dh:
789 htext = _(" (%+d heads)") % dh
789 htext = _(" (%+d heads)") % dh
790
790
791 repo.ui.status(_("added %d changesets"
791 repo.ui.status(_("added %d changesets"
792 " with %d changes to %d files%s\n")
792 " with %d changes to %d files%s\n")
793 % (changesets, revisions, files, htext))
793 % (changesets, revisions, files, htext))
794 repo.invalidatevolatilesets()
794 repo.invalidatevolatilesets()
795
795
796 if changesets > 0:
796 if changesets > 0:
797 p = lambda: tr.writepending() and repo.root or ""
797 p = lambda: tr.writepending() and repo.root or ""
798 if 'node' not in tr.hookargs:
798 if 'node' not in tr.hookargs:
799 tr.hookargs['node'] = hex(cl.node(clstart))
799 tr.hookargs['node'] = hex(cl.node(clstart))
800 hookargs = dict(tr.hookargs)
800 hookargs = dict(tr.hookargs)
801 else:
801 else:
802 hookargs = dict(tr.hookargs)
802 hookargs = dict(tr.hookargs)
803 hookargs['node'] = hex(cl.node(clstart))
803 hookargs['node'] = hex(cl.node(clstart))
804 repo.hook('pretxnchangegroup', throw=True, pending=p, **hookargs)
804 repo.hook('pretxnchangegroup', throw=True, pending=p, **hookargs)
805
805
806 added = [cl.node(r) for r in xrange(clstart, clend)]
806 added = [cl.node(r) for r in xrange(clstart, clend)]
807 publishing = repo.ui.configbool('phases', 'publish', True)
807 publishing = repo.ui.configbool('phases', 'publish', True)
808 if srctype in ('push', 'serve'):
808 if srctype in ('push', 'serve'):
809 # Old servers can not push the boundary themselves.
809 # Old servers can not push the boundary themselves.
810 # New servers won't push the boundary if changeset already
810 # New servers won't push the boundary if changeset already
811 # exists locally as secret
811 # exists locally as secret
812 #
812 #
813 # We should not use added here but the list of all change in
813 # We should not use added here but the list of all change in
814 # the bundle
814 # the bundle
815 if publishing:
815 if publishing:
816 phases.advanceboundary(repo, tr, phases.public, srccontent)
816 phases.advanceboundary(repo, tr, phases.public, srccontent)
817 else:
817 else:
818 # Those changesets have been pushed from the outside, their
818 # Those changesets have been pushed from the outside, their
819 # phases are going to be pushed alongside. Therefor
819 # phases are going to be pushed alongside. Therefor
820 # `targetphase` is ignored.
820 # `targetphase` is ignored.
821 phases.advanceboundary(repo, tr, phases.draft, srccontent)
821 phases.advanceboundary(repo, tr, phases.draft, srccontent)
822 phases.retractboundary(repo, tr, phases.draft, added)
822 phases.retractboundary(repo, tr, phases.draft, added)
823 elif srctype != 'strip':
823 elif srctype != 'strip':
824 # publishing only alter behavior during push
824 # publishing only alter behavior during push
825 #
825 #
826 # strip should not touch boundary at all
826 # strip should not touch boundary at all
827 phases.retractboundary(repo, tr, targetphase, added)
827 phases.retractboundary(repo, tr, targetphase, added)
828
828
829 if changesets > 0:
829 if changesets > 0:
830 if srctype != 'strip':
830 if srctype != 'strip':
831 # During strip, branchcache is invalid but coming call to
831 # During strip, branchcache is invalid but coming call to
832 # `destroyed` will repair it.
832 # `destroyed` will repair it.
833 # In other case we can safely update cache on disk.
833 # In other case we can safely update cache on disk.
834 branchmap.updatecache(repo.filtered('served'))
834 branchmap.updatecache(repo.filtered('served'))
835
835
836 def runhooks():
836 def runhooks():
837 # These hooks run when the lock releases, not when the
837 # These hooks run when the lock releases, not when the
838 # transaction closes. So it's possible for the changelog
838 # transaction closes. So it's possible for the changelog
839 # to have changed since we last saw it.
839 # to have changed since we last saw it.
840 if clstart >= len(repo):
840 if clstart >= len(repo):
841 return
841 return
842
842
843 # forcefully update the on-disk branch cache
843 # forcefully update the on-disk branch cache
844 repo.ui.debug("updating the branch cache\n")
844 repo.ui.debug("updating the branch cache\n")
845 repo.hook("changegroup", **hookargs)
845 repo.hook("changegroup", **hookargs)
846
846
847 for n in added:
847 for n in added:
848 args = hookargs.copy()
848 args = hookargs.copy()
849 args['node'] = hex(n)
849 args['node'] = hex(n)
850 repo.hook("incoming", **args)
850 repo.hook("incoming", **args)
851
851
852 newheads = [h for h in repo.heads() if h not in oldheads]
852 newheads = [h for h in repo.heads() if h not in oldheads]
853 repo.ui.log("incoming",
853 repo.ui.log("incoming",
854 "%s incoming changes - new heads: %s\n",
854 "%s incoming changes - new heads: %s\n",
855 len(added),
855 len(added),
856 ', '.join([hex(c[:6]) for c in newheads]))
856 ', '.join([hex(c[:6]) for c in newheads]))
857
857
858 tr.addpostclose('changegroup-runhooks-%020i' % clstart,
858 tr.addpostclose('changegroup-runhooks-%020i' % clstart,
859 lambda tr: repo._afterlock(runhooks))
859 lambda tr: repo._afterlock(runhooks))
860
860
861 tr.close()
861 tr.close()
862
862
863 finally:
863 finally:
864 tr.release()
864 tr.release()
865 # never return 0 here:
865 # never return 0 here:
866 if dh < 0:
866 if dh < 0:
867 return dh - 1
867 return dh - 1
868 else:
868 else:
869 return dh + 1
869 return dh + 1
@@ -1,6317 +1,6317 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, bin, nullid, nullrev, short
8 from node import hex, bin, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _
10 from i18n import _
11 import os, re, difflib, time, tempfile, errno, shlex
11 import os, re, difflib, time, tempfile, errno, shlex
12 import sys, socket
12 import sys, socket
13 import hg, scmutil, util, revlog, copies, error, bookmarks
13 import hg, scmutil, util, revlog, copies, error, bookmarks
14 import patch, help, encoding, templatekw, discovery
14 import patch, help, encoding, templatekw, discovery
15 import archival, changegroup, cmdutil, hbisect
15 import archival, changegroup, cmdutil, hbisect
16 import sshserver, hgweb, commandserver
16 import sshserver, hgweb, commandserver
17 import extensions
17 import extensions
18 from hgweb import server as hgweb_server
18 from hgweb import server as hgweb_server
19 import merge as mergemod
19 import merge as mergemod
20 import minirst, revset, fileset
20 import minirst, revset, fileset
21 import dagparser, context, simplemerge, graphmod, copies
21 import dagparser, context, simplemerge, graphmod, copies
22 import random
22 import random
23 import setdiscovery, treediscovery, dagutil, pvec, localrepo
23 import setdiscovery, treediscovery, dagutil, pvec, localrepo
24 import phases, obsolete, exchange, bundle2
24 import phases, obsolete, exchange, bundle2
25 import ui as uimod
25 import ui as uimod
26
26
27 table = {}
27 table = {}
28
28
29 command = cmdutil.command(table)
29 command = cmdutil.command(table)
30
30
31 # Space delimited list of commands that don't require local repositories.
31 # Space delimited list of commands that don't require local repositories.
32 # This should be populated by passing norepo=True into the @command decorator.
32 # This should be populated by passing norepo=True into the @command decorator.
33 norepo = ''
33 norepo = ''
34 # Space delimited list of commands that optionally require local repositories.
34 # Space delimited list of commands that optionally require local repositories.
35 # This should be populated by passing optionalrepo=True into the @command
35 # This should be populated by passing optionalrepo=True into the @command
36 # decorator.
36 # decorator.
37 optionalrepo = ''
37 optionalrepo = ''
38 # Space delimited list of commands that will examine arguments looking for
38 # Space delimited list of commands that will examine arguments looking for
39 # a repository. This should be populated by passing inferrepo=True into the
39 # a repository. This should be populated by passing inferrepo=True into the
40 # @command decorator.
40 # @command decorator.
41 inferrepo = ''
41 inferrepo = ''
42
42
43 # common command options
43 # common command options
44
44
45 globalopts = [
45 globalopts = [
46 ('R', 'repository', '',
46 ('R', 'repository', '',
47 _('repository root directory or name of overlay bundle file'),
47 _('repository root directory or name of overlay bundle file'),
48 _('REPO')),
48 _('REPO')),
49 ('', 'cwd', '',
49 ('', 'cwd', '',
50 _('change working directory'), _('DIR')),
50 _('change working directory'), _('DIR')),
51 ('y', 'noninteractive', None,
51 ('y', 'noninteractive', None,
52 _('do not prompt, automatically pick the first choice for all prompts')),
52 _('do not prompt, automatically pick the first choice for all prompts')),
53 ('q', 'quiet', None, _('suppress output')),
53 ('q', 'quiet', None, _('suppress output')),
54 ('v', 'verbose', None, _('enable additional output')),
54 ('v', 'verbose', None, _('enable additional output')),
55 ('', 'config', [],
55 ('', 'config', [],
56 _('set/override config option (use \'section.name=value\')'),
56 _('set/override config option (use \'section.name=value\')'),
57 _('CONFIG')),
57 _('CONFIG')),
58 ('', 'debug', None, _('enable debugging output')),
58 ('', 'debug', None, _('enable debugging output')),
59 ('', 'debugger', None, _('start debugger')),
59 ('', 'debugger', None, _('start debugger')),
60 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
60 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
61 _('ENCODE')),
61 _('ENCODE')),
62 ('', 'encodingmode', encoding.encodingmode,
62 ('', 'encodingmode', encoding.encodingmode,
63 _('set the charset encoding mode'), _('MODE')),
63 _('set the charset encoding mode'), _('MODE')),
64 ('', 'traceback', None, _('always print a traceback on exception')),
64 ('', 'traceback', None, _('always print a traceback on exception')),
65 ('', 'time', None, _('time how long the command takes')),
65 ('', 'time', None, _('time how long the command takes')),
66 ('', 'profile', None, _('print command execution profile')),
66 ('', 'profile', None, _('print command execution profile')),
67 ('', 'version', None, _('output version information and exit')),
67 ('', 'version', None, _('output version information and exit')),
68 ('h', 'help', None, _('display help and exit')),
68 ('h', 'help', None, _('display help and exit')),
69 ('', 'hidden', False, _('consider hidden changesets')),
69 ('', 'hidden', False, _('consider hidden changesets')),
70 ]
70 ]
71
71
72 dryrunopts = [('n', 'dry-run', None,
72 dryrunopts = [('n', 'dry-run', None,
73 _('do not perform actions, just print output'))]
73 _('do not perform actions, just print output'))]
74
74
75 remoteopts = [
75 remoteopts = [
76 ('e', 'ssh', '',
76 ('e', 'ssh', '',
77 _('specify ssh command to use'), _('CMD')),
77 _('specify ssh command to use'), _('CMD')),
78 ('', 'remotecmd', '',
78 ('', 'remotecmd', '',
79 _('specify hg command to run on the remote side'), _('CMD')),
79 _('specify hg command to run on the remote side'), _('CMD')),
80 ('', 'insecure', None,
80 ('', 'insecure', None,
81 _('do not verify server certificate (ignoring web.cacerts config)')),
81 _('do not verify server certificate (ignoring web.cacerts config)')),
82 ]
82 ]
83
83
84 walkopts = [
84 walkopts = [
85 ('I', 'include', [],
85 ('I', 'include', [],
86 _('include names matching the given patterns'), _('PATTERN')),
86 _('include names matching the given patterns'), _('PATTERN')),
87 ('X', 'exclude', [],
87 ('X', 'exclude', [],
88 _('exclude names matching the given patterns'), _('PATTERN')),
88 _('exclude names matching the given patterns'), _('PATTERN')),
89 ]
89 ]
90
90
91 commitopts = [
91 commitopts = [
92 ('m', 'message', '',
92 ('m', 'message', '',
93 _('use text as commit message'), _('TEXT')),
93 _('use text as commit message'), _('TEXT')),
94 ('l', 'logfile', '',
94 ('l', 'logfile', '',
95 _('read commit message from file'), _('FILE')),
95 _('read commit message from file'), _('FILE')),
96 ]
96 ]
97
97
98 commitopts2 = [
98 commitopts2 = [
99 ('d', 'date', '',
99 ('d', 'date', '',
100 _('record the specified date as commit date'), _('DATE')),
100 _('record the specified date as commit date'), _('DATE')),
101 ('u', 'user', '',
101 ('u', 'user', '',
102 _('record the specified user as committer'), _('USER')),
102 _('record the specified user as committer'), _('USER')),
103 ]
103 ]
104
104
105 # hidden for now
105 # hidden for now
106 formatteropts = [
106 formatteropts = [
107 ('T', 'template', '',
107 ('T', 'template', '',
108 _('display with template (DEPRECATED)'), _('TEMPLATE')),
108 _('display with template (DEPRECATED)'), _('TEMPLATE')),
109 ]
109 ]
110
110
111 templateopts = [
111 templateopts = [
112 ('', 'style', '',
112 ('', 'style', '',
113 _('display using template map file (DEPRECATED)'), _('STYLE')),
113 _('display using template map file (DEPRECATED)'), _('STYLE')),
114 ('T', 'template', '',
114 ('T', 'template', '',
115 _('display with template'), _('TEMPLATE')),
115 _('display with template'), _('TEMPLATE')),
116 ]
116 ]
117
117
118 logopts = [
118 logopts = [
119 ('p', 'patch', None, _('show patch')),
119 ('p', 'patch', None, _('show patch')),
120 ('g', 'git', None, _('use git extended diff format')),
120 ('g', 'git', None, _('use git extended diff format')),
121 ('l', 'limit', '',
121 ('l', 'limit', '',
122 _('limit number of changes displayed'), _('NUM')),
122 _('limit number of changes displayed'), _('NUM')),
123 ('M', 'no-merges', None, _('do not show merges')),
123 ('M', 'no-merges', None, _('do not show merges')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
125 ('G', 'graph', None, _("show the revision DAG")),
125 ('G', 'graph', None, _("show the revision DAG")),
126 ] + templateopts
126 ] + templateopts
127
127
128 diffopts = [
128 diffopts = [
129 ('a', 'text', None, _('treat all files as text')),
129 ('a', 'text', None, _('treat all files as text')),
130 ('g', 'git', None, _('use git extended diff format')),
130 ('g', 'git', None, _('use git extended diff format')),
131 ('', 'nodates', None, _('omit dates from diff headers'))
131 ('', 'nodates', None, _('omit dates from diff headers'))
132 ]
132 ]
133
133
134 diffwsopts = [
134 diffwsopts = [
135 ('w', 'ignore-all-space', None,
135 ('w', 'ignore-all-space', None,
136 _('ignore white space when comparing lines')),
136 _('ignore white space when comparing lines')),
137 ('b', 'ignore-space-change', None,
137 ('b', 'ignore-space-change', None,
138 _('ignore changes in the amount of white space')),
138 _('ignore changes in the amount of white space')),
139 ('B', 'ignore-blank-lines', None,
139 ('B', 'ignore-blank-lines', None,
140 _('ignore changes whose lines are all blank')),
140 _('ignore changes whose lines are all blank')),
141 ]
141 ]
142
142
143 diffopts2 = [
143 diffopts2 = [
144 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
144 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
145 ('p', 'show-function', None, _('show which function each change is in')),
145 ('p', 'show-function', None, _('show which function each change is in')),
146 ('', 'reverse', None, _('produce a diff that undoes the changes')),
146 ('', 'reverse', None, _('produce a diff that undoes the changes')),
147 ] + diffwsopts + [
147 ] + diffwsopts + [
148 ('U', 'unified', '',
148 ('U', 'unified', '',
149 _('number of lines of context to show'), _('NUM')),
149 _('number of lines of context to show'), _('NUM')),
150 ('', 'stat', None, _('output diffstat-style summary of changes')),
150 ('', 'stat', None, _('output diffstat-style summary of changes')),
151 ]
151 ]
152
152
153 mergetoolopts = [
153 mergetoolopts = [
154 ('t', 'tool', '', _('specify merge tool')),
154 ('t', 'tool', '', _('specify merge tool')),
155 ]
155 ]
156
156
157 similarityopts = [
157 similarityopts = [
158 ('s', 'similarity', '',
158 ('s', 'similarity', '',
159 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
159 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
160 ]
160 ]
161
161
162 subrepoopts = [
162 subrepoopts = [
163 ('S', 'subrepos', None,
163 ('S', 'subrepos', None,
164 _('recurse into subrepositories'))
164 _('recurse into subrepositories'))
165 ]
165 ]
166
166
167 # Commands start here, listed alphabetically
167 # Commands start here, listed alphabetically
168
168
169 @command('^add',
169 @command('^add',
170 walkopts + subrepoopts + dryrunopts,
170 walkopts + subrepoopts + dryrunopts,
171 _('[OPTION]... [FILE]...'),
171 _('[OPTION]... [FILE]...'),
172 inferrepo=True)
172 inferrepo=True)
173 def add(ui, repo, *pats, **opts):
173 def add(ui, repo, *pats, **opts):
174 """add the specified files on the next commit
174 """add the specified files on the next commit
175
175
176 Schedule files to be version controlled and added to the
176 Schedule files to be version controlled and added to the
177 repository.
177 repository.
178
178
179 The files will be added to the repository at the next commit. To
179 The files will be added to the repository at the next commit. To
180 undo an add before that, see :hg:`forget`.
180 undo an add before that, see :hg:`forget`.
181
181
182 If no names are given, add all files to the repository.
182 If no names are given, add all files to the repository.
183
183
184 .. container:: verbose
184 .. container:: verbose
185
185
186 An example showing how new (unknown) files are added
186 An example showing how new (unknown) files are added
187 automatically by :hg:`add`::
187 automatically by :hg:`add`::
188
188
189 $ ls
189 $ ls
190 foo.c
190 foo.c
191 $ hg status
191 $ hg status
192 ? foo.c
192 ? foo.c
193 $ hg add
193 $ hg add
194 adding foo.c
194 adding foo.c
195 $ hg status
195 $ hg status
196 A foo.c
196 A foo.c
197
197
198 Returns 0 if all files are successfully added.
198 Returns 0 if all files are successfully added.
199 """
199 """
200
200
201 m = scmutil.match(repo[None], pats, opts)
201 m = scmutil.match(repo[None], pats, opts)
202 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
202 rejected = cmdutil.add(ui, repo, m, "", False, **opts)
203 return rejected and 1 or 0
203 return rejected and 1 or 0
204
204
205 @command('addremove',
205 @command('addremove',
206 similarityopts + subrepoopts + walkopts + dryrunopts,
206 similarityopts + subrepoopts + walkopts + dryrunopts,
207 _('[OPTION]... [FILE]...'),
207 _('[OPTION]... [FILE]...'),
208 inferrepo=True)
208 inferrepo=True)
209 def addremove(ui, repo, *pats, **opts):
209 def addremove(ui, repo, *pats, **opts):
210 """add all new files, delete all missing files
210 """add all new files, delete all missing files
211
211
212 Add all new files and remove all missing files from the
212 Add all new files and remove all missing files from the
213 repository.
213 repository.
214
214
215 New files are ignored if they match any of the patterns in
215 New files are ignored if they match any of the patterns in
216 ``.hgignore``. As with add, these changes take effect at the next
216 ``.hgignore``. As with add, these changes take effect at the next
217 commit.
217 commit.
218
218
219 Use the -s/--similarity option to detect renamed files. This
219 Use the -s/--similarity option to detect renamed files. This
220 option takes a percentage between 0 (disabled) and 100 (files must
220 option takes a percentage between 0 (disabled) and 100 (files must
221 be identical) as its parameter. With a parameter greater than 0,
221 be identical) as its parameter. With a parameter greater than 0,
222 this compares every removed file with every added file and records
222 this compares every removed file with every added file and records
223 those similar enough as renames. Detecting renamed files this way
223 those similar enough as renames. Detecting renamed files this way
224 can be expensive. After using this option, :hg:`status -C` can be
224 can be expensive. After using this option, :hg:`status -C` can be
225 used to check which files were identified as moved or renamed. If
225 used to check which files were identified as moved or renamed. If
226 not specified, -s/--similarity defaults to 100 and only renames of
226 not specified, -s/--similarity defaults to 100 and only renames of
227 identical files are detected.
227 identical files are detected.
228
228
229 Returns 0 if all files are successfully added.
229 Returns 0 if all files are successfully added.
230 """
230 """
231 try:
231 try:
232 sim = float(opts.get('similarity') or 100)
232 sim = float(opts.get('similarity') or 100)
233 except ValueError:
233 except ValueError:
234 raise util.Abort(_('similarity must be a number'))
234 raise util.Abort(_('similarity must be a number'))
235 if sim < 0 or sim > 100:
235 if sim < 0 or sim > 100:
236 raise util.Abort(_('similarity must be between 0 and 100'))
236 raise util.Abort(_('similarity must be between 0 and 100'))
237 matcher = scmutil.match(repo[None], pats, opts)
237 matcher = scmutil.match(repo[None], pats, opts)
238 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
238 return scmutil.addremove(repo, matcher, "", opts, similarity=sim / 100.0)
239
239
240 @command('^annotate|blame',
240 @command('^annotate|blame',
241 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
241 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
242 ('', 'follow', None,
242 ('', 'follow', None,
243 _('follow copies/renames and list the filename (DEPRECATED)')),
243 _('follow copies/renames and list the filename (DEPRECATED)')),
244 ('', 'no-follow', None, _("don't follow copies and renames")),
244 ('', 'no-follow', None, _("don't follow copies and renames")),
245 ('a', 'text', None, _('treat all files as text')),
245 ('a', 'text', None, _('treat all files as text')),
246 ('u', 'user', None, _('list the author (long with -v)')),
246 ('u', 'user', None, _('list the author (long with -v)')),
247 ('f', 'file', None, _('list the filename')),
247 ('f', 'file', None, _('list the filename')),
248 ('d', 'date', None, _('list the date (short with -q)')),
248 ('d', 'date', None, _('list the date (short with -q)')),
249 ('n', 'number', None, _('list the revision number (default)')),
249 ('n', 'number', None, _('list the revision number (default)')),
250 ('c', 'changeset', None, _('list the changeset')),
250 ('c', 'changeset', None, _('list the changeset')),
251 ('l', 'line-number', None, _('show line number at the first appearance'))
251 ('l', 'line-number', None, _('show line number at the first appearance'))
252 ] + diffwsopts + walkopts + formatteropts,
252 ] + diffwsopts + walkopts + formatteropts,
253 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
253 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
254 inferrepo=True)
254 inferrepo=True)
255 def annotate(ui, repo, *pats, **opts):
255 def annotate(ui, repo, *pats, **opts):
256 """show changeset information by line for each file
256 """show changeset information by line for each file
257
257
258 List changes in files, showing the revision id responsible for
258 List changes in files, showing the revision id responsible for
259 each line
259 each line
260
260
261 This command is useful for discovering when a change was made and
261 This command is useful for discovering when a change was made and
262 by whom.
262 by whom.
263
263
264 Without the -a/--text option, annotate will avoid processing files
264 Without the -a/--text option, annotate will avoid processing files
265 it detects as binary. With -a, annotate will annotate the file
265 it detects as binary. With -a, annotate will annotate the file
266 anyway, although the results will probably be neither useful
266 anyway, although the results will probably be neither useful
267 nor desirable.
267 nor desirable.
268
268
269 Returns 0 on success.
269 Returns 0 on success.
270 """
270 """
271 if not pats:
271 if not pats:
272 raise util.Abort(_('at least one filename or pattern is required'))
272 raise util.Abort(_('at least one filename or pattern is required'))
273
273
274 if opts.get('follow'):
274 if opts.get('follow'):
275 # --follow is deprecated and now just an alias for -f/--file
275 # --follow is deprecated and now just an alias for -f/--file
276 # to mimic the behavior of Mercurial before version 1.5
276 # to mimic the behavior of Mercurial before version 1.5
277 opts['file'] = True
277 opts['file'] = True
278
278
279 fm = ui.formatter('annotate', opts)
279 fm = ui.formatter('annotate', opts)
280 datefunc = ui.quiet and util.shortdate or util.datestr
280 datefunc = ui.quiet and util.shortdate or util.datestr
281 hexfn = fm.hexfunc
281 hexfn = fm.hexfunc
282
282
283 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
283 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
284 ('number', ' ', lambda x: x[0].rev(), str),
284 ('number', ' ', lambda x: x[0].rev(), str),
285 ('changeset', ' ', lambda x: hexfn(x[0].node()), str),
285 ('changeset', ' ', lambda x: hexfn(x[0].node()), str),
286 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
286 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
287 ('file', ' ', lambda x: x[0].path(), str),
287 ('file', ' ', lambda x: x[0].path(), str),
288 ('line_number', ':', lambda x: x[1], str),
288 ('line_number', ':', lambda x: x[1], str),
289 ]
289 ]
290 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
290 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
291
291
292 if (not opts.get('user') and not opts.get('changeset')
292 if (not opts.get('user') and not opts.get('changeset')
293 and not opts.get('date') and not opts.get('file')):
293 and not opts.get('date') and not opts.get('file')):
294 opts['number'] = True
294 opts['number'] = True
295
295
296 linenumber = opts.get('line_number') is not None
296 linenumber = opts.get('line_number') is not None
297 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
297 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
298 raise util.Abort(_('at least one of -n/-c is required for -l'))
298 raise util.Abort(_('at least one of -n/-c is required for -l'))
299
299
300 if fm:
300 if fm:
301 def makefunc(get, fmt):
301 def makefunc(get, fmt):
302 return get
302 return get
303 else:
303 else:
304 def makefunc(get, fmt):
304 def makefunc(get, fmt):
305 return lambda x: fmt(get(x))
305 return lambda x: fmt(get(x))
306 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
306 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
307 if opts.get(op)]
307 if opts.get(op)]
308 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
308 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
309 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
309 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
310 if opts.get(op))
310 if opts.get(op))
311
311
312 def bad(x, y):
312 def bad(x, y):
313 raise util.Abort("%s: %s" % (x, y))
313 raise util.Abort("%s: %s" % (x, y))
314
314
315 ctx = scmutil.revsingle(repo, opts.get('rev'))
315 ctx = scmutil.revsingle(repo, opts.get('rev'))
316 m = scmutil.match(ctx, pats, opts)
316 m = scmutil.match(ctx, pats, opts)
317 m.bad = bad
317 m.bad = bad
318 follow = not opts.get('no_follow')
318 follow = not opts.get('no_follow')
319 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
319 diffopts = patch.difffeatureopts(ui, opts, section='annotate',
320 whitespace=True)
320 whitespace=True)
321 for abs in ctx.walk(m):
321 for abs in ctx.walk(m):
322 fctx = ctx[abs]
322 fctx = ctx[abs]
323 if not opts.get('text') and util.binary(fctx.data()):
323 if not opts.get('text') and util.binary(fctx.data()):
324 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
324 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
325 continue
325 continue
326
326
327 lines = fctx.annotate(follow=follow, linenumber=linenumber,
327 lines = fctx.annotate(follow=follow, linenumber=linenumber,
328 diffopts=diffopts)
328 diffopts=diffopts)
329 formats = []
329 formats = []
330 pieces = []
330 pieces = []
331
331
332 for f, sep in funcmap:
332 for f, sep in funcmap:
333 l = [f(n) for n, dummy in lines]
333 l = [f(n) for n, dummy in lines]
334 if l:
334 if l:
335 if fm:
335 if fm:
336 formats.append(['%s' for x in l])
336 formats.append(['%s' for x in l])
337 else:
337 else:
338 sizes = [encoding.colwidth(x) for x in l]
338 sizes = [encoding.colwidth(x) for x in l]
339 ml = max(sizes)
339 ml = max(sizes)
340 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
340 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
341 pieces.append(l)
341 pieces.append(l)
342
342
343 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
343 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
344 fm.startitem()
344 fm.startitem()
345 fm.write(fields, "".join(f), *p)
345 fm.write(fields, "".join(f), *p)
346 fm.write('line', ": %s", l[1])
346 fm.write('line', ": %s", l[1])
347
347
348 if lines and not lines[-1][1].endswith('\n'):
348 if lines and not lines[-1][1].endswith('\n'):
349 fm.plain('\n')
349 fm.plain('\n')
350
350
351 fm.end()
351 fm.end()
352
352
353 @command('archive',
353 @command('archive',
354 [('', 'no-decode', None, _('do not pass files through decoders')),
354 [('', 'no-decode', None, _('do not pass files through decoders')),
355 ('p', 'prefix', '', _('directory prefix for files in archive'),
355 ('p', 'prefix', '', _('directory prefix for files in archive'),
356 _('PREFIX')),
356 _('PREFIX')),
357 ('r', 'rev', '', _('revision to distribute'), _('REV')),
357 ('r', 'rev', '', _('revision to distribute'), _('REV')),
358 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
358 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
359 ] + subrepoopts + walkopts,
359 ] + subrepoopts + walkopts,
360 _('[OPTION]... DEST'))
360 _('[OPTION]... DEST'))
361 def archive(ui, repo, dest, **opts):
361 def archive(ui, repo, dest, **opts):
362 '''create an unversioned archive of a repository revision
362 '''create an unversioned archive of a repository revision
363
363
364 By default, the revision used is the parent of the working
364 By default, the revision used is the parent of the working
365 directory; use -r/--rev to specify a different revision.
365 directory; use -r/--rev to specify a different revision.
366
366
367 The archive type is automatically detected based on file
367 The archive type is automatically detected based on file
368 extension (or override using -t/--type).
368 extension (or override using -t/--type).
369
369
370 .. container:: verbose
370 .. container:: verbose
371
371
372 Examples:
372 Examples:
373
373
374 - create a zip file containing the 1.0 release::
374 - create a zip file containing the 1.0 release::
375
375
376 hg archive -r 1.0 project-1.0.zip
376 hg archive -r 1.0 project-1.0.zip
377
377
378 - create a tarball excluding .hg files::
378 - create a tarball excluding .hg files::
379
379
380 hg archive project.tar.gz -X ".hg*"
380 hg archive project.tar.gz -X ".hg*"
381
381
382 Valid types are:
382 Valid types are:
383
383
384 :``files``: a directory full of files (default)
384 :``files``: a directory full of files (default)
385 :``tar``: tar archive, uncompressed
385 :``tar``: tar archive, uncompressed
386 :``tbz2``: tar archive, compressed using bzip2
386 :``tbz2``: tar archive, compressed using bzip2
387 :``tgz``: tar archive, compressed using gzip
387 :``tgz``: tar archive, compressed using gzip
388 :``uzip``: zip archive, uncompressed
388 :``uzip``: zip archive, uncompressed
389 :``zip``: zip archive, compressed using deflate
389 :``zip``: zip archive, compressed using deflate
390
390
391 The exact name of the destination archive or directory is given
391 The exact name of the destination archive or directory is given
392 using a format string; see :hg:`help export` for details.
392 using a format string; see :hg:`help export` for details.
393
393
394 Each member added to an archive file has a directory prefix
394 Each member added to an archive file has a directory prefix
395 prepended. Use -p/--prefix to specify a format string for the
395 prepended. Use -p/--prefix to specify a format string for the
396 prefix. The default is the basename of the archive, with suffixes
396 prefix. The default is the basename of the archive, with suffixes
397 removed.
397 removed.
398
398
399 Returns 0 on success.
399 Returns 0 on success.
400 '''
400 '''
401
401
402 ctx = scmutil.revsingle(repo, opts.get('rev'))
402 ctx = scmutil.revsingle(repo, opts.get('rev'))
403 if not ctx:
403 if not ctx:
404 raise util.Abort(_('no working directory: please specify a revision'))
404 raise util.Abort(_('no working directory: please specify a revision'))
405 node = ctx.node()
405 node = ctx.node()
406 dest = cmdutil.makefilename(repo, dest, node)
406 dest = cmdutil.makefilename(repo, dest, node)
407 if os.path.realpath(dest) == repo.root:
407 if os.path.realpath(dest) == repo.root:
408 raise util.Abort(_('repository root cannot be destination'))
408 raise util.Abort(_('repository root cannot be destination'))
409
409
410 kind = opts.get('type') or archival.guesskind(dest) or 'files'
410 kind = opts.get('type') or archival.guesskind(dest) or 'files'
411 prefix = opts.get('prefix')
411 prefix = opts.get('prefix')
412
412
413 if dest == '-':
413 if dest == '-':
414 if kind == 'files':
414 if kind == 'files':
415 raise util.Abort(_('cannot archive plain files to stdout'))
415 raise util.Abort(_('cannot archive plain files to stdout'))
416 dest = cmdutil.makefileobj(repo, dest)
416 dest = cmdutil.makefileobj(repo, dest)
417 if not prefix:
417 if not prefix:
418 prefix = os.path.basename(repo.root) + '-%h'
418 prefix = os.path.basename(repo.root) + '-%h'
419
419
420 prefix = cmdutil.makefilename(repo, prefix, node)
420 prefix = cmdutil.makefilename(repo, prefix, node)
421 matchfn = scmutil.match(ctx, [], opts)
421 matchfn = scmutil.match(ctx, [], opts)
422 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
422 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
423 matchfn, prefix, subrepos=opts.get('subrepos'))
423 matchfn, prefix, subrepos=opts.get('subrepos'))
424
424
425 @command('backout',
425 @command('backout',
426 [('', 'merge', None, _('merge with old dirstate parent after backout')),
426 [('', 'merge', None, _('merge with old dirstate parent after backout')),
427 ('', 'commit', None, _('commit if no conflicts were encountered')),
427 ('', 'commit', None, _('commit if no conflicts were encountered')),
428 ('', 'parent', '',
428 ('', 'parent', '',
429 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
429 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
430 ('r', 'rev', '', _('revision to backout'), _('REV')),
430 ('r', 'rev', '', _('revision to backout'), _('REV')),
431 ('e', 'edit', False, _('invoke editor on commit messages')),
431 ('e', 'edit', False, _('invoke editor on commit messages')),
432 ] + mergetoolopts + walkopts + commitopts + commitopts2,
432 ] + mergetoolopts + walkopts + commitopts + commitopts2,
433 _('[OPTION]... [-r] REV'))
433 _('[OPTION]... [-r] REV'))
434 def backout(ui, repo, node=None, rev=None, commit=False, **opts):
434 def backout(ui, repo, node=None, rev=None, commit=False, **opts):
435 '''reverse effect of earlier changeset
435 '''reverse effect of earlier changeset
436
436
437 Prepare a new changeset with the effect of REV undone in the
437 Prepare a new changeset with the effect of REV undone in the
438 current working directory.
438 current working directory.
439
439
440 If REV is the parent of the working directory, then this new changeset
440 If REV is the parent of the working directory, then this new changeset
441 is committed automatically. Otherwise, hg needs to merge the
441 is committed automatically. Otherwise, hg needs to merge the
442 changes and the merged result is left uncommitted.
442 changes and the merged result is left uncommitted.
443
443
444 .. note::
444 .. note::
445
445
446 backout cannot be used to fix either an unwanted or
446 backout cannot be used to fix either an unwanted or
447 incorrect merge.
447 incorrect merge.
448
448
449 .. container:: verbose
449 .. container:: verbose
450
450
451 By default, the pending changeset will have one parent,
451 By default, the pending changeset will have one parent,
452 maintaining a linear history. With --merge, the pending
452 maintaining a linear history. With --merge, the pending
453 changeset will instead have two parents: the old parent of the
453 changeset will instead have two parents: the old parent of the
454 working directory and a new child of REV that simply undoes REV.
454 working directory and a new child of REV that simply undoes REV.
455
455
456 Before version 1.7, the behavior without --merge was equivalent
456 Before version 1.7, the behavior without --merge was equivalent
457 to specifying --merge followed by :hg:`update --clean .` to
457 to specifying --merge followed by :hg:`update --clean .` to
458 cancel the merge and leave the child of REV as a head to be
458 cancel the merge and leave the child of REV as a head to be
459 merged separately.
459 merged separately.
460
460
461 See :hg:`help dates` for a list of formats valid for -d/--date.
461 See :hg:`help dates` for a list of formats valid for -d/--date.
462
462
463 Returns 0 on success, 1 if nothing to backout or there are unresolved
463 Returns 0 on success, 1 if nothing to backout or there are unresolved
464 files.
464 files.
465 '''
465 '''
466 if rev and node:
466 if rev and node:
467 raise util.Abort(_("please specify just one revision"))
467 raise util.Abort(_("please specify just one revision"))
468
468
469 if not rev:
469 if not rev:
470 rev = node
470 rev = node
471
471
472 if not rev:
472 if not rev:
473 raise util.Abort(_("please specify a revision to backout"))
473 raise util.Abort(_("please specify a revision to backout"))
474
474
475 date = opts.get('date')
475 date = opts.get('date')
476 if date:
476 if date:
477 opts['date'] = util.parsedate(date)
477 opts['date'] = util.parsedate(date)
478
478
479 cmdutil.checkunfinished(repo)
479 cmdutil.checkunfinished(repo)
480 cmdutil.bailifchanged(repo)
480 cmdutil.bailifchanged(repo)
481 node = scmutil.revsingle(repo, rev).node()
481 node = scmutil.revsingle(repo, rev).node()
482
482
483 op1, op2 = repo.dirstate.parents()
483 op1, op2 = repo.dirstate.parents()
484 if not repo.changelog.isancestor(node, op1):
484 if not repo.changelog.isancestor(node, op1):
485 raise util.Abort(_('cannot backout change that is not an ancestor'))
485 raise util.Abort(_('cannot backout change that is not an ancestor'))
486
486
487 p1, p2 = repo.changelog.parents(node)
487 p1, p2 = repo.changelog.parents(node)
488 if p1 == nullid:
488 if p1 == nullid:
489 raise util.Abort(_('cannot backout a change with no parents'))
489 raise util.Abort(_('cannot backout a change with no parents'))
490 if p2 != nullid:
490 if p2 != nullid:
491 if not opts.get('parent'):
491 if not opts.get('parent'):
492 raise util.Abort(_('cannot backout a merge changeset'))
492 raise util.Abort(_('cannot backout a merge changeset'))
493 p = repo.lookup(opts['parent'])
493 p = repo.lookup(opts['parent'])
494 if p not in (p1, p2):
494 if p not in (p1, p2):
495 raise util.Abort(_('%s is not a parent of %s') %
495 raise util.Abort(_('%s is not a parent of %s') %
496 (short(p), short(node)))
496 (short(p), short(node)))
497 parent = p
497 parent = p
498 else:
498 else:
499 if opts.get('parent'):
499 if opts.get('parent'):
500 raise util.Abort(_('cannot use --parent on non-merge changeset'))
500 raise util.Abort(_('cannot use --parent on non-merge changeset'))
501 parent = p1
501 parent = p1
502
502
503 # the backout should appear on the same branch
503 # the backout should appear on the same branch
504 wlock = repo.wlock()
504 wlock = repo.wlock()
505 try:
505 try:
506 branch = repo.dirstate.branch()
506 branch = repo.dirstate.branch()
507 bheads = repo.branchheads(branch)
507 bheads = repo.branchheads(branch)
508 rctx = scmutil.revsingle(repo, hex(parent))
508 rctx = scmutil.revsingle(repo, hex(parent))
509 if not opts.get('merge') and op1 != node:
509 if not opts.get('merge') and op1 != node:
510 try:
510 try:
511 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
511 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
512 'backout')
512 'backout')
513 repo.dirstate.beginparentchange()
513 repo.dirstate.beginparentchange()
514 stats = mergemod.update(repo, parent, True, True, False,
514 stats = mergemod.update(repo, parent, True, True, False,
515 node, False)
515 node, False)
516 repo.setparents(op1, op2)
516 repo.setparents(op1, op2)
517 repo.dirstate.endparentchange()
517 repo.dirstate.endparentchange()
518 hg._showstats(repo, stats)
518 hg._showstats(repo, stats)
519 if stats[3]:
519 if stats[3]:
520 repo.ui.status(_("use 'hg resolve' to retry unresolved "
520 repo.ui.status(_("use 'hg resolve' to retry unresolved "
521 "file merges\n"))
521 "file merges\n"))
522 return 1
522 return 1
523 elif not commit:
523 elif not commit:
524 msg = _("changeset %s backed out, "
524 msg = _("changeset %s backed out, "
525 "don't forget to commit.\n")
525 "don't forget to commit.\n")
526 ui.status(msg % short(node))
526 ui.status(msg % short(node))
527 return 0
527 return 0
528 finally:
528 finally:
529 ui.setconfig('ui', 'forcemerge', '', '')
529 ui.setconfig('ui', 'forcemerge', '', '')
530 else:
530 else:
531 hg.clean(repo, node, show_stats=False)
531 hg.clean(repo, node, show_stats=False)
532 repo.dirstate.setbranch(branch)
532 repo.dirstate.setbranch(branch)
533 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
533 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
534
534
535
535
536 def commitfunc(ui, repo, message, match, opts):
536 def commitfunc(ui, repo, message, match, opts):
537 editform = 'backout'
537 editform = 'backout'
538 e = cmdutil.getcommiteditor(editform=editform, **opts)
538 e = cmdutil.getcommiteditor(editform=editform, **opts)
539 if not message:
539 if not message:
540 # we don't translate commit messages
540 # we don't translate commit messages
541 message = "Backed out changeset %s" % short(node)
541 message = "Backed out changeset %s" % short(node)
542 e = cmdutil.getcommiteditor(edit=True, editform=editform)
542 e = cmdutil.getcommiteditor(edit=True, editform=editform)
543 return repo.commit(message, opts.get('user'), opts.get('date'),
543 return repo.commit(message, opts.get('user'), opts.get('date'),
544 match, editor=e)
544 match, editor=e)
545 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
545 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
546 if not newnode:
546 if not newnode:
547 ui.status(_("nothing changed\n"))
547 ui.status(_("nothing changed\n"))
548 return 1
548 return 1
549 cmdutil.commitstatus(repo, newnode, branch, bheads)
549 cmdutil.commitstatus(repo, newnode, branch, bheads)
550
550
551 def nice(node):
551 def nice(node):
552 return '%d:%s' % (repo.changelog.rev(node), short(node))
552 return '%d:%s' % (repo.changelog.rev(node), short(node))
553 ui.status(_('changeset %s backs out changeset %s\n') %
553 ui.status(_('changeset %s backs out changeset %s\n') %
554 (nice(repo.changelog.tip()), nice(node)))
554 (nice(repo.changelog.tip()), nice(node)))
555 if opts.get('merge') and op1 != node:
555 if opts.get('merge') and op1 != node:
556 hg.clean(repo, op1, show_stats=False)
556 hg.clean(repo, op1, show_stats=False)
557 ui.status(_('merging with changeset %s\n')
557 ui.status(_('merging with changeset %s\n')
558 % nice(repo.changelog.tip()))
558 % nice(repo.changelog.tip()))
559 try:
559 try:
560 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
560 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
561 'backout')
561 'backout')
562 return hg.merge(repo, hex(repo.changelog.tip()))
562 return hg.merge(repo, hex(repo.changelog.tip()))
563 finally:
563 finally:
564 ui.setconfig('ui', 'forcemerge', '', '')
564 ui.setconfig('ui', 'forcemerge', '', '')
565 finally:
565 finally:
566 wlock.release()
566 wlock.release()
567 return 0
567 return 0
568
568
569 @command('bisect',
569 @command('bisect',
570 [('r', 'reset', False, _('reset bisect state')),
570 [('r', 'reset', False, _('reset bisect state')),
571 ('g', 'good', False, _('mark changeset good')),
571 ('g', 'good', False, _('mark changeset good')),
572 ('b', 'bad', False, _('mark changeset bad')),
572 ('b', 'bad', False, _('mark changeset bad')),
573 ('s', 'skip', False, _('skip testing changeset')),
573 ('s', 'skip', False, _('skip testing changeset')),
574 ('e', 'extend', False, _('extend the bisect range')),
574 ('e', 'extend', False, _('extend the bisect range')),
575 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
575 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
576 ('U', 'noupdate', False, _('do not update to target'))],
576 ('U', 'noupdate', False, _('do not update to target'))],
577 _("[-gbsr] [-U] [-c CMD] [REV]"))
577 _("[-gbsr] [-U] [-c CMD] [REV]"))
578 def bisect(ui, repo, rev=None, extra=None, command=None,
578 def bisect(ui, repo, rev=None, extra=None, command=None,
579 reset=None, good=None, bad=None, skip=None, extend=None,
579 reset=None, good=None, bad=None, skip=None, extend=None,
580 noupdate=None):
580 noupdate=None):
581 """subdivision search of changesets
581 """subdivision search of changesets
582
582
583 This command helps to find changesets which introduce problems. To
583 This command helps to find changesets which introduce problems. To
584 use, mark the earliest changeset you know exhibits the problem as
584 use, mark the earliest changeset you know exhibits the problem as
585 bad, then mark the latest changeset which is free from the problem
585 bad, then mark the latest changeset which is free from the problem
586 as good. Bisect will update your working directory to a revision
586 as good. Bisect will update your working directory to a revision
587 for testing (unless the -U/--noupdate option is specified). Once
587 for testing (unless the -U/--noupdate option is specified). Once
588 you have performed tests, mark the working directory as good or
588 you have performed tests, mark the working directory as good or
589 bad, and bisect will either update to another candidate changeset
589 bad, and bisect will either update to another candidate changeset
590 or announce that it has found the bad revision.
590 or announce that it has found the bad revision.
591
591
592 As a shortcut, you can also use the revision argument to mark a
592 As a shortcut, you can also use the revision argument to mark a
593 revision as good or bad without checking it out first.
593 revision as good or bad without checking it out first.
594
594
595 If you supply a command, it will be used for automatic bisection.
595 If you supply a command, it will be used for automatic bisection.
596 The environment variable HG_NODE will contain the ID of the
596 The environment variable HG_NODE will contain the ID of the
597 changeset being tested. The exit status of the command will be
597 changeset being tested. The exit status of the command will be
598 used to mark revisions as good or bad: status 0 means good, 125
598 used to mark revisions as good or bad: status 0 means good, 125
599 means to skip the revision, 127 (command not found) will abort the
599 means to skip the revision, 127 (command not found) will abort the
600 bisection, and any other non-zero exit status means the revision
600 bisection, and any other non-zero exit status means the revision
601 is bad.
601 is bad.
602
602
603 .. container:: verbose
603 .. container:: verbose
604
604
605 Some examples:
605 Some examples:
606
606
607 - start a bisection with known bad revision 34, and good revision 12::
607 - start a bisection with known bad revision 34, and good revision 12::
608
608
609 hg bisect --bad 34
609 hg bisect --bad 34
610 hg bisect --good 12
610 hg bisect --good 12
611
611
612 - advance the current bisection by marking current revision as good or
612 - advance the current bisection by marking current revision as good or
613 bad::
613 bad::
614
614
615 hg bisect --good
615 hg bisect --good
616 hg bisect --bad
616 hg bisect --bad
617
617
618 - mark the current revision, or a known revision, to be skipped (e.g. if
618 - mark the current revision, or a known revision, to be skipped (e.g. if
619 that revision is not usable because of another issue)::
619 that revision is not usable because of another issue)::
620
620
621 hg bisect --skip
621 hg bisect --skip
622 hg bisect --skip 23
622 hg bisect --skip 23
623
623
624 - skip all revisions that do not touch directories ``foo`` or ``bar``::
624 - skip all revisions that do not touch directories ``foo`` or ``bar``::
625
625
626 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
626 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
627
627
628 - forget the current bisection::
628 - forget the current bisection::
629
629
630 hg bisect --reset
630 hg bisect --reset
631
631
632 - use 'make && make tests' to automatically find the first broken
632 - use 'make && make tests' to automatically find the first broken
633 revision::
633 revision::
634
634
635 hg bisect --reset
635 hg bisect --reset
636 hg bisect --bad 34
636 hg bisect --bad 34
637 hg bisect --good 12
637 hg bisect --good 12
638 hg bisect --command "make && make tests"
638 hg bisect --command "make && make tests"
639
639
640 - see all changesets whose states are already known in the current
640 - see all changesets whose states are already known in the current
641 bisection::
641 bisection::
642
642
643 hg log -r "bisect(pruned)"
643 hg log -r "bisect(pruned)"
644
644
645 - see the changeset currently being bisected (especially useful
645 - see the changeset currently being bisected (especially useful
646 if running with -U/--noupdate)::
646 if running with -U/--noupdate)::
647
647
648 hg log -r "bisect(current)"
648 hg log -r "bisect(current)"
649
649
650 - see all changesets that took part in the current bisection::
650 - see all changesets that took part in the current bisection::
651
651
652 hg log -r "bisect(range)"
652 hg log -r "bisect(range)"
653
653
654 - you can even get a nice graph::
654 - you can even get a nice graph::
655
655
656 hg log --graph -r "bisect(range)"
656 hg log --graph -r "bisect(range)"
657
657
658 See :hg:`help revsets` for more about the `bisect()` keyword.
658 See :hg:`help revsets` for more about the `bisect()` keyword.
659
659
660 Returns 0 on success.
660 Returns 0 on success.
661 """
661 """
662 def extendbisectrange(nodes, good):
662 def extendbisectrange(nodes, good):
663 # bisect is incomplete when it ends on a merge node and
663 # bisect is incomplete when it ends on a merge node and
664 # one of the parent was not checked.
664 # one of the parent was not checked.
665 parents = repo[nodes[0]].parents()
665 parents = repo[nodes[0]].parents()
666 if len(parents) > 1:
666 if len(parents) > 1:
667 side = good and state['bad'] or state['good']
667 side = good and state['bad'] or state['good']
668 num = len(set(i.node() for i in parents) & set(side))
668 num = len(set(i.node() for i in parents) & set(side))
669 if num == 1:
669 if num == 1:
670 return parents[0].ancestor(parents[1])
670 return parents[0].ancestor(parents[1])
671 return None
671 return None
672
672
673 def print_result(nodes, good):
673 def print_result(nodes, good):
674 displayer = cmdutil.show_changeset(ui, repo, {})
674 displayer = cmdutil.show_changeset(ui, repo, {})
675 if len(nodes) == 1:
675 if len(nodes) == 1:
676 # narrowed it down to a single revision
676 # narrowed it down to a single revision
677 if good:
677 if good:
678 ui.write(_("The first good revision is:\n"))
678 ui.write(_("The first good revision is:\n"))
679 else:
679 else:
680 ui.write(_("The first bad revision is:\n"))
680 ui.write(_("The first bad revision is:\n"))
681 displayer.show(repo[nodes[0]])
681 displayer.show(repo[nodes[0]])
682 extendnode = extendbisectrange(nodes, good)
682 extendnode = extendbisectrange(nodes, good)
683 if extendnode is not None:
683 if extendnode is not None:
684 ui.write(_('Not all ancestors of this changeset have been'
684 ui.write(_('Not all ancestors of this changeset have been'
685 ' checked.\nUse bisect --extend to continue the '
685 ' checked.\nUse bisect --extend to continue the '
686 'bisection from\nthe common ancestor, %s.\n')
686 'bisection from\nthe common ancestor, %s.\n')
687 % extendnode)
687 % extendnode)
688 else:
688 else:
689 # multiple possible revisions
689 # multiple possible revisions
690 if good:
690 if good:
691 ui.write(_("Due to skipped revisions, the first "
691 ui.write(_("Due to skipped revisions, the first "
692 "good revision could be any of:\n"))
692 "good revision could be any of:\n"))
693 else:
693 else:
694 ui.write(_("Due to skipped revisions, the first "
694 ui.write(_("Due to skipped revisions, the first "
695 "bad revision could be any of:\n"))
695 "bad revision could be any of:\n"))
696 for n in nodes:
696 for n in nodes:
697 displayer.show(repo[n])
697 displayer.show(repo[n])
698 displayer.close()
698 displayer.close()
699
699
700 def check_state(state, interactive=True):
700 def check_state(state, interactive=True):
701 if not state['good'] or not state['bad']:
701 if not state['good'] or not state['bad']:
702 if (good or bad or skip or reset) and interactive:
702 if (good or bad or skip or reset) and interactive:
703 return
703 return
704 if not state['good']:
704 if not state['good']:
705 raise util.Abort(_('cannot bisect (no known good revisions)'))
705 raise util.Abort(_('cannot bisect (no known good revisions)'))
706 else:
706 else:
707 raise util.Abort(_('cannot bisect (no known bad revisions)'))
707 raise util.Abort(_('cannot bisect (no known bad revisions)'))
708 return True
708 return True
709
709
710 # backward compatibility
710 # backward compatibility
711 if rev in "good bad reset init".split():
711 if rev in "good bad reset init".split():
712 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
712 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
713 cmd, rev, extra = rev, extra, None
713 cmd, rev, extra = rev, extra, None
714 if cmd == "good":
714 if cmd == "good":
715 good = True
715 good = True
716 elif cmd == "bad":
716 elif cmd == "bad":
717 bad = True
717 bad = True
718 else:
718 else:
719 reset = True
719 reset = True
720 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
720 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
721 raise util.Abort(_('incompatible arguments'))
721 raise util.Abort(_('incompatible arguments'))
722
722
723 cmdutil.checkunfinished(repo)
723 cmdutil.checkunfinished(repo)
724
724
725 if reset:
725 if reset:
726 p = repo.join("bisect.state")
726 p = repo.join("bisect.state")
727 if os.path.exists(p):
727 if os.path.exists(p):
728 os.unlink(p)
728 os.unlink(p)
729 return
729 return
730
730
731 state = hbisect.load_state(repo)
731 state = hbisect.load_state(repo)
732
732
733 if command:
733 if command:
734 changesets = 1
734 changesets = 1
735 if noupdate:
735 if noupdate:
736 try:
736 try:
737 node = state['current'][0]
737 node = state['current'][0]
738 except LookupError:
738 except LookupError:
739 raise util.Abort(_('current bisect revision is unknown - '
739 raise util.Abort(_('current bisect revision is unknown - '
740 'start a new bisect to fix'))
740 'start a new bisect to fix'))
741 else:
741 else:
742 node, p2 = repo.dirstate.parents()
742 node, p2 = repo.dirstate.parents()
743 if p2 != nullid:
743 if p2 != nullid:
744 raise util.Abort(_('current bisect revision is a merge'))
744 raise util.Abort(_('current bisect revision is a merge'))
745 try:
745 try:
746 while changesets:
746 while changesets:
747 # update state
747 # update state
748 state['current'] = [node]
748 state['current'] = [node]
749 hbisect.save_state(repo, state)
749 hbisect.save_state(repo, state)
750 status = ui.system(command, environ={'HG_NODE': hex(node)})
750 status = ui.system(command, environ={'HG_NODE': hex(node)})
751 if status == 125:
751 if status == 125:
752 transition = "skip"
752 transition = "skip"
753 elif status == 0:
753 elif status == 0:
754 transition = "good"
754 transition = "good"
755 # status < 0 means process was killed
755 # status < 0 means process was killed
756 elif status == 127:
756 elif status == 127:
757 raise util.Abort(_("failed to execute %s") % command)
757 raise util.Abort(_("failed to execute %s") % command)
758 elif status < 0:
758 elif status < 0:
759 raise util.Abort(_("%s killed") % command)
759 raise util.Abort(_("%s killed") % command)
760 else:
760 else:
761 transition = "bad"
761 transition = "bad"
762 ctx = scmutil.revsingle(repo, rev, node)
762 ctx = scmutil.revsingle(repo, rev, node)
763 rev = None # clear for future iterations
763 rev = None # clear for future iterations
764 state[transition].append(ctx.node())
764 state[transition].append(ctx.node())
765 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
765 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
766 check_state(state, interactive=False)
766 check_state(state, interactive=False)
767 # bisect
767 # bisect
768 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
768 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
769 # update to next check
769 # update to next check
770 node = nodes[0]
770 node = nodes[0]
771 if not noupdate:
771 if not noupdate:
772 cmdutil.bailifchanged(repo)
772 cmdutil.bailifchanged(repo)
773 hg.clean(repo, node, show_stats=False)
773 hg.clean(repo, node, show_stats=False)
774 finally:
774 finally:
775 state['current'] = [node]
775 state['current'] = [node]
776 hbisect.save_state(repo, state)
776 hbisect.save_state(repo, state)
777 print_result(nodes, bgood)
777 print_result(nodes, bgood)
778 return
778 return
779
779
780 # update state
780 # update state
781
781
782 if rev:
782 if rev:
783 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
783 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
784 else:
784 else:
785 nodes = [repo.lookup('.')]
785 nodes = [repo.lookup('.')]
786
786
787 if good or bad or skip:
787 if good or bad or skip:
788 if good:
788 if good:
789 state['good'] += nodes
789 state['good'] += nodes
790 elif bad:
790 elif bad:
791 state['bad'] += nodes
791 state['bad'] += nodes
792 elif skip:
792 elif skip:
793 state['skip'] += nodes
793 state['skip'] += nodes
794 hbisect.save_state(repo, state)
794 hbisect.save_state(repo, state)
795
795
796 if not check_state(state):
796 if not check_state(state):
797 return
797 return
798
798
799 # actually bisect
799 # actually bisect
800 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
800 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
801 if extend:
801 if extend:
802 if not changesets:
802 if not changesets:
803 extendnode = extendbisectrange(nodes, good)
803 extendnode = extendbisectrange(nodes, good)
804 if extendnode is not None:
804 if extendnode is not None:
805 ui.write(_("Extending search to changeset %d:%s\n")
805 ui.write(_("Extending search to changeset %d:%s\n")
806 % (extendnode.rev(), extendnode))
806 % (extendnode.rev(), extendnode))
807 state['current'] = [extendnode.node()]
807 state['current'] = [extendnode.node()]
808 hbisect.save_state(repo, state)
808 hbisect.save_state(repo, state)
809 if noupdate:
809 if noupdate:
810 return
810 return
811 cmdutil.bailifchanged(repo)
811 cmdutil.bailifchanged(repo)
812 return hg.clean(repo, extendnode.node())
812 return hg.clean(repo, extendnode.node())
813 raise util.Abort(_("nothing to extend"))
813 raise util.Abort(_("nothing to extend"))
814
814
815 if changesets == 0:
815 if changesets == 0:
816 print_result(nodes, good)
816 print_result(nodes, good)
817 else:
817 else:
818 assert len(nodes) == 1 # only a single node can be tested next
818 assert len(nodes) == 1 # only a single node can be tested next
819 node = nodes[0]
819 node = nodes[0]
820 # compute the approximate number of remaining tests
820 # compute the approximate number of remaining tests
821 tests, size = 0, 2
821 tests, size = 0, 2
822 while size <= changesets:
822 while size <= changesets:
823 tests, size = tests + 1, size * 2
823 tests, size = tests + 1, size * 2
824 rev = repo.changelog.rev(node)
824 rev = repo.changelog.rev(node)
825 ui.write(_("Testing changeset %d:%s "
825 ui.write(_("Testing changeset %d:%s "
826 "(%d changesets remaining, ~%d tests)\n")
826 "(%d changesets remaining, ~%d tests)\n")
827 % (rev, short(node), changesets, tests))
827 % (rev, short(node), changesets, tests))
828 state['current'] = [node]
828 state['current'] = [node]
829 hbisect.save_state(repo, state)
829 hbisect.save_state(repo, state)
830 if not noupdate:
830 if not noupdate:
831 cmdutil.bailifchanged(repo)
831 cmdutil.bailifchanged(repo)
832 return hg.clean(repo, node)
832 return hg.clean(repo, node)
833
833
834 @command('bookmarks|bookmark',
834 @command('bookmarks|bookmark',
835 [('f', 'force', False, _('force')),
835 [('f', 'force', False, _('force')),
836 ('r', 'rev', '', _('revision'), _('REV')),
836 ('r', 'rev', '', _('revision'), _('REV')),
837 ('d', 'delete', False, _('delete a given bookmark')),
837 ('d', 'delete', False, _('delete a given bookmark')),
838 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
838 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
839 ('i', 'inactive', False, _('mark a bookmark inactive')),
839 ('i', 'inactive', False, _('mark a bookmark inactive')),
840 ] + formatteropts,
840 ] + formatteropts,
841 _('hg bookmarks [OPTIONS]... [NAME]...'))
841 _('hg bookmarks [OPTIONS]... [NAME]...'))
842 def bookmark(ui, repo, *names, **opts):
842 def bookmark(ui, repo, *names, **opts):
843 '''create a new bookmark or list existing bookmarks
843 '''create a new bookmark or list existing bookmarks
844
844
845 Bookmarks are labels on changesets to help track lines of development.
845 Bookmarks are labels on changesets to help track lines of development.
846 Bookmarks are unversioned and can be moved, renamed and deleted.
846 Bookmarks are unversioned and can be moved, renamed and deleted.
847 Deleting or moving a bookmark has no effect on the associated changesets.
847 Deleting or moving a bookmark has no effect on the associated changesets.
848
848
849 Creating or updating to a bookmark causes it to be marked as 'active'.
849 Creating or updating to a bookmark causes it to be marked as 'active'.
850 The active bookmark is indicated with a '*'.
850 The active bookmark is indicated with a '*'.
851 When a commit is made, the active bookmark will advance to the new commit.
851 When a commit is made, the active bookmark will advance to the new commit.
852 A plain :hg:`update` will also advance an active bookmark, if possible.
852 A plain :hg:`update` will also advance an active bookmark, if possible.
853 Updating away from a bookmark will cause it to be deactivated.
853 Updating away from a bookmark will cause it to be deactivated.
854
854
855 Bookmarks can be pushed and pulled between repositories (see
855 Bookmarks can be pushed and pulled between repositories (see
856 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
856 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
857 diverged, a new 'divergent bookmark' of the form 'name@path' will
857 diverged, a new 'divergent bookmark' of the form 'name@path' will
858 be created. Using :hg:`merge` will resolve the divergence.
858 be created. Using :hg:`merge` will resolve the divergence.
859
859
860 A bookmark named '@' has the special property that :hg:`clone` will
860 A bookmark named '@' has the special property that :hg:`clone` will
861 check it out by default if it exists.
861 check it out by default if it exists.
862
862
863 .. container:: verbose
863 .. container:: verbose
864
864
865 Examples:
865 Examples:
866
866
867 - create an active bookmark for a new line of development::
867 - create an active bookmark for a new line of development::
868
868
869 hg book new-feature
869 hg book new-feature
870
870
871 - create an inactive bookmark as a place marker::
871 - create an inactive bookmark as a place marker::
872
872
873 hg book -i reviewed
873 hg book -i reviewed
874
874
875 - create an inactive bookmark on another changeset::
875 - create an inactive bookmark on another changeset::
876
876
877 hg book -r .^ tested
877 hg book -r .^ tested
878
878
879 - move the '@' bookmark from another branch::
879 - move the '@' bookmark from another branch::
880
880
881 hg book -f @
881 hg book -f @
882 '''
882 '''
883 force = opts.get('force')
883 force = opts.get('force')
884 rev = opts.get('rev')
884 rev = opts.get('rev')
885 delete = opts.get('delete')
885 delete = opts.get('delete')
886 rename = opts.get('rename')
886 rename = opts.get('rename')
887 inactive = opts.get('inactive')
887 inactive = opts.get('inactive')
888
888
889 def checkformat(mark):
889 def checkformat(mark):
890 mark = mark.strip()
890 mark = mark.strip()
891 if not mark:
891 if not mark:
892 raise util.Abort(_("bookmark names cannot consist entirely of "
892 raise util.Abort(_("bookmark names cannot consist entirely of "
893 "whitespace"))
893 "whitespace"))
894 scmutil.checknewlabel(repo, mark, 'bookmark')
894 scmutil.checknewlabel(repo, mark, 'bookmark')
895 return mark
895 return mark
896
896
897 def checkconflict(repo, mark, cur, force=False, target=None):
897 def checkconflict(repo, mark, cur, force=False, target=None):
898 if mark in marks and not force:
898 if mark in marks and not force:
899 if target:
899 if target:
900 if marks[mark] == target and target == cur:
900 if marks[mark] == target and target == cur:
901 # re-activating a bookmark
901 # re-activating a bookmark
902 return
902 return
903 anc = repo.changelog.ancestors([repo[target].rev()])
903 anc = repo.changelog.ancestors([repo[target].rev()])
904 bmctx = repo[marks[mark]]
904 bmctx = repo[marks[mark]]
905 divs = [repo[b].node() for b in marks
905 divs = [repo[b].node() for b in marks
906 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
906 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
907
907
908 # allow resolving a single divergent bookmark even if moving
908 # allow resolving a single divergent bookmark even if moving
909 # the bookmark across branches when a revision is specified
909 # the bookmark across branches when a revision is specified
910 # that contains a divergent bookmark
910 # that contains a divergent bookmark
911 if bmctx.rev() not in anc and target in divs:
911 if bmctx.rev() not in anc and target in divs:
912 bookmarks.deletedivergent(repo, [target], mark)
912 bookmarks.deletedivergent(repo, [target], mark)
913 return
913 return
914
914
915 deletefrom = [b for b in divs
915 deletefrom = [b for b in divs
916 if repo[b].rev() in anc or b == target]
916 if repo[b].rev() in anc or b == target]
917 bookmarks.deletedivergent(repo, deletefrom, mark)
917 bookmarks.deletedivergent(repo, deletefrom, mark)
918 if bookmarks.validdest(repo, bmctx, repo[target]):
918 if bookmarks.validdest(repo, bmctx, repo[target]):
919 ui.status(_("moving bookmark '%s' forward from %s\n") %
919 ui.status(_("moving bookmark '%s' forward from %s\n") %
920 (mark, short(bmctx.node())))
920 (mark, short(bmctx.node())))
921 return
921 return
922 raise util.Abort(_("bookmark '%s' already exists "
922 raise util.Abort(_("bookmark '%s' already exists "
923 "(use -f to force)") % mark)
923 "(use -f to force)") % mark)
924 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
924 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
925 and not force):
925 and not force):
926 raise util.Abort(
926 raise util.Abort(
927 _("a bookmark cannot have the name of an existing branch"))
927 _("a bookmark cannot have the name of an existing branch"))
928
928
929 if delete and rename:
929 if delete and rename:
930 raise util.Abort(_("--delete and --rename are incompatible"))
930 raise util.Abort(_("--delete and --rename are incompatible"))
931 if delete and rev:
931 if delete and rev:
932 raise util.Abort(_("--rev is incompatible with --delete"))
932 raise util.Abort(_("--rev is incompatible with --delete"))
933 if rename and rev:
933 if rename and rev:
934 raise util.Abort(_("--rev is incompatible with --rename"))
934 raise util.Abort(_("--rev is incompatible with --rename"))
935 if not names and (delete or rev):
935 if not names and (delete or rev):
936 raise util.Abort(_("bookmark name required"))
936 raise util.Abort(_("bookmark name required"))
937
937
938 if delete or rename or names or inactive:
938 if delete or rename or names or inactive:
939 wlock = repo.wlock()
939 wlock = repo.wlock()
940 try:
940 try:
941 cur = repo.changectx('.').node()
941 cur = repo.changectx('.').node()
942 marks = repo._bookmarks
942 marks = repo._bookmarks
943 if delete:
943 if delete:
944 for mark in names:
944 for mark in names:
945 if mark not in marks:
945 if mark not in marks:
946 raise util.Abort(_("bookmark '%s' does not exist") %
946 raise util.Abort(_("bookmark '%s' does not exist") %
947 mark)
947 mark)
948 if mark == repo._bookmarkcurrent:
948 if mark == repo._bookmarkcurrent:
949 bookmarks.unsetcurrent(repo)
949 bookmarks.unsetcurrent(repo)
950 del marks[mark]
950 del marks[mark]
951 marks.write()
951 marks.write()
952
952
953 elif rename:
953 elif rename:
954 if not names:
954 if not names:
955 raise util.Abort(_("new bookmark name required"))
955 raise util.Abort(_("new bookmark name required"))
956 elif len(names) > 1:
956 elif len(names) > 1:
957 raise util.Abort(_("only one new bookmark name allowed"))
957 raise util.Abort(_("only one new bookmark name allowed"))
958 mark = checkformat(names[0])
958 mark = checkformat(names[0])
959 if rename not in marks:
959 if rename not in marks:
960 raise util.Abort(_("bookmark '%s' does not exist") % rename)
960 raise util.Abort(_("bookmark '%s' does not exist") % rename)
961 checkconflict(repo, mark, cur, force)
961 checkconflict(repo, mark, cur, force)
962 marks[mark] = marks[rename]
962 marks[mark] = marks[rename]
963 if repo._bookmarkcurrent == rename and not inactive:
963 if repo._bookmarkcurrent == rename and not inactive:
964 bookmarks.setcurrent(repo, mark)
964 bookmarks.setcurrent(repo, mark)
965 del marks[rename]
965 del marks[rename]
966 marks.write()
966 marks.write()
967
967
968 elif names:
968 elif names:
969 newact = None
969 newact = None
970 for mark in names:
970 for mark in names:
971 mark = checkformat(mark)
971 mark = checkformat(mark)
972 if newact is None:
972 if newact is None:
973 newact = mark
973 newact = mark
974 if inactive and mark == repo._bookmarkcurrent:
974 if inactive and mark == repo._bookmarkcurrent:
975 bookmarks.unsetcurrent(repo)
975 bookmarks.unsetcurrent(repo)
976 return
976 return
977 tgt = cur
977 tgt = cur
978 if rev:
978 if rev:
979 tgt = scmutil.revsingle(repo, rev).node()
979 tgt = scmutil.revsingle(repo, rev).node()
980 checkconflict(repo, mark, cur, force, tgt)
980 checkconflict(repo, mark, cur, force, tgt)
981 marks[mark] = tgt
981 marks[mark] = tgt
982 if not inactive and cur == marks[newact] and not rev:
982 if not inactive and cur == marks[newact] and not rev:
983 bookmarks.setcurrent(repo, newact)
983 bookmarks.setcurrent(repo, newact)
984 elif cur != tgt and newact == repo._bookmarkcurrent:
984 elif cur != tgt and newact == repo._bookmarkcurrent:
985 bookmarks.unsetcurrent(repo)
985 bookmarks.unsetcurrent(repo)
986 marks.write()
986 marks.write()
987
987
988 elif inactive:
988 elif inactive:
989 if len(marks) == 0:
989 if len(marks) == 0:
990 ui.status(_("no bookmarks set\n"))
990 ui.status(_("no bookmarks set\n"))
991 elif not repo._bookmarkcurrent:
991 elif not repo._bookmarkcurrent:
992 ui.status(_("no active bookmark\n"))
992 ui.status(_("no active bookmark\n"))
993 else:
993 else:
994 bookmarks.unsetcurrent(repo)
994 bookmarks.unsetcurrent(repo)
995 finally:
995 finally:
996 wlock.release()
996 wlock.release()
997 else: # show bookmarks
997 else: # show bookmarks
998 fm = ui.formatter('bookmarks', opts)
998 fm = ui.formatter('bookmarks', opts)
999 hexfn = fm.hexfunc
999 hexfn = fm.hexfunc
1000 marks = repo._bookmarks
1000 marks = repo._bookmarks
1001 if len(marks) == 0 and not fm:
1001 if len(marks) == 0 and not fm:
1002 ui.status(_("no bookmarks set\n"))
1002 ui.status(_("no bookmarks set\n"))
1003 for bmark, n in sorted(marks.iteritems()):
1003 for bmark, n in sorted(marks.iteritems()):
1004 current = repo._bookmarkcurrent
1004 current = repo._bookmarkcurrent
1005 if bmark == current:
1005 if bmark == current:
1006 prefix, label = '*', 'bookmarks.current'
1006 prefix, label = '*', 'bookmarks.current'
1007 else:
1007 else:
1008 prefix, label = ' ', ''
1008 prefix, label = ' ', ''
1009
1009
1010 fm.startitem()
1010 fm.startitem()
1011 if not ui.quiet:
1011 if not ui.quiet:
1012 fm.plain(' %s ' % prefix, label=label)
1012 fm.plain(' %s ' % prefix, label=label)
1013 fm.write('bookmark', '%s', bmark, label=label)
1013 fm.write('bookmark', '%s', bmark, label=label)
1014 pad = " " * (25 - encoding.colwidth(bmark))
1014 pad = " " * (25 - encoding.colwidth(bmark))
1015 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1015 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1016 repo.changelog.rev(n), hexfn(n), label=label)
1016 repo.changelog.rev(n), hexfn(n), label=label)
1017 fm.data(active=(bmark == current))
1017 fm.data(active=(bmark == current))
1018 fm.plain('\n')
1018 fm.plain('\n')
1019 fm.end()
1019 fm.end()
1020
1020
1021 @command('branch',
1021 @command('branch',
1022 [('f', 'force', None,
1022 [('f', 'force', None,
1023 _('set branch name even if it shadows an existing branch')),
1023 _('set branch name even if it shadows an existing branch')),
1024 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1024 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1025 _('[-fC] [NAME]'))
1025 _('[-fC] [NAME]'))
1026 def branch(ui, repo, label=None, **opts):
1026 def branch(ui, repo, label=None, **opts):
1027 """set or show the current branch name
1027 """set or show the current branch name
1028
1028
1029 .. note::
1029 .. note::
1030
1030
1031 Branch names are permanent and global. Use :hg:`bookmark` to create a
1031 Branch names are permanent and global. Use :hg:`bookmark` to create a
1032 light-weight bookmark instead. See :hg:`help glossary` for more
1032 light-weight bookmark instead. See :hg:`help glossary` for more
1033 information about named branches and bookmarks.
1033 information about named branches and bookmarks.
1034
1034
1035 With no argument, show the current branch name. With one argument,
1035 With no argument, show the current branch name. With one argument,
1036 set the working directory branch name (the branch will not exist
1036 set the working directory branch name (the branch will not exist
1037 in the repository until the next commit). Standard practice
1037 in the repository until the next commit). Standard practice
1038 recommends that primary development take place on the 'default'
1038 recommends that primary development take place on the 'default'
1039 branch.
1039 branch.
1040
1040
1041 Unless -f/--force is specified, branch will not let you set a
1041 Unless -f/--force is specified, branch will not let you set a
1042 branch name that already exists.
1042 branch name that already exists.
1043
1043
1044 Use -C/--clean to reset the working directory branch to that of
1044 Use -C/--clean to reset the working directory branch to that of
1045 the parent of the working directory, negating a previous branch
1045 the parent of the working directory, negating a previous branch
1046 change.
1046 change.
1047
1047
1048 Use the command :hg:`update` to switch to an existing branch. Use
1048 Use the command :hg:`update` to switch to an existing branch. Use
1049 :hg:`commit --close-branch` to mark this branch as closed.
1049 :hg:`commit --close-branch` to mark this branch as closed.
1050
1050
1051 Returns 0 on success.
1051 Returns 0 on success.
1052 """
1052 """
1053 if label:
1053 if label:
1054 label = label.strip()
1054 label = label.strip()
1055
1055
1056 if not opts.get('clean') and not label:
1056 if not opts.get('clean') and not label:
1057 ui.write("%s\n" % repo.dirstate.branch())
1057 ui.write("%s\n" % repo.dirstate.branch())
1058 return
1058 return
1059
1059
1060 wlock = repo.wlock()
1060 wlock = repo.wlock()
1061 try:
1061 try:
1062 if opts.get('clean'):
1062 if opts.get('clean'):
1063 label = repo[None].p1().branch()
1063 label = repo[None].p1().branch()
1064 repo.dirstate.setbranch(label)
1064 repo.dirstate.setbranch(label)
1065 ui.status(_('reset working directory to branch %s\n') % label)
1065 ui.status(_('reset working directory to branch %s\n') % label)
1066 elif label:
1066 elif label:
1067 if not opts.get('force') and label in repo.branchmap():
1067 if not opts.get('force') and label in repo.branchmap():
1068 if label not in [p.branch() for p in repo.parents()]:
1068 if label not in [p.branch() for p in repo.parents()]:
1069 raise util.Abort(_('a branch of the same name already'
1069 raise util.Abort(_('a branch of the same name already'
1070 ' exists'),
1070 ' exists'),
1071 # i18n: "it" refers to an existing branch
1071 # i18n: "it" refers to an existing branch
1072 hint=_("use 'hg update' to switch to it"))
1072 hint=_("use 'hg update' to switch to it"))
1073 scmutil.checknewlabel(repo, label, 'branch')
1073 scmutil.checknewlabel(repo, label, 'branch')
1074 repo.dirstate.setbranch(label)
1074 repo.dirstate.setbranch(label)
1075 ui.status(_('marked working directory as branch %s\n') % label)
1075 ui.status(_('marked working directory as branch %s\n') % label)
1076 ui.status(_('(branches are permanent and global, '
1076 ui.status(_('(branches are permanent and global, '
1077 'did you want a bookmark?)\n'))
1077 'did you want a bookmark?)\n'))
1078 finally:
1078 finally:
1079 wlock.release()
1079 wlock.release()
1080
1080
1081 @command('branches',
1081 @command('branches',
1082 [('a', 'active', False,
1082 [('a', 'active', False,
1083 _('show only branches that have unmerged heads (DEPRECATED)')),
1083 _('show only branches that have unmerged heads (DEPRECATED)')),
1084 ('c', 'closed', False, _('show normal and closed branches')),
1084 ('c', 'closed', False, _('show normal and closed branches')),
1085 ] + formatteropts,
1085 ] + formatteropts,
1086 _('[-ac]'))
1086 _('[-ac]'))
1087 def branches(ui, repo, active=False, closed=False, **opts):
1087 def branches(ui, repo, active=False, closed=False, **opts):
1088 """list repository named branches
1088 """list repository named branches
1089
1089
1090 List the repository's named branches, indicating which ones are
1090 List the repository's named branches, indicating which ones are
1091 inactive. If -c/--closed is specified, also list branches which have
1091 inactive. If -c/--closed is specified, also list branches which have
1092 been marked closed (see :hg:`commit --close-branch`).
1092 been marked closed (see :hg:`commit --close-branch`).
1093
1093
1094 Use the command :hg:`update` to switch to an existing branch.
1094 Use the command :hg:`update` to switch to an existing branch.
1095
1095
1096 Returns 0.
1096 Returns 0.
1097 """
1097 """
1098
1098
1099 fm = ui.formatter('branches', opts)
1099 fm = ui.formatter('branches', opts)
1100 hexfunc = fm.hexfunc
1100 hexfunc = fm.hexfunc
1101
1101
1102 allheads = set(repo.heads())
1102 allheads = set(repo.heads())
1103 branches = []
1103 branches = []
1104 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1104 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1105 isactive = not isclosed and bool(set(heads) & allheads)
1105 isactive = not isclosed and bool(set(heads) & allheads)
1106 branches.append((tag, repo[tip], isactive, not isclosed))
1106 branches.append((tag, repo[tip], isactive, not isclosed))
1107 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1107 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1108 reverse=True)
1108 reverse=True)
1109
1109
1110 for tag, ctx, isactive, isopen in branches:
1110 for tag, ctx, isactive, isopen in branches:
1111 if active and not isactive:
1111 if active and not isactive:
1112 continue
1112 continue
1113 if isactive:
1113 if isactive:
1114 label = 'branches.active'
1114 label = 'branches.active'
1115 notice = ''
1115 notice = ''
1116 elif not isopen:
1116 elif not isopen:
1117 if not closed:
1117 if not closed:
1118 continue
1118 continue
1119 label = 'branches.closed'
1119 label = 'branches.closed'
1120 notice = _(' (closed)')
1120 notice = _(' (closed)')
1121 else:
1121 else:
1122 label = 'branches.inactive'
1122 label = 'branches.inactive'
1123 notice = _(' (inactive)')
1123 notice = _(' (inactive)')
1124 current = (tag == repo.dirstate.branch())
1124 current = (tag == repo.dirstate.branch())
1125 if current:
1125 if current:
1126 label = 'branches.current'
1126 label = 'branches.current'
1127
1127
1128 fm.startitem()
1128 fm.startitem()
1129 fm.write('branch', '%s', tag, label=label)
1129 fm.write('branch', '%s', tag, label=label)
1130 rev = ctx.rev()
1130 rev = ctx.rev()
1131 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1131 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1132 fmt = ' ' * padsize + ' %d:%s'
1132 fmt = ' ' * padsize + ' %d:%s'
1133 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1133 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1134 label='log.changeset changeset.%s' % ctx.phasestr())
1134 label='log.changeset changeset.%s' % ctx.phasestr())
1135 fm.data(active=isactive, closed=not isopen, current=current)
1135 fm.data(active=isactive, closed=not isopen, current=current)
1136 if not ui.quiet:
1136 if not ui.quiet:
1137 fm.plain(notice)
1137 fm.plain(notice)
1138 fm.plain('\n')
1138 fm.plain('\n')
1139 fm.end()
1139 fm.end()
1140
1140
1141 @command('bundle',
1141 @command('bundle',
1142 [('f', 'force', None, _('run even when the destination is unrelated')),
1142 [('f', 'force', None, _('run even when the destination is unrelated')),
1143 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1143 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1144 _('REV')),
1144 _('REV')),
1145 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1145 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1146 _('BRANCH')),
1146 _('BRANCH')),
1147 ('', 'base', [],
1147 ('', 'base', [],
1148 _('a base changeset assumed to be available at the destination'),
1148 _('a base changeset assumed to be available at the destination'),
1149 _('REV')),
1149 _('REV')),
1150 ('a', 'all', None, _('bundle all changesets in the repository')),
1150 ('a', 'all', None, _('bundle all changesets in the repository')),
1151 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1151 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1152 ] + remoteopts,
1152 ] + remoteopts,
1153 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1153 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1154 def bundle(ui, repo, fname, dest=None, **opts):
1154 def bundle(ui, repo, fname, dest=None, **opts):
1155 """create a changegroup file
1155 """create a changegroup file
1156
1156
1157 Generate a compressed changegroup file collecting changesets not
1157 Generate a compressed changegroup file collecting changesets not
1158 known to be in another repository.
1158 known to be in another repository.
1159
1159
1160 If you omit the destination repository, then hg assumes the
1160 If you omit the destination repository, then hg assumes the
1161 destination will have all the nodes you specify with --base
1161 destination will have all the nodes you specify with --base
1162 parameters. To create a bundle containing all changesets, use
1162 parameters. To create a bundle containing all changesets, use
1163 -a/--all (or --base null).
1163 -a/--all (or --base null).
1164
1164
1165 You can change compression method with the -t/--type option.
1165 You can change compression method with the -t/--type option.
1166 The available compression methods are: none, bzip2, and
1166 The available compression methods are: none, bzip2, and
1167 gzip (by default, bundles are compressed using bzip2).
1167 gzip (by default, bundles are compressed using bzip2).
1168
1168
1169 The bundle file can then be transferred using conventional means
1169 The bundle file can then be transferred using conventional means
1170 and applied to another repository with the unbundle or pull
1170 and applied to another repository with the unbundle or pull
1171 command. This is useful when direct push and pull are not
1171 command. This is useful when direct push and pull are not
1172 available or when exporting an entire repository is undesirable.
1172 available or when exporting an entire repository is undesirable.
1173
1173
1174 Applying bundles preserves all changeset contents including
1174 Applying bundles preserves all changeset contents including
1175 permissions, copy/rename information, and revision history.
1175 permissions, copy/rename information, and revision history.
1176
1176
1177 Returns 0 on success, 1 if no changes found.
1177 Returns 0 on success, 1 if no changes found.
1178 """
1178 """
1179 revs = None
1179 revs = None
1180 if 'rev' in opts:
1180 if 'rev' in opts:
1181 revs = scmutil.revrange(repo, opts['rev'])
1181 revs = scmutil.revrange(repo, opts['rev'])
1182
1182
1183 bundletype = opts.get('type', 'bzip2').lower()
1183 bundletype = opts.get('type', 'bzip2').lower()
1184 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1184 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1185 bundletype = btypes.get(bundletype)
1185 bundletype = btypes.get(bundletype)
1186 if bundletype not in changegroup.bundletypes:
1186 if bundletype not in changegroup.bundletypes:
1187 raise util.Abort(_('unknown bundle type specified with --type'))
1187 raise util.Abort(_('unknown bundle type specified with --type'))
1188
1188
1189 if opts.get('all'):
1189 if opts.get('all'):
1190 base = ['null']
1190 base = ['null']
1191 else:
1191 else:
1192 base = scmutil.revrange(repo, opts.get('base'))
1192 base = scmutil.revrange(repo, opts.get('base'))
1193 # TODO: get desired bundlecaps from command line.
1193 # TODO: get desired bundlecaps from command line.
1194 bundlecaps = None
1194 bundlecaps = None
1195 if base:
1195 if base:
1196 if dest:
1196 if dest:
1197 raise util.Abort(_("--base is incompatible with specifying "
1197 raise util.Abort(_("--base is incompatible with specifying "
1198 "a destination"))
1198 "a destination"))
1199 common = [repo.lookup(rev) for rev in base]
1199 common = [repo.lookup(rev) for rev in base]
1200 heads = revs and map(repo.lookup, revs) or revs
1200 heads = revs and map(repo.lookup, revs) or revs
1201 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1201 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1202 common=common, bundlecaps=bundlecaps)
1202 common=common, bundlecaps=bundlecaps)
1203 outgoing = None
1203 outgoing = None
1204 else:
1204 else:
1205 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1205 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1206 dest, branches = hg.parseurl(dest, opts.get('branch'))
1206 dest, branches = hg.parseurl(dest, opts.get('branch'))
1207 other = hg.peer(repo, opts, dest)
1207 other = hg.peer(repo, opts, dest)
1208 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1208 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1209 heads = revs and map(repo.lookup, revs) or revs
1209 heads = revs and map(repo.lookup, revs) or revs
1210 outgoing = discovery.findcommonoutgoing(repo, other,
1210 outgoing = discovery.findcommonoutgoing(repo, other,
1211 onlyheads=heads,
1211 onlyheads=heads,
1212 force=opts.get('force'),
1212 force=opts.get('force'),
1213 portable=True)
1213 portable=True)
1214 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1214 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1215 bundlecaps)
1215 bundlecaps)
1216 if not cg:
1216 if not cg:
1217 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1217 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1218 return 1
1218 return 1
1219
1219
1220 changegroup.writebundle(cg, fname, bundletype)
1220 changegroup.writebundle(ui, cg, fname, bundletype)
1221
1221
1222 @command('cat',
1222 @command('cat',
1223 [('o', 'output', '',
1223 [('o', 'output', '',
1224 _('print output to file with formatted name'), _('FORMAT')),
1224 _('print output to file with formatted name'), _('FORMAT')),
1225 ('r', 'rev', '', _('print the given revision'), _('REV')),
1225 ('r', 'rev', '', _('print the given revision'), _('REV')),
1226 ('', 'decode', None, _('apply any matching decode filter')),
1226 ('', 'decode', None, _('apply any matching decode filter')),
1227 ] + walkopts,
1227 ] + walkopts,
1228 _('[OPTION]... FILE...'),
1228 _('[OPTION]... FILE...'),
1229 inferrepo=True)
1229 inferrepo=True)
1230 def cat(ui, repo, file1, *pats, **opts):
1230 def cat(ui, repo, file1, *pats, **opts):
1231 """output the current or given revision of files
1231 """output the current or given revision of files
1232
1232
1233 Print the specified files as they were at the given revision. If
1233 Print the specified files as they were at the given revision. If
1234 no revision is given, the parent of the working directory is used.
1234 no revision is given, the parent of the working directory is used.
1235
1235
1236 Output may be to a file, in which case the name of the file is
1236 Output may be to a file, in which case the name of the file is
1237 given using a format string. The formatting rules as follows:
1237 given using a format string. The formatting rules as follows:
1238
1238
1239 :``%%``: literal "%" character
1239 :``%%``: literal "%" character
1240 :``%s``: basename of file being printed
1240 :``%s``: basename of file being printed
1241 :``%d``: dirname of file being printed, or '.' if in repository root
1241 :``%d``: dirname of file being printed, or '.' if in repository root
1242 :``%p``: root-relative path name of file being printed
1242 :``%p``: root-relative path name of file being printed
1243 :``%H``: changeset hash (40 hexadecimal digits)
1243 :``%H``: changeset hash (40 hexadecimal digits)
1244 :``%R``: changeset revision number
1244 :``%R``: changeset revision number
1245 :``%h``: short-form changeset hash (12 hexadecimal digits)
1245 :``%h``: short-form changeset hash (12 hexadecimal digits)
1246 :``%r``: zero-padded changeset revision number
1246 :``%r``: zero-padded changeset revision number
1247 :``%b``: basename of the exporting repository
1247 :``%b``: basename of the exporting repository
1248
1248
1249 Returns 0 on success.
1249 Returns 0 on success.
1250 """
1250 """
1251 ctx = scmutil.revsingle(repo, opts.get('rev'))
1251 ctx = scmutil.revsingle(repo, opts.get('rev'))
1252 m = scmutil.match(ctx, (file1,) + pats, opts)
1252 m = scmutil.match(ctx, (file1,) + pats, opts)
1253
1253
1254 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1254 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1255
1255
1256 @command('^clone',
1256 @command('^clone',
1257 [('U', 'noupdate', None,
1257 [('U', 'noupdate', None,
1258 _('the clone will include an empty working copy (only a repository)')),
1258 _('the clone will include an empty working copy (only a repository)')),
1259 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1259 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1260 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1260 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1261 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1261 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1262 ('', 'pull', None, _('use pull protocol to copy metadata')),
1262 ('', 'pull', None, _('use pull protocol to copy metadata')),
1263 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1263 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1264 ] + remoteopts,
1264 ] + remoteopts,
1265 _('[OPTION]... SOURCE [DEST]'),
1265 _('[OPTION]... SOURCE [DEST]'),
1266 norepo=True)
1266 norepo=True)
1267 def clone(ui, source, dest=None, **opts):
1267 def clone(ui, source, dest=None, **opts):
1268 """make a copy of an existing repository
1268 """make a copy of an existing repository
1269
1269
1270 Create a copy of an existing repository in a new directory.
1270 Create a copy of an existing repository in a new directory.
1271
1271
1272 If no destination directory name is specified, it defaults to the
1272 If no destination directory name is specified, it defaults to the
1273 basename of the source.
1273 basename of the source.
1274
1274
1275 The location of the source is added to the new repository's
1275 The location of the source is added to the new repository's
1276 ``.hg/hgrc`` file, as the default to be used for future pulls.
1276 ``.hg/hgrc`` file, as the default to be used for future pulls.
1277
1277
1278 Only local paths and ``ssh://`` URLs are supported as
1278 Only local paths and ``ssh://`` URLs are supported as
1279 destinations. For ``ssh://`` destinations, no working directory or
1279 destinations. For ``ssh://`` destinations, no working directory or
1280 ``.hg/hgrc`` will be created on the remote side.
1280 ``.hg/hgrc`` will be created on the remote side.
1281
1281
1282 To pull only a subset of changesets, specify one or more revisions
1282 To pull only a subset of changesets, specify one or more revisions
1283 identifiers with -r/--rev or branches with -b/--branch. The
1283 identifiers with -r/--rev or branches with -b/--branch. The
1284 resulting clone will contain only the specified changesets and
1284 resulting clone will contain only the specified changesets and
1285 their ancestors. These options (or 'clone src#rev dest') imply
1285 their ancestors. These options (or 'clone src#rev dest') imply
1286 --pull, even for local source repositories. Note that specifying a
1286 --pull, even for local source repositories. Note that specifying a
1287 tag will include the tagged changeset but not the changeset
1287 tag will include the tagged changeset but not the changeset
1288 containing the tag.
1288 containing the tag.
1289
1289
1290 If the source repository has a bookmark called '@' set, that
1290 If the source repository has a bookmark called '@' set, that
1291 revision will be checked out in the new repository by default.
1291 revision will be checked out in the new repository by default.
1292
1292
1293 To check out a particular version, use -u/--update, or
1293 To check out a particular version, use -u/--update, or
1294 -U/--noupdate to create a clone with no working directory.
1294 -U/--noupdate to create a clone with no working directory.
1295
1295
1296 .. container:: verbose
1296 .. container:: verbose
1297
1297
1298 For efficiency, hardlinks are used for cloning whenever the
1298 For efficiency, hardlinks are used for cloning whenever the
1299 source and destination are on the same filesystem (note this
1299 source and destination are on the same filesystem (note this
1300 applies only to the repository data, not to the working
1300 applies only to the repository data, not to the working
1301 directory). Some filesystems, such as AFS, implement hardlinking
1301 directory). Some filesystems, such as AFS, implement hardlinking
1302 incorrectly, but do not report errors. In these cases, use the
1302 incorrectly, but do not report errors. In these cases, use the
1303 --pull option to avoid hardlinking.
1303 --pull option to avoid hardlinking.
1304
1304
1305 In some cases, you can clone repositories and the working
1305 In some cases, you can clone repositories and the working
1306 directory using full hardlinks with ::
1306 directory using full hardlinks with ::
1307
1307
1308 $ cp -al REPO REPOCLONE
1308 $ cp -al REPO REPOCLONE
1309
1309
1310 This is the fastest way to clone, but it is not always safe. The
1310 This is the fastest way to clone, but it is not always safe. The
1311 operation is not atomic (making sure REPO is not modified during
1311 operation is not atomic (making sure REPO is not modified during
1312 the operation is up to you) and you have to make sure your
1312 the operation is up to you) and you have to make sure your
1313 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1313 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1314 so). Also, this is not compatible with certain extensions that
1314 so). Also, this is not compatible with certain extensions that
1315 place their metadata under the .hg directory, such as mq.
1315 place their metadata under the .hg directory, such as mq.
1316
1316
1317 Mercurial will update the working directory to the first applicable
1317 Mercurial will update the working directory to the first applicable
1318 revision from this list:
1318 revision from this list:
1319
1319
1320 a) null if -U or the source repository has no changesets
1320 a) null if -U or the source repository has no changesets
1321 b) if -u . and the source repository is local, the first parent of
1321 b) if -u . and the source repository is local, the first parent of
1322 the source repository's working directory
1322 the source repository's working directory
1323 c) the changeset specified with -u (if a branch name, this means the
1323 c) the changeset specified with -u (if a branch name, this means the
1324 latest head of that branch)
1324 latest head of that branch)
1325 d) the changeset specified with -r
1325 d) the changeset specified with -r
1326 e) the tipmost head specified with -b
1326 e) the tipmost head specified with -b
1327 f) the tipmost head specified with the url#branch source syntax
1327 f) the tipmost head specified with the url#branch source syntax
1328 g) the revision marked with the '@' bookmark, if present
1328 g) the revision marked with the '@' bookmark, if present
1329 h) the tipmost head of the default branch
1329 h) the tipmost head of the default branch
1330 i) tip
1330 i) tip
1331
1331
1332 Examples:
1332 Examples:
1333
1333
1334 - clone a remote repository to a new directory named hg/::
1334 - clone a remote repository to a new directory named hg/::
1335
1335
1336 hg clone http://selenic.com/hg
1336 hg clone http://selenic.com/hg
1337
1337
1338 - create a lightweight local clone::
1338 - create a lightweight local clone::
1339
1339
1340 hg clone project/ project-feature/
1340 hg clone project/ project-feature/
1341
1341
1342 - clone from an absolute path on an ssh server (note double-slash)::
1342 - clone from an absolute path on an ssh server (note double-slash)::
1343
1343
1344 hg clone ssh://user@server//home/projects/alpha/
1344 hg clone ssh://user@server//home/projects/alpha/
1345
1345
1346 - do a high-speed clone over a LAN while checking out a
1346 - do a high-speed clone over a LAN while checking out a
1347 specified version::
1347 specified version::
1348
1348
1349 hg clone --uncompressed http://server/repo -u 1.5
1349 hg clone --uncompressed http://server/repo -u 1.5
1350
1350
1351 - create a repository without changesets after a particular revision::
1351 - create a repository without changesets after a particular revision::
1352
1352
1353 hg clone -r 04e544 experimental/ good/
1353 hg clone -r 04e544 experimental/ good/
1354
1354
1355 - clone (and track) a particular named branch::
1355 - clone (and track) a particular named branch::
1356
1356
1357 hg clone http://selenic.com/hg#stable
1357 hg clone http://selenic.com/hg#stable
1358
1358
1359 See :hg:`help urls` for details on specifying URLs.
1359 See :hg:`help urls` for details on specifying URLs.
1360
1360
1361 Returns 0 on success.
1361 Returns 0 on success.
1362 """
1362 """
1363 if opts.get('noupdate') and opts.get('updaterev'):
1363 if opts.get('noupdate') and opts.get('updaterev'):
1364 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1364 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1365
1365
1366 r = hg.clone(ui, opts, source, dest,
1366 r = hg.clone(ui, opts, source, dest,
1367 pull=opts.get('pull'),
1367 pull=opts.get('pull'),
1368 stream=opts.get('uncompressed'),
1368 stream=opts.get('uncompressed'),
1369 rev=opts.get('rev'),
1369 rev=opts.get('rev'),
1370 update=opts.get('updaterev') or not opts.get('noupdate'),
1370 update=opts.get('updaterev') or not opts.get('noupdate'),
1371 branch=opts.get('branch'))
1371 branch=opts.get('branch'))
1372
1372
1373 return r is None
1373 return r is None
1374
1374
1375 @command('^commit|ci',
1375 @command('^commit|ci',
1376 [('A', 'addremove', None,
1376 [('A', 'addremove', None,
1377 _('mark new/missing files as added/removed before committing')),
1377 _('mark new/missing files as added/removed before committing')),
1378 ('', 'close-branch', None,
1378 ('', 'close-branch', None,
1379 _('mark a branch as closed, hiding it from the branch list')),
1379 _('mark a branch as closed, hiding it from the branch list')),
1380 ('', 'amend', None, _('amend the parent of the working dir')),
1380 ('', 'amend', None, _('amend the parent of the working dir')),
1381 ('s', 'secret', None, _('use the secret phase for committing')),
1381 ('s', 'secret', None, _('use the secret phase for committing')),
1382 ('e', 'edit', None, _('invoke editor on commit messages')),
1382 ('e', 'edit', None, _('invoke editor on commit messages')),
1383 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1383 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1384 _('[OPTION]... [FILE]...'),
1384 _('[OPTION]... [FILE]...'),
1385 inferrepo=True)
1385 inferrepo=True)
1386 def commit(ui, repo, *pats, **opts):
1386 def commit(ui, repo, *pats, **opts):
1387 """commit the specified files or all outstanding changes
1387 """commit the specified files or all outstanding changes
1388
1388
1389 Commit changes to the given files into the repository. Unlike a
1389 Commit changes to the given files into the repository. Unlike a
1390 centralized SCM, this operation is a local operation. See
1390 centralized SCM, this operation is a local operation. See
1391 :hg:`push` for a way to actively distribute your changes.
1391 :hg:`push` for a way to actively distribute your changes.
1392
1392
1393 If a list of files is omitted, all changes reported by :hg:`status`
1393 If a list of files is omitted, all changes reported by :hg:`status`
1394 will be committed.
1394 will be committed.
1395
1395
1396 If you are committing the result of a merge, do not provide any
1396 If you are committing the result of a merge, do not provide any
1397 filenames or -I/-X filters.
1397 filenames or -I/-X filters.
1398
1398
1399 If no commit message is specified, Mercurial starts your
1399 If no commit message is specified, Mercurial starts your
1400 configured editor where you can enter a message. In case your
1400 configured editor where you can enter a message. In case your
1401 commit fails, you will find a backup of your message in
1401 commit fails, you will find a backup of your message in
1402 ``.hg/last-message.txt``.
1402 ``.hg/last-message.txt``.
1403
1403
1404 The --amend flag can be used to amend the parent of the
1404 The --amend flag can be used to amend the parent of the
1405 working directory with a new commit that contains the changes
1405 working directory with a new commit that contains the changes
1406 in the parent in addition to those currently reported by :hg:`status`,
1406 in the parent in addition to those currently reported by :hg:`status`,
1407 if there are any. The old commit is stored in a backup bundle in
1407 if there are any. The old commit is stored in a backup bundle in
1408 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1408 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1409 on how to restore it).
1409 on how to restore it).
1410
1410
1411 Message, user and date are taken from the amended commit unless
1411 Message, user and date are taken from the amended commit unless
1412 specified. When a message isn't specified on the command line,
1412 specified. When a message isn't specified on the command line,
1413 the editor will open with the message of the amended commit.
1413 the editor will open with the message of the amended commit.
1414
1414
1415 It is not possible to amend public changesets (see :hg:`help phases`)
1415 It is not possible to amend public changesets (see :hg:`help phases`)
1416 or changesets that have children.
1416 or changesets that have children.
1417
1417
1418 See :hg:`help dates` for a list of formats valid for -d/--date.
1418 See :hg:`help dates` for a list of formats valid for -d/--date.
1419
1419
1420 Returns 0 on success, 1 if nothing changed.
1420 Returns 0 on success, 1 if nothing changed.
1421 """
1421 """
1422 if opts.get('subrepos'):
1422 if opts.get('subrepos'):
1423 if opts.get('amend'):
1423 if opts.get('amend'):
1424 raise util.Abort(_('cannot amend with --subrepos'))
1424 raise util.Abort(_('cannot amend with --subrepos'))
1425 # Let --subrepos on the command line override config setting.
1425 # Let --subrepos on the command line override config setting.
1426 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1426 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1427
1427
1428 cmdutil.checkunfinished(repo, commit=True)
1428 cmdutil.checkunfinished(repo, commit=True)
1429
1429
1430 branch = repo[None].branch()
1430 branch = repo[None].branch()
1431 bheads = repo.branchheads(branch)
1431 bheads = repo.branchheads(branch)
1432
1432
1433 extra = {}
1433 extra = {}
1434 if opts.get('close_branch'):
1434 if opts.get('close_branch'):
1435 extra['close'] = 1
1435 extra['close'] = 1
1436
1436
1437 if not bheads:
1437 if not bheads:
1438 raise util.Abort(_('can only close branch heads'))
1438 raise util.Abort(_('can only close branch heads'))
1439 elif opts.get('amend'):
1439 elif opts.get('amend'):
1440 if repo.parents()[0].p1().branch() != branch and \
1440 if repo.parents()[0].p1().branch() != branch and \
1441 repo.parents()[0].p2().branch() != branch:
1441 repo.parents()[0].p2().branch() != branch:
1442 raise util.Abort(_('can only close branch heads'))
1442 raise util.Abort(_('can only close branch heads'))
1443
1443
1444 if opts.get('amend'):
1444 if opts.get('amend'):
1445 if ui.configbool('ui', 'commitsubrepos'):
1445 if ui.configbool('ui', 'commitsubrepos'):
1446 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1446 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1447
1447
1448 old = repo['.']
1448 old = repo['.']
1449 if not old.mutable():
1449 if not old.mutable():
1450 raise util.Abort(_('cannot amend public changesets'))
1450 raise util.Abort(_('cannot amend public changesets'))
1451 if len(repo[None].parents()) > 1:
1451 if len(repo[None].parents()) > 1:
1452 raise util.Abort(_('cannot amend while merging'))
1452 raise util.Abort(_('cannot amend while merging'))
1453 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1453 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1454 if not allowunstable and old.children():
1454 if not allowunstable and old.children():
1455 raise util.Abort(_('cannot amend changeset with children'))
1455 raise util.Abort(_('cannot amend changeset with children'))
1456
1456
1457 # commitfunc is used only for temporary amend commit by cmdutil.amend
1457 # commitfunc is used only for temporary amend commit by cmdutil.amend
1458 def commitfunc(ui, repo, message, match, opts):
1458 def commitfunc(ui, repo, message, match, opts):
1459 return repo.commit(message,
1459 return repo.commit(message,
1460 opts.get('user') or old.user(),
1460 opts.get('user') or old.user(),
1461 opts.get('date') or old.date(),
1461 opts.get('date') or old.date(),
1462 match,
1462 match,
1463 extra=extra)
1463 extra=extra)
1464
1464
1465 current = repo._bookmarkcurrent
1465 current = repo._bookmarkcurrent
1466 marks = old.bookmarks()
1466 marks = old.bookmarks()
1467 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1467 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1468 if node == old.node():
1468 if node == old.node():
1469 ui.status(_("nothing changed\n"))
1469 ui.status(_("nothing changed\n"))
1470 return 1
1470 return 1
1471 elif marks:
1471 elif marks:
1472 ui.debug('moving bookmarks %r from %s to %s\n' %
1472 ui.debug('moving bookmarks %r from %s to %s\n' %
1473 (marks, old.hex(), hex(node)))
1473 (marks, old.hex(), hex(node)))
1474 newmarks = repo._bookmarks
1474 newmarks = repo._bookmarks
1475 for bm in marks:
1475 for bm in marks:
1476 newmarks[bm] = node
1476 newmarks[bm] = node
1477 if bm == current:
1477 if bm == current:
1478 bookmarks.setcurrent(repo, bm)
1478 bookmarks.setcurrent(repo, bm)
1479 newmarks.write()
1479 newmarks.write()
1480 else:
1480 else:
1481 def commitfunc(ui, repo, message, match, opts):
1481 def commitfunc(ui, repo, message, match, opts):
1482 backup = ui.backupconfig('phases', 'new-commit')
1482 backup = ui.backupconfig('phases', 'new-commit')
1483 baseui = repo.baseui
1483 baseui = repo.baseui
1484 basebackup = baseui.backupconfig('phases', 'new-commit')
1484 basebackup = baseui.backupconfig('phases', 'new-commit')
1485 try:
1485 try:
1486 if opts.get('secret'):
1486 if opts.get('secret'):
1487 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1487 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1488 # Propagate to subrepos
1488 # Propagate to subrepos
1489 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1489 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1490
1490
1491 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1491 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1492 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1492 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1493 return repo.commit(message, opts.get('user'), opts.get('date'),
1493 return repo.commit(message, opts.get('user'), opts.get('date'),
1494 match,
1494 match,
1495 editor=editor,
1495 editor=editor,
1496 extra=extra)
1496 extra=extra)
1497 finally:
1497 finally:
1498 ui.restoreconfig(backup)
1498 ui.restoreconfig(backup)
1499 repo.baseui.restoreconfig(basebackup)
1499 repo.baseui.restoreconfig(basebackup)
1500
1500
1501
1501
1502 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1502 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1503
1503
1504 if not node:
1504 if not node:
1505 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1505 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1506 if stat[3]:
1506 if stat[3]:
1507 ui.status(_("nothing changed (%d missing files, see "
1507 ui.status(_("nothing changed (%d missing files, see "
1508 "'hg status')\n") % len(stat[3]))
1508 "'hg status')\n") % len(stat[3]))
1509 else:
1509 else:
1510 ui.status(_("nothing changed\n"))
1510 ui.status(_("nothing changed\n"))
1511 return 1
1511 return 1
1512
1512
1513 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1513 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1514
1514
1515 @command('config|showconfig|debugconfig',
1515 @command('config|showconfig|debugconfig',
1516 [('u', 'untrusted', None, _('show untrusted configuration options')),
1516 [('u', 'untrusted', None, _('show untrusted configuration options')),
1517 ('e', 'edit', None, _('edit user config')),
1517 ('e', 'edit', None, _('edit user config')),
1518 ('l', 'local', None, _('edit repository config')),
1518 ('l', 'local', None, _('edit repository config')),
1519 ('g', 'global', None, _('edit global config'))],
1519 ('g', 'global', None, _('edit global config'))],
1520 _('[-u] [NAME]...'),
1520 _('[-u] [NAME]...'),
1521 optionalrepo=True)
1521 optionalrepo=True)
1522 def config(ui, repo, *values, **opts):
1522 def config(ui, repo, *values, **opts):
1523 """show combined config settings from all hgrc files
1523 """show combined config settings from all hgrc files
1524
1524
1525 With no arguments, print names and values of all config items.
1525 With no arguments, print names and values of all config items.
1526
1526
1527 With one argument of the form section.name, print just the value
1527 With one argument of the form section.name, print just the value
1528 of that config item.
1528 of that config item.
1529
1529
1530 With multiple arguments, print names and values of all config
1530 With multiple arguments, print names and values of all config
1531 items with matching section names.
1531 items with matching section names.
1532
1532
1533 With --edit, start an editor on the user-level config file. With
1533 With --edit, start an editor on the user-level config file. With
1534 --global, edit the system-wide config file. With --local, edit the
1534 --global, edit the system-wide config file. With --local, edit the
1535 repository-level config file.
1535 repository-level config file.
1536
1536
1537 With --debug, the source (filename and line number) is printed
1537 With --debug, the source (filename and line number) is printed
1538 for each config item.
1538 for each config item.
1539
1539
1540 See :hg:`help config` for more information about config files.
1540 See :hg:`help config` for more information about config files.
1541
1541
1542 Returns 0 on success, 1 if NAME does not exist.
1542 Returns 0 on success, 1 if NAME does not exist.
1543
1543
1544 """
1544 """
1545
1545
1546 if opts.get('edit') or opts.get('local') or opts.get('global'):
1546 if opts.get('edit') or opts.get('local') or opts.get('global'):
1547 if opts.get('local') and opts.get('global'):
1547 if opts.get('local') and opts.get('global'):
1548 raise util.Abort(_("can't use --local and --global together"))
1548 raise util.Abort(_("can't use --local and --global together"))
1549
1549
1550 if opts.get('local'):
1550 if opts.get('local'):
1551 if not repo:
1551 if not repo:
1552 raise util.Abort(_("can't use --local outside a repository"))
1552 raise util.Abort(_("can't use --local outside a repository"))
1553 paths = [repo.join('hgrc')]
1553 paths = [repo.join('hgrc')]
1554 elif opts.get('global'):
1554 elif opts.get('global'):
1555 paths = scmutil.systemrcpath()
1555 paths = scmutil.systemrcpath()
1556 else:
1556 else:
1557 paths = scmutil.userrcpath()
1557 paths = scmutil.userrcpath()
1558
1558
1559 for f in paths:
1559 for f in paths:
1560 if os.path.exists(f):
1560 if os.path.exists(f):
1561 break
1561 break
1562 else:
1562 else:
1563 if opts.get('global'):
1563 if opts.get('global'):
1564 samplehgrc = uimod.samplehgrcs['global']
1564 samplehgrc = uimod.samplehgrcs['global']
1565 elif opts.get('local'):
1565 elif opts.get('local'):
1566 samplehgrc = uimod.samplehgrcs['local']
1566 samplehgrc = uimod.samplehgrcs['local']
1567 else:
1567 else:
1568 samplehgrc = uimod.samplehgrcs['user']
1568 samplehgrc = uimod.samplehgrcs['user']
1569
1569
1570 f = paths[0]
1570 f = paths[0]
1571 fp = open(f, "w")
1571 fp = open(f, "w")
1572 fp.write(samplehgrc)
1572 fp.write(samplehgrc)
1573 fp.close()
1573 fp.close()
1574
1574
1575 editor = ui.geteditor()
1575 editor = ui.geteditor()
1576 ui.system("%s \"%s\"" % (editor, f),
1576 ui.system("%s \"%s\"" % (editor, f),
1577 onerr=util.Abort, errprefix=_("edit failed"))
1577 onerr=util.Abort, errprefix=_("edit failed"))
1578 return
1578 return
1579
1579
1580 for f in scmutil.rcpath():
1580 for f in scmutil.rcpath():
1581 ui.debug('read config from: %s\n' % f)
1581 ui.debug('read config from: %s\n' % f)
1582 untrusted = bool(opts.get('untrusted'))
1582 untrusted = bool(opts.get('untrusted'))
1583 if values:
1583 if values:
1584 sections = [v for v in values if '.' not in v]
1584 sections = [v for v in values if '.' not in v]
1585 items = [v for v in values if '.' in v]
1585 items = [v for v in values if '.' in v]
1586 if len(items) > 1 or items and sections:
1586 if len(items) > 1 or items and sections:
1587 raise util.Abort(_('only one config item permitted'))
1587 raise util.Abort(_('only one config item permitted'))
1588 matched = False
1588 matched = False
1589 for section, name, value in ui.walkconfig(untrusted=untrusted):
1589 for section, name, value in ui.walkconfig(untrusted=untrusted):
1590 value = str(value).replace('\n', '\\n')
1590 value = str(value).replace('\n', '\\n')
1591 sectname = section + '.' + name
1591 sectname = section + '.' + name
1592 if values:
1592 if values:
1593 for v in values:
1593 for v in values:
1594 if v == section:
1594 if v == section:
1595 ui.debug('%s: ' %
1595 ui.debug('%s: ' %
1596 ui.configsource(section, name, untrusted))
1596 ui.configsource(section, name, untrusted))
1597 ui.write('%s=%s\n' % (sectname, value))
1597 ui.write('%s=%s\n' % (sectname, value))
1598 matched = True
1598 matched = True
1599 elif v == sectname:
1599 elif v == sectname:
1600 ui.debug('%s: ' %
1600 ui.debug('%s: ' %
1601 ui.configsource(section, name, untrusted))
1601 ui.configsource(section, name, untrusted))
1602 ui.write(value, '\n')
1602 ui.write(value, '\n')
1603 matched = True
1603 matched = True
1604 else:
1604 else:
1605 ui.debug('%s: ' %
1605 ui.debug('%s: ' %
1606 ui.configsource(section, name, untrusted))
1606 ui.configsource(section, name, untrusted))
1607 ui.write('%s=%s\n' % (sectname, value))
1607 ui.write('%s=%s\n' % (sectname, value))
1608 matched = True
1608 matched = True
1609 if matched:
1609 if matched:
1610 return 0
1610 return 0
1611 return 1
1611 return 1
1612
1612
1613 @command('copy|cp',
1613 @command('copy|cp',
1614 [('A', 'after', None, _('record a copy that has already occurred')),
1614 [('A', 'after', None, _('record a copy that has already occurred')),
1615 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1615 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1616 ] + walkopts + dryrunopts,
1616 ] + walkopts + dryrunopts,
1617 _('[OPTION]... [SOURCE]... DEST'))
1617 _('[OPTION]... [SOURCE]... DEST'))
1618 def copy(ui, repo, *pats, **opts):
1618 def copy(ui, repo, *pats, **opts):
1619 """mark files as copied for the next commit
1619 """mark files as copied for the next commit
1620
1620
1621 Mark dest as having copies of source files. If dest is a
1621 Mark dest as having copies of source files. If dest is a
1622 directory, copies are put in that directory. If dest is a file,
1622 directory, copies are put in that directory. If dest is a file,
1623 the source must be a single file.
1623 the source must be a single file.
1624
1624
1625 By default, this command copies the contents of files as they
1625 By default, this command copies the contents of files as they
1626 exist in the working directory. If invoked with -A/--after, the
1626 exist in the working directory. If invoked with -A/--after, the
1627 operation is recorded, but no copying is performed.
1627 operation is recorded, but no copying is performed.
1628
1628
1629 This command takes effect with the next commit. To undo a copy
1629 This command takes effect with the next commit. To undo a copy
1630 before that, see :hg:`revert`.
1630 before that, see :hg:`revert`.
1631
1631
1632 Returns 0 on success, 1 if errors are encountered.
1632 Returns 0 on success, 1 if errors are encountered.
1633 """
1633 """
1634 wlock = repo.wlock(False)
1634 wlock = repo.wlock(False)
1635 try:
1635 try:
1636 return cmdutil.copy(ui, repo, pats, opts)
1636 return cmdutil.copy(ui, repo, pats, opts)
1637 finally:
1637 finally:
1638 wlock.release()
1638 wlock.release()
1639
1639
1640 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1640 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1641 def debugancestor(ui, repo, *args):
1641 def debugancestor(ui, repo, *args):
1642 """find the ancestor revision of two revisions in a given index"""
1642 """find the ancestor revision of two revisions in a given index"""
1643 if len(args) == 3:
1643 if len(args) == 3:
1644 index, rev1, rev2 = args
1644 index, rev1, rev2 = args
1645 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1645 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1646 lookup = r.lookup
1646 lookup = r.lookup
1647 elif len(args) == 2:
1647 elif len(args) == 2:
1648 if not repo:
1648 if not repo:
1649 raise util.Abort(_("there is no Mercurial repository here "
1649 raise util.Abort(_("there is no Mercurial repository here "
1650 "(.hg not found)"))
1650 "(.hg not found)"))
1651 rev1, rev2 = args
1651 rev1, rev2 = args
1652 r = repo.changelog
1652 r = repo.changelog
1653 lookup = repo.lookup
1653 lookup = repo.lookup
1654 else:
1654 else:
1655 raise util.Abort(_('either two or three arguments required'))
1655 raise util.Abort(_('either two or three arguments required'))
1656 a = r.ancestor(lookup(rev1), lookup(rev2))
1656 a = r.ancestor(lookup(rev1), lookup(rev2))
1657 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1657 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1658
1658
1659 @command('debugbuilddag',
1659 @command('debugbuilddag',
1660 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1660 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1661 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1661 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1662 ('n', 'new-file', None, _('add new file at each rev'))],
1662 ('n', 'new-file', None, _('add new file at each rev'))],
1663 _('[OPTION]... [TEXT]'))
1663 _('[OPTION]... [TEXT]'))
1664 def debugbuilddag(ui, repo, text=None,
1664 def debugbuilddag(ui, repo, text=None,
1665 mergeable_file=False,
1665 mergeable_file=False,
1666 overwritten_file=False,
1666 overwritten_file=False,
1667 new_file=False):
1667 new_file=False):
1668 """builds a repo with a given DAG from scratch in the current empty repo
1668 """builds a repo with a given DAG from scratch in the current empty repo
1669
1669
1670 The description of the DAG is read from stdin if not given on the
1670 The description of the DAG is read from stdin if not given on the
1671 command line.
1671 command line.
1672
1672
1673 Elements:
1673 Elements:
1674
1674
1675 - "+n" is a linear run of n nodes based on the current default parent
1675 - "+n" is a linear run of n nodes based on the current default parent
1676 - "." is a single node based on the current default parent
1676 - "." is a single node based on the current default parent
1677 - "$" resets the default parent to null (implied at the start);
1677 - "$" resets the default parent to null (implied at the start);
1678 otherwise the default parent is always the last node created
1678 otherwise the default parent is always the last node created
1679 - "<p" sets the default parent to the backref p
1679 - "<p" sets the default parent to the backref p
1680 - "*p" is a fork at parent p, which is a backref
1680 - "*p" is a fork at parent p, which is a backref
1681 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1681 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1682 - "/p2" is a merge of the preceding node and p2
1682 - "/p2" is a merge of the preceding node and p2
1683 - ":tag" defines a local tag for the preceding node
1683 - ":tag" defines a local tag for the preceding node
1684 - "@branch" sets the named branch for subsequent nodes
1684 - "@branch" sets the named branch for subsequent nodes
1685 - "#...\\n" is a comment up to the end of the line
1685 - "#...\\n" is a comment up to the end of the line
1686
1686
1687 Whitespace between the above elements is ignored.
1687 Whitespace between the above elements is ignored.
1688
1688
1689 A backref is either
1689 A backref is either
1690
1690
1691 - a number n, which references the node curr-n, where curr is the current
1691 - a number n, which references the node curr-n, where curr is the current
1692 node, or
1692 node, or
1693 - the name of a local tag you placed earlier using ":tag", or
1693 - the name of a local tag you placed earlier using ":tag", or
1694 - empty to denote the default parent.
1694 - empty to denote the default parent.
1695
1695
1696 All string valued-elements are either strictly alphanumeric, or must
1696 All string valued-elements are either strictly alphanumeric, or must
1697 be enclosed in double quotes ("..."), with "\\" as escape character.
1697 be enclosed in double quotes ("..."), with "\\" as escape character.
1698 """
1698 """
1699
1699
1700 if text is None:
1700 if text is None:
1701 ui.status(_("reading DAG from stdin\n"))
1701 ui.status(_("reading DAG from stdin\n"))
1702 text = ui.fin.read()
1702 text = ui.fin.read()
1703
1703
1704 cl = repo.changelog
1704 cl = repo.changelog
1705 if len(cl) > 0:
1705 if len(cl) > 0:
1706 raise util.Abort(_('repository is not empty'))
1706 raise util.Abort(_('repository is not empty'))
1707
1707
1708 # determine number of revs in DAG
1708 # determine number of revs in DAG
1709 total = 0
1709 total = 0
1710 for type, data in dagparser.parsedag(text):
1710 for type, data in dagparser.parsedag(text):
1711 if type == 'n':
1711 if type == 'n':
1712 total += 1
1712 total += 1
1713
1713
1714 if mergeable_file:
1714 if mergeable_file:
1715 linesperrev = 2
1715 linesperrev = 2
1716 # make a file with k lines per rev
1716 # make a file with k lines per rev
1717 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1717 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1718 initialmergedlines.append("")
1718 initialmergedlines.append("")
1719
1719
1720 tags = []
1720 tags = []
1721
1721
1722 lock = tr = None
1722 lock = tr = None
1723 try:
1723 try:
1724 lock = repo.lock()
1724 lock = repo.lock()
1725 tr = repo.transaction("builddag")
1725 tr = repo.transaction("builddag")
1726
1726
1727 at = -1
1727 at = -1
1728 atbranch = 'default'
1728 atbranch = 'default'
1729 nodeids = []
1729 nodeids = []
1730 id = 0
1730 id = 0
1731 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1731 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1732 for type, data in dagparser.parsedag(text):
1732 for type, data in dagparser.parsedag(text):
1733 if type == 'n':
1733 if type == 'n':
1734 ui.note(('node %s\n' % str(data)))
1734 ui.note(('node %s\n' % str(data)))
1735 id, ps = data
1735 id, ps = data
1736
1736
1737 files = []
1737 files = []
1738 fctxs = {}
1738 fctxs = {}
1739
1739
1740 p2 = None
1740 p2 = None
1741 if mergeable_file:
1741 if mergeable_file:
1742 fn = "mf"
1742 fn = "mf"
1743 p1 = repo[ps[0]]
1743 p1 = repo[ps[0]]
1744 if len(ps) > 1:
1744 if len(ps) > 1:
1745 p2 = repo[ps[1]]
1745 p2 = repo[ps[1]]
1746 pa = p1.ancestor(p2)
1746 pa = p1.ancestor(p2)
1747 base, local, other = [x[fn].data() for x in (pa, p1,
1747 base, local, other = [x[fn].data() for x in (pa, p1,
1748 p2)]
1748 p2)]
1749 m3 = simplemerge.Merge3Text(base, local, other)
1749 m3 = simplemerge.Merge3Text(base, local, other)
1750 ml = [l.strip() for l in m3.merge_lines()]
1750 ml = [l.strip() for l in m3.merge_lines()]
1751 ml.append("")
1751 ml.append("")
1752 elif at > 0:
1752 elif at > 0:
1753 ml = p1[fn].data().split("\n")
1753 ml = p1[fn].data().split("\n")
1754 else:
1754 else:
1755 ml = initialmergedlines
1755 ml = initialmergedlines
1756 ml[id * linesperrev] += " r%i" % id
1756 ml[id * linesperrev] += " r%i" % id
1757 mergedtext = "\n".join(ml)
1757 mergedtext = "\n".join(ml)
1758 files.append(fn)
1758 files.append(fn)
1759 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1759 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1760
1760
1761 if overwritten_file:
1761 if overwritten_file:
1762 fn = "of"
1762 fn = "of"
1763 files.append(fn)
1763 files.append(fn)
1764 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1764 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1765
1765
1766 if new_file:
1766 if new_file:
1767 fn = "nf%i" % id
1767 fn = "nf%i" % id
1768 files.append(fn)
1768 files.append(fn)
1769 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1769 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1770 if len(ps) > 1:
1770 if len(ps) > 1:
1771 if not p2:
1771 if not p2:
1772 p2 = repo[ps[1]]
1772 p2 = repo[ps[1]]
1773 for fn in p2:
1773 for fn in p2:
1774 if fn.startswith("nf"):
1774 if fn.startswith("nf"):
1775 files.append(fn)
1775 files.append(fn)
1776 fctxs[fn] = p2[fn]
1776 fctxs[fn] = p2[fn]
1777
1777
1778 def fctxfn(repo, cx, path):
1778 def fctxfn(repo, cx, path):
1779 return fctxs.get(path)
1779 return fctxs.get(path)
1780
1780
1781 if len(ps) == 0 or ps[0] < 0:
1781 if len(ps) == 0 or ps[0] < 0:
1782 pars = [None, None]
1782 pars = [None, None]
1783 elif len(ps) == 1:
1783 elif len(ps) == 1:
1784 pars = [nodeids[ps[0]], None]
1784 pars = [nodeids[ps[0]], None]
1785 else:
1785 else:
1786 pars = [nodeids[p] for p in ps]
1786 pars = [nodeids[p] for p in ps]
1787 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1787 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1788 date=(id, 0),
1788 date=(id, 0),
1789 user="debugbuilddag",
1789 user="debugbuilddag",
1790 extra={'branch': atbranch})
1790 extra={'branch': atbranch})
1791 nodeid = repo.commitctx(cx)
1791 nodeid = repo.commitctx(cx)
1792 nodeids.append(nodeid)
1792 nodeids.append(nodeid)
1793 at = id
1793 at = id
1794 elif type == 'l':
1794 elif type == 'l':
1795 id, name = data
1795 id, name = data
1796 ui.note(('tag %s\n' % name))
1796 ui.note(('tag %s\n' % name))
1797 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1797 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1798 elif type == 'a':
1798 elif type == 'a':
1799 ui.note(('branch %s\n' % data))
1799 ui.note(('branch %s\n' % data))
1800 atbranch = data
1800 atbranch = data
1801 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1801 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1802 tr.close()
1802 tr.close()
1803
1803
1804 if tags:
1804 if tags:
1805 repo.vfs.write("localtags", "".join(tags))
1805 repo.vfs.write("localtags", "".join(tags))
1806 finally:
1806 finally:
1807 ui.progress(_('building'), None)
1807 ui.progress(_('building'), None)
1808 release(tr, lock)
1808 release(tr, lock)
1809
1809
1810 @command('debugbundle',
1810 @command('debugbundle',
1811 [('a', 'all', None, _('show all details'))],
1811 [('a', 'all', None, _('show all details'))],
1812 _('FILE'),
1812 _('FILE'),
1813 norepo=True)
1813 norepo=True)
1814 def debugbundle(ui, bundlepath, all=None, **opts):
1814 def debugbundle(ui, bundlepath, all=None, **opts):
1815 """lists the contents of a bundle"""
1815 """lists the contents of a bundle"""
1816 f = hg.openpath(ui, bundlepath)
1816 f = hg.openpath(ui, bundlepath)
1817 try:
1817 try:
1818 gen = exchange.readbundle(ui, f, bundlepath)
1818 gen = exchange.readbundle(ui, f, bundlepath)
1819 if isinstance(gen, bundle2.unbundle20):
1819 if isinstance(gen, bundle2.unbundle20):
1820 return _debugbundle2(ui, gen, all=all, **opts)
1820 return _debugbundle2(ui, gen, all=all, **opts)
1821 if all:
1821 if all:
1822 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1822 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1823
1823
1824 def showchunks(named):
1824 def showchunks(named):
1825 ui.write("\n%s\n" % named)
1825 ui.write("\n%s\n" % named)
1826 chain = None
1826 chain = None
1827 while True:
1827 while True:
1828 chunkdata = gen.deltachunk(chain)
1828 chunkdata = gen.deltachunk(chain)
1829 if not chunkdata:
1829 if not chunkdata:
1830 break
1830 break
1831 node = chunkdata['node']
1831 node = chunkdata['node']
1832 p1 = chunkdata['p1']
1832 p1 = chunkdata['p1']
1833 p2 = chunkdata['p2']
1833 p2 = chunkdata['p2']
1834 cs = chunkdata['cs']
1834 cs = chunkdata['cs']
1835 deltabase = chunkdata['deltabase']
1835 deltabase = chunkdata['deltabase']
1836 delta = chunkdata['delta']
1836 delta = chunkdata['delta']
1837 ui.write("%s %s %s %s %s %s\n" %
1837 ui.write("%s %s %s %s %s %s\n" %
1838 (hex(node), hex(p1), hex(p2),
1838 (hex(node), hex(p1), hex(p2),
1839 hex(cs), hex(deltabase), len(delta)))
1839 hex(cs), hex(deltabase), len(delta)))
1840 chain = node
1840 chain = node
1841
1841
1842 chunkdata = gen.changelogheader()
1842 chunkdata = gen.changelogheader()
1843 showchunks("changelog")
1843 showchunks("changelog")
1844 chunkdata = gen.manifestheader()
1844 chunkdata = gen.manifestheader()
1845 showchunks("manifest")
1845 showchunks("manifest")
1846 while True:
1846 while True:
1847 chunkdata = gen.filelogheader()
1847 chunkdata = gen.filelogheader()
1848 if not chunkdata:
1848 if not chunkdata:
1849 break
1849 break
1850 fname = chunkdata['filename']
1850 fname = chunkdata['filename']
1851 showchunks(fname)
1851 showchunks(fname)
1852 else:
1852 else:
1853 if isinstance(gen, bundle2.unbundle20):
1853 if isinstance(gen, bundle2.unbundle20):
1854 raise util.Abort(_('use debugbundle2 for this file'))
1854 raise util.Abort(_('use debugbundle2 for this file'))
1855 chunkdata = gen.changelogheader()
1855 chunkdata = gen.changelogheader()
1856 chain = None
1856 chain = None
1857 while True:
1857 while True:
1858 chunkdata = gen.deltachunk(chain)
1858 chunkdata = gen.deltachunk(chain)
1859 if not chunkdata:
1859 if not chunkdata:
1860 break
1860 break
1861 node = chunkdata['node']
1861 node = chunkdata['node']
1862 ui.write("%s\n" % hex(node))
1862 ui.write("%s\n" % hex(node))
1863 chain = node
1863 chain = node
1864 finally:
1864 finally:
1865 f.close()
1865 f.close()
1866
1866
1867 def _debugbundle2(ui, gen, **opts):
1867 def _debugbundle2(ui, gen, **opts):
1868 """lists the contents of a bundle2"""
1868 """lists the contents of a bundle2"""
1869 if not isinstance(gen, bundle2.unbundle20):
1869 if not isinstance(gen, bundle2.unbundle20):
1870 raise util.Abort(_('not a bundle2 file'))
1870 raise util.Abort(_('not a bundle2 file'))
1871 ui.write(('Stream params: %s\n' % repr(gen.params)))
1871 ui.write(('Stream params: %s\n' % repr(gen.params)))
1872 for part in gen.iterparts():
1872 for part in gen.iterparts():
1873 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
1873 ui.write('%s -- %r\n' % (part.type, repr(part.params)))
1874 if part.type == 'b2x:changegroup':
1874 if part.type == 'b2x:changegroup':
1875 version = part.params.get('version', '01')
1875 version = part.params.get('version', '01')
1876 cg = changegroup.packermap[version][1](part, 'UN')
1876 cg = changegroup.packermap[version][1](part, 'UN')
1877 chunkdata = cg.changelogheader()
1877 chunkdata = cg.changelogheader()
1878 chain = None
1878 chain = None
1879 while True:
1879 while True:
1880 chunkdata = cg.deltachunk(chain)
1880 chunkdata = cg.deltachunk(chain)
1881 if not chunkdata:
1881 if not chunkdata:
1882 break
1882 break
1883 node = chunkdata['node']
1883 node = chunkdata['node']
1884 ui.write(" %s\n" % hex(node))
1884 ui.write(" %s\n" % hex(node))
1885 chain = node
1885 chain = node
1886
1886
1887 @command('debugcheckstate', [], '')
1887 @command('debugcheckstate', [], '')
1888 def debugcheckstate(ui, repo):
1888 def debugcheckstate(ui, repo):
1889 """validate the correctness of the current dirstate"""
1889 """validate the correctness of the current dirstate"""
1890 parent1, parent2 = repo.dirstate.parents()
1890 parent1, parent2 = repo.dirstate.parents()
1891 m1 = repo[parent1].manifest()
1891 m1 = repo[parent1].manifest()
1892 m2 = repo[parent2].manifest()
1892 m2 = repo[parent2].manifest()
1893 errors = 0
1893 errors = 0
1894 for f in repo.dirstate:
1894 for f in repo.dirstate:
1895 state = repo.dirstate[f]
1895 state = repo.dirstate[f]
1896 if state in "nr" and f not in m1:
1896 if state in "nr" and f not in m1:
1897 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1897 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1898 errors += 1
1898 errors += 1
1899 if state in "a" and f in m1:
1899 if state in "a" and f in m1:
1900 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1900 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1901 errors += 1
1901 errors += 1
1902 if state in "m" and f not in m1 and f not in m2:
1902 if state in "m" and f not in m1 and f not in m2:
1903 ui.warn(_("%s in state %s, but not in either manifest\n") %
1903 ui.warn(_("%s in state %s, but not in either manifest\n") %
1904 (f, state))
1904 (f, state))
1905 errors += 1
1905 errors += 1
1906 for f in m1:
1906 for f in m1:
1907 state = repo.dirstate[f]
1907 state = repo.dirstate[f]
1908 if state not in "nrm":
1908 if state not in "nrm":
1909 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1909 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1910 errors += 1
1910 errors += 1
1911 if errors:
1911 if errors:
1912 error = _(".hg/dirstate inconsistent with current parent's manifest")
1912 error = _(".hg/dirstate inconsistent with current parent's manifest")
1913 raise util.Abort(error)
1913 raise util.Abort(error)
1914
1914
1915 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1915 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1916 def debugcommands(ui, cmd='', *args):
1916 def debugcommands(ui, cmd='', *args):
1917 """list all available commands and options"""
1917 """list all available commands and options"""
1918 for cmd, vals in sorted(table.iteritems()):
1918 for cmd, vals in sorted(table.iteritems()):
1919 cmd = cmd.split('|')[0].strip('^')
1919 cmd = cmd.split('|')[0].strip('^')
1920 opts = ', '.join([i[1] for i in vals[1]])
1920 opts = ', '.join([i[1] for i in vals[1]])
1921 ui.write('%s: %s\n' % (cmd, opts))
1921 ui.write('%s: %s\n' % (cmd, opts))
1922
1922
1923 @command('debugcomplete',
1923 @command('debugcomplete',
1924 [('o', 'options', None, _('show the command options'))],
1924 [('o', 'options', None, _('show the command options'))],
1925 _('[-o] CMD'),
1925 _('[-o] CMD'),
1926 norepo=True)
1926 norepo=True)
1927 def debugcomplete(ui, cmd='', **opts):
1927 def debugcomplete(ui, cmd='', **opts):
1928 """returns the completion list associated with the given command"""
1928 """returns the completion list associated with the given command"""
1929
1929
1930 if opts.get('options'):
1930 if opts.get('options'):
1931 options = []
1931 options = []
1932 otables = [globalopts]
1932 otables = [globalopts]
1933 if cmd:
1933 if cmd:
1934 aliases, entry = cmdutil.findcmd(cmd, table, False)
1934 aliases, entry = cmdutil.findcmd(cmd, table, False)
1935 otables.append(entry[1])
1935 otables.append(entry[1])
1936 for t in otables:
1936 for t in otables:
1937 for o in t:
1937 for o in t:
1938 if "(DEPRECATED)" in o[3]:
1938 if "(DEPRECATED)" in o[3]:
1939 continue
1939 continue
1940 if o[0]:
1940 if o[0]:
1941 options.append('-%s' % o[0])
1941 options.append('-%s' % o[0])
1942 options.append('--%s' % o[1])
1942 options.append('--%s' % o[1])
1943 ui.write("%s\n" % "\n".join(options))
1943 ui.write("%s\n" % "\n".join(options))
1944 return
1944 return
1945
1945
1946 cmdlist = cmdutil.findpossible(cmd, table)
1946 cmdlist = cmdutil.findpossible(cmd, table)
1947 if ui.verbose:
1947 if ui.verbose:
1948 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1948 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1949 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1949 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1950
1950
1951 @command('debugdag',
1951 @command('debugdag',
1952 [('t', 'tags', None, _('use tags as labels')),
1952 [('t', 'tags', None, _('use tags as labels')),
1953 ('b', 'branches', None, _('annotate with branch names')),
1953 ('b', 'branches', None, _('annotate with branch names')),
1954 ('', 'dots', None, _('use dots for runs')),
1954 ('', 'dots', None, _('use dots for runs')),
1955 ('s', 'spaces', None, _('separate elements by spaces'))],
1955 ('s', 'spaces', None, _('separate elements by spaces'))],
1956 _('[OPTION]... [FILE [REV]...]'),
1956 _('[OPTION]... [FILE [REV]...]'),
1957 optionalrepo=True)
1957 optionalrepo=True)
1958 def debugdag(ui, repo, file_=None, *revs, **opts):
1958 def debugdag(ui, repo, file_=None, *revs, **opts):
1959 """format the changelog or an index DAG as a concise textual description
1959 """format the changelog or an index DAG as a concise textual description
1960
1960
1961 If you pass a revlog index, the revlog's DAG is emitted. If you list
1961 If you pass a revlog index, the revlog's DAG is emitted. If you list
1962 revision numbers, they get labeled in the output as rN.
1962 revision numbers, they get labeled in the output as rN.
1963
1963
1964 Otherwise, the changelog DAG of the current repo is emitted.
1964 Otherwise, the changelog DAG of the current repo is emitted.
1965 """
1965 """
1966 spaces = opts.get('spaces')
1966 spaces = opts.get('spaces')
1967 dots = opts.get('dots')
1967 dots = opts.get('dots')
1968 if file_:
1968 if file_:
1969 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1969 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1970 revs = set((int(r) for r in revs))
1970 revs = set((int(r) for r in revs))
1971 def events():
1971 def events():
1972 for r in rlog:
1972 for r in rlog:
1973 yield 'n', (r, list(p for p in rlog.parentrevs(r)
1973 yield 'n', (r, list(p for p in rlog.parentrevs(r)
1974 if p != -1))
1974 if p != -1))
1975 if r in revs:
1975 if r in revs:
1976 yield 'l', (r, "r%i" % r)
1976 yield 'l', (r, "r%i" % r)
1977 elif repo:
1977 elif repo:
1978 cl = repo.changelog
1978 cl = repo.changelog
1979 tags = opts.get('tags')
1979 tags = opts.get('tags')
1980 branches = opts.get('branches')
1980 branches = opts.get('branches')
1981 if tags:
1981 if tags:
1982 labels = {}
1982 labels = {}
1983 for l, n in repo.tags().items():
1983 for l, n in repo.tags().items():
1984 labels.setdefault(cl.rev(n), []).append(l)
1984 labels.setdefault(cl.rev(n), []).append(l)
1985 def events():
1985 def events():
1986 b = "default"
1986 b = "default"
1987 for r in cl:
1987 for r in cl:
1988 if branches:
1988 if branches:
1989 newb = cl.read(cl.node(r))[5]['branch']
1989 newb = cl.read(cl.node(r))[5]['branch']
1990 if newb != b:
1990 if newb != b:
1991 yield 'a', newb
1991 yield 'a', newb
1992 b = newb
1992 b = newb
1993 yield 'n', (r, list(p for p in cl.parentrevs(r)
1993 yield 'n', (r, list(p for p in cl.parentrevs(r)
1994 if p != -1))
1994 if p != -1))
1995 if tags:
1995 if tags:
1996 ls = labels.get(r)
1996 ls = labels.get(r)
1997 if ls:
1997 if ls:
1998 for l in ls:
1998 for l in ls:
1999 yield 'l', (r, l)
1999 yield 'l', (r, l)
2000 else:
2000 else:
2001 raise util.Abort(_('need repo for changelog dag'))
2001 raise util.Abort(_('need repo for changelog dag'))
2002
2002
2003 for line in dagparser.dagtextlines(events(),
2003 for line in dagparser.dagtextlines(events(),
2004 addspaces=spaces,
2004 addspaces=spaces,
2005 wraplabels=True,
2005 wraplabels=True,
2006 wrapannotations=True,
2006 wrapannotations=True,
2007 wrapnonlinear=dots,
2007 wrapnonlinear=dots,
2008 usedots=dots,
2008 usedots=dots,
2009 maxlinewidth=70):
2009 maxlinewidth=70):
2010 ui.write(line)
2010 ui.write(line)
2011 ui.write("\n")
2011 ui.write("\n")
2012
2012
2013 @command('debugdata',
2013 @command('debugdata',
2014 [('c', 'changelog', False, _('open changelog')),
2014 [('c', 'changelog', False, _('open changelog')),
2015 ('m', 'manifest', False, _('open manifest'))],
2015 ('m', 'manifest', False, _('open manifest'))],
2016 _('-c|-m|FILE REV'))
2016 _('-c|-m|FILE REV'))
2017 def debugdata(ui, repo, file_, rev=None, **opts):
2017 def debugdata(ui, repo, file_, rev=None, **opts):
2018 """dump the contents of a data file revision"""
2018 """dump the contents of a data file revision"""
2019 if opts.get('changelog') or opts.get('manifest'):
2019 if opts.get('changelog') or opts.get('manifest'):
2020 file_, rev = None, file_
2020 file_, rev = None, file_
2021 elif rev is None:
2021 elif rev is None:
2022 raise error.CommandError('debugdata', _('invalid arguments'))
2022 raise error.CommandError('debugdata', _('invalid arguments'))
2023 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
2023 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
2024 try:
2024 try:
2025 ui.write(r.revision(r.lookup(rev)))
2025 ui.write(r.revision(r.lookup(rev)))
2026 except KeyError:
2026 except KeyError:
2027 raise util.Abort(_('invalid revision identifier %s') % rev)
2027 raise util.Abort(_('invalid revision identifier %s') % rev)
2028
2028
2029 @command('debugdate',
2029 @command('debugdate',
2030 [('e', 'extended', None, _('try extended date formats'))],
2030 [('e', 'extended', None, _('try extended date formats'))],
2031 _('[-e] DATE [RANGE]'),
2031 _('[-e] DATE [RANGE]'),
2032 norepo=True, optionalrepo=True)
2032 norepo=True, optionalrepo=True)
2033 def debugdate(ui, date, range=None, **opts):
2033 def debugdate(ui, date, range=None, **opts):
2034 """parse and display a date"""
2034 """parse and display a date"""
2035 if opts["extended"]:
2035 if opts["extended"]:
2036 d = util.parsedate(date, util.extendeddateformats)
2036 d = util.parsedate(date, util.extendeddateformats)
2037 else:
2037 else:
2038 d = util.parsedate(date)
2038 d = util.parsedate(date)
2039 ui.write(("internal: %s %s\n") % d)
2039 ui.write(("internal: %s %s\n") % d)
2040 ui.write(("standard: %s\n") % util.datestr(d))
2040 ui.write(("standard: %s\n") % util.datestr(d))
2041 if range:
2041 if range:
2042 m = util.matchdate(range)
2042 m = util.matchdate(range)
2043 ui.write(("match: %s\n") % m(d[0]))
2043 ui.write(("match: %s\n") % m(d[0]))
2044
2044
2045 @command('debugdiscovery',
2045 @command('debugdiscovery',
2046 [('', 'old', None, _('use old-style discovery')),
2046 [('', 'old', None, _('use old-style discovery')),
2047 ('', 'nonheads', None,
2047 ('', 'nonheads', None,
2048 _('use old-style discovery with non-heads included')),
2048 _('use old-style discovery with non-heads included')),
2049 ] + remoteopts,
2049 ] + remoteopts,
2050 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2050 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2051 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2051 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2052 """runs the changeset discovery protocol in isolation"""
2052 """runs the changeset discovery protocol in isolation"""
2053 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2053 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2054 opts.get('branch'))
2054 opts.get('branch'))
2055 remote = hg.peer(repo, opts, remoteurl)
2055 remote = hg.peer(repo, opts, remoteurl)
2056 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2056 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2057
2057
2058 # make sure tests are repeatable
2058 # make sure tests are repeatable
2059 random.seed(12323)
2059 random.seed(12323)
2060
2060
2061 def doit(localheads, remoteheads, remote=remote):
2061 def doit(localheads, remoteheads, remote=remote):
2062 if opts.get('old'):
2062 if opts.get('old'):
2063 if localheads:
2063 if localheads:
2064 raise util.Abort('cannot use localheads with old style '
2064 raise util.Abort('cannot use localheads with old style '
2065 'discovery')
2065 'discovery')
2066 if not util.safehasattr(remote, 'branches'):
2066 if not util.safehasattr(remote, 'branches'):
2067 # enable in-client legacy support
2067 # enable in-client legacy support
2068 remote = localrepo.locallegacypeer(remote.local())
2068 remote = localrepo.locallegacypeer(remote.local())
2069 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2069 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2070 force=True)
2070 force=True)
2071 common = set(common)
2071 common = set(common)
2072 if not opts.get('nonheads'):
2072 if not opts.get('nonheads'):
2073 ui.write(("unpruned common: %s\n") %
2073 ui.write(("unpruned common: %s\n") %
2074 " ".join(sorted(short(n) for n in common)))
2074 " ".join(sorted(short(n) for n in common)))
2075 dag = dagutil.revlogdag(repo.changelog)
2075 dag = dagutil.revlogdag(repo.changelog)
2076 all = dag.ancestorset(dag.internalizeall(common))
2076 all = dag.ancestorset(dag.internalizeall(common))
2077 common = dag.externalizeall(dag.headsetofconnecteds(all))
2077 common = dag.externalizeall(dag.headsetofconnecteds(all))
2078 else:
2078 else:
2079 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2079 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2080 common = set(common)
2080 common = set(common)
2081 rheads = set(hds)
2081 rheads = set(hds)
2082 lheads = set(repo.heads())
2082 lheads = set(repo.heads())
2083 ui.write(("common heads: %s\n") %
2083 ui.write(("common heads: %s\n") %
2084 " ".join(sorted(short(n) for n in common)))
2084 " ".join(sorted(short(n) for n in common)))
2085 if lheads <= common:
2085 if lheads <= common:
2086 ui.write(("local is subset\n"))
2086 ui.write(("local is subset\n"))
2087 elif rheads <= common:
2087 elif rheads <= common:
2088 ui.write(("remote is subset\n"))
2088 ui.write(("remote is subset\n"))
2089
2089
2090 serverlogs = opts.get('serverlog')
2090 serverlogs = opts.get('serverlog')
2091 if serverlogs:
2091 if serverlogs:
2092 for filename in serverlogs:
2092 for filename in serverlogs:
2093 logfile = open(filename, 'r')
2093 logfile = open(filename, 'r')
2094 try:
2094 try:
2095 line = logfile.readline()
2095 line = logfile.readline()
2096 while line:
2096 while line:
2097 parts = line.strip().split(';')
2097 parts = line.strip().split(';')
2098 op = parts[1]
2098 op = parts[1]
2099 if op == 'cg':
2099 if op == 'cg':
2100 pass
2100 pass
2101 elif op == 'cgss':
2101 elif op == 'cgss':
2102 doit(parts[2].split(' '), parts[3].split(' '))
2102 doit(parts[2].split(' '), parts[3].split(' '))
2103 elif op == 'unb':
2103 elif op == 'unb':
2104 doit(parts[3].split(' '), parts[2].split(' '))
2104 doit(parts[3].split(' '), parts[2].split(' '))
2105 line = logfile.readline()
2105 line = logfile.readline()
2106 finally:
2106 finally:
2107 logfile.close()
2107 logfile.close()
2108
2108
2109 else:
2109 else:
2110 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2110 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2111 opts.get('remote_head'))
2111 opts.get('remote_head'))
2112 localrevs = opts.get('local_head')
2112 localrevs = opts.get('local_head')
2113 doit(localrevs, remoterevs)
2113 doit(localrevs, remoterevs)
2114
2114
2115 @command('debugfileset',
2115 @command('debugfileset',
2116 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2116 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2117 _('[-r REV] FILESPEC'))
2117 _('[-r REV] FILESPEC'))
2118 def debugfileset(ui, repo, expr, **opts):
2118 def debugfileset(ui, repo, expr, **opts):
2119 '''parse and apply a fileset specification'''
2119 '''parse and apply a fileset specification'''
2120 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2120 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2121 if ui.verbose:
2121 if ui.verbose:
2122 tree = fileset.parse(expr)[0]
2122 tree = fileset.parse(expr)[0]
2123 ui.note(tree, "\n")
2123 ui.note(tree, "\n")
2124
2124
2125 for f in ctx.getfileset(expr):
2125 for f in ctx.getfileset(expr):
2126 ui.write("%s\n" % f)
2126 ui.write("%s\n" % f)
2127
2127
2128 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2128 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2129 def debugfsinfo(ui, path="."):
2129 def debugfsinfo(ui, path="."):
2130 """show information detected about current filesystem"""
2130 """show information detected about current filesystem"""
2131 util.writefile('.debugfsinfo', '')
2131 util.writefile('.debugfsinfo', '')
2132 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2132 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2133 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2133 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2134 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2134 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2135 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2135 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2136 and 'yes' or 'no'))
2136 and 'yes' or 'no'))
2137 os.unlink('.debugfsinfo')
2137 os.unlink('.debugfsinfo')
2138
2138
2139 @command('debuggetbundle',
2139 @command('debuggetbundle',
2140 [('H', 'head', [], _('id of head node'), _('ID')),
2140 [('H', 'head', [], _('id of head node'), _('ID')),
2141 ('C', 'common', [], _('id of common node'), _('ID')),
2141 ('C', 'common', [], _('id of common node'), _('ID')),
2142 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2142 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2143 _('REPO FILE [-H|-C ID]...'),
2143 _('REPO FILE [-H|-C ID]...'),
2144 norepo=True)
2144 norepo=True)
2145 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2145 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2146 """retrieves a bundle from a repo
2146 """retrieves a bundle from a repo
2147
2147
2148 Every ID must be a full-length hex node id string. Saves the bundle to the
2148 Every ID must be a full-length hex node id string. Saves the bundle to the
2149 given file.
2149 given file.
2150 """
2150 """
2151 repo = hg.peer(ui, opts, repopath)
2151 repo = hg.peer(ui, opts, repopath)
2152 if not repo.capable('getbundle'):
2152 if not repo.capable('getbundle'):
2153 raise util.Abort("getbundle() not supported by target repository")
2153 raise util.Abort("getbundle() not supported by target repository")
2154 args = {}
2154 args = {}
2155 if common:
2155 if common:
2156 args['common'] = [bin(s) for s in common]
2156 args['common'] = [bin(s) for s in common]
2157 if head:
2157 if head:
2158 args['heads'] = [bin(s) for s in head]
2158 args['heads'] = [bin(s) for s in head]
2159 # TODO: get desired bundlecaps from command line.
2159 # TODO: get desired bundlecaps from command line.
2160 args['bundlecaps'] = None
2160 args['bundlecaps'] = None
2161 bundle = repo.getbundle('debug', **args)
2161 bundle = repo.getbundle('debug', **args)
2162
2162
2163 bundletype = opts.get('type', 'bzip2').lower()
2163 bundletype = opts.get('type', 'bzip2').lower()
2164 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
2164 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
2165 bundletype = btypes.get(bundletype)
2165 bundletype = btypes.get(bundletype)
2166 if bundletype not in changegroup.bundletypes:
2166 if bundletype not in changegroup.bundletypes:
2167 raise util.Abort(_('unknown bundle type specified with --type'))
2167 raise util.Abort(_('unknown bundle type specified with --type'))
2168 changegroup.writebundle(bundle, bundlepath, bundletype)
2168 changegroup.writebundle(ui, bundle, bundlepath, bundletype)
2169
2169
2170 @command('debugignore', [], '')
2170 @command('debugignore', [], '')
2171 def debugignore(ui, repo, *values, **opts):
2171 def debugignore(ui, repo, *values, **opts):
2172 """display the combined ignore pattern"""
2172 """display the combined ignore pattern"""
2173 ignore = repo.dirstate._ignore
2173 ignore = repo.dirstate._ignore
2174 includepat = getattr(ignore, 'includepat', None)
2174 includepat = getattr(ignore, 'includepat', None)
2175 if includepat is not None:
2175 if includepat is not None:
2176 ui.write("%s\n" % includepat)
2176 ui.write("%s\n" % includepat)
2177 else:
2177 else:
2178 raise util.Abort(_("no ignore patterns found"))
2178 raise util.Abort(_("no ignore patterns found"))
2179
2179
2180 @command('debugindex',
2180 @command('debugindex',
2181 [('c', 'changelog', False, _('open changelog')),
2181 [('c', 'changelog', False, _('open changelog')),
2182 ('m', 'manifest', False, _('open manifest')),
2182 ('m', 'manifest', False, _('open manifest')),
2183 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2183 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2184 _('[-f FORMAT] -c|-m|FILE'),
2184 _('[-f FORMAT] -c|-m|FILE'),
2185 optionalrepo=True)
2185 optionalrepo=True)
2186 def debugindex(ui, repo, file_=None, **opts):
2186 def debugindex(ui, repo, file_=None, **opts):
2187 """dump the contents of an index file"""
2187 """dump the contents of an index file"""
2188 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2188 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2189 format = opts.get('format', 0)
2189 format = opts.get('format', 0)
2190 if format not in (0, 1):
2190 if format not in (0, 1):
2191 raise util.Abort(_("unknown format %d") % format)
2191 raise util.Abort(_("unknown format %d") % format)
2192
2192
2193 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2193 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2194 if generaldelta:
2194 if generaldelta:
2195 basehdr = ' delta'
2195 basehdr = ' delta'
2196 else:
2196 else:
2197 basehdr = ' base'
2197 basehdr = ' base'
2198
2198
2199 if ui.debugflag:
2199 if ui.debugflag:
2200 shortfn = hex
2200 shortfn = hex
2201 else:
2201 else:
2202 shortfn = short
2202 shortfn = short
2203
2203
2204 # There might not be anything in r, so have a sane default
2204 # There might not be anything in r, so have a sane default
2205 idlen = 12
2205 idlen = 12
2206 for i in r:
2206 for i in r:
2207 idlen = len(shortfn(r.node(i)))
2207 idlen = len(shortfn(r.node(i)))
2208 break
2208 break
2209
2209
2210 if format == 0:
2210 if format == 0:
2211 ui.write(" rev offset length " + basehdr + " linkrev"
2211 ui.write(" rev offset length " + basehdr + " linkrev"
2212 " %s %s p2\n" % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2212 " %s %s p2\n" % ("nodeid".ljust(idlen), "p1".ljust(idlen)))
2213 elif format == 1:
2213 elif format == 1:
2214 ui.write(" rev flag offset length"
2214 ui.write(" rev flag offset length"
2215 " size " + basehdr + " link p1 p2"
2215 " size " + basehdr + " link p1 p2"
2216 " %s\n" % "nodeid".rjust(idlen))
2216 " %s\n" % "nodeid".rjust(idlen))
2217
2217
2218 for i in r:
2218 for i in r:
2219 node = r.node(i)
2219 node = r.node(i)
2220 if generaldelta:
2220 if generaldelta:
2221 base = r.deltaparent(i)
2221 base = r.deltaparent(i)
2222 else:
2222 else:
2223 base = r.chainbase(i)
2223 base = r.chainbase(i)
2224 if format == 0:
2224 if format == 0:
2225 try:
2225 try:
2226 pp = r.parents(node)
2226 pp = r.parents(node)
2227 except Exception:
2227 except Exception:
2228 pp = [nullid, nullid]
2228 pp = [nullid, nullid]
2229 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2229 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2230 i, r.start(i), r.length(i), base, r.linkrev(i),
2230 i, r.start(i), r.length(i), base, r.linkrev(i),
2231 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2231 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
2232 elif format == 1:
2232 elif format == 1:
2233 pr = r.parentrevs(i)
2233 pr = r.parentrevs(i)
2234 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2234 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2235 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2235 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2236 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2236 base, r.linkrev(i), pr[0], pr[1], shortfn(node)))
2237
2237
2238 @command('debugindexdot', [], _('FILE'), optionalrepo=True)
2238 @command('debugindexdot', [], _('FILE'), optionalrepo=True)
2239 def debugindexdot(ui, repo, file_):
2239 def debugindexdot(ui, repo, file_):
2240 """dump an index DAG as a graphviz dot file"""
2240 """dump an index DAG as a graphviz dot file"""
2241 r = None
2241 r = None
2242 if repo:
2242 if repo:
2243 filelog = repo.file(file_)
2243 filelog = repo.file(file_)
2244 if len(filelog):
2244 if len(filelog):
2245 r = filelog
2245 r = filelog
2246 if not r:
2246 if not r:
2247 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2247 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2248 ui.write(("digraph G {\n"))
2248 ui.write(("digraph G {\n"))
2249 for i in r:
2249 for i in r:
2250 node = r.node(i)
2250 node = r.node(i)
2251 pp = r.parents(node)
2251 pp = r.parents(node)
2252 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2252 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2253 if pp[1] != nullid:
2253 if pp[1] != nullid:
2254 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2254 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2255 ui.write("}\n")
2255 ui.write("}\n")
2256
2256
2257 @command('debuginstall', [], '', norepo=True)
2257 @command('debuginstall', [], '', norepo=True)
2258 def debuginstall(ui):
2258 def debuginstall(ui):
2259 '''test Mercurial installation
2259 '''test Mercurial installation
2260
2260
2261 Returns 0 on success.
2261 Returns 0 on success.
2262 '''
2262 '''
2263
2263
2264 def writetemp(contents):
2264 def writetemp(contents):
2265 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2265 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2266 f = os.fdopen(fd, "wb")
2266 f = os.fdopen(fd, "wb")
2267 f.write(contents)
2267 f.write(contents)
2268 f.close()
2268 f.close()
2269 return name
2269 return name
2270
2270
2271 problems = 0
2271 problems = 0
2272
2272
2273 # encoding
2273 # encoding
2274 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2274 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2275 try:
2275 try:
2276 encoding.fromlocal("test")
2276 encoding.fromlocal("test")
2277 except util.Abort, inst:
2277 except util.Abort, inst:
2278 ui.write(" %s\n" % inst)
2278 ui.write(" %s\n" % inst)
2279 ui.write(_(" (check that your locale is properly set)\n"))
2279 ui.write(_(" (check that your locale is properly set)\n"))
2280 problems += 1
2280 problems += 1
2281
2281
2282 # Python
2282 # Python
2283 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2283 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2284 ui.status(_("checking Python version (%s)\n")
2284 ui.status(_("checking Python version (%s)\n")
2285 % ("%s.%s.%s" % sys.version_info[:3]))
2285 % ("%s.%s.%s" % sys.version_info[:3]))
2286 ui.status(_("checking Python lib (%s)...\n")
2286 ui.status(_("checking Python lib (%s)...\n")
2287 % os.path.dirname(os.__file__))
2287 % os.path.dirname(os.__file__))
2288
2288
2289 # compiled modules
2289 # compiled modules
2290 ui.status(_("checking installed modules (%s)...\n")
2290 ui.status(_("checking installed modules (%s)...\n")
2291 % os.path.dirname(__file__))
2291 % os.path.dirname(__file__))
2292 try:
2292 try:
2293 import bdiff, mpatch, base85, osutil
2293 import bdiff, mpatch, base85, osutil
2294 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2294 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2295 except Exception, inst:
2295 except Exception, inst:
2296 ui.write(" %s\n" % inst)
2296 ui.write(" %s\n" % inst)
2297 ui.write(_(" One or more extensions could not be found"))
2297 ui.write(_(" One or more extensions could not be found"))
2298 ui.write(_(" (check that you compiled the extensions)\n"))
2298 ui.write(_(" (check that you compiled the extensions)\n"))
2299 problems += 1
2299 problems += 1
2300
2300
2301 # templates
2301 # templates
2302 import templater
2302 import templater
2303 p = templater.templatepaths()
2303 p = templater.templatepaths()
2304 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2304 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2305 if p:
2305 if p:
2306 m = templater.templatepath("map-cmdline.default")
2306 m = templater.templatepath("map-cmdline.default")
2307 if m:
2307 if m:
2308 # template found, check if it is working
2308 # template found, check if it is working
2309 try:
2309 try:
2310 templater.templater(m)
2310 templater.templater(m)
2311 except Exception, inst:
2311 except Exception, inst:
2312 ui.write(" %s\n" % inst)
2312 ui.write(" %s\n" % inst)
2313 p = None
2313 p = None
2314 else:
2314 else:
2315 ui.write(_(" template 'default' not found\n"))
2315 ui.write(_(" template 'default' not found\n"))
2316 p = None
2316 p = None
2317 else:
2317 else:
2318 ui.write(_(" no template directories found\n"))
2318 ui.write(_(" no template directories found\n"))
2319 if not p:
2319 if not p:
2320 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2320 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2321 problems += 1
2321 problems += 1
2322
2322
2323 # editor
2323 # editor
2324 ui.status(_("checking commit editor...\n"))
2324 ui.status(_("checking commit editor...\n"))
2325 editor = ui.geteditor()
2325 editor = ui.geteditor()
2326 cmdpath = util.findexe(shlex.split(editor)[0])
2326 cmdpath = util.findexe(shlex.split(editor)[0])
2327 if not cmdpath:
2327 if not cmdpath:
2328 if editor == 'vi':
2328 if editor == 'vi':
2329 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2329 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2330 ui.write(_(" (specify a commit editor in your configuration"
2330 ui.write(_(" (specify a commit editor in your configuration"
2331 " file)\n"))
2331 " file)\n"))
2332 else:
2332 else:
2333 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2333 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2334 ui.write(_(" (specify a commit editor in your configuration"
2334 ui.write(_(" (specify a commit editor in your configuration"
2335 " file)\n"))
2335 " file)\n"))
2336 problems += 1
2336 problems += 1
2337
2337
2338 # check username
2338 # check username
2339 ui.status(_("checking username...\n"))
2339 ui.status(_("checking username...\n"))
2340 try:
2340 try:
2341 ui.username()
2341 ui.username()
2342 except util.Abort, e:
2342 except util.Abort, e:
2343 ui.write(" %s\n" % e)
2343 ui.write(" %s\n" % e)
2344 ui.write(_(" (specify a username in your configuration file)\n"))
2344 ui.write(_(" (specify a username in your configuration file)\n"))
2345 problems += 1
2345 problems += 1
2346
2346
2347 if not problems:
2347 if not problems:
2348 ui.status(_("no problems detected\n"))
2348 ui.status(_("no problems detected\n"))
2349 else:
2349 else:
2350 ui.write(_("%s problems detected,"
2350 ui.write(_("%s problems detected,"
2351 " please check your install!\n") % problems)
2351 " please check your install!\n") % problems)
2352
2352
2353 return problems
2353 return problems
2354
2354
2355 @command('debugknown', [], _('REPO ID...'), norepo=True)
2355 @command('debugknown', [], _('REPO ID...'), norepo=True)
2356 def debugknown(ui, repopath, *ids, **opts):
2356 def debugknown(ui, repopath, *ids, **opts):
2357 """test whether node ids are known to a repo
2357 """test whether node ids are known to a repo
2358
2358
2359 Every ID must be a full-length hex node id string. Returns a list of 0s
2359 Every ID must be a full-length hex node id string. Returns a list of 0s
2360 and 1s indicating unknown/known.
2360 and 1s indicating unknown/known.
2361 """
2361 """
2362 repo = hg.peer(ui, opts, repopath)
2362 repo = hg.peer(ui, opts, repopath)
2363 if not repo.capable('known'):
2363 if not repo.capable('known'):
2364 raise util.Abort("known() not supported by target repository")
2364 raise util.Abort("known() not supported by target repository")
2365 flags = repo.known([bin(s) for s in ids])
2365 flags = repo.known([bin(s) for s in ids])
2366 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2366 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2367
2367
2368 @command('debuglabelcomplete', [], _('LABEL...'))
2368 @command('debuglabelcomplete', [], _('LABEL...'))
2369 def debuglabelcomplete(ui, repo, *args):
2369 def debuglabelcomplete(ui, repo, *args):
2370 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2370 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
2371 debugnamecomplete(ui, repo, *args)
2371 debugnamecomplete(ui, repo, *args)
2372
2372
2373 @command('debugnamecomplete', [], _('NAME...'))
2373 @command('debugnamecomplete', [], _('NAME...'))
2374 def debugnamecomplete(ui, repo, *args):
2374 def debugnamecomplete(ui, repo, *args):
2375 '''complete "names" - tags, open branch names, bookmark names'''
2375 '''complete "names" - tags, open branch names, bookmark names'''
2376
2376
2377 names = set()
2377 names = set()
2378 # since we previously only listed open branches, we will handle that
2378 # since we previously only listed open branches, we will handle that
2379 # specially (after this for loop)
2379 # specially (after this for loop)
2380 for name, ns in repo.names.iteritems():
2380 for name, ns in repo.names.iteritems():
2381 if name != 'branches':
2381 if name != 'branches':
2382 names.update(ns.listnames(repo))
2382 names.update(ns.listnames(repo))
2383 names.update(tag for (tag, heads, tip, closed)
2383 names.update(tag for (tag, heads, tip, closed)
2384 in repo.branchmap().iterbranches() if not closed)
2384 in repo.branchmap().iterbranches() if not closed)
2385 completions = set()
2385 completions = set()
2386 if not args:
2386 if not args:
2387 args = ['']
2387 args = ['']
2388 for a in args:
2388 for a in args:
2389 completions.update(n for n in names if n.startswith(a))
2389 completions.update(n for n in names if n.startswith(a))
2390 ui.write('\n'.join(sorted(completions)))
2390 ui.write('\n'.join(sorted(completions)))
2391 ui.write('\n')
2391 ui.write('\n')
2392
2392
2393 @command('debuglocks',
2393 @command('debuglocks',
2394 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2394 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2395 ('W', 'force-wlock', None,
2395 ('W', 'force-wlock', None,
2396 _('free the working state lock (DANGEROUS)'))],
2396 _('free the working state lock (DANGEROUS)'))],
2397 _('[OPTION]...'))
2397 _('[OPTION]...'))
2398 def debuglocks(ui, repo, **opts):
2398 def debuglocks(ui, repo, **opts):
2399 """show or modify state of locks
2399 """show or modify state of locks
2400
2400
2401 By default, this command will show which locks are held. This
2401 By default, this command will show which locks are held. This
2402 includes the user and process holding the lock, the amount of time
2402 includes the user and process holding the lock, the amount of time
2403 the lock has been held, and the machine name where the process is
2403 the lock has been held, and the machine name where the process is
2404 running if it's not local.
2404 running if it's not local.
2405
2405
2406 Locks protect the integrity of Mercurial's data, so should be
2406 Locks protect the integrity of Mercurial's data, so should be
2407 treated with care. System crashes or other interruptions may cause
2407 treated with care. System crashes or other interruptions may cause
2408 locks to not be properly released, though Mercurial will usually
2408 locks to not be properly released, though Mercurial will usually
2409 detect and remove such stale locks automatically.
2409 detect and remove such stale locks automatically.
2410
2410
2411 However, detecting stale locks may not always be possible (for
2411 However, detecting stale locks may not always be possible (for
2412 instance, on a shared filesystem). Removing locks may also be
2412 instance, on a shared filesystem). Removing locks may also be
2413 blocked by filesystem permissions.
2413 blocked by filesystem permissions.
2414
2414
2415 Returns 0 if no locks are held.
2415 Returns 0 if no locks are held.
2416
2416
2417 """
2417 """
2418
2418
2419 if opts.get('force_lock'):
2419 if opts.get('force_lock'):
2420 repo.svfs.unlink('lock')
2420 repo.svfs.unlink('lock')
2421 if opts.get('force_wlock'):
2421 if opts.get('force_wlock'):
2422 repo.vfs.unlink('wlock')
2422 repo.vfs.unlink('wlock')
2423 if opts.get('force_lock') or opts.get('force_lock'):
2423 if opts.get('force_lock') or opts.get('force_lock'):
2424 return 0
2424 return 0
2425
2425
2426 now = time.time()
2426 now = time.time()
2427 held = 0
2427 held = 0
2428
2428
2429 def report(vfs, name, method):
2429 def report(vfs, name, method):
2430 # this causes stale locks to get reaped for more accurate reporting
2430 # this causes stale locks to get reaped for more accurate reporting
2431 try:
2431 try:
2432 l = method(False)
2432 l = method(False)
2433 except error.LockHeld:
2433 except error.LockHeld:
2434 l = None
2434 l = None
2435
2435
2436 if l:
2436 if l:
2437 l.release()
2437 l.release()
2438 else:
2438 else:
2439 try:
2439 try:
2440 stat = repo.svfs.lstat(name)
2440 stat = repo.svfs.lstat(name)
2441 age = now - stat.st_mtime
2441 age = now - stat.st_mtime
2442 user = util.username(stat.st_uid)
2442 user = util.username(stat.st_uid)
2443 locker = vfs.readlock(name)
2443 locker = vfs.readlock(name)
2444 if ":" in locker:
2444 if ":" in locker:
2445 host, pid = locker.split(':')
2445 host, pid = locker.split(':')
2446 if host == socket.gethostname():
2446 if host == socket.gethostname():
2447 locker = 'user %s, process %s' % (user, pid)
2447 locker = 'user %s, process %s' % (user, pid)
2448 else:
2448 else:
2449 locker = 'user %s, process %s, host %s' \
2449 locker = 'user %s, process %s, host %s' \
2450 % (user, pid, host)
2450 % (user, pid, host)
2451 ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
2451 ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
2452 return 1
2452 return 1
2453 except OSError, e:
2453 except OSError, e:
2454 if e.errno != errno.ENOENT:
2454 if e.errno != errno.ENOENT:
2455 raise
2455 raise
2456
2456
2457 ui.write("%-6s free\n" % (name + ":"))
2457 ui.write("%-6s free\n" % (name + ":"))
2458 return 0
2458 return 0
2459
2459
2460 held += report(repo.svfs, "lock", repo.lock)
2460 held += report(repo.svfs, "lock", repo.lock)
2461 held += report(repo.vfs, "wlock", repo.wlock)
2461 held += report(repo.vfs, "wlock", repo.wlock)
2462
2462
2463 return held
2463 return held
2464
2464
2465 @command('debugobsolete',
2465 @command('debugobsolete',
2466 [('', 'flags', 0, _('markers flag')),
2466 [('', 'flags', 0, _('markers flag')),
2467 ('', 'record-parents', False,
2467 ('', 'record-parents', False,
2468 _('record parent information for the precursor')),
2468 _('record parent information for the precursor')),
2469 ('r', 'rev', [], _('display markers relevant to REV')),
2469 ('r', 'rev', [], _('display markers relevant to REV')),
2470 ] + commitopts2,
2470 ] + commitopts2,
2471 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2471 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2472 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2472 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2473 """create arbitrary obsolete marker
2473 """create arbitrary obsolete marker
2474
2474
2475 With no arguments, displays the list of obsolescence markers."""
2475 With no arguments, displays the list of obsolescence markers."""
2476
2476
2477 def parsenodeid(s):
2477 def parsenodeid(s):
2478 try:
2478 try:
2479 # We do not use revsingle/revrange functions here to accept
2479 # We do not use revsingle/revrange functions here to accept
2480 # arbitrary node identifiers, possibly not present in the
2480 # arbitrary node identifiers, possibly not present in the
2481 # local repository.
2481 # local repository.
2482 n = bin(s)
2482 n = bin(s)
2483 if len(n) != len(nullid):
2483 if len(n) != len(nullid):
2484 raise TypeError()
2484 raise TypeError()
2485 return n
2485 return n
2486 except TypeError:
2486 except TypeError:
2487 raise util.Abort('changeset references must be full hexadecimal '
2487 raise util.Abort('changeset references must be full hexadecimal '
2488 'node identifiers')
2488 'node identifiers')
2489
2489
2490 if precursor is not None:
2490 if precursor is not None:
2491 if opts['rev']:
2491 if opts['rev']:
2492 raise util.Abort('cannot select revision when creating marker')
2492 raise util.Abort('cannot select revision when creating marker')
2493 metadata = {}
2493 metadata = {}
2494 metadata['user'] = opts['user'] or ui.username()
2494 metadata['user'] = opts['user'] or ui.username()
2495 succs = tuple(parsenodeid(succ) for succ in successors)
2495 succs = tuple(parsenodeid(succ) for succ in successors)
2496 l = repo.lock()
2496 l = repo.lock()
2497 try:
2497 try:
2498 tr = repo.transaction('debugobsolete')
2498 tr = repo.transaction('debugobsolete')
2499 try:
2499 try:
2500 try:
2500 try:
2501 date = opts.get('date')
2501 date = opts.get('date')
2502 if date:
2502 if date:
2503 date = util.parsedate(date)
2503 date = util.parsedate(date)
2504 else:
2504 else:
2505 date = None
2505 date = None
2506 prec = parsenodeid(precursor)
2506 prec = parsenodeid(precursor)
2507 parents = None
2507 parents = None
2508 if opts['record_parents']:
2508 if opts['record_parents']:
2509 if prec not in repo.unfiltered():
2509 if prec not in repo.unfiltered():
2510 raise util.Abort('cannot used --record-parents on '
2510 raise util.Abort('cannot used --record-parents on '
2511 'unknown changesets')
2511 'unknown changesets')
2512 parents = repo.unfiltered()[prec].parents()
2512 parents = repo.unfiltered()[prec].parents()
2513 parents = tuple(p.node() for p in parents)
2513 parents = tuple(p.node() for p in parents)
2514 repo.obsstore.create(tr, prec, succs, opts['flags'],
2514 repo.obsstore.create(tr, prec, succs, opts['flags'],
2515 parents=parents, date=date,
2515 parents=parents, date=date,
2516 metadata=metadata)
2516 metadata=metadata)
2517 tr.close()
2517 tr.close()
2518 except ValueError, exc:
2518 except ValueError, exc:
2519 raise util.Abort(_('bad obsmarker input: %s') % exc)
2519 raise util.Abort(_('bad obsmarker input: %s') % exc)
2520 finally:
2520 finally:
2521 tr.release()
2521 tr.release()
2522 finally:
2522 finally:
2523 l.release()
2523 l.release()
2524 else:
2524 else:
2525 if opts['rev']:
2525 if opts['rev']:
2526 revs = scmutil.revrange(repo, opts['rev'])
2526 revs = scmutil.revrange(repo, opts['rev'])
2527 nodes = [repo[r].node() for r in revs]
2527 nodes = [repo[r].node() for r in revs]
2528 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2528 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2529 markers.sort(key=lambda x: x._data)
2529 markers.sort(key=lambda x: x._data)
2530 else:
2530 else:
2531 markers = obsolete.getmarkers(repo)
2531 markers = obsolete.getmarkers(repo)
2532
2532
2533 for m in markers:
2533 for m in markers:
2534 cmdutil.showmarker(ui, m)
2534 cmdutil.showmarker(ui, m)
2535
2535
2536 @command('debugpathcomplete',
2536 @command('debugpathcomplete',
2537 [('f', 'full', None, _('complete an entire path')),
2537 [('f', 'full', None, _('complete an entire path')),
2538 ('n', 'normal', None, _('show only normal files')),
2538 ('n', 'normal', None, _('show only normal files')),
2539 ('a', 'added', None, _('show only added files')),
2539 ('a', 'added', None, _('show only added files')),
2540 ('r', 'removed', None, _('show only removed files'))],
2540 ('r', 'removed', None, _('show only removed files'))],
2541 _('FILESPEC...'))
2541 _('FILESPEC...'))
2542 def debugpathcomplete(ui, repo, *specs, **opts):
2542 def debugpathcomplete(ui, repo, *specs, **opts):
2543 '''complete part or all of a tracked path
2543 '''complete part or all of a tracked path
2544
2544
2545 This command supports shells that offer path name completion. It
2545 This command supports shells that offer path name completion. It
2546 currently completes only files already known to the dirstate.
2546 currently completes only files already known to the dirstate.
2547
2547
2548 Completion extends only to the next path segment unless
2548 Completion extends only to the next path segment unless
2549 --full is specified, in which case entire paths are used.'''
2549 --full is specified, in which case entire paths are used.'''
2550
2550
2551 def complete(path, acceptable):
2551 def complete(path, acceptable):
2552 dirstate = repo.dirstate
2552 dirstate = repo.dirstate
2553 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2553 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2554 rootdir = repo.root + os.sep
2554 rootdir = repo.root + os.sep
2555 if spec != repo.root and not spec.startswith(rootdir):
2555 if spec != repo.root and not spec.startswith(rootdir):
2556 return [], []
2556 return [], []
2557 if os.path.isdir(spec):
2557 if os.path.isdir(spec):
2558 spec += '/'
2558 spec += '/'
2559 spec = spec[len(rootdir):]
2559 spec = spec[len(rootdir):]
2560 fixpaths = os.sep != '/'
2560 fixpaths = os.sep != '/'
2561 if fixpaths:
2561 if fixpaths:
2562 spec = spec.replace(os.sep, '/')
2562 spec = spec.replace(os.sep, '/')
2563 speclen = len(spec)
2563 speclen = len(spec)
2564 fullpaths = opts['full']
2564 fullpaths = opts['full']
2565 files, dirs = set(), set()
2565 files, dirs = set(), set()
2566 adddir, addfile = dirs.add, files.add
2566 adddir, addfile = dirs.add, files.add
2567 for f, st in dirstate.iteritems():
2567 for f, st in dirstate.iteritems():
2568 if f.startswith(spec) and st[0] in acceptable:
2568 if f.startswith(spec) and st[0] in acceptable:
2569 if fixpaths:
2569 if fixpaths:
2570 f = f.replace('/', os.sep)
2570 f = f.replace('/', os.sep)
2571 if fullpaths:
2571 if fullpaths:
2572 addfile(f)
2572 addfile(f)
2573 continue
2573 continue
2574 s = f.find(os.sep, speclen)
2574 s = f.find(os.sep, speclen)
2575 if s >= 0:
2575 if s >= 0:
2576 adddir(f[:s])
2576 adddir(f[:s])
2577 else:
2577 else:
2578 addfile(f)
2578 addfile(f)
2579 return files, dirs
2579 return files, dirs
2580
2580
2581 acceptable = ''
2581 acceptable = ''
2582 if opts['normal']:
2582 if opts['normal']:
2583 acceptable += 'nm'
2583 acceptable += 'nm'
2584 if opts['added']:
2584 if opts['added']:
2585 acceptable += 'a'
2585 acceptable += 'a'
2586 if opts['removed']:
2586 if opts['removed']:
2587 acceptable += 'r'
2587 acceptable += 'r'
2588 cwd = repo.getcwd()
2588 cwd = repo.getcwd()
2589 if not specs:
2589 if not specs:
2590 specs = ['.']
2590 specs = ['.']
2591
2591
2592 files, dirs = set(), set()
2592 files, dirs = set(), set()
2593 for spec in specs:
2593 for spec in specs:
2594 f, d = complete(spec, acceptable or 'nmar')
2594 f, d = complete(spec, acceptable or 'nmar')
2595 files.update(f)
2595 files.update(f)
2596 dirs.update(d)
2596 dirs.update(d)
2597 files.update(dirs)
2597 files.update(dirs)
2598 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2598 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2599 ui.write('\n')
2599 ui.write('\n')
2600
2600
2601 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2601 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2602 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2602 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2603 '''access the pushkey key/value protocol
2603 '''access the pushkey key/value protocol
2604
2604
2605 With two args, list the keys in the given namespace.
2605 With two args, list the keys in the given namespace.
2606
2606
2607 With five args, set a key to new if it currently is set to old.
2607 With five args, set a key to new if it currently is set to old.
2608 Reports success or failure.
2608 Reports success or failure.
2609 '''
2609 '''
2610
2610
2611 target = hg.peer(ui, {}, repopath)
2611 target = hg.peer(ui, {}, repopath)
2612 if keyinfo:
2612 if keyinfo:
2613 key, old, new = keyinfo
2613 key, old, new = keyinfo
2614 r = target.pushkey(namespace, key, old, new)
2614 r = target.pushkey(namespace, key, old, new)
2615 ui.status(str(r) + '\n')
2615 ui.status(str(r) + '\n')
2616 return not r
2616 return not r
2617 else:
2617 else:
2618 for k, v in sorted(target.listkeys(namespace).iteritems()):
2618 for k, v in sorted(target.listkeys(namespace).iteritems()):
2619 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2619 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2620 v.encode('string-escape')))
2620 v.encode('string-escape')))
2621
2621
2622 @command('debugpvec', [], _('A B'))
2622 @command('debugpvec', [], _('A B'))
2623 def debugpvec(ui, repo, a, b=None):
2623 def debugpvec(ui, repo, a, b=None):
2624 ca = scmutil.revsingle(repo, a)
2624 ca = scmutil.revsingle(repo, a)
2625 cb = scmutil.revsingle(repo, b)
2625 cb = scmutil.revsingle(repo, b)
2626 pa = pvec.ctxpvec(ca)
2626 pa = pvec.ctxpvec(ca)
2627 pb = pvec.ctxpvec(cb)
2627 pb = pvec.ctxpvec(cb)
2628 if pa == pb:
2628 if pa == pb:
2629 rel = "="
2629 rel = "="
2630 elif pa > pb:
2630 elif pa > pb:
2631 rel = ">"
2631 rel = ">"
2632 elif pa < pb:
2632 elif pa < pb:
2633 rel = "<"
2633 rel = "<"
2634 elif pa | pb:
2634 elif pa | pb:
2635 rel = "|"
2635 rel = "|"
2636 ui.write(_("a: %s\n") % pa)
2636 ui.write(_("a: %s\n") % pa)
2637 ui.write(_("b: %s\n") % pb)
2637 ui.write(_("b: %s\n") % pb)
2638 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2638 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2639 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2639 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2640 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2640 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2641 pa.distance(pb), rel))
2641 pa.distance(pb), rel))
2642
2642
2643 @command('debugrebuilddirstate|debugrebuildstate',
2643 @command('debugrebuilddirstate|debugrebuildstate',
2644 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2644 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2645 _('[-r REV]'))
2645 _('[-r REV]'))
2646 def debugrebuilddirstate(ui, repo, rev):
2646 def debugrebuilddirstate(ui, repo, rev):
2647 """rebuild the dirstate as it would look like for the given revision
2647 """rebuild the dirstate as it would look like for the given revision
2648
2648
2649 If no revision is specified the first current parent will be used.
2649 If no revision is specified the first current parent will be used.
2650
2650
2651 The dirstate will be set to the files of the given revision.
2651 The dirstate will be set to the files of the given revision.
2652 The actual working directory content or existing dirstate
2652 The actual working directory content or existing dirstate
2653 information such as adds or removes is not considered.
2653 information such as adds or removes is not considered.
2654
2654
2655 One use of this command is to make the next :hg:`status` invocation
2655 One use of this command is to make the next :hg:`status` invocation
2656 check the actual file content.
2656 check the actual file content.
2657 """
2657 """
2658 ctx = scmutil.revsingle(repo, rev)
2658 ctx = scmutil.revsingle(repo, rev)
2659 wlock = repo.wlock()
2659 wlock = repo.wlock()
2660 try:
2660 try:
2661 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2661 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2662 finally:
2662 finally:
2663 wlock.release()
2663 wlock.release()
2664
2664
2665 @command('debugrename',
2665 @command('debugrename',
2666 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2666 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2667 _('[-r REV] FILE'))
2667 _('[-r REV] FILE'))
2668 def debugrename(ui, repo, file1, *pats, **opts):
2668 def debugrename(ui, repo, file1, *pats, **opts):
2669 """dump rename information"""
2669 """dump rename information"""
2670
2670
2671 ctx = scmutil.revsingle(repo, opts.get('rev'))
2671 ctx = scmutil.revsingle(repo, opts.get('rev'))
2672 m = scmutil.match(ctx, (file1,) + pats, opts)
2672 m = scmutil.match(ctx, (file1,) + pats, opts)
2673 for abs in ctx.walk(m):
2673 for abs in ctx.walk(m):
2674 fctx = ctx[abs]
2674 fctx = ctx[abs]
2675 o = fctx.filelog().renamed(fctx.filenode())
2675 o = fctx.filelog().renamed(fctx.filenode())
2676 rel = m.rel(abs)
2676 rel = m.rel(abs)
2677 if o:
2677 if o:
2678 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2678 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2679 else:
2679 else:
2680 ui.write(_("%s not renamed\n") % rel)
2680 ui.write(_("%s not renamed\n") % rel)
2681
2681
2682 @command('debugrevlog',
2682 @command('debugrevlog',
2683 [('c', 'changelog', False, _('open changelog')),
2683 [('c', 'changelog', False, _('open changelog')),
2684 ('m', 'manifest', False, _('open manifest')),
2684 ('m', 'manifest', False, _('open manifest')),
2685 ('d', 'dump', False, _('dump index data'))],
2685 ('d', 'dump', False, _('dump index data'))],
2686 _('-c|-m|FILE'),
2686 _('-c|-m|FILE'),
2687 optionalrepo=True)
2687 optionalrepo=True)
2688 def debugrevlog(ui, repo, file_=None, **opts):
2688 def debugrevlog(ui, repo, file_=None, **opts):
2689 """show data and statistics about a revlog"""
2689 """show data and statistics about a revlog"""
2690 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2690 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2691
2691
2692 if opts.get("dump"):
2692 if opts.get("dump"):
2693 numrevs = len(r)
2693 numrevs = len(r)
2694 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2694 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2695 " rawsize totalsize compression heads chainlen\n")
2695 " rawsize totalsize compression heads chainlen\n")
2696 ts = 0
2696 ts = 0
2697 heads = set()
2697 heads = set()
2698
2698
2699 for rev in xrange(numrevs):
2699 for rev in xrange(numrevs):
2700 dbase = r.deltaparent(rev)
2700 dbase = r.deltaparent(rev)
2701 if dbase == -1:
2701 if dbase == -1:
2702 dbase = rev
2702 dbase = rev
2703 cbase = r.chainbase(rev)
2703 cbase = r.chainbase(rev)
2704 clen = r.chainlen(rev)
2704 clen = r.chainlen(rev)
2705 p1, p2 = r.parentrevs(rev)
2705 p1, p2 = r.parentrevs(rev)
2706 rs = r.rawsize(rev)
2706 rs = r.rawsize(rev)
2707 ts = ts + rs
2707 ts = ts + rs
2708 heads -= set(r.parentrevs(rev))
2708 heads -= set(r.parentrevs(rev))
2709 heads.add(rev)
2709 heads.add(rev)
2710 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2710 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2711 "%11d %5d %8d\n" %
2711 "%11d %5d %8d\n" %
2712 (rev, p1, p2, r.start(rev), r.end(rev),
2712 (rev, p1, p2, r.start(rev), r.end(rev),
2713 r.start(dbase), r.start(cbase),
2713 r.start(dbase), r.start(cbase),
2714 r.start(p1), r.start(p2),
2714 r.start(p1), r.start(p2),
2715 rs, ts, ts / r.end(rev), len(heads), clen))
2715 rs, ts, ts / r.end(rev), len(heads), clen))
2716 return 0
2716 return 0
2717
2717
2718 v = r.version
2718 v = r.version
2719 format = v & 0xFFFF
2719 format = v & 0xFFFF
2720 flags = []
2720 flags = []
2721 gdelta = False
2721 gdelta = False
2722 if v & revlog.REVLOGNGINLINEDATA:
2722 if v & revlog.REVLOGNGINLINEDATA:
2723 flags.append('inline')
2723 flags.append('inline')
2724 if v & revlog.REVLOGGENERALDELTA:
2724 if v & revlog.REVLOGGENERALDELTA:
2725 gdelta = True
2725 gdelta = True
2726 flags.append('generaldelta')
2726 flags.append('generaldelta')
2727 if not flags:
2727 if not flags:
2728 flags = ['(none)']
2728 flags = ['(none)']
2729
2729
2730 nummerges = 0
2730 nummerges = 0
2731 numfull = 0
2731 numfull = 0
2732 numprev = 0
2732 numprev = 0
2733 nump1 = 0
2733 nump1 = 0
2734 nump2 = 0
2734 nump2 = 0
2735 numother = 0
2735 numother = 0
2736 nump1prev = 0
2736 nump1prev = 0
2737 nump2prev = 0
2737 nump2prev = 0
2738 chainlengths = []
2738 chainlengths = []
2739
2739
2740 datasize = [None, 0, 0L]
2740 datasize = [None, 0, 0L]
2741 fullsize = [None, 0, 0L]
2741 fullsize = [None, 0, 0L]
2742 deltasize = [None, 0, 0L]
2742 deltasize = [None, 0, 0L]
2743
2743
2744 def addsize(size, l):
2744 def addsize(size, l):
2745 if l[0] is None or size < l[0]:
2745 if l[0] is None or size < l[0]:
2746 l[0] = size
2746 l[0] = size
2747 if size > l[1]:
2747 if size > l[1]:
2748 l[1] = size
2748 l[1] = size
2749 l[2] += size
2749 l[2] += size
2750
2750
2751 numrevs = len(r)
2751 numrevs = len(r)
2752 for rev in xrange(numrevs):
2752 for rev in xrange(numrevs):
2753 p1, p2 = r.parentrevs(rev)
2753 p1, p2 = r.parentrevs(rev)
2754 delta = r.deltaparent(rev)
2754 delta = r.deltaparent(rev)
2755 if format > 0:
2755 if format > 0:
2756 addsize(r.rawsize(rev), datasize)
2756 addsize(r.rawsize(rev), datasize)
2757 if p2 != nullrev:
2757 if p2 != nullrev:
2758 nummerges += 1
2758 nummerges += 1
2759 size = r.length(rev)
2759 size = r.length(rev)
2760 if delta == nullrev:
2760 if delta == nullrev:
2761 chainlengths.append(0)
2761 chainlengths.append(0)
2762 numfull += 1
2762 numfull += 1
2763 addsize(size, fullsize)
2763 addsize(size, fullsize)
2764 else:
2764 else:
2765 chainlengths.append(chainlengths[delta] + 1)
2765 chainlengths.append(chainlengths[delta] + 1)
2766 addsize(size, deltasize)
2766 addsize(size, deltasize)
2767 if delta == rev - 1:
2767 if delta == rev - 1:
2768 numprev += 1
2768 numprev += 1
2769 if delta == p1:
2769 if delta == p1:
2770 nump1prev += 1
2770 nump1prev += 1
2771 elif delta == p2:
2771 elif delta == p2:
2772 nump2prev += 1
2772 nump2prev += 1
2773 elif delta == p1:
2773 elif delta == p1:
2774 nump1 += 1
2774 nump1 += 1
2775 elif delta == p2:
2775 elif delta == p2:
2776 nump2 += 1
2776 nump2 += 1
2777 elif delta != nullrev:
2777 elif delta != nullrev:
2778 numother += 1
2778 numother += 1
2779
2779
2780 # Adjust size min value for empty cases
2780 # Adjust size min value for empty cases
2781 for size in (datasize, fullsize, deltasize):
2781 for size in (datasize, fullsize, deltasize):
2782 if size[0] is None:
2782 if size[0] is None:
2783 size[0] = 0
2783 size[0] = 0
2784
2784
2785 numdeltas = numrevs - numfull
2785 numdeltas = numrevs - numfull
2786 numoprev = numprev - nump1prev - nump2prev
2786 numoprev = numprev - nump1prev - nump2prev
2787 totalrawsize = datasize[2]
2787 totalrawsize = datasize[2]
2788 datasize[2] /= numrevs
2788 datasize[2] /= numrevs
2789 fulltotal = fullsize[2]
2789 fulltotal = fullsize[2]
2790 fullsize[2] /= numfull
2790 fullsize[2] /= numfull
2791 deltatotal = deltasize[2]
2791 deltatotal = deltasize[2]
2792 if numrevs - numfull > 0:
2792 if numrevs - numfull > 0:
2793 deltasize[2] /= numrevs - numfull
2793 deltasize[2] /= numrevs - numfull
2794 totalsize = fulltotal + deltatotal
2794 totalsize = fulltotal + deltatotal
2795 avgchainlen = sum(chainlengths) / numrevs
2795 avgchainlen = sum(chainlengths) / numrevs
2796 compratio = totalrawsize / totalsize
2796 compratio = totalrawsize / totalsize
2797
2797
2798 basedfmtstr = '%%%dd\n'
2798 basedfmtstr = '%%%dd\n'
2799 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2799 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2800
2800
2801 def dfmtstr(max):
2801 def dfmtstr(max):
2802 return basedfmtstr % len(str(max))
2802 return basedfmtstr % len(str(max))
2803 def pcfmtstr(max, padding=0):
2803 def pcfmtstr(max, padding=0):
2804 return basepcfmtstr % (len(str(max)), ' ' * padding)
2804 return basepcfmtstr % (len(str(max)), ' ' * padding)
2805
2805
2806 def pcfmt(value, total):
2806 def pcfmt(value, total):
2807 return (value, 100 * float(value) / total)
2807 return (value, 100 * float(value) / total)
2808
2808
2809 ui.write(('format : %d\n') % format)
2809 ui.write(('format : %d\n') % format)
2810 ui.write(('flags : %s\n') % ', '.join(flags))
2810 ui.write(('flags : %s\n') % ', '.join(flags))
2811
2811
2812 ui.write('\n')
2812 ui.write('\n')
2813 fmt = pcfmtstr(totalsize)
2813 fmt = pcfmtstr(totalsize)
2814 fmt2 = dfmtstr(totalsize)
2814 fmt2 = dfmtstr(totalsize)
2815 ui.write(('revisions : ') + fmt2 % numrevs)
2815 ui.write(('revisions : ') + fmt2 % numrevs)
2816 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2816 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2817 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2817 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2818 ui.write(('revisions : ') + fmt2 % numrevs)
2818 ui.write(('revisions : ') + fmt2 % numrevs)
2819 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2819 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2820 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2820 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2821 ui.write(('revision size : ') + fmt2 % totalsize)
2821 ui.write(('revision size : ') + fmt2 % totalsize)
2822 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2822 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2823 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2823 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2824
2824
2825 ui.write('\n')
2825 ui.write('\n')
2826 fmt = dfmtstr(max(avgchainlen, compratio))
2826 fmt = dfmtstr(max(avgchainlen, compratio))
2827 ui.write(('avg chain length : ') + fmt % avgchainlen)
2827 ui.write(('avg chain length : ') + fmt % avgchainlen)
2828 ui.write(('compression ratio : ') + fmt % compratio)
2828 ui.write(('compression ratio : ') + fmt % compratio)
2829
2829
2830 if format > 0:
2830 if format > 0:
2831 ui.write('\n')
2831 ui.write('\n')
2832 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2832 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2833 % tuple(datasize))
2833 % tuple(datasize))
2834 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2834 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2835 % tuple(fullsize))
2835 % tuple(fullsize))
2836 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2836 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2837 % tuple(deltasize))
2837 % tuple(deltasize))
2838
2838
2839 if numdeltas > 0:
2839 if numdeltas > 0:
2840 ui.write('\n')
2840 ui.write('\n')
2841 fmt = pcfmtstr(numdeltas)
2841 fmt = pcfmtstr(numdeltas)
2842 fmt2 = pcfmtstr(numdeltas, 4)
2842 fmt2 = pcfmtstr(numdeltas, 4)
2843 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2843 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2844 if numprev > 0:
2844 if numprev > 0:
2845 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2845 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2846 numprev))
2846 numprev))
2847 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2847 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2848 numprev))
2848 numprev))
2849 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2849 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2850 numprev))
2850 numprev))
2851 if gdelta:
2851 if gdelta:
2852 ui.write(('deltas against p1 : ')
2852 ui.write(('deltas against p1 : ')
2853 + fmt % pcfmt(nump1, numdeltas))
2853 + fmt % pcfmt(nump1, numdeltas))
2854 ui.write(('deltas against p2 : ')
2854 ui.write(('deltas against p2 : ')
2855 + fmt % pcfmt(nump2, numdeltas))
2855 + fmt % pcfmt(nump2, numdeltas))
2856 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2856 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2857 numdeltas))
2857 numdeltas))
2858
2858
2859 @command('debugrevspec',
2859 @command('debugrevspec',
2860 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2860 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2861 ('REVSPEC'))
2861 ('REVSPEC'))
2862 def debugrevspec(ui, repo, expr, **opts):
2862 def debugrevspec(ui, repo, expr, **opts):
2863 """parse and apply a revision specification
2863 """parse and apply a revision specification
2864
2864
2865 Use --verbose to print the parsed tree before and after aliases
2865 Use --verbose to print the parsed tree before and after aliases
2866 expansion.
2866 expansion.
2867 """
2867 """
2868 if ui.verbose:
2868 if ui.verbose:
2869 tree = revset.parse(expr)[0]
2869 tree = revset.parse(expr)[0]
2870 ui.note(revset.prettyformat(tree), "\n")
2870 ui.note(revset.prettyformat(tree), "\n")
2871 newtree = revset.findaliases(ui, tree)
2871 newtree = revset.findaliases(ui, tree)
2872 if newtree != tree:
2872 if newtree != tree:
2873 ui.note(revset.prettyformat(newtree), "\n")
2873 ui.note(revset.prettyformat(newtree), "\n")
2874 tree = newtree
2874 tree = newtree
2875 newtree = revset.foldconcat(tree)
2875 newtree = revset.foldconcat(tree)
2876 if newtree != tree:
2876 if newtree != tree:
2877 ui.note(revset.prettyformat(newtree), "\n")
2877 ui.note(revset.prettyformat(newtree), "\n")
2878 if opts["optimize"]:
2878 if opts["optimize"]:
2879 weight, optimizedtree = revset.optimize(newtree, True)
2879 weight, optimizedtree = revset.optimize(newtree, True)
2880 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2880 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2881 func = revset.match(ui, expr)
2881 func = revset.match(ui, expr)
2882 for c in func(repo, revset.spanset(repo)):
2882 for c in func(repo, revset.spanset(repo)):
2883 ui.write("%s\n" % c)
2883 ui.write("%s\n" % c)
2884
2884
2885 @command('debugsetparents', [], _('REV1 [REV2]'))
2885 @command('debugsetparents', [], _('REV1 [REV2]'))
2886 def debugsetparents(ui, repo, rev1, rev2=None):
2886 def debugsetparents(ui, repo, rev1, rev2=None):
2887 """manually set the parents of the current working directory
2887 """manually set the parents of the current working directory
2888
2888
2889 This is useful for writing repository conversion tools, but should
2889 This is useful for writing repository conversion tools, but should
2890 be used with care.
2890 be used with care.
2891
2891
2892 Returns 0 on success.
2892 Returns 0 on success.
2893 """
2893 """
2894
2894
2895 r1 = scmutil.revsingle(repo, rev1).node()
2895 r1 = scmutil.revsingle(repo, rev1).node()
2896 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2896 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2897
2897
2898 wlock = repo.wlock()
2898 wlock = repo.wlock()
2899 try:
2899 try:
2900 repo.dirstate.beginparentchange()
2900 repo.dirstate.beginparentchange()
2901 repo.setparents(r1, r2)
2901 repo.setparents(r1, r2)
2902 repo.dirstate.endparentchange()
2902 repo.dirstate.endparentchange()
2903 finally:
2903 finally:
2904 wlock.release()
2904 wlock.release()
2905
2905
2906 @command('debugdirstate|debugstate',
2906 @command('debugdirstate|debugstate',
2907 [('', 'nodates', None, _('do not display the saved mtime')),
2907 [('', 'nodates', None, _('do not display the saved mtime')),
2908 ('', 'datesort', None, _('sort by saved mtime'))],
2908 ('', 'datesort', None, _('sort by saved mtime'))],
2909 _('[OPTION]...'))
2909 _('[OPTION]...'))
2910 def debugstate(ui, repo, nodates=None, datesort=None):
2910 def debugstate(ui, repo, nodates=None, datesort=None):
2911 """show the contents of the current dirstate"""
2911 """show the contents of the current dirstate"""
2912 timestr = ""
2912 timestr = ""
2913 if datesort:
2913 if datesort:
2914 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2914 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2915 else:
2915 else:
2916 keyfunc = None # sort by filename
2916 keyfunc = None # sort by filename
2917 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2917 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2918 if ent[3] == -1:
2918 if ent[3] == -1:
2919 timestr = 'unset '
2919 timestr = 'unset '
2920 elif nodates:
2920 elif nodates:
2921 timestr = 'set '
2921 timestr = 'set '
2922 else:
2922 else:
2923 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2923 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2924 time.localtime(ent[3]))
2924 time.localtime(ent[3]))
2925 if ent[1] & 020000:
2925 if ent[1] & 020000:
2926 mode = 'lnk'
2926 mode = 'lnk'
2927 else:
2927 else:
2928 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2928 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2929 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2929 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2930 for f in repo.dirstate.copies():
2930 for f in repo.dirstate.copies():
2931 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2931 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2932
2932
2933 @command('debugsub',
2933 @command('debugsub',
2934 [('r', 'rev', '',
2934 [('r', 'rev', '',
2935 _('revision to check'), _('REV'))],
2935 _('revision to check'), _('REV'))],
2936 _('[-r REV] [REV]'))
2936 _('[-r REV] [REV]'))
2937 def debugsub(ui, repo, rev=None):
2937 def debugsub(ui, repo, rev=None):
2938 ctx = scmutil.revsingle(repo, rev, None)
2938 ctx = scmutil.revsingle(repo, rev, None)
2939 for k, v in sorted(ctx.substate.items()):
2939 for k, v in sorted(ctx.substate.items()):
2940 ui.write(('path %s\n') % k)
2940 ui.write(('path %s\n') % k)
2941 ui.write((' source %s\n') % v[0])
2941 ui.write((' source %s\n') % v[0])
2942 ui.write((' revision %s\n') % v[1])
2942 ui.write((' revision %s\n') % v[1])
2943
2943
2944 @command('debugsuccessorssets',
2944 @command('debugsuccessorssets',
2945 [],
2945 [],
2946 _('[REV]'))
2946 _('[REV]'))
2947 def debugsuccessorssets(ui, repo, *revs):
2947 def debugsuccessorssets(ui, repo, *revs):
2948 """show set of successors for revision
2948 """show set of successors for revision
2949
2949
2950 A successors set of changeset A is a consistent group of revisions that
2950 A successors set of changeset A is a consistent group of revisions that
2951 succeed A. It contains non-obsolete changesets only.
2951 succeed A. It contains non-obsolete changesets only.
2952
2952
2953 In most cases a changeset A has a single successors set containing a single
2953 In most cases a changeset A has a single successors set containing a single
2954 successor (changeset A replaced by A').
2954 successor (changeset A replaced by A').
2955
2955
2956 A changeset that is made obsolete with no successors are called "pruned".
2956 A changeset that is made obsolete with no successors are called "pruned".
2957 Such changesets have no successors sets at all.
2957 Such changesets have no successors sets at all.
2958
2958
2959 A changeset that has been "split" will have a successors set containing
2959 A changeset that has been "split" will have a successors set containing
2960 more than one successor.
2960 more than one successor.
2961
2961
2962 A changeset that has been rewritten in multiple different ways is called
2962 A changeset that has been rewritten in multiple different ways is called
2963 "divergent". Such changesets have multiple successor sets (each of which
2963 "divergent". Such changesets have multiple successor sets (each of which
2964 may also be split, i.e. have multiple successors).
2964 may also be split, i.e. have multiple successors).
2965
2965
2966 Results are displayed as follows::
2966 Results are displayed as follows::
2967
2967
2968 <rev1>
2968 <rev1>
2969 <successors-1A>
2969 <successors-1A>
2970 <rev2>
2970 <rev2>
2971 <successors-2A>
2971 <successors-2A>
2972 <successors-2B1> <successors-2B2> <successors-2B3>
2972 <successors-2B1> <successors-2B2> <successors-2B3>
2973
2973
2974 Here rev2 has two possible (i.e. divergent) successors sets. The first
2974 Here rev2 has two possible (i.e. divergent) successors sets. The first
2975 holds one element, whereas the second holds three (i.e. the changeset has
2975 holds one element, whereas the second holds three (i.e. the changeset has
2976 been split).
2976 been split).
2977 """
2977 """
2978 # passed to successorssets caching computation from one call to another
2978 # passed to successorssets caching computation from one call to another
2979 cache = {}
2979 cache = {}
2980 ctx2str = str
2980 ctx2str = str
2981 node2str = short
2981 node2str = short
2982 if ui.debug():
2982 if ui.debug():
2983 def ctx2str(ctx):
2983 def ctx2str(ctx):
2984 return ctx.hex()
2984 return ctx.hex()
2985 node2str = hex
2985 node2str = hex
2986 for rev in scmutil.revrange(repo, revs):
2986 for rev in scmutil.revrange(repo, revs):
2987 ctx = repo[rev]
2987 ctx = repo[rev]
2988 ui.write('%s\n'% ctx2str(ctx))
2988 ui.write('%s\n'% ctx2str(ctx))
2989 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2989 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2990 if succsset:
2990 if succsset:
2991 ui.write(' ')
2991 ui.write(' ')
2992 ui.write(node2str(succsset[0]))
2992 ui.write(node2str(succsset[0]))
2993 for node in succsset[1:]:
2993 for node in succsset[1:]:
2994 ui.write(' ')
2994 ui.write(' ')
2995 ui.write(node2str(node))
2995 ui.write(node2str(node))
2996 ui.write('\n')
2996 ui.write('\n')
2997
2997
2998 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
2998 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
2999 def debugwalk(ui, repo, *pats, **opts):
2999 def debugwalk(ui, repo, *pats, **opts):
3000 """show how files match on given patterns"""
3000 """show how files match on given patterns"""
3001 m = scmutil.match(repo[None], pats, opts)
3001 m = scmutil.match(repo[None], pats, opts)
3002 items = list(repo.walk(m))
3002 items = list(repo.walk(m))
3003 if not items:
3003 if not items:
3004 return
3004 return
3005 f = lambda fn: fn
3005 f = lambda fn: fn
3006 if ui.configbool('ui', 'slash') and os.sep != '/':
3006 if ui.configbool('ui', 'slash') and os.sep != '/':
3007 f = lambda fn: util.normpath(fn)
3007 f = lambda fn: util.normpath(fn)
3008 fmt = 'f %%-%ds %%-%ds %%s' % (
3008 fmt = 'f %%-%ds %%-%ds %%s' % (
3009 max([len(abs) for abs in items]),
3009 max([len(abs) for abs in items]),
3010 max([len(m.rel(abs)) for abs in items]))
3010 max([len(m.rel(abs)) for abs in items]))
3011 for abs in items:
3011 for abs in items:
3012 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3012 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
3013 ui.write("%s\n" % line.rstrip())
3013 ui.write("%s\n" % line.rstrip())
3014
3014
3015 @command('debugwireargs',
3015 @command('debugwireargs',
3016 [('', 'three', '', 'three'),
3016 [('', 'three', '', 'three'),
3017 ('', 'four', '', 'four'),
3017 ('', 'four', '', 'four'),
3018 ('', 'five', '', 'five'),
3018 ('', 'five', '', 'five'),
3019 ] + remoteopts,
3019 ] + remoteopts,
3020 _('REPO [OPTIONS]... [ONE [TWO]]'),
3020 _('REPO [OPTIONS]... [ONE [TWO]]'),
3021 norepo=True)
3021 norepo=True)
3022 def debugwireargs(ui, repopath, *vals, **opts):
3022 def debugwireargs(ui, repopath, *vals, **opts):
3023 repo = hg.peer(ui, opts, repopath)
3023 repo = hg.peer(ui, opts, repopath)
3024 for opt in remoteopts:
3024 for opt in remoteopts:
3025 del opts[opt[1]]
3025 del opts[opt[1]]
3026 args = {}
3026 args = {}
3027 for k, v in opts.iteritems():
3027 for k, v in opts.iteritems():
3028 if v:
3028 if v:
3029 args[k] = v
3029 args[k] = v
3030 # run twice to check that we don't mess up the stream for the next command
3030 # run twice to check that we don't mess up the stream for the next command
3031 res1 = repo.debugwireargs(*vals, **args)
3031 res1 = repo.debugwireargs(*vals, **args)
3032 res2 = repo.debugwireargs(*vals, **args)
3032 res2 = repo.debugwireargs(*vals, **args)
3033 ui.write("%s\n" % res1)
3033 ui.write("%s\n" % res1)
3034 if res1 != res2:
3034 if res1 != res2:
3035 ui.warn("%s\n" % res2)
3035 ui.warn("%s\n" % res2)
3036
3036
3037 @command('^diff',
3037 @command('^diff',
3038 [('r', 'rev', [], _('revision'), _('REV')),
3038 [('r', 'rev', [], _('revision'), _('REV')),
3039 ('c', 'change', '', _('change made by revision'), _('REV'))
3039 ('c', 'change', '', _('change made by revision'), _('REV'))
3040 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3040 ] + diffopts + diffopts2 + walkopts + subrepoopts,
3041 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3041 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
3042 inferrepo=True)
3042 inferrepo=True)
3043 def diff(ui, repo, *pats, **opts):
3043 def diff(ui, repo, *pats, **opts):
3044 """diff repository (or selected files)
3044 """diff repository (or selected files)
3045
3045
3046 Show differences between revisions for the specified files.
3046 Show differences between revisions for the specified files.
3047
3047
3048 Differences between files are shown using the unified diff format.
3048 Differences between files are shown using the unified diff format.
3049
3049
3050 .. note::
3050 .. note::
3051
3051
3052 diff may generate unexpected results for merges, as it will
3052 diff may generate unexpected results for merges, as it will
3053 default to comparing against the working directory's first
3053 default to comparing against the working directory's first
3054 parent changeset if no revisions are specified.
3054 parent changeset if no revisions are specified.
3055
3055
3056 When two revision arguments are given, then changes are shown
3056 When two revision arguments are given, then changes are shown
3057 between those revisions. If only one revision is specified then
3057 between those revisions. If only one revision is specified then
3058 that revision is compared to the working directory, and, when no
3058 that revision is compared to the working directory, and, when no
3059 revisions are specified, the working directory files are compared
3059 revisions are specified, the working directory files are compared
3060 to its parent.
3060 to its parent.
3061
3061
3062 Alternatively you can specify -c/--change with a revision to see
3062 Alternatively you can specify -c/--change with a revision to see
3063 the changes in that changeset relative to its first parent.
3063 the changes in that changeset relative to its first parent.
3064
3064
3065 Without the -a/--text option, diff will avoid generating diffs of
3065 Without the -a/--text option, diff will avoid generating diffs of
3066 files it detects as binary. With -a, diff will generate a diff
3066 files it detects as binary. With -a, diff will generate a diff
3067 anyway, probably with undesirable results.
3067 anyway, probably with undesirable results.
3068
3068
3069 Use the -g/--git option to generate diffs in the git extended diff
3069 Use the -g/--git option to generate diffs in the git extended diff
3070 format. For more information, read :hg:`help diffs`.
3070 format. For more information, read :hg:`help diffs`.
3071
3071
3072 .. container:: verbose
3072 .. container:: verbose
3073
3073
3074 Examples:
3074 Examples:
3075
3075
3076 - compare a file in the current working directory to its parent::
3076 - compare a file in the current working directory to its parent::
3077
3077
3078 hg diff foo.c
3078 hg diff foo.c
3079
3079
3080 - compare two historical versions of a directory, with rename info::
3080 - compare two historical versions of a directory, with rename info::
3081
3081
3082 hg diff --git -r 1.0:1.2 lib/
3082 hg diff --git -r 1.0:1.2 lib/
3083
3083
3084 - get change stats relative to the last change on some date::
3084 - get change stats relative to the last change on some date::
3085
3085
3086 hg diff --stat -r "date('may 2')"
3086 hg diff --stat -r "date('may 2')"
3087
3087
3088 - diff all newly-added files that contain a keyword::
3088 - diff all newly-added files that contain a keyword::
3089
3089
3090 hg diff "set:added() and grep(GNU)"
3090 hg diff "set:added() and grep(GNU)"
3091
3091
3092 - compare a revision and its parents::
3092 - compare a revision and its parents::
3093
3093
3094 hg diff -c 9353 # compare against first parent
3094 hg diff -c 9353 # compare against first parent
3095 hg diff -r 9353^:9353 # same using revset syntax
3095 hg diff -r 9353^:9353 # same using revset syntax
3096 hg diff -r 9353^2:9353 # compare against the second parent
3096 hg diff -r 9353^2:9353 # compare against the second parent
3097
3097
3098 Returns 0 on success.
3098 Returns 0 on success.
3099 """
3099 """
3100
3100
3101 revs = opts.get('rev')
3101 revs = opts.get('rev')
3102 change = opts.get('change')
3102 change = opts.get('change')
3103 stat = opts.get('stat')
3103 stat = opts.get('stat')
3104 reverse = opts.get('reverse')
3104 reverse = opts.get('reverse')
3105
3105
3106 if revs and change:
3106 if revs and change:
3107 msg = _('cannot specify --rev and --change at the same time')
3107 msg = _('cannot specify --rev and --change at the same time')
3108 raise util.Abort(msg)
3108 raise util.Abort(msg)
3109 elif change:
3109 elif change:
3110 node2 = scmutil.revsingle(repo, change, None).node()
3110 node2 = scmutil.revsingle(repo, change, None).node()
3111 node1 = repo[node2].p1().node()
3111 node1 = repo[node2].p1().node()
3112 else:
3112 else:
3113 node1, node2 = scmutil.revpair(repo, revs)
3113 node1, node2 = scmutil.revpair(repo, revs)
3114
3114
3115 if reverse:
3115 if reverse:
3116 node1, node2 = node2, node1
3116 node1, node2 = node2, node1
3117
3117
3118 diffopts = patch.diffallopts(ui, opts)
3118 diffopts = patch.diffallopts(ui, opts)
3119 m = scmutil.match(repo[node2], pats, opts)
3119 m = scmutil.match(repo[node2], pats, opts)
3120 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3120 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3121 listsubrepos=opts.get('subrepos'))
3121 listsubrepos=opts.get('subrepos'))
3122
3122
3123 @command('^export',
3123 @command('^export',
3124 [('o', 'output', '',
3124 [('o', 'output', '',
3125 _('print output to file with formatted name'), _('FORMAT')),
3125 _('print output to file with formatted name'), _('FORMAT')),
3126 ('', 'switch-parent', None, _('diff against the second parent')),
3126 ('', 'switch-parent', None, _('diff against the second parent')),
3127 ('r', 'rev', [], _('revisions to export'), _('REV')),
3127 ('r', 'rev', [], _('revisions to export'), _('REV')),
3128 ] + diffopts,
3128 ] + diffopts,
3129 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3129 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3130 def export(ui, repo, *changesets, **opts):
3130 def export(ui, repo, *changesets, **opts):
3131 """dump the header and diffs for one or more changesets
3131 """dump the header and diffs for one or more changesets
3132
3132
3133 Print the changeset header and diffs for one or more revisions.
3133 Print the changeset header and diffs for one or more revisions.
3134 If no revision is given, the parent of the working directory is used.
3134 If no revision is given, the parent of the working directory is used.
3135
3135
3136 The information shown in the changeset header is: author, date,
3136 The information shown in the changeset header is: author, date,
3137 branch name (if non-default), changeset hash, parent(s) and commit
3137 branch name (if non-default), changeset hash, parent(s) and commit
3138 comment.
3138 comment.
3139
3139
3140 .. note::
3140 .. note::
3141
3141
3142 export may generate unexpected diff output for merge
3142 export may generate unexpected diff output for merge
3143 changesets, as it will compare the merge changeset against its
3143 changesets, as it will compare the merge changeset against its
3144 first parent only.
3144 first parent only.
3145
3145
3146 Output may be to a file, in which case the name of the file is
3146 Output may be to a file, in which case the name of the file is
3147 given using a format string. The formatting rules are as follows:
3147 given using a format string. The formatting rules are as follows:
3148
3148
3149 :``%%``: literal "%" character
3149 :``%%``: literal "%" character
3150 :``%H``: changeset hash (40 hexadecimal digits)
3150 :``%H``: changeset hash (40 hexadecimal digits)
3151 :``%N``: number of patches being generated
3151 :``%N``: number of patches being generated
3152 :``%R``: changeset revision number
3152 :``%R``: changeset revision number
3153 :``%b``: basename of the exporting repository
3153 :``%b``: basename of the exporting repository
3154 :``%h``: short-form changeset hash (12 hexadecimal digits)
3154 :``%h``: short-form changeset hash (12 hexadecimal digits)
3155 :``%m``: first line of the commit message (only alphanumeric characters)
3155 :``%m``: first line of the commit message (only alphanumeric characters)
3156 :``%n``: zero-padded sequence number, starting at 1
3156 :``%n``: zero-padded sequence number, starting at 1
3157 :``%r``: zero-padded changeset revision number
3157 :``%r``: zero-padded changeset revision number
3158
3158
3159 Without the -a/--text option, export will avoid generating diffs
3159 Without the -a/--text option, export will avoid generating diffs
3160 of files it detects as binary. With -a, export will generate a
3160 of files it detects as binary. With -a, export will generate a
3161 diff anyway, probably with undesirable results.
3161 diff anyway, probably with undesirable results.
3162
3162
3163 Use the -g/--git option to generate diffs in the git extended diff
3163 Use the -g/--git option to generate diffs in the git extended diff
3164 format. See :hg:`help diffs` for more information.
3164 format. See :hg:`help diffs` for more information.
3165
3165
3166 With the --switch-parent option, the diff will be against the
3166 With the --switch-parent option, the diff will be against the
3167 second parent. It can be useful to review a merge.
3167 second parent. It can be useful to review a merge.
3168
3168
3169 .. container:: verbose
3169 .. container:: verbose
3170
3170
3171 Examples:
3171 Examples:
3172
3172
3173 - use export and import to transplant a bugfix to the current
3173 - use export and import to transplant a bugfix to the current
3174 branch::
3174 branch::
3175
3175
3176 hg export -r 9353 | hg import -
3176 hg export -r 9353 | hg import -
3177
3177
3178 - export all the changesets between two revisions to a file with
3178 - export all the changesets between two revisions to a file with
3179 rename information::
3179 rename information::
3180
3180
3181 hg export --git -r 123:150 > changes.txt
3181 hg export --git -r 123:150 > changes.txt
3182
3182
3183 - split outgoing changes into a series of patches with
3183 - split outgoing changes into a series of patches with
3184 descriptive names::
3184 descriptive names::
3185
3185
3186 hg export -r "outgoing()" -o "%n-%m.patch"
3186 hg export -r "outgoing()" -o "%n-%m.patch"
3187
3187
3188 Returns 0 on success.
3188 Returns 0 on success.
3189 """
3189 """
3190 changesets += tuple(opts.get('rev', []))
3190 changesets += tuple(opts.get('rev', []))
3191 if not changesets:
3191 if not changesets:
3192 changesets = ['.']
3192 changesets = ['.']
3193 revs = scmutil.revrange(repo, changesets)
3193 revs = scmutil.revrange(repo, changesets)
3194 if not revs:
3194 if not revs:
3195 raise util.Abort(_("export requires at least one changeset"))
3195 raise util.Abort(_("export requires at least one changeset"))
3196 if len(revs) > 1:
3196 if len(revs) > 1:
3197 ui.note(_('exporting patches:\n'))
3197 ui.note(_('exporting patches:\n'))
3198 else:
3198 else:
3199 ui.note(_('exporting patch:\n'))
3199 ui.note(_('exporting patch:\n'))
3200 cmdutil.export(repo, revs, template=opts.get('output'),
3200 cmdutil.export(repo, revs, template=opts.get('output'),
3201 switch_parent=opts.get('switch_parent'),
3201 switch_parent=opts.get('switch_parent'),
3202 opts=patch.diffallopts(ui, opts))
3202 opts=patch.diffallopts(ui, opts))
3203
3203
3204 @command('files',
3204 @command('files',
3205 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3205 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3206 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3206 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3207 ] + walkopts + formatteropts,
3207 ] + walkopts + formatteropts,
3208 _('[OPTION]... [PATTERN]...'))
3208 _('[OPTION]... [PATTERN]...'))
3209 def files(ui, repo, *pats, **opts):
3209 def files(ui, repo, *pats, **opts):
3210 """list tracked files
3210 """list tracked files
3211
3211
3212 Print files under Mercurial control in the working directory or
3212 Print files under Mercurial control in the working directory or
3213 specified revision whose names match the given patterns (excluding
3213 specified revision whose names match the given patterns (excluding
3214 removed files).
3214 removed files).
3215
3215
3216 If no patterns are given to match, this command prints the names
3216 If no patterns are given to match, this command prints the names
3217 of all files under Mercurial control in the working copy.
3217 of all files under Mercurial control in the working copy.
3218
3218
3219 .. container:: verbose
3219 .. container:: verbose
3220
3220
3221 Examples:
3221 Examples:
3222
3222
3223 - list all files under the current directory::
3223 - list all files under the current directory::
3224
3224
3225 hg files .
3225 hg files .
3226
3226
3227 - shows sizes and flags for current revision::
3227 - shows sizes and flags for current revision::
3228
3228
3229 hg files -vr .
3229 hg files -vr .
3230
3230
3231 - list all files named README::
3231 - list all files named README::
3232
3232
3233 hg files -I "**/README"
3233 hg files -I "**/README"
3234
3234
3235 - list all binary files::
3235 - list all binary files::
3236
3236
3237 hg files "set:binary()"
3237 hg files "set:binary()"
3238
3238
3239 - find files containing a regular expression::
3239 - find files containing a regular expression::
3240
3240
3241 hg files "set:grep('bob')"
3241 hg files "set:grep('bob')"
3242
3242
3243 - search tracked file contents with xargs and grep::
3243 - search tracked file contents with xargs and grep::
3244
3244
3245 hg files -0 | xargs -0 grep foo
3245 hg files -0 | xargs -0 grep foo
3246
3246
3247 See :hg:`help patterns` and :hg:`help filesets` for more information
3247 See :hg:`help patterns` and :hg:`help filesets` for more information
3248 on specifying file patterns.
3248 on specifying file patterns.
3249
3249
3250 Returns 0 if a match is found, 1 otherwise.
3250 Returns 0 if a match is found, 1 otherwise.
3251
3251
3252 """
3252 """
3253 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3253 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3254 rev = ctx.rev()
3254 rev = ctx.rev()
3255 ret = 1
3255 ret = 1
3256
3256
3257 end = '\n'
3257 end = '\n'
3258 if opts.get('print0'):
3258 if opts.get('print0'):
3259 end = '\0'
3259 end = '\0'
3260 fm = ui.formatter('files', opts)
3260 fm = ui.formatter('files', opts)
3261 fmt = '%s' + end
3261 fmt = '%s' + end
3262
3262
3263 m = scmutil.match(ctx, pats, opts)
3263 m = scmutil.match(ctx, pats, opts)
3264 ds = repo.dirstate
3264 ds = repo.dirstate
3265 for f in ctx.matches(m):
3265 for f in ctx.matches(m):
3266 if rev is None and ds[f] == 'r':
3266 if rev is None and ds[f] == 'r':
3267 continue
3267 continue
3268 fm.startitem()
3268 fm.startitem()
3269 if ui.verbose:
3269 if ui.verbose:
3270 fc = ctx[f]
3270 fc = ctx[f]
3271 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
3271 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
3272 fm.data(abspath=f)
3272 fm.data(abspath=f)
3273 fm.write('path', fmt, m.rel(f))
3273 fm.write('path', fmt, m.rel(f))
3274 ret = 0
3274 ret = 0
3275
3275
3276 fm.end()
3276 fm.end()
3277
3277
3278 return ret
3278 return ret
3279
3279
3280 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3280 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3281 def forget(ui, repo, *pats, **opts):
3281 def forget(ui, repo, *pats, **opts):
3282 """forget the specified files on the next commit
3282 """forget the specified files on the next commit
3283
3283
3284 Mark the specified files so they will no longer be tracked
3284 Mark the specified files so they will no longer be tracked
3285 after the next commit.
3285 after the next commit.
3286
3286
3287 This only removes files from the current branch, not from the
3287 This only removes files from the current branch, not from the
3288 entire project history, and it does not delete them from the
3288 entire project history, and it does not delete them from the
3289 working directory.
3289 working directory.
3290
3290
3291 To undo a forget before the next commit, see :hg:`add`.
3291 To undo a forget before the next commit, see :hg:`add`.
3292
3292
3293 .. container:: verbose
3293 .. container:: verbose
3294
3294
3295 Examples:
3295 Examples:
3296
3296
3297 - forget newly-added binary files::
3297 - forget newly-added binary files::
3298
3298
3299 hg forget "set:added() and binary()"
3299 hg forget "set:added() and binary()"
3300
3300
3301 - forget files that would be excluded by .hgignore::
3301 - forget files that would be excluded by .hgignore::
3302
3302
3303 hg forget "set:hgignore()"
3303 hg forget "set:hgignore()"
3304
3304
3305 Returns 0 on success.
3305 Returns 0 on success.
3306 """
3306 """
3307
3307
3308 if not pats:
3308 if not pats:
3309 raise util.Abort(_('no files specified'))
3309 raise util.Abort(_('no files specified'))
3310
3310
3311 m = scmutil.match(repo[None], pats, opts)
3311 m = scmutil.match(repo[None], pats, opts)
3312 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3312 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3313 return rejected and 1 or 0
3313 return rejected and 1 or 0
3314
3314
3315 @command(
3315 @command(
3316 'graft',
3316 'graft',
3317 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3317 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3318 ('c', 'continue', False, _('resume interrupted graft')),
3318 ('c', 'continue', False, _('resume interrupted graft')),
3319 ('e', 'edit', False, _('invoke editor on commit messages')),
3319 ('e', 'edit', False, _('invoke editor on commit messages')),
3320 ('', 'log', None, _('append graft info to log message')),
3320 ('', 'log', None, _('append graft info to log message')),
3321 ('f', 'force', False, _('force graft')),
3321 ('f', 'force', False, _('force graft')),
3322 ('D', 'currentdate', False,
3322 ('D', 'currentdate', False,
3323 _('record the current date as commit date')),
3323 _('record the current date as commit date')),
3324 ('U', 'currentuser', False,
3324 ('U', 'currentuser', False,
3325 _('record the current user as committer'), _('DATE'))]
3325 _('record the current user as committer'), _('DATE'))]
3326 + commitopts2 + mergetoolopts + dryrunopts,
3326 + commitopts2 + mergetoolopts + dryrunopts,
3327 _('[OPTION]... [-r] REV...'))
3327 _('[OPTION]... [-r] REV...'))
3328 def graft(ui, repo, *revs, **opts):
3328 def graft(ui, repo, *revs, **opts):
3329 '''copy changes from other branches onto the current branch
3329 '''copy changes from other branches onto the current branch
3330
3330
3331 This command uses Mercurial's merge logic to copy individual
3331 This command uses Mercurial's merge logic to copy individual
3332 changes from other branches without merging branches in the
3332 changes from other branches without merging branches in the
3333 history graph. This is sometimes known as 'backporting' or
3333 history graph. This is sometimes known as 'backporting' or
3334 'cherry-picking'. By default, graft will copy user, date, and
3334 'cherry-picking'. By default, graft will copy user, date, and
3335 description from the source changesets.
3335 description from the source changesets.
3336
3336
3337 Changesets that are ancestors of the current revision, that have
3337 Changesets that are ancestors of the current revision, that have
3338 already been grafted, or that are merges will be skipped.
3338 already been grafted, or that are merges will be skipped.
3339
3339
3340 If --log is specified, log messages will have a comment appended
3340 If --log is specified, log messages will have a comment appended
3341 of the form::
3341 of the form::
3342
3342
3343 (grafted from CHANGESETHASH)
3343 (grafted from CHANGESETHASH)
3344
3344
3345 If --force is specified, revisions will be grafted even if they
3345 If --force is specified, revisions will be grafted even if they
3346 are already ancestors of or have been grafted to the destination.
3346 are already ancestors of or have been grafted to the destination.
3347 This is useful when the revisions have since been backed out.
3347 This is useful when the revisions have since been backed out.
3348
3348
3349 If a graft merge results in conflicts, the graft process is
3349 If a graft merge results in conflicts, the graft process is
3350 interrupted so that the current merge can be manually resolved.
3350 interrupted so that the current merge can be manually resolved.
3351 Once all conflicts are addressed, the graft process can be
3351 Once all conflicts are addressed, the graft process can be
3352 continued with the -c/--continue option.
3352 continued with the -c/--continue option.
3353
3353
3354 .. note::
3354 .. note::
3355
3355
3356 The -c/--continue option does not reapply earlier options, except
3356 The -c/--continue option does not reapply earlier options, except
3357 for --force.
3357 for --force.
3358
3358
3359 .. container:: verbose
3359 .. container:: verbose
3360
3360
3361 Examples:
3361 Examples:
3362
3362
3363 - copy a single change to the stable branch and edit its description::
3363 - copy a single change to the stable branch and edit its description::
3364
3364
3365 hg update stable
3365 hg update stable
3366 hg graft --edit 9393
3366 hg graft --edit 9393
3367
3367
3368 - graft a range of changesets with one exception, updating dates::
3368 - graft a range of changesets with one exception, updating dates::
3369
3369
3370 hg graft -D "2085::2093 and not 2091"
3370 hg graft -D "2085::2093 and not 2091"
3371
3371
3372 - continue a graft after resolving conflicts::
3372 - continue a graft after resolving conflicts::
3373
3373
3374 hg graft -c
3374 hg graft -c
3375
3375
3376 - show the source of a grafted changeset::
3376 - show the source of a grafted changeset::
3377
3377
3378 hg log --debug -r .
3378 hg log --debug -r .
3379
3379
3380 See :hg:`help revisions` and :hg:`help revsets` for more about
3380 See :hg:`help revisions` and :hg:`help revsets` for more about
3381 specifying revisions.
3381 specifying revisions.
3382
3382
3383 Returns 0 on successful completion.
3383 Returns 0 on successful completion.
3384 '''
3384 '''
3385
3385
3386 revs = list(revs)
3386 revs = list(revs)
3387 revs.extend(opts['rev'])
3387 revs.extend(opts['rev'])
3388
3388
3389 if not opts.get('user') and opts.get('currentuser'):
3389 if not opts.get('user') and opts.get('currentuser'):
3390 opts['user'] = ui.username()
3390 opts['user'] = ui.username()
3391 if not opts.get('date') and opts.get('currentdate'):
3391 if not opts.get('date') and opts.get('currentdate'):
3392 opts['date'] = "%d %d" % util.makedate()
3392 opts['date'] = "%d %d" % util.makedate()
3393
3393
3394 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3394 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3395
3395
3396 cont = False
3396 cont = False
3397 if opts['continue']:
3397 if opts['continue']:
3398 cont = True
3398 cont = True
3399 if revs:
3399 if revs:
3400 raise util.Abort(_("can't specify --continue and revisions"))
3400 raise util.Abort(_("can't specify --continue and revisions"))
3401 # read in unfinished revisions
3401 # read in unfinished revisions
3402 try:
3402 try:
3403 nodes = repo.vfs.read('graftstate').splitlines()
3403 nodes = repo.vfs.read('graftstate').splitlines()
3404 revs = [repo[node].rev() for node in nodes]
3404 revs = [repo[node].rev() for node in nodes]
3405 except IOError, inst:
3405 except IOError, inst:
3406 if inst.errno != errno.ENOENT:
3406 if inst.errno != errno.ENOENT:
3407 raise
3407 raise
3408 raise util.Abort(_("no graft state found, can't continue"))
3408 raise util.Abort(_("no graft state found, can't continue"))
3409 else:
3409 else:
3410 cmdutil.checkunfinished(repo)
3410 cmdutil.checkunfinished(repo)
3411 cmdutil.bailifchanged(repo)
3411 cmdutil.bailifchanged(repo)
3412 if not revs:
3412 if not revs:
3413 raise util.Abort(_('no revisions specified'))
3413 raise util.Abort(_('no revisions specified'))
3414 revs = scmutil.revrange(repo, revs)
3414 revs = scmutil.revrange(repo, revs)
3415
3415
3416 skipped = set()
3416 skipped = set()
3417 # check for merges
3417 # check for merges
3418 for rev in repo.revs('%ld and merge()', revs):
3418 for rev in repo.revs('%ld and merge()', revs):
3419 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3419 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3420 skipped.add(rev)
3420 skipped.add(rev)
3421 revs = [r for r in revs if r not in skipped]
3421 revs = [r for r in revs if r not in skipped]
3422 if not revs:
3422 if not revs:
3423 return -1
3423 return -1
3424
3424
3425 # Don't check in the --continue case, in effect retaining --force across
3425 # Don't check in the --continue case, in effect retaining --force across
3426 # --continues. That's because without --force, any revisions we decided to
3426 # --continues. That's because without --force, any revisions we decided to
3427 # skip would have been filtered out here, so they wouldn't have made their
3427 # skip would have been filtered out here, so they wouldn't have made their
3428 # way to the graftstate. With --force, any revisions we would have otherwise
3428 # way to the graftstate. With --force, any revisions we would have otherwise
3429 # skipped would not have been filtered out, and if they hadn't been applied
3429 # skipped would not have been filtered out, and if they hadn't been applied
3430 # already, they'd have been in the graftstate.
3430 # already, they'd have been in the graftstate.
3431 if not (cont or opts.get('force')):
3431 if not (cont or opts.get('force')):
3432 # check for ancestors of dest branch
3432 # check for ancestors of dest branch
3433 crev = repo['.'].rev()
3433 crev = repo['.'].rev()
3434 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3434 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3435 # Cannot use x.remove(y) on smart set, this has to be a list.
3435 # Cannot use x.remove(y) on smart set, this has to be a list.
3436 # XXX make this lazy in the future
3436 # XXX make this lazy in the future
3437 revs = list(revs)
3437 revs = list(revs)
3438 # don't mutate while iterating, create a copy
3438 # don't mutate while iterating, create a copy
3439 for rev in list(revs):
3439 for rev in list(revs):
3440 if rev in ancestors:
3440 if rev in ancestors:
3441 ui.warn(_('skipping ancestor revision %d:%s\n') %
3441 ui.warn(_('skipping ancestor revision %d:%s\n') %
3442 (rev, repo[rev]))
3442 (rev, repo[rev]))
3443 # XXX remove on list is slow
3443 # XXX remove on list is slow
3444 revs.remove(rev)
3444 revs.remove(rev)
3445 if not revs:
3445 if not revs:
3446 return -1
3446 return -1
3447
3447
3448 # analyze revs for earlier grafts
3448 # analyze revs for earlier grafts
3449 ids = {}
3449 ids = {}
3450 for ctx in repo.set("%ld", revs):
3450 for ctx in repo.set("%ld", revs):
3451 ids[ctx.hex()] = ctx.rev()
3451 ids[ctx.hex()] = ctx.rev()
3452 n = ctx.extra().get('source')
3452 n = ctx.extra().get('source')
3453 if n:
3453 if n:
3454 ids[n] = ctx.rev()
3454 ids[n] = ctx.rev()
3455
3455
3456 # check ancestors for earlier grafts
3456 # check ancestors for earlier grafts
3457 ui.debug('scanning for duplicate grafts\n')
3457 ui.debug('scanning for duplicate grafts\n')
3458
3458
3459 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3459 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3460 ctx = repo[rev]
3460 ctx = repo[rev]
3461 n = ctx.extra().get('source')
3461 n = ctx.extra().get('source')
3462 if n in ids:
3462 if n in ids:
3463 try:
3463 try:
3464 r = repo[n].rev()
3464 r = repo[n].rev()
3465 except error.RepoLookupError:
3465 except error.RepoLookupError:
3466 r = None
3466 r = None
3467 if r in revs:
3467 if r in revs:
3468 ui.warn(_('skipping revision %d:%s '
3468 ui.warn(_('skipping revision %d:%s '
3469 '(already grafted to %d:%s)\n')
3469 '(already grafted to %d:%s)\n')
3470 % (r, repo[r], rev, ctx))
3470 % (r, repo[r], rev, ctx))
3471 revs.remove(r)
3471 revs.remove(r)
3472 elif ids[n] in revs:
3472 elif ids[n] in revs:
3473 if r is None:
3473 if r is None:
3474 ui.warn(_('skipping already grafted revision %d:%s '
3474 ui.warn(_('skipping already grafted revision %d:%s '
3475 '(%d:%s also has unknown origin %s)\n')
3475 '(%d:%s also has unknown origin %s)\n')
3476 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3476 % (ids[n], repo[ids[n]], rev, ctx, n[:12]))
3477 else:
3477 else:
3478 ui.warn(_('skipping already grafted revision %d:%s '
3478 ui.warn(_('skipping already grafted revision %d:%s '
3479 '(%d:%s also has origin %d:%s)\n')
3479 '(%d:%s also has origin %d:%s)\n')
3480 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3480 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12]))
3481 revs.remove(ids[n])
3481 revs.remove(ids[n])
3482 elif ctx.hex() in ids:
3482 elif ctx.hex() in ids:
3483 r = ids[ctx.hex()]
3483 r = ids[ctx.hex()]
3484 ui.warn(_('skipping already grafted revision %d:%s '
3484 ui.warn(_('skipping already grafted revision %d:%s '
3485 '(was grafted from %d:%s)\n') %
3485 '(was grafted from %d:%s)\n') %
3486 (r, repo[r], rev, ctx))
3486 (r, repo[r], rev, ctx))
3487 revs.remove(r)
3487 revs.remove(r)
3488 if not revs:
3488 if not revs:
3489 return -1
3489 return -1
3490
3490
3491 wlock = repo.wlock()
3491 wlock = repo.wlock()
3492 try:
3492 try:
3493 for pos, ctx in enumerate(repo.set("%ld", revs)):
3493 for pos, ctx in enumerate(repo.set("%ld", revs)):
3494 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3494 desc = '%d:%s "%s"' % (ctx.rev(), ctx,
3495 ctx.description().split('\n', 1)[0])
3495 ctx.description().split('\n', 1)[0])
3496 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3496 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
3497 if names:
3497 if names:
3498 desc += ' (%s)' % ' '.join(names)
3498 desc += ' (%s)' % ' '.join(names)
3499 ui.status(_('grafting %s\n') % desc)
3499 ui.status(_('grafting %s\n') % desc)
3500 if opts.get('dry_run'):
3500 if opts.get('dry_run'):
3501 continue
3501 continue
3502
3502
3503 source = ctx.extra().get('source')
3503 source = ctx.extra().get('source')
3504 if not source:
3504 if not source:
3505 source = ctx.hex()
3505 source = ctx.hex()
3506 extra = {'source': source}
3506 extra = {'source': source}
3507 user = ctx.user()
3507 user = ctx.user()
3508 if opts.get('user'):
3508 if opts.get('user'):
3509 user = opts['user']
3509 user = opts['user']
3510 date = ctx.date()
3510 date = ctx.date()
3511 if opts.get('date'):
3511 if opts.get('date'):
3512 date = opts['date']
3512 date = opts['date']
3513 message = ctx.description()
3513 message = ctx.description()
3514 if opts.get('log'):
3514 if opts.get('log'):
3515 message += '\n(grafted from %s)' % ctx.hex()
3515 message += '\n(grafted from %s)' % ctx.hex()
3516
3516
3517 # we don't merge the first commit when continuing
3517 # we don't merge the first commit when continuing
3518 if not cont:
3518 if not cont:
3519 # perform the graft merge with p1(rev) as 'ancestor'
3519 # perform the graft merge with p1(rev) as 'ancestor'
3520 try:
3520 try:
3521 # ui.forcemerge is an internal variable, do not document
3521 # ui.forcemerge is an internal variable, do not document
3522 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3522 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3523 'graft')
3523 'graft')
3524 stats = mergemod.graft(repo, ctx, ctx.p1(),
3524 stats = mergemod.graft(repo, ctx, ctx.p1(),
3525 ['local', 'graft'])
3525 ['local', 'graft'])
3526 finally:
3526 finally:
3527 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3527 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3528 # report any conflicts
3528 # report any conflicts
3529 if stats and stats[3] > 0:
3529 if stats and stats[3] > 0:
3530 # write out state for --continue
3530 # write out state for --continue
3531 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3531 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3532 repo.vfs.write('graftstate', ''.join(nodelines))
3532 repo.vfs.write('graftstate', ''.join(nodelines))
3533 raise util.Abort(
3533 raise util.Abort(
3534 _("unresolved conflicts, can't continue"),
3534 _("unresolved conflicts, can't continue"),
3535 hint=_('use hg resolve and hg graft --continue'))
3535 hint=_('use hg resolve and hg graft --continue'))
3536 else:
3536 else:
3537 cont = False
3537 cont = False
3538
3538
3539 # commit
3539 # commit
3540 node = repo.commit(text=message, user=user,
3540 node = repo.commit(text=message, user=user,
3541 date=date, extra=extra, editor=editor)
3541 date=date, extra=extra, editor=editor)
3542 if node is None:
3542 if node is None:
3543 ui.warn(
3543 ui.warn(
3544 _('note: graft of %d:%s created no changes to commit\n') %
3544 _('note: graft of %d:%s created no changes to commit\n') %
3545 (ctx.rev(), ctx))
3545 (ctx.rev(), ctx))
3546 finally:
3546 finally:
3547 wlock.release()
3547 wlock.release()
3548
3548
3549 # remove state when we complete successfully
3549 # remove state when we complete successfully
3550 if not opts.get('dry_run'):
3550 if not opts.get('dry_run'):
3551 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3551 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3552
3552
3553 return 0
3553 return 0
3554
3554
3555 @command('grep',
3555 @command('grep',
3556 [('0', 'print0', None, _('end fields with NUL')),
3556 [('0', 'print0', None, _('end fields with NUL')),
3557 ('', 'all', None, _('print all revisions that match')),
3557 ('', 'all', None, _('print all revisions that match')),
3558 ('a', 'text', None, _('treat all files as text')),
3558 ('a', 'text', None, _('treat all files as text')),
3559 ('f', 'follow', None,
3559 ('f', 'follow', None,
3560 _('follow changeset history,'
3560 _('follow changeset history,'
3561 ' or file history across copies and renames')),
3561 ' or file history across copies and renames')),
3562 ('i', 'ignore-case', None, _('ignore case when matching')),
3562 ('i', 'ignore-case', None, _('ignore case when matching')),
3563 ('l', 'files-with-matches', None,
3563 ('l', 'files-with-matches', None,
3564 _('print only filenames and revisions that match')),
3564 _('print only filenames and revisions that match')),
3565 ('n', 'line-number', None, _('print matching line numbers')),
3565 ('n', 'line-number', None, _('print matching line numbers')),
3566 ('r', 'rev', [],
3566 ('r', 'rev', [],
3567 _('only search files changed within revision range'), _('REV')),
3567 _('only search files changed within revision range'), _('REV')),
3568 ('u', 'user', None, _('list the author (long with -v)')),
3568 ('u', 'user', None, _('list the author (long with -v)')),
3569 ('d', 'date', None, _('list the date (short with -q)')),
3569 ('d', 'date', None, _('list the date (short with -q)')),
3570 ] + walkopts,
3570 ] + walkopts,
3571 _('[OPTION]... PATTERN [FILE]...'),
3571 _('[OPTION]... PATTERN [FILE]...'),
3572 inferrepo=True)
3572 inferrepo=True)
3573 def grep(ui, repo, pattern, *pats, **opts):
3573 def grep(ui, repo, pattern, *pats, **opts):
3574 """search for a pattern in specified files and revisions
3574 """search for a pattern in specified files and revisions
3575
3575
3576 Search revisions of files for a regular expression.
3576 Search revisions of files for a regular expression.
3577
3577
3578 This command behaves differently than Unix grep. It only accepts
3578 This command behaves differently than Unix grep. It only accepts
3579 Python/Perl regexps. It searches repository history, not the
3579 Python/Perl regexps. It searches repository history, not the
3580 working directory. It always prints the revision number in which a
3580 working directory. It always prints the revision number in which a
3581 match appears.
3581 match appears.
3582
3582
3583 By default, grep only prints output for the first revision of a
3583 By default, grep only prints output for the first revision of a
3584 file in which it finds a match. To get it to print every revision
3584 file in which it finds a match. To get it to print every revision
3585 that contains a change in match status ("-" for a match that
3585 that contains a change in match status ("-" for a match that
3586 becomes a non-match, or "+" for a non-match that becomes a match),
3586 becomes a non-match, or "+" for a non-match that becomes a match),
3587 use the --all flag.
3587 use the --all flag.
3588
3588
3589 Returns 0 if a match is found, 1 otherwise.
3589 Returns 0 if a match is found, 1 otherwise.
3590 """
3590 """
3591 reflags = re.M
3591 reflags = re.M
3592 if opts.get('ignore_case'):
3592 if opts.get('ignore_case'):
3593 reflags |= re.I
3593 reflags |= re.I
3594 try:
3594 try:
3595 regexp = util.re.compile(pattern, reflags)
3595 regexp = util.re.compile(pattern, reflags)
3596 except re.error, inst:
3596 except re.error, inst:
3597 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3597 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3598 return 1
3598 return 1
3599 sep, eol = ':', '\n'
3599 sep, eol = ':', '\n'
3600 if opts.get('print0'):
3600 if opts.get('print0'):
3601 sep = eol = '\0'
3601 sep = eol = '\0'
3602
3602
3603 getfile = util.lrucachefunc(repo.file)
3603 getfile = util.lrucachefunc(repo.file)
3604
3604
3605 def matchlines(body):
3605 def matchlines(body):
3606 begin = 0
3606 begin = 0
3607 linenum = 0
3607 linenum = 0
3608 while begin < len(body):
3608 while begin < len(body):
3609 match = regexp.search(body, begin)
3609 match = regexp.search(body, begin)
3610 if not match:
3610 if not match:
3611 break
3611 break
3612 mstart, mend = match.span()
3612 mstart, mend = match.span()
3613 linenum += body.count('\n', begin, mstart) + 1
3613 linenum += body.count('\n', begin, mstart) + 1
3614 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3614 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3615 begin = body.find('\n', mend) + 1 or len(body) + 1
3615 begin = body.find('\n', mend) + 1 or len(body) + 1
3616 lend = begin - 1
3616 lend = begin - 1
3617 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3617 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3618
3618
3619 class linestate(object):
3619 class linestate(object):
3620 def __init__(self, line, linenum, colstart, colend):
3620 def __init__(self, line, linenum, colstart, colend):
3621 self.line = line
3621 self.line = line
3622 self.linenum = linenum
3622 self.linenum = linenum
3623 self.colstart = colstart
3623 self.colstart = colstart
3624 self.colend = colend
3624 self.colend = colend
3625
3625
3626 def __hash__(self):
3626 def __hash__(self):
3627 return hash((self.linenum, self.line))
3627 return hash((self.linenum, self.line))
3628
3628
3629 def __eq__(self, other):
3629 def __eq__(self, other):
3630 return self.line == other.line
3630 return self.line == other.line
3631
3631
3632 def __iter__(self):
3632 def __iter__(self):
3633 yield (self.line[:self.colstart], '')
3633 yield (self.line[:self.colstart], '')
3634 yield (self.line[self.colstart:self.colend], 'grep.match')
3634 yield (self.line[self.colstart:self.colend], 'grep.match')
3635 rest = self.line[self.colend:]
3635 rest = self.line[self.colend:]
3636 while rest != '':
3636 while rest != '':
3637 match = regexp.search(rest)
3637 match = regexp.search(rest)
3638 if not match:
3638 if not match:
3639 yield (rest, '')
3639 yield (rest, '')
3640 break
3640 break
3641 mstart, mend = match.span()
3641 mstart, mend = match.span()
3642 yield (rest[:mstart], '')
3642 yield (rest[:mstart], '')
3643 yield (rest[mstart:mend], 'grep.match')
3643 yield (rest[mstart:mend], 'grep.match')
3644 rest = rest[mend:]
3644 rest = rest[mend:]
3645
3645
3646 matches = {}
3646 matches = {}
3647 copies = {}
3647 copies = {}
3648 def grepbody(fn, rev, body):
3648 def grepbody(fn, rev, body):
3649 matches[rev].setdefault(fn, [])
3649 matches[rev].setdefault(fn, [])
3650 m = matches[rev][fn]
3650 m = matches[rev][fn]
3651 for lnum, cstart, cend, line in matchlines(body):
3651 for lnum, cstart, cend, line in matchlines(body):
3652 s = linestate(line, lnum, cstart, cend)
3652 s = linestate(line, lnum, cstart, cend)
3653 m.append(s)
3653 m.append(s)
3654
3654
3655 def difflinestates(a, b):
3655 def difflinestates(a, b):
3656 sm = difflib.SequenceMatcher(None, a, b)
3656 sm = difflib.SequenceMatcher(None, a, b)
3657 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3657 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3658 if tag == 'insert':
3658 if tag == 'insert':
3659 for i in xrange(blo, bhi):
3659 for i in xrange(blo, bhi):
3660 yield ('+', b[i])
3660 yield ('+', b[i])
3661 elif tag == 'delete':
3661 elif tag == 'delete':
3662 for i in xrange(alo, ahi):
3662 for i in xrange(alo, ahi):
3663 yield ('-', a[i])
3663 yield ('-', a[i])
3664 elif tag == 'replace':
3664 elif tag == 'replace':
3665 for i in xrange(alo, ahi):
3665 for i in xrange(alo, ahi):
3666 yield ('-', a[i])
3666 yield ('-', a[i])
3667 for i in xrange(blo, bhi):
3667 for i in xrange(blo, bhi):
3668 yield ('+', b[i])
3668 yield ('+', b[i])
3669
3669
3670 def display(fn, ctx, pstates, states):
3670 def display(fn, ctx, pstates, states):
3671 rev = ctx.rev()
3671 rev = ctx.rev()
3672 datefunc = ui.quiet and util.shortdate or util.datestr
3672 datefunc = ui.quiet and util.shortdate or util.datestr
3673 found = False
3673 found = False
3674 @util.cachefunc
3674 @util.cachefunc
3675 def binary():
3675 def binary():
3676 flog = getfile(fn)
3676 flog = getfile(fn)
3677 return util.binary(flog.read(ctx.filenode(fn)))
3677 return util.binary(flog.read(ctx.filenode(fn)))
3678
3678
3679 if opts.get('all'):
3679 if opts.get('all'):
3680 iter = difflinestates(pstates, states)
3680 iter = difflinestates(pstates, states)
3681 else:
3681 else:
3682 iter = [('', l) for l in states]
3682 iter = [('', l) for l in states]
3683 for change, l in iter:
3683 for change, l in iter:
3684 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3684 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3685
3685
3686 if opts.get('line_number'):
3686 if opts.get('line_number'):
3687 cols.append((str(l.linenum), 'grep.linenumber'))
3687 cols.append((str(l.linenum), 'grep.linenumber'))
3688 if opts.get('all'):
3688 if opts.get('all'):
3689 cols.append((change, 'grep.change'))
3689 cols.append((change, 'grep.change'))
3690 if opts.get('user'):
3690 if opts.get('user'):
3691 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3691 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3692 if opts.get('date'):
3692 if opts.get('date'):
3693 cols.append((datefunc(ctx.date()), 'grep.date'))
3693 cols.append((datefunc(ctx.date()), 'grep.date'))
3694 for col, label in cols[:-1]:
3694 for col, label in cols[:-1]:
3695 ui.write(col, label=label)
3695 ui.write(col, label=label)
3696 ui.write(sep, label='grep.sep')
3696 ui.write(sep, label='grep.sep')
3697 ui.write(cols[-1][0], label=cols[-1][1])
3697 ui.write(cols[-1][0], label=cols[-1][1])
3698 if not opts.get('files_with_matches'):
3698 if not opts.get('files_with_matches'):
3699 ui.write(sep, label='grep.sep')
3699 ui.write(sep, label='grep.sep')
3700 if not opts.get('text') and binary():
3700 if not opts.get('text') and binary():
3701 ui.write(" Binary file matches")
3701 ui.write(" Binary file matches")
3702 else:
3702 else:
3703 for s, label in l:
3703 for s, label in l:
3704 ui.write(s, label=label)
3704 ui.write(s, label=label)
3705 ui.write(eol)
3705 ui.write(eol)
3706 found = True
3706 found = True
3707 if opts.get('files_with_matches'):
3707 if opts.get('files_with_matches'):
3708 break
3708 break
3709 return found
3709 return found
3710
3710
3711 skip = {}
3711 skip = {}
3712 revfiles = {}
3712 revfiles = {}
3713 matchfn = scmutil.match(repo[None], pats, opts)
3713 matchfn = scmutil.match(repo[None], pats, opts)
3714 found = False
3714 found = False
3715 follow = opts.get('follow')
3715 follow = opts.get('follow')
3716
3716
3717 def prep(ctx, fns):
3717 def prep(ctx, fns):
3718 rev = ctx.rev()
3718 rev = ctx.rev()
3719 pctx = ctx.p1()
3719 pctx = ctx.p1()
3720 parent = pctx.rev()
3720 parent = pctx.rev()
3721 matches.setdefault(rev, {})
3721 matches.setdefault(rev, {})
3722 matches.setdefault(parent, {})
3722 matches.setdefault(parent, {})
3723 files = revfiles.setdefault(rev, [])
3723 files = revfiles.setdefault(rev, [])
3724 for fn in fns:
3724 for fn in fns:
3725 flog = getfile(fn)
3725 flog = getfile(fn)
3726 try:
3726 try:
3727 fnode = ctx.filenode(fn)
3727 fnode = ctx.filenode(fn)
3728 except error.LookupError:
3728 except error.LookupError:
3729 continue
3729 continue
3730
3730
3731 copied = flog.renamed(fnode)
3731 copied = flog.renamed(fnode)
3732 copy = follow and copied and copied[0]
3732 copy = follow and copied and copied[0]
3733 if copy:
3733 if copy:
3734 copies.setdefault(rev, {})[fn] = copy
3734 copies.setdefault(rev, {})[fn] = copy
3735 if fn in skip:
3735 if fn in skip:
3736 if copy:
3736 if copy:
3737 skip[copy] = True
3737 skip[copy] = True
3738 continue
3738 continue
3739 files.append(fn)
3739 files.append(fn)
3740
3740
3741 if fn not in matches[rev]:
3741 if fn not in matches[rev]:
3742 grepbody(fn, rev, flog.read(fnode))
3742 grepbody(fn, rev, flog.read(fnode))
3743
3743
3744 pfn = copy or fn
3744 pfn = copy or fn
3745 if pfn not in matches[parent]:
3745 if pfn not in matches[parent]:
3746 try:
3746 try:
3747 fnode = pctx.filenode(pfn)
3747 fnode = pctx.filenode(pfn)
3748 grepbody(pfn, parent, flog.read(fnode))
3748 grepbody(pfn, parent, flog.read(fnode))
3749 except error.LookupError:
3749 except error.LookupError:
3750 pass
3750 pass
3751
3751
3752 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3752 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3753 rev = ctx.rev()
3753 rev = ctx.rev()
3754 parent = ctx.p1().rev()
3754 parent = ctx.p1().rev()
3755 for fn in sorted(revfiles.get(rev, [])):
3755 for fn in sorted(revfiles.get(rev, [])):
3756 states = matches[rev][fn]
3756 states = matches[rev][fn]
3757 copy = copies.get(rev, {}).get(fn)
3757 copy = copies.get(rev, {}).get(fn)
3758 if fn in skip:
3758 if fn in skip:
3759 if copy:
3759 if copy:
3760 skip[copy] = True
3760 skip[copy] = True
3761 continue
3761 continue
3762 pstates = matches.get(parent, {}).get(copy or fn, [])
3762 pstates = matches.get(parent, {}).get(copy or fn, [])
3763 if pstates or states:
3763 if pstates or states:
3764 r = display(fn, ctx, pstates, states)
3764 r = display(fn, ctx, pstates, states)
3765 found = found or r
3765 found = found or r
3766 if r and not opts.get('all'):
3766 if r and not opts.get('all'):
3767 skip[fn] = True
3767 skip[fn] = True
3768 if copy:
3768 if copy:
3769 skip[copy] = True
3769 skip[copy] = True
3770 del matches[rev]
3770 del matches[rev]
3771 del revfiles[rev]
3771 del revfiles[rev]
3772
3772
3773 return not found
3773 return not found
3774
3774
3775 @command('heads',
3775 @command('heads',
3776 [('r', 'rev', '',
3776 [('r', 'rev', '',
3777 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3777 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3778 ('t', 'topo', False, _('show topological heads only')),
3778 ('t', 'topo', False, _('show topological heads only')),
3779 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3779 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3780 ('c', 'closed', False, _('show normal and closed branch heads')),
3780 ('c', 'closed', False, _('show normal and closed branch heads')),
3781 ] + templateopts,
3781 ] + templateopts,
3782 _('[-ct] [-r STARTREV] [REV]...'))
3782 _('[-ct] [-r STARTREV] [REV]...'))
3783 def heads(ui, repo, *branchrevs, **opts):
3783 def heads(ui, repo, *branchrevs, **opts):
3784 """show branch heads
3784 """show branch heads
3785
3785
3786 With no arguments, show all open branch heads in the repository.
3786 With no arguments, show all open branch heads in the repository.
3787 Branch heads are changesets that have no descendants on the
3787 Branch heads are changesets that have no descendants on the
3788 same branch. They are where development generally takes place and
3788 same branch. They are where development generally takes place and
3789 are the usual targets for update and merge operations.
3789 are the usual targets for update and merge operations.
3790
3790
3791 If one or more REVs are given, only open branch heads on the
3791 If one or more REVs are given, only open branch heads on the
3792 branches associated with the specified changesets are shown. This
3792 branches associated with the specified changesets are shown. This
3793 means that you can use :hg:`heads .` to see the heads on the
3793 means that you can use :hg:`heads .` to see the heads on the
3794 currently checked-out branch.
3794 currently checked-out branch.
3795
3795
3796 If -c/--closed is specified, also show branch heads marked closed
3796 If -c/--closed is specified, also show branch heads marked closed
3797 (see :hg:`commit --close-branch`).
3797 (see :hg:`commit --close-branch`).
3798
3798
3799 If STARTREV is specified, only those heads that are descendants of
3799 If STARTREV is specified, only those heads that are descendants of
3800 STARTREV will be displayed.
3800 STARTREV will be displayed.
3801
3801
3802 If -t/--topo is specified, named branch mechanics will be ignored and only
3802 If -t/--topo is specified, named branch mechanics will be ignored and only
3803 topological heads (changesets with no children) will be shown.
3803 topological heads (changesets with no children) will be shown.
3804
3804
3805 Returns 0 if matching heads are found, 1 if not.
3805 Returns 0 if matching heads are found, 1 if not.
3806 """
3806 """
3807
3807
3808 start = None
3808 start = None
3809 if 'rev' in opts:
3809 if 'rev' in opts:
3810 start = scmutil.revsingle(repo, opts['rev'], None).node()
3810 start = scmutil.revsingle(repo, opts['rev'], None).node()
3811
3811
3812 if opts.get('topo'):
3812 if opts.get('topo'):
3813 heads = [repo[h] for h in repo.heads(start)]
3813 heads = [repo[h] for h in repo.heads(start)]
3814 else:
3814 else:
3815 heads = []
3815 heads = []
3816 for branch in repo.branchmap():
3816 for branch in repo.branchmap():
3817 heads += repo.branchheads(branch, start, opts.get('closed'))
3817 heads += repo.branchheads(branch, start, opts.get('closed'))
3818 heads = [repo[h] for h in heads]
3818 heads = [repo[h] for h in heads]
3819
3819
3820 if branchrevs:
3820 if branchrevs:
3821 branches = set(repo[br].branch() for br in branchrevs)
3821 branches = set(repo[br].branch() for br in branchrevs)
3822 heads = [h for h in heads if h.branch() in branches]
3822 heads = [h for h in heads if h.branch() in branches]
3823
3823
3824 if opts.get('active') and branchrevs:
3824 if opts.get('active') and branchrevs:
3825 dagheads = repo.heads(start)
3825 dagheads = repo.heads(start)
3826 heads = [h for h in heads if h.node() in dagheads]
3826 heads = [h for h in heads if h.node() in dagheads]
3827
3827
3828 if branchrevs:
3828 if branchrevs:
3829 haveheads = set(h.branch() for h in heads)
3829 haveheads = set(h.branch() for h in heads)
3830 if branches - haveheads:
3830 if branches - haveheads:
3831 headless = ', '.join(b for b in branches - haveheads)
3831 headless = ', '.join(b for b in branches - haveheads)
3832 msg = _('no open branch heads found on branches %s')
3832 msg = _('no open branch heads found on branches %s')
3833 if opts.get('rev'):
3833 if opts.get('rev'):
3834 msg += _(' (started at %s)') % opts['rev']
3834 msg += _(' (started at %s)') % opts['rev']
3835 ui.warn((msg + '\n') % headless)
3835 ui.warn((msg + '\n') % headless)
3836
3836
3837 if not heads:
3837 if not heads:
3838 return 1
3838 return 1
3839
3839
3840 heads = sorted(heads, key=lambda x: -x.rev())
3840 heads = sorted(heads, key=lambda x: -x.rev())
3841 displayer = cmdutil.show_changeset(ui, repo, opts)
3841 displayer = cmdutil.show_changeset(ui, repo, opts)
3842 for ctx in heads:
3842 for ctx in heads:
3843 displayer.show(ctx)
3843 displayer.show(ctx)
3844 displayer.close()
3844 displayer.close()
3845
3845
3846 @command('help',
3846 @command('help',
3847 [('e', 'extension', None, _('show only help for extensions')),
3847 [('e', 'extension', None, _('show only help for extensions')),
3848 ('c', 'command', None, _('show only help for commands')),
3848 ('c', 'command', None, _('show only help for commands')),
3849 ('k', 'keyword', '', _('show topics matching keyword')),
3849 ('k', 'keyword', '', _('show topics matching keyword')),
3850 ],
3850 ],
3851 _('[-ec] [TOPIC]'),
3851 _('[-ec] [TOPIC]'),
3852 norepo=True)
3852 norepo=True)
3853 def help_(ui, name=None, **opts):
3853 def help_(ui, name=None, **opts):
3854 """show help for a given topic or a help overview
3854 """show help for a given topic or a help overview
3855
3855
3856 With no arguments, print a list of commands with short help messages.
3856 With no arguments, print a list of commands with short help messages.
3857
3857
3858 Given a topic, extension, or command name, print help for that
3858 Given a topic, extension, or command name, print help for that
3859 topic.
3859 topic.
3860
3860
3861 Returns 0 if successful.
3861 Returns 0 if successful.
3862 """
3862 """
3863
3863
3864 textwidth = min(ui.termwidth(), 80) - 2
3864 textwidth = min(ui.termwidth(), 80) - 2
3865
3865
3866 keep = []
3866 keep = []
3867 if ui.verbose:
3867 if ui.verbose:
3868 keep.append('verbose')
3868 keep.append('verbose')
3869 if sys.platform.startswith('win'):
3869 if sys.platform.startswith('win'):
3870 keep.append('windows')
3870 keep.append('windows')
3871 elif sys.platform == 'OpenVMS':
3871 elif sys.platform == 'OpenVMS':
3872 keep.append('vms')
3872 keep.append('vms')
3873 elif sys.platform == 'plan9':
3873 elif sys.platform == 'plan9':
3874 keep.append('plan9')
3874 keep.append('plan9')
3875 else:
3875 else:
3876 keep.append('unix')
3876 keep.append('unix')
3877 keep.append(sys.platform.lower())
3877 keep.append(sys.platform.lower())
3878
3878
3879 section = None
3879 section = None
3880 if name and '.' in name:
3880 if name and '.' in name:
3881 name, section = name.split('.', 1)
3881 name, section = name.split('.', 1)
3882
3882
3883 text = help.help_(ui, name, **opts)
3883 text = help.help_(ui, name, **opts)
3884
3884
3885 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3885 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3886 section=section)
3886 section=section)
3887 if section and not formatted:
3887 if section and not formatted:
3888 raise util.Abort(_("help section not found"))
3888 raise util.Abort(_("help section not found"))
3889
3889
3890 if 'verbose' in pruned:
3890 if 'verbose' in pruned:
3891 keep.append('omitted')
3891 keep.append('omitted')
3892 else:
3892 else:
3893 keep.append('notomitted')
3893 keep.append('notomitted')
3894 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3894 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3895 section=section)
3895 section=section)
3896 ui.write(formatted)
3896 ui.write(formatted)
3897
3897
3898
3898
3899 @command('identify|id',
3899 @command('identify|id',
3900 [('r', 'rev', '',
3900 [('r', 'rev', '',
3901 _('identify the specified revision'), _('REV')),
3901 _('identify the specified revision'), _('REV')),
3902 ('n', 'num', None, _('show local revision number')),
3902 ('n', 'num', None, _('show local revision number')),
3903 ('i', 'id', None, _('show global revision id')),
3903 ('i', 'id', None, _('show global revision id')),
3904 ('b', 'branch', None, _('show branch')),
3904 ('b', 'branch', None, _('show branch')),
3905 ('t', 'tags', None, _('show tags')),
3905 ('t', 'tags', None, _('show tags')),
3906 ('B', 'bookmarks', None, _('show bookmarks')),
3906 ('B', 'bookmarks', None, _('show bookmarks')),
3907 ] + remoteopts,
3907 ] + remoteopts,
3908 _('[-nibtB] [-r REV] [SOURCE]'),
3908 _('[-nibtB] [-r REV] [SOURCE]'),
3909 optionalrepo=True)
3909 optionalrepo=True)
3910 def identify(ui, repo, source=None, rev=None,
3910 def identify(ui, repo, source=None, rev=None,
3911 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3911 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3912 """identify the working copy or specified revision
3912 """identify the working copy or specified revision
3913
3913
3914 Print a summary identifying the repository state at REV using one or
3914 Print a summary identifying the repository state at REV using one or
3915 two parent hash identifiers, followed by a "+" if the working
3915 two parent hash identifiers, followed by a "+" if the working
3916 directory has uncommitted changes, the branch name (if not default),
3916 directory has uncommitted changes, the branch name (if not default),
3917 a list of tags, and a list of bookmarks.
3917 a list of tags, and a list of bookmarks.
3918
3918
3919 When REV is not given, print a summary of the current state of the
3919 When REV is not given, print a summary of the current state of the
3920 repository.
3920 repository.
3921
3921
3922 Specifying a path to a repository root or Mercurial bundle will
3922 Specifying a path to a repository root or Mercurial bundle will
3923 cause lookup to operate on that repository/bundle.
3923 cause lookup to operate on that repository/bundle.
3924
3924
3925 .. container:: verbose
3925 .. container:: verbose
3926
3926
3927 Examples:
3927 Examples:
3928
3928
3929 - generate a build identifier for the working directory::
3929 - generate a build identifier for the working directory::
3930
3930
3931 hg id --id > build-id.dat
3931 hg id --id > build-id.dat
3932
3932
3933 - find the revision corresponding to a tag::
3933 - find the revision corresponding to a tag::
3934
3934
3935 hg id -n -r 1.3
3935 hg id -n -r 1.3
3936
3936
3937 - check the most recent revision of a remote repository::
3937 - check the most recent revision of a remote repository::
3938
3938
3939 hg id -r tip http://selenic.com/hg/
3939 hg id -r tip http://selenic.com/hg/
3940
3940
3941 Returns 0 if successful.
3941 Returns 0 if successful.
3942 """
3942 """
3943
3943
3944 if not repo and not source:
3944 if not repo and not source:
3945 raise util.Abort(_("there is no Mercurial repository here "
3945 raise util.Abort(_("there is no Mercurial repository here "
3946 "(.hg not found)"))
3946 "(.hg not found)"))
3947
3947
3948 hexfunc = ui.debugflag and hex or short
3948 hexfunc = ui.debugflag and hex or short
3949 default = not (num or id or branch or tags or bookmarks)
3949 default = not (num or id or branch or tags or bookmarks)
3950 output = []
3950 output = []
3951 revs = []
3951 revs = []
3952
3952
3953 if source:
3953 if source:
3954 source, branches = hg.parseurl(ui.expandpath(source))
3954 source, branches = hg.parseurl(ui.expandpath(source))
3955 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3955 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3956 repo = peer.local()
3956 repo = peer.local()
3957 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3957 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3958
3958
3959 if not repo:
3959 if not repo:
3960 if num or branch or tags:
3960 if num or branch or tags:
3961 raise util.Abort(
3961 raise util.Abort(
3962 _("can't query remote revision number, branch, or tags"))
3962 _("can't query remote revision number, branch, or tags"))
3963 if not rev and revs:
3963 if not rev and revs:
3964 rev = revs[0]
3964 rev = revs[0]
3965 if not rev:
3965 if not rev:
3966 rev = "tip"
3966 rev = "tip"
3967
3967
3968 remoterev = peer.lookup(rev)
3968 remoterev = peer.lookup(rev)
3969 if default or id:
3969 if default or id:
3970 output = [hexfunc(remoterev)]
3970 output = [hexfunc(remoterev)]
3971
3971
3972 def getbms():
3972 def getbms():
3973 bms = []
3973 bms = []
3974
3974
3975 if 'bookmarks' in peer.listkeys('namespaces'):
3975 if 'bookmarks' in peer.listkeys('namespaces'):
3976 hexremoterev = hex(remoterev)
3976 hexremoterev = hex(remoterev)
3977 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3977 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3978 if bmr == hexremoterev]
3978 if bmr == hexremoterev]
3979
3979
3980 return sorted(bms)
3980 return sorted(bms)
3981
3981
3982 if bookmarks:
3982 if bookmarks:
3983 output.extend(getbms())
3983 output.extend(getbms())
3984 elif default and not ui.quiet:
3984 elif default and not ui.quiet:
3985 # multiple bookmarks for a single parent separated by '/'
3985 # multiple bookmarks for a single parent separated by '/'
3986 bm = '/'.join(getbms())
3986 bm = '/'.join(getbms())
3987 if bm:
3987 if bm:
3988 output.append(bm)
3988 output.append(bm)
3989 else:
3989 else:
3990 if not rev:
3990 if not rev:
3991 ctx = repo[None]
3991 ctx = repo[None]
3992 parents = ctx.parents()
3992 parents = ctx.parents()
3993 changed = ""
3993 changed = ""
3994 if default or id or num:
3994 if default or id or num:
3995 if (util.any(repo.status())
3995 if (util.any(repo.status())
3996 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
3996 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
3997 changed = '+'
3997 changed = '+'
3998 if default or id:
3998 if default or id:
3999 output = ["%s%s" %
3999 output = ["%s%s" %
4000 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4000 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
4001 if num:
4001 if num:
4002 output.append("%s%s" %
4002 output.append("%s%s" %
4003 ('+'.join([str(p.rev()) for p in parents]), changed))
4003 ('+'.join([str(p.rev()) for p in parents]), changed))
4004 else:
4004 else:
4005 ctx = scmutil.revsingle(repo, rev)
4005 ctx = scmutil.revsingle(repo, rev)
4006 if default or id:
4006 if default or id:
4007 output = [hexfunc(ctx.node())]
4007 output = [hexfunc(ctx.node())]
4008 if num:
4008 if num:
4009 output.append(str(ctx.rev()))
4009 output.append(str(ctx.rev()))
4010
4010
4011 if default and not ui.quiet:
4011 if default and not ui.quiet:
4012 b = ctx.branch()
4012 b = ctx.branch()
4013 if b != 'default':
4013 if b != 'default':
4014 output.append("(%s)" % b)
4014 output.append("(%s)" % b)
4015
4015
4016 # multiple tags for a single parent separated by '/'
4016 # multiple tags for a single parent separated by '/'
4017 t = '/'.join(ctx.tags())
4017 t = '/'.join(ctx.tags())
4018 if t:
4018 if t:
4019 output.append(t)
4019 output.append(t)
4020
4020
4021 # multiple bookmarks for a single parent separated by '/'
4021 # multiple bookmarks for a single parent separated by '/'
4022 bm = '/'.join(ctx.bookmarks())
4022 bm = '/'.join(ctx.bookmarks())
4023 if bm:
4023 if bm:
4024 output.append(bm)
4024 output.append(bm)
4025 else:
4025 else:
4026 if branch:
4026 if branch:
4027 output.append(ctx.branch())
4027 output.append(ctx.branch())
4028
4028
4029 if tags:
4029 if tags:
4030 output.extend(ctx.tags())
4030 output.extend(ctx.tags())
4031
4031
4032 if bookmarks:
4032 if bookmarks:
4033 output.extend(ctx.bookmarks())
4033 output.extend(ctx.bookmarks())
4034
4034
4035 ui.write("%s\n" % ' '.join(output))
4035 ui.write("%s\n" % ' '.join(output))
4036
4036
4037 @command('import|patch',
4037 @command('import|patch',
4038 [('p', 'strip', 1,
4038 [('p', 'strip', 1,
4039 _('directory strip option for patch. This has the same '
4039 _('directory strip option for patch. This has the same '
4040 'meaning as the corresponding patch option'), _('NUM')),
4040 'meaning as the corresponding patch option'), _('NUM')),
4041 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4041 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
4042 ('e', 'edit', False, _('invoke editor on commit messages')),
4042 ('e', 'edit', False, _('invoke editor on commit messages')),
4043 ('f', 'force', None,
4043 ('f', 'force', None,
4044 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4044 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
4045 ('', 'no-commit', None,
4045 ('', 'no-commit', None,
4046 _("don't commit, just update the working directory")),
4046 _("don't commit, just update the working directory")),
4047 ('', 'bypass', None,
4047 ('', 'bypass', None,
4048 _("apply patch without touching the working directory")),
4048 _("apply patch without touching the working directory")),
4049 ('', 'partial', None,
4049 ('', 'partial', None,
4050 _('commit even if some hunks fail')),
4050 _('commit even if some hunks fail')),
4051 ('', 'exact', None,
4051 ('', 'exact', None,
4052 _('apply patch to the nodes from which it was generated')),
4052 _('apply patch to the nodes from which it was generated')),
4053 ('', 'import-branch', None,
4053 ('', 'import-branch', None,
4054 _('use any branch information in patch (implied by --exact)'))] +
4054 _('use any branch information in patch (implied by --exact)'))] +
4055 commitopts + commitopts2 + similarityopts,
4055 commitopts + commitopts2 + similarityopts,
4056 _('[OPTION]... PATCH...'))
4056 _('[OPTION]... PATCH...'))
4057 def import_(ui, repo, patch1=None, *patches, **opts):
4057 def import_(ui, repo, patch1=None, *patches, **opts):
4058 """import an ordered set of patches
4058 """import an ordered set of patches
4059
4059
4060 Import a list of patches and commit them individually (unless
4060 Import a list of patches and commit them individually (unless
4061 --no-commit is specified).
4061 --no-commit is specified).
4062
4062
4063 Because import first applies changes to the working directory,
4063 Because import first applies changes to the working directory,
4064 import will abort if there are outstanding changes.
4064 import will abort if there are outstanding changes.
4065
4065
4066 You can import a patch straight from a mail message. Even patches
4066 You can import a patch straight from a mail message. Even patches
4067 as attachments work (to use the body part, it must have type
4067 as attachments work (to use the body part, it must have type
4068 text/plain or text/x-patch). From and Subject headers of email
4068 text/plain or text/x-patch). From and Subject headers of email
4069 message are used as default committer and commit message. All
4069 message are used as default committer and commit message. All
4070 text/plain body parts before first diff are added to commit
4070 text/plain body parts before first diff are added to commit
4071 message.
4071 message.
4072
4072
4073 If the imported patch was generated by :hg:`export`, user and
4073 If the imported patch was generated by :hg:`export`, user and
4074 description from patch override values from message headers and
4074 description from patch override values from message headers and
4075 body. Values given on command line with -m/--message and -u/--user
4075 body. Values given on command line with -m/--message and -u/--user
4076 override these.
4076 override these.
4077
4077
4078 If --exact is specified, import will set the working directory to
4078 If --exact is specified, import will set the working directory to
4079 the parent of each patch before applying it, and will abort if the
4079 the parent of each patch before applying it, and will abort if the
4080 resulting changeset has a different ID than the one recorded in
4080 resulting changeset has a different ID than the one recorded in
4081 the patch. This may happen due to character set problems or other
4081 the patch. This may happen due to character set problems or other
4082 deficiencies in the text patch format.
4082 deficiencies in the text patch format.
4083
4083
4084 Use --bypass to apply and commit patches directly to the
4084 Use --bypass to apply and commit patches directly to the
4085 repository, not touching the working directory. Without --exact,
4085 repository, not touching the working directory. Without --exact,
4086 patches will be applied on top of the working directory parent
4086 patches will be applied on top of the working directory parent
4087 revision.
4087 revision.
4088
4088
4089 With -s/--similarity, hg will attempt to discover renames and
4089 With -s/--similarity, hg will attempt to discover renames and
4090 copies in the patch in the same way as :hg:`addremove`.
4090 copies in the patch in the same way as :hg:`addremove`.
4091
4091
4092 Use --partial to ensure a changeset will be created from the patch
4092 Use --partial to ensure a changeset will be created from the patch
4093 even if some hunks fail to apply. Hunks that fail to apply will be
4093 even if some hunks fail to apply. Hunks that fail to apply will be
4094 written to a <target-file>.rej file. Conflicts can then be resolved
4094 written to a <target-file>.rej file. Conflicts can then be resolved
4095 by hand before :hg:`commit --amend` is run to update the created
4095 by hand before :hg:`commit --amend` is run to update the created
4096 changeset. This flag exists to let people import patches that
4096 changeset. This flag exists to let people import patches that
4097 partially apply without losing the associated metadata (author,
4097 partially apply without losing the associated metadata (author,
4098 date, description, ...). Note that when none of the hunk applies
4098 date, description, ...). Note that when none of the hunk applies
4099 cleanly, :hg:`import --partial` will create an empty changeset,
4099 cleanly, :hg:`import --partial` will create an empty changeset,
4100 importing only the patch metadata.
4100 importing only the patch metadata.
4101
4101
4102 To read a patch from standard input, use "-" as the patch name. If
4102 To read a patch from standard input, use "-" as the patch name. If
4103 a URL is specified, the patch will be downloaded from it.
4103 a URL is specified, the patch will be downloaded from it.
4104 See :hg:`help dates` for a list of formats valid for -d/--date.
4104 See :hg:`help dates` for a list of formats valid for -d/--date.
4105
4105
4106 .. container:: verbose
4106 .. container:: verbose
4107
4107
4108 Examples:
4108 Examples:
4109
4109
4110 - import a traditional patch from a website and detect renames::
4110 - import a traditional patch from a website and detect renames::
4111
4111
4112 hg import -s 80 http://example.com/bugfix.patch
4112 hg import -s 80 http://example.com/bugfix.patch
4113
4113
4114 - import a changeset from an hgweb server::
4114 - import a changeset from an hgweb server::
4115
4115
4116 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4116 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4117
4117
4118 - import all the patches in an Unix-style mbox::
4118 - import all the patches in an Unix-style mbox::
4119
4119
4120 hg import incoming-patches.mbox
4120 hg import incoming-patches.mbox
4121
4121
4122 - attempt to exactly restore an exported changeset (not always
4122 - attempt to exactly restore an exported changeset (not always
4123 possible)::
4123 possible)::
4124
4124
4125 hg import --exact proposed-fix.patch
4125 hg import --exact proposed-fix.patch
4126
4126
4127 Returns 0 on success, 1 on partial success (see --partial).
4127 Returns 0 on success, 1 on partial success (see --partial).
4128 """
4128 """
4129
4129
4130 if not patch1:
4130 if not patch1:
4131 raise util.Abort(_('need at least one patch to import'))
4131 raise util.Abort(_('need at least one patch to import'))
4132
4132
4133 patches = (patch1,) + patches
4133 patches = (patch1,) + patches
4134
4134
4135 date = opts.get('date')
4135 date = opts.get('date')
4136 if date:
4136 if date:
4137 opts['date'] = util.parsedate(date)
4137 opts['date'] = util.parsedate(date)
4138
4138
4139 update = not opts.get('bypass')
4139 update = not opts.get('bypass')
4140 if not update and opts.get('no_commit'):
4140 if not update and opts.get('no_commit'):
4141 raise util.Abort(_('cannot use --no-commit with --bypass'))
4141 raise util.Abort(_('cannot use --no-commit with --bypass'))
4142 try:
4142 try:
4143 sim = float(opts.get('similarity') or 0)
4143 sim = float(opts.get('similarity') or 0)
4144 except ValueError:
4144 except ValueError:
4145 raise util.Abort(_('similarity must be a number'))
4145 raise util.Abort(_('similarity must be a number'))
4146 if sim < 0 or sim > 100:
4146 if sim < 0 or sim > 100:
4147 raise util.Abort(_('similarity must be between 0 and 100'))
4147 raise util.Abort(_('similarity must be between 0 and 100'))
4148 if sim and not update:
4148 if sim and not update:
4149 raise util.Abort(_('cannot use --similarity with --bypass'))
4149 raise util.Abort(_('cannot use --similarity with --bypass'))
4150 if opts.get('exact') and opts.get('edit'):
4150 if opts.get('exact') and opts.get('edit'):
4151 raise util.Abort(_('cannot use --exact with --edit'))
4151 raise util.Abort(_('cannot use --exact with --edit'))
4152
4152
4153 if update:
4153 if update:
4154 cmdutil.checkunfinished(repo)
4154 cmdutil.checkunfinished(repo)
4155 if (opts.get('exact') or not opts.get('force')) and update:
4155 if (opts.get('exact') or not opts.get('force')) and update:
4156 cmdutil.bailifchanged(repo)
4156 cmdutil.bailifchanged(repo)
4157
4157
4158 base = opts["base"]
4158 base = opts["base"]
4159 wlock = lock = tr = None
4159 wlock = lock = tr = None
4160 msgs = []
4160 msgs = []
4161 ret = 0
4161 ret = 0
4162
4162
4163
4163
4164 try:
4164 try:
4165 try:
4165 try:
4166 wlock = repo.wlock()
4166 wlock = repo.wlock()
4167 repo.dirstate.beginparentchange()
4167 repo.dirstate.beginparentchange()
4168 if not opts.get('no_commit'):
4168 if not opts.get('no_commit'):
4169 lock = repo.lock()
4169 lock = repo.lock()
4170 tr = repo.transaction('import')
4170 tr = repo.transaction('import')
4171 parents = repo.parents()
4171 parents = repo.parents()
4172 for patchurl in patches:
4172 for patchurl in patches:
4173 if patchurl == '-':
4173 if patchurl == '-':
4174 ui.status(_('applying patch from stdin\n'))
4174 ui.status(_('applying patch from stdin\n'))
4175 patchfile = ui.fin
4175 patchfile = ui.fin
4176 patchurl = 'stdin' # for error message
4176 patchurl = 'stdin' # for error message
4177 else:
4177 else:
4178 patchurl = os.path.join(base, patchurl)
4178 patchurl = os.path.join(base, patchurl)
4179 ui.status(_('applying %s\n') % patchurl)
4179 ui.status(_('applying %s\n') % patchurl)
4180 patchfile = hg.openpath(ui, patchurl)
4180 patchfile = hg.openpath(ui, patchurl)
4181
4181
4182 haspatch = False
4182 haspatch = False
4183 for hunk in patch.split(patchfile):
4183 for hunk in patch.split(patchfile):
4184 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4184 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4185 parents, opts,
4185 parents, opts,
4186 msgs, hg.clean)
4186 msgs, hg.clean)
4187 if msg:
4187 if msg:
4188 haspatch = True
4188 haspatch = True
4189 ui.note(msg + '\n')
4189 ui.note(msg + '\n')
4190 if update or opts.get('exact'):
4190 if update or opts.get('exact'):
4191 parents = repo.parents()
4191 parents = repo.parents()
4192 else:
4192 else:
4193 parents = [repo[node]]
4193 parents = [repo[node]]
4194 if rej:
4194 if rej:
4195 ui.write_err(_("patch applied partially\n"))
4195 ui.write_err(_("patch applied partially\n"))
4196 ui.write_err(_("(fix the .rej files and run "
4196 ui.write_err(_("(fix the .rej files and run "
4197 "`hg commit --amend`)\n"))
4197 "`hg commit --amend`)\n"))
4198 ret = 1
4198 ret = 1
4199 break
4199 break
4200
4200
4201 if not haspatch:
4201 if not haspatch:
4202 raise util.Abort(_('%s: no diffs found') % patchurl)
4202 raise util.Abort(_('%s: no diffs found') % patchurl)
4203
4203
4204 if tr:
4204 if tr:
4205 tr.close()
4205 tr.close()
4206 if msgs:
4206 if msgs:
4207 repo.savecommitmessage('\n* * *\n'.join(msgs))
4207 repo.savecommitmessage('\n* * *\n'.join(msgs))
4208 repo.dirstate.endparentchange()
4208 repo.dirstate.endparentchange()
4209 return ret
4209 return ret
4210 except: # re-raises
4210 except: # re-raises
4211 # wlock.release() indirectly calls dirstate.write(): since
4211 # wlock.release() indirectly calls dirstate.write(): since
4212 # we're crashing, we do not want to change the working dir
4212 # we're crashing, we do not want to change the working dir
4213 # parent after all, so make sure it writes nothing
4213 # parent after all, so make sure it writes nothing
4214 repo.dirstate.invalidate()
4214 repo.dirstate.invalidate()
4215 raise
4215 raise
4216 finally:
4216 finally:
4217 if tr:
4217 if tr:
4218 tr.release()
4218 tr.release()
4219 release(lock, wlock)
4219 release(lock, wlock)
4220
4220
4221 @command('incoming|in',
4221 @command('incoming|in',
4222 [('f', 'force', None,
4222 [('f', 'force', None,
4223 _('run even if remote repository is unrelated')),
4223 _('run even if remote repository is unrelated')),
4224 ('n', 'newest-first', None, _('show newest record first')),
4224 ('n', 'newest-first', None, _('show newest record first')),
4225 ('', 'bundle', '',
4225 ('', 'bundle', '',
4226 _('file to store the bundles into'), _('FILE')),
4226 _('file to store the bundles into'), _('FILE')),
4227 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4227 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4228 ('B', 'bookmarks', False, _("compare bookmarks")),
4228 ('B', 'bookmarks', False, _("compare bookmarks")),
4229 ('b', 'branch', [],
4229 ('b', 'branch', [],
4230 _('a specific branch you would like to pull'), _('BRANCH')),
4230 _('a specific branch you would like to pull'), _('BRANCH')),
4231 ] + logopts + remoteopts + subrepoopts,
4231 ] + logopts + remoteopts + subrepoopts,
4232 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4232 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4233 def incoming(ui, repo, source="default", **opts):
4233 def incoming(ui, repo, source="default", **opts):
4234 """show new changesets found in source
4234 """show new changesets found in source
4235
4235
4236 Show new changesets found in the specified path/URL or the default
4236 Show new changesets found in the specified path/URL or the default
4237 pull location. These are the changesets that would have been pulled
4237 pull location. These are the changesets that would have been pulled
4238 if a pull at the time you issued this command.
4238 if a pull at the time you issued this command.
4239
4239
4240 For remote repository, using --bundle avoids downloading the
4240 For remote repository, using --bundle avoids downloading the
4241 changesets twice if the incoming is followed by a pull.
4241 changesets twice if the incoming is followed by a pull.
4242
4242
4243 See pull for valid source format details.
4243 See pull for valid source format details.
4244
4244
4245 .. container:: verbose
4245 .. container:: verbose
4246
4246
4247 Examples:
4247 Examples:
4248
4248
4249 - show incoming changes with patches and full description::
4249 - show incoming changes with patches and full description::
4250
4250
4251 hg incoming -vp
4251 hg incoming -vp
4252
4252
4253 - show incoming changes excluding merges, store a bundle::
4253 - show incoming changes excluding merges, store a bundle::
4254
4254
4255 hg in -vpM --bundle incoming.hg
4255 hg in -vpM --bundle incoming.hg
4256 hg pull incoming.hg
4256 hg pull incoming.hg
4257
4257
4258 - briefly list changes inside a bundle::
4258 - briefly list changes inside a bundle::
4259
4259
4260 hg in changes.hg -T "{desc|firstline}\\n"
4260 hg in changes.hg -T "{desc|firstline}\\n"
4261
4261
4262 Returns 0 if there are incoming changes, 1 otherwise.
4262 Returns 0 if there are incoming changes, 1 otherwise.
4263 """
4263 """
4264 if opts.get('graph'):
4264 if opts.get('graph'):
4265 cmdutil.checkunsupportedgraphflags([], opts)
4265 cmdutil.checkunsupportedgraphflags([], opts)
4266 def display(other, chlist, displayer):
4266 def display(other, chlist, displayer):
4267 revdag = cmdutil.graphrevs(other, chlist, opts)
4267 revdag = cmdutil.graphrevs(other, chlist, opts)
4268 showparents = [ctx.node() for ctx in repo[None].parents()]
4268 showparents = [ctx.node() for ctx in repo[None].parents()]
4269 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4269 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4270 graphmod.asciiedges)
4270 graphmod.asciiedges)
4271
4271
4272 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4272 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4273 return 0
4273 return 0
4274
4274
4275 if opts.get('bundle') and opts.get('subrepos'):
4275 if opts.get('bundle') and opts.get('subrepos'):
4276 raise util.Abort(_('cannot combine --bundle and --subrepos'))
4276 raise util.Abort(_('cannot combine --bundle and --subrepos'))
4277
4277
4278 if opts.get('bookmarks'):
4278 if opts.get('bookmarks'):
4279 source, branches = hg.parseurl(ui.expandpath(source),
4279 source, branches = hg.parseurl(ui.expandpath(source),
4280 opts.get('branch'))
4280 opts.get('branch'))
4281 other = hg.peer(repo, opts, source)
4281 other = hg.peer(repo, opts, source)
4282 if 'bookmarks' not in other.listkeys('namespaces'):
4282 if 'bookmarks' not in other.listkeys('namespaces'):
4283 ui.warn(_("remote doesn't support bookmarks\n"))
4283 ui.warn(_("remote doesn't support bookmarks\n"))
4284 return 0
4284 return 0
4285 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4285 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4286 return bookmarks.diff(ui, repo, other)
4286 return bookmarks.diff(ui, repo, other)
4287
4287
4288 repo._subtoppath = ui.expandpath(source)
4288 repo._subtoppath = ui.expandpath(source)
4289 try:
4289 try:
4290 return hg.incoming(ui, repo, source, opts)
4290 return hg.incoming(ui, repo, source, opts)
4291 finally:
4291 finally:
4292 del repo._subtoppath
4292 del repo._subtoppath
4293
4293
4294
4294
4295 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4295 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4296 norepo=True)
4296 norepo=True)
4297 def init(ui, dest=".", **opts):
4297 def init(ui, dest=".", **opts):
4298 """create a new repository in the given directory
4298 """create a new repository in the given directory
4299
4299
4300 Initialize a new repository in the given directory. If the given
4300 Initialize a new repository in the given directory. If the given
4301 directory does not exist, it will be created.
4301 directory does not exist, it will be created.
4302
4302
4303 If no directory is given, the current directory is used.
4303 If no directory is given, the current directory is used.
4304
4304
4305 It is possible to specify an ``ssh://`` URL as the destination.
4305 It is possible to specify an ``ssh://`` URL as the destination.
4306 See :hg:`help urls` for more information.
4306 See :hg:`help urls` for more information.
4307
4307
4308 Returns 0 on success.
4308 Returns 0 on success.
4309 """
4309 """
4310 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4310 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4311
4311
4312 @command('locate',
4312 @command('locate',
4313 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4313 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4314 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4314 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4315 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4315 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4316 ] + walkopts,
4316 ] + walkopts,
4317 _('[OPTION]... [PATTERN]...'))
4317 _('[OPTION]... [PATTERN]...'))
4318 def locate(ui, repo, *pats, **opts):
4318 def locate(ui, repo, *pats, **opts):
4319 """locate files matching specific patterns (DEPRECATED)
4319 """locate files matching specific patterns (DEPRECATED)
4320
4320
4321 Print files under Mercurial control in the working directory whose
4321 Print files under Mercurial control in the working directory whose
4322 names match the given patterns.
4322 names match the given patterns.
4323
4323
4324 By default, this command searches all directories in the working
4324 By default, this command searches all directories in the working
4325 directory. To search just the current directory and its
4325 directory. To search just the current directory and its
4326 subdirectories, use "--include .".
4326 subdirectories, use "--include .".
4327
4327
4328 If no patterns are given to match, this command prints the names
4328 If no patterns are given to match, this command prints the names
4329 of all files under Mercurial control in the working directory.
4329 of all files under Mercurial control in the working directory.
4330
4330
4331 If you want to feed the output of this command into the "xargs"
4331 If you want to feed the output of this command into the "xargs"
4332 command, use the -0 option to both this command and "xargs". This
4332 command, use the -0 option to both this command and "xargs". This
4333 will avoid the problem of "xargs" treating single filenames that
4333 will avoid the problem of "xargs" treating single filenames that
4334 contain whitespace as multiple filenames.
4334 contain whitespace as multiple filenames.
4335
4335
4336 See :hg:`help files` for a more versatile command.
4336 See :hg:`help files` for a more versatile command.
4337
4337
4338 Returns 0 if a match is found, 1 otherwise.
4338 Returns 0 if a match is found, 1 otherwise.
4339 """
4339 """
4340 end = opts.get('print0') and '\0' or '\n'
4340 end = opts.get('print0') and '\0' or '\n'
4341 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4341 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4342
4342
4343 ret = 1
4343 ret = 1
4344 ctx = repo[rev]
4344 ctx = repo[rev]
4345 m = scmutil.match(ctx, pats, opts, default='relglob')
4345 m = scmutil.match(ctx, pats, opts, default='relglob')
4346 m.bad = lambda x, y: False
4346 m.bad = lambda x, y: False
4347
4347
4348 for abs in ctx.matches(m):
4348 for abs in ctx.matches(m):
4349 if opts.get('fullpath'):
4349 if opts.get('fullpath'):
4350 ui.write(repo.wjoin(abs), end)
4350 ui.write(repo.wjoin(abs), end)
4351 else:
4351 else:
4352 ui.write(((pats and m.rel(abs)) or abs), end)
4352 ui.write(((pats and m.rel(abs)) or abs), end)
4353 ret = 0
4353 ret = 0
4354
4354
4355 return ret
4355 return ret
4356
4356
4357 @command('^log|history',
4357 @command('^log|history',
4358 [('f', 'follow', None,
4358 [('f', 'follow', None,
4359 _('follow changeset history, or file history across copies and renames')),
4359 _('follow changeset history, or file history across copies and renames')),
4360 ('', 'follow-first', None,
4360 ('', 'follow-first', None,
4361 _('only follow the first parent of merge changesets (DEPRECATED)')),
4361 _('only follow the first parent of merge changesets (DEPRECATED)')),
4362 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4362 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4363 ('C', 'copies', None, _('show copied files')),
4363 ('C', 'copies', None, _('show copied files')),
4364 ('k', 'keyword', [],
4364 ('k', 'keyword', [],
4365 _('do case-insensitive search for a given text'), _('TEXT')),
4365 _('do case-insensitive search for a given text'), _('TEXT')),
4366 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4366 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4367 ('', 'removed', None, _('include revisions where files were removed')),
4367 ('', 'removed', None, _('include revisions where files were removed')),
4368 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4368 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4369 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4369 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4370 ('', 'only-branch', [],
4370 ('', 'only-branch', [],
4371 _('show only changesets within the given named branch (DEPRECATED)'),
4371 _('show only changesets within the given named branch (DEPRECATED)'),
4372 _('BRANCH')),
4372 _('BRANCH')),
4373 ('b', 'branch', [],
4373 ('b', 'branch', [],
4374 _('show changesets within the given named branch'), _('BRANCH')),
4374 _('show changesets within the given named branch'), _('BRANCH')),
4375 ('P', 'prune', [],
4375 ('P', 'prune', [],
4376 _('do not display revision or any of its ancestors'), _('REV')),
4376 _('do not display revision or any of its ancestors'), _('REV')),
4377 ] + logopts + walkopts,
4377 ] + logopts + walkopts,
4378 _('[OPTION]... [FILE]'),
4378 _('[OPTION]... [FILE]'),
4379 inferrepo=True)
4379 inferrepo=True)
4380 def log(ui, repo, *pats, **opts):
4380 def log(ui, repo, *pats, **opts):
4381 """show revision history of entire repository or files
4381 """show revision history of entire repository or files
4382
4382
4383 Print the revision history of the specified files or the entire
4383 Print the revision history of the specified files or the entire
4384 project.
4384 project.
4385
4385
4386 If no revision range is specified, the default is ``tip:0`` unless
4386 If no revision range is specified, the default is ``tip:0`` unless
4387 --follow is set, in which case the working directory parent is
4387 --follow is set, in which case the working directory parent is
4388 used as the starting revision.
4388 used as the starting revision.
4389
4389
4390 File history is shown without following rename or copy history of
4390 File history is shown without following rename or copy history of
4391 files. Use -f/--follow with a filename to follow history across
4391 files. Use -f/--follow with a filename to follow history across
4392 renames and copies. --follow without a filename will only show
4392 renames and copies. --follow without a filename will only show
4393 ancestors or descendants of the starting revision.
4393 ancestors or descendants of the starting revision.
4394
4394
4395 By default this command prints revision number and changeset id,
4395 By default this command prints revision number and changeset id,
4396 tags, non-trivial parents, user, date and time, and a summary for
4396 tags, non-trivial parents, user, date and time, and a summary for
4397 each commit. When the -v/--verbose switch is used, the list of
4397 each commit. When the -v/--verbose switch is used, the list of
4398 changed files and full commit message are shown.
4398 changed files and full commit message are shown.
4399
4399
4400 With --graph the revisions are shown as an ASCII art DAG with the most
4400 With --graph the revisions are shown as an ASCII art DAG with the most
4401 recent changeset at the top.
4401 recent changeset at the top.
4402 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4402 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4403 and '+' represents a fork where the changeset from the lines below is a
4403 and '+' represents a fork where the changeset from the lines below is a
4404 parent of the 'o' merge on the same line.
4404 parent of the 'o' merge on the same line.
4405
4405
4406 .. note::
4406 .. note::
4407
4407
4408 log -p/--patch may generate unexpected diff output for merge
4408 log -p/--patch may generate unexpected diff output for merge
4409 changesets, as it will only compare the merge changeset against
4409 changesets, as it will only compare the merge changeset against
4410 its first parent. Also, only files different from BOTH parents
4410 its first parent. Also, only files different from BOTH parents
4411 will appear in files:.
4411 will appear in files:.
4412
4412
4413 .. note::
4413 .. note::
4414
4414
4415 for performance reasons, log FILE may omit duplicate changes
4415 for performance reasons, log FILE may omit duplicate changes
4416 made on branches and will not show removals or mode changes. To
4416 made on branches and will not show removals or mode changes. To
4417 see all such changes, use the --removed switch.
4417 see all such changes, use the --removed switch.
4418
4418
4419 .. container:: verbose
4419 .. container:: verbose
4420
4420
4421 Some examples:
4421 Some examples:
4422
4422
4423 - changesets with full descriptions and file lists::
4423 - changesets with full descriptions and file lists::
4424
4424
4425 hg log -v
4425 hg log -v
4426
4426
4427 - changesets ancestral to the working directory::
4427 - changesets ancestral to the working directory::
4428
4428
4429 hg log -f
4429 hg log -f
4430
4430
4431 - last 10 commits on the current branch::
4431 - last 10 commits on the current branch::
4432
4432
4433 hg log -l 10 -b .
4433 hg log -l 10 -b .
4434
4434
4435 - changesets showing all modifications of a file, including removals::
4435 - changesets showing all modifications of a file, including removals::
4436
4436
4437 hg log --removed file.c
4437 hg log --removed file.c
4438
4438
4439 - all changesets that touch a directory, with diffs, excluding merges::
4439 - all changesets that touch a directory, with diffs, excluding merges::
4440
4440
4441 hg log -Mp lib/
4441 hg log -Mp lib/
4442
4442
4443 - all revision numbers that match a keyword::
4443 - all revision numbers that match a keyword::
4444
4444
4445 hg log -k bug --template "{rev}\\n"
4445 hg log -k bug --template "{rev}\\n"
4446
4446
4447 - list available log templates::
4447 - list available log templates::
4448
4448
4449 hg log -T list
4449 hg log -T list
4450
4450
4451 - check if a given changeset is included in a tagged release::
4451 - check if a given changeset is included in a tagged release::
4452
4452
4453 hg log -r "a21ccf and ancestor(1.9)"
4453 hg log -r "a21ccf and ancestor(1.9)"
4454
4454
4455 - find all changesets by some user in a date range::
4455 - find all changesets by some user in a date range::
4456
4456
4457 hg log -k alice -d "may 2008 to jul 2008"
4457 hg log -k alice -d "may 2008 to jul 2008"
4458
4458
4459 - summary of all changesets after the last tag::
4459 - summary of all changesets after the last tag::
4460
4460
4461 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4461 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4462
4462
4463 See :hg:`help dates` for a list of formats valid for -d/--date.
4463 See :hg:`help dates` for a list of formats valid for -d/--date.
4464
4464
4465 See :hg:`help revisions` and :hg:`help revsets` for more about
4465 See :hg:`help revisions` and :hg:`help revsets` for more about
4466 specifying revisions.
4466 specifying revisions.
4467
4467
4468 See :hg:`help templates` for more about pre-packaged styles and
4468 See :hg:`help templates` for more about pre-packaged styles and
4469 specifying custom templates.
4469 specifying custom templates.
4470
4470
4471 Returns 0 on success.
4471 Returns 0 on success.
4472
4472
4473 """
4473 """
4474 if opts.get('graph'):
4474 if opts.get('graph'):
4475 return cmdutil.graphlog(ui, repo, *pats, **opts)
4475 return cmdutil.graphlog(ui, repo, *pats, **opts)
4476
4476
4477 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4477 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4478 limit = cmdutil.loglimit(opts)
4478 limit = cmdutil.loglimit(opts)
4479 count = 0
4479 count = 0
4480
4480
4481 getrenamed = None
4481 getrenamed = None
4482 if opts.get('copies'):
4482 if opts.get('copies'):
4483 endrev = None
4483 endrev = None
4484 if opts.get('rev'):
4484 if opts.get('rev'):
4485 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4485 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4486 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4486 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4487
4487
4488 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4488 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4489 for rev in revs:
4489 for rev in revs:
4490 if count == limit:
4490 if count == limit:
4491 break
4491 break
4492 ctx = repo[rev]
4492 ctx = repo[rev]
4493 copies = None
4493 copies = None
4494 if getrenamed is not None and rev:
4494 if getrenamed is not None and rev:
4495 copies = []
4495 copies = []
4496 for fn in ctx.files():
4496 for fn in ctx.files():
4497 rename = getrenamed(fn, rev)
4497 rename = getrenamed(fn, rev)
4498 if rename:
4498 if rename:
4499 copies.append((fn, rename[0]))
4499 copies.append((fn, rename[0]))
4500 revmatchfn = filematcher and filematcher(ctx.rev()) or None
4500 revmatchfn = filematcher and filematcher(ctx.rev()) or None
4501 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4501 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4502 if displayer.flush(rev):
4502 if displayer.flush(rev):
4503 count += 1
4503 count += 1
4504
4504
4505 displayer.close()
4505 displayer.close()
4506
4506
4507 @command('manifest',
4507 @command('manifest',
4508 [('r', 'rev', '', _('revision to display'), _('REV')),
4508 [('r', 'rev', '', _('revision to display'), _('REV')),
4509 ('', 'all', False, _("list files from all revisions"))]
4509 ('', 'all', False, _("list files from all revisions"))]
4510 + formatteropts,
4510 + formatteropts,
4511 _('[-r REV]'))
4511 _('[-r REV]'))
4512 def manifest(ui, repo, node=None, rev=None, **opts):
4512 def manifest(ui, repo, node=None, rev=None, **opts):
4513 """output the current or given revision of the project manifest
4513 """output the current or given revision of the project manifest
4514
4514
4515 Print a list of version controlled files for the given revision.
4515 Print a list of version controlled files for the given revision.
4516 If no revision is given, the first parent of the working directory
4516 If no revision is given, the first parent of the working directory
4517 is used, or the null revision if no revision is checked out.
4517 is used, or the null revision if no revision is checked out.
4518
4518
4519 With -v, print file permissions, symlink and executable bits.
4519 With -v, print file permissions, symlink and executable bits.
4520 With --debug, print file revision hashes.
4520 With --debug, print file revision hashes.
4521
4521
4522 If option --all is specified, the list of all files from all revisions
4522 If option --all is specified, the list of all files from all revisions
4523 is printed. This includes deleted and renamed files.
4523 is printed. This includes deleted and renamed files.
4524
4524
4525 Returns 0 on success.
4525 Returns 0 on success.
4526 """
4526 """
4527
4527
4528 fm = ui.formatter('manifest', opts)
4528 fm = ui.formatter('manifest', opts)
4529
4529
4530 if opts.get('all'):
4530 if opts.get('all'):
4531 if rev or node:
4531 if rev or node:
4532 raise util.Abort(_("can't specify a revision with --all"))
4532 raise util.Abort(_("can't specify a revision with --all"))
4533
4533
4534 res = []
4534 res = []
4535 prefix = "data/"
4535 prefix = "data/"
4536 suffix = ".i"
4536 suffix = ".i"
4537 plen = len(prefix)
4537 plen = len(prefix)
4538 slen = len(suffix)
4538 slen = len(suffix)
4539 lock = repo.lock()
4539 lock = repo.lock()
4540 try:
4540 try:
4541 for fn, b, size in repo.store.datafiles():
4541 for fn, b, size in repo.store.datafiles():
4542 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4542 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4543 res.append(fn[plen:-slen])
4543 res.append(fn[plen:-slen])
4544 finally:
4544 finally:
4545 lock.release()
4545 lock.release()
4546 for f in res:
4546 for f in res:
4547 fm.startitem()
4547 fm.startitem()
4548 fm.write("path", '%s\n', f)
4548 fm.write("path", '%s\n', f)
4549 fm.end()
4549 fm.end()
4550 return
4550 return
4551
4551
4552 if rev and node:
4552 if rev and node:
4553 raise util.Abort(_("please specify just one revision"))
4553 raise util.Abort(_("please specify just one revision"))
4554
4554
4555 if not node:
4555 if not node:
4556 node = rev
4556 node = rev
4557
4557
4558 char = {'l': '@', 'x': '*', '': ''}
4558 char = {'l': '@', 'x': '*', '': ''}
4559 mode = {'l': '644', 'x': '755', '': '644'}
4559 mode = {'l': '644', 'x': '755', '': '644'}
4560 ctx = scmutil.revsingle(repo, node)
4560 ctx = scmutil.revsingle(repo, node)
4561 mf = ctx.manifest()
4561 mf = ctx.manifest()
4562 for f in ctx:
4562 for f in ctx:
4563 fm.startitem()
4563 fm.startitem()
4564 fl = ctx[f].flags()
4564 fl = ctx[f].flags()
4565 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4565 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4566 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4566 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4567 fm.write('path', '%s\n', f)
4567 fm.write('path', '%s\n', f)
4568 fm.end()
4568 fm.end()
4569
4569
4570 @command('^merge',
4570 @command('^merge',
4571 [('f', 'force', None,
4571 [('f', 'force', None,
4572 _('force a merge including outstanding changes (DEPRECATED)')),
4572 _('force a merge including outstanding changes (DEPRECATED)')),
4573 ('r', 'rev', '', _('revision to merge'), _('REV')),
4573 ('r', 'rev', '', _('revision to merge'), _('REV')),
4574 ('P', 'preview', None,
4574 ('P', 'preview', None,
4575 _('review revisions to merge (no merge is performed)'))
4575 _('review revisions to merge (no merge is performed)'))
4576 ] + mergetoolopts,
4576 ] + mergetoolopts,
4577 _('[-P] [-f] [[-r] REV]'))
4577 _('[-P] [-f] [[-r] REV]'))
4578 def merge(ui, repo, node=None, **opts):
4578 def merge(ui, repo, node=None, **opts):
4579 """merge another revision into working directory
4579 """merge another revision into working directory
4580
4580
4581 The current working directory is updated with all changes made in
4581 The current working directory is updated with all changes made in
4582 the requested revision since the last common predecessor revision.
4582 the requested revision since the last common predecessor revision.
4583
4583
4584 Files that changed between either parent are marked as changed for
4584 Files that changed between either parent are marked as changed for
4585 the next commit and a commit must be performed before any further
4585 the next commit and a commit must be performed before any further
4586 updates to the repository are allowed. The next commit will have
4586 updates to the repository are allowed. The next commit will have
4587 two parents.
4587 two parents.
4588
4588
4589 ``--tool`` can be used to specify the merge tool used for file
4589 ``--tool`` can be used to specify the merge tool used for file
4590 merges. It overrides the HGMERGE environment variable and your
4590 merges. It overrides the HGMERGE environment variable and your
4591 configuration files. See :hg:`help merge-tools` for options.
4591 configuration files. See :hg:`help merge-tools` for options.
4592
4592
4593 If no revision is specified, the working directory's parent is a
4593 If no revision is specified, the working directory's parent is a
4594 head revision, and the current branch contains exactly one other
4594 head revision, and the current branch contains exactly one other
4595 head, the other head is merged with by default. Otherwise, an
4595 head, the other head is merged with by default. Otherwise, an
4596 explicit revision with which to merge with must be provided.
4596 explicit revision with which to merge with must be provided.
4597
4597
4598 :hg:`resolve` must be used to resolve unresolved files.
4598 :hg:`resolve` must be used to resolve unresolved files.
4599
4599
4600 To undo an uncommitted merge, use :hg:`update --clean .` which
4600 To undo an uncommitted merge, use :hg:`update --clean .` which
4601 will check out a clean copy of the original merge parent, losing
4601 will check out a clean copy of the original merge parent, losing
4602 all changes.
4602 all changes.
4603
4603
4604 Returns 0 on success, 1 if there are unresolved files.
4604 Returns 0 on success, 1 if there are unresolved files.
4605 """
4605 """
4606
4606
4607 if opts.get('rev') and node:
4607 if opts.get('rev') and node:
4608 raise util.Abort(_("please specify just one revision"))
4608 raise util.Abort(_("please specify just one revision"))
4609 if not node:
4609 if not node:
4610 node = opts.get('rev')
4610 node = opts.get('rev')
4611
4611
4612 if node:
4612 if node:
4613 node = scmutil.revsingle(repo, node).node()
4613 node = scmutil.revsingle(repo, node).node()
4614
4614
4615 if not node and repo._bookmarkcurrent:
4615 if not node and repo._bookmarkcurrent:
4616 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4616 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4617 curhead = repo[repo._bookmarkcurrent].node()
4617 curhead = repo[repo._bookmarkcurrent].node()
4618 if len(bmheads) == 2:
4618 if len(bmheads) == 2:
4619 if curhead == bmheads[0]:
4619 if curhead == bmheads[0]:
4620 node = bmheads[1]
4620 node = bmheads[1]
4621 else:
4621 else:
4622 node = bmheads[0]
4622 node = bmheads[0]
4623 elif len(bmheads) > 2:
4623 elif len(bmheads) > 2:
4624 raise util.Abort(_("multiple matching bookmarks to merge - "
4624 raise util.Abort(_("multiple matching bookmarks to merge - "
4625 "please merge with an explicit rev or bookmark"),
4625 "please merge with an explicit rev or bookmark"),
4626 hint=_("run 'hg heads' to see all heads"))
4626 hint=_("run 'hg heads' to see all heads"))
4627 elif len(bmheads) <= 1:
4627 elif len(bmheads) <= 1:
4628 raise util.Abort(_("no matching bookmark to merge - "
4628 raise util.Abort(_("no matching bookmark to merge - "
4629 "please merge with an explicit rev or bookmark"),
4629 "please merge with an explicit rev or bookmark"),
4630 hint=_("run 'hg heads' to see all heads"))
4630 hint=_("run 'hg heads' to see all heads"))
4631
4631
4632 if not node and not repo._bookmarkcurrent:
4632 if not node and not repo._bookmarkcurrent:
4633 branch = repo[None].branch()
4633 branch = repo[None].branch()
4634 bheads = repo.branchheads(branch)
4634 bheads = repo.branchheads(branch)
4635 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4635 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4636
4636
4637 if len(nbhs) > 2:
4637 if len(nbhs) > 2:
4638 raise util.Abort(_("branch '%s' has %d heads - "
4638 raise util.Abort(_("branch '%s' has %d heads - "
4639 "please merge with an explicit rev")
4639 "please merge with an explicit rev")
4640 % (branch, len(bheads)),
4640 % (branch, len(bheads)),
4641 hint=_("run 'hg heads .' to see heads"))
4641 hint=_("run 'hg heads .' to see heads"))
4642
4642
4643 parent = repo.dirstate.p1()
4643 parent = repo.dirstate.p1()
4644 if len(nbhs) <= 1:
4644 if len(nbhs) <= 1:
4645 if len(bheads) > 1:
4645 if len(bheads) > 1:
4646 raise util.Abort(_("heads are bookmarked - "
4646 raise util.Abort(_("heads are bookmarked - "
4647 "please merge with an explicit rev"),
4647 "please merge with an explicit rev"),
4648 hint=_("run 'hg heads' to see all heads"))
4648 hint=_("run 'hg heads' to see all heads"))
4649 if len(repo.heads()) > 1:
4649 if len(repo.heads()) > 1:
4650 raise util.Abort(_("branch '%s' has one head - "
4650 raise util.Abort(_("branch '%s' has one head - "
4651 "please merge with an explicit rev")
4651 "please merge with an explicit rev")
4652 % branch,
4652 % branch,
4653 hint=_("run 'hg heads' to see all heads"))
4653 hint=_("run 'hg heads' to see all heads"))
4654 msg, hint = _('nothing to merge'), None
4654 msg, hint = _('nothing to merge'), None
4655 if parent != repo.lookup(branch):
4655 if parent != repo.lookup(branch):
4656 hint = _("use 'hg update' instead")
4656 hint = _("use 'hg update' instead")
4657 raise util.Abort(msg, hint=hint)
4657 raise util.Abort(msg, hint=hint)
4658
4658
4659 if parent not in bheads:
4659 if parent not in bheads:
4660 raise util.Abort(_('working directory not at a head revision'),
4660 raise util.Abort(_('working directory not at a head revision'),
4661 hint=_("use 'hg update' or merge with an "
4661 hint=_("use 'hg update' or merge with an "
4662 "explicit revision"))
4662 "explicit revision"))
4663 if parent == nbhs[0]:
4663 if parent == nbhs[0]:
4664 node = nbhs[-1]
4664 node = nbhs[-1]
4665 else:
4665 else:
4666 node = nbhs[0]
4666 node = nbhs[0]
4667
4667
4668 if opts.get('preview'):
4668 if opts.get('preview'):
4669 # find nodes that are ancestors of p2 but not of p1
4669 # find nodes that are ancestors of p2 but not of p1
4670 p1 = repo.lookup('.')
4670 p1 = repo.lookup('.')
4671 p2 = repo.lookup(node)
4671 p2 = repo.lookup(node)
4672 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4672 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4673
4673
4674 displayer = cmdutil.show_changeset(ui, repo, opts)
4674 displayer = cmdutil.show_changeset(ui, repo, opts)
4675 for node in nodes:
4675 for node in nodes:
4676 displayer.show(repo[node])
4676 displayer.show(repo[node])
4677 displayer.close()
4677 displayer.close()
4678 return 0
4678 return 0
4679
4679
4680 try:
4680 try:
4681 # ui.forcemerge is an internal variable, do not document
4681 # ui.forcemerge is an internal variable, do not document
4682 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4682 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4683 return hg.merge(repo, node, force=opts.get('force'))
4683 return hg.merge(repo, node, force=opts.get('force'))
4684 finally:
4684 finally:
4685 ui.setconfig('ui', 'forcemerge', '', 'merge')
4685 ui.setconfig('ui', 'forcemerge', '', 'merge')
4686
4686
4687 @command('outgoing|out',
4687 @command('outgoing|out',
4688 [('f', 'force', None, _('run even when the destination is unrelated')),
4688 [('f', 'force', None, _('run even when the destination is unrelated')),
4689 ('r', 'rev', [],
4689 ('r', 'rev', [],
4690 _('a changeset intended to be included in the destination'), _('REV')),
4690 _('a changeset intended to be included in the destination'), _('REV')),
4691 ('n', 'newest-first', None, _('show newest record first')),
4691 ('n', 'newest-first', None, _('show newest record first')),
4692 ('B', 'bookmarks', False, _('compare bookmarks')),
4692 ('B', 'bookmarks', False, _('compare bookmarks')),
4693 ('b', 'branch', [], _('a specific branch you would like to push'),
4693 ('b', 'branch', [], _('a specific branch you would like to push'),
4694 _('BRANCH')),
4694 _('BRANCH')),
4695 ] + logopts + remoteopts + subrepoopts,
4695 ] + logopts + remoteopts + subrepoopts,
4696 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4696 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4697 def outgoing(ui, repo, dest=None, **opts):
4697 def outgoing(ui, repo, dest=None, **opts):
4698 """show changesets not found in the destination
4698 """show changesets not found in the destination
4699
4699
4700 Show changesets not found in the specified destination repository
4700 Show changesets not found in the specified destination repository
4701 or the default push location. These are the changesets that would
4701 or the default push location. These are the changesets that would
4702 be pushed if a push was requested.
4702 be pushed if a push was requested.
4703
4703
4704 See pull for details of valid destination formats.
4704 See pull for details of valid destination formats.
4705
4705
4706 Returns 0 if there are outgoing changes, 1 otherwise.
4706 Returns 0 if there are outgoing changes, 1 otherwise.
4707 """
4707 """
4708 if opts.get('graph'):
4708 if opts.get('graph'):
4709 cmdutil.checkunsupportedgraphflags([], opts)
4709 cmdutil.checkunsupportedgraphflags([], opts)
4710 o, other = hg._outgoing(ui, repo, dest, opts)
4710 o, other = hg._outgoing(ui, repo, dest, opts)
4711 if not o:
4711 if not o:
4712 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4712 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4713 return
4713 return
4714
4714
4715 revdag = cmdutil.graphrevs(repo, o, opts)
4715 revdag = cmdutil.graphrevs(repo, o, opts)
4716 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4716 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4717 showparents = [ctx.node() for ctx in repo[None].parents()]
4717 showparents = [ctx.node() for ctx in repo[None].parents()]
4718 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4718 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4719 graphmod.asciiedges)
4719 graphmod.asciiedges)
4720 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4720 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4721 return 0
4721 return 0
4722
4722
4723 if opts.get('bookmarks'):
4723 if opts.get('bookmarks'):
4724 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4724 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4725 dest, branches = hg.parseurl(dest, opts.get('branch'))
4725 dest, branches = hg.parseurl(dest, opts.get('branch'))
4726 other = hg.peer(repo, opts, dest)
4726 other = hg.peer(repo, opts, dest)
4727 if 'bookmarks' not in other.listkeys('namespaces'):
4727 if 'bookmarks' not in other.listkeys('namespaces'):
4728 ui.warn(_("remote doesn't support bookmarks\n"))
4728 ui.warn(_("remote doesn't support bookmarks\n"))
4729 return 0
4729 return 0
4730 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4730 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4731 return bookmarks.diff(ui, other, repo)
4731 return bookmarks.diff(ui, other, repo)
4732
4732
4733 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4733 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4734 try:
4734 try:
4735 return hg.outgoing(ui, repo, dest, opts)
4735 return hg.outgoing(ui, repo, dest, opts)
4736 finally:
4736 finally:
4737 del repo._subtoppath
4737 del repo._subtoppath
4738
4738
4739 @command('parents',
4739 @command('parents',
4740 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4740 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4741 ] + templateopts,
4741 ] + templateopts,
4742 _('[-r REV] [FILE]'),
4742 _('[-r REV] [FILE]'),
4743 inferrepo=True)
4743 inferrepo=True)
4744 def parents(ui, repo, file_=None, **opts):
4744 def parents(ui, repo, file_=None, **opts):
4745 """show the parents of the working directory or revision (DEPRECATED)
4745 """show the parents of the working directory or revision (DEPRECATED)
4746
4746
4747 Print the working directory's parent revisions. If a revision is
4747 Print the working directory's parent revisions. If a revision is
4748 given via -r/--rev, the parent of that revision will be printed.
4748 given via -r/--rev, the parent of that revision will be printed.
4749 If a file argument is given, the revision in which the file was
4749 If a file argument is given, the revision in which the file was
4750 last changed (before the working directory revision or the
4750 last changed (before the working directory revision or the
4751 argument to --rev if given) is printed.
4751 argument to --rev if given) is printed.
4752
4752
4753 See :hg:`summary` and :hg:`help revsets` for related information.
4753 See :hg:`summary` and :hg:`help revsets` for related information.
4754
4754
4755 Returns 0 on success.
4755 Returns 0 on success.
4756 """
4756 """
4757
4757
4758 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4758 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4759
4759
4760 if file_:
4760 if file_:
4761 m = scmutil.match(ctx, (file_,), opts)
4761 m = scmutil.match(ctx, (file_,), opts)
4762 if m.anypats() or len(m.files()) != 1:
4762 if m.anypats() or len(m.files()) != 1:
4763 raise util.Abort(_('can only specify an explicit filename'))
4763 raise util.Abort(_('can only specify an explicit filename'))
4764 file_ = m.files()[0]
4764 file_ = m.files()[0]
4765 filenodes = []
4765 filenodes = []
4766 for cp in ctx.parents():
4766 for cp in ctx.parents():
4767 if not cp:
4767 if not cp:
4768 continue
4768 continue
4769 try:
4769 try:
4770 filenodes.append(cp.filenode(file_))
4770 filenodes.append(cp.filenode(file_))
4771 except error.LookupError:
4771 except error.LookupError:
4772 pass
4772 pass
4773 if not filenodes:
4773 if not filenodes:
4774 raise util.Abort(_("'%s' not found in manifest!") % file_)
4774 raise util.Abort(_("'%s' not found in manifest!") % file_)
4775 p = []
4775 p = []
4776 for fn in filenodes:
4776 for fn in filenodes:
4777 fctx = repo.filectx(file_, fileid=fn)
4777 fctx = repo.filectx(file_, fileid=fn)
4778 p.append(fctx.node())
4778 p.append(fctx.node())
4779 else:
4779 else:
4780 p = [cp.node() for cp in ctx.parents()]
4780 p = [cp.node() for cp in ctx.parents()]
4781
4781
4782 displayer = cmdutil.show_changeset(ui, repo, opts)
4782 displayer = cmdutil.show_changeset(ui, repo, opts)
4783 for n in p:
4783 for n in p:
4784 if n != nullid:
4784 if n != nullid:
4785 displayer.show(repo[n])
4785 displayer.show(repo[n])
4786 displayer.close()
4786 displayer.close()
4787
4787
4788 @command('paths', [], _('[NAME]'), optionalrepo=True)
4788 @command('paths', [], _('[NAME]'), optionalrepo=True)
4789 def paths(ui, repo, search=None):
4789 def paths(ui, repo, search=None):
4790 """show aliases for remote repositories
4790 """show aliases for remote repositories
4791
4791
4792 Show definition of symbolic path name NAME. If no name is given,
4792 Show definition of symbolic path name NAME. If no name is given,
4793 show definition of all available names.
4793 show definition of all available names.
4794
4794
4795 Option -q/--quiet suppresses all output when searching for NAME
4795 Option -q/--quiet suppresses all output when searching for NAME
4796 and shows only the path names when listing all definitions.
4796 and shows only the path names when listing all definitions.
4797
4797
4798 Path names are defined in the [paths] section of your
4798 Path names are defined in the [paths] section of your
4799 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4799 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4800 repository, ``.hg/hgrc`` is used, too.
4800 repository, ``.hg/hgrc`` is used, too.
4801
4801
4802 The path names ``default`` and ``default-push`` have a special
4802 The path names ``default`` and ``default-push`` have a special
4803 meaning. When performing a push or pull operation, they are used
4803 meaning. When performing a push or pull operation, they are used
4804 as fallbacks if no location is specified on the command-line.
4804 as fallbacks if no location is specified on the command-line.
4805 When ``default-push`` is set, it will be used for push and
4805 When ``default-push`` is set, it will be used for push and
4806 ``default`` will be used for pull; otherwise ``default`` is used
4806 ``default`` will be used for pull; otherwise ``default`` is used
4807 as the fallback for both. When cloning a repository, the clone
4807 as the fallback for both. When cloning a repository, the clone
4808 source is written as ``default`` in ``.hg/hgrc``. Note that
4808 source is written as ``default`` in ``.hg/hgrc``. Note that
4809 ``default`` and ``default-push`` apply to all inbound (e.g.
4809 ``default`` and ``default-push`` apply to all inbound (e.g.
4810 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4810 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4811 :hg:`bundle`) operations.
4811 :hg:`bundle`) operations.
4812
4812
4813 See :hg:`help urls` for more information.
4813 See :hg:`help urls` for more information.
4814
4814
4815 Returns 0 on success.
4815 Returns 0 on success.
4816 """
4816 """
4817 if search:
4817 if search:
4818 for name, path in ui.configitems("paths"):
4818 for name, path in ui.configitems("paths"):
4819 if name == search:
4819 if name == search:
4820 ui.status("%s\n" % util.hidepassword(path))
4820 ui.status("%s\n" % util.hidepassword(path))
4821 return
4821 return
4822 if not ui.quiet:
4822 if not ui.quiet:
4823 ui.warn(_("not found!\n"))
4823 ui.warn(_("not found!\n"))
4824 return 1
4824 return 1
4825 else:
4825 else:
4826 for name, path in ui.configitems("paths"):
4826 for name, path in ui.configitems("paths"):
4827 if ui.quiet:
4827 if ui.quiet:
4828 ui.write("%s\n" % name)
4828 ui.write("%s\n" % name)
4829 else:
4829 else:
4830 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4830 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4831
4831
4832 @command('phase',
4832 @command('phase',
4833 [('p', 'public', False, _('set changeset phase to public')),
4833 [('p', 'public', False, _('set changeset phase to public')),
4834 ('d', 'draft', False, _('set changeset phase to draft')),
4834 ('d', 'draft', False, _('set changeset phase to draft')),
4835 ('s', 'secret', False, _('set changeset phase to secret')),
4835 ('s', 'secret', False, _('set changeset phase to secret')),
4836 ('f', 'force', False, _('allow to move boundary backward')),
4836 ('f', 'force', False, _('allow to move boundary backward')),
4837 ('r', 'rev', [], _('target revision'), _('REV')),
4837 ('r', 'rev', [], _('target revision'), _('REV')),
4838 ],
4838 ],
4839 _('[-p|-d|-s] [-f] [-r] REV...'))
4839 _('[-p|-d|-s] [-f] [-r] REV...'))
4840 def phase(ui, repo, *revs, **opts):
4840 def phase(ui, repo, *revs, **opts):
4841 """set or show the current phase name
4841 """set or show the current phase name
4842
4842
4843 With no argument, show the phase name of specified revisions.
4843 With no argument, show the phase name of specified revisions.
4844
4844
4845 With one of -p/--public, -d/--draft or -s/--secret, change the
4845 With one of -p/--public, -d/--draft or -s/--secret, change the
4846 phase value of the specified revisions.
4846 phase value of the specified revisions.
4847
4847
4848 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4848 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4849 lower phase to an higher phase. Phases are ordered as follows::
4849 lower phase to an higher phase. Phases are ordered as follows::
4850
4850
4851 public < draft < secret
4851 public < draft < secret
4852
4852
4853 Returns 0 on success, 1 if no phases were changed or some could not
4853 Returns 0 on success, 1 if no phases were changed or some could not
4854 be changed.
4854 be changed.
4855 """
4855 """
4856 # search for a unique phase argument
4856 # search for a unique phase argument
4857 targetphase = None
4857 targetphase = None
4858 for idx, name in enumerate(phases.phasenames):
4858 for idx, name in enumerate(phases.phasenames):
4859 if opts[name]:
4859 if opts[name]:
4860 if targetphase is not None:
4860 if targetphase is not None:
4861 raise util.Abort(_('only one phase can be specified'))
4861 raise util.Abort(_('only one phase can be specified'))
4862 targetphase = idx
4862 targetphase = idx
4863
4863
4864 # look for specified revision
4864 # look for specified revision
4865 revs = list(revs)
4865 revs = list(revs)
4866 revs.extend(opts['rev'])
4866 revs.extend(opts['rev'])
4867 if not revs:
4867 if not revs:
4868 raise util.Abort(_('no revisions specified'))
4868 raise util.Abort(_('no revisions specified'))
4869
4869
4870 revs = scmutil.revrange(repo, revs)
4870 revs = scmutil.revrange(repo, revs)
4871
4871
4872 lock = None
4872 lock = None
4873 ret = 0
4873 ret = 0
4874 if targetphase is None:
4874 if targetphase is None:
4875 # display
4875 # display
4876 for r in revs:
4876 for r in revs:
4877 ctx = repo[r]
4877 ctx = repo[r]
4878 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4878 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4879 else:
4879 else:
4880 tr = None
4880 tr = None
4881 lock = repo.lock()
4881 lock = repo.lock()
4882 try:
4882 try:
4883 tr = repo.transaction("phase")
4883 tr = repo.transaction("phase")
4884 # set phase
4884 # set phase
4885 if not revs:
4885 if not revs:
4886 raise util.Abort(_('empty revision set'))
4886 raise util.Abort(_('empty revision set'))
4887 nodes = [repo[r].node() for r in revs]
4887 nodes = [repo[r].node() for r in revs]
4888 # moving revision from public to draft may hide them
4888 # moving revision from public to draft may hide them
4889 # We have to check result on an unfiltered repository
4889 # We have to check result on an unfiltered repository
4890 unfi = repo.unfiltered()
4890 unfi = repo.unfiltered()
4891 getphase = unfi._phasecache.phase
4891 getphase = unfi._phasecache.phase
4892 olddata = [getphase(unfi, r) for r in unfi]
4892 olddata = [getphase(unfi, r) for r in unfi]
4893 phases.advanceboundary(repo, tr, targetphase, nodes)
4893 phases.advanceboundary(repo, tr, targetphase, nodes)
4894 if opts['force']:
4894 if opts['force']:
4895 phases.retractboundary(repo, tr, targetphase, nodes)
4895 phases.retractboundary(repo, tr, targetphase, nodes)
4896 tr.close()
4896 tr.close()
4897 finally:
4897 finally:
4898 if tr is not None:
4898 if tr is not None:
4899 tr.release()
4899 tr.release()
4900 lock.release()
4900 lock.release()
4901 getphase = unfi._phasecache.phase
4901 getphase = unfi._phasecache.phase
4902 newdata = [getphase(unfi, r) for r in unfi]
4902 newdata = [getphase(unfi, r) for r in unfi]
4903 changes = sum(newdata[r] != olddata[r] for r in unfi)
4903 changes = sum(newdata[r] != olddata[r] for r in unfi)
4904 cl = unfi.changelog
4904 cl = unfi.changelog
4905 rejected = [n for n in nodes
4905 rejected = [n for n in nodes
4906 if newdata[cl.rev(n)] < targetphase]
4906 if newdata[cl.rev(n)] < targetphase]
4907 if rejected:
4907 if rejected:
4908 ui.warn(_('cannot move %i changesets to a higher '
4908 ui.warn(_('cannot move %i changesets to a higher '
4909 'phase, use --force\n') % len(rejected))
4909 'phase, use --force\n') % len(rejected))
4910 ret = 1
4910 ret = 1
4911 if changes:
4911 if changes:
4912 msg = _('phase changed for %i changesets\n') % changes
4912 msg = _('phase changed for %i changesets\n') % changes
4913 if ret:
4913 if ret:
4914 ui.status(msg)
4914 ui.status(msg)
4915 else:
4915 else:
4916 ui.note(msg)
4916 ui.note(msg)
4917 else:
4917 else:
4918 ui.warn(_('no phases changed\n'))
4918 ui.warn(_('no phases changed\n'))
4919 ret = 1
4919 ret = 1
4920 return ret
4920 return ret
4921
4921
4922 def postincoming(ui, repo, modheads, optupdate, checkout):
4922 def postincoming(ui, repo, modheads, optupdate, checkout):
4923 if modheads == 0:
4923 if modheads == 0:
4924 return
4924 return
4925 if optupdate:
4925 if optupdate:
4926 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
4926 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
4927 try:
4927 try:
4928 ret = hg.update(repo, checkout)
4928 ret = hg.update(repo, checkout)
4929 except util.Abort, inst:
4929 except util.Abort, inst:
4930 ui.warn(_("not updating: %s\n") % str(inst))
4930 ui.warn(_("not updating: %s\n") % str(inst))
4931 if inst.hint:
4931 if inst.hint:
4932 ui.warn(_("(%s)\n") % inst.hint)
4932 ui.warn(_("(%s)\n") % inst.hint)
4933 return 0
4933 return 0
4934 if not ret and not checkout:
4934 if not ret and not checkout:
4935 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4935 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4936 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4936 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4937 return ret
4937 return ret
4938 if modheads > 1:
4938 if modheads > 1:
4939 currentbranchheads = len(repo.branchheads())
4939 currentbranchheads = len(repo.branchheads())
4940 if currentbranchheads == modheads:
4940 if currentbranchheads == modheads:
4941 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4941 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4942 elif currentbranchheads > 1:
4942 elif currentbranchheads > 1:
4943 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4943 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4944 "merge)\n"))
4944 "merge)\n"))
4945 else:
4945 else:
4946 ui.status(_("(run 'hg heads' to see heads)\n"))
4946 ui.status(_("(run 'hg heads' to see heads)\n"))
4947 else:
4947 else:
4948 ui.status(_("(run 'hg update' to get a working copy)\n"))
4948 ui.status(_("(run 'hg update' to get a working copy)\n"))
4949
4949
4950 @command('^pull',
4950 @command('^pull',
4951 [('u', 'update', None,
4951 [('u', 'update', None,
4952 _('update to new branch head if changesets were pulled')),
4952 _('update to new branch head if changesets were pulled')),
4953 ('f', 'force', None, _('run even when remote repository is unrelated')),
4953 ('f', 'force', None, _('run even when remote repository is unrelated')),
4954 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4954 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4955 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4955 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4956 ('b', 'branch', [], _('a specific branch you would like to pull'),
4956 ('b', 'branch', [], _('a specific branch you would like to pull'),
4957 _('BRANCH')),
4957 _('BRANCH')),
4958 ] + remoteopts,
4958 ] + remoteopts,
4959 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4959 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4960 def pull(ui, repo, source="default", **opts):
4960 def pull(ui, repo, source="default", **opts):
4961 """pull changes from the specified source
4961 """pull changes from the specified source
4962
4962
4963 Pull changes from a remote repository to a local one.
4963 Pull changes from a remote repository to a local one.
4964
4964
4965 This finds all changes from the repository at the specified path
4965 This finds all changes from the repository at the specified path
4966 or URL and adds them to a local repository (the current one unless
4966 or URL and adds them to a local repository (the current one unless
4967 -R is specified). By default, this does not update the copy of the
4967 -R is specified). By default, this does not update the copy of the
4968 project in the working directory.
4968 project in the working directory.
4969
4969
4970 Use :hg:`incoming` if you want to see what would have been added
4970 Use :hg:`incoming` if you want to see what would have been added
4971 by a pull at the time you issued this command. If you then decide
4971 by a pull at the time you issued this command. If you then decide
4972 to add those changes to the repository, you should use :hg:`pull
4972 to add those changes to the repository, you should use :hg:`pull
4973 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4973 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4974
4974
4975 If SOURCE is omitted, the 'default' path will be used.
4975 If SOURCE is omitted, the 'default' path will be used.
4976 See :hg:`help urls` for more information.
4976 See :hg:`help urls` for more information.
4977
4977
4978 Returns 0 on success, 1 if an update had unresolved files.
4978 Returns 0 on success, 1 if an update had unresolved files.
4979 """
4979 """
4980 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4980 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4981 other = hg.peer(repo, opts, source)
4981 other = hg.peer(repo, opts, source)
4982 try:
4982 try:
4983 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4983 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4984 revs, checkout = hg.addbranchrevs(repo, other, branches,
4984 revs, checkout = hg.addbranchrevs(repo, other, branches,
4985 opts.get('rev'))
4985 opts.get('rev'))
4986
4986
4987 remotebookmarks = other.listkeys('bookmarks')
4987 remotebookmarks = other.listkeys('bookmarks')
4988
4988
4989 if opts.get('bookmark'):
4989 if opts.get('bookmark'):
4990 if not revs:
4990 if not revs:
4991 revs = []
4991 revs = []
4992 for b in opts['bookmark']:
4992 for b in opts['bookmark']:
4993 if b not in remotebookmarks:
4993 if b not in remotebookmarks:
4994 raise util.Abort(_('remote bookmark %s not found!') % b)
4994 raise util.Abort(_('remote bookmark %s not found!') % b)
4995 revs.append(remotebookmarks[b])
4995 revs.append(remotebookmarks[b])
4996
4996
4997 if revs:
4997 if revs:
4998 try:
4998 try:
4999 revs = [other.lookup(rev) for rev in revs]
4999 revs = [other.lookup(rev) for rev in revs]
5000 except error.CapabilityError:
5000 except error.CapabilityError:
5001 err = _("other repository doesn't support revision lookup, "
5001 err = _("other repository doesn't support revision lookup, "
5002 "so a rev cannot be specified.")
5002 "so a rev cannot be specified.")
5003 raise util.Abort(err)
5003 raise util.Abort(err)
5004
5004
5005 modheads = exchange.pull(repo, other, heads=revs,
5005 modheads = exchange.pull(repo, other, heads=revs,
5006 force=opts.get('force'),
5006 force=opts.get('force'),
5007 bookmarks=opts.get('bookmark', ())).cgresult
5007 bookmarks=opts.get('bookmark', ())).cgresult
5008 if checkout:
5008 if checkout:
5009 checkout = str(repo.changelog.rev(other.lookup(checkout)))
5009 checkout = str(repo.changelog.rev(other.lookup(checkout)))
5010 repo._subtoppath = source
5010 repo._subtoppath = source
5011 try:
5011 try:
5012 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
5012 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
5013
5013
5014 finally:
5014 finally:
5015 del repo._subtoppath
5015 del repo._subtoppath
5016
5016
5017 finally:
5017 finally:
5018 other.close()
5018 other.close()
5019 return ret
5019 return ret
5020
5020
5021 @command('^push',
5021 @command('^push',
5022 [('f', 'force', None, _('force push')),
5022 [('f', 'force', None, _('force push')),
5023 ('r', 'rev', [],
5023 ('r', 'rev', [],
5024 _('a changeset intended to be included in the destination'),
5024 _('a changeset intended to be included in the destination'),
5025 _('REV')),
5025 _('REV')),
5026 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5026 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
5027 ('b', 'branch', [],
5027 ('b', 'branch', [],
5028 _('a specific branch you would like to push'), _('BRANCH')),
5028 _('a specific branch you would like to push'), _('BRANCH')),
5029 ('', 'new-branch', False, _('allow pushing a new branch')),
5029 ('', 'new-branch', False, _('allow pushing a new branch')),
5030 ] + remoteopts,
5030 ] + remoteopts,
5031 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5031 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
5032 def push(ui, repo, dest=None, **opts):
5032 def push(ui, repo, dest=None, **opts):
5033 """push changes to the specified destination
5033 """push changes to the specified destination
5034
5034
5035 Push changesets from the local repository to the specified
5035 Push changesets from the local repository to the specified
5036 destination.
5036 destination.
5037
5037
5038 This operation is symmetrical to pull: it is identical to a pull
5038 This operation is symmetrical to pull: it is identical to a pull
5039 in the destination repository from the current one.
5039 in the destination repository from the current one.
5040
5040
5041 By default, push will not allow creation of new heads at the
5041 By default, push will not allow creation of new heads at the
5042 destination, since multiple heads would make it unclear which head
5042 destination, since multiple heads would make it unclear which head
5043 to use. In this situation, it is recommended to pull and merge
5043 to use. In this situation, it is recommended to pull and merge
5044 before pushing.
5044 before pushing.
5045
5045
5046 Use --new-branch if you want to allow push to create a new named
5046 Use --new-branch if you want to allow push to create a new named
5047 branch that is not present at the destination. This allows you to
5047 branch that is not present at the destination. This allows you to
5048 only create a new branch without forcing other changes.
5048 only create a new branch without forcing other changes.
5049
5049
5050 .. note::
5050 .. note::
5051
5051
5052 Extra care should be taken with the -f/--force option,
5052 Extra care should be taken with the -f/--force option,
5053 which will push all new heads on all branches, an action which will
5053 which will push all new heads on all branches, an action which will
5054 almost always cause confusion for collaborators.
5054 almost always cause confusion for collaborators.
5055
5055
5056 If -r/--rev is used, the specified revision and all its ancestors
5056 If -r/--rev is used, the specified revision and all its ancestors
5057 will be pushed to the remote repository.
5057 will be pushed to the remote repository.
5058
5058
5059 If -B/--bookmark is used, the specified bookmarked revision, its
5059 If -B/--bookmark is used, the specified bookmarked revision, its
5060 ancestors, and the bookmark will be pushed to the remote
5060 ancestors, and the bookmark will be pushed to the remote
5061 repository.
5061 repository.
5062
5062
5063 Please see :hg:`help urls` for important details about ``ssh://``
5063 Please see :hg:`help urls` for important details about ``ssh://``
5064 URLs. If DESTINATION is omitted, a default path will be used.
5064 URLs. If DESTINATION is omitted, a default path will be used.
5065
5065
5066 Returns 0 if push was successful, 1 if nothing to push.
5066 Returns 0 if push was successful, 1 if nothing to push.
5067 """
5067 """
5068
5068
5069 if opts.get('bookmark'):
5069 if opts.get('bookmark'):
5070 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5070 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5071 for b in opts['bookmark']:
5071 for b in opts['bookmark']:
5072 # translate -B options to -r so changesets get pushed
5072 # translate -B options to -r so changesets get pushed
5073 if b in repo._bookmarks:
5073 if b in repo._bookmarks:
5074 opts.setdefault('rev', []).append(b)
5074 opts.setdefault('rev', []).append(b)
5075 else:
5075 else:
5076 # if we try to push a deleted bookmark, translate it to null
5076 # if we try to push a deleted bookmark, translate it to null
5077 # this lets simultaneous -r, -b options continue working
5077 # this lets simultaneous -r, -b options continue working
5078 opts.setdefault('rev', []).append("null")
5078 opts.setdefault('rev', []).append("null")
5079
5079
5080 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5080 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5081 dest, branches = hg.parseurl(dest, opts.get('branch'))
5081 dest, branches = hg.parseurl(dest, opts.get('branch'))
5082 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5082 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5083 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5083 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5084 try:
5084 try:
5085 other = hg.peer(repo, opts, dest)
5085 other = hg.peer(repo, opts, dest)
5086 except error.RepoError:
5086 except error.RepoError:
5087 if dest == "default-push":
5087 if dest == "default-push":
5088 raise util.Abort(_("default repository not configured!"),
5088 raise util.Abort(_("default repository not configured!"),
5089 hint=_('see the "path" section in "hg help config"'))
5089 hint=_('see the "path" section in "hg help config"'))
5090 else:
5090 else:
5091 raise
5091 raise
5092
5092
5093 if revs:
5093 if revs:
5094 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5094 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5095
5095
5096 repo._subtoppath = dest
5096 repo._subtoppath = dest
5097 try:
5097 try:
5098 # push subrepos depth-first for coherent ordering
5098 # push subrepos depth-first for coherent ordering
5099 c = repo['']
5099 c = repo['']
5100 subs = c.substate # only repos that are committed
5100 subs = c.substate # only repos that are committed
5101 for s in sorted(subs):
5101 for s in sorted(subs):
5102 result = c.sub(s).push(opts)
5102 result = c.sub(s).push(opts)
5103 if result == 0:
5103 if result == 0:
5104 return not result
5104 return not result
5105 finally:
5105 finally:
5106 del repo._subtoppath
5106 del repo._subtoppath
5107 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5107 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5108 newbranch=opts.get('new_branch'),
5108 newbranch=opts.get('new_branch'),
5109 bookmarks=opts.get('bookmark', ()))
5109 bookmarks=opts.get('bookmark', ()))
5110
5110
5111 result = not pushop.cgresult
5111 result = not pushop.cgresult
5112
5112
5113 if pushop.bkresult is not None:
5113 if pushop.bkresult is not None:
5114 if pushop.bkresult == 2:
5114 if pushop.bkresult == 2:
5115 result = 2
5115 result = 2
5116 elif not result and pushop.bkresult:
5116 elif not result and pushop.bkresult:
5117 result = 2
5117 result = 2
5118
5118
5119 return result
5119 return result
5120
5120
5121 @command('recover', [])
5121 @command('recover', [])
5122 def recover(ui, repo):
5122 def recover(ui, repo):
5123 """roll back an interrupted transaction
5123 """roll back an interrupted transaction
5124
5124
5125 Recover from an interrupted commit or pull.
5125 Recover from an interrupted commit or pull.
5126
5126
5127 This command tries to fix the repository status after an
5127 This command tries to fix the repository status after an
5128 interrupted operation. It should only be necessary when Mercurial
5128 interrupted operation. It should only be necessary when Mercurial
5129 suggests it.
5129 suggests it.
5130
5130
5131 Returns 0 if successful, 1 if nothing to recover or verify fails.
5131 Returns 0 if successful, 1 if nothing to recover or verify fails.
5132 """
5132 """
5133 if repo.recover():
5133 if repo.recover():
5134 return hg.verify(repo)
5134 return hg.verify(repo)
5135 return 1
5135 return 1
5136
5136
5137 @command('^remove|rm',
5137 @command('^remove|rm',
5138 [('A', 'after', None, _('record delete for missing files')),
5138 [('A', 'after', None, _('record delete for missing files')),
5139 ('f', 'force', None,
5139 ('f', 'force', None,
5140 _('remove (and delete) file even if added or modified')),
5140 _('remove (and delete) file even if added or modified')),
5141 ] + subrepoopts + walkopts,
5141 ] + subrepoopts + walkopts,
5142 _('[OPTION]... FILE...'),
5142 _('[OPTION]... FILE...'),
5143 inferrepo=True)
5143 inferrepo=True)
5144 def remove(ui, repo, *pats, **opts):
5144 def remove(ui, repo, *pats, **opts):
5145 """remove the specified files on the next commit
5145 """remove the specified files on the next commit
5146
5146
5147 Schedule the indicated files for removal from the current branch.
5147 Schedule the indicated files for removal from the current branch.
5148
5148
5149 This command schedules the files to be removed at the next commit.
5149 This command schedules the files to be removed at the next commit.
5150 To undo a remove before that, see :hg:`revert`. To undo added
5150 To undo a remove before that, see :hg:`revert`. To undo added
5151 files, see :hg:`forget`.
5151 files, see :hg:`forget`.
5152
5152
5153 .. container:: verbose
5153 .. container:: verbose
5154
5154
5155 -A/--after can be used to remove only files that have already
5155 -A/--after can be used to remove only files that have already
5156 been deleted, -f/--force can be used to force deletion, and -Af
5156 been deleted, -f/--force can be used to force deletion, and -Af
5157 can be used to remove files from the next revision without
5157 can be used to remove files from the next revision without
5158 deleting them from the working directory.
5158 deleting them from the working directory.
5159
5159
5160 The following table details the behavior of remove for different
5160 The following table details the behavior of remove for different
5161 file states (columns) and option combinations (rows). The file
5161 file states (columns) and option combinations (rows). The file
5162 states are Added [A], Clean [C], Modified [M] and Missing [!]
5162 states are Added [A], Clean [C], Modified [M] and Missing [!]
5163 (as reported by :hg:`status`). The actions are Warn, Remove
5163 (as reported by :hg:`status`). The actions are Warn, Remove
5164 (from branch) and Delete (from disk):
5164 (from branch) and Delete (from disk):
5165
5165
5166 ========= == == == ==
5166 ========= == == == ==
5167 opt/state A C M !
5167 opt/state A C M !
5168 ========= == == == ==
5168 ========= == == == ==
5169 none W RD W R
5169 none W RD W R
5170 -f R RD RD R
5170 -f R RD RD R
5171 -A W W W R
5171 -A W W W R
5172 -Af R R R R
5172 -Af R R R R
5173 ========= == == == ==
5173 ========= == == == ==
5174
5174
5175 Note that remove never deletes files in Added [A] state from the
5175 Note that remove never deletes files in Added [A] state from the
5176 working directory, not even if option --force is specified.
5176 working directory, not even if option --force is specified.
5177
5177
5178 Returns 0 on success, 1 if any warnings encountered.
5178 Returns 0 on success, 1 if any warnings encountered.
5179 """
5179 """
5180
5180
5181 after, force = opts.get('after'), opts.get('force')
5181 after, force = opts.get('after'), opts.get('force')
5182 if not pats and not after:
5182 if not pats and not after:
5183 raise util.Abort(_('no files specified'))
5183 raise util.Abort(_('no files specified'))
5184
5184
5185 m = scmutil.match(repo[None], pats, opts)
5185 m = scmutil.match(repo[None], pats, opts)
5186 subrepos = opts.get('subrepos')
5186 subrepos = opts.get('subrepos')
5187 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5187 return cmdutil.remove(ui, repo, m, "", after, force, subrepos)
5188
5188
5189 @command('rename|move|mv',
5189 @command('rename|move|mv',
5190 [('A', 'after', None, _('record a rename that has already occurred')),
5190 [('A', 'after', None, _('record a rename that has already occurred')),
5191 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5191 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5192 ] + walkopts + dryrunopts,
5192 ] + walkopts + dryrunopts,
5193 _('[OPTION]... SOURCE... DEST'))
5193 _('[OPTION]... SOURCE... DEST'))
5194 def rename(ui, repo, *pats, **opts):
5194 def rename(ui, repo, *pats, **opts):
5195 """rename files; equivalent of copy + remove
5195 """rename files; equivalent of copy + remove
5196
5196
5197 Mark dest as copies of sources; mark sources for deletion. If dest
5197 Mark dest as copies of sources; mark sources for deletion. If dest
5198 is a directory, copies are put in that directory. If dest is a
5198 is a directory, copies are put in that directory. If dest is a
5199 file, there can only be one source.
5199 file, there can only be one source.
5200
5200
5201 By default, this command copies the contents of files as they
5201 By default, this command copies the contents of files as they
5202 exist in the working directory. If invoked with -A/--after, the
5202 exist in the working directory. If invoked with -A/--after, the
5203 operation is recorded, but no copying is performed.
5203 operation is recorded, but no copying is performed.
5204
5204
5205 This command takes effect at the next commit. To undo a rename
5205 This command takes effect at the next commit. To undo a rename
5206 before that, see :hg:`revert`.
5206 before that, see :hg:`revert`.
5207
5207
5208 Returns 0 on success, 1 if errors are encountered.
5208 Returns 0 on success, 1 if errors are encountered.
5209 """
5209 """
5210 wlock = repo.wlock(False)
5210 wlock = repo.wlock(False)
5211 try:
5211 try:
5212 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5212 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5213 finally:
5213 finally:
5214 wlock.release()
5214 wlock.release()
5215
5215
5216 @command('resolve',
5216 @command('resolve',
5217 [('a', 'all', None, _('select all unresolved files')),
5217 [('a', 'all', None, _('select all unresolved files')),
5218 ('l', 'list', None, _('list state of files needing merge')),
5218 ('l', 'list', None, _('list state of files needing merge')),
5219 ('m', 'mark', None, _('mark files as resolved')),
5219 ('m', 'mark', None, _('mark files as resolved')),
5220 ('u', 'unmark', None, _('mark files as unresolved')),
5220 ('u', 'unmark', None, _('mark files as unresolved')),
5221 ('n', 'no-status', None, _('hide status prefix'))]
5221 ('n', 'no-status', None, _('hide status prefix'))]
5222 + mergetoolopts + walkopts,
5222 + mergetoolopts + walkopts,
5223 _('[OPTION]... [FILE]...'),
5223 _('[OPTION]... [FILE]...'),
5224 inferrepo=True)
5224 inferrepo=True)
5225 def resolve(ui, repo, *pats, **opts):
5225 def resolve(ui, repo, *pats, **opts):
5226 """redo merges or set/view the merge status of files
5226 """redo merges or set/view the merge status of files
5227
5227
5228 Merges with unresolved conflicts are often the result of
5228 Merges with unresolved conflicts are often the result of
5229 non-interactive merging using the ``internal:merge`` configuration
5229 non-interactive merging using the ``internal:merge`` configuration
5230 setting, or a command-line merge tool like ``diff3``. The resolve
5230 setting, or a command-line merge tool like ``diff3``. The resolve
5231 command is used to manage the files involved in a merge, after
5231 command is used to manage the files involved in a merge, after
5232 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5232 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5233 working directory must have two parents). See :hg:`help
5233 working directory must have two parents). See :hg:`help
5234 merge-tools` for information on configuring merge tools.
5234 merge-tools` for information on configuring merge tools.
5235
5235
5236 The resolve command can be used in the following ways:
5236 The resolve command can be used in the following ways:
5237
5237
5238 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5238 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5239 files, discarding any previous merge attempts. Re-merging is not
5239 files, discarding any previous merge attempts. Re-merging is not
5240 performed for files already marked as resolved. Use ``--all/-a``
5240 performed for files already marked as resolved. Use ``--all/-a``
5241 to select all unresolved files. ``--tool`` can be used to specify
5241 to select all unresolved files. ``--tool`` can be used to specify
5242 the merge tool used for the given files. It overrides the HGMERGE
5242 the merge tool used for the given files. It overrides the HGMERGE
5243 environment variable and your configuration files. Previous file
5243 environment variable and your configuration files. Previous file
5244 contents are saved with a ``.orig`` suffix.
5244 contents are saved with a ``.orig`` suffix.
5245
5245
5246 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5246 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5247 (e.g. after having manually fixed-up the files). The default is
5247 (e.g. after having manually fixed-up the files). The default is
5248 to mark all unresolved files.
5248 to mark all unresolved files.
5249
5249
5250 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5250 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5251 default is to mark all resolved files.
5251 default is to mark all resolved files.
5252
5252
5253 - :hg:`resolve -l`: list files which had or still have conflicts.
5253 - :hg:`resolve -l`: list files which had or still have conflicts.
5254 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5254 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5255
5255
5256 Note that Mercurial will not let you commit files with unresolved
5256 Note that Mercurial will not let you commit files with unresolved
5257 merge conflicts. You must use :hg:`resolve -m ...` before you can
5257 merge conflicts. You must use :hg:`resolve -m ...` before you can
5258 commit after a conflicting merge.
5258 commit after a conflicting merge.
5259
5259
5260 Returns 0 on success, 1 if any files fail a resolve attempt.
5260 Returns 0 on success, 1 if any files fail a resolve attempt.
5261 """
5261 """
5262
5262
5263 all, mark, unmark, show, nostatus = \
5263 all, mark, unmark, show, nostatus = \
5264 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5264 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5265
5265
5266 if (show and (mark or unmark)) or (mark and unmark):
5266 if (show and (mark or unmark)) or (mark and unmark):
5267 raise util.Abort(_("too many options specified"))
5267 raise util.Abort(_("too many options specified"))
5268 if pats and all:
5268 if pats and all:
5269 raise util.Abort(_("can't specify --all and patterns"))
5269 raise util.Abort(_("can't specify --all and patterns"))
5270 if not (all or pats or show or mark or unmark):
5270 if not (all or pats or show or mark or unmark):
5271 raise util.Abort(_('no files or directories specified'),
5271 raise util.Abort(_('no files or directories specified'),
5272 hint=('use --all to remerge all files'))
5272 hint=('use --all to remerge all files'))
5273
5273
5274 wlock = repo.wlock()
5274 wlock = repo.wlock()
5275 try:
5275 try:
5276 ms = mergemod.mergestate(repo)
5276 ms = mergemod.mergestate(repo)
5277
5277
5278 if not (ms.active() or repo.dirstate.p2() != nullid) and not show:
5278 if not (ms.active() or repo.dirstate.p2() != nullid) and not show:
5279 raise util.Abort(
5279 raise util.Abort(
5280 _('resolve command not applicable when not merging'))
5280 _('resolve command not applicable when not merging'))
5281
5281
5282 m = scmutil.match(repo[None], pats, opts)
5282 m = scmutil.match(repo[None], pats, opts)
5283 ret = 0
5283 ret = 0
5284 didwork = False
5284 didwork = False
5285
5285
5286 for f in ms:
5286 for f in ms:
5287 if not m(f):
5287 if not m(f):
5288 continue
5288 continue
5289
5289
5290 didwork = True
5290 didwork = True
5291
5291
5292 if show:
5292 if show:
5293 if nostatus:
5293 if nostatus:
5294 ui.write("%s\n" % f)
5294 ui.write("%s\n" % f)
5295 else:
5295 else:
5296 ui.write("%s %s\n" % (ms[f].upper(), f),
5296 ui.write("%s %s\n" % (ms[f].upper(), f),
5297 label='resolve.' +
5297 label='resolve.' +
5298 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
5298 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
5299 elif mark:
5299 elif mark:
5300 ms.mark(f, "r")
5300 ms.mark(f, "r")
5301 elif unmark:
5301 elif unmark:
5302 ms.mark(f, "u")
5302 ms.mark(f, "u")
5303 else:
5303 else:
5304 wctx = repo[None]
5304 wctx = repo[None]
5305
5305
5306 # backup pre-resolve (merge uses .orig for its own purposes)
5306 # backup pre-resolve (merge uses .orig for its own purposes)
5307 a = repo.wjoin(f)
5307 a = repo.wjoin(f)
5308 util.copyfile(a, a + ".resolve")
5308 util.copyfile(a, a + ".resolve")
5309
5309
5310 try:
5310 try:
5311 # resolve file
5311 # resolve file
5312 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5312 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5313 'resolve')
5313 'resolve')
5314 if ms.resolve(f, wctx):
5314 if ms.resolve(f, wctx):
5315 ret = 1
5315 ret = 1
5316 finally:
5316 finally:
5317 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5317 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5318 ms.commit()
5318 ms.commit()
5319
5319
5320 # replace filemerge's .orig file with our resolve file
5320 # replace filemerge's .orig file with our resolve file
5321 util.rename(a + ".resolve", a + ".orig")
5321 util.rename(a + ".resolve", a + ".orig")
5322
5322
5323 ms.commit()
5323 ms.commit()
5324
5324
5325 if not didwork and pats:
5325 if not didwork and pats:
5326 ui.warn(_("arguments do not match paths that need resolving\n"))
5326 ui.warn(_("arguments do not match paths that need resolving\n"))
5327
5327
5328 finally:
5328 finally:
5329 wlock.release()
5329 wlock.release()
5330
5330
5331 # Nudge users into finishing an unfinished operation. We don't print
5331 # Nudge users into finishing an unfinished operation. We don't print
5332 # this with the list/show operation because we want list/show to remain
5332 # this with the list/show operation because we want list/show to remain
5333 # machine readable.
5333 # machine readable.
5334 if not list(ms.unresolved()) and not show:
5334 if not list(ms.unresolved()) and not show:
5335 ui.status(_('(no more unresolved files)\n'))
5335 ui.status(_('(no more unresolved files)\n'))
5336
5336
5337 return ret
5337 return ret
5338
5338
5339 @command('revert',
5339 @command('revert',
5340 [('a', 'all', None, _('revert all changes when no arguments given')),
5340 [('a', 'all', None, _('revert all changes when no arguments given')),
5341 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5341 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5342 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5342 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5343 ('C', 'no-backup', None, _('do not save backup copies of files')),
5343 ('C', 'no-backup', None, _('do not save backup copies of files')),
5344 ] + walkopts + dryrunopts,
5344 ] + walkopts + dryrunopts,
5345 _('[OPTION]... [-r REV] [NAME]...'))
5345 _('[OPTION]... [-r REV] [NAME]...'))
5346 def revert(ui, repo, *pats, **opts):
5346 def revert(ui, repo, *pats, **opts):
5347 """restore files to their checkout state
5347 """restore files to their checkout state
5348
5348
5349 .. note::
5349 .. note::
5350
5350
5351 To check out earlier revisions, you should use :hg:`update REV`.
5351 To check out earlier revisions, you should use :hg:`update REV`.
5352 To cancel an uncommitted merge (and lose your changes),
5352 To cancel an uncommitted merge (and lose your changes),
5353 use :hg:`update --clean .`.
5353 use :hg:`update --clean .`.
5354
5354
5355 With no revision specified, revert the specified files or directories
5355 With no revision specified, revert the specified files or directories
5356 to the contents they had in the parent of the working directory.
5356 to the contents they had in the parent of the working directory.
5357 This restores the contents of files to an unmodified
5357 This restores the contents of files to an unmodified
5358 state and unschedules adds, removes, copies, and renames. If the
5358 state and unschedules adds, removes, copies, and renames. If the
5359 working directory has two parents, you must explicitly specify a
5359 working directory has two parents, you must explicitly specify a
5360 revision.
5360 revision.
5361
5361
5362 Using the -r/--rev or -d/--date options, revert the given files or
5362 Using the -r/--rev or -d/--date options, revert the given files or
5363 directories to their states as of a specific revision. Because
5363 directories to their states as of a specific revision. Because
5364 revert does not change the working directory parents, this will
5364 revert does not change the working directory parents, this will
5365 cause these files to appear modified. This can be helpful to "back
5365 cause these files to appear modified. This can be helpful to "back
5366 out" some or all of an earlier change. See :hg:`backout` for a
5366 out" some or all of an earlier change. See :hg:`backout` for a
5367 related method.
5367 related method.
5368
5368
5369 Modified files are saved with a .orig suffix before reverting.
5369 Modified files are saved with a .orig suffix before reverting.
5370 To disable these backups, use --no-backup.
5370 To disable these backups, use --no-backup.
5371
5371
5372 See :hg:`help dates` for a list of formats valid for -d/--date.
5372 See :hg:`help dates` for a list of formats valid for -d/--date.
5373
5373
5374 Returns 0 on success.
5374 Returns 0 on success.
5375 """
5375 """
5376
5376
5377 if opts.get("date"):
5377 if opts.get("date"):
5378 if opts.get("rev"):
5378 if opts.get("rev"):
5379 raise util.Abort(_("you can't specify a revision and a date"))
5379 raise util.Abort(_("you can't specify a revision and a date"))
5380 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5380 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5381
5381
5382 parent, p2 = repo.dirstate.parents()
5382 parent, p2 = repo.dirstate.parents()
5383 if not opts.get('rev') and p2 != nullid:
5383 if not opts.get('rev') and p2 != nullid:
5384 # revert after merge is a trap for new users (issue2915)
5384 # revert after merge is a trap for new users (issue2915)
5385 raise util.Abort(_('uncommitted merge with no revision specified'),
5385 raise util.Abort(_('uncommitted merge with no revision specified'),
5386 hint=_('use "hg update" or see "hg help revert"'))
5386 hint=_('use "hg update" or see "hg help revert"'))
5387
5387
5388 ctx = scmutil.revsingle(repo, opts.get('rev'))
5388 ctx = scmutil.revsingle(repo, opts.get('rev'))
5389
5389
5390 if not pats and not opts.get('all'):
5390 if not pats and not opts.get('all'):
5391 msg = _("no files or directories specified")
5391 msg = _("no files or directories specified")
5392 if p2 != nullid:
5392 if p2 != nullid:
5393 hint = _("uncommitted merge, use --all to discard all changes,"
5393 hint = _("uncommitted merge, use --all to discard all changes,"
5394 " or 'hg update -C .' to abort the merge")
5394 " or 'hg update -C .' to abort the merge")
5395 raise util.Abort(msg, hint=hint)
5395 raise util.Abort(msg, hint=hint)
5396 dirty = util.any(repo.status())
5396 dirty = util.any(repo.status())
5397 node = ctx.node()
5397 node = ctx.node()
5398 if node != parent:
5398 if node != parent:
5399 if dirty:
5399 if dirty:
5400 hint = _("uncommitted changes, use --all to discard all"
5400 hint = _("uncommitted changes, use --all to discard all"
5401 " changes, or 'hg update %s' to update") % ctx.rev()
5401 " changes, or 'hg update %s' to update") % ctx.rev()
5402 else:
5402 else:
5403 hint = _("use --all to revert all files,"
5403 hint = _("use --all to revert all files,"
5404 " or 'hg update %s' to update") % ctx.rev()
5404 " or 'hg update %s' to update") % ctx.rev()
5405 elif dirty:
5405 elif dirty:
5406 hint = _("uncommitted changes, use --all to discard all changes")
5406 hint = _("uncommitted changes, use --all to discard all changes")
5407 else:
5407 else:
5408 hint = _("use --all to revert all files")
5408 hint = _("use --all to revert all files")
5409 raise util.Abort(msg, hint=hint)
5409 raise util.Abort(msg, hint=hint)
5410
5410
5411 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5411 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5412
5412
5413 @command('rollback', dryrunopts +
5413 @command('rollback', dryrunopts +
5414 [('f', 'force', False, _('ignore safety measures'))])
5414 [('f', 'force', False, _('ignore safety measures'))])
5415 def rollback(ui, repo, **opts):
5415 def rollback(ui, repo, **opts):
5416 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5416 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5417
5417
5418 Please use :hg:`commit --amend` instead of rollback to correct
5418 Please use :hg:`commit --amend` instead of rollback to correct
5419 mistakes in the last commit.
5419 mistakes in the last commit.
5420
5420
5421 This command should be used with care. There is only one level of
5421 This command should be used with care. There is only one level of
5422 rollback, and there is no way to undo a rollback. It will also
5422 rollback, and there is no way to undo a rollback. It will also
5423 restore the dirstate at the time of the last transaction, losing
5423 restore the dirstate at the time of the last transaction, losing
5424 any dirstate changes since that time. This command does not alter
5424 any dirstate changes since that time. This command does not alter
5425 the working directory.
5425 the working directory.
5426
5426
5427 Transactions are used to encapsulate the effects of all commands
5427 Transactions are used to encapsulate the effects of all commands
5428 that create new changesets or propagate existing changesets into a
5428 that create new changesets or propagate existing changesets into a
5429 repository.
5429 repository.
5430
5430
5431 .. container:: verbose
5431 .. container:: verbose
5432
5432
5433 For example, the following commands are transactional, and their
5433 For example, the following commands are transactional, and their
5434 effects can be rolled back:
5434 effects can be rolled back:
5435
5435
5436 - commit
5436 - commit
5437 - import
5437 - import
5438 - pull
5438 - pull
5439 - push (with this repository as the destination)
5439 - push (with this repository as the destination)
5440 - unbundle
5440 - unbundle
5441
5441
5442 To avoid permanent data loss, rollback will refuse to rollback a
5442 To avoid permanent data loss, rollback will refuse to rollback a
5443 commit transaction if it isn't checked out. Use --force to
5443 commit transaction if it isn't checked out. Use --force to
5444 override this protection.
5444 override this protection.
5445
5445
5446 This command is not intended for use on public repositories. Once
5446 This command is not intended for use on public repositories. Once
5447 changes are visible for pull by other users, rolling a transaction
5447 changes are visible for pull by other users, rolling a transaction
5448 back locally is ineffective (someone else may already have pulled
5448 back locally is ineffective (someone else may already have pulled
5449 the changes). Furthermore, a race is possible with readers of the
5449 the changes). Furthermore, a race is possible with readers of the
5450 repository; for example an in-progress pull from the repository
5450 repository; for example an in-progress pull from the repository
5451 may fail if a rollback is performed.
5451 may fail if a rollback is performed.
5452
5452
5453 Returns 0 on success, 1 if no rollback data is available.
5453 Returns 0 on success, 1 if no rollback data is available.
5454 """
5454 """
5455 return repo.rollback(dryrun=opts.get('dry_run'),
5455 return repo.rollback(dryrun=opts.get('dry_run'),
5456 force=opts.get('force'))
5456 force=opts.get('force'))
5457
5457
5458 @command('root', [])
5458 @command('root', [])
5459 def root(ui, repo):
5459 def root(ui, repo):
5460 """print the root (top) of the current working directory
5460 """print the root (top) of the current working directory
5461
5461
5462 Print the root directory of the current repository.
5462 Print the root directory of the current repository.
5463
5463
5464 Returns 0 on success.
5464 Returns 0 on success.
5465 """
5465 """
5466 ui.write(repo.root + "\n")
5466 ui.write(repo.root + "\n")
5467
5467
5468 @command('^serve',
5468 @command('^serve',
5469 [('A', 'accesslog', '', _('name of access log file to write to'),
5469 [('A', 'accesslog', '', _('name of access log file to write to'),
5470 _('FILE')),
5470 _('FILE')),
5471 ('d', 'daemon', None, _('run server in background')),
5471 ('d', 'daemon', None, _('run server in background')),
5472 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('FILE')),
5472 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('FILE')),
5473 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5473 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5474 # use string type, then we can check if something was passed
5474 # use string type, then we can check if something was passed
5475 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5475 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5476 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5476 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5477 _('ADDR')),
5477 _('ADDR')),
5478 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5478 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5479 _('PREFIX')),
5479 _('PREFIX')),
5480 ('n', 'name', '',
5480 ('n', 'name', '',
5481 _('name to show in web pages (default: working directory)'), _('NAME')),
5481 _('name to show in web pages (default: working directory)'), _('NAME')),
5482 ('', 'web-conf', '',
5482 ('', 'web-conf', '',
5483 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5483 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5484 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5484 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5485 _('FILE')),
5485 _('FILE')),
5486 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5486 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5487 ('', 'stdio', None, _('for remote clients')),
5487 ('', 'stdio', None, _('for remote clients')),
5488 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5488 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5489 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5489 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5490 ('', 'style', '', _('template style to use'), _('STYLE')),
5490 ('', 'style', '', _('template style to use'), _('STYLE')),
5491 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5491 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5492 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5492 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5493 _('[OPTION]...'),
5493 _('[OPTION]...'),
5494 optionalrepo=True)
5494 optionalrepo=True)
5495 def serve(ui, repo, **opts):
5495 def serve(ui, repo, **opts):
5496 """start stand-alone webserver
5496 """start stand-alone webserver
5497
5497
5498 Start a local HTTP repository browser and pull server. You can use
5498 Start a local HTTP repository browser and pull server. You can use
5499 this for ad-hoc sharing and browsing of repositories. It is
5499 this for ad-hoc sharing and browsing of repositories. It is
5500 recommended to use a real web server to serve a repository for
5500 recommended to use a real web server to serve a repository for
5501 longer periods of time.
5501 longer periods of time.
5502
5502
5503 Please note that the server does not implement access control.
5503 Please note that the server does not implement access control.
5504 This means that, by default, anybody can read from the server and
5504 This means that, by default, anybody can read from the server and
5505 nobody can write to it by default. Set the ``web.allow_push``
5505 nobody can write to it by default. Set the ``web.allow_push``
5506 option to ``*`` to allow everybody to push to the server. You
5506 option to ``*`` to allow everybody to push to the server. You
5507 should use a real web server if you need to authenticate users.
5507 should use a real web server if you need to authenticate users.
5508
5508
5509 By default, the server logs accesses to stdout and errors to
5509 By default, the server logs accesses to stdout and errors to
5510 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5510 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5511 files.
5511 files.
5512
5512
5513 To have the server choose a free port number to listen on, specify
5513 To have the server choose a free port number to listen on, specify
5514 a port number of 0; in this case, the server will print the port
5514 a port number of 0; in this case, the server will print the port
5515 number it uses.
5515 number it uses.
5516
5516
5517 Returns 0 on success.
5517 Returns 0 on success.
5518 """
5518 """
5519
5519
5520 if opts["stdio"] and opts["cmdserver"]:
5520 if opts["stdio"] and opts["cmdserver"]:
5521 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5521 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5522
5522
5523 if opts["stdio"]:
5523 if opts["stdio"]:
5524 if repo is None:
5524 if repo is None:
5525 raise error.RepoError(_("there is no Mercurial repository here"
5525 raise error.RepoError(_("there is no Mercurial repository here"
5526 " (.hg not found)"))
5526 " (.hg not found)"))
5527 s = sshserver.sshserver(ui, repo)
5527 s = sshserver.sshserver(ui, repo)
5528 s.serve_forever()
5528 s.serve_forever()
5529
5529
5530 if opts["cmdserver"]:
5530 if opts["cmdserver"]:
5531 service = commandserver.createservice(ui, repo, opts)
5531 service = commandserver.createservice(ui, repo, opts)
5532 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
5532 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
5533
5533
5534 # this way we can check if something was given in the command-line
5534 # this way we can check if something was given in the command-line
5535 if opts.get('port'):
5535 if opts.get('port'):
5536 opts['port'] = util.getport(opts.get('port'))
5536 opts['port'] = util.getport(opts.get('port'))
5537
5537
5538 baseui = repo and repo.baseui or ui
5538 baseui = repo and repo.baseui or ui
5539 optlist = ("name templates style address port prefix ipv6"
5539 optlist = ("name templates style address port prefix ipv6"
5540 " accesslog errorlog certificate encoding")
5540 " accesslog errorlog certificate encoding")
5541 for o in optlist.split():
5541 for o in optlist.split():
5542 val = opts.get(o, '')
5542 val = opts.get(o, '')
5543 if val in (None, ''): # should check against default options instead
5543 if val in (None, ''): # should check against default options instead
5544 continue
5544 continue
5545 baseui.setconfig("web", o, val, 'serve')
5545 baseui.setconfig("web", o, val, 'serve')
5546 if repo and repo.ui != baseui:
5546 if repo and repo.ui != baseui:
5547 repo.ui.setconfig("web", o, val, 'serve')
5547 repo.ui.setconfig("web", o, val, 'serve')
5548
5548
5549 o = opts.get('web_conf') or opts.get('webdir_conf')
5549 o = opts.get('web_conf') or opts.get('webdir_conf')
5550 if not o:
5550 if not o:
5551 if not repo:
5551 if not repo:
5552 raise error.RepoError(_("there is no Mercurial repository"
5552 raise error.RepoError(_("there is no Mercurial repository"
5553 " here (.hg not found)"))
5553 " here (.hg not found)"))
5554 o = repo
5554 o = repo
5555
5555
5556 app = hgweb.hgweb(o, baseui=baseui)
5556 app = hgweb.hgweb(o, baseui=baseui)
5557 service = httpservice(ui, app, opts)
5557 service = httpservice(ui, app, opts)
5558 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5558 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5559
5559
5560 class httpservice(object):
5560 class httpservice(object):
5561 def __init__(self, ui, app, opts):
5561 def __init__(self, ui, app, opts):
5562 self.ui = ui
5562 self.ui = ui
5563 self.app = app
5563 self.app = app
5564 self.opts = opts
5564 self.opts = opts
5565
5565
5566 def init(self):
5566 def init(self):
5567 util.setsignalhandler()
5567 util.setsignalhandler()
5568 self.httpd = hgweb_server.create_server(self.ui, self.app)
5568 self.httpd = hgweb_server.create_server(self.ui, self.app)
5569
5569
5570 if self.opts['port'] and not self.ui.verbose:
5570 if self.opts['port'] and not self.ui.verbose:
5571 return
5571 return
5572
5572
5573 if self.httpd.prefix:
5573 if self.httpd.prefix:
5574 prefix = self.httpd.prefix.strip('/') + '/'
5574 prefix = self.httpd.prefix.strip('/') + '/'
5575 else:
5575 else:
5576 prefix = ''
5576 prefix = ''
5577
5577
5578 port = ':%d' % self.httpd.port
5578 port = ':%d' % self.httpd.port
5579 if port == ':80':
5579 if port == ':80':
5580 port = ''
5580 port = ''
5581
5581
5582 bindaddr = self.httpd.addr
5582 bindaddr = self.httpd.addr
5583 if bindaddr == '0.0.0.0':
5583 if bindaddr == '0.0.0.0':
5584 bindaddr = '*'
5584 bindaddr = '*'
5585 elif ':' in bindaddr: # IPv6
5585 elif ':' in bindaddr: # IPv6
5586 bindaddr = '[%s]' % bindaddr
5586 bindaddr = '[%s]' % bindaddr
5587
5587
5588 fqaddr = self.httpd.fqaddr
5588 fqaddr = self.httpd.fqaddr
5589 if ':' in fqaddr:
5589 if ':' in fqaddr:
5590 fqaddr = '[%s]' % fqaddr
5590 fqaddr = '[%s]' % fqaddr
5591 if self.opts['port']:
5591 if self.opts['port']:
5592 write = self.ui.status
5592 write = self.ui.status
5593 else:
5593 else:
5594 write = self.ui.write
5594 write = self.ui.write
5595 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5595 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5596 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5596 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5597 self.ui.flush() # avoid buffering of status message
5597 self.ui.flush() # avoid buffering of status message
5598
5598
5599 def run(self):
5599 def run(self):
5600 self.httpd.serve_forever()
5600 self.httpd.serve_forever()
5601
5601
5602
5602
5603 @command('^status|st',
5603 @command('^status|st',
5604 [('A', 'all', None, _('show status of all files')),
5604 [('A', 'all', None, _('show status of all files')),
5605 ('m', 'modified', None, _('show only modified files')),
5605 ('m', 'modified', None, _('show only modified files')),
5606 ('a', 'added', None, _('show only added files')),
5606 ('a', 'added', None, _('show only added files')),
5607 ('r', 'removed', None, _('show only removed files')),
5607 ('r', 'removed', None, _('show only removed files')),
5608 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5608 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5609 ('c', 'clean', None, _('show only files without changes')),
5609 ('c', 'clean', None, _('show only files without changes')),
5610 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5610 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5611 ('i', 'ignored', None, _('show only ignored files')),
5611 ('i', 'ignored', None, _('show only ignored files')),
5612 ('n', 'no-status', None, _('hide status prefix')),
5612 ('n', 'no-status', None, _('hide status prefix')),
5613 ('C', 'copies', None, _('show source of copied files')),
5613 ('C', 'copies', None, _('show source of copied files')),
5614 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5614 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5615 ('', 'rev', [], _('show difference from revision'), _('REV')),
5615 ('', 'rev', [], _('show difference from revision'), _('REV')),
5616 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5616 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5617 ] + walkopts + subrepoopts + formatteropts,
5617 ] + walkopts + subrepoopts + formatteropts,
5618 _('[OPTION]... [FILE]...'),
5618 _('[OPTION]... [FILE]...'),
5619 inferrepo=True)
5619 inferrepo=True)
5620 def status(ui, repo, *pats, **opts):
5620 def status(ui, repo, *pats, **opts):
5621 """show changed files in the working directory
5621 """show changed files in the working directory
5622
5622
5623 Show status of files in the repository. If names are given, only
5623 Show status of files in the repository. If names are given, only
5624 files that match are shown. Files that are clean or ignored or
5624 files that match are shown. Files that are clean or ignored or
5625 the source of a copy/move operation, are not listed unless
5625 the source of a copy/move operation, are not listed unless
5626 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5626 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5627 Unless options described with "show only ..." are given, the
5627 Unless options described with "show only ..." are given, the
5628 options -mardu are used.
5628 options -mardu are used.
5629
5629
5630 Option -q/--quiet hides untracked (unknown and ignored) files
5630 Option -q/--quiet hides untracked (unknown and ignored) files
5631 unless explicitly requested with -u/--unknown or -i/--ignored.
5631 unless explicitly requested with -u/--unknown or -i/--ignored.
5632
5632
5633 .. note::
5633 .. note::
5634
5634
5635 status may appear to disagree with diff if permissions have
5635 status may appear to disagree with diff if permissions have
5636 changed or a merge has occurred. The standard diff format does
5636 changed or a merge has occurred. The standard diff format does
5637 not report permission changes and diff only reports changes
5637 not report permission changes and diff only reports changes
5638 relative to one merge parent.
5638 relative to one merge parent.
5639
5639
5640 If one revision is given, it is used as the base revision.
5640 If one revision is given, it is used as the base revision.
5641 If two revisions are given, the differences between them are
5641 If two revisions are given, the differences between them are
5642 shown. The --change option can also be used as a shortcut to list
5642 shown. The --change option can also be used as a shortcut to list
5643 the changed files of a revision from its first parent.
5643 the changed files of a revision from its first parent.
5644
5644
5645 The codes used to show the status of files are::
5645 The codes used to show the status of files are::
5646
5646
5647 M = modified
5647 M = modified
5648 A = added
5648 A = added
5649 R = removed
5649 R = removed
5650 C = clean
5650 C = clean
5651 ! = missing (deleted by non-hg command, but still tracked)
5651 ! = missing (deleted by non-hg command, but still tracked)
5652 ? = not tracked
5652 ? = not tracked
5653 I = ignored
5653 I = ignored
5654 = origin of the previous file (with --copies)
5654 = origin of the previous file (with --copies)
5655
5655
5656 .. container:: verbose
5656 .. container:: verbose
5657
5657
5658 Examples:
5658 Examples:
5659
5659
5660 - show changes in the working directory relative to a
5660 - show changes in the working directory relative to a
5661 changeset::
5661 changeset::
5662
5662
5663 hg status --rev 9353
5663 hg status --rev 9353
5664
5664
5665 - show all changes including copies in an existing changeset::
5665 - show all changes including copies in an existing changeset::
5666
5666
5667 hg status --copies --change 9353
5667 hg status --copies --change 9353
5668
5668
5669 - get a NUL separated list of added files, suitable for xargs::
5669 - get a NUL separated list of added files, suitable for xargs::
5670
5670
5671 hg status -an0
5671 hg status -an0
5672
5672
5673 Returns 0 on success.
5673 Returns 0 on success.
5674 """
5674 """
5675
5675
5676 revs = opts.get('rev')
5676 revs = opts.get('rev')
5677 change = opts.get('change')
5677 change = opts.get('change')
5678
5678
5679 if revs and change:
5679 if revs and change:
5680 msg = _('cannot specify --rev and --change at the same time')
5680 msg = _('cannot specify --rev and --change at the same time')
5681 raise util.Abort(msg)
5681 raise util.Abort(msg)
5682 elif change:
5682 elif change:
5683 node2 = scmutil.revsingle(repo, change, None).node()
5683 node2 = scmutil.revsingle(repo, change, None).node()
5684 node1 = repo[node2].p1().node()
5684 node1 = repo[node2].p1().node()
5685 else:
5685 else:
5686 node1, node2 = scmutil.revpair(repo, revs)
5686 node1, node2 = scmutil.revpair(repo, revs)
5687
5687
5688 cwd = (pats and repo.getcwd()) or ''
5688 cwd = (pats and repo.getcwd()) or ''
5689 end = opts.get('print0') and '\0' or '\n'
5689 end = opts.get('print0') and '\0' or '\n'
5690 copy = {}
5690 copy = {}
5691 states = 'modified added removed deleted unknown ignored clean'.split()
5691 states = 'modified added removed deleted unknown ignored clean'.split()
5692 show = [k for k in states if opts.get(k)]
5692 show = [k for k in states if opts.get(k)]
5693 if opts.get('all'):
5693 if opts.get('all'):
5694 show += ui.quiet and (states[:4] + ['clean']) or states
5694 show += ui.quiet and (states[:4] + ['clean']) or states
5695 if not show:
5695 if not show:
5696 show = ui.quiet and states[:4] or states[:5]
5696 show = ui.quiet and states[:4] or states[:5]
5697
5697
5698 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5698 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5699 'ignored' in show, 'clean' in show, 'unknown' in show,
5699 'ignored' in show, 'clean' in show, 'unknown' in show,
5700 opts.get('subrepos'))
5700 opts.get('subrepos'))
5701 changestates = zip(states, 'MAR!?IC', stat)
5701 changestates = zip(states, 'MAR!?IC', stat)
5702
5702
5703 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5703 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5704 copy = copies.pathcopies(repo[node1], repo[node2])
5704 copy = copies.pathcopies(repo[node1], repo[node2])
5705
5705
5706 fm = ui.formatter('status', opts)
5706 fm = ui.formatter('status', opts)
5707 fmt = '%s' + end
5707 fmt = '%s' + end
5708 showchar = not opts.get('no_status')
5708 showchar = not opts.get('no_status')
5709
5709
5710 for state, char, files in changestates:
5710 for state, char, files in changestates:
5711 if state in show:
5711 if state in show:
5712 label = 'status.' + state
5712 label = 'status.' + state
5713 for f in files:
5713 for f in files:
5714 fm.startitem()
5714 fm.startitem()
5715 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5715 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5716 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5716 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5717 if f in copy:
5717 if f in copy:
5718 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5718 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5719 label='status.copied')
5719 label='status.copied')
5720 fm.end()
5720 fm.end()
5721
5721
5722 @command('^summary|sum',
5722 @command('^summary|sum',
5723 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5723 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5724 def summary(ui, repo, **opts):
5724 def summary(ui, repo, **opts):
5725 """summarize working directory state
5725 """summarize working directory state
5726
5726
5727 This generates a brief summary of the working directory state,
5727 This generates a brief summary of the working directory state,
5728 including parents, branch, commit status, and available updates.
5728 including parents, branch, commit status, and available updates.
5729
5729
5730 With the --remote option, this will check the default paths for
5730 With the --remote option, this will check the default paths for
5731 incoming and outgoing changes. This can be time-consuming.
5731 incoming and outgoing changes. This can be time-consuming.
5732
5732
5733 Returns 0 on success.
5733 Returns 0 on success.
5734 """
5734 """
5735
5735
5736 ctx = repo[None]
5736 ctx = repo[None]
5737 parents = ctx.parents()
5737 parents = ctx.parents()
5738 pnode = parents[0].node()
5738 pnode = parents[0].node()
5739 marks = []
5739 marks = []
5740
5740
5741 for p in parents:
5741 for p in parents:
5742 # label with log.changeset (instead of log.parent) since this
5742 # label with log.changeset (instead of log.parent) since this
5743 # shows a working directory parent *changeset*:
5743 # shows a working directory parent *changeset*:
5744 # i18n: column positioning for "hg summary"
5744 # i18n: column positioning for "hg summary"
5745 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5745 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5746 label='log.changeset changeset.%s' % p.phasestr())
5746 label='log.changeset changeset.%s' % p.phasestr())
5747 ui.write(' '.join(p.tags()), label='log.tag')
5747 ui.write(' '.join(p.tags()), label='log.tag')
5748 if p.bookmarks():
5748 if p.bookmarks():
5749 marks.extend(p.bookmarks())
5749 marks.extend(p.bookmarks())
5750 if p.rev() == -1:
5750 if p.rev() == -1:
5751 if not len(repo):
5751 if not len(repo):
5752 ui.write(_(' (empty repository)'))
5752 ui.write(_(' (empty repository)'))
5753 else:
5753 else:
5754 ui.write(_(' (no revision checked out)'))
5754 ui.write(_(' (no revision checked out)'))
5755 ui.write('\n')
5755 ui.write('\n')
5756 if p.description():
5756 if p.description():
5757 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5757 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5758 label='log.summary')
5758 label='log.summary')
5759
5759
5760 branch = ctx.branch()
5760 branch = ctx.branch()
5761 bheads = repo.branchheads(branch)
5761 bheads = repo.branchheads(branch)
5762 # i18n: column positioning for "hg summary"
5762 # i18n: column positioning for "hg summary"
5763 m = _('branch: %s\n') % branch
5763 m = _('branch: %s\n') % branch
5764 if branch != 'default':
5764 if branch != 'default':
5765 ui.write(m, label='log.branch')
5765 ui.write(m, label='log.branch')
5766 else:
5766 else:
5767 ui.status(m, label='log.branch')
5767 ui.status(m, label='log.branch')
5768
5768
5769 if marks:
5769 if marks:
5770 current = repo._bookmarkcurrent
5770 current = repo._bookmarkcurrent
5771 # i18n: column positioning for "hg summary"
5771 # i18n: column positioning for "hg summary"
5772 ui.write(_('bookmarks:'), label='log.bookmark')
5772 ui.write(_('bookmarks:'), label='log.bookmark')
5773 if current is not None:
5773 if current is not None:
5774 if current in marks:
5774 if current in marks:
5775 ui.write(' *' + current, label='bookmarks.current')
5775 ui.write(' *' + current, label='bookmarks.current')
5776 marks.remove(current)
5776 marks.remove(current)
5777 else:
5777 else:
5778 ui.write(' [%s]' % current, label='bookmarks.current')
5778 ui.write(' [%s]' % current, label='bookmarks.current')
5779 for m in marks:
5779 for m in marks:
5780 ui.write(' ' + m, label='log.bookmark')
5780 ui.write(' ' + m, label='log.bookmark')
5781 ui.write('\n', label='log.bookmark')
5781 ui.write('\n', label='log.bookmark')
5782
5782
5783 status = repo.status(unknown=True)
5783 status = repo.status(unknown=True)
5784
5784
5785 c = repo.dirstate.copies()
5785 c = repo.dirstate.copies()
5786 copied, renamed = [], []
5786 copied, renamed = [], []
5787 for d, s in c.iteritems():
5787 for d, s in c.iteritems():
5788 if s in status.removed:
5788 if s in status.removed:
5789 status.removed.remove(s)
5789 status.removed.remove(s)
5790 renamed.append(d)
5790 renamed.append(d)
5791 else:
5791 else:
5792 copied.append(d)
5792 copied.append(d)
5793 if d in status.added:
5793 if d in status.added:
5794 status.added.remove(d)
5794 status.added.remove(d)
5795
5795
5796 ms = mergemod.mergestate(repo)
5796 ms = mergemod.mergestate(repo)
5797 unresolved = [f for f in ms if ms[f] == 'u']
5797 unresolved = [f for f in ms if ms[f] == 'u']
5798
5798
5799 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5799 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5800
5800
5801 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5801 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5802 (ui.label(_('%d added'), 'status.added'), status.added),
5802 (ui.label(_('%d added'), 'status.added'), status.added),
5803 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5803 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5804 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5804 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5805 (ui.label(_('%d copied'), 'status.copied'), copied),
5805 (ui.label(_('%d copied'), 'status.copied'), copied),
5806 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5806 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5807 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5807 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5808 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5808 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5809 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5809 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5810 t = []
5810 t = []
5811 for l, s in labels:
5811 for l, s in labels:
5812 if s:
5812 if s:
5813 t.append(l % len(s))
5813 t.append(l % len(s))
5814
5814
5815 t = ', '.join(t)
5815 t = ', '.join(t)
5816 cleanworkdir = False
5816 cleanworkdir = False
5817
5817
5818 if repo.vfs.exists('updatestate'):
5818 if repo.vfs.exists('updatestate'):
5819 t += _(' (interrupted update)')
5819 t += _(' (interrupted update)')
5820 elif len(parents) > 1:
5820 elif len(parents) > 1:
5821 t += _(' (merge)')
5821 t += _(' (merge)')
5822 elif branch != parents[0].branch():
5822 elif branch != parents[0].branch():
5823 t += _(' (new branch)')
5823 t += _(' (new branch)')
5824 elif (parents[0].closesbranch() and
5824 elif (parents[0].closesbranch() and
5825 pnode in repo.branchheads(branch, closed=True)):
5825 pnode in repo.branchheads(branch, closed=True)):
5826 t += _(' (head closed)')
5826 t += _(' (head closed)')
5827 elif not (status.modified or status.added or status.removed or renamed or
5827 elif not (status.modified or status.added or status.removed or renamed or
5828 copied or subs):
5828 copied or subs):
5829 t += _(' (clean)')
5829 t += _(' (clean)')
5830 cleanworkdir = True
5830 cleanworkdir = True
5831 elif pnode not in bheads:
5831 elif pnode not in bheads:
5832 t += _(' (new branch head)')
5832 t += _(' (new branch head)')
5833
5833
5834 if cleanworkdir:
5834 if cleanworkdir:
5835 # i18n: column positioning for "hg summary"
5835 # i18n: column positioning for "hg summary"
5836 ui.status(_('commit: %s\n') % t.strip())
5836 ui.status(_('commit: %s\n') % t.strip())
5837 else:
5837 else:
5838 # i18n: column positioning for "hg summary"
5838 # i18n: column positioning for "hg summary"
5839 ui.write(_('commit: %s\n') % t.strip())
5839 ui.write(_('commit: %s\n') % t.strip())
5840
5840
5841 # all ancestors of branch heads - all ancestors of parent = new csets
5841 # all ancestors of branch heads - all ancestors of parent = new csets
5842 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5842 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5843 bheads))
5843 bheads))
5844
5844
5845 if new == 0:
5845 if new == 0:
5846 # i18n: column positioning for "hg summary"
5846 # i18n: column positioning for "hg summary"
5847 ui.status(_('update: (current)\n'))
5847 ui.status(_('update: (current)\n'))
5848 elif pnode not in bheads:
5848 elif pnode not in bheads:
5849 # i18n: column positioning for "hg summary"
5849 # i18n: column positioning for "hg summary"
5850 ui.write(_('update: %d new changesets (update)\n') % new)
5850 ui.write(_('update: %d new changesets (update)\n') % new)
5851 else:
5851 else:
5852 # i18n: column positioning for "hg summary"
5852 # i18n: column positioning for "hg summary"
5853 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5853 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5854 (new, len(bheads)))
5854 (new, len(bheads)))
5855
5855
5856 cmdutil.summaryhooks(ui, repo)
5856 cmdutil.summaryhooks(ui, repo)
5857
5857
5858 if opts.get('remote'):
5858 if opts.get('remote'):
5859 needsincoming, needsoutgoing = True, True
5859 needsincoming, needsoutgoing = True, True
5860 else:
5860 else:
5861 needsincoming, needsoutgoing = False, False
5861 needsincoming, needsoutgoing = False, False
5862 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5862 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5863 if i:
5863 if i:
5864 needsincoming = True
5864 needsincoming = True
5865 if o:
5865 if o:
5866 needsoutgoing = True
5866 needsoutgoing = True
5867 if not needsincoming and not needsoutgoing:
5867 if not needsincoming and not needsoutgoing:
5868 return
5868 return
5869
5869
5870 def getincoming():
5870 def getincoming():
5871 source, branches = hg.parseurl(ui.expandpath('default'))
5871 source, branches = hg.parseurl(ui.expandpath('default'))
5872 sbranch = branches[0]
5872 sbranch = branches[0]
5873 try:
5873 try:
5874 other = hg.peer(repo, {}, source)
5874 other = hg.peer(repo, {}, source)
5875 except error.RepoError:
5875 except error.RepoError:
5876 if opts.get('remote'):
5876 if opts.get('remote'):
5877 raise
5877 raise
5878 return source, sbranch, None, None, None
5878 return source, sbranch, None, None, None
5879 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5879 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5880 if revs:
5880 if revs:
5881 revs = [other.lookup(rev) for rev in revs]
5881 revs = [other.lookup(rev) for rev in revs]
5882 ui.debug('comparing with %s\n' % util.hidepassword(source))
5882 ui.debug('comparing with %s\n' % util.hidepassword(source))
5883 repo.ui.pushbuffer()
5883 repo.ui.pushbuffer()
5884 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5884 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5885 repo.ui.popbuffer()
5885 repo.ui.popbuffer()
5886 return source, sbranch, other, commoninc, commoninc[1]
5886 return source, sbranch, other, commoninc, commoninc[1]
5887
5887
5888 if needsincoming:
5888 if needsincoming:
5889 source, sbranch, sother, commoninc, incoming = getincoming()
5889 source, sbranch, sother, commoninc, incoming = getincoming()
5890 else:
5890 else:
5891 source = sbranch = sother = commoninc = incoming = None
5891 source = sbranch = sother = commoninc = incoming = None
5892
5892
5893 def getoutgoing():
5893 def getoutgoing():
5894 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5894 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5895 dbranch = branches[0]
5895 dbranch = branches[0]
5896 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5896 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5897 if source != dest:
5897 if source != dest:
5898 try:
5898 try:
5899 dother = hg.peer(repo, {}, dest)
5899 dother = hg.peer(repo, {}, dest)
5900 except error.RepoError:
5900 except error.RepoError:
5901 if opts.get('remote'):
5901 if opts.get('remote'):
5902 raise
5902 raise
5903 return dest, dbranch, None, None
5903 return dest, dbranch, None, None
5904 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5904 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5905 elif sother is None:
5905 elif sother is None:
5906 # there is no explicit destination peer, but source one is invalid
5906 # there is no explicit destination peer, but source one is invalid
5907 return dest, dbranch, None, None
5907 return dest, dbranch, None, None
5908 else:
5908 else:
5909 dother = sother
5909 dother = sother
5910 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5910 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5911 common = None
5911 common = None
5912 else:
5912 else:
5913 common = commoninc
5913 common = commoninc
5914 if revs:
5914 if revs:
5915 revs = [repo.lookup(rev) for rev in revs]
5915 revs = [repo.lookup(rev) for rev in revs]
5916 repo.ui.pushbuffer()
5916 repo.ui.pushbuffer()
5917 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5917 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5918 commoninc=common)
5918 commoninc=common)
5919 repo.ui.popbuffer()
5919 repo.ui.popbuffer()
5920 return dest, dbranch, dother, outgoing
5920 return dest, dbranch, dother, outgoing
5921
5921
5922 if needsoutgoing:
5922 if needsoutgoing:
5923 dest, dbranch, dother, outgoing = getoutgoing()
5923 dest, dbranch, dother, outgoing = getoutgoing()
5924 else:
5924 else:
5925 dest = dbranch = dother = outgoing = None
5925 dest = dbranch = dother = outgoing = None
5926
5926
5927 if opts.get('remote'):
5927 if opts.get('remote'):
5928 t = []
5928 t = []
5929 if incoming:
5929 if incoming:
5930 t.append(_('1 or more incoming'))
5930 t.append(_('1 or more incoming'))
5931 o = outgoing.missing
5931 o = outgoing.missing
5932 if o:
5932 if o:
5933 t.append(_('%d outgoing') % len(o))
5933 t.append(_('%d outgoing') % len(o))
5934 other = dother or sother
5934 other = dother or sother
5935 if 'bookmarks' in other.listkeys('namespaces'):
5935 if 'bookmarks' in other.listkeys('namespaces'):
5936 lmarks = repo.listkeys('bookmarks')
5936 lmarks = repo.listkeys('bookmarks')
5937 rmarks = other.listkeys('bookmarks')
5937 rmarks = other.listkeys('bookmarks')
5938 diff = set(rmarks) - set(lmarks)
5938 diff = set(rmarks) - set(lmarks)
5939 if len(diff) > 0:
5939 if len(diff) > 0:
5940 t.append(_('%d incoming bookmarks') % len(diff))
5940 t.append(_('%d incoming bookmarks') % len(diff))
5941 diff = set(lmarks) - set(rmarks)
5941 diff = set(lmarks) - set(rmarks)
5942 if len(diff) > 0:
5942 if len(diff) > 0:
5943 t.append(_('%d outgoing bookmarks') % len(diff))
5943 t.append(_('%d outgoing bookmarks') % len(diff))
5944
5944
5945 if t:
5945 if t:
5946 # i18n: column positioning for "hg summary"
5946 # i18n: column positioning for "hg summary"
5947 ui.write(_('remote: %s\n') % (', '.join(t)))
5947 ui.write(_('remote: %s\n') % (', '.join(t)))
5948 else:
5948 else:
5949 # i18n: column positioning for "hg summary"
5949 # i18n: column positioning for "hg summary"
5950 ui.status(_('remote: (synced)\n'))
5950 ui.status(_('remote: (synced)\n'))
5951
5951
5952 cmdutil.summaryremotehooks(ui, repo, opts,
5952 cmdutil.summaryremotehooks(ui, repo, opts,
5953 ((source, sbranch, sother, commoninc),
5953 ((source, sbranch, sother, commoninc),
5954 (dest, dbranch, dother, outgoing)))
5954 (dest, dbranch, dother, outgoing)))
5955
5955
5956 @command('tag',
5956 @command('tag',
5957 [('f', 'force', None, _('force tag')),
5957 [('f', 'force', None, _('force tag')),
5958 ('l', 'local', None, _('make the tag local')),
5958 ('l', 'local', None, _('make the tag local')),
5959 ('r', 'rev', '', _('revision to tag'), _('REV')),
5959 ('r', 'rev', '', _('revision to tag'), _('REV')),
5960 ('', 'remove', None, _('remove a tag')),
5960 ('', 'remove', None, _('remove a tag')),
5961 # -l/--local is already there, commitopts cannot be used
5961 # -l/--local is already there, commitopts cannot be used
5962 ('e', 'edit', None, _('invoke editor on commit messages')),
5962 ('e', 'edit', None, _('invoke editor on commit messages')),
5963 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5963 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5964 ] + commitopts2,
5964 ] + commitopts2,
5965 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5965 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5966 def tag(ui, repo, name1, *names, **opts):
5966 def tag(ui, repo, name1, *names, **opts):
5967 """add one or more tags for the current or given revision
5967 """add one or more tags for the current or given revision
5968
5968
5969 Name a particular revision using <name>.
5969 Name a particular revision using <name>.
5970
5970
5971 Tags are used to name particular revisions of the repository and are
5971 Tags are used to name particular revisions of the repository and are
5972 very useful to compare different revisions, to go back to significant
5972 very useful to compare different revisions, to go back to significant
5973 earlier versions or to mark branch points as releases, etc. Changing
5973 earlier versions or to mark branch points as releases, etc. Changing
5974 an existing tag is normally disallowed; use -f/--force to override.
5974 an existing tag is normally disallowed; use -f/--force to override.
5975
5975
5976 If no revision is given, the parent of the working directory is
5976 If no revision is given, the parent of the working directory is
5977 used.
5977 used.
5978
5978
5979 To facilitate version control, distribution, and merging of tags,
5979 To facilitate version control, distribution, and merging of tags,
5980 they are stored as a file named ".hgtags" which is managed similarly
5980 they are stored as a file named ".hgtags" which is managed similarly
5981 to other project files and can be hand-edited if necessary. This
5981 to other project files and can be hand-edited if necessary. This
5982 also means that tagging creates a new commit. The file
5982 also means that tagging creates a new commit. The file
5983 ".hg/localtags" is used for local tags (not shared among
5983 ".hg/localtags" is used for local tags (not shared among
5984 repositories).
5984 repositories).
5985
5985
5986 Tag commits are usually made at the head of a branch. If the parent
5986 Tag commits are usually made at the head of a branch. If the parent
5987 of the working directory is not a branch head, :hg:`tag` aborts; use
5987 of the working directory is not a branch head, :hg:`tag` aborts; use
5988 -f/--force to force the tag commit to be based on a non-head
5988 -f/--force to force the tag commit to be based on a non-head
5989 changeset.
5989 changeset.
5990
5990
5991 See :hg:`help dates` for a list of formats valid for -d/--date.
5991 See :hg:`help dates` for a list of formats valid for -d/--date.
5992
5992
5993 Since tag names have priority over branch names during revision
5993 Since tag names have priority over branch names during revision
5994 lookup, using an existing branch name as a tag name is discouraged.
5994 lookup, using an existing branch name as a tag name is discouraged.
5995
5995
5996 Returns 0 on success.
5996 Returns 0 on success.
5997 """
5997 """
5998 wlock = lock = None
5998 wlock = lock = None
5999 try:
5999 try:
6000 wlock = repo.wlock()
6000 wlock = repo.wlock()
6001 lock = repo.lock()
6001 lock = repo.lock()
6002 rev_ = "."
6002 rev_ = "."
6003 names = [t.strip() for t in (name1,) + names]
6003 names = [t.strip() for t in (name1,) + names]
6004 if len(names) != len(set(names)):
6004 if len(names) != len(set(names)):
6005 raise util.Abort(_('tag names must be unique'))
6005 raise util.Abort(_('tag names must be unique'))
6006 for n in names:
6006 for n in names:
6007 scmutil.checknewlabel(repo, n, 'tag')
6007 scmutil.checknewlabel(repo, n, 'tag')
6008 if not n:
6008 if not n:
6009 raise util.Abort(_('tag names cannot consist entirely of '
6009 raise util.Abort(_('tag names cannot consist entirely of '
6010 'whitespace'))
6010 'whitespace'))
6011 if opts.get('rev') and opts.get('remove'):
6011 if opts.get('rev') and opts.get('remove'):
6012 raise util.Abort(_("--rev and --remove are incompatible"))
6012 raise util.Abort(_("--rev and --remove are incompatible"))
6013 if opts.get('rev'):
6013 if opts.get('rev'):
6014 rev_ = opts['rev']
6014 rev_ = opts['rev']
6015 message = opts.get('message')
6015 message = opts.get('message')
6016 if opts.get('remove'):
6016 if opts.get('remove'):
6017 expectedtype = opts.get('local') and 'local' or 'global'
6017 expectedtype = opts.get('local') and 'local' or 'global'
6018 for n in names:
6018 for n in names:
6019 if not repo.tagtype(n):
6019 if not repo.tagtype(n):
6020 raise util.Abort(_("tag '%s' does not exist") % n)
6020 raise util.Abort(_("tag '%s' does not exist") % n)
6021 if repo.tagtype(n) != expectedtype:
6021 if repo.tagtype(n) != expectedtype:
6022 if expectedtype == 'global':
6022 if expectedtype == 'global':
6023 raise util.Abort(_("tag '%s' is not a global tag") % n)
6023 raise util.Abort(_("tag '%s' is not a global tag") % n)
6024 else:
6024 else:
6025 raise util.Abort(_("tag '%s' is not a local tag") % n)
6025 raise util.Abort(_("tag '%s' is not a local tag") % n)
6026 rev_ = nullid
6026 rev_ = nullid
6027 if not message:
6027 if not message:
6028 # we don't translate commit messages
6028 # we don't translate commit messages
6029 message = 'Removed tag %s' % ', '.join(names)
6029 message = 'Removed tag %s' % ', '.join(names)
6030 elif not opts.get('force'):
6030 elif not opts.get('force'):
6031 for n in names:
6031 for n in names:
6032 if n in repo.tags():
6032 if n in repo.tags():
6033 raise util.Abort(_("tag '%s' already exists "
6033 raise util.Abort(_("tag '%s' already exists "
6034 "(use -f to force)") % n)
6034 "(use -f to force)") % n)
6035 if not opts.get('local'):
6035 if not opts.get('local'):
6036 p1, p2 = repo.dirstate.parents()
6036 p1, p2 = repo.dirstate.parents()
6037 if p2 != nullid:
6037 if p2 != nullid:
6038 raise util.Abort(_('uncommitted merge'))
6038 raise util.Abort(_('uncommitted merge'))
6039 bheads = repo.branchheads()
6039 bheads = repo.branchheads()
6040 if not opts.get('force') and bheads and p1 not in bheads:
6040 if not opts.get('force') and bheads and p1 not in bheads:
6041 raise util.Abort(_('not at a branch head (use -f to force)'))
6041 raise util.Abort(_('not at a branch head (use -f to force)'))
6042 r = scmutil.revsingle(repo, rev_).node()
6042 r = scmutil.revsingle(repo, rev_).node()
6043
6043
6044 if not message:
6044 if not message:
6045 # we don't translate commit messages
6045 # we don't translate commit messages
6046 message = ('Added tag %s for changeset %s' %
6046 message = ('Added tag %s for changeset %s' %
6047 (', '.join(names), short(r)))
6047 (', '.join(names), short(r)))
6048
6048
6049 date = opts.get('date')
6049 date = opts.get('date')
6050 if date:
6050 if date:
6051 date = util.parsedate(date)
6051 date = util.parsedate(date)
6052
6052
6053 if opts.get('remove'):
6053 if opts.get('remove'):
6054 editform = 'tag.remove'
6054 editform = 'tag.remove'
6055 else:
6055 else:
6056 editform = 'tag.add'
6056 editform = 'tag.add'
6057 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6057 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6058
6058
6059 # don't allow tagging the null rev
6059 # don't allow tagging the null rev
6060 if (not opts.get('remove') and
6060 if (not opts.get('remove') and
6061 scmutil.revsingle(repo, rev_).rev() == nullrev):
6061 scmutil.revsingle(repo, rev_).rev() == nullrev):
6062 raise util.Abort(_("cannot tag null revision"))
6062 raise util.Abort(_("cannot tag null revision"))
6063
6063
6064 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6064 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6065 editor=editor)
6065 editor=editor)
6066 finally:
6066 finally:
6067 release(lock, wlock)
6067 release(lock, wlock)
6068
6068
6069 @command('tags', formatteropts, '')
6069 @command('tags', formatteropts, '')
6070 def tags(ui, repo, **opts):
6070 def tags(ui, repo, **opts):
6071 """list repository tags
6071 """list repository tags
6072
6072
6073 This lists both regular and local tags. When the -v/--verbose
6073 This lists both regular and local tags. When the -v/--verbose
6074 switch is used, a third column "local" is printed for local tags.
6074 switch is used, a third column "local" is printed for local tags.
6075
6075
6076 Returns 0 on success.
6076 Returns 0 on success.
6077 """
6077 """
6078
6078
6079 fm = ui.formatter('tags', opts)
6079 fm = ui.formatter('tags', opts)
6080 hexfunc = fm.hexfunc
6080 hexfunc = fm.hexfunc
6081 tagtype = ""
6081 tagtype = ""
6082
6082
6083 for t, n in reversed(repo.tagslist()):
6083 for t, n in reversed(repo.tagslist()):
6084 hn = hexfunc(n)
6084 hn = hexfunc(n)
6085 label = 'tags.normal'
6085 label = 'tags.normal'
6086 tagtype = ''
6086 tagtype = ''
6087 if repo.tagtype(t) == 'local':
6087 if repo.tagtype(t) == 'local':
6088 label = 'tags.local'
6088 label = 'tags.local'
6089 tagtype = 'local'
6089 tagtype = 'local'
6090
6090
6091 fm.startitem()
6091 fm.startitem()
6092 fm.write('tag', '%s', t, label=label)
6092 fm.write('tag', '%s', t, label=label)
6093 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6093 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6094 fm.condwrite(not ui.quiet, 'rev node', fmt,
6094 fm.condwrite(not ui.quiet, 'rev node', fmt,
6095 repo.changelog.rev(n), hn, label=label)
6095 repo.changelog.rev(n), hn, label=label)
6096 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6096 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6097 tagtype, label=label)
6097 tagtype, label=label)
6098 fm.plain('\n')
6098 fm.plain('\n')
6099 fm.end()
6099 fm.end()
6100
6100
6101 @command('tip',
6101 @command('tip',
6102 [('p', 'patch', None, _('show patch')),
6102 [('p', 'patch', None, _('show patch')),
6103 ('g', 'git', None, _('use git extended diff format')),
6103 ('g', 'git', None, _('use git extended diff format')),
6104 ] + templateopts,
6104 ] + templateopts,
6105 _('[-p] [-g]'))
6105 _('[-p] [-g]'))
6106 def tip(ui, repo, **opts):
6106 def tip(ui, repo, **opts):
6107 """show the tip revision (DEPRECATED)
6107 """show the tip revision (DEPRECATED)
6108
6108
6109 The tip revision (usually just called the tip) is the changeset
6109 The tip revision (usually just called the tip) is the changeset
6110 most recently added to the repository (and therefore the most
6110 most recently added to the repository (and therefore the most
6111 recently changed head).
6111 recently changed head).
6112
6112
6113 If you have just made a commit, that commit will be the tip. If
6113 If you have just made a commit, that commit will be the tip. If
6114 you have just pulled changes from another repository, the tip of
6114 you have just pulled changes from another repository, the tip of
6115 that repository becomes the current tip. The "tip" tag is special
6115 that repository becomes the current tip. The "tip" tag is special
6116 and cannot be renamed or assigned to a different changeset.
6116 and cannot be renamed or assigned to a different changeset.
6117
6117
6118 This command is deprecated, please use :hg:`heads` instead.
6118 This command is deprecated, please use :hg:`heads` instead.
6119
6119
6120 Returns 0 on success.
6120 Returns 0 on success.
6121 """
6121 """
6122 displayer = cmdutil.show_changeset(ui, repo, opts)
6122 displayer = cmdutil.show_changeset(ui, repo, opts)
6123 displayer.show(repo['tip'])
6123 displayer.show(repo['tip'])
6124 displayer.close()
6124 displayer.close()
6125
6125
6126 @command('unbundle',
6126 @command('unbundle',
6127 [('u', 'update', None,
6127 [('u', 'update', None,
6128 _('update to new branch head if changesets were unbundled'))],
6128 _('update to new branch head if changesets were unbundled'))],
6129 _('[-u] FILE...'))
6129 _('[-u] FILE...'))
6130 def unbundle(ui, repo, fname1, *fnames, **opts):
6130 def unbundle(ui, repo, fname1, *fnames, **opts):
6131 """apply one or more changegroup files
6131 """apply one or more changegroup files
6132
6132
6133 Apply one or more compressed changegroup files generated by the
6133 Apply one or more compressed changegroup files generated by the
6134 bundle command.
6134 bundle command.
6135
6135
6136 Returns 0 on success, 1 if an update has unresolved files.
6136 Returns 0 on success, 1 if an update has unresolved files.
6137 """
6137 """
6138 fnames = (fname1,) + fnames
6138 fnames = (fname1,) + fnames
6139
6139
6140 lock = repo.lock()
6140 lock = repo.lock()
6141 try:
6141 try:
6142 for fname in fnames:
6142 for fname in fnames:
6143 f = hg.openpath(ui, fname)
6143 f = hg.openpath(ui, fname)
6144 gen = exchange.readbundle(ui, f, fname)
6144 gen = exchange.readbundle(ui, f, fname)
6145 if isinstance(gen, bundle2.unbundle20):
6145 if isinstance(gen, bundle2.unbundle20):
6146 tr = repo.transaction('unbundle')
6146 tr = repo.transaction('unbundle')
6147 try:
6147 try:
6148 op = bundle2.processbundle(repo, gen, lambda: tr)
6148 op = bundle2.processbundle(repo, gen, lambda: tr)
6149 tr.close()
6149 tr.close()
6150 finally:
6150 finally:
6151 if tr:
6151 if tr:
6152 tr.release()
6152 tr.release()
6153 changes = [r.get('result', 0)
6153 changes = [r.get('result', 0)
6154 for r in op.records['changegroup']]
6154 for r in op.records['changegroup']]
6155 modheads = changegroup.combineresults(changes)
6155 modheads = changegroup.combineresults(changes)
6156 else:
6156 else:
6157 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
6157 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
6158 'bundle:' + fname)
6158 'bundle:' + fname)
6159 finally:
6159 finally:
6160 lock.release()
6160 lock.release()
6161
6161
6162 return postincoming(ui, repo, modheads, opts.get('update'), None)
6162 return postincoming(ui, repo, modheads, opts.get('update'), None)
6163
6163
6164 @command('^update|up|checkout|co',
6164 @command('^update|up|checkout|co',
6165 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6165 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6166 ('c', 'check', None,
6166 ('c', 'check', None,
6167 _('update across branches if no uncommitted changes')),
6167 _('update across branches if no uncommitted changes')),
6168 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6168 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6169 ('r', 'rev', '', _('revision'), _('REV'))
6169 ('r', 'rev', '', _('revision'), _('REV'))
6170 ] + mergetoolopts,
6170 ] + mergetoolopts,
6171 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6171 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6172 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6172 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6173 tool=None):
6173 tool=None):
6174 """update working directory (or switch revisions)
6174 """update working directory (or switch revisions)
6175
6175
6176 Update the repository's working directory to the specified
6176 Update the repository's working directory to the specified
6177 changeset. If no changeset is specified, update to the tip of the
6177 changeset. If no changeset is specified, update to the tip of the
6178 current named branch and move the current bookmark (see :hg:`help
6178 current named branch and move the current bookmark (see :hg:`help
6179 bookmarks`).
6179 bookmarks`).
6180
6180
6181 Update sets the working directory's parent revision to the specified
6181 Update sets the working directory's parent revision to the specified
6182 changeset (see :hg:`help parents`).
6182 changeset (see :hg:`help parents`).
6183
6183
6184 If the changeset is not a descendant or ancestor of the working
6184 If the changeset is not a descendant or ancestor of the working
6185 directory's parent, the update is aborted. With the -c/--check
6185 directory's parent, the update is aborted. With the -c/--check
6186 option, the working directory is checked for uncommitted changes; if
6186 option, the working directory is checked for uncommitted changes; if
6187 none are found, the working directory is updated to the specified
6187 none are found, the working directory is updated to the specified
6188 changeset.
6188 changeset.
6189
6189
6190 .. container:: verbose
6190 .. container:: verbose
6191
6191
6192 The following rules apply when the working directory contains
6192 The following rules apply when the working directory contains
6193 uncommitted changes:
6193 uncommitted changes:
6194
6194
6195 1. If neither -c/--check nor -C/--clean is specified, and if
6195 1. If neither -c/--check nor -C/--clean is specified, and if
6196 the requested changeset is an ancestor or descendant of
6196 the requested changeset is an ancestor or descendant of
6197 the working directory's parent, the uncommitted changes
6197 the working directory's parent, the uncommitted changes
6198 are merged into the requested changeset and the merged
6198 are merged into the requested changeset and the merged
6199 result is left uncommitted. If the requested changeset is
6199 result is left uncommitted. If the requested changeset is
6200 not an ancestor or descendant (that is, it is on another
6200 not an ancestor or descendant (that is, it is on another
6201 branch), the update is aborted and the uncommitted changes
6201 branch), the update is aborted and the uncommitted changes
6202 are preserved.
6202 are preserved.
6203
6203
6204 2. With the -c/--check option, the update is aborted and the
6204 2. With the -c/--check option, the update is aborted and the
6205 uncommitted changes are preserved.
6205 uncommitted changes are preserved.
6206
6206
6207 3. With the -C/--clean option, uncommitted changes are discarded and
6207 3. With the -C/--clean option, uncommitted changes are discarded and
6208 the working directory is updated to the requested changeset.
6208 the working directory is updated to the requested changeset.
6209
6209
6210 To cancel an uncommitted merge (and lose your changes), use
6210 To cancel an uncommitted merge (and lose your changes), use
6211 :hg:`update --clean .`.
6211 :hg:`update --clean .`.
6212
6212
6213 Use null as the changeset to remove the working directory (like
6213 Use null as the changeset to remove the working directory (like
6214 :hg:`clone -U`).
6214 :hg:`clone -U`).
6215
6215
6216 If you want to revert just one file to an older revision, use
6216 If you want to revert just one file to an older revision, use
6217 :hg:`revert [-r REV] NAME`.
6217 :hg:`revert [-r REV] NAME`.
6218
6218
6219 See :hg:`help dates` for a list of formats valid for -d/--date.
6219 See :hg:`help dates` for a list of formats valid for -d/--date.
6220
6220
6221 Returns 0 on success, 1 if there are unresolved files.
6221 Returns 0 on success, 1 if there are unresolved files.
6222 """
6222 """
6223 if rev and node:
6223 if rev and node:
6224 raise util.Abort(_("please specify just one revision"))
6224 raise util.Abort(_("please specify just one revision"))
6225
6225
6226 if rev is None or rev == '':
6226 if rev is None or rev == '':
6227 rev = node
6227 rev = node
6228
6228
6229 cmdutil.clearunfinished(repo)
6229 cmdutil.clearunfinished(repo)
6230
6230
6231 # with no argument, we also move the current bookmark, if any
6231 # with no argument, we also move the current bookmark, if any
6232 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
6232 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
6233
6233
6234 # if we defined a bookmark, we have to remember the original bookmark name
6234 # if we defined a bookmark, we have to remember the original bookmark name
6235 brev = rev
6235 brev = rev
6236 rev = scmutil.revsingle(repo, rev, rev).rev()
6236 rev = scmutil.revsingle(repo, rev, rev).rev()
6237
6237
6238 if check and clean:
6238 if check and clean:
6239 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
6239 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
6240
6240
6241 if date:
6241 if date:
6242 if rev is not None:
6242 if rev is not None:
6243 raise util.Abort(_("you can't specify a revision and a date"))
6243 raise util.Abort(_("you can't specify a revision and a date"))
6244 rev = cmdutil.finddate(ui, repo, date)
6244 rev = cmdutil.finddate(ui, repo, date)
6245
6245
6246 if check:
6246 if check:
6247 c = repo[None]
6247 c = repo[None]
6248 if c.dirty(merge=False, branch=False, missing=True):
6248 if c.dirty(merge=False, branch=False, missing=True):
6249 raise util.Abort(_("uncommitted changes"))
6249 raise util.Abort(_("uncommitted changes"))
6250 if rev is None:
6250 if rev is None:
6251 rev = repo[repo[None].branch()].rev()
6251 rev = repo[repo[None].branch()].rev()
6252
6252
6253 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6253 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6254
6254
6255 if clean:
6255 if clean:
6256 ret = hg.clean(repo, rev)
6256 ret = hg.clean(repo, rev)
6257 else:
6257 else:
6258 ret = hg.update(repo, rev)
6258 ret = hg.update(repo, rev)
6259
6259
6260 if not ret and movemarkfrom:
6260 if not ret and movemarkfrom:
6261 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6261 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6262 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
6262 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
6263 elif brev in repo._bookmarks:
6263 elif brev in repo._bookmarks:
6264 bookmarks.setcurrent(repo, brev)
6264 bookmarks.setcurrent(repo, brev)
6265 ui.status(_("(activating bookmark %s)\n") % brev)
6265 ui.status(_("(activating bookmark %s)\n") % brev)
6266 elif brev:
6266 elif brev:
6267 if repo._bookmarkcurrent:
6267 if repo._bookmarkcurrent:
6268 ui.status(_("(leaving bookmark %s)\n") %
6268 ui.status(_("(leaving bookmark %s)\n") %
6269 repo._bookmarkcurrent)
6269 repo._bookmarkcurrent)
6270 bookmarks.unsetcurrent(repo)
6270 bookmarks.unsetcurrent(repo)
6271
6271
6272 return ret
6272 return ret
6273
6273
6274 @command('verify', [])
6274 @command('verify', [])
6275 def verify(ui, repo):
6275 def verify(ui, repo):
6276 """verify the integrity of the repository
6276 """verify the integrity of the repository
6277
6277
6278 Verify the integrity of the current repository.
6278 Verify the integrity of the current repository.
6279
6279
6280 This will perform an extensive check of the repository's
6280 This will perform an extensive check of the repository's
6281 integrity, validating the hashes and checksums of each entry in
6281 integrity, validating the hashes and checksums of each entry in
6282 the changelog, manifest, and tracked files, as well as the
6282 the changelog, manifest, and tracked files, as well as the
6283 integrity of their crosslinks and indices.
6283 integrity of their crosslinks and indices.
6284
6284
6285 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6285 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6286 for more information about recovery from corruption of the
6286 for more information about recovery from corruption of the
6287 repository.
6287 repository.
6288
6288
6289 Returns 0 on success, 1 if errors are encountered.
6289 Returns 0 on success, 1 if errors are encountered.
6290 """
6290 """
6291 return hg.verify(repo)
6291 return hg.verify(repo)
6292
6292
6293 @command('version', [], norepo=True)
6293 @command('version', [], norepo=True)
6294 def version_(ui):
6294 def version_(ui):
6295 """output version and copyright information"""
6295 """output version and copyright information"""
6296 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6296 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6297 % util.version())
6297 % util.version())
6298 ui.status(_(
6298 ui.status(_(
6299 "(see http://mercurial.selenic.com for more information)\n"
6299 "(see http://mercurial.selenic.com for more information)\n"
6300 "\nCopyright (C) 2005-2014 Matt Mackall and others\n"
6300 "\nCopyright (C) 2005-2014 Matt Mackall and others\n"
6301 "This is free software; see the source for copying conditions. "
6301 "This is free software; see the source for copying conditions. "
6302 "There is NO\nwarranty; "
6302 "There is NO\nwarranty; "
6303 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6303 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6304 ))
6304 ))
6305
6305
6306 ui.note(_("\nEnabled extensions:\n\n"))
6306 ui.note(_("\nEnabled extensions:\n\n"))
6307 if ui.verbose:
6307 if ui.verbose:
6308 # format names and versions into columns
6308 # format names and versions into columns
6309 names = []
6309 names = []
6310 vers = []
6310 vers = []
6311 for name, module in extensions.extensions():
6311 for name, module in extensions.extensions():
6312 names.append(name)
6312 names.append(name)
6313 vers.append(extensions.moduleversion(module))
6313 vers.append(extensions.moduleversion(module))
6314 if names:
6314 if names:
6315 maxnamelen = max(len(n) for n in names)
6315 maxnamelen = max(len(n) for n in names)
6316 for i, name in enumerate(names):
6316 for i, name in enumerate(names):
6317 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
6317 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
@@ -1,275 +1,275 b''
1 # httppeer.py - HTTP repository proxy classes for mercurial
1 # httppeer.py - HTTP repository proxy classes for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from node import nullid
9 from node import nullid
10 from i18n import _
10 from i18n import _
11 import tempfile
11 import tempfile
12 import changegroup, statichttprepo, error, httpconnection, url, util, wireproto
12 import changegroup, statichttprepo, error, httpconnection, url, util, wireproto
13 import os, urllib, urllib2, zlib, httplib
13 import os, urllib, urllib2, zlib, httplib
14 import errno, socket
14 import errno, socket
15
15
16 def zgenerator(f):
16 def zgenerator(f):
17 zd = zlib.decompressobj()
17 zd = zlib.decompressobj()
18 try:
18 try:
19 for chunk in util.filechunkiter(f):
19 for chunk in util.filechunkiter(f):
20 while chunk:
20 while chunk:
21 yield zd.decompress(chunk, 2**18)
21 yield zd.decompress(chunk, 2**18)
22 chunk = zd.unconsumed_tail
22 chunk = zd.unconsumed_tail
23 except httplib.HTTPException:
23 except httplib.HTTPException:
24 raise IOError(None, _('connection ended unexpectedly'))
24 raise IOError(None, _('connection ended unexpectedly'))
25 yield zd.flush()
25 yield zd.flush()
26
26
27 class httppeer(wireproto.wirepeer):
27 class httppeer(wireproto.wirepeer):
28 def __init__(self, ui, path):
28 def __init__(self, ui, path):
29 self.path = path
29 self.path = path
30 self.caps = None
30 self.caps = None
31 self.handler = None
31 self.handler = None
32 self.urlopener = None
32 self.urlopener = None
33 u = util.url(path)
33 u = util.url(path)
34 if u.query or u.fragment:
34 if u.query or u.fragment:
35 raise util.Abort(_('unsupported URL component: "%s"') %
35 raise util.Abort(_('unsupported URL component: "%s"') %
36 (u.query or u.fragment))
36 (u.query or u.fragment))
37
37
38 # urllib cannot handle URLs with embedded user or passwd
38 # urllib cannot handle URLs with embedded user or passwd
39 self._url, authinfo = u.authinfo()
39 self._url, authinfo = u.authinfo()
40
40
41 self.ui = ui
41 self.ui = ui
42 self.ui.debug('using %s\n' % self._url)
42 self.ui.debug('using %s\n' % self._url)
43
43
44 self.urlopener = url.opener(ui, authinfo)
44 self.urlopener = url.opener(ui, authinfo)
45
45
46 def __del__(self):
46 def __del__(self):
47 if self.urlopener:
47 if self.urlopener:
48 for h in self.urlopener.handlers:
48 for h in self.urlopener.handlers:
49 h.close()
49 h.close()
50 getattr(h, "close_all", lambda : None)()
50 getattr(h, "close_all", lambda : None)()
51
51
52 def url(self):
52 def url(self):
53 return self.path
53 return self.path
54
54
55 # look up capabilities only when needed
55 # look up capabilities only when needed
56
56
57 def _fetchcaps(self):
57 def _fetchcaps(self):
58 self.caps = set(self._call('capabilities').split())
58 self.caps = set(self._call('capabilities').split())
59
59
60 def _capabilities(self):
60 def _capabilities(self):
61 if self.caps is None:
61 if self.caps is None:
62 try:
62 try:
63 self._fetchcaps()
63 self._fetchcaps()
64 except error.RepoError:
64 except error.RepoError:
65 self.caps = set()
65 self.caps = set()
66 self.ui.debug('capabilities: %s\n' %
66 self.ui.debug('capabilities: %s\n' %
67 (' '.join(self.caps or ['none'])))
67 (' '.join(self.caps or ['none'])))
68 return self.caps
68 return self.caps
69
69
70 def lock(self):
70 def lock(self):
71 raise util.Abort(_('operation not supported over http'))
71 raise util.Abort(_('operation not supported over http'))
72
72
73 def _callstream(self, cmd, **args):
73 def _callstream(self, cmd, **args):
74 if cmd == 'pushkey':
74 if cmd == 'pushkey':
75 args['data'] = ''
75 args['data'] = ''
76 data = args.pop('data', None)
76 data = args.pop('data', None)
77 size = 0
77 size = 0
78 if util.safehasattr(data, 'length'):
78 if util.safehasattr(data, 'length'):
79 size = data.length
79 size = data.length
80 elif data is not None:
80 elif data is not None:
81 size = len(data)
81 size = len(data)
82 headers = args.pop('headers', {})
82 headers = args.pop('headers', {})
83 if data is not None and 'Content-Type' not in headers:
83 if data is not None and 'Content-Type' not in headers:
84 headers['Content-Type'] = 'application/mercurial-0.1'
84 headers['Content-Type'] = 'application/mercurial-0.1'
85
85
86
86
87 if size and self.ui.configbool('ui', 'usehttp2', False):
87 if size and self.ui.configbool('ui', 'usehttp2', False):
88 headers['Expect'] = '100-Continue'
88 headers['Expect'] = '100-Continue'
89 headers['X-HgHttp2'] = '1'
89 headers['X-HgHttp2'] = '1'
90
90
91 self.ui.debug("sending %s command\n" % cmd)
91 self.ui.debug("sending %s command\n" % cmd)
92 q = [('cmd', cmd)]
92 q = [('cmd', cmd)]
93 headersize = 0
93 headersize = 0
94 if len(args) > 0:
94 if len(args) > 0:
95 httpheader = self.capable('httpheader')
95 httpheader = self.capable('httpheader')
96 if httpheader:
96 if httpheader:
97 headersize = int(httpheader.split(',')[0])
97 headersize = int(httpheader.split(',')[0])
98 if headersize > 0:
98 if headersize > 0:
99 # The headers can typically carry more data than the URL.
99 # The headers can typically carry more data than the URL.
100 encargs = urllib.urlencode(sorted(args.items()))
100 encargs = urllib.urlencode(sorted(args.items()))
101 headerfmt = 'X-HgArg-%s'
101 headerfmt = 'X-HgArg-%s'
102 contentlen = headersize - len(headerfmt % '000' + ': \r\n')
102 contentlen = headersize - len(headerfmt % '000' + ': \r\n')
103 headernum = 0
103 headernum = 0
104 for i in xrange(0, len(encargs), contentlen):
104 for i in xrange(0, len(encargs), contentlen):
105 headernum += 1
105 headernum += 1
106 header = headerfmt % str(headernum)
106 header = headerfmt % str(headernum)
107 headers[header] = encargs[i:i + contentlen]
107 headers[header] = encargs[i:i + contentlen]
108 varyheaders = [headerfmt % str(h) for h in range(1, headernum + 1)]
108 varyheaders = [headerfmt % str(h) for h in range(1, headernum + 1)]
109 headers['Vary'] = ','.join(varyheaders)
109 headers['Vary'] = ','.join(varyheaders)
110 else:
110 else:
111 q += sorted(args.items())
111 q += sorted(args.items())
112 qs = '?%s' % urllib.urlencode(q)
112 qs = '?%s' % urllib.urlencode(q)
113 cu = "%s%s" % (self._url, qs)
113 cu = "%s%s" % (self._url, qs)
114 req = urllib2.Request(cu, data, headers)
114 req = urllib2.Request(cu, data, headers)
115 if data is not None:
115 if data is not None:
116 self.ui.debug("sending %s bytes\n" % size)
116 self.ui.debug("sending %s bytes\n" % size)
117 req.add_unredirected_header('Content-Length', '%d' % size)
117 req.add_unredirected_header('Content-Length', '%d' % size)
118 try:
118 try:
119 resp = self.urlopener.open(req)
119 resp = self.urlopener.open(req)
120 except urllib2.HTTPError, inst:
120 except urllib2.HTTPError, inst:
121 if inst.code == 401:
121 if inst.code == 401:
122 raise util.Abort(_('authorization failed'))
122 raise util.Abort(_('authorization failed'))
123 raise
123 raise
124 except httplib.HTTPException, inst:
124 except httplib.HTTPException, inst:
125 self.ui.debug('http error while sending %s command\n' % cmd)
125 self.ui.debug('http error while sending %s command\n' % cmd)
126 self.ui.traceback()
126 self.ui.traceback()
127 raise IOError(None, inst)
127 raise IOError(None, inst)
128 except IndexError:
128 except IndexError:
129 # this only happens with Python 2.3, later versions raise URLError
129 # this only happens with Python 2.3, later versions raise URLError
130 raise util.Abort(_('http error, possibly caused by proxy setting'))
130 raise util.Abort(_('http error, possibly caused by proxy setting'))
131 # record the url we got redirected to
131 # record the url we got redirected to
132 resp_url = resp.geturl()
132 resp_url = resp.geturl()
133 if resp_url.endswith(qs):
133 if resp_url.endswith(qs):
134 resp_url = resp_url[:-len(qs)]
134 resp_url = resp_url[:-len(qs)]
135 if self._url.rstrip('/') != resp_url.rstrip('/'):
135 if self._url.rstrip('/') != resp_url.rstrip('/'):
136 if not self.ui.quiet:
136 if not self.ui.quiet:
137 self.ui.warn(_('real URL is %s\n') % resp_url)
137 self.ui.warn(_('real URL is %s\n') % resp_url)
138 self._url = resp_url
138 self._url = resp_url
139 try:
139 try:
140 proto = resp.getheader('content-type')
140 proto = resp.getheader('content-type')
141 except AttributeError:
141 except AttributeError:
142 proto = resp.headers.get('content-type', '')
142 proto = resp.headers.get('content-type', '')
143
143
144 safeurl = util.hidepassword(self._url)
144 safeurl = util.hidepassword(self._url)
145 if proto.startswith('application/hg-error'):
145 if proto.startswith('application/hg-error'):
146 raise error.OutOfBandError(resp.read())
146 raise error.OutOfBandError(resp.read())
147 # accept old "text/plain" and "application/hg-changegroup" for now
147 # accept old "text/plain" and "application/hg-changegroup" for now
148 if not (proto.startswith('application/mercurial-') or
148 if not (proto.startswith('application/mercurial-') or
149 (proto.startswith('text/plain')
149 (proto.startswith('text/plain')
150 and not resp.headers.get('content-length')) or
150 and not resp.headers.get('content-length')) or
151 proto.startswith('application/hg-changegroup')):
151 proto.startswith('application/hg-changegroup')):
152 self.ui.debug("requested URL: '%s'\n" % util.hidepassword(cu))
152 self.ui.debug("requested URL: '%s'\n" % util.hidepassword(cu))
153 raise error.RepoError(
153 raise error.RepoError(
154 _("'%s' does not appear to be an hg repository:\n"
154 _("'%s' does not appear to be an hg repository:\n"
155 "---%%<--- (%s)\n%s\n---%%<---\n")
155 "---%%<--- (%s)\n%s\n---%%<---\n")
156 % (safeurl, proto or 'no content-type', resp.read(1024)))
156 % (safeurl, proto or 'no content-type', resp.read(1024)))
157
157
158 if proto.startswith('application/mercurial-'):
158 if proto.startswith('application/mercurial-'):
159 try:
159 try:
160 version = proto.split('-', 1)[1]
160 version = proto.split('-', 1)[1]
161 version_info = tuple([int(n) for n in version.split('.')])
161 version_info = tuple([int(n) for n in version.split('.')])
162 except ValueError:
162 except ValueError:
163 raise error.RepoError(_("'%s' sent a broken Content-Type "
163 raise error.RepoError(_("'%s' sent a broken Content-Type "
164 "header (%s)") % (safeurl, proto))
164 "header (%s)") % (safeurl, proto))
165 if version_info > (0, 1):
165 if version_info > (0, 1):
166 raise error.RepoError(_("'%s' uses newer protocol %s") %
166 raise error.RepoError(_("'%s' uses newer protocol %s") %
167 (safeurl, version))
167 (safeurl, version))
168
168
169 return resp
169 return resp
170
170
171 def _call(self, cmd, **args):
171 def _call(self, cmd, **args):
172 fp = self._callstream(cmd, **args)
172 fp = self._callstream(cmd, **args)
173 try:
173 try:
174 return fp.read()
174 return fp.read()
175 finally:
175 finally:
176 # if using keepalive, allow connection to be reused
176 # if using keepalive, allow connection to be reused
177 fp.close()
177 fp.close()
178
178
179 def _callpush(self, cmd, cg, **args):
179 def _callpush(self, cmd, cg, **args):
180 # have to stream bundle to a temp file because we do not have
180 # have to stream bundle to a temp file because we do not have
181 # http 1.1 chunked transfer.
181 # http 1.1 chunked transfer.
182
182
183 types = self.capable('unbundle')
183 types = self.capable('unbundle')
184 try:
184 try:
185 types = types.split(',')
185 types = types.split(',')
186 except AttributeError:
186 except AttributeError:
187 # servers older than d1b16a746db6 will send 'unbundle' as a
187 # servers older than d1b16a746db6 will send 'unbundle' as a
188 # boolean capability. They only support headerless/uncompressed
188 # boolean capability. They only support headerless/uncompressed
189 # bundles.
189 # bundles.
190 types = [""]
190 types = [""]
191 for x in types:
191 for x in types:
192 if x in changegroup.bundletypes:
192 if x in changegroup.bundletypes:
193 type = x
193 type = x
194 break
194 break
195
195
196 tempname = changegroup.writebundle(cg, None, type)
196 tempname = changegroup.writebundle(self.ui, cg, None, type)
197 fp = httpconnection.httpsendfile(self.ui, tempname, "rb")
197 fp = httpconnection.httpsendfile(self.ui, tempname, "rb")
198 headers = {'Content-Type': 'application/mercurial-0.1'}
198 headers = {'Content-Type': 'application/mercurial-0.1'}
199
199
200 try:
200 try:
201 try:
201 try:
202 r = self._call(cmd, data=fp, headers=headers, **args)
202 r = self._call(cmd, data=fp, headers=headers, **args)
203 vals = r.split('\n', 1)
203 vals = r.split('\n', 1)
204 if len(vals) < 2:
204 if len(vals) < 2:
205 raise error.ResponseError(_("unexpected response:"), r)
205 raise error.ResponseError(_("unexpected response:"), r)
206 return vals
206 return vals
207 except socket.error, err:
207 except socket.error, err:
208 if err.args[0] in (errno.ECONNRESET, errno.EPIPE):
208 if err.args[0] in (errno.ECONNRESET, errno.EPIPE):
209 raise util.Abort(_('push failed: %s') % err.args[1])
209 raise util.Abort(_('push failed: %s') % err.args[1])
210 raise util.Abort(err.args[1])
210 raise util.Abort(err.args[1])
211 finally:
211 finally:
212 fp.close()
212 fp.close()
213 os.unlink(tempname)
213 os.unlink(tempname)
214
214
215 def _calltwowaystream(self, cmd, fp, **args):
215 def _calltwowaystream(self, cmd, fp, **args):
216 fh = None
216 fh = None
217 fp_ = None
217 fp_ = None
218 filename = None
218 filename = None
219 try:
219 try:
220 # dump bundle to disk
220 # dump bundle to disk
221 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
221 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
222 fh = os.fdopen(fd, "wb")
222 fh = os.fdopen(fd, "wb")
223 d = fp.read(4096)
223 d = fp.read(4096)
224 while d:
224 while d:
225 fh.write(d)
225 fh.write(d)
226 d = fp.read(4096)
226 d = fp.read(4096)
227 fh.close()
227 fh.close()
228 # start http push
228 # start http push
229 fp_ = httpconnection.httpsendfile(self.ui, filename, "rb")
229 fp_ = httpconnection.httpsendfile(self.ui, filename, "rb")
230 headers = {'Content-Type': 'application/mercurial-0.1'}
230 headers = {'Content-Type': 'application/mercurial-0.1'}
231 return self._callstream(cmd, data=fp_, headers=headers, **args)
231 return self._callstream(cmd, data=fp_, headers=headers, **args)
232 finally:
232 finally:
233 if fp_ is not None:
233 if fp_ is not None:
234 fp_.close()
234 fp_.close()
235 if fh is not None:
235 if fh is not None:
236 fh.close()
236 fh.close()
237 os.unlink(filename)
237 os.unlink(filename)
238
238
239 def _callcompressable(self, cmd, **args):
239 def _callcompressable(self, cmd, **args):
240 stream = self._callstream(cmd, **args)
240 stream = self._callstream(cmd, **args)
241 return util.chunkbuffer(zgenerator(stream))
241 return util.chunkbuffer(zgenerator(stream))
242
242
243 def _abort(self, exception):
243 def _abort(self, exception):
244 raise exception
244 raise exception
245
245
246 class httpspeer(httppeer):
246 class httpspeer(httppeer):
247 def __init__(self, ui, path):
247 def __init__(self, ui, path):
248 if not url.has_https:
248 if not url.has_https:
249 raise util.Abort(_('Python support for SSL and HTTPS '
249 raise util.Abort(_('Python support for SSL and HTTPS '
250 'is not installed'))
250 'is not installed'))
251 httppeer.__init__(self, ui, path)
251 httppeer.__init__(self, ui, path)
252
252
253 def instance(ui, path, create):
253 def instance(ui, path, create):
254 if create:
254 if create:
255 raise util.Abort(_('cannot create new http repository'))
255 raise util.Abort(_('cannot create new http repository'))
256 try:
256 try:
257 if path.startswith('https:'):
257 if path.startswith('https:'):
258 inst = httpspeer(ui, path)
258 inst = httpspeer(ui, path)
259 else:
259 else:
260 inst = httppeer(ui, path)
260 inst = httppeer(ui, path)
261 try:
261 try:
262 # Try to do useful work when checking compatibility.
262 # Try to do useful work when checking compatibility.
263 # Usually saves a roundtrip since we want the caps anyway.
263 # Usually saves a roundtrip since we want the caps anyway.
264 inst._fetchcaps()
264 inst._fetchcaps()
265 except error.RepoError:
265 except error.RepoError:
266 # No luck, try older compatibility check.
266 # No luck, try older compatibility check.
267 inst.between([(nullid, nullid)])
267 inst.between([(nullid, nullid)])
268 return inst
268 return inst
269 except error.RepoError, httpexception:
269 except error.RepoError, httpexception:
270 try:
270 try:
271 r = statichttprepo.instance(ui, "static-" + path, create)
271 r = statichttprepo.instance(ui, "static-" + path, create)
272 ui.note('(falling back to static-http)\n')
272 ui.note('(falling back to static-http)\n')
273 return r
273 return r
274 except error.RepoError:
274 except error.RepoError:
275 raise httpexception # use the original http RepoError instead
275 raise httpexception # use the original http RepoError instead
@@ -1,197 +1,197 b''
1 # repair.py - functions for repository repair for mercurial
1 # repair.py - functions for repository repair for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 # Copyright 2007 Matt Mackall
4 # Copyright 2007 Matt Mackall
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from mercurial import changegroup, exchange, util
9 from mercurial import changegroup, exchange, util
10 from mercurial.node import short, hex
10 from mercurial.node import short, hex
11 from mercurial.i18n import _
11 from mercurial.i18n import _
12 import errno
12 import errno
13
13
14 def _bundle(repo, bases, heads, node, suffix, compress=True):
14 def _bundle(repo, bases, heads, node, suffix, compress=True):
15 """create a bundle with the specified revisions as a backup"""
15 """create a bundle with the specified revisions as a backup"""
16 cg = changegroup.changegroupsubset(repo, bases, heads, 'strip')
16 cg = changegroup.changegroupsubset(repo, bases, heads, 'strip')
17 backupdir = "strip-backup"
17 backupdir = "strip-backup"
18 vfs = repo.vfs
18 vfs = repo.vfs
19 if not vfs.isdir(backupdir):
19 if not vfs.isdir(backupdir):
20 vfs.mkdir(backupdir)
20 vfs.mkdir(backupdir)
21
21
22 # Include a hash of all the nodes in the filename for uniqueness
22 # Include a hash of all the nodes in the filename for uniqueness
23 hexbases = (hex(n) for n in bases)
23 hexbases = (hex(n) for n in bases)
24 hexheads = (hex(n) for n in heads)
24 hexheads = (hex(n) for n in heads)
25 allcommits = repo.set('%ls::%ls', hexbases, hexheads)
25 allcommits = repo.set('%ls::%ls', hexbases, hexheads)
26 allhashes = sorted(c.hex() for c in allcommits)
26 allhashes = sorted(c.hex() for c in allcommits)
27 totalhash = util.sha1(''.join(allhashes)).hexdigest()
27 totalhash = util.sha1(''.join(allhashes)).hexdigest()
28 name = "%s/%s-%s-%s.hg" % (backupdir, short(node), totalhash[:8], suffix)
28 name = "%s/%s-%s-%s.hg" % (backupdir, short(node), totalhash[:8], suffix)
29
29
30 if compress:
30 if compress:
31 bundletype = "HG10BZ"
31 bundletype = "HG10BZ"
32 else:
32 else:
33 bundletype = "HG10UN"
33 bundletype = "HG10UN"
34 return changegroup.writebundle(cg, name, bundletype, vfs)
34 return changegroup.writebundle(repo.ui, cg, name, bundletype, vfs)
35
35
36 def _collectfiles(repo, striprev):
36 def _collectfiles(repo, striprev):
37 """find out the filelogs affected by the strip"""
37 """find out the filelogs affected by the strip"""
38 files = set()
38 files = set()
39
39
40 for x in xrange(striprev, len(repo)):
40 for x in xrange(striprev, len(repo)):
41 files.update(repo[x].files())
41 files.update(repo[x].files())
42
42
43 return sorted(files)
43 return sorted(files)
44
44
45 def _collectbrokencsets(repo, files, striprev):
45 def _collectbrokencsets(repo, files, striprev):
46 """return the changesets which will be broken by the truncation"""
46 """return the changesets which will be broken by the truncation"""
47 s = set()
47 s = set()
48 def collectone(revlog):
48 def collectone(revlog):
49 _, brokenset = revlog.getstrippoint(striprev)
49 _, brokenset = revlog.getstrippoint(striprev)
50 s.update([revlog.linkrev(r) for r in brokenset])
50 s.update([revlog.linkrev(r) for r in brokenset])
51
51
52 collectone(repo.manifest)
52 collectone(repo.manifest)
53 for fname in files:
53 for fname in files:
54 collectone(repo.file(fname))
54 collectone(repo.file(fname))
55
55
56 return s
56 return s
57
57
58 def strip(ui, repo, nodelist, backup=True, topic='backup'):
58 def strip(ui, repo, nodelist, backup=True, topic='backup'):
59
59
60 # Simple way to maintain backwards compatibility for this
60 # Simple way to maintain backwards compatibility for this
61 # argument.
61 # argument.
62 if backup in ['none', 'strip']:
62 if backup in ['none', 'strip']:
63 backup = False
63 backup = False
64
64
65 repo = repo.unfiltered()
65 repo = repo.unfiltered()
66 repo.destroying()
66 repo.destroying()
67
67
68 cl = repo.changelog
68 cl = repo.changelog
69 # TODO handle undo of merge sets
69 # TODO handle undo of merge sets
70 if isinstance(nodelist, str):
70 if isinstance(nodelist, str):
71 nodelist = [nodelist]
71 nodelist = [nodelist]
72 striplist = [cl.rev(node) for node in nodelist]
72 striplist = [cl.rev(node) for node in nodelist]
73 striprev = min(striplist)
73 striprev = min(striplist)
74
74
75 # Some revisions with rev > striprev may not be descendants of striprev.
75 # Some revisions with rev > striprev may not be descendants of striprev.
76 # We have to find these revisions and put them in a bundle, so that
76 # We have to find these revisions and put them in a bundle, so that
77 # we can restore them after the truncations.
77 # we can restore them after the truncations.
78 # To create the bundle we use repo.changegroupsubset which requires
78 # To create the bundle we use repo.changegroupsubset which requires
79 # the list of heads and bases of the set of interesting revisions.
79 # the list of heads and bases of the set of interesting revisions.
80 # (head = revision in the set that has no descendant in the set;
80 # (head = revision in the set that has no descendant in the set;
81 # base = revision in the set that has no ancestor in the set)
81 # base = revision in the set that has no ancestor in the set)
82 tostrip = set(striplist)
82 tostrip = set(striplist)
83 for rev in striplist:
83 for rev in striplist:
84 for desc in cl.descendants([rev]):
84 for desc in cl.descendants([rev]):
85 tostrip.add(desc)
85 tostrip.add(desc)
86
86
87 files = _collectfiles(repo, striprev)
87 files = _collectfiles(repo, striprev)
88 saverevs = _collectbrokencsets(repo, files, striprev)
88 saverevs = _collectbrokencsets(repo, files, striprev)
89
89
90 # compute heads
90 # compute heads
91 saveheads = set(saverevs)
91 saveheads = set(saverevs)
92 for r in xrange(striprev + 1, len(cl)):
92 for r in xrange(striprev + 1, len(cl)):
93 if r not in tostrip:
93 if r not in tostrip:
94 saverevs.add(r)
94 saverevs.add(r)
95 saveheads.difference_update(cl.parentrevs(r))
95 saveheads.difference_update(cl.parentrevs(r))
96 saveheads.add(r)
96 saveheads.add(r)
97 saveheads = [cl.node(r) for r in saveheads]
97 saveheads = [cl.node(r) for r in saveheads]
98
98
99 # compute base nodes
99 # compute base nodes
100 if saverevs:
100 if saverevs:
101 descendants = set(cl.descendants(saverevs))
101 descendants = set(cl.descendants(saverevs))
102 saverevs.difference_update(descendants)
102 saverevs.difference_update(descendants)
103 savebases = [cl.node(r) for r in saverevs]
103 savebases = [cl.node(r) for r in saverevs]
104 stripbases = [cl.node(r) for r in tostrip]
104 stripbases = [cl.node(r) for r in tostrip]
105
105
106 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
106 # For a set s, max(parents(s) - s) is the same as max(heads(::s - s)), but
107 # is much faster
107 # is much faster
108 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
108 newbmtarget = repo.revs('max(parents(%ld) - (%ld))', tostrip, tostrip)
109 if newbmtarget:
109 if newbmtarget:
110 newbmtarget = repo[newbmtarget.first()].node()
110 newbmtarget = repo[newbmtarget.first()].node()
111 else:
111 else:
112 newbmtarget = '.'
112 newbmtarget = '.'
113
113
114 bm = repo._bookmarks
114 bm = repo._bookmarks
115 updatebm = []
115 updatebm = []
116 for m in bm:
116 for m in bm:
117 rev = repo[bm[m]].rev()
117 rev = repo[bm[m]].rev()
118 if rev in tostrip:
118 if rev in tostrip:
119 updatebm.append(m)
119 updatebm.append(m)
120
120
121 # create a changegroup for all the branches we need to keep
121 # create a changegroup for all the branches we need to keep
122 backupfile = None
122 backupfile = None
123 vfs = repo.vfs
123 vfs = repo.vfs
124 if backup:
124 if backup:
125 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
125 backupfile = _bundle(repo, stripbases, cl.heads(), node, topic)
126 repo.ui.status(_("saved backup bundle to %s\n") %
126 repo.ui.status(_("saved backup bundle to %s\n") %
127 vfs.join(backupfile))
127 vfs.join(backupfile))
128 repo.ui.log("backupbundle", "saved backup bundle to %s\n",
128 repo.ui.log("backupbundle", "saved backup bundle to %s\n",
129 vfs.join(backupfile))
129 vfs.join(backupfile))
130 if saveheads or savebases:
130 if saveheads or savebases:
131 # do not compress partial bundle if we remove it from disk later
131 # do not compress partial bundle if we remove it from disk later
132 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
132 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
133 compress=False)
133 compress=False)
134
134
135 mfst = repo.manifest
135 mfst = repo.manifest
136
136
137 tr = repo.transaction("strip")
137 tr = repo.transaction("strip")
138 offset = len(tr.entries)
138 offset = len(tr.entries)
139
139
140 try:
140 try:
141 tr.startgroup()
141 tr.startgroup()
142 cl.strip(striprev, tr)
142 cl.strip(striprev, tr)
143 mfst.strip(striprev, tr)
143 mfst.strip(striprev, tr)
144 for fn in files:
144 for fn in files:
145 repo.file(fn).strip(striprev, tr)
145 repo.file(fn).strip(striprev, tr)
146 tr.endgroup()
146 tr.endgroup()
147
147
148 try:
148 try:
149 for i in xrange(offset, len(tr.entries)):
149 for i in xrange(offset, len(tr.entries)):
150 file, troffset, ignore = tr.entries[i]
150 file, troffset, ignore = tr.entries[i]
151 repo.svfs(file, 'a').truncate(troffset)
151 repo.svfs(file, 'a').truncate(troffset)
152 if troffset == 0:
152 if troffset == 0:
153 repo.store.markremoved(file)
153 repo.store.markremoved(file)
154 tr.close()
154 tr.close()
155 except: # re-raises
155 except: # re-raises
156 tr.abort()
156 tr.abort()
157 raise
157 raise
158
158
159 if saveheads or savebases:
159 if saveheads or savebases:
160 ui.note(_("adding branch\n"))
160 ui.note(_("adding branch\n"))
161 f = vfs.open(chgrpfile, "rb")
161 f = vfs.open(chgrpfile, "rb")
162 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
162 gen = exchange.readbundle(ui, f, chgrpfile, vfs)
163 if not repo.ui.verbose:
163 if not repo.ui.verbose:
164 # silence internal shuffling chatter
164 # silence internal shuffling chatter
165 repo.ui.pushbuffer()
165 repo.ui.pushbuffer()
166 changegroup.addchangegroup(repo, gen, 'strip',
166 changegroup.addchangegroup(repo, gen, 'strip',
167 'bundle:' + vfs.join(chgrpfile), True)
167 'bundle:' + vfs.join(chgrpfile), True)
168 if not repo.ui.verbose:
168 if not repo.ui.verbose:
169 repo.ui.popbuffer()
169 repo.ui.popbuffer()
170 f.close()
170 f.close()
171
171
172 # remove undo files
172 # remove undo files
173 for undovfs, undofile in repo.undofiles():
173 for undovfs, undofile in repo.undofiles():
174 try:
174 try:
175 undovfs.unlink(undofile)
175 undovfs.unlink(undofile)
176 except OSError, e:
176 except OSError, e:
177 if e.errno != errno.ENOENT:
177 if e.errno != errno.ENOENT:
178 ui.warn(_('error removing %s: %s\n') %
178 ui.warn(_('error removing %s: %s\n') %
179 (undovfs.join(undofile), str(e)))
179 (undovfs.join(undofile), str(e)))
180
180
181 for m in updatebm:
181 for m in updatebm:
182 bm[m] = repo[newbmtarget].node()
182 bm[m] = repo[newbmtarget].node()
183 bm.write()
183 bm.write()
184 except: # re-raises
184 except: # re-raises
185 if backupfile:
185 if backupfile:
186 ui.warn(_("strip failed, full bundle stored in '%s'\n")
186 ui.warn(_("strip failed, full bundle stored in '%s'\n")
187 % vfs.join(backupfile))
187 % vfs.join(backupfile))
188 elif saveheads:
188 elif saveheads:
189 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
189 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
190 % vfs.join(chgrpfile))
190 % vfs.join(chgrpfile))
191 raise
191 raise
192 else:
192 else:
193 if saveheads or savebases:
193 if saveheads or savebases:
194 # Remove partial backup only if there were no exceptions
194 # Remove partial backup only if there were no exceptions
195 vfs.unlink(chgrpfile)
195 vfs.unlink(chgrpfile)
196
196
197 repo.destroyed()
197 repo.destroyed()
General Comments 0
You need to be logged in to leave comments. Login now