##// END OF EJS Templates
url: use open and not url.open for local files (issue3624)
Siddharth Agarwal -
r17887:0e2846b2 stable
parent child Browse files
Show More
@@ -1,377 +1,377 b''
1 # synthrepo.py - repo synthesis
1 # synthrepo.py - repo synthesis
2 #
2 #
3 # Copyright 2012 Facebook
3 # Copyright 2012 Facebook
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 '''synthesize structurally interesting change history
8 '''synthesize structurally interesting change history
9
9
10 This extension is useful for creating a repository with properties
10 This extension is useful for creating a repository with properties
11 that are statistically similar to an existing repository. During
11 that are statistically similar to an existing repository. During
12 analysis, a simple probability table is constructed from the history
12 analysis, a simple probability table is constructed from the history
13 of an existing repository. During synthesis, these properties are
13 of an existing repository. During synthesis, these properties are
14 reconstructed.
14 reconstructed.
15
15
16 Properties that are analyzed and synthesized include the following:
16 Properties that are analyzed and synthesized include the following:
17
17
18 - Lines added or removed when an existing file is modified
18 - Lines added or removed when an existing file is modified
19 - Number and sizes of files added
19 - Number and sizes of files added
20 - Number of files removed
20 - Number of files removed
21 - Line lengths
21 - Line lengths
22 - Topological distance to parent changeset(s)
22 - Topological distance to parent changeset(s)
23 - Probability of a commit being a merge
23 - Probability of a commit being a merge
24 - Probability of a newly added file being added to a new directory
24 - Probability of a newly added file being added to a new directory
25 - Interarrival time, and time zone, of commits
25 - Interarrival time, and time zone, of commits
26
26
27 A few obvious properties that are not currently handled realistically:
27 A few obvious properties that are not currently handled realistically:
28
28
29 - Merges are treated as regular commits with two parents, which is not
29 - Merges are treated as regular commits with two parents, which is not
30 realistic
30 realistic
31 - Modifications are not treated as operations on hunks of lines, but
31 - Modifications are not treated as operations on hunks of lines, but
32 as insertions and deletions of randomly chosen single lines
32 as insertions and deletions of randomly chosen single lines
33 - Committer ID (always random)
33 - Committer ID (always random)
34 - Executability of files
34 - Executability of files
35 - Symlinks and binary files are ignored
35 - Symlinks and binary files are ignored
36 '''
36 '''
37
37
38 import bisect, collections, json, os, random, time
38 import bisect, collections, json, os, random, time
39 from mercurial import cmdutil, context, patch, scmutil, url, util
39 from mercurial import cmdutil, context, patch, scmutil, url, util, hg
40 from mercurial.i18n import _
40 from mercurial.i18n import _
41 from mercurial.node import nullrev, nullid
41 from mercurial.node import nullrev, nullid
42
42
43 testedwith = 'internal'
43 testedwith = 'internal'
44
44
45 cmdtable = {}
45 cmdtable = {}
46 command = cmdutil.command(cmdtable)
46 command = cmdutil.command(cmdtable)
47
47
48 newfile = set(('new fi', 'rename', 'copy f', 'copy t'))
48 newfile = set(('new fi', 'rename', 'copy f', 'copy t'))
49
49
50 def zerodict():
50 def zerodict():
51 return collections.defaultdict(lambda: 0)
51 return collections.defaultdict(lambda: 0)
52
52
53 def roundto(x, k):
53 def roundto(x, k):
54 if x > k * 2:
54 if x > k * 2:
55 return int(round(x / float(k)) * k)
55 return int(round(x / float(k)) * k)
56 return int(round(x))
56 return int(round(x))
57
57
58 def parsegitdiff(lines):
58 def parsegitdiff(lines):
59 filename, mar, lineadd, lineremove = None, None, zerodict(), 0
59 filename, mar, lineadd, lineremove = None, None, zerodict(), 0
60 binary = False
60 binary = False
61 for line in lines:
61 for line in lines:
62 start = line[:6]
62 start = line[:6]
63 if start == 'diff -':
63 if start == 'diff -':
64 if filename:
64 if filename:
65 yield filename, mar, lineadd, lineremove, binary
65 yield filename, mar, lineadd, lineremove, binary
66 mar, lineadd, lineremove, binary = 'm', zerodict(), 0, False
66 mar, lineadd, lineremove, binary = 'm', zerodict(), 0, False
67 filename = patch.gitre.match(line).group(1)
67 filename = patch.gitre.match(line).group(1)
68 elif start in newfile:
68 elif start in newfile:
69 mar = 'a'
69 mar = 'a'
70 elif start == 'GIT bi':
70 elif start == 'GIT bi':
71 binary = True
71 binary = True
72 elif start == 'delete':
72 elif start == 'delete':
73 mar = 'r'
73 mar = 'r'
74 elif start:
74 elif start:
75 s = start[0]
75 s = start[0]
76 if s == '-' and not line.startswith('--- '):
76 if s == '-' and not line.startswith('--- '):
77 lineremove += 1
77 lineremove += 1
78 elif s == '+' and not line.startswith('+++ '):
78 elif s == '+' and not line.startswith('+++ '):
79 lineadd[roundto(len(line) - 1, 5)] += 1
79 lineadd[roundto(len(line) - 1, 5)] += 1
80 if filename:
80 if filename:
81 yield filename, mar, lineadd, lineremove, binary
81 yield filename, mar, lineadd, lineremove, binary
82
82
83 @command('analyze',
83 @command('analyze',
84 [('o', 'output', [], _('write output to given file'), _('FILE')),
84 [('o', 'output', [], _('write output to given file'), _('FILE')),
85 ('r', 'rev', [], _('analyze specified revisions'), _('REV'))],
85 ('r', 'rev', [], _('analyze specified revisions'), _('REV'))],
86 _('hg analyze'))
86 _('hg analyze'))
87 def analyze(ui, repo, *revs, **opts):
87 def analyze(ui, repo, *revs, **opts):
88 '''create a simple model of a repository to use for later synthesis
88 '''create a simple model of a repository to use for later synthesis
89
89
90 This command examines every changeset in the given range (or all
90 This command examines every changeset in the given range (or all
91 of history if none are specified) and creates a simple statistical
91 of history if none are specified) and creates a simple statistical
92 model of the history of the repository.
92 model of the history of the repository.
93
93
94 The model is written out to a JSON file, and can be used by
94 The model is written out to a JSON file, and can be used by
95 :hg:`synthesize` to create or augment a repository with synthetic
95 :hg:`synthesize` to create or augment a repository with synthetic
96 commits that have a structure that is statistically similar to the
96 commits that have a structure that is statistically similar to the
97 analyzed repository.
97 analyzed repository.
98 '''
98 '''
99
99
100 revs = list(revs)
100 revs = list(revs)
101 revs.extend(opts['rev'])
101 revs.extend(opts['rev'])
102 if not revs:
102 if not revs:
103 revs = [':']
103 revs = [':']
104
104
105 output = opts['output']
105 output = opts['output']
106 if not output:
106 if not output:
107 output = os.path.basename(repo.root) + '.json'
107 output = os.path.basename(repo.root) + '.json'
108
108
109 if output == '-':
109 if output == '-':
110 fp = sys.stdout
110 fp = sys.stdout
111 else:
111 else:
112 fp = open(output, 'w')
112 fp = open(output, 'w')
113
113
114 revs = scmutil.revrange(repo, revs)
114 revs = scmutil.revrange(repo, revs)
115 revs.sort()
115 revs.sort()
116
116
117 lineschanged = zerodict()
117 lineschanged = zerodict()
118 children = zerodict()
118 children = zerodict()
119 p1distance = zerodict()
119 p1distance = zerodict()
120 p2distance = zerodict()
120 p2distance = zerodict()
121 linesinfilesadded = zerodict()
121 linesinfilesadded = zerodict()
122 fileschanged = zerodict()
122 fileschanged = zerodict()
123 filesadded = zerodict()
123 filesadded = zerodict()
124 filesremoved = zerodict()
124 filesremoved = zerodict()
125 linelengths = zerodict()
125 linelengths = zerodict()
126 interarrival = zerodict()
126 interarrival = zerodict()
127 parents = zerodict()
127 parents = zerodict()
128 dirsadded = zerodict()
128 dirsadded = zerodict()
129 tzoffset = zerodict()
129 tzoffset = zerodict()
130
130
131 progress = ui.progress
131 progress = ui.progress
132 _analyzing = _('analyzing')
132 _analyzing = _('analyzing')
133 _changesets = _('changesets')
133 _changesets = _('changesets')
134 _total = len(revs)
134 _total = len(revs)
135
135
136 for i, rev in enumerate(revs):
136 for i, rev in enumerate(revs):
137 progress(_analyzing, i, unit=_changesets, total=_total)
137 progress(_analyzing, i, unit=_changesets, total=_total)
138 ctx = repo[rev]
138 ctx = repo[rev]
139 pl = ctx.parents()
139 pl = ctx.parents()
140 pctx = pl[0]
140 pctx = pl[0]
141 prev = pctx.rev()
141 prev = pctx.rev()
142 children[prev] += 1
142 children[prev] += 1
143 p1distance[rev - prev] += 1
143 p1distance[rev - prev] += 1
144 parents[len(pl)] += 1
144 parents[len(pl)] += 1
145 tzoffset[ctx.date()[1]] += 1
145 tzoffset[ctx.date()[1]] += 1
146 if len(pl) > 1:
146 if len(pl) > 1:
147 p2distance[rev - pl[1].rev()] += 1
147 p2distance[rev - pl[1].rev()] += 1
148 if prev == rev - 1:
148 if prev == rev - 1:
149 lastctx = pctx
149 lastctx = pctx
150 else:
150 else:
151 lastctx = repo[rev - 1]
151 lastctx = repo[rev - 1]
152 if lastctx.rev() != nullrev:
152 if lastctx.rev() != nullrev:
153 interarrival[roundto(ctx.date()[0] - lastctx.date()[0], 300)] += 1
153 interarrival[roundto(ctx.date()[0] - lastctx.date()[0], 300)] += 1
154 diff = sum((d.splitlines()
154 diff = sum((d.splitlines()
155 for d in ctx.diff(pctx, opts=dict(git=True))), [])
155 for d in ctx.diff(pctx, opts=dict(git=True))), [])
156 fileadds, diradds, fileremoves, filechanges = 0, 0, 0, 0
156 fileadds, diradds, fileremoves, filechanges = 0, 0, 0, 0
157 for filename, mar, lineadd, lineremove, binary in parsegitdiff(diff):
157 for filename, mar, lineadd, lineremove, binary in parsegitdiff(diff):
158 if binary:
158 if binary:
159 continue
159 continue
160 added = sum(lineadd.itervalues(), 0)
160 added = sum(lineadd.itervalues(), 0)
161 if mar == 'm':
161 if mar == 'm':
162 if added and lineremove:
162 if added and lineremove:
163 lineschanged[roundto(added, 5), roundto(lineremove, 5)] += 1
163 lineschanged[roundto(added, 5), roundto(lineremove, 5)] += 1
164 filechanges += 1
164 filechanges += 1
165 elif mar == 'a':
165 elif mar == 'a':
166 fileadds += 1
166 fileadds += 1
167 if '/' in filename:
167 if '/' in filename:
168 filedir = filename.rsplit('/', 1)[0]
168 filedir = filename.rsplit('/', 1)[0]
169 if filedir not in pctx.dirs():
169 if filedir not in pctx.dirs():
170 diradds += 1
170 diradds += 1
171 linesinfilesadded[roundto(added, 5)] += 1
171 linesinfilesadded[roundto(added, 5)] += 1
172 elif mar == 'r':
172 elif mar == 'r':
173 fileremoves += 1
173 fileremoves += 1
174 for length, count in lineadd.iteritems():
174 for length, count in lineadd.iteritems():
175 linelengths[length] += count
175 linelengths[length] += count
176 fileschanged[filechanges] += 1
176 fileschanged[filechanges] += 1
177 filesadded[fileadds] += 1
177 filesadded[fileadds] += 1
178 dirsadded[diradds] += 1
178 dirsadded[diradds] += 1
179 filesremoved[fileremoves] += 1
179 filesremoved[fileremoves] += 1
180
180
181 invchildren = zerodict()
181 invchildren = zerodict()
182
182
183 for rev, count in children.iteritems():
183 for rev, count in children.iteritems():
184 invchildren[count] += 1
184 invchildren[count] += 1
185
185
186 if output != '-':
186 if output != '-':
187 ui.status(_('writing output to %s\n') % output)
187 ui.status(_('writing output to %s\n') % output)
188
188
189 def pronk(d):
189 def pronk(d):
190 return sorted(d.iteritems(), key=lambda x: x[1], reverse=True)
190 return sorted(d.iteritems(), key=lambda x: x[1], reverse=True)
191
191
192 json.dump(dict(revs=len(revs),
192 json.dump(dict(revs=len(revs),
193 lineschanged=pronk(lineschanged),
193 lineschanged=pronk(lineschanged),
194 children=pronk(invchildren),
194 children=pronk(invchildren),
195 fileschanged=pronk(fileschanged),
195 fileschanged=pronk(fileschanged),
196 filesadded=pronk(filesadded),
196 filesadded=pronk(filesadded),
197 linesinfilesadded=pronk(linesinfilesadded),
197 linesinfilesadded=pronk(linesinfilesadded),
198 dirsadded=pronk(dirsadded),
198 dirsadded=pronk(dirsadded),
199 filesremoved=pronk(filesremoved),
199 filesremoved=pronk(filesremoved),
200 linelengths=pronk(linelengths),
200 linelengths=pronk(linelengths),
201 parents=pronk(parents),
201 parents=pronk(parents),
202 p1distance=pronk(p1distance),
202 p1distance=pronk(p1distance),
203 p2distance=pronk(p2distance),
203 p2distance=pronk(p2distance),
204 interarrival=pronk(interarrival),
204 interarrival=pronk(interarrival),
205 tzoffset=pronk(tzoffset),
205 tzoffset=pronk(tzoffset),
206 ),
206 ),
207 fp)
207 fp)
208 fp.close()
208 fp.close()
209
209
210 @command('synthesize',
210 @command('synthesize',
211 [('c', 'count', 0, _('create given number of commits'), _('COUNT')),
211 [('c', 'count', 0, _('create given number of commits'), _('COUNT')),
212 ('', 'dict', '', _('path to a dictionary of words'), _('FILE'))],
212 ('', 'dict', '', _('path to a dictionary of words'), _('FILE'))],
213 _('hg synthesize [OPTION].. DESCFILE'))
213 _('hg synthesize [OPTION].. DESCFILE'))
214 def synthesize(ui, repo, descpath, **opts):
214 def synthesize(ui, repo, descpath, **opts):
215 '''synthesize commits based on a model of an existing repository
215 '''synthesize commits based on a model of an existing repository
216
216
217 The model must have been generated by :hg:`analyze`. Commits will
217 The model must have been generated by :hg:`analyze`. Commits will
218 be generated randomly according to the probabilities described in
218 be generated randomly according to the probabilities described in
219 the model.
219 the model.
220
220
221 When synthesizing new content, commit descriptions, and user
221 When synthesizing new content, commit descriptions, and user
222 names, words will be chosen randomly from a dictionary that is
222 names, words will be chosen randomly from a dictionary that is
223 presumed to contain one word per line. Use --dict to specify the
223 presumed to contain one word per line. Use --dict to specify the
224 path to an alternate dictionary to use.
224 path to an alternate dictionary to use.
225 '''
225 '''
226 try:
226 try:
227 fp = url.open(ui, descpath)
227 fp = hg.openpath(ui, descpath)
228 except Exception, err:
228 except Exception, err:
229 raise util.Abort('%s: %s' % (descpath, err[0].strerror))
229 raise util.Abort('%s: %s' % (descpath, err[0].strerror))
230 desc = json.load(fp)
230 desc = json.load(fp)
231 fp.close()
231 fp.close()
232
232
233 def cdf(l):
233 def cdf(l):
234 vals, probs = zip(*sorted(l, key=lambda x: x[1], reverse=True))
234 vals, probs = zip(*sorted(l, key=lambda x: x[1], reverse=True))
235 t = float(sum(probs, 0))
235 t = float(sum(probs, 0))
236 s, cdfs = 0, []
236 s, cdfs = 0, []
237 for v in probs:
237 for v in probs:
238 s += v
238 s += v
239 cdfs.append(s / t)
239 cdfs.append(s / t)
240 return vals, cdfs
240 return vals, cdfs
241
241
242 lineschanged = cdf(desc['lineschanged'])
242 lineschanged = cdf(desc['lineschanged'])
243 fileschanged = cdf(desc['fileschanged'])
243 fileschanged = cdf(desc['fileschanged'])
244 filesadded = cdf(desc['filesadded'])
244 filesadded = cdf(desc['filesadded'])
245 dirsadded = cdf(desc['dirsadded'])
245 dirsadded = cdf(desc['dirsadded'])
246 filesremoved = cdf(desc['filesremoved'])
246 filesremoved = cdf(desc['filesremoved'])
247 linelengths = cdf(desc['linelengths'])
247 linelengths = cdf(desc['linelengths'])
248 parents = cdf(desc['parents'])
248 parents = cdf(desc['parents'])
249 p1distance = cdf(desc['p1distance'])
249 p1distance = cdf(desc['p1distance'])
250 p2distance = cdf(desc['p2distance'])
250 p2distance = cdf(desc['p2distance'])
251 interarrival = cdf(desc['interarrival'])
251 interarrival = cdf(desc['interarrival'])
252 linesinfilesadded = cdf(desc['linesinfilesadded'])
252 linesinfilesadded = cdf(desc['linesinfilesadded'])
253 tzoffset = cdf(desc['tzoffset'])
253 tzoffset = cdf(desc['tzoffset'])
254
254
255 dictfile = opts.get('dict') or '/usr/share/dict/words'
255 dictfile = opts.get('dict') or '/usr/share/dict/words'
256 try:
256 try:
257 fp = open(dictfile, 'rU')
257 fp = open(dictfile, 'rU')
258 except IOError, err:
258 except IOError, err:
259 raise util.Abort('%s: %s' % (dictfile, err.strerror))
259 raise util.Abort('%s: %s' % (dictfile, err.strerror))
260 words = fp.read().splitlines()
260 words = fp.read().splitlines()
261 fp.close()
261 fp.close()
262
262
263 def pick(cdf):
263 def pick(cdf):
264 return cdf[0][bisect.bisect_left(cdf[1], random.random())]
264 return cdf[0][bisect.bisect_left(cdf[1], random.random())]
265
265
266 def makeline(minimum=0):
266 def makeline(minimum=0):
267 total = max(minimum, pick(linelengths))
267 total = max(minimum, pick(linelengths))
268 c, l = 0, []
268 c, l = 0, []
269 while c < total:
269 while c < total:
270 w = random.choice(words)
270 w = random.choice(words)
271 c += len(w) + 1
271 c += len(w) + 1
272 l.append(w)
272 l.append(w)
273 return ' '.join(l)
273 return ' '.join(l)
274
274
275 wlock = repo.wlock()
275 wlock = repo.wlock()
276 lock = repo.lock()
276 lock = repo.lock()
277
277
278 nevertouch = set(('.hgsub', '.hgignore', '.hgtags'))
278 nevertouch = set(('.hgsub', '.hgignore', '.hgtags'))
279
279
280 progress = ui.progress
280 progress = ui.progress
281 _synthesizing = _('synthesizing')
281 _synthesizing = _('synthesizing')
282 _changesets = _('changesets')
282 _changesets = _('changesets')
283
283
284 count = int(opts['count'])
284 count = int(opts['count'])
285 heads = set(map(repo.changelog.rev, repo.heads()))
285 heads = set(map(repo.changelog.rev, repo.heads()))
286 for i in xrange(count):
286 for i in xrange(count):
287 progress(_synthesizing, i, unit=_changesets, total=count)
287 progress(_synthesizing, i, unit=_changesets, total=count)
288
288
289 node = repo.changelog.node
289 node = repo.changelog.node
290 revs = len(repo)
290 revs = len(repo)
291
291
292 def pickhead(heads, distance):
292 def pickhead(heads, distance):
293 if heads:
293 if heads:
294 lheads = sorted(heads)
294 lheads = sorted(heads)
295 rev = revs - min(pick(distance), revs)
295 rev = revs - min(pick(distance), revs)
296 if rev < lheads[-1]:
296 if rev < lheads[-1]:
297 rev = lheads[bisect.bisect_left(lheads, rev)]
297 rev = lheads[bisect.bisect_left(lheads, rev)]
298 else:
298 else:
299 rev = lheads[-1]
299 rev = lheads[-1]
300 return rev, node(rev)
300 return rev, node(rev)
301 return nullrev, nullid
301 return nullrev, nullid
302
302
303 r1 = revs - min(pick(p1distance), revs)
303 r1 = revs - min(pick(p1distance), revs)
304 p1 = node(r1)
304 p1 = node(r1)
305
305
306 # the number of heads will grow without bound if we use a pure
306 # the number of heads will grow without bound if we use a pure
307 # model, so artificially constrain their proliferation
307 # model, so artificially constrain their proliferation
308 if pick(parents) == 2 or len(heads) > random.randint(1, 20):
308 if pick(parents) == 2 or len(heads) > random.randint(1, 20):
309 r2, p2 = pickhead(heads.difference([r1]), p2distance)
309 r2, p2 = pickhead(heads.difference([r1]), p2distance)
310 else:
310 else:
311 r2, p2 = nullrev, nullid
311 r2, p2 = nullrev, nullid
312
312
313 pl = [p1, p2]
313 pl = [p1, p2]
314 pctx = repo[r1]
314 pctx = repo[r1]
315 mf = pctx.manifest()
315 mf = pctx.manifest()
316 mfk = mf.keys()
316 mfk = mf.keys()
317 changes = {}
317 changes = {}
318 if mfk:
318 if mfk:
319 for __ in xrange(pick(fileschanged)):
319 for __ in xrange(pick(fileschanged)):
320 for __ in xrange(10):
320 for __ in xrange(10):
321 fctx = pctx.filectx(random.choice(mfk))
321 fctx = pctx.filectx(random.choice(mfk))
322 path = fctx.path()
322 path = fctx.path()
323 if not (path in nevertouch or fctx.isbinary() or
323 if not (path in nevertouch or fctx.isbinary() or
324 'l' in fctx.flags()):
324 'l' in fctx.flags()):
325 break
325 break
326 lines = fctx.data().splitlines()
326 lines = fctx.data().splitlines()
327 add, remove = pick(lineschanged)
327 add, remove = pick(lineschanged)
328 for __ in xrange(remove):
328 for __ in xrange(remove):
329 if not lines:
329 if not lines:
330 break
330 break
331 del lines[random.randrange(0, len(lines))]
331 del lines[random.randrange(0, len(lines))]
332 for __ in xrange(add):
332 for __ in xrange(add):
333 lines.insert(random.randint(0, len(lines)), makeline())
333 lines.insert(random.randint(0, len(lines)), makeline())
334 path = fctx.path()
334 path = fctx.path()
335 changes[path] = context.memfilectx(path,
335 changes[path] = context.memfilectx(path,
336 '\n'.join(lines) + '\n')
336 '\n'.join(lines) + '\n')
337 for __ in xrange(pick(filesremoved)):
337 for __ in xrange(pick(filesremoved)):
338 path = random.choice(mfk)
338 path = random.choice(mfk)
339 for __ in xrange(10):
339 for __ in xrange(10):
340 path = random.choice(mfk)
340 path = random.choice(mfk)
341 if path not in changes:
341 if path not in changes:
342 changes[path] = None
342 changes[path] = None
343 break
343 break
344 if filesadded:
344 if filesadded:
345 dirs = list(pctx.dirs())
345 dirs = list(pctx.dirs())
346 dirs.append('')
346 dirs.append('')
347 for __ in xrange(pick(filesadded)):
347 for __ in xrange(pick(filesadded)):
348 path = [random.choice(dirs)]
348 path = [random.choice(dirs)]
349 if pick(dirsadded):
349 if pick(dirsadded):
350 path.append(random.choice(words))
350 path.append(random.choice(words))
351 path.append(random.choice(words))
351 path.append(random.choice(words))
352 path = '/'.join(filter(None, path))
352 path = '/'.join(filter(None, path))
353 data = '\n'.join(makeline()
353 data = '\n'.join(makeline()
354 for __ in xrange(pick(linesinfilesadded))) + '\n'
354 for __ in xrange(pick(linesinfilesadded))) + '\n'
355 changes[path] = context.memfilectx(path, data)
355 changes[path] = context.memfilectx(path, data)
356 def filectxfn(repo, memctx, path):
356 def filectxfn(repo, memctx, path):
357 data = changes[path]
357 data = changes[path]
358 if data is None:
358 if data is None:
359 raise IOError
359 raise IOError
360 return data
360 return data
361 if not changes:
361 if not changes:
362 continue
362 continue
363 if revs:
363 if revs:
364 date = repo['tip'].date()[0] + pick(interarrival)
364 date = repo['tip'].date()[0] + pick(interarrival)
365 else:
365 else:
366 date = time.time() - (86400 * count)
366 date = time.time() - (86400 * count)
367 user = random.choice(words) + '@' + random.choice(words)
367 user = random.choice(words) + '@' + random.choice(words)
368 mc = context.memctx(repo, pl, makeline(minimum=2),
368 mc = context.memctx(repo, pl, makeline(minimum=2),
369 sorted(changes.iterkeys()),
369 sorted(changes.iterkeys()),
370 filectxfn, user, '%d %d' % (date, pick(tzoffset)))
370 filectxfn, user, '%d %d' % (date, pick(tzoffset)))
371 newnode = mc.commit()
371 newnode = mc.commit()
372 heads.add(repo.changelog.rev(newnode))
372 heads.add(repo.changelog.rev(newnode))
373 heads.discard(r1)
373 heads.discard(r1)
374 heads.discard(r2)
374 heads.discard(r2)
375
375
376 lock.release()
376 lock.release()
377 wlock.release()
377 wlock.release()
@@ -1,3606 +1,3606 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.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 '''manage a stack of patches
8 '''manage a stack of patches
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use :hg:`help command` for more details)::
17 Common tasks (use :hg:`help command` for more details)::
18
18
19 create new patch qnew
19 create new patch qnew
20 import existing patch qimport
20 import existing patch qimport
21
21
22 print patch series qseries
22 print patch series qseries
23 print applied patches qapplied
23 print applied patches qapplied
24
24
25 add known patch to applied stack qpush
25 add known patch to applied stack qpush
26 remove patch from applied stack qpop
26 remove patch from applied stack qpop
27 refresh contents of top applied patch qrefresh
27 refresh contents of top applied patch qrefresh
28
28
29 By default, mq will automatically use git patches when required to
29 By default, mq will automatically use git patches when required to
30 avoid losing file mode changes, copy records, binary files or empty
30 avoid losing file mode changes, copy records, binary files or empty
31 files creations or deletions. This behaviour can be configured with::
31 files creations or deletions. This behaviour can be configured with::
32
32
33 [mq]
33 [mq]
34 git = auto/keep/yes/no
34 git = auto/keep/yes/no
35
35
36 If set to 'keep', mq will obey the [diff] section configuration while
36 If set to 'keep', mq will obey the [diff] section configuration while
37 preserving existing git patches upon qrefresh. If set to 'yes' or
37 preserving existing git patches upon qrefresh. If set to 'yes' or
38 'no', mq will override the [diff] section and always generate git or
38 'no', mq will override the [diff] section and always generate git or
39 regular patches, possibly losing data in the second case.
39 regular patches, possibly losing data in the second case.
40
40
41 It may be desirable for mq changesets to be kept in the secret phase (see
41 It may be desirable for mq changesets to be kept in the secret phase (see
42 :hg:`help phases`), which can be enabled with the following setting::
42 :hg:`help phases`), which can be enabled with the following setting::
43
43
44 [mq]
44 [mq]
45 secret = True
45 secret = True
46
46
47 You will by default be managing a patch queue named "patches". You can
47 You will by default be managing a patch queue named "patches". You can
48 create other, independent patch queues with the :hg:`qqueue` command.
48 create other, independent patch queues with the :hg:`qqueue` command.
49
49
50 If the working directory contains uncommitted files, qpush, qpop and
50 If the working directory contains uncommitted files, qpush, qpop and
51 qgoto abort immediately. If -f/--force is used, the changes are
51 qgoto abort immediately. If -f/--force is used, the changes are
52 discarded. Setting::
52 discarded. Setting::
53
53
54 [mq]
54 [mq]
55 keepchanges = True
55 keepchanges = True
56
56
57 make them behave as if --keep-changes were passed, and non-conflicting
57 make them behave as if --keep-changes were passed, and non-conflicting
58 local changes will be tolerated and preserved. If incompatible options
58 local changes will be tolerated and preserved. If incompatible options
59 such as -f/--force or --exact are passed, this setting is ignored.
59 such as -f/--force or --exact are passed, this setting is ignored.
60 '''
60 '''
61
61
62 from mercurial.i18n import _
62 from mercurial.i18n import _
63 from mercurial.node import bin, hex, short, nullid, nullrev
63 from mercurial.node import bin, hex, short, nullid, nullrev
64 from mercurial.lock import release
64 from mercurial.lock import release
65 from mercurial import commands, cmdutil, hg, scmutil, util, revset
65 from mercurial import commands, cmdutil, hg, scmutil, util, revset
66 from mercurial import repair, extensions, url, error, phases, bookmarks
66 from mercurial import repair, extensions, error, phases, bookmarks
67 from mercurial import patch as patchmod
67 from mercurial import patch as patchmod
68 import os, re, errno, shutil
68 import os, re, errno, shutil
69
69
70 commands.norepo += " qclone"
70 commands.norepo += " qclone"
71
71
72 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
72 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
73
73
74 cmdtable = {}
74 cmdtable = {}
75 command = cmdutil.command(cmdtable)
75 command = cmdutil.command(cmdtable)
76 testedwith = 'internal'
76 testedwith = 'internal'
77
77
78 # Patch names looks like unix-file names.
78 # Patch names looks like unix-file names.
79 # They must be joinable with queue directory and result in the patch path.
79 # They must be joinable with queue directory and result in the patch path.
80 normname = util.normpath
80 normname = util.normpath
81
81
82 class statusentry(object):
82 class statusentry(object):
83 def __init__(self, node, name):
83 def __init__(self, node, name):
84 self.node, self.name = node, name
84 self.node, self.name = node, name
85 def __repr__(self):
85 def __repr__(self):
86 return hex(self.node) + ':' + self.name
86 return hex(self.node) + ':' + self.name
87
87
88 class patchheader(object):
88 class patchheader(object):
89 def __init__(self, pf, plainmode=False):
89 def __init__(self, pf, plainmode=False):
90 def eatdiff(lines):
90 def eatdiff(lines):
91 while lines:
91 while lines:
92 l = lines[-1]
92 l = lines[-1]
93 if (l.startswith("diff -") or
93 if (l.startswith("diff -") or
94 l.startswith("Index:") or
94 l.startswith("Index:") or
95 l.startswith("===========")):
95 l.startswith("===========")):
96 del lines[-1]
96 del lines[-1]
97 else:
97 else:
98 break
98 break
99 def eatempty(lines):
99 def eatempty(lines):
100 while lines:
100 while lines:
101 if not lines[-1].strip():
101 if not lines[-1].strip():
102 del lines[-1]
102 del lines[-1]
103 else:
103 else:
104 break
104 break
105
105
106 message = []
106 message = []
107 comments = []
107 comments = []
108 user = None
108 user = None
109 date = None
109 date = None
110 parent = None
110 parent = None
111 format = None
111 format = None
112 subject = None
112 subject = None
113 branch = None
113 branch = None
114 nodeid = None
114 nodeid = None
115 diffstart = 0
115 diffstart = 0
116
116
117 for line in file(pf):
117 for line in file(pf):
118 line = line.rstrip()
118 line = line.rstrip()
119 if (line.startswith('diff --git')
119 if (line.startswith('diff --git')
120 or (diffstart and line.startswith('+++ '))):
120 or (diffstart and line.startswith('+++ '))):
121 diffstart = 2
121 diffstart = 2
122 break
122 break
123 diffstart = 0 # reset
123 diffstart = 0 # reset
124 if line.startswith("--- "):
124 if line.startswith("--- "):
125 diffstart = 1
125 diffstart = 1
126 continue
126 continue
127 elif format == "hgpatch":
127 elif format == "hgpatch":
128 # parse values when importing the result of an hg export
128 # parse values when importing the result of an hg export
129 if line.startswith("# User "):
129 if line.startswith("# User "):
130 user = line[7:]
130 user = line[7:]
131 elif line.startswith("# Date "):
131 elif line.startswith("# Date "):
132 date = line[7:]
132 date = line[7:]
133 elif line.startswith("# Parent "):
133 elif line.startswith("# Parent "):
134 parent = line[9:].lstrip()
134 parent = line[9:].lstrip()
135 elif line.startswith("# Branch "):
135 elif line.startswith("# Branch "):
136 branch = line[9:]
136 branch = line[9:]
137 elif line.startswith("# Node ID "):
137 elif line.startswith("# Node ID "):
138 nodeid = line[10:]
138 nodeid = line[10:]
139 elif not line.startswith("# ") and line:
139 elif not line.startswith("# ") and line:
140 message.append(line)
140 message.append(line)
141 format = None
141 format = None
142 elif line == '# HG changeset patch':
142 elif line == '# HG changeset patch':
143 message = []
143 message = []
144 format = "hgpatch"
144 format = "hgpatch"
145 elif (format != "tagdone" and (line.startswith("Subject: ") or
145 elif (format != "tagdone" and (line.startswith("Subject: ") or
146 line.startswith("subject: "))):
146 line.startswith("subject: "))):
147 subject = line[9:]
147 subject = line[9:]
148 format = "tag"
148 format = "tag"
149 elif (format != "tagdone" and (line.startswith("From: ") or
149 elif (format != "tagdone" and (line.startswith("From: ") or
150 line.startswith("from: "))):
150 line.startswith("from: "))):
151 user = line[6:]
151 user = line[6:]
152 format = "tag"
152 format = "tag"
153 elif (format != "tagdone" and (line.startswith("Date: ") or
153 elif (format != "tagdone" and (line.startswith("Date: ") or
154 line.startswith("date: "))):
154 line.startswith("date: "))):
155 date = line[6:]
155 date = line[6:]
156 format = "tag"
156 format = "tag"
157 elif format == "tag" and line == "":
157 elif format == "tag" and line == "":
158 # when looking for tags (subject: from: etc) they
158 # when looking for tags (subject: from: etc) they
159 # end once you find a blank line in the source
159 # end once you find a blank line in the source
160 format = "tagdone"
160 format = "tagdone"
161 elif message or line:
161 elif message or line:
162 message.append(line)
162 message.append(line)
163 comments.append(line)
163 comments.append(line)
164
164
165 eatdiff(message)
165 eatdiff(message)
166 eatdiff(comments)
166 eatdiff(comments)
167 # Remember the exact starting line of the patch diffs before consuming
167 # Remember the exact starting line of the patch diffs before consuming
168 # empty lines, for external use by TortoiseHg and others
168 # empty lines, for external use by TortoiseHg and others
169 self.diffstartline = len(comments)
169 self.diffstartline = len(comments)
170 eatempty(message)
170 eatempty(message)
171 eatempty(comments)
171 eatempty(comments)
172
172
173 # make sure message isn't empty
173 # make sure message isn't empty
174 if format and format.startswith("tag") and subject:
174 if format and format.startswith("tag") and subject:
175 message.insert(0, "")
175 message.insert(0, "")
176 message.insert(0, subject)
176 message.insert(0, subject)
177
177
178 self.message = message
178 self.message = message
179 self.comments = comments
179 self.comments = comments
180 self.user = user
180 self.user = user
181 self.date = date
181 self.date = date
182 self.parent = parent
182 self.parent = parent
183 # nodeid and branch are for external use by TortoiseHg and others
183 # nodeid and branch are for external use by TortoiseHg and others
184 self.nodeid = nodeid
184 self.nodeid = nodeid
185 self.branch = branch
185 self.branch = branch
186 self.haspatch = diffstart > 1
186 self.haspatch = diffstart > 1
187 self.plainmode = plainmode
187 self.plainmode = plainmode
188
188
189 def setuser(self, user):
189 def setuser(self, user):
190 if not self.updateheader(['From: ', '# User '], user):
190 if not self.updateheader(['From: ', '# User '], user):
191 try:
191 try:
192 patchheaderat = self.comments.index('# HG changeset patch')
192 patchheaderat = self.comments.index('# HG changeset patch')
193 self.comments.insert(patchheaderat + 1, '# User ' + user)
193 self.comments.insert(patchheaderat + 1, '# User ' + user)
194 except ValueError:
194 except ValueError:
195 if self.plainmode or self._hasheader(['Date: ']):
195 if self.plainmode or self._hasheader(['Date: ']):
196 self.comments = ['From: ' + user] + self.comments
196 self.comments = ['From: ' + user] + self.comments
197 else:
197 else:
198 tmp = ['# HG changeset patch', '# User ' + user, '']
198 tmp = ['# HG changeset patch', '# User ' + user, '']
199 self.comments = tmp + self.comments
199 self.comments = tmp + self.comments
200 self.user = user
200 self.user = user
201
201
202 def setdate(self, date):
202 def setdate(self, date):
203 if not self.updateheader(['Date: ', '# Date '], date):
203 if not self.updateheader(['Date: ', '# Date '], date):
204 try:
204 try:
205 patchheaderat = self.comments.index('# HG changeset patch')
205 patchheaderat = self.comments.index('# HG changeset patch')
206 self.comments.insert(patchheaderat + 1, '# Date ' + date)
206 self.comments.insert(patchheaderat + 1, '# Date ' + date)
207 except ValueError:
207 except ValueError:
208 if self.plainmode or self._hasheader(['From: ']):
208 if self.plainmode or self._hasheader(['From: ']):
209 self.comments = ['Date: ' + date] + self.comments
209 self.comments = ['Date: ' + date] + self.comments
210 else:
210 else:
211 tmp = ['# HG changeset patch', '# Date ' + date, '']
211 tmp = ['# HG changeset patch', '# Date ' + date, '']
212 self.comments = tmp + self.comments
212 self.comments = tmp + self.comments
213 self.date = date
213 self.date = date
214
214
215 def setparent(self, parent):
215 def setparent(self, parent):
216 if not self.updateheader(['# Parent '], parent):
216 if not self.updateheader(['# Parent '], parent):
217 try:
217 try:
218 patchheaderat = self.comments.index('# HG changeset patch')
218 patchheaderat = self.comments.index('# HG changeset patch')
219 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
219 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
220 except ValueError:
220 except ValueError:
221 pass
221 pass
222 self.parent = parent
222 self.parent = parent
223
223
224 def setmessage(self, message):
224 def setmessage(self, message):
225 if self.comments:
225 if self.comments:
226 self._delmsg()
226 self._delmsg()
227 self.message = [message]
227 self.message = [message]
228 self.comments += self.message
228 self.comments += self.message
229
229
230 def updateheader(self, prefixes, new):
230 def updateheader(self, prefixes, new):
231 '''Update all references to a field in the patch header.
231 '''Update all references to a field in the patch header.
232 Return whether the field is present.'''
232 Return whether the field is present.'''
233 res = False
233 res = False
234 for prefix in prefixes:
234 for prefix in prefixes:
235 for i in xrange(len(self.comments)):
235 for i in xrange(len(self.comments)):
236 if self.comments[i].startswith(prefix):
236 if self.comments[i].startswith(prefix):
237 self.comments[i] = prefix + new
237 self.comments[i] = prefix + new
238 res = True
238 res = True
239 break
239 break
240 return res
240 return res
241
241
242 def _hasheader(self, prefixes):
242 def _hasheader(self, prefixes):
243 '''Check if a header starts with any of the given prefixes.'''
243 '''Check if a header starts with any of the given prefixes.'''
244 for prefix in prefixes:
244 for prefix in prefixes:
245 for comment in self.comments:
245 for comment in self.comments:
246 if comment.startswith(prefix):
246 if comment.startswith(prefix):
247 return True
247 return True
248 return False
248 return False
249
249
250 def __str__(self):
250 def __str__(self):
251 if not self.comments:
251 if not self.comments:
252 return ''
252 return ''
253 return '\n'.join(self.comments) + '\n\n'
253 return '\n'.join(self.comments) + '\n\n'
254
254
255 def _delmsg(self):
255 def _delmsg(self):
256 '''Remove existing message, keeping the rest of the comments fields.
256 '''Remove existing message, keeping the rest of the comments fields.
257 If comments contains 'subject: ', message will prepend
257 If comments contains 'subject: ', message will prepend
258 the field and a blank line.'''
258 the field and a blank line.'''
259 if self.message:
259 if self.message:
260 subj = 'subject: ' + self.message[0].lower()
260 subj = 'subject: ' + self.message[0].lower()
261 for i in xrange(len(self.comments)):
261 for i in xrange(len(self.comments)):
262 if subj == self.comments[i].lower():
262 if subj == self.comments[i].lower():
263 del self.comments[i]
263 del self.comments[i]
264 self.message = self.message[2:]
264 self.message = self.message[2:]
265 break
265 break
266 ci = 0
266 ci = 0
267 for mi in self.message:
267 for mi in self.message:
268 while mi != self.comments[ci]:
268 while mi != self.comments[ci]:
269 ci += 1
269 ci += 1
270 del self.comments[ci]
270 del self.comments[ci]
271
271
272 def newcommit(repo, phase, *args, **kwargs):
272 def newcommit(repo, phase, *args, **kwargs):
273 """helper dedicated to ensure a commit respect mq.secret setting
273 """helper dedicated to ensure a commit respect mq.secret setting
274
274
275 It should be used instead of repo.commit inside the mq source for operation
275 It should be used instead of repo.commit inside the mq source for operation
276 creating new changeset.
276 creating new changeset.
277 """
277 """
278 if phase is None:
278 if phase is None:
279 if repo.ui.configbool('mq', 'secret', False):
279 if repo.ui.configbool('mq', 'secret', False):
280 phase = phases.secret
280 phase = phases.secret
281 if phase is not None:
281 if phase is not None:
282 backup = repo.ui.backupconfig('phases', 'new-commit')
282 backup = repo.ui.backupconfig('phases', 'new-commit')
283 # Marking the repository as committing an mq patch can be used
283 # Marking the repository as committing an mq patch can be used
284 # to optimize operations like _branchtags().
284 # to optimize operations like _branchtags().
285 repo._committingpatch = True
285 repo._committingpatch = True
286 try:
286 try:
287 if phase is not None:
287 if phase is not None:
288 repo.ui.setconfig('phases', 'new-commit', phase)
288 repo.ui.setconfig('phases', 'new-commit', phase)
289 return repo.commit(*args, **kwargs)
289 return repo.commit(*args, **kwargs)
290 finally:
290 finally:
291 repo._committingpatch = False
291 repo._committingpatch = False
292 if phase is not None:
292 if phase is not None:
293 repo.ui.restoreconfig(backup)
293 repo.ui.restoreconfig(backup)
294
294
295 class AbortNoCleanup(error.Abort):
295 class AbortNoCleanup(error.Abort):
296 pass
296 pass
297
297
298 class queue(object):
298 class queue(object):
299 def __init__(self, ui, path, patchdir=None):
299 def __init__(self, ui, path, patchdir=None):
300 self.basepath = path
300 self.basepath = path
301 try:
301 try:
302 fh = open(os.path.join(path, 'patches.queue'))
302 fh = open(os.path.join(path, 'patches.queue'))
303 cur = fh.read().rstrip()
303 cur = fh.read().rstrip()
304 fh.close()
304 fh.close()
305 if not cur:
305 if not cur:
306 curpath = os.path.join(path, 'patches')
306 curpath = os.path.join(path, 'patches')
307 else:
307 else:
308 curpath = os.path.join(path, 'patches-' + cur)
308 curpath = os.path.join(path, 'patches-' + cur)
309 except IOError:
309 except IOError:
310 curpath = os.path.join(path, 'patches')
310 curpath = os.path.join(path, 'patches')
311 self.path = patchdir or curpath
311 self.path = patchdir or curpath
312 self.opener = scmutil.opener(self.path)
312 self.opener = scmutil.opener(self.path)
313 self.ui = ui
313 self.ui = ui
314 self.applieddirty = False
314 self.applieddirty = False
315 self.seriesdirty = False
315 self.seriesdirty = False
316 self.added = []
316 self.added = []
317 self.seriespath = "series"
317 self.seriespath = "series"
318 self.statuspath = "status"
318 self.statuspath = "status"
319 self.guardspath = "guards"
319 self.guardspath = "guards"
320 self.activeguards = None
320 self.activeguards = None
321 self.guardsdirty = False
321 self.guardsdirty = False
322 # Handle mq.git as a bool with extended values
322 # Handle mq.git as a bool with extended values
323 try:
323 try:
324 gitmode = ui.configbool('mq', 'git', None)
324 gitmode = ui.configbool('mq', 'git', None)
325 if gitmode is None:
325 if gitmode is None:
326 raise error.ConfigError
326 raise error.ConfigError
327 self.gitmode = gitmode and 'yes' or 'no'
327 self.gitmode = gitmode and 'yes' or 'no'
328 except error.ConfigError:
328 except error.ConfigError:
329 self.gitmode = ui.config('mq', 'git', 'auto').lower()
329 self.gitmode = ui.config('mq', 'git', 'auto').lower()
330 self.plainmode = ui.configbool('mq', 'plain', False)
330 self.plainmode = ui.configbool('mq', 'plain', False)
331
331
332 @util.propertycache
332 @util.propertycache
333 def applied(self):
333 def applied(self):
334 def parselines(lines):
334 def parselines(lines):
335 for l in lines:
335 for l in lines:
336 entry = l.split(':', 1)
336 entry = l.split(':', 1)
337 if len(entry) > 1:
337 if len(entry) > 1:
338 n, name = entry
338 n, name = entry
339 yield statusentry(bin(n), name)
339 yield statusentry(bin(n), name)
340 elif l.strip():
340 elif l.strip():
341 self.ui.warn(_('malformated mq status line: %s\n') % entry)
341 self.ui.warn(_('malformated mq status line: %s\n') % entry)
342 # else we ignore empty lines
342 # else we ignore empty lines
343 try:
343 try:
344 lines = self.opener.read(self.statuspath).splitlines()
344 lines = self.opener.read(self.statuspath).splitlines()
345 return list(parselines(lines))
345 return list(parselines(lines))
346 except IOError, e:
346 except IOError, e:
347 if e.errno == errno.ENOENT:
347 if e.errno == errno.ENOENT:
348 return []
348 return []
349 raise
349 raise
350
350
351 @util.propertycache
351 @util.propertycache
352 def fullseries(self):
352 def fullseries(self):
353 try:
353 try:
354 return self.opener.read(self.seriespath).splitlines()
354 return self.opener.read(self.seriespath).splitlines()
355 except IOError, e:
355 except IOError, e:
356 if e.errno == errno.ENOENT:
356 if e.errno == errno.ENOENT:
357 return []
357 return []
358 raise
358 raise
359
359
360 @util.propertycache
360 @util.propertycache
361 def series(self):
361 def series(self):
362 self.parseseries()
362 self.parseseries()
363 return self.series
363 return self.series
364
364
365 @util.propertycache
365 @util.propertycache
366 def seriesguards(self):
366 def seriesguards(self):
367 self.parseseries()
367 self.parseseries()
368 return self.seriesguards
368 return self.seriesguards
369
369
370 def invalidate(self):
370 def invalidate(self):
371 for a in 'applied fullseries series seriesguards'.split():
371 for a in 'applied fullseries series seriesguards'.split():
372 if a in self.__dict__:
372 if a in self.__dict__:
373 delattr(self, a)
373 delattr(self, a)
374 self.applieddirty = False
374 self.applieddirty = False
375 self.seriesdirty = False
375 self.seriesdirty = False
376 self.guardsdirty = False
376 self.guardsdirty = False
377 self.activeguards = None
377 self.activeguards = None
378
378
379 def diffopts(self, opts={}, patchfn=None):
379 def diffopts(self, opts={}, patchfn=None):
380 diffopts = patchmod.diffopts(self.ui, opts)
380 diffopts = patchmod.diffopts(self.ui, opts)
381 if self.gitmode == 'auto':
381 if self.gitmode == 'auto':
382 diffopts.upgrade = True
382 diffopts.upgrade = True
383 elif self.gitmode == 'keep':
383 elif self.gitmode == 'keep':
384 pass
384 pass
385 elif self.gitmode in ('yes', 'no'):
385 elif self.gitmode in ('yes', 'no'):
386 diffopts.git = self.gitmode == 'yes'
386 diffopts.git = self.gitmode == 'yes'
387 else:
387 else:
388 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
388 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
389 ' got %s') % self.gitmode)
389 ' got %s') % self.gitmode)
390 if patchfn:
390 if patchfn:
391 diffopts = self.patchopts(diffopts, patchfn)
391 diffopts = self.patchopts(diffopts, patchfn)
392 return diffopts
392 return diffopts
393
393
394 def patchopts(self, diffopts, *patches):
394 def patchopts(self, diffopts, *patches):
395 """Return a copy of input diff options with git set to true if
395 """Return a copy of input diff options with git set to true if
396 referenced patch is a git patch and should be preserved as such.
396 referenced patch is a git patch and should be preserved as such.
397 """
397 """
398 diffopts = diffopts.copy()
398 diffopts = diffopts.copy()
399 if not diffopts.git and self.gitmode == 'keep':
399 if not diffopts.git and self.gitmode == 'keep':
400 for patchfn in patches:
400 for patchfn in patches:
401 patchf = self.opener(patchfn, 'r')
401 patchf = self.opener(patchfn, 'r')
402 # if the patch was a git patch, refresh it as a git patch
402 # if the patch was a git patch, refresh it as a git patch
403 for line in patchf:
403 for line in patchf:
404 if line.startswith('diff --git'):
404 if line.startswith('diff --git'):
405 diffopts.git = True
405 diffopts.git = True
406 break
406 break
407 patchf.close()
407 patchf.close()
408 return diffopts
408 return diffopts
409
409
410 def join(self, *p):
410 def join(self, *p):
411 return os.path.join(self.path, *p)
411 return os.path.join(self.path, *p)
412
412
413 def findseries(self, patch):
413 def findseries(self, patch):
414 def matchpatch(l):
414 def matchpatch(l):
415 l = l.split('#', 1)[0]
415 l = l.split('#', 1)[0]
416 return l.strip() == patch
416 return l.strip() == patch
417 for index, l in enumerate(self.fullseries):
417 for index, l in enumerate(self.fullseries):
418 if matchpatch(l):
418 if matchpatch(l):
419 return index
419 return index
420 return None
420 return None
421
421
422 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
422 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
423
423
424 def parseseries(self):
424 def parseseries(self):
425 self.series = []
425 self.series = []
426 self.seriesguards = []
426 self.seriesguards = []
427 for l in self.fullseries:
427 for l in self.fullseries:
428 h = l.find('#')
428 h = l.find('#')
429 if h == -1:
429 if h == -1:
430 patch = l
430 patch = l
431 comment = ''
431 comment = ''
432 elif h == 0:
432 elif h == 0:
433 continue
433 continue
434 else:
434 else:
435 patch = l[:h]
435 patch = l[:h]
436 comment = l[h:]
436 comment = l[h:]
437 patch = patch.strip()
437 patch = patch.strip()
438 if patch:
438 if patch:
439 if patch in self.series:
439 if patch in self.series:
440 raise util.Abort(_('%s appears more than once in %s') %
440 raise util.Abort(_('%s appears more than once in %s') %
441 (patch, self.join(self.seriespath)))
441 (patch, self.join(self.seriespath)))
442 self.series.append(patch)
442 self.series.append(patch)
443 self.seriesguards.append(self.guard_re.findall(comment))
443 self.seriesguards.append(self.guard_re.findall(comment))
444
444
445 def checkguard(self, guard):
445 def checkguard(self, guard):
446 if not guard:
446 if not guard:
447 return _('guard cannot be an empty string')
447 return _('guard cannot be an empty string')
448 bad_chars = '# \t\r\n\f'
448 bad_chars = '# \t\r\n\f'
449 first = guard[0]
449 first = guard[0]
450 if first in '-+':
450 if first in '-+':
451 return (_('guard %r starts with invalid character: %r') %
451 return (_('guard %r starts with invalid character: %r') %
452 (guard, first))
452 (guard, first))
453 for c in bad_chars:
453 for c in bad_chars:
454 if c in guard:
454 if c in guard:
455 return _('invalid character in guard %r: %r') % (guard, c)
455 return _('invalid character in guard %r: %r') % (guard, c)
456
456
457 def setactive(self, guards):
457 def setactive(self, guards):
458 for guard in guards:
458 for guard in guards:
459 bad = self.checkguard(guard)
459 bad = self.checkguard(guard)
460 if bad:
460 if bad:
461 raise util.Abort(bad)
461 raise util.Abort(bad)
462 guards = sorted(set(guards))
462 guards = sorted(set(guards))
463 self.ui.debug('active guards: %s\n' % ' '.join(guards))
463 self.ui.debug('active guards: %s\n' % ' '.join(guards))
464 self.activeguards = guards
464 self.activeguards = guards
465 self.guardsdirty = True
465 self.guardsdirty = True
466
466
467 def active(self):
467 def active(self):
468 if self.activeguards is None:
468 if self.activeguards is None:
469 self.activeguards = []
469 self.activeguards = []
470 try:
470 try:
471 guards = self.opener.read(self.guardspath).split()
471 guards = self.opener.read(self.guardspath).split()
472 except IOError, err:
472 except IOError, err:
473 if err.errno != errno.ENOENT:
473 if err.errno != errno.ENOENT:
474 raise
474 raise
475 guards = []
475 guards = []
476 for i, guard in enumerate(guards):
476 for i, guard in enumerate(guards):
477 bad = self.checkguard(guard)
477 bad = self.checkguard(guard)
478 if bad:
478 if bad:
479 self.ui.warn('%s:%d: %s\n' %
479 self.ui.warn('%s:%d: %s\n' %
480 (self.join(self.guardspath), i + 1, bad))
480 (self.join(self.guardspath), i + 1, bad))
481 else:
481 else:
482 self.activeguards.append(guard)
482 self.activeguards.append(guard)
483 return self.activeguards
483 return self.activeguards
484
484
485 def setguards(self, idx, guards):
485 def setguards(self, idx, guards):
486 for g in guards:
486 for g in guards:
487 if len(g) < 2:
487 if len(g) < 2:
488 raise util.Abort(_('guard %r too short') % g)
488 raise util.Abort(_('guard %r too short') % g)
489 if g[0] not in '-+':
489 if g[0] not in '-+':
490 raise util.Abort(_('guard %r starts with invalid char') % g)
490 raise util.Abort(_('guard %r starts with invalid char') % g)
491 bad = self.checkguard(g[1:])
491 bad = self.checkguard(g[1:])
492 if bad:
492 if bad:
493 raise util.Abort(bad)
493 raise util.Abort(bad)
494 drop = self.guard_re.sub('', self.fullseries[idx])
494 drop = self.guard_re.sub('', self.fullseries[idx])
495 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
495 self.fullseries[idx] = drop + ''.join([' #' + g for g in guards])
496 self.parseseries()
496 self.parseseries()
497 self.seriesdirty = True
497 self.seriesdirty = True
498
498
499 def pushable(self, idx):
499 def pushable(self, idx):
500 if isinstance(idx, str):
500 if isinstance(idx, str):
501 idx = self.series.index(idx)
501 idx = self.series.index(idx)
502 patchguards = self.seriesguards[idx]
502 patchguards = self.seriesguards[idx]
503 if not patchguards:
503 if not patchguards:
504 return True, None
504 return True, None
505 guards = self.active()
505 guards = self.active()
506 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
506 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
507 if exactneg:
507 if exactneg:
508 return False, repr(exactneg[0])
508 return False, repr(exactneg[0])
509 pos = [g for g in patchguards if g[0] == '+']
509 pos = [g for g in patchguards if g[0] == '+']
510 exactpos = [g for g in pos if g[1:] in guards]
510 exactpos = [g for g in pos if g[1:] in guards]
511 if pos:
511 if pos:
512 if exactpos:
512 if exactpos:
513 return True, repr(exactpos[0])
513 return True, repr(exactpos[0])
514 return False, ' '.join(map(repr, pos))
514 return False, ' '.join(map(repr, pos))
515 return True, ''
515 return True, ''
516
516
517 def explainpushable(self, idx, all_patches=False):
517 def explainpushable(self, idx, all_patches=False):
518 write = all_patches and self.ui.write or self.ui.warn
518 write = all_patches and self.ui.write or self.ui.warn
519 if all_patches or self.ui.verbose:
519 if all_patches or self.ui.verbose:
520 if isinstance(idx, str):
520 if isinstance(idx, str):
521 idx = self.series.index(idx)
521 idx = self.series.index(idx)
522 pushable, why = self.pushable(idx)
522 pushable, why = self.pushable(idx)
523 if all_patches and pushable:
523 if all_patches and pushable:
524 if why is None:
524 if why is None:
525 write(_('allowing %s - no guards in effect\n') %
525 write(_('allowing %s - no guards in effect\n') %
526 self.series[idx])
526 self.series[idx])
527 else:
527 else:
528 if not why:
528 if not why:
529 write(_('allowing %s - no matching negative guards\n') %
529 write(_('allowing %s - no matching negative guards\n') %
530 self.series[idx])
530 self.series[idx])
531 else:
531 else:
532 write(_('allowing %s - guarded by %s\n') %
532 write(_('allowing %s - guarded by %s\n') %
533 (self.series[idx], why))
533 (self.series[idx], why))
534 if not pushable:
534 if not pushable:
535 if why:
535 if why:
536 write(_('skipping %s - guarded by %s\n') %
536 write(_('skipping %s - guarded by %s\n') %
537 (self.series[idx], why))
537 (self.series[idx], why))
538 else:
538 else:
539 write(_('skipping %s - no matching guards\n') %
539 write(_('skipping %s - no matching guards\n') %
540 self.series[idx])
540 self.series[idx])
541
541
542 def savedirty(self):
542 def savedirty(self):
543 def writelist(items, path):
543 def writelist(items, path):
544 fp = self.opener(path, 'w')
544 fp = self.opener(path, 'w')
545 for i in items:
545 for i in items:
546 fp.write("%s\n" % i)
546 fp.write("%s\n" % i)
547 fp.close()
547 fp.close()
548 if self.applieddirty:
548 if self.applieddirty:
549 writelist(map(str, self.applied), self.statuspath)
549 writelist(map(str, self.applied), self.statuspath)
550 self.applieddirty = False
550 self.applieddirty = False
551 if self.seriesdirty:
551 if self.seriesdirty:
552 writelist(self.fullseries, self.seriespath)
552 writelist(self.fullseries, self.seriespath)
553 self.seriesdirty = False
553 self.seriesdirty = False
554 if self.guardsdirty:
554 if self.guardsdirty:
555 writelist(self.activeguards, self.guardspath)
555 writelist(self.activeguards, self.guardspath)
556 self.guardsdirty = False
556 self.guardsdirty = False
557 if self.added:
557 if self.added:
558 qrepo = self.qrepo()
558 qrepo = self.qrepo()
559 if qrepo:
559 if qrepo:
560 qrepo[None].add(f for f in self.added if f not in qrepo[None])
560 qrepo[None].add(f for f in self.added if f not in qrepo[None])
561 self.added = []
561 self.added = []
562
562
563 def removeundo(self, repo):
563 def removeundo(self, repo):
564 undo = repo.sjoin('undo')
564 undo = repo.sjoin('undo')
565 if not os.path.exists(undo):
565 if not os.path.exists(undo):
566 return
566 return
567 try:
567 try:
568 os.unlink(undo)
568 os.unlink(undo)
569 except OSError, inst:
569 except OSError, inst:
570 self.ui.warn(_('error removing undo: %s\n') % str(inst))
570 self.ui.warn(_('error removing undo: %s\n') % str(inst))
571
571
572 def backup(self, repo, files, copy=False):
572 def backup(self, repo, files, copy=False):
573 # backup local changes in --force case
573 # backup local changes in --force case
574 for f in sorted(files):
574 for f in sorted(files):
575 absf = repo.wjoin(f)
575 absf = repo.wjoin(f)
576 if os.path.lexists(absf):
576 if os.path.lexists(absf):
577 self.ui.note(_('saving current version of %s as %s\n') %
577 self.ui.note(_('saving current version of %s as %s\n') %
578 (f, f + '.orig'))
578 (f, f + '.orig'))
579 if copy:
579 if copy:
580 util.copyfile(absf, absf + '.orig')
580 util.copyfile(absf, absf + '.orig')
581 else:
581 else:
582 util.rename(absf, absf + '.orig')
582 util.rename(absf, absf + '.orig')
583
583
584 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
584 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
585 fp=None, changes=None, opts={}):
585 fp=None, changes=None, opts={}):
586 stat = opts.get('stat')
586 stat = opts.get('stat')
587 m = scmutil.match(repo[node1], files, opts)
587 m = scmutil.match(repo[node1], files, opts)
588 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
588 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
589 changes, stat, fp)
589 changes, stat, fp)
590
590
591 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
591 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
592 # first try just applying the patch
592 # first try just applying the patch
593 (err, n) = self.apply(repo, [patch], update_status=False,
593 (err, n) = self.apply(repo, [patch], update_status=False,
594 strict=True, merge=rev)
594 strict=True, merge=rev)
595
595
596 if err == 0:
596 if err == 0:
597 return (err, n)
597 return (err, n)
598
598
599 if n is None:
599 if n is None:
600 raise util.Abort(_("apply failed for patch %s") % patch)
600 raise util.Abort(_("apply failed for patch %s") % patch)
601
601
602 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
602 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
603
603
604 # apply failed, strip away that rev and merge.
604 # apply failed, strip away that rev and merge.
605 hg.clean(repo, head)
605 hg.clean(repo, head)
606 self.strip(repo, [n], update=False, backup='strip')
606 self.strip(repo, [n], update=False, backup='strip')
607
607
608 ctx = repo[rev]
608 ctx = repo[rev]
609 ret = hg.merge(repo, rev)
609 ret = hg.merge(repo, rev)
610 if ret:
610 if ret:
611 raise util.Abort(_("update returned %d") % ret)
611 raise util.Abort(_("update returned %d") % ret)
612 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
612 n = newcommit(repo, None, ctx.description(), ctx.user(), force=True)
613 if n is None:
613 if n is None:
614 raise util.Abort(_("repo commit failed"))
614 raise util.Abort(_("repo commit failed"))
615 try:
615 try:
616 ph = patchheader(mergeq.join(patch), self.plainmode)
616 ph = patchheader(mergeq.join(patch), self.plainmode)
617 except Exception:
617 except Exception:
618 raise util.Abort(_("unable to read %s") % patch)
618 raise util.Abort(_("unable to read %s") % patch)
619
619
620 diffopts = self.patchopts(diffopts, patch)
620 diffopts = self.patchopts(diffopts, patch)
621 patchf = self.opener(patch, "w")
621 patchf = self.opener(patch, "w")
622 comments = str(ph)
622 comments = str(ph)
623 if comments:
623 if comments:
624 patchf.write(comments)
624 patchf.write(comments)
625 self.printdiff(repo, diffopts, head, n, fp=patchf)
625 self.printdiff(repo, diffopts, head, n, fp=patchf)
626 patchf.close()
626 patchf.close()
627 self.removeundo(repo)
627 self.removeundo(repo)
628 return (0, n)
628 return (0, n)
629
629
630 def qparents(self, repo, rev=None):
630 def qparents(self, repo, rev=None):
631 if rev is None:
631 if rev is None:
632 (p1, p2) = repo.dirstate.parents()
632 (p1, p2) = repo.dirstate.parents()
633 if p2 == nullid:
633 if p2 == nullid:
634 return p1
634 return p1
635 if not self.applied:
635 if not self.applied:
636 return None
636 return None
637 return self.applied[-1].node
637 return self.applied[-1].node
638 p1, p2 = repo.changelog.parents(rev)
638 p1, p2 = repo.changelog.parents(rev)
639 if p2 != nullid and p2 in [x.node for x in self.applied]:
639 if p2 != nullid and p2 in [x.node for x in self.applied]:
640 return p2
640 return p2
641 return p1
641 return p1
642
642
643 def mergepatch(self, repo, mergeq, series, diffopts):
643 def mergepatch(self, repo, mergeq, series, diffopts):
644 if not self.applied:
644 if not self.applied:
645 # each of the patches merged in will have two parents. This
645 # each of the patches merged in will have two parents. This
646 # can confuse the qrefresh, qdiff, and strip code because it
646 # can confuse the qrefresh, qdiff, and strip code because it
647 # needs to know which parent is actually in the patch queue.
647 # needs to know which parent is actually in the patch queue.
648 # so, we insert a merge marker with only one parent. This way
648 # so, we insert a merge marker with only one parent. This way
649 # the first patch in the queue is never a merge patch
649 # the first patch in the queue is never a merge patch
650 #
650 #
651 pname = ".hg.patches.merge.marker"
651 pname = ".hg.patches.merge.marker"
652 n = newcommit(repo, None, '[mq]: merge marker', force=True)
652 n = newcommit(repo, None, '[mq]: merge marker', force=True)
653 self.removeundo(repo)
653 self.removeundo(repo)
654 self.applied.append(statusentry(n, pname))
654 self.applied.append(statusentry(n, pname))
655 self.applieddirty = True
655 self.applieddirty = True
656
656
657 head = self.qparents(repo)
657 head = self.qparents(repo)
658
658
659 for patch in series:
659 for patch in series:
660 patch = mergeq.lookup(patch, strict=True)
660 patch = mergeq.lookup(patch, strict=True)
661 if not patch:
661 if not patch:
662 self.ui.warn(_("patch %s does not exist\n") % patch)
662 self.ui.warn(_("patch %s does not exist\n") % patch)
663 return (1, None)
663 return (1, None)
664 pushable, reason = self.pushable(patch)
664 pushable, reason = self.pushable(patch)
665 if not pushable:
665 if not pushable:
666 self.explainpushable(patch, all_patches=True)
666 self.explainpushable(patch, all_patches=True)
667 continue
667 continue
668 info = mergeq.isapplied(patch)
668 info = mergeq.isapplied(patch)
669 if not info:
669 if not info:
670 self.ui.warn(_("patch %s is not applied\n") % patch)
670 self.ui.warn(_("patch %s is not applied\n") % patch)
671 return (1, None)
671 return (1, None)
672 rev = info[1]
672 rev = info[1]
673 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
673 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
674 if head:
674 if head:
675 self.applied.append(statusentry(head, patch))
675 self.applied.append(statusentry(head, patch))
676 self.applieddirty = True
676 self.applieddirty = True
677 if err:
677 if err:
678 return (err, head)
678 return (err, head)
679 self.savedirty()
679 self.savedirty()
680 return (0, head)
680 return (0, head)
681
681
682 def patch(self, repo, patchfile):
682 def patch(self, repo, patchfile):
683 '''Apply patchfile to the working directory.
683 '''Apply patchfile to the working directory.
684 patchfile: name of patch file'''
684 patchfile: name of patch file'''
685 files = set()
685 files = set()
686 try:
686 try:
687 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
687 fuzz = patchmod.patch(self.ui, repo, patchfile, strip=1,
688 files=files, eolmode=None)
688 files=files, eolmode=None)
689 return (True, list(files), fuzz)
689 return (True, list(files), fuzz)
690 except Exception, inst:
690 except Exception, inst:
691 self.ui.note(str(inst) + '\n')
691 self.ui.note(str(inst) + '\n')
692 if not self.ui.verbose:
692 if not self.ui.verbose:
693 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
693 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
694 self.ui.traceback()
694 self.ui.traceback()
695 return (False, list(files), False)
695 return (False, list(files), False)
696
696
697 def apply(self, repo, series, list=False, update_status=True,
697 def apply(self, repo, series, list=False, update_status=True,
698 strict=False, patchdir=None, merge=None, all_files=None,
698 strict=False, patchdir=None, merge=None, all_files=None,
699 tobackup=None, keepchanges=False):
699 tobackup=None, keepchanges=False):
700 wlock = lock = tr = None
700 wlock = lock = tr = None
701 try:
701 try:
702 wlock = repo.wlock()
702 wlock = repo.wlock()
703 lock = repo.lock()
703 lock = repo.lock()
704 tr = repo.transaction("qpush")
704 tr = repo.transaction("qpush")
705 try:
705 try:
706 ret = self._apply(repo, series, list, update_status,
706 ret = self._apply(repo, series, list, update_status,
707 strict, patchdir, merge, all_files=all_files,
707 strict, patchdir, merge, all_files=all_files,
708 tobackup=tobackup, keepchanges=keepchanges)
708 tobackup=tobackup, keepchanges=keepchanges)
709 tr.close()
709 tr.close()
710 self.savedirty()
710 self.savedirty()
711 return ret
711 return ret
712 except AbortNoCleanup:
712 except AbortNoCleanup:
713 tr.close()
713 tr.close()
714 self.savedirty()
714 self.savedirty()
715 return 2, repo.dirstate.p1()
715 return 2, repo.dirstate.p1()
716 except: # re-raises
716 except: # re-raises
717 try:
717 try:
718 tr.abort()
718 tr.abort()
719 finally:
719 finally:
720 repo.invalidate()
720 repo.invalidate()
721 repo.dirstate.invalidate()
721 repo.dirstate.invalidate()
722 self.invalidate()
722 self.invalidate()
723 raise
723 raise
724 finally:
724 finally:
725 release(tr, lock, wlock)
725 release(tr, lock, wlock)
726 self.removeundo(repo)
726 self.removeundo(repo)
727
727
728 def _apply(self, repo, series, list=False, update_status=True,
728 def _apply(self, repo, series, list=False, update_status=True,
729 strict=False, patchdir=None, merge=None, all_files=None,
729 strict=False, patchdir=None, merge=None, all_files=None,
730 tobackup=None, keepchanges=False):
730 tobackup=None, keepchanges=False):
731 """returns (error, hash)
731 """returns (error, hash)
732
732
733 error = 1 for unable to read, 2 for patch failed, 3 for patch
733 error = 1 for unable to read, 2 for patch failed, 3 for patch
734 fuzz. tobackup is None or a set of files to backup before they
734 fuzz. tobackup is None or a set of files to backup before they
735 are modified by a patch.
735 are modified by a patch.
736 """
736 """
737 # TODO unify with commands.py
737 # TODO unify with commands.py
738 if not patchdir:
738 if not patchdir:
739 patchdir = self.path
739 patchdir = self.path
740 err = 0
740 err = 0
741 n = None
741 n = None
742 for patchname in series:
742 for patchname in series:
743 pushable, reason = self.pushable(patchname)
743 pushable, reason = self.pushable(patchname)
744 if not pushable:
744 if not pushable:
745 self.explainpushable(patchname, all_patches=True)
745 self.explainpushable(patchname, all_patches=True)
746 continue
746 continue
747 self.ui.status(_("applying %s\n") % patchname)
747 self.ui.status(_("applying %s\n") % patchname)
748 pf = os.path.join(patchdir, patchname)
748 pf = os.path.join(patchdir, patchname)
749
749
750 try:
750 try:
751 ph = patchheader(self.join(patchname), self.plainmode)
751 ph = patchheader(self.join(patchname), self.plainmode)
752 except IOError:
752 except IOError:
753 self.ui.warn(_("unable to read %s\n") % patchname)
753 self.ui.warn(_("unable to read %s\n") % patchname)
754 err = 1
754 err = 1
755 break
755 break
756
756
757 message = ph.message
757 message = ph.message
758 if not message:
758 if not message:
759 # The commit message should not be translated
759 # The commit message should not be translated
760 message = "imported patch %s\n" % patchname
760 message = "imported patch %s\n" % patchname
761 else:
761 else:
762 if list:
762 if list:
763 # The commit message should not be translated
763 # The commit message should not be translated
764 message.append("\nimported patch %s" % patchname)
764 message.append("\nimported patch %s" % patchname)
765 message = '\n'.join(message)
765 message = '\n'.join(message)
766
766
767 if ph.haspatch:
767 if ph.haspatch:
768 if tobackup:
768 if tobackup:
769 touched = patchmod.changedfiles(self.ui, repo, pf)
769 touched = patchmod.changedfiles(self.ui, repo, pf)
770 touched = set(touched) & tobackup
770 touched = set(touched) & tobackup
771 if touched and keepchanges:
771 if touched and keepchanges:
772 raise AbortNoCleanup(
772 raise AbortNoCleanup(
773 _("local changes found, refresh first"))
773 _("local changes found, refresh first"))
774 self.backup(repo, touched, copy=True)
774 self.backup(repo, touched, copy=True)
775 tobackup = tobackup - touched
775 tobackup = tobackup - touched
776 (patcherr, files, fuzz) = self.patch(repo, pf)
776 (patcherr, files, fuzz) = self.patch(repo, pf)
777 if all_files is not None:
777 if all_files is not None:
778 all_files.update(files)
778 all_files.update(files)
779 patcherr = not patcherr
779 patcherr = not patcherr
780 else:
780 else:
781 self.ui.warn(_("patch %s is empty\n") % patchname)
781 self.ui.warn(_("patch %s is empty\n") % patchname)
782 patcherr, files, fuzz = 0, [], 0
782 patcherr, files, fuzz = 0, [], 0
783
783
784 if merge and files:
784 if merge and files:
785 # Mark as removed/merged and update dirstate parent info
785 # Mark as removed/merged and update dirstate parent info
786 removed = []
786 removed = []
787 merged = []
787 merged = []
788 for f in files:
788 for f in files:
789 if os.path.lexists(repo.wjoin(f)):
789 if os.path.lexists(repo.wjoin(f)):
790 merged.append(f)
790 merged.append(f)
791 else:
791 else:
792 removed.append(f)
792 removed.append(f)
793 for f in removed:
793 for f in removed:
794 repo.dirstate.remove(f)
794 repo.dirstate.remove(f)
795 for f in merged:
795 for f in merged:
796 repo.dirstate.merge(f)
796 repo.dirstate.merge(f)
797 p1, p2 = repo.dirstate.parents()
797 p1, p2 = repo.dirstate.parents()
798 repo.setparents(p1, merge)
798 repo.setparents(p1, merge)
799
799
800 match = scmutil.matchfiles(repo, files or [])
800 match = scmutil.matchfiles(repo, files or [])
801 oldtip = repo['tip']
801 oldtip = repo['tip']
802 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
802 n = newcommit(repo, None, message, ph.user, ph.date, match=match,
803 force=True)
803 force=True)
804 if repo['tip'] == oldtip:
804 if repo['tip'] == oldtip:
805 raise util.Abort(_("qpush exactly duplicates child changeset"))
805 raise util.Abort(_("qpush exactly duplicates child changeset"))
806 if n is None:
806 if n is None:
807 raise util.Abort(_("repository commit failed"))
807 raise util.Abort(_("repository commit failed"))
808
808
809 if update_status:
809 if update_status:
810 self.applied.append(statusentry(n, patchname))
810 self.applied.append(statusentry(n, patchname))
811
811
812 if patcherr:
812 if patcherr:
813 self.ui.warn(_("patch failed, rejects left in working dir\n"))
813 self.ui.warn(_("patch failed, rejects left in working dir\n"))
814 err = 2
814 err = 2
815 break
815 break
816
816
817 if fuzz and strict:
817 if fuzz and strict:
818 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
818 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
819 err = 3
819 err = 3
820 break
820 break
821 return (err, n)
821 return (err, n)
822
822
823 def _cleanup(self, patches, numrevs, keep=False):
823 def _cleanup(self, patches, numrevs, keep=False):
824 if not keep:
824 if not keep:
825 r = self.qrepo()
825 r = self.qrepo()
826 if r:
826 if r:
827 r[None].forget(patches)
827 r[None].forget(patches)
828 for p in patches:
828 for p in patches:
829 os.unlink(self.join(p))
829 os.unlink(self.join(p))
830
830
831 qfinished = []
831 qfinished = []
832 if numrevs:
832 if numrevs:
833 qfinished = self.applied[:numrevs]
833 qfinished = self.applied[:numrevs]
834 del self.applied[:numrevs]
834 del self.applied[:numrevs]
835 self.applieddirty = True
835 self.applieddirty = True
836
836
837 unknown = []
837 unknown = []
838
838
839 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
839 for (i, p) in sorted([(self.findseries(p), p) for p in patches],
840 reverse=True):
840 reverse=True):
841 if i is not None:
841 if i is not None:
842 del self.fullseries[i]
842 del self.fullseries[i]
843 else:
843 else:
844 unknown.append(p)
844 unknown.append(p)
845
845
846 if unknown:
846 if unknown:
847 if numrevs:
847 if numrevs:
848 rev = dict((entry.name, entry.node) for entry in qfinished)
848 rev = dict((entry.name, entry.node) for entry in qfinished)
849 for p in unknown:
849 for p in unknown:
850 msg = _('revision %s refers to unknown patches: %s\n')
850 msg = _('revision %s refers to unknown patches: %s\n')
851 self.ui.warn(msg % (short(rev[p]), p))
851 self.ui.warn(msg % (short(rev[p]), p))
852 else:
852 else:
853 msg = _('unknown patches: %s\n')
853 msg = _('unknown patches: %s\n')
854 raise util.Abort(''.join(msg % p for p in unknown))
854 raise util.Abort(''.join(msg % p for p in unknown))
855
855
856 self.parseseries()
856 self.parseseries()
857 self.seriesdirty = True
857 self.seriesdirty = True
858 return [entry.node for entry in qfinished]
858 return [entry.node for entry in qfinished]
859
859
860 def _revpatches(self, repo, revs):
860 def _revpatches(self, repo, revs):
861 firstrev = repo[self.applied[0].node].rev()
861 firstrev = repo[self.applied[0].node].rev()
862 patches = []
862 patches = []
863 for i, rev in enumerate(revs):
863 for i, rev in enumerate(revs):
864
864
865 if rev < firstrev:
865 if rev < firstrev:
866 raise util.Abort(_('revision %d is not managed') % rev)
866 raise util.Abort(_('revision %d is not managed') % rev)
867
867
868 ctx = repo[rev]
868 ctx = repo[rev]
869 base = self.applied[i].node
869 base = self.applied[i].node
870 if ctx.node() != base:
870 if ctx.node() != base:
871 msg = _('cannot delete revision %d above applied patches')
871 msg = _('cannot delete revision %d above applied patches')
872 raise util.Abort(msg % rev)
872 raise util.Abort(msg % rev)
873
873
874 patch = self.applied[i].name
874 patch = self.applied[i].name
875 for fmt in ('[mq]: %s', 'imported patch %s'):
875 for fmt in ('[mq]: %s', 'imported patch %s'):
876 if ctx.description() == fmt % patch:
876 if ctx.description() == fmt % patch:
877 msg = _('patch %s finalized without changeset message\n')
877 msg = _('patch %s finalized without changeset message\n')
878 repo.ui.status(msg % patch)
878 repo.ui.status(msg % patch)
879 break
879 break
880
880
881 patches.append(patch)
881 patches.append(patch)
882 return patches
882 return patches
883
883
884 def finish(self, repo, revs):
884 def finish(self, repo, revs):
885 # Manually trigger phase computation to ensure phasedefaults is
885 # Manually trigger phase computation to ensure phasedefaults is
886 # executed before we remove the patches.
886 # executed before we remove the patches.
887 repo._phasecache
887 repo._phasecache
888 patches = self._revpatches(repo, sorted(revs))
888 patches = self._revpatches(repo, sorted(revs))
889 qfinished = self._cleanup(patches, len(patches))
889 qfinished = self._cleanup(patches, len(patches))
890 if qfinished and repo.ui.configbool('mq', 'secret', False):
890 if qfinished and repo.ui.configbool('mq', 'secret', False):
891 # only use this logic when the secret option is added
891 # only use this logic when the secret option is added
892 oldqbase = repo[qfinished[0]]
892 oldqbase = repo[qfinished[0]]
893 tphase = repo.ui.config('phases', 'new-commit', phases.draft)
893 tphase = repo.ui.config('phases', 'new-commit', phases.draft)
894 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
894 if oldqbase.phase() > tphase and oldqbase.p1().phase() <= tphase:
895 phases.advanceboundary(repo, tphase, qfinished)
895 phases.advanceboundary(repo, tphase, qfinished)
896
896
897 def delete(self, repo, patches, opts):
897 def delete(self, repo, patches, opts):
898 if not patches and not opts.get('rev'):
898 if not patches and not opts.get('rev'):
899 raise util.Abort(_('qdelete requires at least one revision or '
899 raise util.Abort(_('qdelete requires at least one revision or '
900 'patch name'))
900 'patch name'))
901
901
902 realpatches = []
902 realpatches = []
903 for patch in patches:
903 for patch in patches:
904 patch = self.lookup(patch, strict=True)
904 patch = self.lookup(patch, strict=True)
905 info = self.isapplied(patch)
905 info = self.isapplied(patch)
906 if info:
906 if info:
907 raise util.Abort(_("cannot delete applied patch %s") % patch)
907 raise util.Abort(_("cannot delete applied patch %s") % patch)
908 if patch not in self.series:
908 if patch not in self.series:
909 raise util.Abort(_("patch %s not in series file") % patch)
909 raise util.Abort(_("patch %s not in series file") % patch)
910 if patch not in realpatches:
910 if patch not in realpatches:
911 realpatches.append(patch)
911 realpatches.append(patch)
912
912
913 numrevs = 0
913 numrevs = 0
914 if opts.get('rev'):
914 if opts.get('rev'):
915 if not self.applied:
915 if not self.applied:
916 raise util.Abort(_('no patches applied'))
916 raise util.Abort(_('no patches applied'))
917 revs = scmutil.revrange(repo, opts.get('rev'))
917 revs = scmutil.revrange(repo, opts.get('rev'))
918 if len(revs) > 1 and revs[0] > revs[1]:
918 if len(revs) > 1 and revs[0] > revs[1]:
919 revs.reverse()
919 revs.reverse()
920 revpatches = self._revpatches(repo, revs)
920 revpatches = self._revpatches(repo, revs)
921 realpatches += revpatches
921 realpatches += revpatches
922 numrevs = len(revpatches)
922 numrevs = len(revpatches)
923
923
924 self._cleanup(realpatches, numrevs, opts.get('keep'))
924 self._cleanup(realpatches, numrevs, opts.get('keep'))
925
925
926 def checktoppatch(self, repo):
926 def checktoppatch(self, repo):
927 if self.applied:
927 if self.applied:
928 top = self.applied[-1].node
928 top = self.applied[-1].node
929 patch = self.applied[-1].name
929 patch = self.applied[-1].name
930 pp = repo.dirstate.parents()
930 pp = repo.dirstate.parents()
931 if top not in pp:
931 if top not in pp:
932 raise util.Abort(_("working directory revision is not qtip"))
932 raise util.Abort(_("working directory revision is not qtip"))
933 return top, patch
933 return top, patch
934 return None, None
934 return None, None
935
935
936 def checksubstate(self, repo, baserev=None):
936 def checksubstate(self, repo, baserev=None):
937 '''return list of subrepos at a different revision than substate.
937 '''return list of subrepos at a different revision than substate.
938 Abort if any subrepos have uncommitted changes.'''
938 Abort if any subrepos have uncommitted changes.'''
939 inclsubs = []
939 inclsubs = []
940 wctx = repo[None]
940 wctx = repo[None]
941 if baserev:
941 if baserev:
942 bctx = repo[baserev]
942 bctx = repo[baserev]
943 else:
943 else:
944 bctx = wctx.parents()[0]
944 bctx = wctx.parents()[0]
945 for s in wctx.substate:
945 for s in wctx.substate:
946 if wctx.sub(s).dirty(True):
946 if wctx.sub(s).dirty(True):
947 raise util.Abort(
947 raise util.Abort(
948 _("uncommitted changes in subrepository %s") % s)
948 _("uncommitted changes in subrepository %s") % s)
949 elif s not in bctx.substate or bctx.sub(s).dirty():
949 elif s not in bctx.substate or bctx.sub(s).dirty():
950 inclsubs.append(s)
950 inclsubs.append(s)
951 return inclsubs
951 return inclsubs
952
952
953 def putsubstate2changes(self, substatestate, changes):
953 def putsubstate2changes(self, substatestate, changes):
954 for files in changes[:3]:
954 for files in changes[:3]:
955 if '.hgsubstate' in files:
955 if '.hgsubstate' in files:
956 return # already listed up
956 return # already listed up
957 # not yet listed up
957 # not yet listed up
958 if substatestate in 'a?':
958 if substatestate in 'a?':
959 changes[1].append('.hgsubstate')
959 changes[1].append('.hgsubstate')
960 elif substatestate in 'r':
960 elif substatestate in 'r':
961 changes[2].append('.hgsubstate')
961 changes[2].append('.hgsubstate')
962 else: # modified
962 else: # modified
963 changes[0].append('.hgsubstate')
963 changes[0].append('.hgsubstate')
964
964
965 def localchangesfound(self, refresh=True):
965 def localchangesfound(self, refresh=True):
966 if refresh:
966 if refresh:
967 raise util.Abort(_("local changes found, refresh first"))
967 raise util.Abort(_("local changes found, refresh first"))
968 else:
968 else:
969 raise util.Abort(_("local changes found"))
969 raise util.Abort(_("local changes found"))
970
970
971 def checklocalchanges(self, repo, force=False, refresh=True):
971 def checklocalchanges(self, repo, force=False, refresh=True):
972 m, a, r, d = repo.status()[:4]
972 m, a, r, d = repo.status()[:4]
973 if (m or a or r or d) and not force:
973 if (m or a or r or d) and not force:
974 self.localchangesfound(refresh)
974 self.localchangesfound(refresh)
975 return m, a, r, d
975 return m, a, r, d
976
976
977 _reserved = ('series', 'status', 'guards', '.', '..')
977 _reserved = ('series', 'status', 'guards', '.', '..')
978 def checkreservedname(self, name):
978 def checkreservedname(self, name):
979 if name in self._reserved:
979 if name in self._reserved:
980 raise util.Abort(_('"%s" cannot be used as the name of a patch')
980 raise util.Abort(_('"%s" cannot be used as the name of a patch')
981 % name)
981 % name)
982 for prefix in ('.hg', '.mq'):
982 for prefix in ('.hg', '.mq'):
983 if name.startswith(prefix):
983 if name.startswith(prefix):
984 raise util.Abort(_('patch name cannot begin with "%s"')
984 raise util.Abort(_('patch name cannot begin with "%s"')
985 % prefix)
985 % prefix)
986 for c in ('#', ':'):
986 for c in ('#', ':'):
987 if c in name:
987 if c in name:
988 raise util.Abort(_('"%s" cannot be used in the name of a patch')
988 raise util.Abort(_('"%s" cannot be used in the name of a patch')
989 % c)
989 % c)
990
990
991 def checkpatchname(self, name, force=False):
991 def checkpatchname(self, name, force=False):
992 self.checkreservedname(name)
992 self.checkreservedname(name)
993 if not force and os.path.exists(self.join(name)):
993 if not force and os.path.exists(self.join(name)):
994 if os.path.isdir(self.join(name)):
994 if os.path.isdir(self.join(name)):
995 raise util.Abort(_('"%s" already exists as a directory')
995 raise util.Abort(_('"%s" already exists as a directory')
996 % name)
996 % name)
997 else:
997 else:
998 raise util.Abort(_('patch "%s" already exists') % name)
998 raise util.Abort(_('patch "%s" already exists') % name)
999
999
1000 def checkkeepchanges(self, keepchanges, force):
1000 def checkkeepchanges(self, keepchanges, force):
1001 if force and keepchanges:
1001 if force and keepchanges:
1002 raise util.Abort(_('cannot use both --force and --keep-changes'))
1002 raise util.Abort(_('cannot use both --force and --keep-changes'))
1003
1003
1004 def new(self, repo, patchfn, *pats, **opts):
1004 def new(self, repo, patchfn, *pats, **opts):
1005 """options:
1005 """options:
1006 msg: a string or a no-argument function returning a string
1006 msg: a string or a no-argument function returning a string
1007 """
1007 """
1008 msg = opts.get('msg')
1008 msg = opts.get('msg')
1009 user = opts.get('user')
1009 user = opts.get('user')
1010 date = opts.get('date')
1010 date = opts.get('date')
1011 if date:
1011 if date:
1012 date = util.parsedate(date)
1012 date = util.parsedate(date)
1013 diffopts = self.diffopts({'git': opts.get('git')})
1013 diffopts = self.diffopts({'git': opts.get('git')})
1014 if opts.get('checkname', True):
1014 if opts.get('checkname', True):
1015 self.checkpatchname(patchfn)
1015 self.checkpatchname(patchfn)
1016 inclsubs = self.checksubstate(repo)
1016 inclsubs = self.checksubstate(repo)
1017 if inclsubs:
1017 if inclsubs:
1018 inclsubs.append('.hgsubstate')
1018 inclsubs.append('.hgsubstate')
1019 substatestate = repo.dirstate['.hgsubstate']
1019 substatestate = repo.dirstate['.hgsubstate']
1020 if opts.get('include') or opts.get('exclude') or pats:
1020 if opts.get('include') or opts.get('exclude') or pats:
1021 if inclsubs:
1021 if inclsubs:
1022 pats = list(pats or []) + inclsubs
1022 pats = list(pats or []) + inclsubs
1023 match = scmutil.match(repo[None], pats, opts)
1023 match = scmutil.match(repo[None], pats, opts)
1024 # detect missing files in pats
1024 # detect missing files in pats
1025 def badfn(f, msg):
1025 def badfn(f, msg):
1026 if f != '.hgsubstate': # .hgsubstate is auto-created
1026 if f != '.hgsubstate': # .hgsubstate is auto-created
1027 raise util.Abort('%s: %s' % (f, msg))
1027 raise util.Abort('%s: %s' % (f, msg))
1028 match.bad = badfn
1028 match.bad = badfn
1029 changes = repo.status(match=match)
1029 changes = repo.status(match=match)
1030 m, a, r, d = changes[:4]
1030 m, a, r, d = changes[:4]
1031 else:
1031 else:
1032 changes = self.checklocalchanges(repo, force=True)
1032 changes = self.checklocalchanges(repo, force=True)
1033 m, a, r, d = changes
1033 m, a, r, d = changes
1034 match = scmutil.matchfiles(repo, m + a + r + inclsubs)
1034 match = scmutil.matchfiles(repo, m + a + r + inclsubs)
1035 if len(repo[None].parents()) > 1:
1035 if len(repo[None].parents()) > 1:
1036 raise util.Abort(_('cannot manage merge changesets'))
1036 raise util.Abort(_('cannot manage merge changesets'))
1037 commitfiles = m + a + r
1037 commitfiles = m + a + r
1038 self.checktoppatch(repo)
1038 self.checktoppatch(repo)
1039 insert = self.fullseriesend()
1039 insert = self.fullseriesend()
1040 wlock = repo.wlock()
1040 wlock = repo.wlock()
1041 try:
1041 try:
1042 try:
1042 try:
1043 # if patch file write fails, abort early
1043 # if patch file write fails, abort early
1044 p = self.opener(patchfn, "w")
1044 p = self.opener(patchfn, "w")
1045 except IOError, e:
1045 except IOError, e:
1046 raise util.Abort(_('cannot write patch "%s": %s')
1046 raise util.Abort(_('cannot write patch "%s": %s')
1047 % (patchfn, e.strerror))
1047 % (patchfn, e.strerror))
1048 try:
1048 try:
1049 if self.plainmode:
1049 if self.plainmode:
1050 if user:
1050 if user:
1051 p.write("From: " + user + "\n")
1051 p.write("From: " + user + "\n")
1052 if not date:
1052 if not date:
1053 p.write("\n")
1053 p.write("\n")
1054 if date:
1054 if date:
1055 p.write("Date: %d %d\n\n" % date)
1055 p.write("Date: %d %d\n\n" % date)
1056 else:
1056 else:
1057 p.write("# HG changeset patch\n")
1057 p.write("# HG changeset patch\n")
1058 p.write("# Parent "
1058 p.write("# Parent "
1059 + hex(repo[None].p1().node()) + "\n")
1059 + hex(repo[None].p1().node()) + "\n")
1060 if user:
1060 if user:
1061 p.write("# User " + user + "\n")
1061 p.write("# User " + user + "\n")
1062 if date:
1062 if date:
1063 p.write("# Date %s %s\n\n" % date)
1063 p.write("# Date %s %s\n\n" % date)
1064 if util.safehasattr(msg, '__call__'):
1064 if util.safehasattr(msg, '__call__'):
1065 msg = msg()
1065 msg = msg()
1066 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
1066 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
1067 n = newcommit(repo, None, commitmsg, user, date, match=match,
1067 n = newcommit(repo, None, commitmsg, user, date, match=match,
1068 force=True)
1068 force=True)
1069 if n is None:
1069 if n is None:
1070 raise util.Abort(_("repo commit failed"))
1070 raise util.Abort(_("repo commit failed"))
1071 try:
1071 try:
1072 self.fullseries[insert:insert] = [patchfn]
1072 self.fullseries[insert:insert] = [patchfn]
1073 self.applied.append(statusentry(n, patchfn))
1073 self.applied.append(statusentry(n, patchfn))
1074 self.parseseries()
1074 self.parseseries()
1075 self.seriesdirty = True
1075 self.seriesdirty = True
1076 self.applieddirty = True
1076 self.applieddirty = True
1077 if msg:
1077 if msg:
1078 msg = msg + "\n\n"
1078 msg = msg + "\n\n"
1079 p.write(msg)
1079 p.write(msg)
1080 if commitfiles:
1080 if commitfiles:
1081 parent = self.qparents(repo, n)
1081 parent = self.qparents(repo, n)
1082 if inclsubs:
1082 if inclsubs:
1083 self.putsubstate2changes(substatestate, changes)
1083 self.putsubstate2changes(substatestate, changes)
1084 chunks = patchmod.diff(repo, node1=parent, node2=n,
1084 chunks = patchmod.diff(repo, node1=parent, node2=n,
1085 changes=changes, opts=diffopts)
1085 changes=changes, opts=diffopts)
1086 for chunk in chunks:
1086 for chunk in chunks:
1087 p.write(chunk)
1087 p.write(chunk)
1088 p.close()
1088 p.close()
1089 r = self.qrepo()
1089 r = self.qrepo()
1090 if r:
1090 if r:
1091 r[None].add([patchfn])
1091 r[None].add([patchfn])
1092 except: # re-raises
1092 except: # re-raises
1093 repo.rollback()
1093 repo.rollback()
1094 raise
1094 raise
1095 except Exception:
1095 except Exception:
1096 patchpath = self.join(patchfn)
1096 patchpath = self.join(patchfn)
1097 try:
1097 try:
1098 os.unlink(patchpath)
1098 os.unlink(patchpath)
1099 except OSError:
1099 except OSError:
1100 self.ui.warn(_('error unlinking %s\n') % patchpath)
1100 self.ui.warn(_('error unlinking %s\n') % patchpath)
1101 raise
1101 raise
1102 self.removeundo(repo)
1102 self.removeundo(repo)
1103 finally:
1103 finally:
1104 release(wlock)
1104 release(wlock)
1105
1105
1106 def strip(self, repo, revs, update=True, backup="all", force=None):
1106 def strip(self, repo, revs, update=True, backup="all", force=None):
1107 wlock = lock = None
1107 wlock = lock = None
1108 try:
1108 try:
1109 wlock = repo.wlock()
1109 wlock = repo.wlock()
1110 lock = repo.lock()
1110 lock = repo.lock()
1111
1111
1112 if update:
1112 if update:
1113 self.checklocalchanges(repo, force=force, refresh=False)
1113 self.checklocalchanges(repo, force=force, refresh=False)
1114 urev = self.qparents(repo, revs[0])
1114 urev = self.qparents(repo, revs[0])
1115 hg.clean(repo, urev)
1115 hg.clean(repo, urev)
1116 repo.dirstate.write()
1116 repo.dirstate.write()
1117
1117
1118 repair.strip(self.ui, repo, revs, backup)
1118 repair.strip(self.ui, repo, revs, backup)
1119 finally:
1119 finally:
1120 release(lock, wlock)
1120 release(lock, wlock)
1121
1121
1122 def isapplied(self, patch):
1122 def isapplied(self, patch):
1123 """returns (index, rev, patch)"""
1123 """returns (index, rev, patch)"""
1124 for i, a in enumerate(self.applied):
1124 for i, a in enumerate(self.applied):
1125 if a.name == patch:
1125 if a.name == patch:
1126 return (i, a.node, a.name)
1126 return (i, a.node, a.name)
1127 return None
1127 return None
1128
1128
1129 # if the exact patch name does not exist, we try a few
1129 # if the exact patch name does not exist, we try a few
1130 # variations. If strict is passed, we try only #1
1130 # variations. If strict is passed, we try only #1
1131 #
1131 #
1132 # 1) a number (as string) to indicate an offset in the series file
1132 # 1) a number (as string) to indicate an offset in the series file
1133 # 2) a unique substring of the patch name was given
1133 # 2) a unique substring of the patch name was given
1134 # 3) patchname[-+]num to indicate an offset in the series file
1134 # 3) patchname[-+]num to indicate an offset in the series file
1135 def lookup(self, patch, strict=False):
1135 def lookup(self, patch, strict=False):
1136 def partialname(s):
1136 def partialname(s):
1137 if s in self.series:
1137 if s in self.series:
1138 return s
1138 return s
1139 matches = [x for x in self.series if s in x]
1139 matches = [x for x in self.series if s in x]
1140 if len(matches) > 1:
1140 if len(matches) > 1:
1141 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1141 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
1142 for m in matches:
1142 for m in matches:
1143 self.ui.warn(' %s\n' % m)
1143 self.ui.warn(' %s\n' % m)
1144 return None
1144 return None
1145 if matches:
1145 if matches:
1146 return matches[0]
1146 return matches[0]
1147 if self.series and self.applied:
1147 if self.series and self.applied:
1148 if s == 'qtip':
1148 if s == 'qtip':
1149 return self.series[self.seriesend(True)-1]
1149 return self.series[self.seriesend(True)-1]
1150 if s == 'qbase':
1150 if s == 'qbase':
1151 return self.series[0]
1151 return self.series[0]
1152 return None
1152 return None
1153
1153
1154 if patch in self.series:
1154 if patch in self.series:
1155 return patch
1155 return patch
1156
1156
1157 if not os.path.isfile(self.join(patch)):
1157 if not os.path.isfile(self.join(patch)):
1158 try:
1158 try:
1159 sno = int(patch)
1159 sno = int(patch)
1160 except (ValueError, OverflowError):
1160 except (ValueError, OverflowError):
1161 pass
1161 pass
1162 else:
1162 else:
1163 if -len(self.series) <= sno < len(self.series):
1163 if -len(self.series) <= sno < len(self.series):
1164 return self.series[sno]
1164 return self.series[sno]
1165
1165
1166 if not strict:
1166 if not strict:
1167 res = partialname(patch)
1167 res = partialname(patch)
1168 if res:
1168 if res:
1169 return res
1169 return res
1170 minus = patch.rfind('-')
1170 minus = patch.rfind('-')
1171 if minus >= 0:
1171 if minus >= 0:
1172 res = partialname(patch[:minus])
1172 res = partialname(patch[:minus])
1173 if res:
1173 if res:
1174 i = self.series.index(res)
1174 i = self.series.index(res)
1175 try:
1175 try:
1176 off = int(patch[minus + 1:] or 1)
1176 off = int(patch[minus + 1:] or 1)
1177 except (ValueError, OverflowError):
1177 except (ValueError, OverflowError):
1178 pass
1178 pass
1179 else:
1179 else:
1180 if i - off >= 0:
1180 if i - off >= 0:
1181 return self.series[i - off]
1181 return self.series[i - off]
1182 plus = patch.rfind('+')
1182 plus = patch.rfind('+')
1183 if plus >= 0:
1183 if plus >= 0:
1184 res = partialname(patch[:plus])
1184 res = partialname(patch[:plus])
1185 if res:
1185 if res:
1186 i = self.series.index(res)
1186 i = self.series.index(res)
1187 try:
1187 try:
1188 off = int(patch[plus + 1:] or 1)
1188 off = int(patch[plus + 1:] or 1)
1189 except (ValueError, OverflowError):
1189 except (ValueError, OverflowError):
1190 pass
1190 pass
1191 else:
1191 else:
1192 if i + off < len(self.series):
1192 if i + off < len(self.series):
1193 return self.series[i + off]
1193 return self.series[i + off]
1194 raise util.Abort(_("patch %s not in series") % patch)
1194 raise util.Abort(_("patch %s not in series") % patch)
1195
1195
1196 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1196 def push(self, repo, patch=None, force=False, list=False, mergeq=None,
1197 all=False, move=False, exact=False, nobackup=False,
1197 all=False, move=False, exact=False, nobackup=False,
1198 keepchanges=False):
1198 keepchanges=False):
1199 self.checkkeepchanges(keepchanges, force)
1199 self.checkkeepchanges(keepchanges, force)
1200 diffopts = self.diffopts()
1200 diffopts = self.diffopts()
1201 wlock = repo.wlock()
1201 wlock = repo.wlock()
1202 try:
1202 try:
1203 heads = []
1203 heads = []
1204 for b, ls in repo.branchmap().iteritems():
1204 for b, ls in repo.branchmap().iteritems():
1205 heads += ls
1205 heads += ls
1206 if not heads:
1206 if not heads:
1207 heads = [nullid]
1207 heads = [nullid]
1208 if repo.dirstate.p1() not in heads and not exact:
1208 if repo.dirstate.p1() not in heads and not exact:
1209 self.ui.status(_("(working directory not at a head)\n"))
1209 self.ui.status(_("(working directory not at a head)\n"))
1210
1210
1211 if not self.series:
1211 if not self.series:
1212 self.ui.warn(_('no patches in series\n'))
1212 self.ui.warn(_('no patches in series\n'))
1213 return 0
1213 return 0
1214
1214
1215 # Suppose our series file is: A B C and the current 'top'
1215 # Suppose our series file is: A B C and the current 'top'
1216 # patch is B. qpush C should be performed (moving forward)
1216 # patch is B. qpush C should be performed (moving forward)
1217 # qpush B is a NOP (no change) qpush A is an error (can't
1217 # qpush B is a NOP (no change) qpush A is an error (can't
1218 # go backwards with qpush)
1218 # go backwards with qpush)
1219 if patch:
1219 if patch:
1220 patch = self.lookup(patch)
1220 patch = self.lookup(patch)
1221 info = self.isapplied(patch)
1221 info = self.isapplied(patch)
1222 if info and info[0] >= len(self.applied) - 1:
1222 if info and info[0] >= len(self.applied) - 1:
1223 self.ui.warn(
1223 self.ui.warn(
1224 _('qpush: %s is already at the top\n') % patch)
1224 _('qpush: %s is already at the top\n') % patch)
1225 return 0
1225 return 0
1226
1226
1227 pushable, reason = self.pushable(patch)
1227 pushable, reason = self.pushable(patch)
1228 if pushable:
1228 if pushable:
1229 if self.series.index(patch) < self.seriesend():
1229 if self.series.index(patch) < self.seriesend():
1230 raise util.Abort(
1230 raise util.Abort(
1231 _("cannot push to a previous patch: %s") % patch)
1231 _("cannot push to a previous patch: %s") % patch)
1232 else:
1232 else:
1233 if reason:
1233 if reason:
1234 reason = _('guarded by %s') % reason
1234 reason = _('guarded by %s') % reason
1235 else:
1235 else:
1236 reason = _('no matching guards')
1236 reason = _('no matching guards')
1237 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1237 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1238 return 1
1238 return 1
1239 elif all:
1239 elif all:
1240 patch = self.series[-1]
1240 patch = self.series[-1]
1241 if self.isapplied(patch):
1241 if self.isapplied(patch):
1242 self.ui.warn(_('all patches are currently applied\n'))
1242 self.ui.warn(_('all patches are currently applied\n'))
1243 return 0
1243 return 0
1244
1244
1245 # Following the above example, starting at 'top' of B:
1245 # Following the above example, starting at 'top' of B:
1246 # qpush should be performed (pushes C), but a subsequent
1246 # qpush should be performed (pushes C), but a subsequent
1247 # qpush without an argument is an error (nothing to
1247 # qpush without an argument is an error (nothing to
1248 # apply). This allows a loop of "...while hg qpush..." to
1248 # apply). This allows a loop of "...while hg qpush..." to
1249 # work as it detects an error when done
1249 # work as it detects an error when done
1250 start = self.seriesend()
1250 start = self.seriesend()
1251 if start == len(self.series):
1251 if start == len(self.series):
1252 self.ui.warn(_('patch series already fully applied\n'))
1252 self.ui.warn(_('patch series already fully applied\n'))
1253 return 1
1253 return 1
1254 if not force and not keepchanges:
1254 if not force and not keepchanges:
1255 self.checklocalchanges(repo, refresh=self.applied)
1255 self.checklocalchanges(repo, refresh=self.applied)
1256
1256
1257 if exact:
1257 if exact:
1258 if keepchanges:
1258 if keepchanges:
1259 raise util.Abort(
1259 raise util.Abort(
1260 _("cannot use --exact and --keep-changes together"))
1260 _("cannot use --exact and --keep-changes together"))
1261 if move:
1261 if move:
1262 raise util.Abort(_('cannot use --exact and --move '
1262 raise util.Abort(_('cannot use --exact and --move '
1263 'together'))
1263 'together'))
1264 if self.applied:
1264 if self.applied:
1265 raise util.Abort(_('cannot push --exact with applied '
1265 raise util.Abort(_('cannot push --exact with applied '
1266 'patches'))
1266 'patches'))
1267 root = self.series[start]
1267 root = self.series[start]
1268 target = patchheader(self.join(root), self.plainmode).parent
1268 target = patchheader(self.join(root), self.plainmode).parent
1269 if not target:
1269 if not target:
1270 raise util.Abort(
1270 raise util.Abort(
1271 _("%s does not have a parent recorded") % root)
1271 _("%s does not have a parent recorded") % root)
1272 if not repo[target] == repo['.']:
1272 if not repo[target] == repo['.']:
1273 hg.update(repo, target)
1273 hg.update(repo, target)
1274
1274
1275 if move:
1275 if move:
1276 if not patch:
1276 if not patch:
1277 raise util.Abort(_("please specify the patch to move"))
1277 raise util.Abort(_("please specify the patch to move"))
1278 for fullstart, rpn in enumerate(self.fullseries):
1278 for fullstart, rpn in enumerate(self.fullseries):
1279 # strip markers for patch guards
1279 # strip markers for patch guards
1280 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1280 if self.guard_re.split(rpn, 1)[0] == self.series[start]:
1281 break
1281 break
1282 for i, rpn in enumerate(self.fullseries[fullstart:]):
1282 for i, rpn in enumerate(self.fullseries[fullstart:]):
1283 # strip markers for patch guards
1283 # strip markers for patch guards
1284 if self.guard_re.split(rpn, 1)[0] == patch:
1284 if self.guard_re.split(rpn, 1)[0] == patch:
1285 break
1285 break
1286 index = fullstart + i
1286 index = fullstart + i
1287 assert index < len(self.fullseries)
1287 assert index < len(self.fullseries)
1288 fullpatch = self.fullseries[index]
1288 fullpatch = self.fullseries[index]
1289 del self.fullseries[index]
1289 del self.fullseries[index]
1290 self.fullseries.insert(fullstart, fullpatch)
1290 self.fullseries.insert(fullstart, fullpatch)
1291 self.parseseries()
1291 self.parseseries()
1292 self.seriesdirty = True
1292 self.seriesdirty = True
1293
1293
1294 self.applieddirty = True
1294 self.applieddirty = True
1295 if start > 0:
1295 if start > 0:
1296 self.checktoppatch(repo)
1296 self.checktoppatch(repo)
1297 if not patch:
1297 if not patch:
1298 patch = self.series[start]
1298 patch = self.series[start]
1299 end = start + 1
1299 end = start + 1
1300 else:
1300 else:
1301 end = self.series.index(patch, start) + 1
1301 end = self.series.index(patch, start) + 1
1302
1302
1303 tobackup = set()
1303 tobackup = set()
1304 if (not nobackup and force) or keepchanges:
1304 if (not nobackup and force) or keepchanges:
1305 m, a, r, d = self.checklocalchanges(repo, force=True)
1305 m, a, r, d = self.checklocalchanges(repo, force=True)
1306 if keepchanges:
1306 if keepchanges:
1307 tobackup.update(m + a + r + d)
1307 tobackup.update(m + a + r + d)
1308 else:
1308 else:
1309 tobackup.update(m + a)
1309 tobackup.update(m + a)
1310
1310
1311 s = self.series[start:end]
1311 s = self.series[start:end]
1312 all_files = set()
1312 all_files = set()
1313 try:
1313 try:
1314 if mergeq:
1314 if mergeq:
1315 ret = self.mergepatch(repo, mergeq, s, diffopts)
1315 ret = self.mergepatch(repo, mergeq, s, diffopts)
1316 else:
1316 else:
1317 ret = self.apply(repo, s, list, all_files=all_files,
1317 ret = self.apply(repo, s, list, all_files=all_files,
1318 tobackup=tobackup, keepchanges=keepchanges)
1318 tobackup=tobackup, keepchanges=keepchanges)
1319 except: # re-raises
1319 except: # re-raises
1320 self.ui.warn(_('cleaning up working directory...'))
1320 self.ui.warn(_('cleaning up working directory...'))
1321 node = repo.dirstate.p1()
1321 node = repo.dirstate.p1()
1322 hg.revert(repo, node, None)
1322 hg.revert(repo, node, None)
1323 # only remove unknown files that we know we touched or
1323 # only remove unknown files that we know we touched or
1324 # created while patching
1324 # created while patching
1325 for f in all_files:
1325 for f in all_files:
1326 if f not in repo.dirstate:
1326 if f not in repo.dirstate:
1327 try:
1327 try:
1328 util.unlinkpath(repo.wjoin(f))
1328 util.unlinkpath(repo.wjoin(f))
1329 except OSError, inst:
1329 except OSError, inst:
1330 if inst.errno != errno.ENOENT:
1330 if inst.errno != errno.ENOENT:
1331 raise
1331 raise
1332 self.ui.warn(_('done\n'))
1332 self.ui.warn(_('done\n'))
1333 raise
1333 raise
1334
1334
1335 if not self.applied:
1335 if not self.applied:
1336 return ret[0]
1336 return ret[0]
1337 top = self.applied[-1].name
1337 top = self.applied[-1].name
1338 if ret[0] and ret[0] > 1:
1338 if ret[0] and ret[0] > 1:
1339 msg = _("errors during apply, please fix and refresh %s\n")
1339 msg = _("errors during apply, please fix and refresh %s\n")
1340 self.ui.write(msg % top)
1340 self.ui.write(msg % top)
1341 else:
1341 else:
1342 self.ui.write(_("now at: %s\n") % top)
1342 self.ui.write(_("now at: %s\n") % top)
1343 return ret[0]
1343 return ret[0]
1344
1344
1345 finally:
1345 finally:
1346 wlock.release()
1346 wlock.release()
1347
1347
1348 def pop(self, repo, patch=None, force=False, update=True, all=False,
1348 def pop(self, repo, patch=None, force=False, update=True, all=False,
1349 nobackup=False, keepchanges=False):
1349 nobackup=False, keepchanges=False):
1350 self.checkkeepchanges(keepchanges, force)
1350 self.checkkeepchanges(keepchanges, force)
1351 wlock = repo.wlock()
1351 wlock = repo.wlock()
1352 try:
1352 try:
1353 if patch:
1353 if patch:
1354 # index, rev, patch
1354 # index, rev, patch
1355 info = self.isapplied(patch)
1355 info = self.isapplied(patch)
1356 if not info:
1356 if not info:
1357 patch = self.lookup(patch)
1357 patch = self.lookup(patch)
1358 info = self.isapplied(patch)
1358 info = self.isapplied(patch)
1359 if not info:
1359 if not info:
1360 raise util.Abort(_("patch %s is not applied") % patch)
1360 raise util.Abort(_("patch %s is not applied") % patch)
1361
1361
1362 if not self.applied:
1362 if not self.applied:
1363 # Allow qpop -a to work repeatedly,
1363 # Allow qpop -a to work repeatedly,
1364 # but not qpop without an argument
1364 # but not qpop without an argument
1365 self.ui.warn(_("no patches applied\n"))
1365 self.ui.warn(_("no patches applied\n"))
1366 return not all
1366 return not all
1367
1367
1368 if all:
1368 if all:
1369 start = 0
1369 start = 0
1370 elif patch:
1370 elif patch:
1371 start = info[0] + 1
1371 start = info[0] + 1
1372 else:
1372 else:
1373 start = len(self.applied) - 1
1373 start = len(self.applied) - 1
1374
1374
1375 if start >= len(self.applied):
1375 if start >= len(self.applied):
1376 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1376 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1377 return
1377 return
1378
1378
1379 if not update:
1379 if not update:
1380 parents = repo.dirstate.parents()
1380 parents = repo.dirstate.parents()
1381 rr = [x.node for x in self.applied]
1381 rr = [x.node for x in self.applied]
1382 for p in parents:
1382 for p in parents:
1383 if p in rr:
1383 if p in rr:
1384 self.ui.warn(_("qpop: forcing dirstate update\n"))
1384 self.ui.warn(_("qpop: forcing dirstate update\n"))
1385 update = True
1385 update = True
1386 else:
1386 else:
1387 parents = [p.node() for p in repo[None].parents()]
1387 parents = [p.node() for p in repo[None].parents()]
1388 needupdate = False
1388 needupdate = False
1389 for entry in self.applied[start:]:
1389 for entry in self.applied[start:]:
1390 if entry.node in parents:
1390 if entry.node in parents:
1391 needupdate = True
1391 needupdate = True
1392 break
1392 break
1393 update = needupdate
1393 update = needupdate
1394
1394
1395 tobackup = set()
1395 tobackup = set()
1396 if update:
1396 if update:
1397 m, a, r, d = self.checklocalchanges(
1397 m, a, r, d = self.checklocalchanges(
1398 repo, force=force or keepchanges)
1398 repo, force=force or keepchanges)
1399 if force:
1399 if force:
1400 if not nobackup:
1400 if not nobackup:
1401 tobackup.update(m + a)
1401 tobackup.update(m + a)
1402 elif keepchanges:
1402 elif keepchanges:
1403 tobackup.update(m + a + r + d)
1403 tobackup.update(m + a + r + d)
1404
1404
1405 self.applieddirty = True
1405 self.applieddirty = True
1406 end = len(self.applied)
1406 end = len(self.applied)
1407 rev = self.applied[start].node
1407 rev = self.applied[start].node
1408 if update:
1408 if update:
1409 top = self.checktoppatch(repo)[0]
1409 top = self.checktoppatch(repo)[0]
1410
1410
1411 try:
1411 try:
1412 heads = repo.changelog.heads(rev)
1412 heads = repo.changelog.heads(rev)
1413 except error.LookupError:
1413 except error.LookupError:
1414 node = short(rev)
1414 node = short(rev)
1415 raise util.Abort(_('trying to pop unknown node %s') % node)
1415 raise util.Abort(_('trying to pop unknown node %s') % node)
1416
1416
1417 if heads != [self.applied[-1].node]:
1417 if heads != [self.applied[-1].node]:
1418 raise util.Abort(_("popping would remove a revision not "
1418 raise util.Abort(_("popping would remove a revision not "
1419 "managed by this patch queue"))
1419 "managed by this patch queue"))
1420 if not repo[self.applied[-1].node].mutable():
1420 if not repo[self.applied[-1].node].mutable():
1421 raise util.Abort(
1421 raise util.Abort(
1422 _("popping would remove an immutable revision"),
1422 _("popping would remove an immutable revision"),
1423 hint=_('see "hg help phases" for details'))
1423 hint=_('see "hg help phases" for details'))
1424
1424
1425 # we know there are no local changes, so we can make a simplified
1425 # we know there are no local changes, so we can make a simplified
1426 # form of hg.update.
1426 # form of hg.update.
1427 if update:
1427 if update:
1428 qp = self.qparents(repo, rev)
1428 qp = self.qparents(repo, rev)
1429 ctx = repo[qp]
1429 ctx = repo[qp]
1430 m, a, r, d = repo.status(qp, top)[:4]
1430 m, a, r, d = repo.status(qp, top)[:4]
1431 if d:
1431 if d:
1432 raise util.Abort(_("deletions found between repo revs"))
1432 raise util.Abort(_("deletions found between repo revs"))
1433
1433
1434 tobackup = set(a + m + r) & tobackup
1434 tobackup = set(a + m + r) & tobackup
1435 if keepchanges and tobackup:
1435 if keepchanges and tobackup:
1436 self.localchangesfound()
1436 self.localchangesfound()
1437 self.backup(repo, tobackup)
1437 self.backup(repo, tobackup)
1438
1438
1439 for f in a:
1439 for f in a:
1440 try:
1440 try:
1441 util.unlinkpath(repo.wjoin(f))
1441 util.unlinkpath(repo.wjoin(f))
1442 except OSError, e:
1442 except OSError, e:
1443 if e.errno != errno.ENOENT:
1443 if e.errno != errno.ENOENT:
1444 raise
1444 raise
1445 repo.dirstate.drop(f)
1445 repo.dirstate.drop(f)
1446 for f in m + r:
1446 for f in m + r:
1447 fctx = ctx[f]
1447 fctx = ctx[f]
1448 repo.wwrite(f, fctx.data(), fctx.flags())
1448 repo.wwrite(f, fctx.data(), fctx.flags())
1449 repo.dirstate.normal(f)
1449 repo.dirstate.normal(f)
1450 repo.setparents(qp, nullid)
1450 repo.setparents(qp, nullid)
1451 for patch in reversed(self.applied[start:end]):
1451 for patch in reversed(self.applied[start:end]):
1452 self.ui.status(_("popping %s\n") % patch.name)
1452 self.ui.status(_("popping %s\n") % patch.name)
1453 del self.applied[start:end]
1453 del self.applied[start:end]
1454 self.strip(repo, [rev], update=False, backup='strip')
1454 self.strip(repo, [rev], update=False, backup='strip')
1455 if self.applied:
1455 if self.applied:
1456 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1456 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1457 else:
1457 else:
1458 self.ui.write(_("patch queue now empty\n"))
1458 self.ui.write(_("patch queue now empty\n"))
1459 finally:
1459 finally:
1460 wlock.release()
1460 wlock.release()
1461
1461
1462 def diff(self, repo, pats, opts):
1462 def diff(self, repo, pats, opts):
1463 top, patch = self.checktoppatch(repo)
1463 top, patch = self.checktoppatch(repo)
1464 if not top:
1464 if not top:
1465 self.ui.write(_("no patches applied\n"))
1465 self.ui.write(_("no patches applied\n"))
1466 return
1466 return
1467 qp = self.qparents(repo, top)
1467 qp = self.qparents(repo, top)
1468 if opts.get('reverse'):
1468 if opts.get('reverse'):
1469 node1, node2 = None, qp
1469 node1, node2 = None, qp
1470 else:
1470 else:
1471 node1, node2 = qp, None
1471 node1, node2 = qp, None
1472 diffopts = self.diffopts(opts, patch)
1472 diffopts = self.diffopts(opts, patch)
1473 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1473 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1474
1474
1475 def refresh(self, repo, pats=None, **opts):
1475 def refresh(self, repo, pats=None, **opts):
1476 if not self.applied:
1476 if not self.applied:
1477 self.ui.write(_("no patches applied\n"))
1477 self.ui.write(_("no patches applied\n"))
1478 return 1
1478 return 1
1479 msg = opts.get('msg', '').rstrip()
1479 msg = opts.get('msg', '').rstrip()
1480 newuser = opts.get('user')
1480 newuser = opts.get('user')
1481 newdate = opts.get('date')
1481 newdate = opts.get('date')
1482 if newdate:
1482 if newdate:
1483 newdate = '%d %d' % util.parsedate(newdate)
1483 newdate = '%d %d' % util.parsedate(newdate)
1484 wlock = repo.wlock()
1484 wlock = repo.wlock()
1485
1485
1486 try:
1486 try:
1487 self.checktoppatch(repo)
1487 self.checktoppatch(repo)
1488 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1488 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1489 if repo.changelog.heads(top) != [top]:
1489 if repo.changelog.heads(top) != [top]:
1490 raise util.Abort(_("cannot refresh a revision with children"))
1490 raise util.Abort(_("cannot refresh a revision with children"))
1491 if not repo[top].mutable():
1491 if not repo[top].mutable():
1492 raise util.Abort(_("cannot refresh immutable revision"),
1492 raise util.Abort(_("cannot refresh immutable revision"),
1493 hint=_('see "hg help phases" for details'))
1493 hint=_('see "hg help phases" for details'))
1494
1494
1495 cparents = repo.changelog.parents(top)
1495 cparents = repo.changelog.parents(top)
1496 patchparent = self.qparents(repo, top)
1496 patchparent = self.qparents(repo, top)
1497
1497
1498 inclsubs = self.checksubstate(repo, hex(patchparent))
1498 inclsubs = self.checksubstate(repo, hex(patchparent))
1499 if inclsubs:
1499 if inclsubs:
1500 inclsubs.append('.hgsubstate')
1500 inclsubs.append('.hgsubstate')
1501 substatestate = repo.dirstate['.hgsubstate']
1501 substatestate = repo.dirstate['.hgsubstate']
1502
1502
1503 ph = patchheader(self.join(patchfn), self.plainmode)
1503 ph = patchheader(self.join(patchfn), self.plainmode)
1504 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1504 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1505 if msg:
1505 if msg:
1506 ph.setmessage(msg)
1506 ph.setmessage(msg)
1507 if newuser:
1507 if newuser:
1508 ph.setuser(newuser)
1508 ph.setuser(newuser)
1509 if newdate:
1509 if newdate:
1510 ph.setdate(newdate)
1510 ph.setdate(newdate)
1511 ph.setparent(hex(patchparent))
1511 ph.setparent(hex(patchparent))
1512
1512
1513 # only commit new patch when write is complete
1513 # only commit new patch when write is complete
1514 patchf = self.opener(patchfn, 'w', atomictemp=True)
1514 patchf = self.opener(patchfn, 'w', atomictemp=True)
1515
1515
1516 comments = str(ph)
1516 comments = str(ph)
1517 if comments:
1517 if comments:
1518 patchf.write(comments)
1518 patchf.write(comments)
1519
1519
1520 # update the dirstate in place, strip off the qtip commit
1520 # update the dirstate in place, strip off the qtip commit
1521 # and then commit.
1521 # and then commit.
1522 #
1522 #
1523 # this should really read:
1523 # this should really read:
1524 # mm, dd, aa = repo.status(top, patchparent)[:3]
1524 # mm, dd, aa = repo.status(top, patchparent)[:3]
1525 # but we do it backwards to take advantage of manifest/changelog
1525 # but we do it backwards to take advantage of manifest/changelog
1526 # caching against the next repo.status call
1526 # caching against the next repo.status call
1527 mm, aa, dd = repo.status(patchparent, top)[:3]
1527 mm, aa, dd = repo.status(patchparent, top)[:3]
1528 changes = repo.changelog.read(top)
1528 changes = repo.changelog.read(top)
1529 man = repo.manifest.read(changes[0])
1529 man = repo.manifest.read(changes[0])
1530 aaa = aa[:]
1530 aaa = aa[:]
1531 matchfn = scmutil.match(repo[None], pats, opts)
1531 matchfn = scmutil.match(repo[None], pats, opts)
1532 # in short mode, we only diff the files included in the
1532 # in short mode, we only diff the files included in the
1533 # patch already plus specified files
1533 # patch already plus specified files
1534 if opts.get('short'):
1534 if opts.get('short'):
1535 # if amending a patch, we start with existing
1535 # if amending a patch, we start with existing
1536 # files plus specified files - unfiltered
1536 # files plus specified files - unfiltered
1537 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1537 match = scmutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1538 # filter with include/exclude options
1538 # filter with include/exclude options
1539 matchfn = scmutil.match(repo[None], opts=opts)
1539 matchfn = scmutil.match(repo[None], opts=opts)
1540 else:
1540 else:
1541 match = scmutil.matchall(repo)
1541 match = scmutil.matchall(repo)
1542 m, a, r, d = repo.status(match=match)[:4]
1542 m, a, r, d = repo.status(match=match)[:4]
1543 mm = set(mm)
1543 mm = set(mm)
1544 aa = set(aa)
1544 aa = set(aa)
1545 dd = set(dd)
1545 dd = set(dd)
1546
1546
1547 # we might end up with files that were added between
1547 # we might end up with files that were added between
1548 # qtip and the dirstate parent, but then changed in the
1548 # qtip and the dirstate parent, but then changed in the
1549 # local dirstate. in this case, we want them to only
1549 # local dirstate. in this case, we want them to only
1550 # show up in the added section
1550 # show up in the added section
1551 for x in m:
1551 for x in m:
1552 if x not in aa:
1552 if x not in aa:
1553 mm.add(x)
1553 mm.add(x)
1554 # we might end up with files added by the local dirstate that
1554 # we might end up with files added by the local dirstate that
1555 # were deleted by the patch. In this case, they should only
1555 # were deleted by the patch. In this case, they should only
1556 # show up in the changed section.
1556 # show up in the changed section.
1557 for x in a:
1557 for x in a:
1558 if x in dd:
1558 if x in dd:
1559 dd.remove(x)
1559 dd.remove(x)
1560 mm.add(x)
1560 mm.add(x)
1561 else:
1561 else:
1562 aa.add(x)
1562 aa.add(x)
1563 # make sure any files deleted in the local dirstate
1563 # make sure any files deleted in the local dirstate
1564 # are not in the add or change column of the patch
1564 # are not in the add or change column of the patch
1565 forget = []
1565 forget = []
1566 for x in d + r:
1566 for x in d + r:
1567 if x in aa:
1567 if x in aa:
1568 aa.remove(x)
1568 aa.remove(x)
1569 forget.append(x)
1569 forget.append(x)
1570 continue
1570 continue
1571 else:
1571 else:
1572 mm.discard(x)
1572 mm.discard(x)
1573 dd.add(x)
1573 dd.add(x)
1574
1574
1575 m = list(mm)
1575 m = list(mm)
1576 r = list(dd)
1576 r = list(dd)
1577 a = list(aa)
1577 a = list(aa)
1578 c = [filter(matchfn, l) for l in (m, a, r)]
1578 c = [filter(matchfn, l) for l in (m, a, r)]
1579 match = scmutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1579 match = scmutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1580 bmlist = repo[top].bookmarks()
1580 bmlist = repo[top].bookmarks()
1581
1581
1582 try:
1582 try:
1583 if diffopts.git or diffopts.upgrade:
1583 if diffopts.git or diffopts.upgrade:
1584 copies = {}
1584 copies = {}
1585 for dst in a:
1585 for dst in a:
1586 src = repo.dirstate.copied(dst)
1586 src = repo.dirstate.copied(dst)
1587 # during qfold, the source file for copies may
1587 # during qfold, the source file for copies may
1588 # be removed. Treat this as a simple add.
1588 # be removed. Treat this as a simple add.
1589 if src is not None and src in repo.dirstate:
1589 if src is not None and src in repo.dirstate:
1590 copies.setdefault(src, []).append(dst)
1590 copies.setdefault(src, []).append(dst)
1591 repo.dirstate.add(dst)
1591 repo.dirstate.add(dst)
1592 # remember the copies between patchparent and qtip
1592 # remember the copies between patchparent and qtip
1593 for dst in aaa:
1593 for dst in aaa:
1594 f = repo.file(dst)
1594 f = repo.file(dst)
1595 src = f.renamed(man[dst])
1595 src = f.renamed(man[dst])
1596 if src:
1596 if src:
1597 copies.setdefault(src[0], []).extend(
1597 copies.setdefault(src[0], []).extend(
1598 copies.get(dst, []))
1598 copies.get(dst, []))
1599 if dst in a:
1599 if dst in a:
1600 copies[src[0]].append(dst)
1600 copies[src[0]].append(dst)
1601 # we can't copy a file created by the patch itself
1601 # we can't copy a file created by the patch itself
1602 if dst in copies:
1602 if dst in copies:
1603 del copies[dst]
1603 del copies[dst]
1604 for src, dsts in copies.iteritems():
1604 for src, dsts in copies.iteritems():
1605 for dst in dsts:
1605 for dst in dsts:
1606 repo.dirstate.copy(src, dst)
1606 repo.dirstate.copy(src, dst)
1607 else:
1607 else:
1608 for dst in a:
1608 for dst in a:
1609 repo.dirstate.add(dst)
1609 repo.dirstate.add(dst)
1610 # Drop useless copy information
1610 # Drop useless copy information
1611 for f in list(repo.dirstate.copies()):
1611 for f in list(repo.dirstate.copies()):
1612 repo.dirstate.copy(None, f)
1612 repo.dirstate.copy(None, f)
1613 for f in r:
1613 for f in r:
1614 repo.dirstate.remove(f)
1614 repo.dirstate.remove(f)
1615 # if the patch excludes a modified file, mark that
1615 # if the patch excludes a modified file, mark that
1616 # file with mtime=0 so status can see it.
1616 # file with mtime=0 so status can see it.
1617 mm = []
1617 mm = []
1618 for i in xrange(len(m)-1, -1, -1):
1618 for i in xrange(len(m)-1, -1, -1):
1619 if not matchfn(m[i]):
1619 if not matchfn(m[i]):
1620 mm.append(m[i])
1620 mm.append(m[i])
1621 del m[i]
1621 del m[i]
1622 for f in m:
1622 for f in m:
1623 repo.dirstate.normal(f)
1623 repo.dirstate.normal(f)
1624 for f in mm:
1624 for f in mm:
1625 repo.dirstate.normallookup(f)
1625 repo.dirstate.normallookup(f)
1626 for f in forget:
1626 for f in forget:
1627 repo.dirstate.drop(f)
1627 repo.dirstate.drop(f)
1628
1628
1629 if not msg:
1629 if not msg:
1630 if not ph.message:
1630 if not ph.message:
1631 message = "[mq]: %s\n" % patchfn
1631 message = "[mq]: %s\n" % patchfn
1632 else:
1632 else:
1633 message = "\n".join(ph.message)
1633 message = "\n".join(ph.message)
1634 else:
1634 else:
1635 message = msg
1635 message = msg
1636
1636
1637 user = ph.user or changes[1]
1637 user = ph.user or changes[1]
1638
1638
1639 oldphase = repo[top].phase()
1639 oldphase = repo[top].phase()
1640
1640
1641 # assumes strip can roll itself back if interrupted
1641 # assumes strip can roll itself back if interrupted
1642 repo.setparents(*cparents)
1642 repo.setparents(*cparents)
1643 self.applied.pop()
1643 self.applied.pop()
1644 self.applieddirty = True
1644 self.applieddirty = True
1645 self.strip(repo, [top], update=False,
1645 self.strip(repo, [top], update=False,
1646 backup='strip')
1646 backup='strip')
1647 except: # re-raises
1647 except: # re-raises
1648 repo.dirstate.invalidate()
1648 repo.dirstate.invalidate()
1649 raise
1649 raise
1650
1650
1651 try:
1651 try:
1652 # might be nice to attempt to roll back strip after this
1652 # might be nice to attempt to roll back strip after this
1653
1653
1654 # Ensure we create a new changeset in the same phase than
1654 # Ensure we create a new changeset in the same phase than
1655 # the old one.
1655 # the old one.
1656 n = newcommit(repo, oldphase, message, user, ph.date,
1656 n = newcommit(repo, oldphase, message, user, ph.date,
1657 match=match, force=True)
1657 match=match, force=True)
1658 # only write patch after a successful commit
1658 # only write patch after a successful commit
1659 if inclsubs:
1659 if inclsubs:
1660 self.putsubstate2changes(substatestate, c)
1660 self.putsubstate2changes(substatestate, c)
1661 chunks = patchmod.diff(repo, patchparent,
1661 chunks = patchmod.diff(repo, patchparent,
1662 changes=c, opts=diffopts)
1662 changes=c, opts=diffopts)
1663 for chunk in chunks:
1663 for chunk in chunks:
1664 patchf.write(chunk)
1664 patchf.write(chunk)
1665 patchf.close()
1665 patchf.close()
1666
1666
1667 for bm in bmlist:
1667 for bm in bmlist:
1668 repo._bookmarks[bm] = n
1668 repo._bookmarks[bm] = n
1669 bookmarks.write(repo)
1669 bookmarks.write(repo)
1670
1670
1671 self.applied.append(statusentry(n, patchfn))
1671 self.applied.append(statusentry(n, patchfn))
1672 except: # re-raises
1672 except: # re-raises
1673 ctx = repo[cparents[0]]
1673 ctx = repo[cparents[0]]
1674 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1674 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1675 self.savedirty()
1675 self.savedirty()
1676 self.ui.warn(_('refresh interrupted while patch was popped! '
1676 self.ui.warn(_('refresh interrupted while patch was popped! '
1677 '(revert --all, qpush to recover)\n'))
1677 '(revert --all, qpush to recover)\n'))
1678 raise
1678 raise
1679 finally:
1679 finally:
1680 wlock.release()
1680 wlock.release()
1681 self.removeundo(repo)
1681 self.removeundo(repo)
1682
1682
1683 def init(self, repo, create=False):
1683 def init(self, repo, create=False):
1684 if not create and os.path.isdir(self.path):
1684 if not create and os.path.isdir(self.path):
1685 raise util.Abort(_("patch queue directory already exists"))
1685 raise util.Abort(_("patch queue directory already exists"))
1686 try:
1686 try:
1687 os.mkdir(self.path)
1687 os.mkdir(self.path)
1688 except OSError, inst:
1688 except OSError, inst:
1689 if inst.errno != errno.EEXIST or not create:
1689 if inst.errno != errno.EEXIST or not create:
1690 raise
1690 raise
1691 if create:
1691 if create:
1692 return self.qrepo(create=True)
1692 return self.qrepo(create=True)
1693
1693
1694 def unapplied(self, repo, patch=None):
1694 def unapplied(self, repo, patch=None):
1695 if patch and patch not in self.series:
1695 if patch and patch not in self.series:
1696 raise util.Abort(_("patch %s is not in series file") % patch)
1696 raise util.Abort(_("patch %s is not in series file") % patch)
1697 if not patch:
1697 if not patch:
1698 start = self.seriesend()
1698 start = self.seriesend()
1699 else:
1699 else:
1700 start = self.series.index(patch) + 1
1700 start = self.series.index(patch) + 1
1701 unapplied = []
1701 unapplied = []
1702 for i in xrange(start, len(self.series)):
1702 for i in xrange(start, len(self.series)):
1703 pushable, reason = self.pushable(i)
1703 pushable, reason = self.pushable(i)
1704 if pushable:
1704 if pushable:
1705 unapplied.append((i, self.series[i]))
1705 unapplied.append((i, self.series[i]))
1706 self.explainpushable(i)
1706 self.explainpushable(i)
1707 return unapplied
1707 return unapplied
1708
1708
1709 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1709 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1710 summary=False):
1710 summary=False):
1711 def displayname(pfx, patchname, state):
1711 def displayname(pfx, patchname, state):
1712 if pfx:
1712 if pfx:
1713 self.ui.write(pfx)
1713 self.ui.write(pfx)
1714 if summary:
1714 if summary:
1715 ph = patchheader(self.join(patchname), self.plainmode)
1715 ph = patchheader(self.join(patchname), self.plainmode)
1716 msg = ph.message and ph.message[0] or ''
1716 msg = ph.message and ph.message[0] or ''
1717 if self.ui.formatted():
1717 if self.ui.formatted():
1718 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1718 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1719 if width > 0:
1719 if width > 0:
1720 msg = util.ellipsis(msg, width)
1720 msg = util.ellipsis(msg, width)
1721 else:
1721 else:
1722 msg = ''
1722 msg = ''
1723 self.ui.write(patchname, label='qseries.' + state)
1723 self.ui.write(patchname, label='qseries.' + state)
1724 self.ui.write(': ')
1724 self.ui.write(': ')
1725 self.ui.write(msg, label='qseries.message.' + state)
1725 self.ui.write(msg, label='qseries.message.' + state)
1726 else:
1726 else:
1727 self.ui.write(patchname, label='qseries.' + state)
1727 self.ui.write(patchname, label='qseries.' + state)
1728 self.ui.write('\n')
1728 self.ui.write('\n')
1729
1729
1730 applied = set([p.name for p in self.applied])
1730 applied = set([p.name for p in self.applied])
1731 if length is None:
1731 if length is None:
1732 length = len(self.series) - start
1732 length = len(self.series) - start
1733 if not missing:
1733 if not missing:
1734 if self.ui.verbose:
1734 if self.ui.verbose:
1735 idxwidth = len(str(start + length - 1))
1735 idxwidth = len(str(start + length - 1))
1736 for i in xrange(start, start + length):
1736 for i in xrange(start, start + length):
1737 patch = self.series[i]
1737 patch = self.series[i]
1738 if patch in applied:
1738 if patch in applied:
1739 char, state = 'A', 'applied'
1739 char, state = 'A', 'applied'
1740 elif self.pushable(i)[0]:
1740 elif self.pushable(i)[0]:
1741 char, state = 'U', 'unapplied'
1741 char, state = 'U', 'unapplied'
1742 else:
1742 else:
1743 char, state = 'G', 'guarded'
1743 char, state = 'G', 'guarded'
1744 pfx = ''
1744 pfx = ''
1745 if self.ui.verbose:
1745 if self.ui.verbose:
1746 pfx = '%*d %s ' % (idxwidth, i, char)
1746 pfx = '%*d %s ' % (idxwidth, i, char)
1747 elif status and status != char:
1747 elif status and status != char:
1748 continue
1748 continue
1749 displayname(pfx, patch, state)
1749 displayname(pfx, patch, state)
1750 else:
1750 else:
1751 msng_list = []
1751 msng_list = []
1752 for root, dirs, files in os.walk(self.path):
1752 for root, dirs, files in os.walk(self.path):
1753 d = root[len(self.path) + 1:]
1753 d = root[len(self.path) + 1:]
1754 for f in files:
1754 for f in files:
1755 fl = os.path.join(d, f)
1755 fl = os.path.join(d, f)
1756 if (fl not in self.series and
1756 if (fl not in self.series and
1757 fl not in (self.statuspath, self.seriespath,
1757 fl not in (self.statuspath, self.seriespath,
1758 self.guardspath)
1758 self.guardspath)
1759 and not fl.startswith('.')):
1759 and not fl.startswith('.')):
1760 msng_list.append(fl)
1760 msng_list.append(fl)
1761 for x in sorted(msng_list):
1761 for x in sorted(msng_list):
1762 pfx = self.ui.verbose and ('D ') or ''
1762 pfx = self.ui.verbose and ('D ') or ''
1763 displayname(pfx, x, 'missing')
1763 displayname(pfx, x, 'missing')
1764
1764
1765 def issaveline(self, l):
1765 def issaveline(self, l):
1766 if l.name == '.hg.patches.save.line':
1766 if l.name == '.hg.patches.save.line':
1767 return True
1767 return True
1768
1768
1769 def qrepo(self, create=False):
1769 def qrepo(self, create=False):
1770 ui = self.ui.copy()
1770 ui = self.ui.copy()
1771 ui.setconfig('paths', 'default', '', overlay=False)
1771 ui.setconfig('paths', 'default', '', overlay=False)
1772 ui.setconfig('paths', 'default-push', '', overlay=False)
1772 ui.setconfig('paths', 'default-push', '', overlay=False)
1773 if create or os.path.isdir(self.join(".hg")):
1773 if create or os.path.isdir(self.join(".hg")):
1774 return hg.repository(ui, path=self.path, create=create)
1774 return hg.repository(ui, path=self.path, create=create)
1775
1775
1776 def restore(self, repo, rev, delete=None, qupdate=None):
1776 def restore(self, repo, rev, delete=None, qupdate=None):
1777 desc = repo[rev].description().strip()
1777 desc = repo[rev].description().strip()
1778 lines = desc.splitlines()
1778 lines = desc.splitlines()
1779 i = 0
1779 i = 0
1780 datastart = None
1780 datastart = None
1781 series = []
1781 series = []
1782 applied = []
1782 applied = []
1783 qpp = None
1783 qpp = None
1784 for i, line in enumerate(lines):
1784 for i, line in enumerate(lines):
1785 if line == 'Patch Data:':
1785 if line == 'Patch Data:':
1786 datastart = i + 1
1786 datastart = i + 1
1787 elif line.startswith('Dirstate:'):
1787 elif line.startswith('Dirstate:'):
1788 l = line.rstrip()
1788 l = line.rstrip()
1789 l = l[10:].split(' ')
1789 l = l[10:].split(' ')
1790 qpp = [bin(x) for x in l]
1790 qpp = [bin(x) for x in l]
1791 elif datastart is not None:
1791 elif datastart is not None:
1792 l = line.rstrip()
1792 l = line.rstrip()
1793 n, name = l.split(':', 1)
1793 n, name = l.split(':', 1)
1794 if n:
1794 if n:
1795 applied.append(statusentry(bin(n), name))
1795 applied.append(statusentry(bin(n), name))
1796 else:
1796 else:
1797 series.append(l)
1797 series.append(l)
1798 if datastart is None:
1798 if datastart is None:
1799 self.ui.warn(_("no saved patch data found\n"))
1799 self.ui.warn(_("no saved patch data found\n"))
1800 return 1
1800 return 1
1801 self.ui.warn(_("restoring status: %s\n") % lines[0])
1801 self.ui.warn(_("restoring status: %s\n") % lines[0])
1802 self.fullseries = series
1802 self.fullseries = series
1803 self.applied = applied
1803 self.applied = applied
1804 self.parseseries()
1804 self.parseseries()
1805 self.seriesdirty = True
1805 self.seriesdirty = True
1806 self.applieddirty = True
1806 self.applieddirty = True
1807 heads = repo.changelog.heads()
1807 heads = repo.changelog.heads()
1808 if delete:
1808 if delete:
1809 if rev not in heads:
1809 if rev not in heads:
1810 self.ui.warn(_("save entry has children, leaving it alone\n"))
1810 self.ui.warn(_("save entry has children, leaving it alone\n"))
1811 else:
1811 else:
1812 self.ui.warn(_("removing save entry %s\n") % short(rev))
1812 self.ui.warn(_("removing save entry %s\n") % short(rev))
1813 pp = repo.dirstate.parents()
1813 pp = repo.dirstate.parents()
1814 if rev in pp:
1814 if rev in pp:
1815 update = True
1815 update = True
1816 else:
1816 else:
1817 update = False
1817 update = False
1818 self.strip(repo, [rev], update=update, backup='strip')
1818 self.strip(repo, [rev], update=update, backup='strip')
1819 if qpp:
1819 if qpp:
1820 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1820 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1821 (short(qpp[0]), short(qpp[1])))
1821 (short(qpp[0]), short(qpp[1])))
1822 if qupdate:
1822 if qupdate:
1823 self.ui.status(_("updating queue directory\n"))
1823 self.ui.status(_("updating queue directory\n"))
1824 r = self.qrepo()
1824 r = self.qrepo()
1825 if not r:
1825 if not r:
1826 self.ui.warn(_("unable to load queue repository\n"))
1826 self.ui.warn(_("unable to load queue repository\n"))
1827 return 1
1827 return 1
1828 hg.clean(r, qpp[0])
1828 hg.clean(r, qpp[0])
1829
1829
1830 def save(self, repo, msg=None):
1830 def save(self, repo, msg=None):
1831 if not self.applied:
1831 if not self.applied:
1832 self.ui.warn(_("save: no patches applied, exiting\n"))
1832 self.ui.warn(_("save: no patches applied, exiting\n"))
1833 return 1
1833 return 1
1834 if self.issaveline(self.applied[-1]):
1834 if self.issaveline(self.applied[-1]):
1835 self.ui.warn(_("status is already saved\n"))
1835 self.ui.warn(_("status is already saved\n"))
1836 return 1
1836 return 1
1837
1837
1838 if not msg:
1838 if not msg:
1839 msg = _("hg patches saved state")
1839 msg = _("hg patches saved state")
1840 else:
1840 else:
1841 msg = "hg patches: " + msg.rstrip('\r\n')
1841 msg = "hg patches: " + msg.rstrip('\r\n')
1842 r = self.qrepo()
1842 r = self.qrepo()
1843 if r:
1843 if r:
1844 pp = r.dirstate.parents()
1844 pp = r.dirstate.parents()
1845 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1845 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1846 msg += "\n\nPatch Data:\n"
1846 msg += "\n\nPatch Data:\n"
1847 msg += ''.join('%s\n' % x for x in self.applied)
1847 msg += ''.join('%s\n' % x for x in self.applied)
1848 msg += ''.join(':%s\n' % x for x in self.fullseries)
1848 msg += ''.join(':%s\n' % x for x in self.fullseries)
1849 n = repo.commit(msg, force=True)
1849 n = repo.commit(msg, force=True)
1850 if not n:
1850 if not n:
1851 self.ui.warn(_("repo commit failed\n"))
1851 self.ui.warn(_("repo commit failed\n"))
1852 return 1
1852 return 1
1853 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1853 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1854 self.applieddirty = True
1854 self.applieddirty = True
1855 self.removeundo(repo)
1855 self.removeundo(repo)
1856
1856
1857 def fullseriesend(self):
1857 def fullseriesend(self):
1858 if self.applied:
1858 if self.applied:
1859 p = self.applied[-1].name
1859 p = self.applied[-1].name
1860 end = self.findseries(p)
1860 end = self.findseries(p)
1861 if end is None:
1861 if end is None:
1862 return len(self.fullseries)
1862 return len(self.fullseries)
1863 return end + 1
1863 return end + 1
1864 return 0
1864 return 0
1865
1865
1866 def seriesend(self, all_patches=False):
1866 def seriesend(self, all_patches=False):
1867 """If all_patches is False, return the index of the next pushable patch
1867 """If all_patches is False, return the index of the next pushable patch
1868 in the series, or the series length. If all_patches is True, return the
1868 in the series, or the series length. If all_patches is True, return the
1869 index of the first patch past the last applied one.
1869 index of the first patch past the last applied one.
1870 """
1870 """
1871 end = 0
1871 end = 0
1872 def next(start):
1872 def next(start):
1873 if all_patches or start >= len(self.series):
1873 if all_patches or start >= len(self.series):
1874 return start
1874 return start
1875 for i in xrange(start, len(self.series)):
1875 for i in xrange(start, len(self.series)):
1876 p, reason = self.pushable(i)
1876 p, reason = self.pushable(i)
1877 if p:
1877 if p:
1878 return i
1878 return i
1879 self.explainpushable(i)
1879 self.explainpushable(i)
1880 return len(self.series)
1880 return len(self.series)
1881 if self.applied:
1881 if self.applied:
1882 p = self.applied[-1].name
1882 p = self.applied[-1].name
1883 try:
1883 try:
1884 end = self.series.index(p)
1884 end = self.series.index(p)
1885 except ValueError:
1885 except ValueError:
1886 return 0
1886 return 0
1887 return next(end + 1)
1887 return next(end + 1)
1888 return next(end)
1888 return next(end)
1889
1889
1890 def appliedname(self, index):
1890 def appliedname(self, index):
1891 pname = self.applied[index].name
1891 pname = self.applied[index].name
1892 if not self.ui.verbose:
1892 if not self.ui.verbose:
1893 p = pname
1893 p = pname
1894 else:
1894 else:
1895 p = str(self.series.index(pname)) + " " + pname
1895 p = str(self.series.index(pname)) + " " + pname
1896 return p
1896 return p
1897
1897
1898 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1898 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1899 force=None, git=False):
1899 force=None, git=False):
1900 def checkseries(patchname):
1900 def checkseries(patchname):
1901 if patchname in self.series:
1901 if patchname in self.series:
1902 raise util.Abort(_('patch %s is already in the series file')
1902 raise util.Abort(_('patch %s is already in the series file')
1903 % patchname)
1903 % patchname)
1904
1904
1905 if rev:
1905 if rev:
1906 if files:
1906 if files:
1907 raise util.Abort(_('option "-r" not valid when importing '
1907 raise util.Abort(_('option "-r" not valid when importing '
1908 'files'))
1908 'files'))
1909 rev = scmutil.revrange(repo, rev)
1909 rev = scmutil.revrange(repo, rev)
1910 rev.sort(reverse=True)
1910 rev.sort(reverse=True)
1911 elif not files:
1911 elif not files:
1912 raise util.Abort(_('no files or revisions specified'))
1912 raise util.Abort(_('no files or revisions specified'))
1913 if (len(files) > 1 or len(rev) > 1) and patchname:
1913 if (len(files) > 1 or len(rev) > 1) and patchname:
1914 raise util.Abort(_('option "-n" not valid when importing multiple '
1914 raise util.Abort(_('option "-n" not valid when importing multiple '
1915 'patches'))
1915 'patches'))
1916 imported = []
1916 imported = []
1917 if rev:
1917 if rev:
1918 # If mq patches are applied, we can only import revisions
1918 # If mq patches are applied, we can only import revisions
1919 # that form a linear path to qbase.
1919 # that form a linear path to qbase.
1920 # Otherwise, they should form a linear path to a head.
1920 # Otherwise, they should form a linear path to a head.
1921 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1921 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1922 if len(heads) > 1:
1922 if len(heads) > 1:
1923 raise util.Abort(_('revision %d is the root of more than one '
1923 raise util.Abort(_('revision %d is the root of more than one '
1924 'branch') % rev[-1])
1924 'branch') % rev[-1])
1925 if self.applied:
1925 if self.applied:
1926 base = repo.changelog.node(rev[0])
1926 base = repo.changelog.node(rev[0])
1927 if base in [n.node for n in self.applied]:
1927 if base in [n.node for n in self.applied]:
1928 raise util.Abort(_('revision %d is already managed')
1928 raise util.Abort(_('revision %d is already managed')
1929 % rev[0])
1929 % rev[0])
1930 if heads != [self.applied[-1].node]:
1930 if heads != [self.applied[-1].node]:
1931 raise util.Abort(_('revision %d is not the parent of '
1931 raise util.Abort(_('revision %d is not the parent of '
1932 'the queue') % rev[0])
1932 'the queue') % rev[0])
1933 base = repo.changelog.rev(self.applied[0].node)
1933 base = repo.changelog.rev(self.applied[0].node)
1934 lastparent = repo.changelog.parentrevs(base)[0]
1934 lastparent = repo.changelog.parentrevs(base)[0]
1935 else:
1935 else:
1936 if heads != [repo.changelog.node(rev[0])]:
1936 if heads != [repo.changelog.node(rev[0])]:
1937 raise util.Abort(_('revision %d has unmanaged children')
1937 raise util.Abort(_('revision %d has unmanaged children')
1938 % rev[0])
1938 % rev[0])
1939 lastparent = None
1939 lastparent = None
1940
1940
1941 diffopts = self.diffopts({'git': git})
1941 diffopts = self.diffopts({'git': git})
1942 for r in rev:
1942 for r in rev:
1943 if not repo[r].mutable():
1943 if not repo[r].mutable():
1944 raise util.Abort(_('revision %d is not mutable') % r,
1944 raise util.Abort(_('revision %d is not mutable') % r,
1945 hint=_('see "hg help phases" for details'))
1945 hint=_('see "hg help phases" for details'))
1946 p1, p2 = repo.changelog.parentrevs(r)
1946 p1, p2 = repo.changelog.parentrevs(r)
1947 n = repo.changelog.node(r)
1947 n = repo.changelog.node(r)
1948 if p2 != nullrev:
1948 if p2 != nullrev:
1949 raise util.Abort(_('cannot import merge revision %d') % r)
1949 raise util.Abort(_('cannot import merge revision %d') % r)
1950 if lastparent and lastparent != r:
1950 if lastparent and lastparent != r:
1951 raise util.Abort(_('revision %d is not the parent of %d')
1951 raise util.Abort(_('revision %d is not the parent of %d')
1952 % (r, lastparent))
1952 % (r, lastparent))
1953 lastparent = p1
1953 lastparent = p1
1954
1954
1955 if not patchname:
1955 if not patchname:
1956 patchname = normname('%d.diff' % r)
1956 patchname = normname('%d.diff' % r)
1957 checkseries(patchname)
1957 checkseries(patchname)
1958 self.checkpatchname(patchname, force)
1958 self.checkpatchname(patchname, force)
1959 self.fullseries.insert(0, patchname)
1959 self.fullseries.insert(0, patchname)
1960
1960
1961 patchf = self.opener(patchname, "w")
1961 patchf = self.opener(patchname, "w")
1962 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1962 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1963 patchf.close()
1963 patchf.close()
1964
1964
1965 se = statusentry(n, patchname)
1965 se = statusentry(n, patchname)
1966 self.applied.insert(0, se)
1966 self.applied.insert(0, se)
1967
1967
1968 self.added.append(patchname)
1968 self.added.append(patchname)
1969 imported.append(patchname)
1969 imported.append(patchname)
1970 patchname = None
1970 patchname = None
1971 if rev and repo.ui.configbool('mq', 'secret', False):
1971 if rev and repo.ui.configbool('mq', 'secret', False):
1972 # if we added anything with --rev, we must move the secret root
1972 # if we added anything with --rev, we must move the secret root
1973 phases.retractboundary(repo, phases.secret, [n])
1973 phases.retractboundary(repo, phases.secret, [n])
1974 self.parseseries()
1974 self.parseseries()
1975 self.applieddirty = True
1975 self.applieddirty = True
1976 self.seriesdirty = True
1976 self.seriesdirty = True
1977
1977
1978 for i, filename in enumerate(files):
1978 for i, filename in enumerate(files):
1979 if existing:
1979 if existing:
1980 if filename == '-':
1980 if filename == '-':
1981 raise util.Abort(_('-e is incompatible with import from -'))
1981 raise util.Abort(_('-e is incompatible with import from -'))
1982 filename = normname(filename)
1982 filename = normname(filename)
1983 self.checkreservedname(filename)
1983 self.checkreservedname(filename)
1984 originpath = self.join(filename)
1984 originpath = self.join(filename)
1985 if not os.path.isfile(originpath):
1985 if not os.path.isfile(originpath):
1986 raise util.Abort(_("patch %s does not exist") % filename)
1986 raise util.Abort(_("patch %s does not exist") % filename)
1987
1987
1988 if patchname:
1988 if patchname:
1989 self.checkpatchname(patchname, force)
1989 self.checkpatchname(patchname, force)
1990
1990
1991 self.ui.write(_('renaming %s to %s\n')
1991 self.ui.write(_('renaming %s to %s\n')
1992 % (filename, patchname))
1992 % (filename, patchname))
1993 util.rename(originpath, self.join(patchname))
1993 util.rename(originpath, self.join(patchname))
1994 else:
1994 else:
1995 patchname = filename
1995 patchname = filename
1996
1996
1997 else:
1997 else:
1998 if filename == '-' and not patchname:
1998 if filename == '-' and not patchname:
1999 raise util.Abort(_('need --name to import a patch from -'))
1999 raise util.Abort(_('need --name to import a patch from -'))
2000 elif not patchname:
2000 elif not patchname:
2001 patchname = normname(os.path.basename(filename.rstrip('/')))
2001 patchname = normname(os.path.basename(filename.rstrip('/')))
2002 self.checkpatchname(patchname, force)
2002 self.checkpatchname(patchname, force)
2003 try:
2003 try:
2004 if filename == '-':
2004 if filename == '-':
2005 text = self.ui.fin.read()
2005 text = self.ui.fin.read()
2006 else:
2006 else:
2007 fp = url.open(self.ui, filename)
2007 fp = hg.openpath(self.ui, filename)
2008 text = fp.read()
2008 text = fp.read()
2009 fp.close()
2009 fp.close()
2010 except (OSError, IOError):
2010 except (OSError, IOError):
2011 raise util.Abort(_("unable to read file %s") % filename)
2011 raise util.Abort(_("unable to read file %s") % filename)
2012 patchf = self.opener(patchname, "w")
2012 patchf = self.opener(patchname, "w")
2013 patchf.write(text)
2013 patchf.write(text)
2014 patchf.close()
2014 patchf.close()
2015 if not force:
2015 if not force:
2016 checkseries(patchname)
2016 checkseries(patchname)
2017 if patchname not in self.series:
2017 if patchname not in self.series:
2018 index = self.fullseriesend() + i
2018 index = self.fullseriesend() + i
2019 self.fullseries[index:index] = [patchname]
2019 self.fullseries[index:index] = [patchname]
2020 self.parseseries()
2020 self.parseseries()
2021 self.seriesdirty = True
2021 self.seriesdirty = True
2022 self.ui.warn(_("adding %s to series file\n") % patchname)
2022 self.ui.warn(_("adding %s to series file\n") % patchname)
2023 self.added.append(patchname)
2023 self.added.append(patchname)
2024 imported.append(patchname)
2024 imported.append(patchname)
2025 patchname = None
2025 patchname = None
2026
2026
2027 self.removeundo(repo)
2027 self.removeundo(repo)
2028 return imported
2028 return imported
2029
2029
2030 def fixkeepchangesopts(ui, opts):
2030 def fixkeepchangesopts(ui, opts):
2031 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2031 if (not ui.configbool('mq', 'keepchanges') or opts.get('force')
2032 or opts.get('exact')):
2032 or opts.get('exact')):
2033 return opts
2033 return opts
2034 opts = dict(opts)
2034 opts = dict(opts)
2035 opts['keep_changes'] = True
2035 opts['keep_changes'] = True
2036 return opts
2036 return opts
2037
2037
2038 @command("qdelete|qremove|qrm",
2038 @command("qdelete|qremove|qrm",
2039 [('k', 'keep', None, _('keep patch file')),
2039 [('k', 'keep', None, _('keep patch file')),
2040 ('r', 'rev', [],
2040 ('r', 'rev', [],
2041 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2041 _('stop managing a revision (DEPRECATED)'), _('REV'))],
2042 _('hg qdelete [-k] [PATCH]...'))
2042 _('hg qdelete [-k] [PATCH]...'))
2043 def delete(ui, repo, *patches, **opts):
2043 def delete(ui, repo, *patches, **opts):
2044 """remove patches from queue
2044 """remove patches from queue
2045
2045
2046 The patches must not be applied, and at least one patch is required. Exact
2046 The patches must not be applied, and at least one patch is required. Exact
2047 patch identifiers must be given. With -k/--keep, the patch files are
2047 patch identifiers must be given. With -k/--keep, the patch files are
2048 preserved in the patch directory.
2048 preserved in the patch directory.
2049
2049
2050 To stop managing a patch and move it into permanent history,
2050 To stop managing a patch and move it into permanent history,
2051 use the :hg:`qfinish` command."""
2051 use the :hg:`qfinish` command."""
2052 q = repo.mq
2052 q = repo.mq
2053 q.delete(repo, patches, opts)
2053 q.delete(repo, patches, opts)
2054 q.savedirty()
2054 q.savedirty()
2055 return 0
2055 return 0
2056
2056
2057 @command("qapplied",
2057 @command("qapplied",
2058 [('1', 'last', None, _('show only the preceding applied patch'))
2058 [('1', 'last', None, _('show only the preceding applied patch'))
2059 ] + seriesopts,
2059 ] + seriesopts,
2060 _('hg qapplied [-1] [-s] [PATCH]'))
2060 _('hg qapplied [-1] [-s] [PATCH]'))
2061 def applied(ui, repo, patch=None, **opts):
2061 def applied(ui, repo, patch=None, **opts):
2062 """print the patches already applied
2062 """print the patches already applied
2063
2063
2064 Returns 0 on success."""
2064 Returns 0 on success."""
2065
2065
2066 q = repo.mq
2066 q = repo.mq
2067
2067
2068 if patch:
2068 if patch:
2069 if patch not in q.series:
2069 if patch not in q.series:
2070 raise util.Abort(_("patch %s is not in series file") % patch)
2070 raise util.Abort(_("patch %s is not in series file") % patch)
2071 end = q.series.index(patch) + 1
2071 end = q.series.index(patch) + 1
2072 else:
2072 else:
2073 end = q.seriesend(True)
2073 end = q.seriesend(True)
2074
2074
2075 if opts.get('last') and not end:
2075 if opts.get('last') and not end:
2076 ui.write(_("no patches applied\n"))
2076 ui.write(_("no patches applied\n"))
2077 return 1
2077 return 1
2078 elif opts.get('last') and end == 1:
2078 elif opts.get('last') and end == 1:
2079 ui.write(_("only one patch applied\n"))
2079 ui.write(_("only one patch applied\n"))
2080 return 1
2080 return 1
2081 elif opts.get('last'):
2081 elif opts.get('last'):
2082 start = end - 2
2082 start = end - 2
2083 end = 1
2083 end = 1
2084 else:
2084 else:
2085 start = 0
2085 start = 0
2086
2086
2087 q.qseries(repo, length=end, start=start, status='A',
2087 q.qseries(repo, length=end, start=start, status='A',
2088 summary=opts.get('summary'))
2088 summary=opts.get('summary'))
2089
2089
2090
2090
2091 @command("qunapplied",
2091 @command("qunapplied",
2092 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2092 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
2093 _('hg qunapplied [-1] [-s] [PATCH]'))
2093 _('hg qunapplied [-1] [-s] [PATCH]'))
2094 def unapplied(ui, repo, patch=None, **opts):
2094 def unapplied(ui, repo, patch=None, **opts):
2095 """print the patches not yet applied
2095 """print the patches not yet applied
2096
2096
2097 Returns 0 on success."""
2097 Returns 0 on success."""
2098
2098
2099 q = repo.mq
2099 q = repo.mq
2100 if patch:
2100 if patch:
2101 if patch not in q.series:
2101 if patch not in q.series:
2102 raise util.Abort(_("patch %s is not in series file") % patch)
2102 raise util.Abort(_("patch %s is not in series file") % patch)
2103 start = q.series.index(patch) + 1
2103 start = q.series.index(patch) + 1
2104 else:
2104 else:
2105 start = q.seriesend(True)
2105 start = q.seriesend(True)
2106
2106
2107 if start == len(q.series) and opts.get('first'):
2107 if start == len(q.series) and opts.get('first'):
2108 ui.write(_("all patches applied\n"))
2108 ui.write(_("all patches applied\n"))
2109 return 1
2109 return 1
2110
2110
2111 length = opts.get('first') and 1 or None
2111 length = opts.get('first') and 1 or None
2112 q.qseries(repo, start=start, length=length, status='U',
2112 q.qseries(repo, start=start, length=length, status='U',
2113 summary=opts.get('summary'))
2113 summary=opts.get('summary'))
2114
2114
2115 @command("qimport",
2115 @command("qimport",
2116 [('e', 'existing', None, _('import file in patch directory')),
2116 [('e', 'existing', None, _('import file in patch directory')),
2117 ('n', 'name', '',
2117 ('n', 'name', '',
2118 _('name of patch file'), _('NAME')),
2118 _('name of patch file'), _('NAME')),
2119 ('f', 'force', None, _('overwrite existing files')),
2119 ('f', 'force', None, _('overwrite existing files')),
2120 ('r', 'rev', [],
2120 ('r', 'rev', [],
2121 _('place existing revisions under mq control'), _('REV')),
2121 _('place existing revisions under mq control'), _('REV')),
2122 ('g', 'git', None, _('use git extended diff format')),
2122 ('g', 'git', None, _('use git extended diff format')),
2123 ('P', 'push', None, _('qpush after importing'))],
2123 ('P', 'push', None, _('qpush after importing'))],
2124 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2124 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... [FILE]...'))
2125 def qimport(ui, repo, *filename, **opts):
2125 def qimport(ui, repo, *filename, **opts):
2126 """import a patch or existing changeset
2126 """import a patch or existing changeset
2127
2127
2128 The patch is inserted into the series after the last applied
2128 The patch is inserted into the series after the last applied
2129 patch. If no patches have been applied, qimport prepends the patch
2129 patch. If no patches have been applied, qimport prepends the patch
2130 to the series.
2130 to the series.
2131
2131
2132 The patch will have the same name as its source file unless you
2132 The patch will have the same name as its source file unless you
2133 give it a new one with -n/--name.
2133 give it a new one with -n/--name.
2134
2134
2135 You can register an existing patch inside the patch directory with
2135 You can register an existing patch inside the patch directory with
2136 the -e/--existing flag.
2136 the -e/--existing flag.
2137
2137
2138 With -f/--force, an existing patch of the same name will be
2138 With -f/--force, an existing patch of the same name will be
2139 overwritten.
2139 overwritten.
2140
2140
2141 An existing changeset may be placed under mq control with -r/--rev
2141 An existing changeset may be placed under mq control with -r/--rev
2142 (e.g. qimport --rev tip -n patch will place tip under mq control).
2142 (e.g. qimport --rev tip -n patch will place tip under mq control).
2143 With -g/--git, patches imported with --rev will use the git diff
2143 With -g/--git, patches imported with --rev will use the git diff
2144 format. See the diffs help topic for information on why this is
2144 format. See the diffs help topic for information on why this is
2145 important for preserving rename/copy information and permission
2145 important for preserving rename/copy information and permission
2146 changes. Use :hg:`qfinish` to remove changesets from mq control.
2146 changes. Use :hg:`qfinish` to remove changesets from mq control.
2147
2147
2148 To import a patch from standard input, pass - as the patch file.
2148 To import a patch from standard input, pass - as the patch file.
2149 When importing from standard input, a patch name must be specified
2149 When importing from standard input, a patch name must be specified
2150 using the --name flag.
2150 using the --name flag.
2151
2151
2152 To import an existing patch while renaming it::
2152 To import an existing patch while renaming it::
2153
2153
2154 hg qimport -e existing-patch -n new-name
2154 hg qimport -e existing-patch -n new-name
2155
2155
2156 Returns 0 if import succeeded.
2156 Returns 0 if import succeeded.
2157 """
2157 """
2158 lock = repo.lock() # cause this may move phase
2158 lock = repo.lock() # cause this may move phase
2159 try:
2159 try:
2160 q = repo.mq
2160 q = repo.mq
2161 try:
2161 try:
2162 imported = q.qimport(
2162 imported = q.qimport(
2163 repo, filename, patchname=opts.get('name'),
2163 repo, filename, patchname=opts.get('name'),
2164 existing=opts.get('existing'), force=opts.get('force'),
2164 existing=opts.get('existing'), force=opts.get('force'),
2165 rev=opts.get('rev'), git=opts.get('git'))
2165 rev=opts.get('rev'), git=opts.get('git'))
2166 finally:
2166 finally:
2167 q.savedirty()
2167 q.savedirty()
2168 finally:
2168 finally:
2169 lock.release()
2169 lock.release()
2170
2170
2171 if imported and opts.get('push') and not opts.get('rev'):
2171 if imported and opts.get('push') and not opts.get('rev'):
2172 return q.push(repo, imported[-1])
2172 return q.push(repo, imported[-1])
2173 return 0
2173 return 0
2174
2174
2175 def qinit(ui, repo, create):
2175 def qinit(ui, repo, create):
2176 """initialize a new queue repository
2176 """initialize a new queue repository
2177
2177
2178 This command also creates a series file for ordering patches, and
2178 This command also creates a series file for ordering patches, and
2179 an mq-specific .hgignore file in the queue repository, to exclude
2179 an mq-specific .hgignore file in the queue repository, to exclude
2180 the status and guards files (these contain mostly transient state).
2180 the status and guards files (these contain mostly transient state).
2181
2181
2182 Returns 0 if initialization succeeded."""
2182 Returns 0 if initialization succeeded."""
2183 q = repo.mq
2183 q = repo.mq
2184 r = q.init(repo, create)
2184 r = q.init(repo, create)
2185 q.savedirty()
2185 q.savedirty()
2186 if r:
2186 if r:
2187 if not os.path.exists(r.wjoin('.hgignore')):
2187 if not os.path.exists(r.wjoin('.hgignore')):
2188 fp = r.wopener('.hgignore', 'w')
2188 fp = r.wopener('.hgignore', 'w')
2189 fp.write('^\\.hg\n')
2189 fp.write('^\\.hg\n')
2190 fp.write('^\\.mq\n')
2190 fp.write('^\\.mq\n')
2191 fp.write('syntax: glob\n')
2191 fp.write('syntax: glob\n')
2192 fp.write('status\n')
2192 fp.write('status\n')
2193 fp.write('guards\n')
2193 fp.write('guards\n')
2194 fp.close()
2194 fp.close()
2195 if not os.path.exists(r.wjoin('series')):
2195 if not os.path.exists(r.wjoin('series')):
2196 r.wopener('series', 'w').close()
2196 r.wopener('series', 'w').close()
2197 r[None].add(['.hgignore', 'series'])
2197 r[None].add(['.hgignore', 'series'])
2198 commands.add(ui, r)
2198 commands.add(ui, r)
2199 return 0
2199 return 0
2200
2200
2201 @command("^qinit",
2201 @command("^qinit",
2202 [('c', 'create-repo', None, _('create queue repository'))],
2202 [('c', 'create-repo', None, _('create queue repository'))],
2203 _('hg qinit [-c]'))
2203 _('hg qinit [-c]'))
2204 def init(ui, repo, **opts):
2204 def init(ui, repo, **opts):
2205 """init a new queue repository (DEPRECATED)
2205 """init a new queue repository (DEPRECATED)
2206
2206
2207 The queue repository is unversioned by default. If
2207 The queue repository is unversioned by default. If
2208 -c/--create-repo is specified, qinit will create a separate nested
2208 -c/--create-repo is specified, qinit will create a separate nested
2209 repository for patches (qinit -c may also be run later to convert
2209 repository for patches (qinit -c may also be run later to convert
2210 an unversioned patch repository into a versioned one). You can use
2210 an unversioned patch repository into a versioned one). You can use
2211 qcommit to commit changes to this queue repository.
2211 qcommit to commit changes to this queue repository.
2212
2212
2213 This command is deprecated. Without -c, it's implied by other relevant
2213 This command is deprecated. Without -c, it's implied by other relevant
2214 commands. With -c, use :hg:`init --mq` instead."""
2214 commands. With -c, use :hg:`init --mq` instead."""
2215 return qinit(ui, repo, create=opts.get('create_repo'))
2215 return qinit(ui, repo, create=opts.get('create_repo'))
2216
2216
2217 @command("qclone",
2217 @command("qclone",
2218 [('', 'pull', None, _('use pull protocol to copy metadata')),
2218 [('', 'pull', None, _('use pull protocol to copy metadata')),
2219 ('U', 'noupdate', None,
2219 ('U', 'noupdate', None,
2220 _('do not update the new working directories')),
2220 _('do not update the new working directories')),
2221 ('', 'uncompressed', None,
2221 ('', 'uncompressed', None,
2222 _('use uncompressed transfer (fast over LAN)')),
2222 _('use uncompressed transfer (fast over LAN)')),
2223 ('p', 'patches', '',
2223 ('p', 'patches', '',
2224 _('location of source patch repository'), _('REPO')),
2224 _('location of source patch repository'), _('REPO')),
2225 ] + commands.remoteopts,
2225 ] + commands.remoteopts,
2226 _('hg qclone [OPTION]... SOURCE [DEST]'))
2226 _('hg qclone [OPTION]... SOURCE [DEST]'))
2227 def clone(ui, source, dest=None, **opts):
2227 def clone(ui, source, dest=None, **opts):
2228 '''clone main and patch repository at same time
2228 '''clone main and patch repository at same time
2229
2229
2230 If source is local, destination will have no patches applied. If
2230 If source is local, destination will have no patches applied. If
2231 source is remote, this command can not check if patches are
2231 source is remote, this command can not check if patches are
2232 applied in source, so cannot guarantee that patches are not
2232 applied in source, so cannot guarantee that patches are not
2233 applied in destination. If you clone remote repository, be sure
2233 applied in destination. If you clone remote repository, be sure
2234 before that it has no patches applied.
2234 before that it has no patches applied.
2235
2235
2236 Source patch repository is looked for in <src>/.hg/patches by
2236 Source patch repository is looked for in <src>/.hg/patches by
2237 default. Use -p <url> to change.
2237 default. Use -p <url> to change.
2238
2238
2239 The patch directory must be a nested Mercurial repository, as
2239 The patch directory must be a nested Mercurial repository, as
2240 would be created by :hg:`init --mq`.
2240 would be created by :hg:`init --mq`.
2241
2241
2242 Return 0 on success.
2242 Return 0 on success.
2243 '''
2243 '''
2244 def patchdir(repo):
2244 def patchdir(repo):
2245 """compute a patch repo url from a repo object"""
2245 """compute a patch repo url from a repo object"""
2246 url = repo.url()
2246 url = repo.url()
2247 if url.endswith('/'):
2247 if url.endswith('/'):
2248 url = url[:-1]
2248 url = url[:-1]
2249 return url + '/.hg/patches'
2249 return url + '/.hg/patches'
2250
2250
2251 # main repo (destination and sources)
2251 # main repo (destination and sources)
2252 if dest is None:
2252 if dest is None:
2253 dest = hg.defaultdest(source)
2253 dest = hg.defaultdest(source)
2254 sr = hg.peer(ui, opts, ui.expandpath(source))
2254 sr = hg.peer(ui, opts, ui.expandpath(source))
2255
2255
2256 # patches repo (source only)
2256 # patches repo (source only)
2257 if opts.get('patches'):
2257 if opts.get('patches'):
2258 patchespath = ui.expandpath(opts.get('patches'))
2258 patchespath = ui.expandpath(opts.get('patches'))
2259 else:
2259 else:
2260 patchespath = patchdir(sr)
2260 patchespath = patchdir(sr)
2261 try:
2261 try:
2262 hg.peer(ui, opts, patchespath)
2262 hg.peer(ui, opts, patchespath)
2263 except error.RepoError:
2263 except error.RepoError:
2264 raise util.Abort(_('versioned patch repository not found'
2264 raise util.Abort(_('versioned patch repository not found'
2265 ' (see init --mq)'))
2265 ' (see init --mq)'))
2266 qbase, destrev = None, None
2266 qbase, destrev = None, None
2267 if sr.local():
2267 if sr.local():
2268 repo = sr.local()
2268 repo = sr.local()
2269 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2269 if repo.mq.applied and repo[qbase].phase() != phases.secret:
2270 qbase = repo.mq.applied[0].node
2270 qbase = repo.mq.applied[0].node
2271 if not hg.islocal(dest):
2271 if not hg.islocal(dest):
2272 heads = set(repo.heads())
2272 heads = set(repo.heads())
2273 destrev = list(heads.difference(repo.heads(qbase)))
2273 destrev = list(heads.difference(repo.heads(qbase)))
2274 destrev.append(repo.changelog.parents(qbase)[0])
2274 destrev.append(repo.changelog.parents(qbase)[0])
2275 elif sr.capable('lookup'):
2275 elif sr.capable('lookup'):
2276 try:
2276 try:
2277 qbase = sr.lookup('qbase')
2277 qbase = sr.lookup('qbase')
2278 except error.RepoError:
2278 except error.RepoError:
2279 pass
2279 pass
2280
2280
2281 ui.note(_('cloning main repository\n'))
2281 ui.note(_('cloning main repository\n'))
2282 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2282 sr, dr = hg.clone(ui, opts, sr.url(), dest,
2283 pull=opts.get('pull'),
2283 pull=opts.get('pull'),
2284 rev=destrev,
2284 rev=destrev,
2285 update=False,
2285 update=False,
2286 stream=opts.get('uncompressed'))
2286 stream=opts.get('uncompressed'))
2287
2287
2288 ui.note(_('cloning patch repository\n'))
2288 ui.note(_('cloning patch repository\n'))
2289 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2289 hg.clone(ui, opts, opts.get('patches') or patchdir(sr), patchdir(dr),
2290 pull=opts.get('pull'), update=not opts.get('noupdate'),
2290 pull=opts.get('pull'), update=not opts.get('noupdate'),
2291 stream=opts.get('uncompressed'))
2291 stream=opts.get('uncompressed'))
2292
2292
2293 if dr.local():
2293 if dr.local():
2294 repo = dr.local()
2294 repo = dr.local()
2295 if qbase:
2295 if qbase:
2296 ui.note(_('stripping applied patches from destination '
2296 ui.note(_('stripping applied patches from destination '
2297 'repository\n'))
2297 'repository\n'))
2298 repo.mq.strip(repo, [qbase], update=False, backup=None)
2298 repo.mq.strip(repo, [qbase], update=False, backup=None)
2299 if not opts.get('noupdate'):
2299 if not opts.get('noupdate'):
2300 ui.note(_('updating destination repository\n'))
2300 ui.note(_('updating destination repository\n'))
2301 hg.update(repo, repo.changelog.tip())
2301 hg.update(repo, repo.changelog.tip())
2302
2302
2303 @command("qcommit|qci",
2303 @command("qcommit|qci",
2304 commands.table["^commit|ci"][1],
2304 commands.table["^commit|ci"][1],
2305 _('hg qcommit [OPTION]... [FILE]...'))
2305 _('hg qcommit [OPTION]... [FILE]...'))
2306 def commit(ui, repo, *pats, **opts):
2306 def commit(ui, repo, *pats, **opts):
2307 """commit changes in the queue repository (DEPRECATED)
2307 """commit changes in the queue repository (DEPRECATED)
2308
2308
2309 This command is deprecated; use :hg:`commit --mq` instead."""
2309 This command is deprecated; use :hg:`commit --mq` instead."""
2310 q = repo.mq
2310 q = repo.mq
2311 r = q.qrepo()
2311 r = q.qrepo()
2312 if not r:
2312 if not r:
2313 raise util.Abort('no queue repository')
2313 raise util.Abort('no queue repository')
2314 commands.commit(r.ui, r, *pats, **opts)
2314 commands.commit(r.ui, r, *pats, **opts)
2315
2315
2316 @command("qseries",
2316 @command("qseries",
2317 [('m', 'missing', None, _('print patches not in series')),
2317 [('m', 'missing', None, _('print patches not in series')),
2318 ] + seriesopts,
2318 ] + seriesopts,
2319 _('hg qseries [-ms]'))
2319 _('hg qseries [-ms]'))
2320 def series(ui, repo, **opts):
2320 def series(ui, repo, **opts):
2321 """print the entire series file
2321 """print the entire series file
2322
2322
2323 Returns 0 on success."""
2323 Returns 0 on success."""
2324 repo.mq.qseries(repo, missing=opts.get('missing'),
2324 repo.mq.qseries(repo, missing=opts.get('missing'),
2325 summary=opts.get('summary'))
2325 summary=opts.get('summary'))
2326 return 0
2326 return 0
2327
2327
2328 @command("qtop", seriesopts, _('hg qtop [-s]'))
2328 @command("qtop", seriesopts, _('hg qtop [-s]'))
2329 def top(ui, repo, **opts):
2329 def top(ui, repo, **opts):
2330 """print the name of the current patch
2330 """print the name of the current patch
2331
2331
2332 Returns 0 on success."""
2332 Returns 0 on success."""
2333 q = repo.mq
2333 q = repo.mq
2334 t = q.applied and q.seriesend(True) or 0
2334 t = q.applied and q.seriesend(True) or 0
2335 if t:
2335 if t:
2336 q.qseries(repo, start=t - 1, length=1, status='A',
2336 q.qseries(repo, start=t - 1, length=1, status='A',
2337 summary=opts.get('summary'))
2337 summary=opts.get('summary'))
2338 else:
2338 else:
2339 ui.write(_("no patches applied\n"))
2339 ui.write(_("no patches applied\n"))
2340 return 1
2340 return 1
2341
2341
2342 @command("qnext", seriesopts, _('hg qnext [-s]'))
2342 @command("qnext", seriesopts, _('hg qnext [-s]'))
2343 def next(ui, repo, **opts):
2343 def next(ui, repo, **opts):
2344 """print the name of the next pushable patch
2344 """print the name of the next pushable patch
2345
2345
2346 Returns 0 on success."""
2346 Returns 0 on success."""
2347 q = repo.mq
2347 q = repo.mq
2348 end = q.seriesend()
2348 end = q.seriesend()
2349 if end == len(q.series):
2349 if end == len(q.series):
2350 ui.write(_("all patches applied\n"))
2350 ui.write(_("all patches applied\n"))
2351 return 1
2351 return 1
2352 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2352 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2353
2353
2354 @command("qprev", seriesopts, _('hg qprev [-s]'))
2354 @command("qprev", seriesopts, _('hg qprev [-s]'))
2355 def prev(ui, repo, **opts):
2355 def prev(ui, repo, **opts):
2356 """print the name of the preceding applied patch
2356 """print the name of the preceding applied patch
2357
2357
2358 Returns 0 on success."""
2358 Returns 0 on success."""
2359 q = repo.mq
2359 q = repo.mq
2360 l = len(q.applied)
2360 l = len(q.applied)
2361 if l == 1:
2361 if l == 1:
2362 ui.write(_("only one patch applied\n"))
2362 ui.write(_("only one patch applied\n"))
2363 return 1
2363 return 1
2364 if not l:
2364 if not l:
2365 ui.write(_("no patches applied\n"))
2365 ui.write(_("no patches applied\n"))
2366 return 1
2366 return 1
2367 idx = q.series.index(q.applied[-2].name)
2367 idx = q.series.index(q.applied[-2].name)
2368 q.qseries(repo, start=idx, length=1, status='A',
2368 q.qseries(repo, start=idx, length=1, status='A',
2369 summary=opts.get('summary'))
2369 summary=opts.get('summary'))
2370
2370
2371 def setupheaderopts(ui, opts):
2371 def setupheaderopts(ui, opts):
2372 if not opts.get('user') and opts.get('currentuser'):
2372 if not opts.get('user') and opts.get('currentuser'):
2373 opts['user'] = ui.username()
2373 opts['user'] = ui.username()
2374 if not opts.get('date') and opts.get('currentdate'):
2374 if not opts.get('date') and opts.get('currentdate'):
2375 opts['date'] = "%d %d" % util.makedate()
2375 opts['date'] = "%d %d" % util.makedate()
2376
2376
2377 @command("^qnew",
2377 @command("^qnew",
2378 [('e', 'edit', None, _('edit commit message')),
2378 [('e', 'edit', None, _('edit commit message')),
2379 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2379 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
2380 ('g', 'git', None, _('use git extended diff format')),
2380 ('g', 'git', None, _('use git extended diff format')),
2381 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2381 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2382 ('u', 'user', '',
2382 ('u', 'user', '',
2383 _('add "From: <USER>" to patch'), _('USER')),
2383 _('add "From: <USER>" to patch'), _('USER')),
2384 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2384 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2385 ('d', 'date', '',
2385 ('d', 'date', '',
2386 _('add "Date: <DATE>" to patch'), _('DATE'))
2386 _('add "Date: <DATE>" to patch'), _('DATE'))
2387 ] + commands.walkopts + commands.commitopts,
2387 ] + commands.walkopts + commands.commitopts,
2388 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2388 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...'))
2389 def new(ui, repo, patch, *args, **opts):
2389 def new(ui, repo, patch, *args, **opts):
2390 """create a new patch
2390 """create a new patch
2391
2391
2392 qnew creates a new patch on top of the currently-applied patch (if
2392 qnew creates a new patch on top of the currently-applied patch (if
2393 any). The patch will be initialized with any outstanding changes
2393 any). The patch will be initialized with any outstanding changes
2394 in the working directory. You may also use -I/--include,
2394 in the working directory. You may also use -I/--include,
2395 -X/--exclude, and/or a list of files after the patch name to add
2395 -X/--exclude, and/or a list of files after the patch name to add
2396 only changes to matching files to the new patch, leaving the rest
2396 only changes to matching files to the new patch, leaving the rest
2397 as uncommitted modifications.
2397 as uncommitted modifications.
2398
2398
2399 -u/--user and -d/--date can be used to set the (given) user and
2399 -u/--user and -d/--date can be used to set the (given) user and
2400 date, respectively. -U/--currentuser and -D/--currentdate set user
2400 date, respectively. -U/--currentuser and -D/--currentdate set user
2401 to current user and date to current date.
2401 to current user and date to current date.
2402
2402
2403 -e/--edit, -m/--message or -l/--logfile set the patch header as
2403 -e/--edit, -m/--message or -l/--logfile set the patch header as
2404 well as the commit message. If none is specified, the header is
2404 well as the commit message. If none is specified, the header is
2405 empty and the commit message is '[mq]: PATCH'.
2405 empty and the commit message is '[mq]: PATCH'.
2406
2406
2407 Use the -g/--git option to keep the patch in the git extended diff
2407 Use the -g/--git option to keep the patch in the git extended diff
2408 format. Read the diffs help topic for more information on why this
2408 format. Read the diffs help topic for more information on why this
2409 is important for preserving permission changes and copy/rename
2409 is important for preserving permission changes and copy/rename
2410 information.
2410 information.
2411
2411
2412 Returns 0 on successful creation of a new patch.
2412 Returns 0 on successful creation of a new patch.
2413 """
2413 """
2414 msg = cmdutil.logmessage(ui, opts)
2414 msg = cmdutil.logmessage(ui, opts)
2415 def getmsg():
2415 def getmsg():
2416 return ui.edit(msg, opts.get('user') or ui.username())
2416 return ui.edit(msg, opts.get('user') or ui.username())
2417 q = repo.mq
2417 q = repo.mq
2418 opts['msg'] = msg
2418 opts['msg'] = msg
2419 if opts.get('edit'):
2419 if opts.get('edit'):
2420 opts['msg'] = getmsg
2420 opts['msg'] = getmsg
2421 else:
2421 else:
2422 opts['msg'] = msg
2422 opts['msg'] = msg
2423 setupheaderopts(ui, opts)
2423 setupheaderopts(ui, opts)
2424 q.new(repo, patch, *args, **opts)
2424 q.new(repo, patch, *args, **opts)
2425 q.savedirty()
2425 q.savedirty()
2426 return 0
2426 return 0
2427
2427
2428 @command("^qrefresh",
2428 @command("^qrefresh",
2429 [('e', 'edit', None, _('edit commit message')),
2429 [('e', 'edit', None, _('edit commit message')),
2430 ('g', 'git', None, _('use git extended diff format')),
2430 ('g', 'git', None, _('use git extended diff format')),
2431 ('s', 'short', None,
2431 ('s', 'short', None,
2432 _('refresh only files already in the patch and specified files')),
2432 _('refresh only files already in the patch and specified files')),
2433 ('U', 'currentuser', None,
2433 ('U', 'currentuser', None,
2434 _('add/update author field in patch with current user')),
2434 _('add/update author field in patch with current user')),
2435 ('u', 'user', '',
2435 ('u', 'user', '',
2436 _('add/update author field in patch with given user'), _('USER')),
2436 _('add/update author field in patch with given user'), _('USER')),
2437 ('D', 'currentdate', None,
2437 ('D', 'currentdate', None,
2438 _('add/update date field in patch with current date')),
2438 _('add/update date field in patch with current date')),
2439 ('d', 'date', '',
2439 ('d', 'date', '',
2440 _('add/update date field in patch with given date'), _('DATE'))
2440 _('add/update date field in patch with given date'), _('DATE'))
2441 ] + commands.walkopts + commands.commitopts,
2441 ] + commands.walkopts + commands.commitopts,
2442 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2442 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...'))
2443 def refresh(ui, repo, *pats, **opts):
2443 def refresh(ui, repo, *pats, **opts):
2444 """update the current patch
2444 """update the current patch
2445
2445
2446 If any file patterns are provided, the refreshed patch will
2446 If any file patterns are provided, the refreshed patch will
2447 contain only the modifications that match those patterns; the
2447 contain only the modifications that match those patterns; the
2448 remaining modifications will remain in the working directory.
2448 remaining modifications will remain in the working directory.
2449
2449
2450 If -s/--short is specified, files currently included in the patch
2450 If -s/--short is specified, files currently included in the patch
2451 will be refreshed just like matched files and remain in the patch.
2451 will be refreshed just like matched files and remain in the patch.
2452
2452
2453 If -e/--edit is specified, Mercurial will start your configured editor for
2453 If -e/--edit is specified, Mercurial will start your configured editor for
2454 you to enter a message. In case qrefresh fails, you will find a backup of
2454 you to enter a message. In case qrefresh fails, you will find a backup of
2455 your message in ``.hg/last-message.txt``.
2455 your message in ``.hg/last-message.txt``.
2456
2456
2457 hg add/remove/copy/rename work as usual, though you might want to
2457 hg add/remove/copy/rename work as usual, though you might want to
2458 use git-style patches (-g/--git or [diff] git=1) to track copies
2458 use git-style patches (-g/--git or [diff] git=1) to track copies
2459 and renames. See the diffs help topic for more information on the
2459 and renames. See the diffs help topic for more information on the
2460 git diff format.
2460 git diff format.
2461
2461
2462 Returns 0 on success.
2462 Returns 0 on success.
2463 """
2463 """
2464 q = repo.mq
2464 q = repo.mq
2465 message = cmdutil.logmessage(ui, opts)
2465 message = cmdutil.logmessage(ui, opts)
2466 if opts.get('edit'):
2466 if opts.get('edit'):
2467 if not q.applied:
2467 if not q.applied:
2468 ui.write(_("no patches applied\n"))
2468 ui.write(_("no patches applied\n"))
2469 return 1
2469 return 1
2470 if message:
2470 if message:
2471 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2471 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2472 patch = q.applied[-1].name
2472 patch = q.applied[-1].name
2473 ph = patchheader(q.join(patch), q.plainmode)
2473 ph = patchheader(q.join(patch), q.plainmode)
2474 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2474 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2475 # We don't want to lose the patch message if qrefresh fails (issue2062)
2475 # We don't want to lose the patch message if qrefresh fails (issue2062)
2476 repo.savecommitmessage(message)
2476 repo.savecommitmessage(message)
2477 setupheaderopts(ui, opts)
2477 setupheaderopts(ui, opts)
2478 wlock = repo.wlock()
2478 wlock = repo.wlock()
2479 try:
2479 try:
2480 ret = q.refresh(repo, pats, msg=message, **opts)
2480 ret = q.refresh(repo, pats, msg=message, **opts)
2481 q.savedirty()
2481 q.savedirty()
2482 return ret
2482 return ret
2483 finally:
2483 finally:
2484 wlock.release()
2484 wlock.release()
2485
2485
2486 @command("^qdiff",
2486 @command("^qdiff",
2487 commands.diffopts + commands.diffopts2 + commands.walkopts,
2487 commands.diffopts + commands.diffopts2 + commands.walkopts,
2488 _('hg qdiff [OPTION]... [FILE]...'))
2488 _('hg qdiff [OPTION]... [FILE]...'))
2489 def diff(ui, repo, *pats, **opts):
2489 def diff(ui, repo, *pats, **opts):
2490 """diff of the current patch and subsequent modifications
2490 """diff of the current patch and subsequent modifications
2491
2491
2492 Shows a diff which includes the current patch as well as any
2492 Shows a diff which includes the current patch as well as any
2493 changes which have been made in the working directory since the
2493 changes which have been made in the working directory since the
2494 last refresh (thus showing what the current patch would become
2494 last refresh (thus showing what the current patch would become
2495 after a qrefresh).
2495 after a qrefresh).
2496
2496
2497 Use :hg:`diff` if you only want to see the changes made since the
2497 Use :hg:`diff` if you only want to see the changes made since the
2498 last qrefresh, or :hg:`export qtip` if you want to see changes
2498 last qrefresh, or :hg:`export qtip` if you want to see changes
2499 made by the current patch without including changes made since the
2499 made by the current patch without including changes made since the
2500 qrefresh.
2500 qrefresh.
2501
2501
2502 Returns 0 on success.
2502 Returns 0 on success.
2503 """
2503 """
2504 repo.mq.diff(repo, pats, opts)
2504 repo.mq.diff(repo, pats, opts)
2505 return 0
2505 return 0
2506
2506
2507 @command('qfold',
2507 @command('qfold',
2508 [('e', 'edit', None, _('edit patch header')),
2508 [('e', 'edit', None, _('edit patch header')),
2509 ('k', 'keep', None, _('keep folded patch files')),
2509 ('k', 'keep', None, _('keep folded patch files')),
2510 ] + commands.commitopts,
2510 ] + commands.commitopts,
2511 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2511 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...'))
2512 def fold(ui, repo, *files, **opts):
2512 def fold(ui, repo, *files, **opts):
2513 """fold the named patches into the current patch
2513 """fold the named patches into the current patch
2514
2514
2515 Patches must not yet be applied. Each patch will be successively
2515 Patches must not yet be applied. Each patch will be successively
2516 applied to the current patch in the order given. If all the
2516 applied to the current patch in the order given. If all the
2517 patches apply successfully, the current patch will be refreshed
2517 patches apply successfully, the current patch will be refreshed
2518 with the new cumulative patch, and the folded patches will be
2518 with the new cumulative patch, and the folded patches will be
2519 deleted. With -k/--keep, the folded patch files will not be
2519 deleted. With -k/--keep, the folded patch files will not be
2520 removed afterwards.
2520 removed afterwards.
2521
2521
2522 The header for each folded patch will be concatenated with the
2522 The header for each folded patch will be concatenated with the
2523 current patch header, separated by a line of ``* * *``.
2523 current patch header, separated by a line of ``* * *``.
2524
2524
2525 Returns 0 on success."""
2525 Returns 0 on success."""
2526 q = repo.mq
2526 q = repo.mq
2527 if not files:
2527 if not files:
2528 raise util.Abort(_('qfold requires at least one patch name'))
2528 raise util.Abort(_('qfold requires at least one patch name'))
2529 if not q.checktoppatch(repo)[0]:
2529 if not q.checktoppatch(repo)[0]:
2530 raise util.Abort(_('no patches applied'))
2530 raise util.Abort(_('no patches applied'))
2531 q.checklocalchanges(repo)
2531 q.checklocalchanges(repo)
2532
2532
2533 message = cmdutil.logmessage(ui, opts)
2533 message = cmdutil.logmessage(ui, opts)
2534 if opts.get('edit'):
2534 if opts.get('edit'):
2535 if message:
2535 if message:
2536 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2536 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2537
2537
2538 parent = q.lookup('qtip')
2538 parent = q.lookup('qtip')
2539 patches = []
2539 patches = []
2540 messages = []
2540 messages = []
2541 for f in files:
2541 for f in files:
2542 p = q.lookup(f)
2542 p = q.lookup(f)
2543 if p in patches or p == parent:
2543 if p in patches or p == parent:
2544 ui.warn(_('skipping already folded patch %s\n') % p)
2544 ui.warn(_('skipping already folded patch %s\n') % p)
2545 if q.isapplied(p):
2545 if q.isapplied(p):
2546 raise util.Abort(_('qfold cannot fold already applied patch %s')
2546 raise util.Abort(_('qfold cannot fold already applied patch %s')
2547 % p)
2547 % p)
2548 patches.append(p)
2548 patches.append(p)
2549
2549
2550 for p in patches:
2550 for p in patches:
2551 if not message:
2551 if not message:
2552 ph = patchheader(q.join(p), q.plainmode)
2552 ph = patchheader(q.join(p), q.plainmode)
2553 if ph.message:
2553 if ph.message:
2554 messages.append(ph.message)
2554 messages.append(ph.message)
2555 pf = q.join(p)
2555 pf = q.join(p)
2556 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2556 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2557 if not patchsuccess:
2557 if not patchsuccess:
2558 raise util.Abort(_('error folding patch %s') % p)
2558 raise util.Abort(_('error folding patch %s') % p)
2559
2559
2560 if not message:
2560 if not message:
2561 ph = patchheader(q.join(parent), q.plainmode)
2561 ph = patchheader(q.join(parent), q.plainmode)
2562 message, user = ph.message, ph.user
2562 message, user = ph.message, ph.user
2563 for msg in messages:
2563 for msg in messages:
2564 message.append('* * *')
2564 message.append('* * *')
2565 message.extend(msg)
2565 message.extend(msg)
2566 message = '\n'.join(message)
2566 message = '\n'.join(message)
2567
2567
2568 if opts.get('edit'):
2568 if opts.get('edit'):
2569 message = ui.edit(message, user or ui.username())
2569 message = ui.edit(message, user or ui.username())
2570
2570
2571 diffopts = q.patchopts(q.diffopts(), *patches)
2571 diffopts = q.patchopts(q.diffopts(), *patches)
2572 wlock = repo.wlock()
2572 wlock = repo.wlock()
2573 try:
2573 try:
2574 q.refresh(repo, msg=message, git=diffopts.git)
2574 q.refresh(repo, msg=message, git=diffopts.git)
2575 q.delete(repo, patches, opts)
2575 q.delete(repo, patches, opts)
2576 q.savedirty()
2576 q.savedirty()
2577 finally:
2577 finally:
2578 wlock.release()
2578 wlock.release()
2579
2579
2580 @command("qgoto",
2580 @command("qgoto",
2581 [('', 'keep-changes', None,
2581 [('', 'keep-changes', None,
2582 _('tolerate non-conflicting local changes')),
2582 _('tolerate non-conflicting local changes')),
2583 ('f', 'force', None, _('overwrite any local changes')),
2583 ('f', 'force', None, _('overwrite any local changes')),
2584 ('', 'no-backup', None, _('do not save backup copies of files'))],
2584 ('', 'no-backup', None, _('do not save backup copies of files'))],
2585 _('hg qgoto [OPTION]... PATCH'))
2585 _('hg qgoto [OPTION]... PATCH'))
2586 def goto(ui, repo, patch, **opts):
2586 def goto(ui, repo, patch, **opts):
2587 '''push or pop patches until named patch is at top of stack
2587 '''push or pop patches until named patch is at top of stack
2588
2588
2589 Returns 0 on success.'''
2589 Returns 0 on success.'''
2590 opts = fixkeepchangesopts(ui, opts)
2590 opts = fixkeepchangesopts(ui, opts)
2591 q = repo.mq
2591 q = repo.mq
2592 patch = q.lookup(patch)
2592 patch = q.lookup(patch)
2593 nobackup = opts.get('no_backup')
2593 nobackup = opts.get('no_backup')
2594 keepchanges = opts.get('keep_changes')
2594 keepchanges = opts.get('keep_changes')
2595 if q.isapplied(patch):
2595 if q.isapplied(patch):
2596 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2596 ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup,
2597 keepchanges=keepchanges)
2597 keepchanges=keepchanges)
2598 else:
2598 else:
2599 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2599 ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup,
2600 keepchanges=keepchanges)
2600 keepchanges=keepchanges)
2601 q.savedirty()
2601 q.savedirty()
2602 return ret
2602 return ret
2603
2603
2604 @command("qguard",
2604 @command("qguard",
2605 [('l', 'list', None, _('list all patches and guards')),
2605 [('l', 'list', None, _('list all patches and guards')),
2606 ('n', 'none', None, _('drop all guards'))],
2606 ('n', 'none', None, _('drop all guards'))],
2607 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2607 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]'))
2608 def guard(ui, repo, *args, **opts):
2608 def guard(ui, repo, *args, **opts):
2609 '''set or print guards for a patch
2609 '''set or print guards for a patch
2610
2610
2611 Guards control whether a patch can be pushed. A patch with no
2611 Guards control whether a patch can be pushed. A patch with no
2612 guards is always pushed. A patch with a positive guard ("+foo") is
2612 guards is always pushed. A patch with a positive guard ("+foo") is
2613 pushed only if the :hg:`qselect` command has activated it. A patch with
2613 pushed only if the :hg:`qselect` command has activated it. A patch with
2614 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2614 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2615 has activated it.
2615 has activated it.
2616
2616
2617 With no arguments, print the currently active guards.
2617 With no arguments, print the currently active guards.
2618 With arguments, set guards for the named patch.
2618 With arguments, set guards for the named patch.
2619
2619
2620 .. note::
2620 .. note::
2621 Specifying negative guards now requires '--'.
2621 Specifying negative guards now requires '--'.
2622
2622
2623 To set guards on another patch::
2623 To set guards on another patch::
2624
2624
2625 hg qguard other.patch -- +2.6.17 -stable
2625 hg qguard other.patch -- +2.6.17 -stable
2626
2626
2627 Returns 0 on success.
2627 Returns 0 on success.
2628 '''
2628 '''
2629 def status(idx):
2629 def status(idx):
2630 guards = q.seriesguards[idx] or ['unguarded']
2630 guards = q.seriesguards[idx] or ['unguarded']
2631 if q.series[idx] in applied:
2631 if q.series[idx] in applied:
2632 state = 'applied'
2632 state = 'applied'
2633 elif q.pushable(idx)[0]:
2633 elif q.pushable(idx)[0]:
2634 state = 'unapplied'
2634 state = 'unapplied'
2635 else:
2635 else:
2636 state = 'guarded'
2636 state = 'guarded'
2637 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2637 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2638 ui.write('%s: ' % ui.label(q.series[idx], label))
2638 ui.write('%s: ' % ui.label(q.series[idx], label))
2639
2639
2640 for i, guard in enumerate(guards):
2640 for i, guard in enumerate(guards):
2641 if guard.startswith('+'):
2641 if guard.startswith('+'):
2642 ui.write(guard, label='qguard.positive')
2642 ui.write(guard, label='qguard.positive')
2643 elif guard.startswith('-'):
2643 elif guard.startswith('-'):
2644 ui.write(guard, label='qguard.negative')
2644 ui.write(guard, label='qguard.negative')
2645 else:
2645 else:
2646 ui.write(guard, label='qguard.unguarded')
2646 ui.write(guard, label='qguard.unguarded')
2647 if i != len(guards) - 1:
2647 if i != len(guards) - 1:
2648 ui.write(' ')
2648 ui.write(' ')
2649 ui.write('\n')
2649 ui.write('\n')
2650 q = repo.mq
2650 q = repo.mq
2651 applied = set(p.name for p in q.applied)
2651 applied = set(p.name for p in q.applied)
2652 patch = None
2652 patch = None
2653 args = list(args)
2653 args = list(args)
2654 if opts.get('list'):
2654 if opts.get('list'):
2655 if args or opts.get('none'):
2655 if args or opts.get('none'):
2656 raise util.Abort(_('cannot mix -l/--list with options or '
2656 raise util.Abort(_('cannot mix -l/--list with options or '
2657 'arguments'))
2657 'arguments'))
2658 for i in xrange(len(q.series)):
2658 for i in xrange(len(q.series)):
2659 status(i)
2659 status(i)
2660 return
2660 return
2661 if not args or args[0][0:1] in '-+':
2661 if not args or args[0][0:1] in '-+':
2662 if not q.applied:
2662 if not q.applied:
2663 raise util.Abort(_('no patches applied'))
2663 raise util.Abort(_('no patches applied'))
2664 patch = q.applied[-1].name
2664 patch = q.applied[-1].name
2665 if patch is None and args[0][0:1] not in '-+':
2665 if patch is None and args[0][0:1] not in '-+':
2666 patch = args.pop(0)
2666 patch = args.pop(0)
2667 if patch is None:
2667 if patch is None:
2668 raise util.Abort(_('no patch to work with'))
2668 raise util.Abort(_('no patch to work with'))
2669 if args or opts.get('none'):
2669 if args or opts.get('none'):
2670 idx = q.findseries(patch)
2670 idx = q.findseries(patch)
2671 if idx is None:
2671 if idx is None:
2672 raise util.Abort(_('no patch named %s') % patch)
2672 raise util.Abort(_('no patch named %s') % patch)
2673 q.setguards(idx, args)
2673 q.setguards(idx, args)
2674 q.savedirty()
2674 q.savedirty()
2675 else:
2675 else:
2676 status(q.series.index(q.lookup(patch)))
2676 status(q.series.index(q.lookup(patch)))
2677
2677
2678 @command("qheader", [], _('hg qheader [PATCH]'))
2678 @command("qheader", [], _('hg qheader [PATCH]'))
2679 def header(ui, repo, patch=None):
2679 def header(ui, repo, patch=None):
2680 """print the header of the topmost or specified patch
2680 """print the header of the topmost or specified patch
2681
2681
2682 Returns 0 on success."""
2682 Returns 0 on success."""
2683 q = repo.mq
2683 q = repo.mq
2684
2684
2685 if patch:
2685 if patch:
2686 patch = q.lookup(patch)
2686 patch = q.lookup(patch)
2687 else:
2687 else:
2688 if not q.applied:
2688 if not q.applied:
2689 ui.write(_('no patches applied\n'))
2689 ui.write(_('no patches applied\n'))
2690 return 1
2690 return 1
2691 patch = q.lookup('qtip')
2691 patch = q.lookup('qtip')
2692 ph = patchheader(q.join(patch), q.plainmode)
2692 ph = patchheader(q.join(patch), q.plainmode)
2693
2693
2694 ui.write('\n'.join(ph.message) + '\n')
2694 ui.write('\n'.join(ph.message) + '\n')
2695
2695
2696 def lastsavename(path):
2696 def lastsavename(path):
2697 (directory, base) = os.path.split(path)
2697 (directory, base) = os.path.split(path)
2698 names = os.listdir(directory)
2698 names = os.listdir(directory)
2699 namere = re.compile("%s.([0-9]+)" % base)
2699 namere = re.compile("%s.([0-9]+)" % base)
2700 maxindex = None
2700 maxindex = None
2701 maxname = None
2701 maxname = None
2702 for f in names:
2702 for f in names:
2703 m = namere.match(f)
2703 m = namere.match(f)
2704 if m:
2704 if m:
2705 index = int(m.group(1))
2705 index = int(m.group(1))
2706 if maxindex is None or index > maxindex:
2706 if maxindex is None or index > maxindex:
2707 maxindex = index
2707 maxindex = index
2708 maxname = f
2708 maxname = f
2709 if maxname:
2709 if maxname:
2710 return (os.path.join(directory, maxname), maxindex)
2710 return (os.path.join(directory, maxname), maxindex)
2711 return (None, None)
2711 return (None, None)
2712
2712
2713 def savename(path):
2713 def savename(path):
2714 (last, index) = lastsavename(path)
2714 (last, index) = lastsavename(path)
2715 if last is None:
2715 if last is None:
2716 index = 0
2716 index = 0
2717 newpath = path + ".%d" % (index + 1)
2717 newpath = path + ".%d" % (index + 1)
2718 return newpath
2718 return newpath
2719
2719
2720 @command("^qpush",
2720 @command("^qpush",
2721 [('', 'keep-changes', None,
2721 [('', 'keep-changes', None,
2722 _('tolerate non-conflicting local changes')),
2722 _('tolerate non-conflicting local changes')),
2723 ('f', 'force', None, _('apply on top of local changes')),
2723 ('f', 'force', None, _('apply on top of local changes')),
2724 ('e', 'exact', None,
2724 ('e', 'exact', None,
2725 _('apply the target patch to its recorded parent')),
2725 _('apply the target patch to its recorded parent')),
2726 ('l', 'list', None, _('list patch name in commit text')),
2726 ('l', 'list', None, _('list patch name in commit text')),
2727 ('a', 'all', None, _('apply all patches')),
2727 ('a', 'all', None, _('apply all patches')),
2728 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2728 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
2729 ('n', 'name', '',
2729 ('n', 'name', '',
2730 _('merge queue name (DEPRECATED)'), _('NAME')),
2730 _('merge queue name (DEPRECATED)'), _('NAME')),
2731 ('', 'move', None,
2731 ('', 'move', None,
2732 _('reorder patch series and apply only the patch')),
2732 _('reorder patch series and apply only the patch')),
2733 ('', 'no-backup', None, _('do not save backup copies of files'))],
2733 ('', 'no-backup', None, _('do not save backup copies of files'))],
2734 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2734 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
2735 def push(ui, repo, patch=None, **opts):
2735 def push(ui, repo, patch=None, **opts):
2736 """push the next patch onto the stack
2736 """push the next patch onto the stack
2737
2737
2738 By default, abort if the working directory contains uncommitted
2738 By default, abort if the working directory contains uncommitted
2739 changes. With --keep-changes, abort only if the uncommitted files
2739 changes. With --keep-changes, abort only if the uncommitted files
2740 overlap with patched files. With -f/--force, backup and patch over
2740 overlap with patched files. With -f/--force, backup and patch over
2741 uncommitted changes.
2741 uncommitted changes.
2742
2742
2743 Return 0 on success.
2743 Return 0 on success.
2744 """
2744 """
2745 q = repo.mq
2745 q = repo.mq
2746 mergeq = None
2746 mergeq = None
2747
2747
2748 opts = fixkeepchangesopts(ui, opts)
2748 opts = fixkeepchangesopts(ui, opts)
2749 if opts.get('merge'):
2749 if opts.get('merge'):
2750 if opts.get('name'):
2750 if opts.get('name'):
2751 newpath = repo.join(opts.get('name'))
2751 newpath = repo.join(opts.get('name'))
2752 else:
2752 else:
2753 newpath, i = lastsavename(q.path)
2753 newpath, i = lastsavename(q.path)
2754 if not newpath:
2754 if not newpath:
2755 ui.warn(_("no saved queues found, please use -n\n"))
2755 ui.warn(_("no saved queues found, please use -n\n"))
2756 return 1
2756 return 1
2757 mergeq = queue(ui, repo.path, newpath)
2757 mergeq = queue(ui, repo.path, newpath)
2758 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2758 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2759 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2759 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2760 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2760 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2761 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2761 exact=opts.get('exact'), nobackup=opts.get('no_backup'),
2762 keepchanges=opts.get('keep_changes'))
2762 keepchanges=opts.get('keep_changes'))
2763 return ret
2763 return ret
2764
2764
2765 @command("^qpop",
2765 @command("^qpop",
2766 [('a', 'all', None, _('pop all patches')),
2766 [('a', 'all', None, _('pop all patches')),
2767 ('n', 'name', '',
2767 ('n', 'name', '',
2768 _('queue name to pop (DEPRECATED)'), _('NAME')),
2768 _('queue name to pop (DEPRECATED)'), _('NAME')),
2769 ('', 'keep-changes', None,
2769 ('', 'keep-changes', None,
2770 _('tolerate non-conflicting local changes')),
2770 _('tolerate non-conflicting local changes')),
2771 ('f', 'force', None, _('forget any local changes to patched files')),
2771 ('f', 'force', None, _('forget any local changes to patched files')),
2772 ('', 'no-backup', None, _('do not save backup copies of files'))],
2772 ('', 'no-backup', None, _('do not save backup copies of files'))],
2773 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2773 _('hg qpop [-a] [-f] [PATCH | INDEX]'))
2774 def pop(ui, repo, patch=None, **opts):
2774 def pop(ui, repo, patch=None, **opts):
2775 """pop the current patch off the stack
2775 """pop the current patch off the stack
2776
2776
2777 Without argument, pops off the top of the patch stack. If given a
2777 Without argument, pops off the top of the patch stack. If given a
2778 patch name, keeps popping off patches until the named patch is at
2778 patch name, keeps popping off patches until the named patch is at
2779 the top of the stack.
2779 the top of the stack.
2780
2780
2781 By default, abort if the working directory contains uncommitted
2781 By default, abort if the working directory contains uncommitted
2782 changes. With --keep-changes, abort only if the uncommitted files
2782 changes. With --keep-changes, abort only if the uncommitted files
2783 overlap with patched files. With -f/--force, backup and discard
2783 overlap with patched files. With -f/--force, backup and discard
2784 changes made to such files.
2784 changes made to such files.
2785
2785
2786 Return 0 on success.
2786 Return 0 on success.
2787 """
2787 """
2788 opts = fixkeepchangesopts(ui, opts)
2788 opts = fixkeepchangesopts(ui, opts)
2789 localupdate = True
2789 localupdate = True
2790 if opts.get('name'):
2790 if opts.get('name'):
2791 q = queue(ui, repo.path, repo.join(opts.get('name')))
2791 q = queue(ui, repo.path, repo.join(opts.get('name')))
2792 ui.warn(_('using patch queue: %s\n') % q.path)
2792 ui.warn(_('using patch queue: %s\n') % q.path)
2793 localupdate = False
2793 localupdate = False
2794 else:
2794 else:
2795 q = repo.mq
2795 q = repo.mq
2796 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2796 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2797 all=opts.get('all'), nobackup=opts.get('no_backup'),
2797 all=opts.get('all'), nobackup=opts.get('no_backup'),
2798 keepchanges=opts.get('keep_changes'))
2798 keepchanges=opts.get('keep_changes'))
2799 q.savedirty()
2799 q.savedirty()
2800 return ret
2800 return ret
2801
2801
2802 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2802 @command("qrename|qmv", [], _('hg qrename PATCH1 [PATCH2]'))
2803 def rename(ui, repo, patch, name=None, **opts):
2803 def rename(ui, repo, patch, name=None, **opts):
2804 """rename a patch
2804 """rename a patch
2805
2805
2806 With one argument, renames the current patch to PATCH1.
2806 With one argument, renames the current patch to PATCH1.
2807 With two arguments, renames PATCH1 to PATCH2.
2807 With two arguments, renames PATCH1 to PATCH2.
2808
2808
2809 Returns 0 on success."""
2809 Returns 0 on success."""
2810 q = repo.mq
2810 q = repo.mq
2811 if not name:
2811 if not name:
2812 name = patch
2812 name = patch
2813 patch = None
2813 patch = None
2814
2814
2815 if patch:
2815 if patch:
2816 patch = q.lookup(patch)
2816 patch = q.lookup(patch)
2817 else:
2817 else:
2818 if not q.applied:
2818 if not q.applied:
2819 ui.write(_('no patches applied\n'))
2819 ui.write(_('no patches applied\n'))
2820 return
2820 return
2821 patch = q.lookup('qtip')
2821 patch = q.lookup('qtip')
2822 absdest = q.join(name)
2822 absdest = q.join(name)
2823 if os.path.isdir(absdest):
2823 if os.path.isdir(absdest):
2824 name = normname(os.path.join(name, os.path.basename(patch)))
2824 name = normname(os.path.join(name, os.path.basename(patch)))
2825 absdest = q.join(name)
2825 absdest = q.join(name)
2826 q.checkpatchname(name)
2826 q.checkpatchname(name)
2827
2827
2828 ui.note(_('renaming %s to %s\n') % (patch, name))
2828 ui.note(_('renaming %s to %s\n') % (patch, name))
2829 i = q.findseries(patch)
2829 i = q.findseries(patch)
2830 guards = q.guard_re.findall(q.fullseries[i])
2830 guards = q.guard_re.findall(q.fullseries[i])
2831 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2831 q.fullseries[i] = name + ''.join([' #' + g for g in guards])
2832 q.parseseries()
2832 q.parseseries()
2833 q.seriesdirty = True
2833 q.seriesdirty = True
2834
2834
2835 info = q.isapplied(patch)
2835 info = q.isapplied(patch)
2836 if info:
2836 if info:
2837 q.applied[info[0]] = statusentry(info[1], name)
2837 q.applied[info[0]] = statusentry(info[1], name)
2838 q.applieddirty = True
2838 q.applieddirty = True
2839
2839
2840 destdir = os.path.dirname(absdest)
2840 destdir = os.path.dirname(absdest)
2841 if not os.path.isdir(destdir):
2841 if not os.path.isdir(destdir):
2842 os.makedirs(destdir)
2842 os.makedirs(destdir)
2843 util.rename(q.join(patch), absdest)
2843 util.rename(q.join(patch), absdest)
2844 r = q.qrepo()
2844 r = q.qrepo()
2845 if r and patch in r.dirstate:
2845 if r and patch in r.dirstate:
2846 wctx = r[None]
2846 wctx = r[None]
2847 wlock = r.wlock()
2847 wlock = r.wlock()
2848 try:
2848 try:
2849 if r.dirstate[patch] == 'a':
2849 if r.dirstate[patch] == 'a':
2850 r.dirstate.drop(patch)
2850 r.dirstate.drop(patch)
2851 r.dirstate.add(name)
2851 r.dirstate.add(name)
2852 else:
2852 else:
2853 wctx.copy(patch, name)
2853 wctx.copy(patch, name)
2854 wctx.forget([patch])
2854 wctx.forget([patch])
2855 finally:
2855 finally:
2856 wlock.release()
2856 wlock.release()
2857
2857
2858 q.savedirty()
2858 q.savedirty()
2859
2859
2860 @command("qrestore",
2860 @command("qrestore",
2861 [('d', 'delete', None, _('delete save entry')),
2861 [('d', 'delete', None, _('delete save entry')),
2862 ('u', 'update', None, _('update queue working directory'))],
2862 ('u', 'update', None, _('update queue working directory'))],
2863 _('hg qrestore [-d] [-u] REV'))
2863 _('hg qrestore [-d] [-u] REV'))
2864 def restore(ui, repo, rev, **opts):
2864 def restore(ui, repo, rev, **opts):
2865 """restore the queue state saved by a revision (DEPRECATED)
2865 """restore the queue state saved by a revision (DEPRECATED)
2866
2866
2867 This command is deprecated, use :hg:`rebase` instead."""
2867 This command is deprecated, use :hg:`rebase` instead."""
2868 rev = repo.lookup(rev)
2868 rev = repo.lookup(rev)
2869 q = repo.mq
2869 q = repo.mq
2870 q.restore(repo, rev, delete=opts.get('delete'),
2870 q.restore(repo, rev, delete=opts.get('delete'),
2871 qupdate=opts.get('update'))
2871 qupdate=opts.get('update'))
2872 q.savedirty()
2872 q.savedirty()
2873 return 0
2873 return 0
2874
2874
2875 @command("qsave",
2875 @command("qsave",
2876 [('c', 'copy', None, _('copy patch directory')),
2876 [('c', 'copy', None, _('copy patch directory')),
2877 ('n', 'name', '',
2877 ('n', 'name', '',
2878 _('copy directory name'), _('NAME')),
2878 _('copy directory name'), _('NAME')),
2879 ('e', 'empty', None, _('clear queue status file')),
2879 ('e', 'empty', None, _('clear queue status file')),
2880 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2880 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2881 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2881 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'))
2882 def save(ui, repo, **opts):
2882 def save(ui, repo, **opts):
2883 """save current queue state (DEPRECATED)
2883 """save current queue state (DEPRECATED)
2884
2884
2885 This command is deprecated, use :hg:`rebase` instead."""
2885 This command is deprecated, use :hg:`rebase` instead."""
2886 q = repo.mq
2886 q = repo.mq
2887 message = cmdutil.logmessage(ui, opts)
2887 message = cmdutil.logmessage(ui, opts)
2888 ret = q.save(repo, msg=message)
2888 ret = q.save(repo, msg=message)
2889 if ret:
2889 if ret:
2890 return ret
2890 return ret
2891 q.savedirty() # save to .hg/patches before copying
2891 q.savedirty() # save to .hg/patches before copying
2892 if opts.get('copy'):
2892 if opts.get('copy'):
2893 path = q.path
2893 path = q.path
2894 if opts.get('name'):
2894 if opts.get('name'):
2895 newpath = os.path.join(q.basepath, opts.get('name'))
2895 newpath = os.path.join(q.basepath, opts.get('name'))
2896 if os.path.exists(newpath):
2896 if os.path.exists(newpath):
2897 if not os.path.isdir(newpath):
2897 if not os.path.isdir(newpath):
2898 raise util.Abort(_('destination %s exists and is not '
2898 raise util.Abort(_('destination %s exists and is not '
2899 'a directory') % newpath)
2899 'a directory') % newpath)
2900 if not opts.get('force'):
2900 if not opts.get('force'):
2901 raise util.Abort(_('destination %s exists, '
2901 raise util.Abort(_('destination %s exists, '
2902 'use -f to force') % newpath)
2902 'use -f to force') % newpath)
2903 else:
2903 else:
2904 newpath = savename(path)
2904 newpath = savename(path)
2905 ui.warn(_("copy %s to %s\n") % (path, newpath))
2905 ui.warn(_("copy %s to %s\n") % (path, newpath))
2906 util.copyfiles(path, newpath)
2906 util.copyfiles(path, newpath)
2907 if opts.get('empty'):
2907 if opts.get('empty'):
2908 del q.applied[:]
2908 del q.applied[:]
2909 q.applieddirty = True
2909 q.applieddirty = True
2910 q.savedirty()
2910 q.savedirty()
2911 return 0
2911 return 0
2912
2912
2913 @command("strip",
2913 @command("strip",
2914 [
2914 [
2915 ('r', 'rev', [], _('strip specified revision (optional, '
2915 ('r', 'rev', [], _('strip specified revision (optional, '
2916 'can specify revisions without this '
2916 'can specify revisions without this '
2917 'option)'), _('REV')),
2917 'option)'), _('REV')),
2918 ('f', 'force', None, _('force removal of changesets, discard '
2918 ('f', 'force', None, _('force removal of changesets, discard '
2919 'uncommitted changes (no backup)')),
2919 'uncommitted changes (no backup)')),
2920 ('b', 'backup', None, _('bundle only changesets with local revision'
2920 ('b', 'backup', None, _('bundle only changesets with local revision'
2921 ' number greater than REV which are not'
2921 ' number greater than REV which are not'
2922 ' descendants of REV (DEPRECATED)')),
2922 ' descendants of REV (DEPRECATED)')),
2923 ('', 'no-backup', None, _('no backups')),
2923 ('', 'no-backup', None, _('no backups')),
2924 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
2924 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
2925 ('n', '', None, _('ignored (DEPRECATED)')),
2925 ('n', '', None, _('ignored (DEPRECATED)')),
2926 ('k', 'keep', None, _("do not modify working copy during strip")),
2926 ('k', 'keep', None, _("do not modify working copy during strip")),
2927 ('B', 'bookmark', '', _("remove revs only reachable from given"
2927 ('B', 'bookmark', '', _("remove revs only reachable from given"
2928 " bookmark"))],
2928 " bookmark"))],
2929 _('hg strip [-k] [-f] [-n] [-B bookmark] [-r] REV...'))
2929 _('hg strip [-k] [-f] [-n] [-B bookmark] [-r] REV...'))
2930 def strip(ui, repo, *revs, **opts):
2930 def strip(ui, repo, *revs, **opts):
2931 """strip changesets and all their descendants from the repository
2931 """strip changesets and all their descendants from the repository
2932
2932
2933 The strip command removes the specified changesets and all their
2933 The strip command removes the specified changesets and all their
2934 descendants. If the working directory has uncommitted changes, the
2934 descendants. If the working directory has uncommitted changes, the
2935 operation is aborted unless the --force flag is supplied, in which
2935 operation is aborted unless the --force flag is supplied, in which
2936 case changes will be discarded.
2936 case changes will be discarded.
2937
2937
2938 If a parent of the working directory is stripped, then the working
2938 If a parent of the working directory is stripped, then the working
2939 directory will automatically be updated to the most recent
2939 directory will automatically be updated to the most recent
2940 available ancestor of the stripped parent after the operation
2940 available ancestor of the stripped parent after the operation
2941 completes.
2941 completes.
2942
2942
2943 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2943 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2944 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2944 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2945 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2945 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2946 where BUNDLE is the bundle file created by the strip. Note that
2946 where BUNDLE is the bundle file created by the strip. Note that
2947 the local revision numbers will in general be different after the
2947 the local revision numbers will in general be different after the
2948 restore.
2948 restore.
2949
2949
2950 Use the --no-backup option to discard the backup bundle once the
2950 Use the --no-backup option to discard the backup bundle once the
2951 operation completes.
2951 operation completes.
2952
2952
2953 Strip is not a history-rewriting operation and can be used on
2953 Strip is not a history-rewriting operation and can be used on
2954 changesets in the public phase. But if the stripped changesets have
2954 changesets in the public phase. But if the stripped changesets have
2955 been pushed to a remote repository you will likely pull them again.
2955 been pushed to a remote repository you will likely pull them again.
2956
2956
2957 Return 0 on success.
2957 Return 0 on success.
2958 """
2958 """
2959 backup = 'all'
2959 backup = 'all'
2960 if opts.get('backup'):
2960 if opts.get('backup'):
2961 backup = 'strip'
2961 backup = 'strip'
2962 elif opts.get('no_backup') or opts.get('nobackup'):
2962 elif opts.get('no_backup') or opts.get('nobackup'):
2963 backup = 'none'
2963 backup = 'none'
2964
2964
2965 cl = repo.changelog
2965 cl = repo.changelog
2966 revs = list(revs) + opts.get('rev')
2966 revs = list(revs) + opts.get('rev')
2967 revs = set(scmutil.revrange(repo, revs))
2967 revs = set(scmutil.revrange(repo, revs))
2968
2968
2969 if opts.get('bookmark'):
2969 if opts.get('bookmark'):
2970 mark = opts.get('bookmark')
2970 mark = opts.get('bookmark')
2971 marks = repo._bookmarks
2971 marks = repo._bookmarks
2972 if mark not in marks:
2972 if mark not in marks:
2973 raise util.Abort(_("bookmark '%s' not found") % mark)
2973 raise util.Abort(_("bookmark '%s' not found") % mark)
2974
2974
2975 # If the requested bookmark is not the only one pointing to a
2975 # If the requested bookmark is not the only one pointing to a
2976 # a revision we have to only delete the bookmark and not strip
2976 # a revision we have to only delete the bookmark and not strip
2977 # anything. revsets cannot detect that case.
2977 # anything. revsets cannot detect that case.
2978 uniquebm = True
2978 uniquebm = True
2979 for m, n in marks.iteritems():
2979 for m, n in marks.iteritems():
2980 if m != mark and n == repo[mark].node():
2980 if m != mark and n == repo[mark].node():
2981 uniquebm = False
2981 uniquebm = False
2982 break
2982 break
2983 if uniquebm:
2983 if uniquebm:
2984 rsrevs = repo.revs("ancestors(bookmark(%s)) - "
2984 rsrevs = repo.revs("ancestors(bookmark(%s)) - "
2985 "ancestors(head() and not bookmark(%s)) - "
2985 "ancestors(head() and not bookmark(%s)) - "
2986 "ancestors(bookmark() and not bookmark(%s))",
2986 "ancestors(bookmark() and not bookmark(%s))",
2987 mark, mark, mark)
2987 mark, mark, mark)
2988 revs.update(set(rsrevs))
2988 revs.update(set(rsrevs))
2989 if not revs:
2989 if not revs:
2990 del marks[mark]
2990 del marks[mark]
2991 repo._writebookmarks(mark)
2991 repo._writebookmarks(mark)
2992 ui.write(_("bookmark '%s' deleted\n") % mark)
2992 ui.write(_("bookmark '%s' deleted\n") % mark)
2993
2993
2994 if not revs:
2994 if not revs:
2995 raise util.Abort(_('empty revision set'))
2995 raise util.Abort(_('empty revision set'))
2996
2996
2997 descendants = set(cl.descendants(revs))
2997 descendants = set(cl.descendants(revs))
2998 strippedrevs = revs.union(descendants)
2998 strippedrevs = revs.union(descendants)
2999 roots = revs.difference(descendants)
2999 roots = revs.difference(descendants)
3000
3000
3001 update = False
3001 update = False
3002 # if one of the wdir parent is stripped we'll need
3002 # if one of the wdir parent is stripped we'll need
3003 # to update away to an earlier revision
3003 # to update away to an earlier revision
3004 for p in repo.dirstate.parents():
3004 for p in repo.dirstate.parents():
3005 if p != nullid and cl.rev(p) in strippedrevs:
3005 if p != nullid and cl.rev(p) in strippedrevs:
3006 update = True
3006 update = True
3007 break
3007 break
3008
3008
3009 rootnodes = set(cl.node(r) for r in roots)
3009 rootnodes = set(cl.node(r) for r in roots)
3010
3010
3011 q = repo.mq
3011 q = repo.mq
3012 if q.applied:
3012 if q.applied:
3013 # refresh queue state if we're about to strip
3013 # refresh queue state if we're about to strip
3014 # applied patches
3014 # applied patches
3015 if cl.rev(repo.lookup('qtip')) in strippedrevs:
3015 if cl.rev(repo.lookup('qtip')) in strippedrevs:
3016 q.applieddirty = True
3016 q.applieddirty = True
3017 start = 0
3017 start = 0
3018 end = len(q.applied)
3018 end = len(q.applied)
3019 for i, statusentry in enumerate(q.applied):
3019 for i, statusentry in enumerate(q.applied):
3020 if statusentry.node in rootnodes:
3020 if statusentry.node in rootnodes:
3021 # if one of the stripped roots is an applied
3021 # if one of the stripped roots is an applied
3022 # patch, only part of the queue is stripped
3022 # patch, only part of the queue is stripped
3023 start = i
3023 start = i
3024 break
3024 break
3025 del q.applied[start:end]
3025 del q.applied[start:end]
3026 q.savedirty()
3026 q.savedirty()
3027
3027
3028 revs = list(rootnodes)
3028 revs = list(rootnodes)
3029 if update and opts.get('keep'):
3029 if update and opts.get('keep'):
3030 wlock = repo.wlock()
3030 wlock = repo.wlock()
3031 try:
3031 try:
3032 urev = repo.mq.qparents(repo, revs[0])
3032 urev = repo.mq.qparents(repo, revs[0])
3033 repo.dirstate.rebuild(urev, repo[urev].manifest())
3033 repo.dirstate.rebuild(urev, repo[urev].manifest())
3034 repo.dirstate.write()
3034 repo.dirstate.write()
3035 update = False
3035 update = False
3036 finally:
3036 finally:
3037 wlock.release()
3037 wlock.release()
3038
3038
3039 if opts.get('bookmark'):
3039 if opts.get('bookmark'):
3040 del marks[mark]
3040 del marks[mark]
3041 repo._writebookmarks(marks)
3041 repo._writebookmarks(marks)
3042 ui.write(_("bookmark '%s' deleted\n") % mark)
3042 ui.write(_("bookmark '%s' deleted\n") % mark)
3043
3043
3044 repo.mq.strip(repo, revs, backup=backup, update=update,
3044 repo.mq.strip(repo, revs, backup=backup, update=update,
3045 force=opts.get('force'))
3045 force=opts.get('force'))
3046
3046
3047 return 0
3047 return 0
3048
3048
3049 @command("qselect",
3049 @command("qselect",
3050 [('n', 'none', None, _('disable all guards')),
3050 [('n', 'none', None, _('disable all guards')),
3051 ('s', 'series', None, _('list all guards in series file')),
3051 ('s', 'series', None, _('list all guards in series file')),
3052 ('', 'pop', None, _('pop to before first guarded applied patch')),
3052 ('', 'pop', None, _('pop to before first guarded applied patch')),
3053 ('', 'reapply', None, _('pop, then reapply patches'))],
3053 ('', 'reapply', None, _('pop, then reapply patches'))],
3054 _('hg qselect [OPTION]... [GUARD]...'))
3054 _('hg qselect [OPTION]... [GUARD]...'))
3055 def select(ui, repo, *args, **opts):
3055 def select(ui, repo, *args, **opts):
3056 '''set or print guarded patches to push
3056 '''set or print guarded patches to push
3057
3057
3058 Use the :hg:`qguard` command to set or print guards on patch, then use
3058 Use the :hg:`qguard` command to set or print guards on patch, then use
3059 qselect to tell mq which guards to use. A patch will be pushed if
3059 qselect to tell mq which guards to use. A patch will be pushed if
3060 it has no guards or any positive guards match the currently
3060 it has no guards or any positive guards match the currently
3061 selected guard, but will not be pushed if any negative guards
3061 selected guard, but will not be pushed if any negative guards
3062 match the current guard. For example::
3062 match the current guard. For example::
3063
3063
3064 qguard foo.patch -- -stable (negative guard)
3064 qguard foo.patch -- -stable (negative guard)
3065 qguard bar.patch +stable (positive guard)
3065 qguard bar.patch +stable (positive guard)
3066 qselect stable
3066 qselect stable
3067
3067
3068 This activates the "stable" guard. mq will skip foo.patch (because
3068 This activates the "stable" guard. mq will skip foo.patch (because
3069 it has a negative match) but push bar.patch (because it has a
3069 it has a negative match) but push bar.patch (because it has a
3070 positive match).
3070 positive match).
3071
3071
3072 With no arguments, prints the currently active guards.
3072 With no arguments, prints the currently active guards.
3073 With one argument, sets the active guard.
3073 With one argument, sets the active guard.
3074
3074
3075 Use -n/--none to deactivate guards (no other arguments needed).
3075 Use -n/--none to deactivate guards (no other arguments needed).
3076 When no guards are active, patches with positive guards are
3076 When no guards are active, patches with positive guards are
3077 skipped and patches with negative guards are pushed.
3077 skipped and patches with negative guards are pushed.
3078
3078
3079 qselect can change the guards on applied patches. It does not pop
3079 qselect can change the guards on applied patches. It does not pop
3080 guarded patches by default. Use --pop to pop back to the last
3080 guarded patches by default. Use --pop to pop back to the last
3081 applied patch that is not guarded. Use --reapply (which implies
3081 applied patch that is not guarded. Use --reapply (which implies
3082 --pop) to push back to the current patch afterwards, but skip
3082 --pop) to push back to the current patch afterwards, but skip
3083 guarded patches.
3083 guarded patches.
3084
3084
3085 Use -s/--series to print a list of all guards in the series file
3085 Use -s/--series to print a list of all guards in the series file
3086 (no other arguments needed). Use -v for more information.
3086 (no other arguments needed). Use -v for more information.
3087
3087
3088 Returns 0 on success.'''
3088 Returns 0 on success.'''
3089
3089
3090 q = repo.mq
3090 q = repo.mq
3091 guards = q.active()
3091 guards = q.active()
3092 if args or opts.get('none'):
3092 if args or opts.get('none'):
3093 old_unapplied = q.unapplied(repo)
3093 old_unapplied = q.unapplied(repo)
3094 old_guarded = [i for i in xrange(len(q.applied)) if
3094 old_guarded = [i for i in xrange(len(q.applied)) if
3095 not q.pushable(i)[0]]
3095 not q.pushable(i)[0]]
3096 q.setactive(args)
3096 q.setactive(args)
3097 q.savedirty()
3097 q.savedirty()
3098 if not args:
3098 if not args:
3099 ui.status(_('guards deactivated\n'))
3099 ui.status(_('guards deactivated\n'))
3100 if not opts.get('pop') and not opts.get('reapply'):
3100 if not opts.get('pop') and not opts.get('reapply'):
3101 unapplied = q.unapplied(repo)
3101 unapplied = q.unapplied(repo)
3102 guarded = [i for i in xrange(len(q.applied))
3102 guarded = [i for i in xrange(len(q.applied))
3103 if not q.pushable(i)[0]]
3103 if not q.pushable(i)[0]]
3104 if len(unapplied) != len(old_unapplied):
3104 if len(unapplied) != len(old_unapplied):
3105 ui.status(_('number of unguarded, unapplied patches has '
3105 ui.status(_('number of unguarded, unapplied patches has '
3106 'changed from %d to %d\n') %
3106 'changed from %d to %d\n') %
3107 (len(old_unapplied), len(unapplied)))
3107 (len(old_unapplied), len(unapplied)))
3108 if len(guarded) != len(old_guarded):
3108 if len(guarded) != len(old_guarded):
3109 ui.status(_('number of guarded, applied patches has changed '
3109 ui.status(_('number of guarded, applied patches has changed '
3110 'from %d to %d\n') %
3110 'from %d to %d\n') %
3111 (len(old_guarded), len(guarded)))
3111 (len(old_guarded), len(guarded)))
3112 elif opts.get('series'):
3112 elif opts.get('series'):
3113 guards = {}
3113 guards = {}
3114 noguards = 0
3114 noguards = 0
3115 for gs in q.seriesguards:
3115 for gs in q.seriesguards:
3116 if not gs:
3116 if not gs:
3117 noguards += 1
3117 noguards += 1
3118 for g in gs:
3118 for g in gs:
3119 guards.setdefault(g, 0)
3119 guards.setdefault(g, 0)
3120 guards[g] += 1
3120 guards[g] += 1
3121 if ui.verbose:
3121 if ui.verbose:
3122 guards['NONE'] = noguards
3122 guards['NONE'] = noguards
3123 guards = guards.items()
3123 guards = guards.items()
3124 guards.sort(key=lambda x: x[0][1:])
3124 guards.sort(key=lambda x: x[0][1:])
3125 if guards:
3125 if guards:
3126 ui.note(_('guards in series file:\n'))
3126 ui.note(_('guards in series file:\n'))
3127 for guard, count in guards:
3127 for guard, count in guards:
3128 ui.note('%2d ' % count)
3128 ui.note('%2d ' % count)
3129 ui.write(guard, '\n')
3129 ui.write(guard, '\n')
3130 else:
3130 else:
3131 ui.note(_('no guards in series file\n'))
3131 ui.note(_('no guards in series file\n'))
3132 else:
3132 else:
3133 if guards:
3133 if guards:
3134 ui.note(_('active guards:\n'))
3134 ui.note(_('active guards:\n'))
3135 for g in guards:
3135 for g in guards:
3136 ui.write(g, '\n')
3136 ui.write(g, '\n')
3137 else:
3137 else:
3138 ui.write(_('no active guards\n'))
3138 ui.write(_('no active guards\n'))
3139 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
3139 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
3140 popped = False
3140 popped = False
3141 if opts.get('pop') or opts.get('reapply'):
3141 if opts.get('pop') or opts.get('reapply'):
3142 for i in xrange(len(q.applied)):
3142 for i in xrange(len(q.applied)):
3143 pushable, reason = q.pushable(i)
3143 pushable, reason = q.pushable(i)
3144 if not pushable:
3144 if not pushable:
3145 ui.status(_('popping guarded patches\n'))
3145 ui.status(_('popping guarded patches\n'))
3146 popped = True
3146 popped = True
3147 if i == 0:
3147 if i == 0:
3148 q.pop(repo, all=True)
3148 q.pop(repo, all=True)
3149 else:
3149 else:
3150 q.pop(repo, str(i - 1))
3150 q.pop(repo, str(i - 1))
3151 break
3151 break
3152 if popped:
3152 if popped:
3153 try:
3153 try:
3154 if reapply:
3154 if reapply:
3155 ui.status(_('reapplying unguarded patches\n'))
3155 ui.status(_('reapplying unguarded patches\n'))
3156 q.push(repo, reapply)
3156 q.push(repo, reapply)
3157 finally:
3157 finally:
3158 q.savedirty()
3158 q.savedirty()
3159
3159
3160 @command("qfinish",
3160 @command("qfinish",
3161 [('a', 'applied', None, _('finish all applied changesets'))],
3161 [('a', 'applied', None, _('finish all applied changesets'))],
3162 _('hg qfinish [-a] [REV]...'))
3162 _('hg qfinish [-a] [REV]...'))
3163 def finish(ui, repo, *revrange, **opts):
3163 def finish(ui, repo, *revrange, **opts):
3164 """move applied patches into repository history
3164 """move applied patches into repository history
3165
3165
3166 Finishes the specified revisions (corresponding to applied
3166 Finishes the specified revisions (corresponding to applied
3167 patches) by moving them out of mq control into regular repository
3167 patches) by moving them out of mq control into regular repository
3168 history.
3168 history.
3169
3169
3170 Accepts a revision range or the -a/--applied option. If --applied
3170 Accepts a revision range or the -a/--applied option. If --applied
3171 is specified, all applied mq revisions are removed from mq
3171 is specified, all applied mq revisions are removed from mq
3172 control. Otherwise, the given revisions must be at the base of the
3172 control. Otherwise, the given revisions must be at the base of the
3173 stack of applied patches.
3173 stack of applied patches.
3174
3174
3175 This can be especially useful if your changes have been applied to
3175 This can be especially useful if your changes have been applied to
3176 an upstream repository, or if you are about to push your changes
3176 an upstream repository, or if you are about to push your changes
3177 to upstream.
3177 to upstream.
3178
3178
3179 Returns 0 on success.
3179 Returns 0 on success.
3180 """
3180 """
3181 if not opts.get('applied') and not revrange:
3181 if not opts.get('applied') and not revrange:
3182 raise util.Abort(_('no revisions specified'))
3182 raise util.Abort(_('no revisions specified'))
3183 elif opts.get('applied'):
3183 elif opts.get('applied'):
3184 revrange = ('qbase::qtip',) + revrange
3184 revrange = ('qbase::qtip',) + revrange
3185
3185
3186 q = repo.mq
3186 q = repo.mq
3187 if not q.applied:
3187 if not q.applied:
3188 ui.status(_('no patches applied\n'))
3188 ui.status(_('no patches applied\n'))
3189 return 0
3189 return 0
3190
3190
3191 revs = scmutil.revrange(repo, revrange)
3191 revs = scmutil.revrange(repo, revrange)
3192 if repo['.'].rev() in revs and repo[None].files():
3192 if repo['.'].rev() in revs and repo[None].files():
3193 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3193 ui.warn(_('warning: uncommitted changes in the working directory\n'))
3194 # queue.finish may changes phases but leave the responsibility to lock the
3194 # queue.finish may changes phases but leave the responsibility to lock the
3195 # repo to the caller to avoid deadlock with wlock. This command code is
3195 # repo to the caller to avoid deadlock with wlock. This command code is
3196 # responsibility for this locking.
3196 # responsibility for this locking.
3197 lock = repo.lock()
3197 lock = repo.lock()
3198 try:
3198 try:
3199 q.finish(repo, revs)
3199 q.finish(repo, revs)
3200 q.savedirty()
3200 q.savedirty()
3201 finally:
3201 finally:
3202 lock.release()
3202 lock.release()
3203 return 0
3203 return 0
3204
3204
3205 @command("qqueue",
3205 @command("qqueue",
3206 [('l', 'list', False, _('list all available queues')),
3206 [('l', 'list', False, _('list all available queues')),
3207 ('', 'active', False, _('print name of active queue')),
3207 ('', 'active', False, _('print name of active queue')),
3208 ('c', 'create', False, _('create new queue')),
3208 ('c', 'create', False, _('create new queue')),
3209 ('', 'rename', False, _('rename active queue')),
3209 ('', 'rename', False, _('rename active queue')),
3210 ('', 'delete', False, _('delete reference to queue')),
3210 ('', 'delete', False, _('delete reference to queue')),
3211 ('', 'purge', False, _('delete queue, and remove patch dir')),
3211 ('', 'purge', False, _('delete queue, and remove patch dir')),
3212 ],
3212 ],
3213 _('[OPTION] [QUEUE]'))
3213 _('[OPTION] [QUEUE]'))
3214 def qqueue(ui, repo, name=None, **opts):
3214 def qqueue(ui, repo, name=None, **opts):
3215 '''manage multiple patch queues
3215 '''manage multiple patch queues
3216
3216
3217 Supports switching between different patch queues, as well as creating
3217 Supports switching between different patch queues, as well as creating
3218 new patch queues and deleting existing ones.
3218 new patch queues and deleting existing ones.
3219
3219
3220 Omitting a queue name or specifying -l/--list will show you the registered
3220 Omitting a queue name or specifying -l/--list will show you the registered
3221 queues - by default the "normal" patches queue is registered. The currently
3221 queues - by default the "normal" patches queue is registered. The currently
3222 active queue will be marked with "(active)". Specifying --active will print
3222 active queue will be marked with "(active)". Specifying --active will print
3223 only the name of the active queue.
3223 only the name of the active queue.
3224
3224
3225 To create a new queue, use -c/--create. The queue is automatically made
3225 To create a new queue, use -c/--create. The queue is automatically made
3226 active, except in the case where there are applied patches from the
3226 active, except in the case where there are applied patches from the
3227 currently active queue in the repository. Then the queue will only be
3227 currently active queue in the repository. Then the queue will only be
3228 created and switching will fail.
3228 created and switching will fail.
3229
3229
3230 To delete an existing queue, use --delete. You cannot delete the currently
3230 To delete an existing queue, use --delete. You cannot delete the currently
3231 active queue.
3231 active queue.
3232
3232
3233 Returns 0 on success.
3233 Returns 0 on success.
3234 '''
3234 '''
3235 q = repo.mq
3235 q = repo.mq
3236 _defaultqueue = 'patches'
3236 _defaultqueue = 'patches'
3237 _allqueues = 'patches.queues'
3237 _allqueues = 'patches.queues'
3238 _activequeue = 'patches.queue'
3238 _activequeue = 'patches.queue'
3239
3239
3240 def _getcurrent():
3240 def _getcurrent():
3241 cur = os.path.basename(q.path)
3241 cur = os.path.basename(q.path)
3242 if cur.startswith('patches-'):
3242 if cur.startswith('patches-'):
3243 cur = cur[8:]
3243 cur = cur[8:]
3244 return cur
3244 return cur
3245
3245
3246 def _noqueues():
3246 def _noqueues():
3247 try:
3247 try:
3248 fh = repo.opener(_allqueues, 'r')
3248 fh = repo.opener(_allqueues, 'r')
3249 fh.close()
3249 fh.close()
3250 except IOError:
3250 except IOError:
3251 return True
3251 return True
3252
3252
3253 return False
3253 return False
3254
3254
3255 def _getqueues():
3255 def _getqueues():
3256 current = _getcurrent()
3256 current = _getcurrent()
3257
3257
3258 try:
3258 try:
3259 fh = repo.opener(_allqueues, 'r')
3259 fh = repo.opener(_allqueues, 'r')
3260 queues = [queue.strip() for queue in fh if queue.strip()]
3260 queues = [queue.strip() for queue in fh if queue.strip()]
3261 fh.close()
3261 fh.close()
3262 if current not in queues:
3262 if current not in queues:
3263 queues.append(current)
3263 queues.append(current)
3264 except IOError:
3264 except IOError:
3265 queues = [_defaultqueue]
3265 queues = [_defaultqueue]
3266
3266
3267 return sorted(queues)
3267 return sorted(queues)
3268
3268
3269 def _setactive(name):
3269 def _setactive(name):
3270 if q.applied:
3270 if q.applied:
3271 raise util.Abort(_('new queue created, but cannot make active '
3271 raise util.Abort(_('new queue created, but cannot make active '
3272 'as patches are applied'))
3272 'as patches are applied'))
3273 _setactivenocheck(name)
3273 _setactivenocheck(name)
3274
3274
3275 def _setactivenocheck(name):
3275 def _setactivenocheck(name):
3276 fh = repo.opener(_activequeue, 'w')
3276 fh = repo.opener(_activequeue, 'w')
3277 if name != 'patches':
3277 if name != 'patches':
3278 fh.write(name)
3278 fh.write(name)
3279 fh.close()
3279 fh.close()
3280
3280
3281 def _addqueue(name):
3281 def _addqueue(name):
3282 fh = repo.opener(_allqueues, 'a')
3282 fh = repo.opener(_allqueues, 'a')
3283 fh.write('%s\n' % (name,))
3283 fh.write('%s\n' % (name,))
3284 fh.close()
3284 fh.close()
3285
3285
3286 def _queuedir(name):
3286 def _queuedir(name):
3287 if name == 'patches':
3287 if name == 'patches':
3288 return repo.join('patches')
3288 return repo.join('patches')
3289 else:
3289 else:
3290 return repo.join('patches-' + name)
3290 return repo.join('patches-' + name)
3291
3291
3292 def _validname(name):
3292 def _validname(name):
3293 for n in name:
3293 for n in name:
3294 if n in ':\\/.':
3294 if n in ':\\/.':
3295 return False
3295 return False
3296 return True
3296 return True
3297
3297
3298 def _delete(name):
3298 def _delete(name):
3299 if name not in existing:
3299 if name not in existing:
3300 raise util.Abort(_('cannot delete queue that does not exist'))
3300 raise util.Abort(_('cannot delete queue that does not exist'))
3301
3301
3302 current = _getcurrent()
3302 current = _getcurrent()
3303
3303
3304 if name == current:
3304 if name == current:
3305 raise util.Abort(_('cannot delete currently active queue'))
3305 raise util.Abort(_('cannot delete currently active queue'))
3306
3306
3307 fh = repo.opener('patches.queues.new', 'w')
3307 fh = repo.opener('patches.queues.new', 'w')
3308 for queue in existing:
3308 for queue in existing:
3309 if queue == name:
3309 if queue == name:
3310 continue
3310 continue
3311 fh.write('%s\n' % (queue,))
3311 fh.write('%s\n' % (queue,))
3312 fh.close()
3312 fh.close()
3313 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3313 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3314
3314
3315 if not name or opts.get('list') or opts.get('active'):
3315 if not name or opts.get('list') or opts.get('active'):
3316 current = _getcurrent()
3316 current = _getcurrent()
3317 if opts.get('active'):
3317 if opts.get('active'):
3318 ui.write('%s\n' % (current,))
3318 ui.write('%s\n' % (current,))
3319 return
3319 return
3320 for queue in _getqueues():
3320 for queue in _getqueues():
3321 ui.write('%s' % (queue,))
3321 ui.write('%s' % (queue,))
3322 if queue == current and not ui.quiet:
3322 if queue == current and not ui.quiet:
3323 ui.write(_(' (active)\n'))
3323 ui.write(_(' (active)\n'))
3324 else:
3324 else:
3325 ui.write('\n')
3325 ui.write('\n')
3326 return
3326 return
3327
3327
3328 if not _validname(name):
3328 if not _validname(name):
3329 raise util.Abort(
3329 raise util.Abort(
3330 _('invalid queue name, may not contain the characters ":\\/."'))
3330 _('invalid queue name, may not contain the characters ":\\/."'))
3331
3331
3332 existing = _getqueues()
3332 existing = _getqueues()
3333
3333
3334 if opts.get('create'):
3334 if opts.get('create'):
3335 if name in existing:
3335 if name in existing:
3336 raise util.Abort(_('queue "%s" already exists') % name)
3336 raise util.Abort(_('queue "%s" already exists') % name)
3337 if _noqueues():
3337 if _noqueues():
3338 _addqueue(_defaultqueue)
3338 _addqueue(_defaultqueue)
3339 _addqueue(name)
3339 _addqueue(name)
3340 _setactive(name)
3340 _setactive(name)
3341 elif opts.get('rename'):
3341 elif opts.get('rename'):
3342 current = _getcurrent()
3342 current = _getcurrent()
3343 if name == current:
3343 if name == current:
3344 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3344 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
3345 if name in existing:
3345 if name in existing:
3346 raise util.Abort(_('queue "%s" already exists') % name)
3346 raise util.Abort(_('queue "%s" already exists') % name)
3347
3347
3348 olddir = _queuedir(current)
3348 olddir = _queuedir(current)
3349 newdir = _queuedir(name)
3349 newdir = _queuedir(name)
3350
3350
3351 if os.path.exists(newdir):
3351 if os.path.exists(newdir):
3352 raise util.Abort(_('non-queue directory "%s" already exists') %
3352 raise util.Abort(_('non-queue directory "%s" already exists') %
3353 newdir)
3353 newdir)
3354
3354
3355 fh = repo.opener('patches.queues.new', 'w')
3355 fh = repo.opener('patches.queues.new', 'w')
3356 for queue in existing:
3356 for queue in existing:
3357 if queue == current:
3357 if queue == current:
3358 fh.write('%s\n' % (name,))
3358 fh.write('%s\n' % (name,))
3359 if os.path.exists(olddir):
3359 if os.path.exists(olddir):
3360 util.rename(olddir, newdir)
3360 util.rename(olddir, newdir)
3361 else:
3361 else:
3362 fh.write('%s\n' % (queue,))
3362 fh.write('%s\n' % (queue,))
3363 fh.close()
3363 fh.close()
3364 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3364 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
3365 _setactivenocheck(name)
3365 _setactivenocheck(name)
3366 elif opts.get('delete'):
3366 elif opts.get('delete'):
3367 _delete(name)
3367 _delete(name)
3368 elif opts.get('purge'):
3368 elif opts.get('purge'):
3369 if name in existing:
3369 if name in existing:
3370 _delete(name)
3370 _delete(name)
3371 qdir = _queuedir(name)
3371 qdir = _queuedir(name)
3372 if os.path.exists(qdir):
3372 if os.path.exists(qdir):
3373 shutil.rmtree(qdir)
3373 shutil.rmtree(qdir)
3374 else:
3374 else:
3375 if name not in existing:
3375 if name not in existing:
3376 raise util.Abort(_('use --create to create a new queue'))
3376 raise util.Abort(_('use --create to create a new queue'))
3377 _setactive(name)
3377 _setactive(name)
3378
3378
3379 def mqphasedefaults(repo, roots):
3379 def mqphasedefaults(repo, roots):
3380 """callback used to set mq changeset as secret when no phase data exists"""
3380 """callback used to set mq changeset as secret when no phase data exists"""
3381 if repo.mq.applied:
3381 if repo.mq.applied:
3382 if repo.ui.configbool('mq', 'secret', False):
3382 if repo.ui.configbool('mq', 'secret', False):
3383 mqphase = phases.secret
3383 mqphase = phases.secret
3384 else:
3384 else:
3385 mqphase = phases.draft
3385 mqphase = phases.draft
3386 qbase = repo[repo.mq.applied[0].node]
3386 qbase = repo[repo.mq.applied[0].node]
3387 roots[mqphase].add(qbase.node())
3387 roots[mqphase].add(qbase.node())
3388 return roots
3388 return roots
3389
3389
3390 def reposetup(ui, repo):
3390 def reposetup(ui, repo):
3391 class mqrepo(repo.__class__):
3391 class mqrepo(repo.__class__):
3392 @util.propertycache
3392 @util.propertycache
3393 def mq(self):
3393 def mq(self):
3394 return queue(self.ui, self.path)
3394 return queue(self.ui, self.path)
3395
3395
3396 def abortifwdirpatched(self, errmsg, force=False):
3396 def abortifwdirpatched(self, errmsg, force=False):
3397 if self.mq.applied and not force:
3397 if self.mq.applied and not force:
3398 parents = self.dirstate.parents()
3398 parents = self.dirstate.parents()
3399 patches = [s.node for s in self.mq.applied]
3399 patches = [s.node for s in self.mq.applied]
3400 if parents[0] in patches or parents[1] in patches:
3400 if parents[0] in patches or parents[1] in patches:
3401 raise util.Abort(errmsg)
3401 raise util.Abort(errmsg)
3402
3402
3403 def commit(self, text="", user=None, date=None, match=None,
3403 def commit(self, text="", user=None, date=None, match=None,
3404 force=False, editor=False, extra={}):
3404 force=False, editor=False, extra={}):
3405 self.abortifwdirpatched(
3405 self.abortifwdirpatched(
3406 _('cannot commit over an applied mq patch'),
3406 _('cannot commit over an applied mq patch'),
3407 force)
3407 force)
3408
3408
3409 return super(mqrepo, self).commit(text, user, date, match, force,
3409 return super(mqrepo, self).commit(text, user, date, match, force,
3410 editor, extra)
3410 editor, extra)
3411
3411
3412 def checkpush(self, force, revs):
3412 def checkpush(self, force, revs):
3413 if self.mq.applied and not force:
3413 if self.mq.applied and not force:
3414 outapplied = [e.node for e in self.mq.applied]
3414 outapplied = [e.node for e in self.mq.applied]
3415 if revs:
3415 if revs:
3416 # Assume applied patches have no non-patch descendants and
3416 # Assume applied patches have no non-patch descendants and
3417 # are not on remote already. Filtering any changeset not
3417 # are not on remote already. Filtering any changeset not
3418 # pushed.
3418 # pushed.
3419 heads = set(revs)
3419 heads = set(revs)
3420 for node in reversed(outapplied):
3420 for node in reversed(outapplied):
3421 if node in heads:
3421 if node in heads:
3422 break
3422 break
3423 else:
3423 else:
3424 outapplied.pop()
3424 outapplied.pop()
3425 # looking for pushed and shared changeset
3425 # looking for pushed and shared changeset
3426 for node in outapplied:
3426 for node in outapplied:
3427 if repo[node].phase() < phases.secret:
3427 if repo[node].phase() < phases.secret:
3428 raise util.Abort(_('source has mq patches applied'))
3428 raise util.Abort(_('source has mq patches applied'))
3429 # no non-secret patches pushed
3429 # no non-secret patches pushed
3430 super(mqrepo, self).checkpush(force, revs)
3430 super(mqrepo, self).checkpush(force, revs)
3431
3431
3432 def _findtags(self):
3432 def _findtags(self):
3433 '''augment tags from base class with patch tags'''
3433 '''augment tags from base class with patch tags'''
3434 result = super(mqrepo, self)._findtags()
3434 result = super(mqrepo, self)._findtags()
3435
3435
3436 q = self.mq
3436 q = self.mq
3437 if not q.applied:
3437 if not q.applied:
3438 return result
3438 return result
3439
3439
3440 mqtags = [(patch.node, patch.name) for patch in q.applied]
3440 mqtags = [(patch.node, patch.name) for patch in q.applied]
3441
3441
3442 try:
3442 try:
3443 self.changelog.rev(mqtags[-1][0])
3443 self.changelog.rev(mqtags[-1][0])
3444 except error.LookupError:
3444 except error.LookupError:
3445 self.ui.warn(_('mq status file refers to unknown node %s\n')
3445 self.ui.warn(_('mq status file refers to unknown node %s\n')
3446 % short(mqtags[-1][0]))
3446 % short(mqtags[-1][0]))
3447 return result
3447 return result
3448
3448
3449 mqtags.append((mqtags[-1][0], 'qtip'))
3449 mqtags.append((mqtags[-1][0], 'qtip'))
3450 mqtags.append((mqtags[0][0], 'qbase'))
3450 mqtags.append((mqtags[0][0], 'qbase'))
3451 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3451 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
3452 tags = result[0]
3452 tags = result[0]
3453 for patch in mqtags:
3453 for patch in mqtags:
3454 if patch[1] in tags:
3454 if patch[1] in tags:
3455 self.ui.warn(_('tag %s overrides mq patch of the same '
3455 self.ui.warn(_('tag %s overrides mq patch of the same '
3456 'name\n') % patch[1])
3456 'name\n') % patch[1])
3457 else:
3457 else:
3458 tags[patch[1]] = patch[0]
3458 tags[patch[1]] = patch[0]
3459
3459
3460 return result
3460 return result
3461
3461
3462 def _branchtags(self, partial, lrev):
3462 def _branchtags(self, partial, lrev):
3463 q = self.mq
3463 q = self.mq
3464 cl = self.changelog
3464 cl = self.changelog
3465 qbase = None
3465 qbase = None
3466 if not q.applied:
3466 if not q.applied:
3467 if getattr(self, '_committingpatch', False):
3467 if getattr(self, '_committingpatch', False):
3468 # Committing a new patch, must be tip
3468 # Committing a new patch, must be tip
3469 qbase = len(cl) - 1
3469 qbase = len(cl) - 1
3470 else:
3470 else:
3471 qbasenode = q.applied[0].node
3471 qbasenode = q.applied[0].node
3472 try:
3472 try:
3473 qbase = cl.rev(qbasenode)
3473 qbase = cl.rev(qbasenode)
3474 except error.LookupError:
3474 except error.LookupError:
3475 self.ui.warn(_('mq status file refers to unknown node %s\n')
3475 self.ui.warn(_('mq status file refers to unknown node %s\n')
3476 % short(qbasenode))
3476 % short(qbasenode))
3477 if qbase is None:
3477 if qbase is None:
3478 return super(mqrepo, self)._branchtags(partial, lrev)
3478 return super(mqrepo, self)._branchtags(partial, lrev)
3479
3479
3480 start = lrev + 1
3480 start = lrev + 1
3481 if start < qbase:
3481 if start < qbase:
3482 # update the cache (excluding the patches) and save it
3482 # update the cache (excluding the patches) and save it
3483 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
3483 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
3484 self._updatebranchcache(partial, ctxgen)
3484 self._updatebranchcache(partial, ctxgen)
3485 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
3485 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
3486 start = qbase
3486 start = qbase
3487 # if start = qbase, the cache is as updated as it should be.
3487 # if start = qbase, the cache is as updated as it should be.
3488 # if start > qbase, the cache includes (part of) the patches.
3488 # if start > qbase, the cache includes (part of) the patches.
3489 # we might as well use it, but we won't save it.
3489 # we might as well use it, but we won't save it.
3490
3490
3491 # update the cache up to the tip
3491 # update the cache up to the tip
3492 ctxgen = (self[r] for r in xrange(start, len(cl)))
3492 ctxgen = (self[r] for r in xrange(start, len(cl)))
3493 self._updatebranchcache(partial, ctxgen)
3493 self._updatebranchcache(partial, ctxgen)
3494
3494
3495 return partial
3495 return partial
3496
3496
3497 if repo.local():
3497 if repo.local():
3498 repo.__class__ = mqrepo
3498 repo.__class__ = mqrepo
3499
3499
3500 repo._phasedefaults.append(mqphasedefaults)
3500 repo._phasedefaults.append(mqphasedefaults)
3501
3501
3502 def mqimport(orig, ui, repo, *args, **kwargs):
3502 def mqimport(orig, ui, repo, *args, **kwargs):
3503 if (util.safehasattr(repo, 'abortifwdirpatched')
3503 if (util.safehasattr(repo, 'abortifwdirpatched')
3504 and not kwargs.get('no_commit', False)):
3504 and not kwargs.get('no_commit', False)):
3505 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3505 repo.abortifwdirpatched(_('cannot import over an applied patch'),
3506 kwargs.get('force'))
3506 kwargs.get('force'))
3507 return orig(ui, repo, *args, **kwargs)
3507 return orig(ui, repo, *args, **kwargs)
3508
3508
3509 def mqinit(orig, ui, *args, **kwargs):
3509 def mqinit(orig, ui, *args, **kwargs):
3510 mq = kwargs.pop('mq', None)
3510 mq = kwargs.pop('mq', None)
3511
3511
3512 if not mq:
3512 if not mq:
3513 return orig(ui, *args, **kwargs)
3513 return orig(ui, *args, **kwargs)
3514
3514
3515 if args:
3515 if args:
3516 repopath = args[0]
3516 repopath = args[0]
3517 if not hg.islocal(repopath):
3517 if not hg.islocal(repopath):
3518 raise util.Abort(_('only a local queue repository '
3518 raise util.Abort(_('only a local queue repository '
3519 'may be initialized'))
3519 'may be initialized'))
3520 else:
3520 else:
3521 repopath = cmdutil.findrepo(os.getcwd())
3521 repopath = cmdutil.findrepo(os.getcwd())
3522 if not repopath:
3522 if not repopath:
3523 raise util.Abort(_('there is no Mercurial repository here '
3523 raise util.Abort(_('there is no Mercurial repository here '
3524 '(.hg not found)'))
3524 '(.hg not found)'))
3525 repo = hg.repository(ui, repopath)
3525 repo = hg.repository(ui, repopath)
3526 return qinit(ui, repo, True)
3526 return qinit(ui, repo, True)
3527
3527
3528 def mqcommand(orig, ui, repo, *args, **kwargs):
3528 def mqcommand(orig, ui, repo, *args, **kwargs):
3529 """Add --mq option to operate on patch repository instead of main"""
3529 """Add --mq option to operate on patch repository instead of main"""
3530
3530
3531 # some commands do not like getting unknown options
3531 # some commands do not like getting unknown options
3532 mq = kwargs.pop('mq', None)
3532 mq = kwargs.pop('mq', None)
3533
3533
3534 if not mq:
3534 if not mq:
3535 return orig(ui, repo, *args, **kwargs)
3535 return orig(ui, repo, *args, **kwargs)
3536
3536
3537 q = repo.mq
3537 q = repo.mq
3538 r = q.qrepo()
3538 r = q.qrepo()
3539 if not r:
3539 if not r:
3540 raise util.Abort(_('no queue repository'))
3540 raise util.Abort(_('no queue repository'))
3541 return orig(r.ui, r, *args, **kwargs)
3541 return orig(r.ui, r, *args, **kwargs)
3542
3542
3543 def summary(orig, ui, repo, *args, **kwargs):
3543 def summary(orig, ui, repo, *args, **kwargs):
3544 r = orig(ui, repo, *args, **kwargs)
3544 r = orig(ui, repo, *args, **kwargs)
3545 q = repo.mq
3545 q = repo.mq
3546 m = []
3546 m = []
3547 a, u = len(q.applied), len(q.unapplied(repo))
3547 a, u = len(q.applied), len(q.unapplied(repo))
3548 if a:
3548 if a:
3549 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3549 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3550 if u:
3550 if u:
3551 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3551 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3552 if m:
3552 if m:
3553 ui.write("mq: %s\n" % ', '.join(m))
3553 ui.write("mq: %s\n" % ', '.join(m))
3554 else:
3554 else:
3555 ui.note(_("mq: (empty queue)\n"))
3555 ui.note(_("mq: (empty queue)\n"))
3556 return r
3556 return r
3557
3557
3558 def revsetmq(repo, subset, x):
3558 def revsetmq(repo, subset, x):
3559 """``mq()``
3559 """``mq()``
3560 Changesets managed by MQ.
3560 Changesets managed by MQ.
3561 """
3561 """
3562 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3562 revset.getargs(x, 0, 0, _("mq takes no arguments"))
3563 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3563 applied = set([repo[r.node].rev() for r in repo.mq.applied])
3564 return [r for r in subset if r in applied]
3564 return [r for r in subset if r in applied]
3565
3565
3566 # tell hggettext to extract docstrings from these functions:
3566 # tell hggettext to extract docstrings from these functions:
3567 i18nfunctions = [revsetmq]
3567 i18nfunctions = [revsetmq]
3568
3568
3569 def extsetup(ui):
3569 def extsetup(ui):
3570 # Ensure mq wrappers are called first, regardless of extension load order by
3570 # Ensure mq wrappers are called first, regardless of extension load order by
3571 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3571 # NOT wrapping in uisetup() and instead deferring to init stage two here.
3572 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3572 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3573
3573
3574 extensions.wrapcommand(commands.table, 'import', mqimport)
3574 extensions.wrapcommand(commands.table, 'import', mqimport)
3575 extensions.wrapcommand(commands.table, 'summary', summary)
3575 extensions.wrapcommand(commands.table, 'summary', summary)
3576
3576
3577 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3577 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3578 entry[1].extend(mqopt)
3578 entry[1].extend(mqopt)
3579
3579
3580 nowrap = set(commands.norepo.split(" "))
3580 nowrap = set(commands.norepo.split(" "))
3581
3581
3582 def dotable(cmdtable):
3582 def dotable(cmdtable):
3583 for cmd in cmdtable.keys():
3583 for cmd in cmdtable.keys():
3584 cmd = cmdutil.parsealiases(cmd)[0]
3584 cmd = cmdutil.parsealiases(cmd)[0]
3585 if cmd in nowrap:
3585 if cmd in nowrap:
3586 continue
3586 continue
3587 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3587 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3588 entry[1].extend(mqopt)
3588 entry[1].extend(mqopt)
3589
3589
3590 dotable(commands.table)
3590 dotable(commands.table)
3591
3591
3592 for extname, extmodule in extensions.extensions():
3592 for extname, extmodule in extensions.extensions():
3593 if extmodule.__file__ != __file__:
3593 if extmodule.__file__ != __file__:
3594 dotable(getattr(extmodule, 'cmdtable', {}))
3594 dotable(getattr(extmodule, 'cmdtable', {}))
3595
3595
3596 revset.symbols['mq'] = revsetmq
3596 revset.symbols['mq'] = revsetmq
3597
3597
3598 colortable = {'qguard.negative': 'red',
3598 colortable = {'qguard.negative': 'red',
3599 'qguard.positive': 'yellow',
3599 'qguard.positive': 'yellow',
3600 'qguard.unguarded': 'green',
3600 'qguard.unguarded': 'green',
3601 'qseries.applied': 'blue bold underline',
3601 'qseries.applied': 'blue bold underline',
3602 'qseries.guarded': 'black bold',
3602 'qseries.guarded': 'black bold',
3603 'qseries.missing': 'red bold',
3603 'qseries.missing': 'red bold',
3604 'qseries.unapplied': 'black bold'}
3604 'qseries.unapplied': 'black bold'}
3605
3605
3606 commands.inferrepo += " qnew qrefresh qdiff qcommit"
3606 commands.inferrepo += " qnew qrefresh qdiff qcommit"
@@ -1,5956 +1,5956 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 _, gettext
10 from i18n import _, gettext
11 import os, re, difflib, time, tempfile, errno
11 import os, re, difflib, time, tempfile, errno
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13 import patch, help, url, encoding, templatekw, discovery
13 import patch, help, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, hbisect
14 import archival, changegroup, cmdutil, hbisect
15 import sshserver, hgweb, hgweb.server, commandserver
15 import sshserver, hgweb, hgweb.server, commandserver
16 import merge as mergemod
16 import merge as mergemod
17 import minirst, revset, fileset
17 import minirst, revset, fileset
18 import dagparser, context, simplemerge, graphmod
18 import dagparser, context, simplemerge, graphmod
19 import random, setdiscovery, treediscovery, dagutil, pvec, localrepo
19 import random, setdiscovery, treediscovery, dagutil, pvec, localrepo
20 import phases, obsolete
20 import phases, obsolete
21
21
22 table = {}
22 table = {}
23
23
24 command = cmdutil.command(table)
24 command = cmdutil.command(table)
25
25
26 # common command options
26 # common command options
27
27
28 globalopts = [
28 globalopts = [
29 ('R', 'repository', '',
29 ('R', 'repository', '',
30 _('repository root directory or name of overlay bundle file'),
30 _('repository root directory or name of overlay bundle file'),
31 _('REPO')),
31 _('REPO')),
32 ('', 'cwd', '',
32 ('', 'cwd', '',
33 _('change working directory'), _('DIR')),
33 _('change working directory'), _('DIR')),
34 ('y', 'noninteractive', None,
34 ('y', 'noninteractive', None,
35 _('do not prompt, automatically pick the first choice for all prompts')),
35 _('do not prompt, automatically pick the first choice for all prompts')),
36 ('q', 'quiet', None, _('suppress output')),
36 ('q', 'quiet', None, _('suppress output')),
37 ('v', 'verbose', None, _('enable additional output')),
37 ('v', 'verbose', None, _('enable additional output')),
38 ('', 'config', [],
38 ('', 'config', [],
39 _('set/override config option (use \'section.name=value\')'),
39 _('set/override config option (use \'section.name=value\')'),
40 _('CONFIG')),
40 _('CONFIG')),
41 ('', 'debug', None, _('enable debugging output')),
41 ('', 'debug', None, _('enable debugging output')),
42 ('', 'debugger', None, _('start debugger')),
42 ('', 'debugger', None, _('start debugger')),
43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
43 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
44 _('ENCODE')),
44 _('ENCODE')),
45 ('', 'encodingmode', encoding.encodingmode,
45 ('', 'encodingmode', encoding.encodingmode,
46 _('set the charset encoding mode'), _('MODE')),
46 _('set the charset encoding mode'), _('MODE')),
47 ('', 'traceback', None, _('always print a traceback on exception')),
47 ('', 'traceback', None, _('always print a traceback on exception')),
48 ('', 'time', None, _('time how long the command takes')),
48 ('', 'time', None, _('time how long the command takes')),
49 ('', 'profile', None, _('print command execution profile')),
49 ('', 'profile', None, _('print command execution profile')),
50 ('', 'version', None, _('output version information and exit')),
50 ('', 'version', None, _('output version information and exit')),
51 ('h', 'help', None, _('display help and exit')),
51 ('h', 'help', None, _('display help and exit')),
52 ]
52 ]
53
53
54 dryrunopts = [('n', 'dry-run', None,
54 dryrunopts = [('n', 'dry-run', None,
55 _('do not perform actions, just print output'))]
55 _('do not perform actions, just print output'))]
56
56
57 remoteopts = [
57 remoteopts = [
58 ('e', 'ssh', '',
58 ('e', 'ssh', '',
59 _('specify ssh command to use'), _('CMD')),
59 _('specify ssh command to use'), _('CMD')),
60 ('', 'remotecmd', '',
60 ('', 'remotecmd', '',
61 _('specify hg command to run on the remote side'), _('CMD')),
61 _('specify hg command to run on the remote side'), _('CMD')),
62 ('', 'insecure', None,
62 ('', 'insecure', None,
63 _('do not verify server certificate (ignoring web.cacerts config)')),
63 _('do not verify server certificate (ignoring web.cacerts config)')),
64 ]
64 ]
65
65
66 walkopts = [
66 walkopts = [
67 ('I', 'include', [],
67 ('I', 'include', [],
68 _('include names matching the given patterns'), _('PATTERN')),
68 _('include names matching the given patterns'), _('PATTERN')),
69 ('X', 'exclude', [],
69 ('X', 'exclude', [],
70 _('exclude names matching the given patterns'), _('PATTERN')),
70 _('exclude names matching the given patterns'), _('PATTERN')),
71 ]
71 ]
72
72
73 commitopts = [
73 commitopts = [
74 ('m', 'message', '',
74 ('m', 'message', '',
75 _('use text as commit message'), _('TEXT')),
75 _('use text as commit message'), _('TEXT')),
76 ('l', 'logfile', '',
76 ('l', 'logfile', '',
77 _('read commit message from file'), _('FILE')),
77 _('read commit message from file'), _('FILE')),
78 ]
78 ]
79
79
80 commitopts2 = [
80 commitopts2 = [
81 ('d', 'date', '',
81 ('d', 'date', '',
82 _('record the specified date as commit date'), _('DATE')),
82 _('record the specified date as commit date'), _('DATE')),
83 ('u', 'user', '',
83 ('u', 'user', '',
84 _('record the specified user as committer'), _('USER')),
84 _('record the specified user as committer'), _('USER')),
85 ]
85 ]
86
86
87 templateopts = [
87 templateopts = [
88 ('', 'style', '',
88 ('', 'style', '',
89 _('display using template map file'), _('STYLE')),
89 _('display using template map file'), _('STYLE')),
90 ('', 'template', '',
90 ('', 'template', '',
91 _('display with template'), _('TEMPLATE')),
91 _('display with template'), _('TEMPLATE')),
92 ]
92 ]
93
93
94 logopts = [
94 logopts = [
95 ('p', 'patch', None, _('show patch')),
95 ('p', 'patch', None, _('show patch')),
96 ('g', 'git', None, _('use git extended diff format')),
96 ('g', 'git', None, _('use git extended diff format')),
97 ('l', 'limit', '',
97 ('l', 'limit', '',
98 _('limit number of changes displayed'), _('NUM')),
98 _('limit number of changes displayed'), _('NUM')),
99 ('M', 'no-merges', None, _('do not show merges')),
99 ('M', 'no-merges', None, _('do not show merges')),
100 ('', 'stat', None, _('output diffstat-style summary of changes')),
100 ('', 'stat', None, _('output diffstat-style summary of changes')),
101 ('G', 'graph', None, _("show the revision DAG")),
101 ('G', 'graph', None, _("show the revision DAG")),
102 ] + templateopts
102 ] + templateopts
103
103
104 diffopts = [
104 diffopts = [
105 ('a', 'text', None, _('treat all files as text')),
105 ('a', 'text', None, _('treat all files as text')),
106 ('g', 'git', None, _('use git extended diff format')),
106 ('g', 'git', None, _('use git extended diff format')),
107 ('', 'nodates', None, _('omit dates from diff headers'))
107 ('', 'nodates', None, _('omit dates from diff headers'))
108 ]
108 ]
109
109
110 diffwsopts = [
110 diffwsopts = [
111 ('w', 'ignore-all-space', None,
111 ('w', 'ignore-all-space', None,
112 _('ignore white space when comparing lines')),
112 _('ignore white space when comparing lines')),
113 ('b', 'ignore-space-change', None,
113 ('b', 'ignore-space-change', None,
114 _('ignore changes in the amount of white space')),
114 _('ignore changes in the amount of white space')),
115 ('B', 'ignore-blank-lines', None,
115 ('B', 'ignore-blank-lines', None,
116 _('ignore changes whose lines are all blank')),
116 _('ignore changes whose lines are all blank')),
117 ]
117 ]
118
118
119 diffopts2 = [
119 diffopts2 = [
120 ('p', 'show-function', None, _('show which function each change is in')),
120 ('p', 'show-function', None, _('show which function each change is in')),
121 ('', 'reverse', None, _('produce a diff that undoes the changes')),
121 ('', 'reverse', None, _('produce a diff that undoes the changes')),
122 ] + diffwsopts + [
122 ] + diffwsopts + [
123 ('U', 'unified', '',
123 ('U', 'unified', '',
124 _('number of lines of context to show'), _('NUM')),
124 _('number of lines of context to show'), _('NUM')),
125 ('', 'stat', None, _('output diffstat-style summary of changes')),
125 ('', 'stat', None, _('output diffstat-style summary of changes')),
126 ]
126 ]
127
127
128 mergetoolopts = [
128 mergetoolopts = [
129 ('t', 'tool', '', _('specify merge tool')),
129 ('t', 'tool', '', _('specify merge tool')),
130 ]
130 ]
131
131
132 similarityopts = [
132 similarityopts = [
133 ('s', 'similarity', '',
133 ('s', 'similarity', '',
134 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
134 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
135 ]
135 ]
136
136
137 subrepoopts = [
137 subrepoopts = [
138 ('S', 'subrepos', None,
138 ('S', 'subrepos', None,
139 _('recurse into subrepositories'))
139 _('recurse into subrepositories'))
140 ]
140 ]
141
141
142 # Commands start here, listed alphabetically
142 # Commands start here, listed alphabetically
143
143
144 @command('^add',
144 @command('^add',
145 walkopts + subrepoopts + dryrunopts,
145 walkopts + subrepoopts + dryrunopts,
146 _('[OPTION]... [FILE]...'))
146 _('[OPTION]... [FILE]...'))
147 def add(ui, repo, *pats, **opts):
147 def add(ui, repo, *pats, **opts):
148 """add the specified files on the next commit
148 """add the specified files on the next commit
149
149
150 Schedule files to be version controlled and added to the
150 Schedule files to be version controlled and added to the
151 repository.
151 repository.
152
152
153 The files will be added to the repository at the next commit. To
153 The files will be added to the repository at the next commit. To
154 undo an add before that, see :hg:`forget`.
154 undo an add before that, see :hg:`forget`.
155
155
156 If no names are given, add all files to the repository.
156 If no names are given, add all files to the repository.
157
157
158 .. container:: verbose
158 .. container:: verbose
159
159
160 An example showing how new (unknown) files are added
160 An example showing how new (unknown) files are added
161 automatically by :hg:`add`::
161 automatically by :hg:`add`::
162
162
163 $ ls
163 $ ls
164 foo.c
164 foo.c
165 $ hg status
165 $ hg status
166 ? foo.c
166 ? foo.c
167 $ hg add
167 $ hg add
168 adding foo.c
168 adding foo.c
169 $ hg status
169 $ hg status
170 A foo.c
170 A foo.c
171
171
172 Returns 0 if all files are successfully added.
172 Returns 0 if all files are successfully added.
173 """
173 """
174
174
175 m = scmutil.match(repo[None], pats, opts)
175 m = scmutil.match(repo[None], pats, opts)
176 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
176 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
177 opts.get('subrepos'), prefix="", explicitonly=False)
177 opts.get('subrepos'), prefix="", explicitonly=False)
178 return rejected and 1 or 0
178 return rejected and 1 or 0
179
179
180 @command('addremove',
180 @command('addremove',
181 similarityopts + walkopts + dryrunopts,
181 similarityopts + walkopts + dryrunopts,
182 _('[OPTION]... [FILE]...'))
182 _('[OPTION]... [FILE]...'))
183 def addremove(ui, repo, *pats, **opts):
183 def addremove(ui, repo, *pats, **opts):
184 """add all new files, delete all missing files
184 """add all new files, delete all missing files
185
185
186 Add all new files and remove all missing files from the
186 Add all new files and remove all missing files from the
187 repository.
187 repository.
188
188
189 New files are ignored if they match any of the patterns in
189 New files are ignored if they match any of the patterns in
190 ``.hgignore``. As with add, these changes take effect at the next
190 ``.hgignore``. As with add, these changes take effect at the next
191 commit.
191 commit.
192
192
193 Use the -s/--similarity option to detect renamed files. This
193 Use the -s/--similarity option to detect renamed files. This
194 option takes a percentage between 0 (disabled) and 100 (files must
194 option takes a percentage between 0 (disabled) and 100 (files must
195 be identical) as its parameter. With a parameter greater than 0,
195 be identical) as its parameter. With a parameter greater than 0,
196 this compares every removed file with every added file and records
196 this compares every removed file with every added file and records
197 those similar enough as renames. Detecting renamed files this way
197 those similar enough as renames. Detecting renamed files this way
198 can be expensive. After using this option, :hg:`status -C` can be
198 can be expensive. After using this option, :hg:`status -C` can be
199 used to check which files were identified as moved or renamed. If
199 used to check which files were identified as moved or renamed. If
200 not specified, -s/--similarity defaults to 100 and only renames of
200 not specified, -s/--similarity defaults to 100 and only renames of
201 identical files are detected.
201 identical files are detected.
202
202
203 Returns 0 if all files are successfully added.
203 Returns 0 if all files are successfully added.
204 """
204 """
205 try:
205 try:
206 sim = float(opts.get('similarity') or 100)
206 sim = float(opts.get('similarity') or 100)
207 except ValueError:
207 except ValueError:
208 raise util.Abort(_('similarity must be a number'))
208 raise util.Abort(_('similarity must be a number'))
209 if sim < 0 or sim > 100:
209 if sim < 0 or sim > 100:
210 raise util.Abort(_('similarity must be between 0 and 100'))
210 raise util.Abort(_('similarity must be between 0 and 100'))
211 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
211 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
212
212
213 @command('^annotate|blame',
213 @command('^annotate|blame',
214 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
214 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
215 ('', 'follow', None,
215 ('', 'follow', None,
216 _('follow copies/renames and list the filename (DEPRECATED)')),
216 _('follow copies/renames and list the filename (DEPRECATED)')),
217 ('', 'no-follow', None, _("don't follow copies and renames")),
217 ('', 'no-follow', None, _("don't follow copies and renames")),
218 ('a', 'text', None, _('treat all files as text')),
218 ('a', 'text', None, _('treat all files as text')),
219 ('u', 'user', None, _('list the author (long with -v)')),
219 ('u', 'user', None, _('list the author (long with -v)')),
220 ('f', 'file', None, _('list the filename')),
220 ('f', 'file', None, _('list the filename')),
221 ('d', 'date', None, _('list the date (short with -q)')),
221 ('d', 'date', None, _('list the date (short with -q)')),
222 ('n', 'number', None, _('list the revision number (default)')),
222 ('n', 'number', None, _('list the revision number (default)')),
223 ('c', 'changeset', None, _('list the changeset')),
223 ('c', 'changeset', None, _('list the changeset')),
224 ('l', 'line-number', None, _('show line number at the first appearance'))
224 ('l', 'line-number', None, _('show line number at the first appearance'))
225 ] + diffwsopts + walkopts,
225 ] + diffwsopts + walkopts,
226 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
226 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
227 def annotate(ui, repo, *pats, **opts):
227 def annotate(ui, repo, *pats, **opts):
228 """show changeset information by line for each file
228 """show changeset information by line for each file
229
229
230 List changes in files, showing the revision id responsible for
230 List changes in files, showing the revision id responsible for
231 each line
231 each line
232
232
233 This command is useful for discovering when a change was made and
233 This command is useful for discovering when a change was made and
234 by whom.
234 by whom.
235
235
236 Without the -a/--text option, annotate will avoid processing files
236 Without the -a/--text option, annotate will avoid processing files
237 it detects as binary. With -a, annotate will annotate the file
237 it detects as binary. With -a, annotate will annotate the file
238 anyway, although the results will probably be neither useful
238 anyway, although the results will probably be neither useful
239 nor desirable.
239 nor desirable.
240
240
241 Returns 0 on success.
241 Returns 0 on success.
242 """
242 """
243 if opts.get('follow'):
243 if opts.get('follow'):
244 # --follow is deprecated and now just an alias for -f/--file
244 # --follow is deprecated and now just an alias for -f/--file
245 # to mimic the behavior of Mercurial before version 1.5
245 # to mimic the behavior of Mercurial before version 1.5
246 opts['file'] = True
246 opts['file'] = True
247
247
248 datefunc = ui.quiet and util.shortdate or util.datestr
248 datefunc = ui.quiet and util.shortdate or util.datestr
249 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
249 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
250
250
251 if not pats:
251 if not pats:
252 raise util.Abort(_('at least one filename or pattern is required'))
252 raise util.Abort(_('at least one filename or pattern is required'))
253
253
254 hexfn = ui.debugflag and hex or short
254 hexfn = ui.debugflag and hex or short
255
255
256 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
256 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
257 ('number', ' ', lambda x: str(x[0].rev())),
257 ('number', ' ', lambda x: str(x[0].rev())),
258 ('changeset', ' ', lambda x: hexfn(x[0].node())),
258 ('changeset', ' ', lambda x: hexfn(x[0].node())),
259 ('date', ' ', getdate),
259 ('date', ' ', getdate),
260 ('file', ' ', lambda x: x[0].path()),
260 ('file', ' ', lambda x: x[0].path()),
261 ('line_number', ':', lambda x: str(x[1])),
261 ('line_number', ':', lambda x: str(x[1])),
262 ]
262 ]
263
263
264 if (not opts.get('user') and not opts.get('changeset')
264 if (not opts.get('user') and not opts.get('changeset')
265 and not opts.get('date') and not opts.get('file')):
265 and not opts.get('date') and not opts.get('file')):
266 opts['number'] = True
266 opts['number'] = True
267
267
268 linenumber = opts.get('line_number') is not None
268 linenumber = opts.get('line_number') is not None
269 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
269 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
270 raise util.Abort(_('at least one of -n/-c is required for -l'))
270 raise util.Abort(_('at least one of -n/-c is required for -l'))
271
271
272 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
272 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
273 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
273 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
274
274
275 def bad(x, y):
275 def bad(x, y):
276 raise util.Abort("%s: %s" % (x, y))
276 raise util.Abort("%s: %s" % (x, y))
277
277
278 ctx = scmutil.revsingle(repo, opts.get('rev'))
278 ctx = scmutil.revsingle(repo, opts.get('rev'))
279 m = scmutil.match(ctx, pats, opts)
279 m = scmutil.match(ctx, pats, opts)
280 m.bad = bad
280 m.bad = bad
281 follow = not opts.get('no_follow')
281 follow = not opts.get('no_follow')
282 diffopts = patch.diffopts(ui, opts, section='annotate')
282 diffopts = patch.diffopts(ui, opts, section='annotate')
283 for abs in ctx.walk(m):
283 for abs in ctx.walk(m):
284 fctx = ctx[abs]
284 fctx = ctx[abs]
285 if not opts.get('text') and util.binary(fctx.data()):
285 if not opts.get('text') and util.binary(fctx.data()):
286 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
286 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
287 continue
287 continue
288
288
289 lines = fctx.annotate(follow=follow, linenumber=linenumber,
289 lines = fctx.annotate(follow=follow, linenumber=linenumber,
290 diffopts=diffopts)
290 diffopts=diffopts)
291 pieces = []
291 pieces = []
292
292
293 for f, sep in funcmap:
293 for f, sep in funcmap:
294 l = [f(n) for n, dummy in lines]
294 l = [f(n) for n, dummy in lines]
295 if l:
295 if l:
296 sized = [(x, encoding.colwidth(x)) for x in l]
296 sized = [(x, encoding.colwidth(x)) for x in l]
297 ml = max([w for x, w in sized])
297 ml = max([w for x, w in sized])
298 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
298 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
299 for x, w in sized])
299 for x, w in sized])
300
300
301 if pieces:
301 if pieces:
302 for p, l in zip(zip(*pieces), lines):
302 for p, l in zip(zip(*pieces), lines):
303 ui.write("%s: %s" % ("".join(p), l[1]))
303 ui.write("%s: %s" % ("".join(p), l[1]))
304
304
305 if lines and not lines[-1][1].endswith('\n'):
305 if lines and not lines[-1][1].endswith('\n'):
306 ui.write('\n')
306 ui.write('\n')
307
307
308 @command('archive',
308 @command('archive',
309 [('', 'no-decode', None, _('do not pass files through decoders')),
309 [('', 'no-decode', None, _('do not pass files through decoders')),
310 ('p', 'prefix', '', _('directory prefix for files in archive'),
310 ('p', 'prefix', '', _('directory prefix for files in archive'),
311 _('PREFIX')),
311 _('PREFIX')),
312 ('r', 'rev', '', _('revision to distribute'), _('REV')),
312 ('r', 'rev', '', _('revision to distribute'), _('REV')),
313 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
313 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
314 ] + subrepoopts + walkopts,
314 ] + subrepoopts + walkopts,
315 _('[OPTION]... DEST'))
315 _('[OPTION]... DEST'))
316 def archive(ui, repo, dest, **opts):
316 def archive(ui, repo, dest, **opts):
317 '''create an unversioned archive of a repository revision
317 '''create an unversioned archive of a repository revision
318
318
319 By default, the revision used is the parent of the working
319 By default, the revision used is the parent of the working
320 directory; use -r/--rev to specify a different revision.
320 directory; use -r/--rev to specify a different revision.
321
321
322 The archive type is automatically detected based on file
322 The archive type is automatically detected based on file
323 extension (or override using -t/--type).
323 extension (or override using -t/--type).
324
324
325 .. container:: verbose
325 .. container:: verbose
326
326
327 Examples:
327 Examples:
328
328
329 - create a zip file containing the 1.0 release::
329 - create a zip file containing the 1.0 release::
330
330
331 hg archive -r 1.0 project-1.0.zip
331 hg archive -r 1.0 project-1.0.zip
332
332
333 - create a tarball excluding .hg files::
333 - create a tarball excluding .hg files::
334
334
335 hg archive project.tar.gz -X ".hg*"
335 hg archive project.tar.gz -X ".hg*"
336
336
337 Valid types are:
337 Valid types are:
338
338
339 :``files``: a directory full of files (default)
339 :``files``: a directory full of files (default)
340 :``tar``: tar archive, uncompressed
340 :``tar``: tar archive, uncompressed
341 :``tbz2``: tar archive, compressed using bzip2
341 :``tbz2``: tar archive, compressed using bzip2
342 :``tgz``: tar archive, compressed using gzip
342 :``tgz``: tar archive, compressed using gzip
343 :``uzip``: zip archive, uncompressed
343 :``uzip``: zip archive, uncompressed
344 :``zip``: zip archive, compressed using deflate
344 :``zip``: zip archive, compressed using deflate
345
345
346 The exact name of the destination archive or directory is given
346 The exact name of the destination archive or directory is given
347 using a format string; see :hg:`help export` for details.
347 using a format string; see :hg:`help export` for details.
348
348
349 Each member added to an archive file has a directory prefix
349 Each member added to an archive file has a directory prefix
350 prepended. Use -p/--prefix to specify a format string for the
350 prepended. Use -p/--prefix to specify a format string for the
351 prefix. The default is the basename of the archive, with suffixes
351 prefix. The default is the basename of the archive, with suffixes
352 removed.
352 removed.
353
353
354 Returns 0 on success.
354 Returns 0 on success.
355 '''
355 '''
356
356
357 ctx = scmutil.revsingle(repo, opts.get('rev'))
357 ctx = scmutil.revsingle(repo, opts.get('rev'))
358 if not ctx:
358 if not ctx:
359 raise util.Abort(_('no working directory: please specify a revision'))
359 raise util.Abort(_('no working directory: please specify a revision'))
360 node = ctx.node()
360 node = ctx.node()
361 dest = cmdutil.makefilename(repo, dest, node)
361 dest = cmdutil.makefilename(repo, dest, node)
362 if os.path.realpath(dest) == repo.root:
362 if os.path.realpath(dest) == repo.root:
363 raise util.Abort(_('repository root cannot be destination'))
363 raise util.Abort(_('repository root cannot be destination'))
364
364
365 kind = opts.get('type') or archival.guesskind(dest) or 'files'
365 kind = opts.get('type') or archival.guesskind(dest) or 'files'
366 prefix = opts.get('prefix')
366 prefix = opts.get('prefix')
367
367
368 if dest == '-':
368 if dest == '-':
369 if kind == 'files':
369 if kind == 'files':
370 raise util.Abort(_('cannot archive plain files to stdout'))
370 raise util.Abort(_('cannot archive plain files to stdout'))
371 dest = cmdutil.makefileobj(repo, dest)
371 dest = cmdutil.makefileobj(repo, dest)
372 if not prefix:
372 if not prefix:
373 prefix = os.path.basename(repo.root) + '-%h'
373 prefix = os.path.basename(repo.root) + '-%h'
374
374
375 prefix = cmdutil.makefilename(repo, prefix, node)
375 prefix = cmdutil.makefilename(repo, prefix, node)
376 matchfn = scmutil.match(ctx, [], opts)
376 matchfn = scmutil.match(ctx, [], opts)
377 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
377 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
378 matchfn, prefix, subrepos=opts.get('subrepos'))
378 matchfn, prefix, subrepos=opts.get('subrepos'))
379
379
380 @command('backout',
380 @command('backout',
381 [('', 'merge', None, _('merge with old dirstate parent after backout')),
381 [('', 'merge', None, _('merge with old dirstate parent after backout')),
382 ('', 'parent', '',
382 ('', 'parent', '',
383 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
383 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
384 ('r', 'rev', '', _('revision to backout'), _('REV')),
384 ('r', 'rev', '', _('revision to backout'), _('REV')),
385 ] + mergetoolopts + walkopts + commitopts + commitopts2,
385 ] + mergetoolopts + walkopts + commitopts + commitopts2,
386 _('[OPTION]... [-r] REV'))
386 _('[OPTION]... [-r] REV'))
387 def backout(ui, repo, node=None, rev=None, **opts):
387 def backout(ui, repo, node=None, rev=None, **opts):
388 '''reverse effect of earlier changeset
388 '''reverse effect of earlier changeset
389
389
390 Prepare a new changeset with the effect of REV undone in the
390 Prepare a new changeset with the effect of REV undone in the
391 current working directory.
391 current working directory.
392
392
393 If REV is the parent of the working directory, then this new changeset
393 If REV is the parent of the working directory, then this new changeset
394 is committed automatically. Otherwise, hg needs to merge the
394 is committed automatically. Otherwise, hg needs to merge the
395 changes and the merged result is left uncommitted.
395 changes and the merged result is left uncommitted.
396
396
397 .. note::
397 .. note::
398 backout cannot be used to fix either an unwanted or
398 backout cannot be used to fix either an unwanted or
399 incorrect merge.
399 incorrect merge.
400
400
401 .. container:: verbose
401 .. container:: verbose
402
402
403 By default, the pending changeset will have one parent,
403 By default, the pending changeset will have one parent,
404 maintaining a linear history. With --merge, the pending
404 maintaining a linear history. With --merge, the pending
405 changeset will instead have two parents: the old parent of the
405 changeset will instead have two parents: the old parent of the
406 working directory and a new child of REV that simply undoes REV.
406 working directory and a new child of REV that simply undoes REV.
407
407
408 Before version 1.7, the behavior without --merge was equivalent
408 Before version 1.7, the behavior without --merge was equivalent
409 to specifying --merge followed by :hg:`update --clean .` to
409 to specifying --merge followed by :hg:`update --clean .` to
410 cancel the merge and leave the child of REV as a head to be
410 cancel the merge and leave the child of REV as a head to be
411 merged separately.
411 merged separately.
412
412
413 See :hg:`help dates` for a list of formats valid for -d/--date.
413 See :hg:`help dates` for a list of formats valid for -d/--date.
414
414
415 Returns 0 on success.
415 Returns 0 on success.
416 '''
416 '''
417 if rev and node:
417 if rev and node:
418 raise util.Abort(_("please specify just one revision"))
418 raise util.Abort(_("please specify just one revision"))
419
419
420 if not rev:
420 if not rev:
421 rev = node
421 rev = node
422
422
423 if not rev:
423 if not rev:
424 raise util.Abort(_("please specify a revision to backout"))
424 raise util.Abort(_("please specify a revision to backout"))
425
425
426 date = opts.get('date')
426 date = opts.get('date')
427 if date:
427 if date:
428 opts['date'] = util.parsedate(date)
428 opts['date'] = util.parsedate(date)
429
429
430 cmdutil.bailifchanged(repo)
430 cmdutil.bailifchanged(repo)
431 node = scmutil.revsingle(repo, rev).node()
431 node = scmutil.revsingle(repo, rev).node()
432
432
433 op1, op2 = repo.dirstate.parents()
433 op1, op2 = repo.dirstate.parents()
434 a = repo.changelog.ancestor(op1, node)
434 a = repo.changelog.ancestor(op1, node)
435 if a != node:
435 if a != node:
436 raise util.Abort(_('cannot backout change on a different branch'))
436 raise util.Abort(_('cannot backout change on a different branch'))
437
437
438 p1, p2 = repo.changelog.parents(node)
438 p1, p2 = repo.changelog.parents(node)
439 if p1 == nullid:
439 if p1 == nullid:
440 raise util.Abort(_('cannot backout a change with no parents'))
440 raise util.Abort(_('cannot backout a change with no parents'))
441 if p2 != nullid:
441 if p2 != nullid:
442 if not opts.get('parent'):
442 if not opts.get('parent'):
443 raise util.Abort(_('cannot backout a merge changeset'))
443 raise util.Abort(_('cannot backout a merge changeset'))
444 p = repo.lookup(opts['parent'])
444 p = repo.lookup(opts['parent'])
445 if p not in (p1, p2):
445 if p not in (p1, p2):
446 raise util.Abort(_('%s is not a parent of %s') %
446 raise util.Abort(_('%s is not a parent of %s') %
447 (short(p), short(node)))
447 (short(p), short(node)))
448 parent = p
448 parent = p
449 else:
449 else:
450 if opts.get('parent'):
450 if opts.get('parent'):
451 raise util.Abort(_('cannot use --parent on non-merge changeset'))
451 raise util.Abort(_('cannot use --parent on non-merge changeset'))
452 parent = p1
452 parent = p1
453
453
454 # the backout should appear on the same branch
454 # the backout should appear on the same branch
455 wlock = repo.wlock()
455 wlock = repo.wlock()
456 try:
456 try:
457 branch = repo.dirstate.branch()
457 branch = repo.dirstate.branch()
458 hg.clean(repo, node, show_stats=False)
458 hg.clean(repo, node, show_stats=False)
459 repo.dirstate.setbranch(branch)
459 repo.dirstate.setbranch(branch)
460 revert_opts = opts.copy()
460 revert_opts = opts.copy()
461 revert_opts['date'] = None
461 revert_opts['date'] = None
462 revert_opts['all'] = True
462 revert_opts['all'] = True
463 revert_opts['rev'] = hex(parent)
463 revert_opts['rev'] = hex(parent)
464 revert_opts['no_backup'] = None
464 revert_opts['no_backup'] = None
465 revert(ui, repo, **revert_opts)
465 revert(ui, repo, **revert_opts)
466 if not opts.get('merge') and op1 != node:
466 if not opts.get('merge') and op1 != node:
467 try:
467 try:
468 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
468 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
469 return hg.update(repo, op1)
469 return hg.update(repo, op1)
470 finally:
470 finally:
471 ui.setconfig('ui', 'forcemerge', '')
471 ui.setconfig('ui', 'forcemerge', '')
472
472
473 commit_opts = opts.copy()
473 commit_opts = opts.copy()
474 commit_opts['addremove'] = False
474 commit_opts['addremove'] = False
475 if not commit_opts['message'] and not commit_opts['logfile']:
475 if not commit_opts['message'] and not commit_opts['logfile']:
476 # we don't translate commit messages
476 # we don't translate commit messages
477 commit_opts['message'] = "Backed out changeset %s" % short(node)
477 commit_opts['message'] = "Backed out changeset %s" % short(node)
478 commit_opts['force_editor'] = True
478 commit_opts['force_editor'] = True
479 commit(ui, repo, **commit_opts)
479 commit(ui, repo, **commit_opts)
480 def nice(node):
480 def nice(node):
481 return '%d:%s' % (repo.changelog.rev(node), short(node))
481 return '%d:%s' % (repo.changelog.rev(node), short(node))
482 ui.status(_('changeset %s backs out changeset %s\n') %
482 ui.status(_('changeset %s backs out changeset %s\n') %
483 (nice(repo.changelog.tip()), nice(node)))
483 (nice(repo.changelog.tip()), nice(node)))
484 if opts.get('merge') and op1 != node:
484 if opts.get('merge') and op1 != node:
485 hg.clean(repo, op1, show_stats=False)
485 hg.clean(repo, op1, show_stats=False)
486 ui.status(_('merging with changeset %s\n')
486 ui.status(_('merging with changeset %s\n')
487 % nice(repo.changelog.tip()))
487 % nice(repo.changelog.tip()))
488 try:
488 try:
489 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
489 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
490 return hg.merge(repo, hex(repo.changelog.tip()))
490 return hg.merge(repo, hex(repo.changelog.tip()))
491 finally:
491 finally:
492 ui.setconfig('ui', 'forcemerge', '')
492 ui.setconfig('ui', 'forcemerge', '')
493 finally:
493 finally:
494 wlock.release()
494 wlock.release()
495 return 0
495 return 0
496
496
497 @command('bisect',
497 @command('bisect',
498 [('r', 'reset', False, _('reset bisect state')),
498 [('r', 'reset', False, _('reset bisect state')),
499 ('g', 'good', False, _('mark changeset good')),
499 ('g', 'good', False, _('mark changeset good')),
500 ('b', 'bad', False, _('mark changeset bad')),
500 ('b', 'bad', False, _('mark changeset bad')),
501 ('s', 'skip', False, _('skip testing changeset')),
501 ('s', 'skip', False, _('skip testing changeset')),
502 ('e', 'extend', False, _('extend the bisect range')),
502 ('e', 'extend', False, _('extend the bisect range')),
503 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
503 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
504 ('U', 'noupdate', False, _('do not update to target'))],
504 ('U', 'noupdate', False, _('do not update to target'))],
505 _("[-gbsr] [-U] [-c CMD] [REV]"))
505 _("[-gbsr] [-U] [-c CMD] [REV]"))
506 def bisect(ui, repo, rev=None, extra=None, command=None,
506 def bisect(ui, repo, rev=None, extra=None, command=None,
507 reset=None, good=None, bad=None, skip=None, extend=None,
507 reset=None, good=None, bad=None, skip=None, extend=None,
508 noupdate=None):
508 noupdate=None):
509 """subdivision search of changesets
509 """subdivision search of changesets
510
510
511 This command helps to find changesets which introduce problems. To
511 This command helps to find changesets which introduce problems. To
512 use, mark the earliest changeset you know exhibits the problem as
512 use, mark the earliest changeset you know exhibits the problem as
513 bad, then mark the latest changeset which is free from the problem
513 bad, then mark the latest changeset which is free from the problem
514 as good. Bisect will update your working directory to a revision
514 as good. Bisect will update your working directory to a revision
515 for testing (unless the -U/--noupdate option is specified). Once
515 for testing (unless the -U/--noupdate option is specified). Once
516 you have performed tests, mark the working directory as good or
516 you have performed tests, mark the working directory as good or
517 bad, and bisect will either update to another candidate changeset
517 bad, and bisect will either update to another candidate changeset
518 or announce that it has found the bad revision.
518 or announce that it has found the bad revision.
519
519
520 As a shortcut, you can also use the revision argument to mark a
520 As a shortcut, you can also use the revision argument to mark a
521 revision as good or bad without checking it out first.
521 revision as good or bad without checking it out first.
522
522
523 If you supply a command, it will be used for automatic bisection.
523 If you supply a command, it will be used for automatic bisection.
524 The environment variable HG_NODE will contain the ID of the
524 The environment variable HG_NODE will contain the ID of the
525 changeset being tested. The exit status of the command will be
525 changeset being tested. The exit status of the command will be
526 used to mark revisions as good or bad: status 0 means good, 125
526 used to mark revisions as good or bad: status 0 means good, 125
527 means to skip the revision, 127 (command not found) will abort the
527 means to skip the revision, 127 (command not found) will abort the
528 bisection, and any other non-zero exit status means the revision
528 bisection, and any other non-zero exit status means the revision
529 is bad.
529 is bad.
530
530
531 .. container:: verbose
531 .. container:: verbose
532
532
533 Some examples:
533 Some examples:
534
534
535 - start a bisection with known bad revision 12, and good revision 34::
535 - start a bisection with known bad revision 12, and good revision 34::
536
536
537 hg bisect --bad 34
537 hg bisect --bad 34
538 hg bisect --good 12
538 hg bisect --good 12
539
539
540 - advance the current bisection by marking current revision as good or
540 - advance the current bisection by marking current revision as good or
541 bad::
541 bad::
542
542
543 hg bisect --good
543 hg bisect --good
544 hg bisect --bad
544 hg bisect --bad
545
545
546 - mark the current revision, or a known revision, to be skipped (e.g. if
546 - mark the current revision, or a known revision, to be skipped (e.g. if
547 that revision is not usable because of another issue)::
547 that revision is not usable because of another issue)::
548
548
549 hg bisect --skip
549 hg bisect --skip
550 hg bisect --skip 23
550 hg bisect --skip 23
551
551
552 - forget the current bisection::
552 - forget the current bisection::
553
553
554 hg bisect --reset
554 hg bisect --reset
555
555
556 - use 'make && make tests' to automatically find the first broken
556 - use 'make && make tests' to automatically find the first broken
557 revision::
557 revision::
558
558
559 hg bisect --reset
559 hg bisect --reset
560 hg bisect --bad 34
560 hg bisect --bad 34
561 hg bisect --good 12
561 hg bisect --good 12
562 hg bisect --command 'make && make tests'
562 hg bisect --command 'make && make tests'
563
563
564 - see all changesets whose states are already known in the current
564 - see all changesets whose states are already known in the current
565 bisection::
565 bisection::
566
566
567 hg log -r "bisect(pruned)"
567 hg log -r "bisect(pruned)"
568
568
569 - see the changeset currently being bisected (especially useful
569 - see the changeset currently being bisected (especially useful
570 if running with -U/--noupdate)::
570 if running with -U/--noupdate)::
571
571
572 hg log -r "bisect(current)"
572 hg log -r "bisect(current)"
573
573
574 - see all changesets that took part in the current bisection::
574 - see all changesets that took part in the current bisection::
575
575
576 hg log -r "bisect(range)"
576 hg log -r "bisect(range)"
577
577
578 - with the graphlog extension, you can even get a nice graph::
578 - with the graphlog extension, you can even get a nice graph::
579
579
580 hg log --graph -r "bisect(range)"
580 hg log --graph -r "bisect(range)"
581
581
582 See :hg:`help revsets` for more about the `bisect()` keyword.
582 See :hg:`help revsets` for more about the `bisect()` keyword.
583
583
584 Returns 0 on success.
584 Returns 0 on success.
585 """
585 """
586 def extendbisectrange(nodes, good):
586 def extendbisectrange(nodes, good):
587 # bisect is incomplete when it ends on a merge node and
587 # bisect is incomplete when it ends on a merge node and
588 # one of the parent was not checked.
588 # one of the parent was not checked.
589 parents = repo[nodes[0]].parents()
589 parents = repo[nodes[0]].parents()
590 if len(parents) > 1:
590 if len(parents) > 1:
591 side = good and state['bad'] or state['good']
591 side = good and state['bad'] or state['good']
592 num = len(set(i.node() for i in parents) & set(side))
592 num = len(set(i.node() for i in parents) & set(side))
593 if num == 1:
593 if num == 1:
594 return parents[0].ancestor(parents[1])
594 return parents[0].ancestor(parents[1])
595 return None
595 return None
596
596
597 def print_result(nodes, good):
597 def print_result(nodes, good):
598 displayer = cmdutil.show_changeset(ui, repo, {})
598 displayer = cmdutil.show_changeset(ui, repo, {})
599 if len(nodes) == 1:
599 if len(nodes) == 1:
600 # narrowed it down to a single revision
600 # narrowed it down to a single revision
601 if good:
601 if good:
602 ui.write(_("The first good revision is:\n"))
602 ui.write(_("The first good revision is:\n"))
603 else:
603 else:
604 ui.write(_("The first bad revision is:\n"))
604 ui.write(_("The first bad revision is:\n"))
605 displayer.show(repo[nodes[0]])
605 displayer.show(repo[nodes[0]])
606 extendnode = extendbisectrange(nodes, good)
606 extendnode = extendbisectrange(nodes, good)
607 if extendnode is not None:
607 if extendnode is not None:
608 ui.write(_('Not all ancestors of this changeset have been'
608 ui.write(_('Not all ancestors of this changeset have been'
609 ' checked.\nUse bisect --extend to continue the '
609 ' checked.\nUse bisect --extend to continue the '
610 'bisection from\nthe common ancestor, %s.\n')
610 'bisection from\nthe common ancestor, %s.\n')
611 % extendnode)
611 % extendnode)
612 else:
612 else:
613 # multiple possible revisions
613 # multiple possible revisions
614 if good:
614 if good:
615 ui.write(_("Due to skipped revisions, the first "
615 ui.write(_("Due to skipped revisions, the first "
616 "good revision could be any of:\n"))
616 "good revision could be any of:\n"))
617 else:
617 else:
618 ui.write(_("Due to skipped revisions, the first "
618 ui.write(_("Due to skipped revisions, the first "
619 "bad revision could be any of:\n"))
619 "bad revision could be any of:\n"))
620 for n in nodes:
620 for n in nodes:
621 displayer.show(repo[n])
621 displayer.show(repo[n])
622 displayer.close()
622 displayer.close()
623
623
624 def check_state(state, interactive=True):
624 def check_state(state, interactive=True):
625 if not state['good'] or not state['bad']:
625 if not state['good'] or not state['bad']:
626 if (good or bad or skip or reset) and interactive:
626 if (good or bad or skip or reset) and interactive:
627 return
627 return
628 if not state['good']:
628 if not state['good']:
629 raise util.Abort(_('cannot bisect (no known good revisions)'))
629 raise util.Abort(_('cannot bisect (no known good revisions)'))
630 else:
630 else:
631 raise util.Abort(_('cannot bisect (no known bad revisions)'))
631 raise util.Abort(_('cannot bisect (no known bad revisions)'))
632 return True
632 return True
633
633
634 # backward compatibility
634 # backward compatibility
635 if rev in "good bad reset init".split():
635 if rev in "good bad reset init".split():
636 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
636 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
637 cmd, rev, extra = rev, extra, None
637 cmd, rev, extra = rev, extra, None
638 if cmd == "good":
638 if cmd == "good":
639 good = True
639 good = True
640 elif cmd == "bad":
640 elif cmd == "bad":
641 bad = True
641 bad = True
642 else:
642 else:
643 reset = True
643 reset = True
644 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
644 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
645 raise util.Abort(_('incompatible arguments'))
645 raise util.Abort(_('incompatible arguments'))
646
646
647 if reset:
647 if reset:
648 p = repo.join("bisect.state")
648 p = repo.join("bisect.state")
649 if os.path.exists(p):
649 if os.path.exists(p):
650 os.unlink(p)
650 os.unlink(p)
651 return
651 return
652
652
653 state = hbisect.load_state(repo)
653 state = hbisect.load_state(repo)
654
654
655 if command:
655 if command:
656 changesets = 1
656 changesets = 1
657 try:
657 try:
658 node = state['current'][0]
658 node = state['current'][0]
659 except LookupError:
659 except LookupError:
660 if noupdate:
660 if noupdate:
661 raise util.Abort(_('current bisect revision is unknown - '
661 raise util.Abort(_('current bisect revision is unknown - '
662 'start a new bisect to fix'))
662 'start a new bisect to fix'))
663 node, p2 = repo.dirstate.parents()
663 node, p2 = repo.dirstate.parents()
664 if p2 != nullid:
664 if p2 != nullid:
665 raise util.Abort(_('current bisect revision is a merge'))
665 raise util.Abort(_('current bisect revision is a merge'))
666 try:
666 try:
667 while changesets:
667 while changesets:
668 # update state
668 # update state
669 state['current'] = [node]
669 state['current'] = [node]
670 hbisect.save_state(repo, state)
670 hbisect.save_state(repo, state)
671 status = util.system(command,
671 status = util.system(command,
672 environ={'HG_NODE': hex(node)},
672 environ={'HG_NODE': hex(node)},
673 out=ui.fout)
673 out=ui.fout)
674 if status == 125:
674 if status == 125:
675 transition = "skip"
675 transition = "skip"
676 elif status == 0:
676 elif status == 0:
677 transition = "good"
677 transition = "good"
678 # status < 0 means process was killed
678 # status < 0 means process was killed
679 elif status == 127:
679 elif status == 127:
680 raise util.Abort(_("failed to execute %s") % command)
680 raise util.Abort(_("failed to execute %s") % command)
681 elif status < 0:
681 elif status < 0:
682 raise util.Abort(_("%s killed") % command)
682 raise util.Abort(_("%s killed") % command)
683 else:
683 else:
684 transition = "bad"
684 transition = "bad"
685 ctx = scmutil.revsingle(repo, rev, node)
685 ctx = scmutil.revsingle(repo, rev, node)
686 rev = None # clear for future iterations
686 rev = None # clear for future iterations
687 state[transition].append(ctx.node())
687 state[transition].append(ctx.node())
688 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
688 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
689 check_state(state, interactive=False)
689 check_state(state, interactive=False)
690 # bisect
690 # bisect
691 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
691 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
692 # update to next check
692 # update to next check
693 node = nodes[0]
693 node = nodes[0]
694 if not noupdate:
694 if not noupdate:
695 cmdutil.bailifchanged(repo)
695 cmdutil.bailifchanged(repo)
696 hg.clean(repo, node, show_stats=False)
696 hg.clean(repo, node, show_stats=False)
697 finally:
697 finally:
698 state['current'] = [node]
698 state['current'] = [node]
699 hbisect.save_state(repo, state)
699 hbisect.save_state(repo, state)
700 print_result(nodes, good)
700 print_result(nodes, good)
701 return
701 return
702
702
703 # update state
703 # update state
704
704
705 if rev:
705 if rev:
706 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
706 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
707 else:
707 else:
708 nodes = [repo.lookup('.')]
708 nodes = [repo.lookup('.')]
709
709
710 if good or bad or skip:
710 if good or bad or skip:
711 if good:
711 if good:
712 state['good'] += nodes
712 state['good'] += nodes
713 elif bad:
713 elif bad:
714 state['bad'] += nodes
714 state['bad'] += nodes
715 elif skip:
715 elif skip:
716 state['skip'] += nodes
716 state['skip'] += nodes
717 hbisect.save_state(repo, state)
717 hbisect.save_state(repo, state)
718
718
719 if not check_state(state):
719 if not check_state(state):
720 return
720 return
721
721
722 # actually bisect
722 # actually bisect
723 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
723 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
724 if extend:
724 if extend:
725 if not changesets:
725 if not changesets:
726 extendnode = extendbisectrange(nodes, good)
726 extendnode = extendbisectrange(nodes, good)
727 if extendnode is not None:
727 if extendnode is not None:
728 ui.write(_("Extending search to changeset %d:%s\n"
728 ui.write(_("Extending search to changeset %d:%s\n"
729 % (extendnode.rev(), extendnode)))
729 % (extendnode.rev(), extendnode)))
730 state['current'] = [extendnode.node()]
730 state['current'] = [extendnode.node()]
731 hbisect.save_state(repo, state)
731 hbisect.save_state(repo, state)
732 if noupdate:
732 if noupdate:
733 return
733 return
734 cmdutil.bailifchanged(repo)
734 cmdutil.bailifchanged(repo)
735 return hg.clean(repo, extendnode.node())
735 return hg.clean(repo, extendnode.node())
736 raise util.Abort(_("nothing to extend"))
736 raise util.Abort(_("nothing to extend"))
737
737
738 if changesets == 0:
738 if changesets == 0:
739 print_result(nodes, good)
739 print_result(nodes, good)
740 else:
740 else:
741 assert len(nodes) == 1 # only a single node can be tested next
741 assert len(nodes) == 1 # only a single node can be tested next
742 node = nodes[0]
742 node = nodes[0]
743 # compute the approximate number of remaining tests
743 # compute the approximate number of remaining tests
744 tests, size = 0, 2
744 tests, size = 0, 2
745 while size <= changesets:
745 while size <= changesets:
746 tests, size = tests + 1, size * 2
746 tests, size = tests + 1, size * 2
747 rev = repo.changelog.rev(node)
747 rev = repo.changelog.rev(node)
748 ui.write(_("Testing changeset %d:%s "
748 ui.write(_("Testing changeset %d:%s "
749 "(%d changesets remaining, ~%d tests)\n")
749 "(%d changesets remaining, ~%d tests)\n")
750 % (rev, short(node), changesets, tests))
750 % (rev, short(node), changesets, tests))
751 state['current'] = [node]
751 state['current'] = [node]
752 hbisect.save_state(repo, state)
752 hbisect.save_state(repo, state)
753 if not noupdate:
753 if not noupdate:
754 cmdutil.bailifchanged(repo)
754 cmdutil.bailifchanged(repo)
755 return hg.clean(repo, node)
755 return hg.clean(repo, node)
756
756
757 @command('bookmarks',
757 @command('bookmarks',
758 [('f', 'force', False, _('force')),
758 [('f', 'force', False, _('force')),
759 ('r', 'rev', '', _('revision'), _('REV')),
759 ('r', 'rev', '', _('revision'), _('REV')),
760 ('d', 'delete', False, _('delete a given bookmark')),
760 ('d', 'delete', False, _('delete a given bookmark')),
761 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
761 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
762 ('i', 'inactive', False, _('mark a bookmark inactive'))],
762 ('i', 'inactive', False, _('mark a bookmark inactive'))],
763 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
763 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
764 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
764 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
765 rename=None, inactive=False):
765 rename=None, inactive=False):
766 '''track a line of development with movable markers
766 '''track a line of development with movable markers
767
767
768 Bookmarks are pointers to certain commits that move when committing.
768 Bookmarks are pointers to certain commits that move when committing.
769 Bookmarks are local. They can be renamed, copied and deleted. It is
769 Bookmarks are local. They can be renamed, copied and deleted. It is
770 possible to use :hg:`merge NAME` to merge from a given bookmark, and
770 possible to use :hg:`merge NAME` to merge from a given bookmark, and
771 :hg:`update NAME` to update to a given bookmark.
771 :hg:`update NAME` to update to a given bookmark.
772
772
773 You can use :hg:`bookmark NAME` to set a bookmark on the working
773 You can use :hg:`bookmark NAME` to set a bookmark on the working
774 directory's parent revision with the given name. If you specify
774 directory's parent revision with the given name. If you specify
775 a revision using -r REV (where REV may be an existing bookmark),
775 a revision using -r REV (where REV may be an existing bookmark),
776 the bookmark is assigned to that revision.
776 the bookmark is assigned to that revision.
777
777
778 Bookmarks can be pushed and pulled between repositories (see :hg:`help
778 Bookmarks can be pushed and pulled between repositories (see :hg:`help
779 push` and :hg:`help pull`). This requires both the local and remote
779 push` and :hg:`help pull`). This requires both the local and remote
780 repositories to support bookmarks. For versions prior to 1.8, this means
780 repositories to support bookmarks. For versions prior to 1.8, this means
781 the bookmarks extension must be enabled.
781 the bookmarks extension must be enabled.
782
782
783 With -i/--inactive, the new bookmark will not be made the active
783 With -i/--inactive, the new bookmark will not be made the active
784 bookmark. If -r/--rev is given, the new bookmark will not be made
784 bookmark. If -r/--rev is given, the new bookmark will not be made
785 active even if -i/--inactive is not given. If no NAME is given, the
785 active even if -i/--inactive is not given. If no NAME is given, the
786 current active bookmark will be marked inactive.
786 current active bookmark will be marked inactive.
787 '''
787 '''
788 hexfn = ui.debugflag and hex or short
788 hexfn = ui.debugflag and hex or short
789 marks = repo._bookmarks
789 marks = repo._bookmarks
790 cur = repo.changectx('.').node()
790 cur = repo.changectx('.').node()
791
791
792 def checkformat(mark):
792 def checkformat(mark):
793 mark = mark.strip()
793 mark = mark.strip()
794 if not mark:
794 if not mark:
795 raise util.Abort(_("bookmark names cannot consist entirely of "
795 raise util.Abort(_("bookmark names cannot consist entirely of "
796 "whitespace"))
796 "whitespace"))
797 scmutil.checknewlabel(repo, mark, 'bookmark')
797 scmutil.checknewlabel(repo, mark, 'bookmark')
798 return mark
798 return mark
799
799
800 def checkconflict(repo, mark, force=False):
800 def checkconflict(repo, mark, force=False):
801 if mark in marks and not force:
801 if mark in marks and not force:
802 raise util.Abort(_("bookmark '%s' already exists "
802 raise util.Abort(_("bookmark '%s' already exists "
803 "(use -f to force)") % mark)
803 "(use -f to force)") % mark)
804 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
804 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
805 and not force):
805 and not force):
806 raise util.Abort(
806 raise util.Abort(
807 _("a bookmark cannot have the name of an existing branch"))
807 _("a bookmark cannot have the name of an existing branch"))
808
808
809 if delete and rename:
809 if delete and rename:
810 raise util.Abort(_("--delete and --rename are incompatible"))
810 raise util.Abort(_("--delete and --rename are incompatible"))
811 if delete and rev:
811 if delete and rev:
812 raise util.Abort(_("--rev is incompatible with --delete"))
812 raise util.Abort(_("--rev is incompatible with --delete"))
813 if rename and rev:
813 if rename and rev:
814 raise util.Abort(_("--rev is incompatible with --rename"))
814 raise util.Abort(_("--rev is incompatible with --rename"))
815 if mark is None and (delete or rev):
815 if mark is None and (delete or rev):
816 raise util.Abort(_("bookmark name required"))
816 raise util.Abort(_("bookmark name required"))
817
817
818 if delete:
818 if delete:
819 if mark not in marks:
819 if mark not in marks:
820 raise util.Abort(_("bookmark '%s' does not exist") % mark)
820 raise util.Abort(_("bookmark '%s' does not exist") % mark)
821 if mark == repo._bookmarkcurrent:
821 if mark == repo._bookmarkcurrent:
822 bookmarks.setcurrent(repo, None)
822 bookmarks.setcurrent(repo, None)
823 del marks[mark]
823 del marks[mark]
824 bookmarks.write(repo)
824 bookmarks.write(repo)
825
825
826 elif rename:
826 elif rename:
827 if mark is None:
827 if mark is None:
828 raise util.Abort(_("new bookmark name required"))
828 raise util.Abort(_("new bookmark name required"))
829 mark = checkformat(mark)
829 mark = checkformat(mark)
830 if rename not in marks:
830 if rename not in marks:
831 raise util.Abort(_("bookmark '%s' does not exist") % rename)
831 raise util.Abort(_("bookmark '%s' does not exist") % rename)
832 checkconflict(repo, mark, force)
832 checkconflict(repo, mark, force)
833 marks[mark] = marks[rename]
833 marks[mark] = marks[rename]
834 if repo._bookmarkcurrent == rename and not inactive:
834 if repo._bookmarkcurrent == rename and not inactive:
835 bookmarks.setcurrent(repo, mark)
835 bookmarks.setcurrent(repo, mark)
836 del marks[rename]
836 del marks[rename]
837 bookmarks.write(repo)
837 bookmarks.write(repo)
838
838
839 elif mark is not None:
839 elif mark is not None:
840 mark = checkformat(mark)
840 mark = checkformat(mark)
841 if inactive and mark == repo._bookmarkcurrent:
841 if inactive and mark == repo._bookmarkcurrent:
842 bookmarks.setcurrent(repo, None)
842 bookmarks.setcurrent(repo, None)
843 return
843 return
844 checkconflict(repo, mark, force)
844 checkconflict(repo, mark, force)
845 if rev:
845 if rev:
846 marks[mark] = scmutil.revsingle(repo, rev).node()
846 marks[mark] = scmutil.revsingle(repo, rev).node()
847 else:
847 else:
848 marks[mark] = cur
848 marks[mark] = cur
849 if not inactive and cur == marks[mark]:
849 if not inactive and cur == marks[mark]:
850 bookmarks.setcurrent(repo, mark)
850 bookmarks.setcurrent(repo, mark)
851 bookmarks.write(repo)
851 bookmarks.write(repo)
852
852
853 # Same message whether trying to deactivate the current bookmark (-i
853 # Same message whether trying to deactivate the current bookmark (-i
854 # with no NAME) or listing bookmarks
854 # with no NAME) or listing bookmarks
855 elif len(marks) == 0:
855 elif len(marks) == 0:
856 ui.status(_("no bookmarks set\n"))
856 ui.status(_("no bookmarks set\n"))
857
857
858 elif inactive:
858 elif inactive:
859 if not repo._bookmarkcurrent:
859 if not repo._bookmarkcurrent:
860 ui.status(_("no active bookmark\n"))
860 ui.status(_("no active bookmark\n"))
861 else:
861 else:
862 bookmarks.setcurrent(repo, None)
862 bookmarks.setcurrent(repo, None)
863
863
864 else: # show bookmarks
864 else: # show bookmarks
865 for bmark, n in sorted(marks.iteritems()):
865 for bmark, n in sorted(marks.iteritems()):
866 current = repo._bookmarkcurrent
866 current = repo._bookmarkcurrent
867 if bmark == current and n == cur:
867 if bmark == current and n == cur:
868 prefix, label = '*', 'bookmarks.current'
868 prefix, label = '*', 'bookmarks.current'
869 else:
869 else:
870 prefix, label = ' ', ''
870 prefix, label = ' ', ''
871
871
872 if ui.quiet:
872 if ui.quiet:
873 ui.write("%s\n" % bmark, label=label)
873 ui.write("%s\n" % bmark, label=label)
874 else:
874 else:
875 ui.write(" %s %-25s %d:%s\n" % (
875 ui.write(" %s %-25s %d:%s\n" % (
876 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
876 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
877 label=label)
877 label=label)
878
878
879 @command('branch',
879 @command('branch',
880 [('f', 'force', None,
880 [('f', 'force', None,
881 _('set branch name even if it shadows an existing branch')),
881 _('set branch name even if it shadows an existing branch')),
882 ('C', 'clean', None, _('reset branch name to parent branch name'))],
882 ('C', 'clean', None, _('reset branch name to parent branch name'))],
883 _('[-fC] [NAME]'))
883 _('[-fC] [NAME]'))
884 def branch(ui, repo, label=None, **opts):
884 def branch(ui, repo, label=None, **opts):
885 """set or show the current branch name
885 """set or show the current branch name
886
886
887 .. note::
887 .. note::
888 Branch names are permanent and global. Use :hg:`bookmark` to create a
888 Branch names are permanent and global. Use :hg:`bookmark` to create a
889 light-weight bookmark instead. See :hg:`help glossary` for more
889 light-weight bookmark instead. See :hg:`help glossary` for more
890 information about named branches and bookmarks.
890 information about named branches and bookmarks.
891
891
892 With no argument, show the current branch name. With one argument,
892 With no argument, show the current branch name. With one argument,
893 set the working directory branch name (the branch will not exist
893 set the working directory branch name (the branch will not exist
894 in the repository until the next commit). Standard practice
894 in the repository until the next commit). Standard practice
895 recommends that primary development take place on the 'default'
895 recommends that primary development take place on the 'default'
896 branch.
896 branch.
897
897
898 Unless -f/--force is specified, branch will not let you set a
898 Unless -f/--force is specified, branch will not let you set a
899 branch name that already exists, even if it's inactive.
899 branch name that already exists, even if it's inactive.
900
900
901 Use -C/--clean to reset the working directory branch to that of
901 Use -C/--clean to reset the working directory branch to that of
902 the parent of the working directory, negating a previous branch
902 the parent of the working directory, negating a previous branch
903 change.
903 change.
904
904
905 Use the command :hg:`update` to switch to an existing branch. Use
905 Use the command :hg:`update` to switch to an existing branch. Use
906 :hg:`commit --close-branch` to mark this branch as closed.
906 :hg:`commit --close-branch` to mark this branch as closed.
907
907
908 Returns 0 on success.
908 Returns 0 on success.
909 """
909 """
910 if not opts.get('clean') and not label:
910 if not opts.get('clean') and not label:
911 ui.write("%s\n" % repo.dirstate.branch())
911 ui.write("%s\n" % repo.dirstate.branch())
912 return
912 return
913
913
914 wlock = repo.wlock()
914 wlock = repo.wlock()
915 try:
915 try:
916 if opts.get('clean'):
916 if opts.get('clean'):
917 label = repo[None].p1().branch()
917 label = repo[None].p1().branch()
918 repo.dirstate.setbranch(label)
918 repo.dirstate.setbranch(label)
919 ui.status(_('reset working directory to branch %s\n') % label)
919 ui.status(_('reset working directory to branch %s\n') % label)
920 elif label:
920 elif label:
921 if not opts.get('force') and label in repo.branchmap():
921 if not opts.get('force') and label in repo.branchmap():
922 if label not in [p.branch() for p in repo.parents()]:
922 if label not in [p.branch() for p in repo.parents()]:
923 raise util.Abort(_('a branch of the same name already'
923 raise util.Abort(_('a branch of the same name already'
924 ' exists'),
924 ' exists'),
925 # i18n: "it" refers to an existing branch
925 # i18n: "it" refers to an existing branch
926 hint=_("use 'hg update' to switch to it"))
926 hint=_("use 'hg update' to switch to it"))
927 repo.dirstate.setbranch(label)
927 repo.dirstate.setbranch(label)
928 ui.status(_('marked working directory as branch %s\n') % label)
928 ui.status(_('marked working directory as branch %s\n') % label)
929 ui.status(_('(branches are permanent and global, '
929 ui.status(_('(branches are permanent and global, '
930 'did you want a bookmark?)\n'))
930 'did you want a bookmark?)\n'))
931 finally:
931 finally:
932 wlock.release()
932 wlock.release()
933
933
934 @command('branches',
934 @command('branches',
935 [('a', 'active', False, _('show only branches that have unmerged heads')),
935 [('a', 'active', False, _('show only branches that have unmerged heads')),
936 ('c', 'closed', False, _('show normal and closed branches'))],
936 ('c', 'closed', False, _('show normal and closed branches'))],
937 _('[-ac]'))
937 _('[-ac]'))
938 def branches(ui, repo, active=False, closed=False):
938 def branches(ui, repo, active=False, closed=False):
939 """list repository named branches
939 """list repository named branches
940
940
941 List the repository's named branches, indicating which ones are
941 List the repository's named branches, indicating which ones are
942 inactive. If -c/--closed is specified, also list branches which have
942 inactive. If -c/--closed is specified, also list branches which have
943 been marked closed (see :hg:`commit --close-branch`).
943 been marked closed (see :hg:`commit --close-branch`).
944
944
945 If -a/--active is specified, only show active branches. A branch
945 If -a/--active is specified, only show active branches. A branch
946 is considered active if it contains repository heads.
946 is considered active if it contains repository heads.
947
947
948 Use the command :hg:`update` to switch to an existing branch.
948 Use the command :hg:`update` to switch to an existing branch.
949
949
950 Returns 0.
950 Returns 0.
951 """
951 """
952
952
953 hexfunc = ui.debugflag and hex or short
953 hexfunc = ui.debugflag and hex or short
954
954
955 activebranches = set([repo[n].branch() for n in repo.heads()])
955 activebranches = set([repo[n].branch() for n in repo.heads()])
956 branches = []
956 branches = []
957 for tag, heads in repo.branchmap().iteritems():
957 for tag, heads in repo.branchmap().iteritems():
958 for h in reversed(heads):
958 for h in reversed(heads):
959 ctx = repo[h]
959 ctx = repo[h]
960 isopen = not ctx.closesbranch()
960 isopen = not ctx.closesbranch()
961 if isopen:
961 if isopen:
962 tip = ctx
962 tip = ctx
963 break
963 break
964 else:
964 else:
965 tip = repo[heads[-1]]
965 tip = repo[heads[-1]]
966 isactive = tag in activebranches and isopen
966 isactive = tag in activebranches and isopen
967 branches.append((tip, isactive, isopen))
967 branches.append((tip, isactive, isopen))
968 branches.sort(key=lambda i: (i[1], i[0].rev(), i[0].branch(), i[2]),
968 branches.sort(key=lambda i: (i[1], i[0].rev(), i[0].branch(), i[2]),
969 reverse=True)
969 reverse=True)
970
970
971 for ctx, isactive, isopen in branches:
971 for ctx, isactive, isopen in branches:
972 if (not active) or isactive:
972 if (not active) or isactive:
973 if isactive:
973 if isactive:
974 label = 'branches.active'
974 label = 'branches.active'
975 notice = ''
975 notice = ''
976 elif not isopen:
976 elif not isopen:
977 if not closed:
977 if not closed:
978 continue
978 continue
979 label = 'branches.closed'
979 label = 'branches.closed'
980 notice = _(' (closed)')
980 notice = _(' (closed)')
981 else:
981 else:
982 label = 'branches.inactive'
982 label = 'branches.inactive'
983 notice = _(' (inactive)')
983 notice = _(' (inactive)')
984 if ctx.branch() == repo.dirstate.branch():
984 if ctx.branch() == repo.dirstate.branch():
985 label = 'branches.current'
985 label = 'branches.current'
986 rev = str(ctx.rev()).rjust(31 - encoding.colwidth(ctx.branch()))
986 rev = str(ctx.rev()).rjust(31 - encoding.colwidth(ctx.branch()))
987 rev = ui.label('%s:%s' % (rev, hexfunc(ctx.node())),
987 rev = ui.label('%s:%s' % (rev, hexfunc(ctx.node())),
988 'log.changeset changeset.%s' % ctx.phasestr())
988 'log.changeset changeset.%s' % ctx.phasestr())
989 tag = ui.label(ctx.branch(), label)
989 tag = ui.label(ctx.branch(), label)
990 if ui.quiet:
990 if ui.quiet:
991 ui.write("%s\n" % tag)
991 ui.write("%s\n" % tag)
992 else:
992 else:
993 ui.write("%s %s%s\n" % (tag, rev, notice))
993 ui.write("%s %s%s\n" % (tag, rev, notice))
994
994
995 @command('bundle',
995 @command('bundle',
996 [('f', 'force', None, _('run even when the destination is unrelated')),
996 [('f', 'force', None, _('run even when the destination is unrelated')),
997 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
997 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
998 _('REV')),
998 _('REV')),
999 ('b', 'branch', [], _('a specific branch you would like to bundle'),
999 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1000 _('BRANCH')),
1000 _('BRANCH')),
1001 ('', 'base', [],
1001 ('', 'base', [],
1002 _('a base changeset assumed to be available at the destination'),
1002 _('a base changeset assumed to be available at the destination'),
1003 _('REV')),
1003 _('REV')),
1004 ('a', 'all', None, _('bundle all changesets in the repository')),
1004 ('a', 'all', None, _('bundle all changesets in the repository')),
1005 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1005 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1006 ] + remoteopts,
1006 ] + remoteopts,
1007 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1007 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1008 def bundle(ui, repo, fname, dest=None, **opts):
1008 def bundle(ui, repo, fname, dest=None, **opts):
1009 """create a changegroup file
1009 """create a changegroup file
1010
1010
1011 Generate a compressed changegroup file collecting changesets not
1011 Generate a compressed changegroup file collecting changesets not
1012 known to be in another repository.
1012 known to be in another repository.
1013
1013
1014 If you omit the destination repository, then hg assumes the
1014 If you omit the destination repository, then hg assumes the
1015 destination will have all the nodes you specify with --base
1015 destination will have all the nodes you specify with --base
1016 parameters. To create a bundle containing all changesets, use
1016 parameters. To create a bundle containing all changesets, use
1017 -a/--all (or --base null).
1017 -a/--all (or --base null).
1018
1018
1019 You can change compression method with the -t/--type option.
1019 You can change compression method with the -t/--type option.
1020 The available compression methods are: none, bzip2, and
1020 The available compression methods are: none, bzip2, and
1021 gzip (by default, bundles are compressed using bzip2).
1021 gzip (by default, bundles are compressed using bzip2).
1022
1022
1023 The bundle file can then be transferred using conventional means
1023 The bundle file can then be transferred using conventional means
1024 and applied to another repository with the unbundle or pull
1024 and applied to another repository with the unbundle or pull
1025 command. This is useful when direct push and pull are not
1025 command. This is useful when direct push and pull are not
1026 available or when exporting an entire repository is undesirable.
1026 available or when exporting an entire repository is undesirable.
1027
1027
1028 Applying bundles preserves all changeset contents including
1028 Applying bundles preserves all changeset contents including
1029 permissions, copy/rename information, and revision history.
1029 permissions, copy/rename information, and revision history.
1030
1030
1031 Returns 0 on success, 1 if no changes found.
1031 Returns 0 on success, 1 if no changes found.
1032 """
1032 """
1033 revs = None
1033 revs = None
1034 if 'rev' in opts:
1034 if 'rev' in opts:
1035 revs = scmutil.revrange(repo, opts['rev'])
1035 revs = scmutil.revrange(repo, opts['rev'])
1036
1036
1037 bundletype = opts.get('type', 'bzip2').lower()
1037 bundletype = opts.get('type', 'bzip2').lower()
1038 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1038 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1039 bundletype = btypes.get(bundletype)
1039 bundletype = btypes.get(bundletype)
1040 if bundletype not in changegroup.bundletypes:
1040 if bundletype not in changegroup.bundletypes:
1041 raise util.Abort(_('unknown bundle type specified with --type'))
1041 raise util.Abort(_('unknown bundle type specified with --type'))
1042
1042
1043 if opts.get('all'):
1043 if opts.get('all'):
1044 base = ['null']
1044 base = ['null']
1045 else:
1045 else:
1046 base = scmutil.revrange(repo, opts.get('base'))
1046 base = scmutil.revrange(repo, opts.get('base'))
1047 if base:
1047 if base:
1048 if dest:
1048 if dest:
1049 raise util.Abort(_("--base is incompatible with specifying "
1049 raise util.Abort(_("--base is incompatible with specifying "
1050 "a destination"))
1050 "a destination"))
1051 common = [repo.lookup(rev) for rev in base]
1051 common = [repo.lookup(rev) for rev in base]
1052 heads = revs and map(repo.lookup, revs) or revs
1052 heads = revs and map(repo.lookup, revs) or revs
1053 cg = repo.getbundle('bundle', heads=heads, common=common)
1053 cg = repo.getbundle('bundle', heads=heads, common=common)
1054 outgoing = None
1054 outgoing = None
1055 else:
1055 else:
1056 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1056 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1057 dest, branches = hg.parseurl(dest, opts.get('branch'))
1057 dest, branches = hg.parseurl(dest, opts.get('branch'))
1058 other = hg.peer(repo, opts, dest)
1058 other = hg.peer(repo, opts, dest)
1059 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
1059 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
1060 heads = revs and map(repo.lookup, revs) or revs
1060 heads = revs and map(repo.lookup, revs) or revs
1061 outgoing = discovery.findcommonoutgoing(repo, other,
1061 outgoing = discovery.findcommonoutgoing(repo, other,
1062 onlyheads=heads,
1062 onlyheads=heads,
1063 force=opts.get('force'),
1063 force=opts.get('force'),
1064 portable=True)
1064 portable=True)
1065 cg = repo.getlocalbundle('bundle', outgoing)
1065 cg = repo.getlocalbundle('bundle', outgoing)
1066 if not cg:
1066 if not cg:
1067 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1067 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1068 return 1
1068 return 1
1069
1069
1070 changegroup.writebundle(cg, fname, bundletype)
1070 changegroup.writebundle(cg, fname, bundletype)
1071
1071
1072 @command('cat',
1072 @command('cat',
1073 [('o', 'output', '',
1073 [('o', 'output', '',
1074 _('print output to file with formatted name'), _('FORMAT')),
1074 _('print output to file with formatted name'), _('FORMAT')),
1075 ('r', 'rev', '', _('print the given revision'), _('REV')),
1075 ('r', 'rev', '', _('print the given revision'), _('REV')),
1076 ('', 'decode', None, _('apply any matching decode filter')),
1076 ('', 'decode', None, _('apply any matching decode filter')),
1077 ] + walkopts,
1077 ] + walkopts,
1078 _('[OPTION]... FILE...'))
1078 _('[OPTION]... FILE...'))
1079 def cat(ui, repo, file1, *pats, **opts):
1079 def cat(ui, repo, file1, *pats, **opts):
1080 """output the current or given revision of files
1080 """output the current or given revision of files
1081
1081
1082 Print the specified files as they were at the given revision. If
1082 Print the specified files as they were at the given revision. If
1083 no revision is given, the parent of the working directory is used,
1083 no revision is given, the parent of the working directory is used,
1084 or tip if no revision is checked out.
1084 or tip if no revision is checked out.
1085
1085
1086 Output may be to a file, in which case the name of the file is
1086 Output may be to a file, in which case the name of the file is
1087 given using a format string. The formatting rules are the same as
1087 given using a format string. The formatting rules are the same as
1088 for the export command, with the following additions:
1088 for the export command, with the following additions:
1089
1089
1090 :``%s``: basename of file being printed
1090 :``%s``: basename of file being printed
1091 :``%d``: dirname of file being printed, or '.' if in repository root
1091 :``%d``: dirname of file being printed, or '.' if in repository root
1092 :``%p``: root-relative path name of file being printed
1092 :``%p``: root-relative path name of file being printed
1093
1093
1094 Returns 0 on success.
1094 Returns 0 on success.
1095 """
1095 """
1096 ctx = scmutil.revsingle(repo, opts.get('rev'))
1096 ctx = scmutil.revsingle(repo, opts.get('rev'))
1097 err = 1
1097 err = 1
1098 m = scmutil.match(ctx, (file1,) + pats, opts)
1098 m = scmutil.match(ctx, (file1,) + pats, opts)
1099 for abs in ctx.walk(m):
1099 for abs in ctx.walk(m):
1100 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1100 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1101 pathname=abs)
1101 pathname=abs)
1102 data = ctx[abs].data()
1102 data = ctx[abs].data()
1103 if opts.get('decode'):
1103 if opts.get('decode'):
1104 data = repo.wwritedata(abs, data)
1104 data = repo.wwritedata(abs, data)
1105 fp.write(data)
1105 fp.write(data)
1106 fp.close()
1106 fp.close()
1107 err = 0
1107 err = 0
1108 return err
1108 return err
1109
1109
1110 @command('^clone',
1110 @command('^clone',
1111 [('U', 'noupdate', None,
1111 [('U', 'noupdate', None,
1112 _('the clone will include an empty working copy (only a repository)')),
1112 _('the clone will include an empty working copy (only a repository)')),
1113 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1113 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1114 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1114 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1115 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1115 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1116 ('', 'pull', None, _('use pull protocol to copy metadata')),
1116 ('', 'pull', None, _('use pull protocol to copy metadata')),
1117 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1117 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1118 ] + remoteopts,
1118 ] + remoteopts,
1119 _('[OPTION]... SOURCE [DEST]'))
1119 _('[OPTION]... SOURCE [DEST]'))
1120 def clone(ui, source, dest=None, **opts):
1120 def clone(ui, source, dest=None, **opts):
1121 """make a copy of an existing repository
1121 """make a copy of an existing repository
1122
1122
1123 Create a copy of an existing repository in a new directory.
1123 Create a copy of an existing repository in a new directory.
1124
1124
1125 If no destination directory name is specified, it defaults to the
1125 If no destination directory name is specified, it defaults to the
1126 basename of the source.
1126 basename of the source.
1127
1127
1128 The location of the source is added to the new repository's
1128 The location of the source is added to the new repository's
1129 ``.hg/hgrc`` file, as the default to be used for future pulls.
1129 ``.hg/hgrc`` file, as the default to be used for future pulls.
1130
1130
1131 Only local paths and ``ssh://`` URLs are supported as
1131 Only local paths and ``ssh://`` URLs are supported as
1132 destinations. For ``ssh://`` destinations, no working directory or
1132 destinations. For ``ssh://`` destinations, no working directory or
1133 ``.hg/hgrc`` will be created on the remote side.
1133 ``.hg/hgrc`` will be created on the remote side.
1134
1134
1135 To pull only a subset of changesets, specify one or more revisions
1135 To pull only a subset of changesets, specify one or more revisions
1136 identifiers with -r/--rev or branches with -b/--branch. The
1136 identifiers with -r/--rev or branches with -b/--branch. The
1137 resulting clone will contain only the specified changesets and
1137 resulting clone will contain only the specified changesets and
1138 their ancestors. These options (or 'clone src#rev dest') imply
1138 their ancestors. These options (or 'clone src#rev dest') imply
1139 --pull, even for local source repositories. Note that specifying a
1139 --pull, even for local source repositories. Note that specifying a
1140 tag will include the tagged changeset but not the changeset
1140 tag will include the tagged changeset but not the changeset
1141 containing the tag.
1141 containing the tag.
1142
1142
1143 To check out a particular version, use -u/--update, or
1143 To check out a particular version, use -u/--update, or
1144 -U/--noupdate to create a clone with no working directory.
1144 -U/--noupdate to create a clone with no working directory.
1145
1145
1146 .. container:: verbose
1146 .. container:: verbose
1147
1147
1148 For efficiency, hardlinks are used for cloning whenever the
1148 For efficiency, hardlinks are used for cloning whenever the
1149 source and destination are on the same filesystem (note this
1149 source and destination are on the same filesystem (note this
1150 applies only to the repository data, not to the working
1150 applies only to the repository data, not to the working
1151 directory). Some filesystems, such as AFS, implement hardlinking
1151 directory). Some filesystems, such as AFS, implement hardlinking
1152 incorrectly, but do not report errors. In these cases, use the
1152 incorrectly, but do not report errors. In these cases, use the
1153 --pull option to avoid hardlinking.
1153 --pull option to avoid hardlinking.
1154
1154
1155 In some cases, you can clone repositories and the working
1155 In some cases, you can clone repositories and the working
1156 directory using full hardlinks with ::
1156 directory using full hardlinks with ::
1157
1157
1158 $ cp -al REPO REPOCLONE
1158 $ cp -al REPO REPOCLONE
1159
1159
1160 This is the fastest way to clone, but it is not always safe. The
1160 This is the fastest way to clone, but it is not always safe. The
1161 operation is not atomic (making sure REPO is not modified during
1161 operation is not atomic (making sure REPO is not modified during
1162 the operation is up to you) and you have to make sure your
1162 the operation is up to you) and you have to make sure your
1163 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1163 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1164 so). Also, this is not compatible with certain extensions that
1164 so). Also, this is not compatible with certain extensions that
1165 place their metadata under the .hg directory, such as mq.
1165 place their metadata under the .hg directory, such as mq.
1166
1166
1167 Mercurial will update the working directory to the first applicable
1167 Mercurial will update the working directory to the first applicable
1168 revision from this list:
1168 revision from this list:
1169
1169
1170 a) null if -U or the source repository has no changesets
1170 a) null if -U or the source repository has no changesets
1171 b) if -u . and the source repository is local, the first parent of
1171 b) if -u . and the source repository is local, the first parent of
1172 the source repository's working directory
1172 the source repository's working directory
1173 c) the changeset specified with -u (if a branch name, this means the
1173 c) the changeset specified with -u (if a branch name, this means the
1174 latest head of that branch)
1174 latest head of that branch)
1175 d) the changeset specified with -r
1175 d) the changeset specified with -r
1176 e) the tipmost head specified with -b
1176 e) the tipmost head specified with -b
1177 f) the tipmost head specified with the url#branch source syntax
1177 f) the tipmost head specified with the url#branch source syntax
1178 g) the tipmost head of the default branch
1178 g) the tipmost head of the default branch
1179 h) tip
1179 h) tip
1180
1180
1181 Examples:
1181 Examples:
1182
1182
1183 - clone a remote repository to a new directory named hg/::
1183 - clone a remote repository to a new directory named hg/::
1184
1184
1185 hg clone http://selenic.com/hg
1185 hg clone http://selenic.com/hg
1186
1186
1187 - create a lightweight local clone::
1187 - create a lightweight local clone::
1188
1188
1189 hg clone project/ project-feature/
1189 hg clone project/ project-feature/
1190
1190
1191 - clone from an absolute path on an ssh server (note double-slash)::
1191 - clone from an absolute path on an ssh server (note double-slash)::
1192
1192
1193 hg clone ssh://user@server//home/projects/alpha/
1193 hg clone ssh://user@server//home/projects/alpha/
1194
1194
1195 - do a high-speed clone over a LAN while checking out a
1195 - do a high-speed clone over a LAN while checking out a
1196 specified version::
1196 specified version::
1197
1197
1198 hg clone --uncompressed http://server/repo -u 1.5
1198 hg clone --uncompressed http://server/repo -u 1.5
1199
1199
1200 - create a repository without changesets after a particular revision::
1200 - create a repository without changesets after a particular revision::
1201
1201
1202 hg clone -r 04e544 experimental/ good/
1202 hg clone -r 04e544 experimental/ good/
1203
1203
1204 - clone (and track) a particular named branch::
1204 - clone (and track) a particular named branch::
1205
1205
1206 hg clone http://selenic.com/hg#stable
1206 hg clone http://selenic.com/hg#stable
1207
1207
1208 See :hg:`help urls` for details on specifying URLs.
1208 See :hg:`help urls` for details on specifying URLs.
1209
1209
1210 Returns 0 on success.
1210 Returns 0 on success.
1211 """
1211 """
1212 if opts.get('noupdate') and opts.get('updaterev'):
1212 if opts.get('noupdate') and opts.get('updaterev'):
1213 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1213 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1214
1214
1215 r = hg.clone(ui, opts, source, dest,
1215 r = hg.clone(ui, opts, source, dest,
1216 pull=opts.get('pull'),
1216 pull=opts.get('pull'),
1217 stream=opts.get('uncompressed'),
1217 stream=opts.get('uncompressed'),
1218 rev=opts.get('rev'),
1218 rev=opts.get('rev'),
1219 update=opts.get('updaterev') or not opts.get('noupdate'),
1219 update=opts.get('updaterev') or not opts.get('noupdate'),
1220 branch=opts.get('branch'))
1220 branch=opts.get('branch'))
1221
1221
1222 return r is None
1222 return r is None
1223
1223
1224 @command('^commit|ci',
1224 @command('^commit|ci',
1225 [('A', 'addremove', None,
1225 [('A', 'addremove', None,
1226 _('mark new/missing files as added/removed before committing')),
1226 _('mark new/missing files as added/removed before committing')),
1227 ('', 'close-branch', None,
1227 ('', 'close-branch', None,
1228 _('mark a branch as closed, hiding it from the branch list')),
1228 _('mark a branch as closed, hiding it from the branch list')),
1229 ('', 'amend', None, _('amend the parent of the working dir')),
1229 ('', 'amend', None, _('amend the parent of the working dir')),
1230 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1230 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1231 _('[OPTION]... [FILE]...'))
1231 _('[OPTION]... [FILE]...'))
1232 def commit(ui, repo, *pats, **opts):
1232 def commit(ui, repo, *pats, **opts):
1233 """commit the specified files or all outstanding changes
1233 """commit the specified files or all outstanding changes
1234
1234
1235 Commit changes to the given files into the repository. Unlike a
1235 Commit changes to the given files into the repository. Unlike a
1236 centralized SCM, this operation is a local operation. See
1236 centralized SCM, this operation is a local operation. See
1237 :hg:`push` for a way to actively distribute your changes.
1237 :hg:`push` for a way to actively distribute your changes.
1238
1238
1239 If a list of files is omitted, all changes reported by :hg:`status`
1239 If a list of files is omitted, all changes reported by :hg:`status`
1240 will be committed.
1240 will be committed.
1241
1241
1242 If you are committing the result of a merge, do not provide any
1242 If you are committing the result of a merge, do not provide any
1243 filenames or -I/-X filters.
1243 filenames or -I/-X filters.
1244
1244
1245 If no commit message is specified, Mercurial starts your
1245 If no commit message is specified, Mercurial starts your
1246 configured editor where you can enter a message. In case your
1246 configured editor where you can enter a message. In case your
1247 commit fails, you will find a backup of your message in
1247 commit fails, you will find a backup of your message in
1248 ``.hg/last-message.txt``.
1248 ``.hg/last-message.txt``.
1249
1249
1250 The --amend flag can be used to amend the parent of the
1250 The --amend flag can be used to amend the parent of the
1251 working directory with a new commit that contains the changes
1251 working directory with a new commit that contains the changes
1252 in the parent in addition to those currently reported by :hg:`status`,
1252 in the parent in addition to those currently reported by :hg:`status`,
1253 if there are any. The old commit is stored in a backup bundle in
1253 if there are any. The old commit is stored in a backup bundle in
1254 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1254 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1255 on how to restore it).
1255 on how to restore it).
1256
1256
1257 Message, user and date are taken from the amended commit unless
1257 Message, user and date are taken from the amended commit unless
1258 specified. When a message isn't specified on the command line,
1258 specified. When a message isn't specified on the command line,
1259 the editor will open with the message of the amended commit.
1259 the editor will open with the message of the amended commit.
1260
1260
1261 It is not possible to amend public changesets (see :hg:`help phases`)
1261 It is not possible to amend public changesets (see :hg:`help phases`)
1262 or changesets that have children.
1262 or changesets that have children.
1263
1263
1264 See :hg:`help dates` for a list of formats valid for -d/--date.
1264 See :hg:`help dates` for a list of formats valid for -d/--date.
1265
1265
1266 Returns 0 on success, 1 if nothing changed.
1266 Returns 0 on success, 1 if nothing changed.
1267 """
1267 """
1268 if opts.get('subrepos'):
1268 if opts.get('subrepos'):
1269 # Let --subrepos on the command line override config setting.
1269 # Let --subrepos on the command line override config setting.
1270 ui.setconfig('ui', 'commitsubrepos', True)
1270 ui.setconfig('ui', 'commitsubrepos', True)
1271
1271
1272 extra = {}
1272 extra = {}
1273 if opts.get('close_branch'):
1273 if opts.get('close_branch'):
1274 if repo['.'].node() not in repo.branchheads():
1274 if repo['.'].node() not in repo.branchheads():
1275 # The topo heads set is included in the branch heads set of the
1275 # The topo heads set is included in the branch heads set of the
1276 # current branch, so it's sufficient to test branchheads
1276 # current branch, so it's sufficient to test branchheads
1277 raise util.Abort(_('can only close branch heads'))
1277 raise util.Abort(_('can only close branch heads'))
1278 extra['close'] = 1
1278 extra['close'] = 1
1279
1279
1280 branch = repo[None].branch()
1280 branch = repo[None].branch()
1281 bheads = repo.branchheads(branch)
1281 bheads = repo.branchheads(branch)
1282
1282
1283 if opts.get('amend'):
1283 if opts.get('amend'):
1284 if ui.configbool('ui', 'commitsubrepos'):
1284 if ui.configbool('ui', 'commitsubrepos'):
1285 raise util.Abort(_('cannot amend recursively'))
1285 raise util.Abort(_('cannot amend recursively'))
1286
1286
1287 old = repo['.']
1287 old = repo['.']
1288 if old.phase() == phases.public:
1288 if old.phase() == phases.public:
1289 raise util.Abort(_('cannot amend public changesets'))
1289 raise util.Abort(_('cannot amend public changesets'))
1290 if len(old.parents()) > 1:
1290 if len(old.parents()) > 1:
1291 raise util.Abort(_('cannot amend merge changesets'))
1291 raise util.Abort(_('cannot amend merge changesets'))
1292 if len(repo[None].parents()) > 1:
1292 if len(repo[None].parents()) > 1:
1293 raise util.Abort(_('cannot amend while merging'))
1293 raise util.Abort(_('cannot amend while merging'))
1294 if old.children():
1294 if old.children():
1295 raise util.Abort(_('cannot amend changeset with children'))
1295 raise util.Abort(_('cannot amend changeset with children'))
1296
1296
1297 e = cmdutil.commiteditor
1297 e = cmdutil.commiteditor
1298 if opts.get('force_editor'):
1298 if opts.get('force_editor'):
1299 e = cmdutil.commitforceeditor
1299 e = cmdutil.commitforceeditor
1300
1300
1301 def commitfunc(ui, repo, message, match, opts):
1301 def commitfunc(ui, repo, message, match, opts):
1302 editor = e
1302 editor = e
1303 # message contains text from -m or -l, if it's empty,
1303 # message contains text from -m or -l, if it's empty,
1304 # open the editor with the old message
1304 # open the editor with the old message
1305 if not message:
1305 if not message:
1306 message = old.description()
1306 message = old.description()
1307 editor = cmdutil.commitforceeditor
1307 editor = cmdutil.commitforceeditor
1308 return repo.commit(message,
1308 return repo.commit(message,
1309 opts.get('user') or old.user(),
1309 opts.get('user') or old.user(),
1310 opts.get('date') or old.date(),
1310 opts.get('date') or old.date(),
1311 match,
1311 match,
1312 editor=editor,
1312 editor=editor,
1313 extra=extra)
1313 extra=extra)
1314
1314
1315 current = repo._bookmarkcurrent
1315 current = repo._bookmarkcurrent
1316 marks = old.bookmarks()
1316 marks = old.bookmarks()
1317 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1317 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1318 if node == old.node():
1318 if node == old.node():
1319 ui.status(_("nothing changed\n"))
1319 ui.status(_("nothing changed\n"))
1320 return 1
1320 return 1
1321 elif marks:
1321 elif marks:
1322 ui.debug('moving bookmarks %r from %s to %s\n' %
1322 ui.debug('moving bookmarks %r from %s to %s\n' %
1323 (marks, old.hex(), hex(node)))
1323 (marks, old.hex(), hex(node)))
1324 for bm in marks:
1324 for bm in marks:
1325 repo._bookmarks[bm] = node
1325 repo._bookmarks[bm] = node
1326 if bm == current:
1326 if bm == current:
1327 bookmarks.setcurrent(repo, bm)
1327 bookmarks.setcurrent(repo, bm)
1328 bookmarks.write(repo)
1328 bookmarks.write(repo)
1329 else:
1329 else:
1330 e = cmdutil.commiteditor
1330 e = cmdutil.commiteditor
1331 if opts.get('force_editor'):
1331 if opts.get('force_editor'):
1332 e = cmdutil.commitforceeditor
1332 e = cmdutil.commitforceeditor
1333
1333
1334 def commitfunc(ui, repo, message, match, opts):
1334 def commitfunc(ui, repo, message, match, opts):
1335 return repo.commit(message, opts.get('user'), opts.get('date'),
1335 return repo.commit(message, opts.get('user'), opts.get('date'),
1336 match, editor=e, extra=extra)
1336 match, editor=e, extra=extra)
1337
1337
1338 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1338 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1339
1339
1340 if not node:
1340 if not node:
1341 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1341 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1342 if stat[3]:
1342 if stat[3]:
1343 ui.status(_("nothing changed (%d missing files, see "
1343 ui.status(_("nothing changed (%d missing files, see "
1344 "'hg status')\n") % len(stat[3]))
1344 "'hg status')\n") % len(stat[3]))
1345 else:
1345 else:
1346 ui.status(_("nothing changed\n"))
1346 ui.status(_("nothing changed\n"))
1347 return 1
1347 return 1
1348
1348
1349 ctx = repo[node]
1349 ctx = repo[node]
1350 parents = ctx.parents()
1350 parents = ctx.parents()
1351
1351
1352 if (not opts.get('amend') and bheads and node not in bheads and not
1352 if (not opts.get('amend') and bheads and node not in bheads and not
1353 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1353 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1354 ui.status(_('created new head\n'))
1354 ui.status(_('created new head\n'))
1355 # The message is not printed for initial roots. For the other
1355 # The message is not printed for initial roots. For the other
1356 # changesets, it is printed in the following situations:
1356 # changesets, it is printed in the following situations:
1357 #
1357 #
1358 # Par column: for the 2 parents with ...
1358 # Par column: for the 2 parents with ...
1359 # N: null or no parent
1359 # N: null or no parent
1360 # B: parent is on another named branch
1360 # B: parent is on another named branch
1361 # C: parent is a regular non head changeset
1361 # C: parent is a regular non head changeset
1362 # H: parent was a branch head of the current branch
1362 # H: parent was a branch head of the current branch
1363 # Msg column: whether we print "created new head" message
1363 # Msg column: whether we print "created new head" message
1364 # In the following, it is assumed that there already exists some
1364 # In the following, it is assumed that there already exists some
1365 # initial branch heads of the current branch, otherwise nothing is
1365 # initial branch heads of the current branch, otherwise nothing is
1366 # printed anyway.
1366 # printed anyway.
1367 #
1367 #
1368 # Par Msg Comment
1368 # Par Msg Comment
1369 # N N y additional topo root
1369 # N N y additional topo root
1370 #
1370 #
1371 # B N y additional branch root
1371 # B N y additional branch root
1372 # C N y additional topo head
1372 # C N y additional topo head
1373 # H N n usual case
1373 # H N n usual case
1374 #
1374 #
1375 # B B y weird additional branch root
1375 # B B y weird additional branch root
1376 # C B y branch merge
1376 # C B y branch merge
1377 # H B n merge with named branch
1377 # H B n merge with named branch
1378 #
1378 #
1379 # C C y additional head from merge
1379 # C C y additional head from merge
1380 # C H n merge with a head
1380 # C H n merge with a head
1381 #
1381 #
1382 # H H n head merge: head count decreases
1382 # H H n head merge: head count decreases
1383
1383
1384 if not opts.get('close_branch'):
1384 if not opts.get('close_branch'):
1385 for r in parents:
1385 for r in parents:
1386 if r.closesbranch() and r.branch() == branch:
1386 if r.closesbranch() and r.branch() == branch:
1387 ui.status(_('reopening closed branch head %d\n') % r)
1387 ui.status(_('reopening closed branch head %d\n') % r)
1388
1388
1389 if ui.debugflag:
1389 if ui.debugflag:
1390 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1390 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1391 elif ui.verbose:
1391 elif ui.verbose:
1392 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1392 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1393
1393
1394 @command('copy|cp',
1394 @command('copy|cp',
1395 [('A', 'after', None, _('record a copy that has already occurred')),
1395 [('A', 'after', None, _('record a copy that has already occurred')),
1396 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1396 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1397 ] + walkopts + dryrunopts,
1397 ] + walkopts + dryrunopts,
1398 _('[OPTION]... [SOURCE]... DEST'))
1398 _('[OPTION]... [SOURCE]... DEST'))
1399 def copy(ui, repo, *pats, **opts):
1399 def copy(ui, repo, *pats, **opts):
1400 """mark files as copied for the next commit
1400 """mark files as copied for the next commit
1401
1401
1402 Mark dest as having copies of source files. If dest is a
1402 Mark dest as having copies of source files. If dest is a
1403 directory, copies are put in that directory. If dest is a file,
1403 directory, copies are put in that directory. If dest is a file,
1404 the source must be a single file.
1404 the source must be a single file.
1405
1405
1406 By default, this command copies the contents of files as they
1406 By default, this command copies the contents of files as they
1407 exist in the working directory. If invoked with -A/--after, the
1407 exist in the working directory. If invoked with -A/--after, the
1408 operation is recorded, but no copying is performed.
1408 operation is recorded, but no copying is performed.
1409
1409
1410 This command takes effect with the next commit. To undo a copy
1410 This command takes effect with the next commit. To undo a copy
1411 before that, see :hg:`revert`.
1411 before that, see :hg:`revert`.
1412
1412
1413 Returns 0 on success, 1 if errors are encountered.
1413 Returns 0 on success, 1 if errors are encountered.
1414 """
1414 """
1415 wlock = repo.wlock(False)
1415 wlock = repo.wlock(False)
1416 try:
1416 try:
1417 return cmdutil.copy(ui, repo, pats, opts)
1417 return cmdutil.copy(ui, repo, pats, opts)
1418 finally:
1418 finally:
1419 wlock.release()
1419 wlock.release()
1420
1420
1421 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1421 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1422 def debugancestor(ui, repo, *args):
1422 def debugancestor(ui, repo, *args):
1423 """find the ancestor revision of two revisions in a given index"""
1423 """find the ancestor revision of two revisions in a given index"""
1424 if len(args) == 3:
1424 if len(args) == 3:
1425 index, rev1, rev2 = args
1425 index, rev1, rev2 = args
1426 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1426 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1427 lookup = r.lookup
1427 lookup = r.lookup
1428 elif len(args) == 2:
1428 elif len(args) == 2:
1429 if not repo:
1429 if not repo:
1430 raise util.Abort(_("there is no Mercurial repository here "
1430 raise util.Abort(_("there is no Mercurial repository here "
1431 "(.hg not found)"))
1431 "(.hg not found)"))
1432 rev1, rev2 = args
1432 rev1, rev2 = args
1433 r = repo.changelog
1433 r = repo.changelog
1434 lookup = repo.lookup
1434 lookup = repo.lookup
1435 else:
1435 else:
1436 raise util.Abort(_('either two or three arguments required'))
1436 raise util.Abort(_('either two or three arguments required'))
1437 a = r.ancestor(lookup(rev1), lookup(rev2))
1437 a = r.ancestor(lookup(rev1), lookup(rev2))
1438 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1438 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1439
1439
1440 @command('debugbuilddag',
1440 @command('debugbuilddag',
1441 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1441 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1442 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1442 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1443 ('n', 'new-file', None, _('add new file at each rev'))],
1443 ('n', 'new-file', None, _('add new file at each rev'))],
1444 _('[OPTION]... [TEXT]'))
1444 _('[OPTION]... [TEXT]'))
1445 def debugbuilddag(ui, repo, text=None,
1445 def debugbuilddag(ui, repo, text=None,
1446 mergeable_file=False,
1446 mergeable_file=False,
1447 overwritten_file=False,
1447 overwritten_file=False,
1448 new_file=False):
1448 new_file=False):
1449 """builds a repo with a given DAG from scratch in the current empty repo
1449 """builds a repo with a given DAG from scratch in the current empty repo
1450
1450
1451 The description of the DAG is read from stdin if not given on the
1451 The description of the DAG is read from stdin if not given on the
1452 command line.
1452 command line.
1453
1453
1454 Elements:
1454 Elements:
1455
1455
1456 - "+n" is a linear run of n nodes based on the current default parent
1456 - "+n" is a linear run of n nodes based on the current default parent
1457 - "." is a single node based on the current default parent
1457 - "." is a single node based on the current default parent
1458 - "$" resets the default parent to null (implied at the start);
1458 - "$" resets the default parent to null (implied at the start);
1459 otherwise the default parent is always the last node created
1459 otherwise the default parent is always the last node created
1460 - "<p" sets the default parent to the backref p
1460 - "<p" sets the default parent to the backref p
1461 - "*p" is a fork at parent p, which is a backref
1461 - "*p" is a fork at parent p, which is a backref
1462 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1462 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1463 - "/p2" is a merge of the preceding node and p2
1463 - "/p2" is a merge of the preceding node and p2
1464 - ":tag" defines a local tag for the preceding node
1464 - ":tag" defines a local tag for the preceding node
1465 - "@branch" sets the named branch for subsequent nodes
1465 - "@branch" sets the named branch for subsequent nodes
1466 - "#...\\n" is a comment up to the end of the line
1466 - "#...\\n" is a comment up to the end of the line
1467
1467
1468 Whitespace between the above elements is ignored.
1468 Whitespace between the above elements is ignored.
1469
1469
1470 A backref is either
1470 A backref is either
1471
1471
1472 - a number n, which references the node curr-n, where curr is the current
1472 - a number n, which references the node curr-n, where curr is the current
1473 node, or
1473 node, or
1474 - the name of a local tag you placed earlier using ":tag", or
1474 - the name of a local tag you placed earlier using ":tag", or
1475 - empty to denote the default parent.
1475 - empty to denote the default parent.
1476
1476
1477 All string valued-elements are either strictly alphanumeric, or must
1477 All string valued-elements are either strictly alphanumeric, or must
1478 be enclosed in double quotes ("..."), with "\\" as escape character.
1478 be enclosed in double quotes ("..."), with "\\" as escape character.
1479 """
1479 """
1480
1480
1481 if text is None:
1481 if text is None:
1482 ui.status(_("reading DAG from stdin\n"))
1482 ui.status(_("reading DAG from stdin\n"))
1483 text = ui.fin.read()
1483 text = ui.fin.read()
1484
1484
1485 cl = repo.changelog
1485 cl = repo.changelog
1486 if len(cl) > 0:
1486 if len(cl) > 0:
1487 raise util.Abort(_('repository is not empty'))
1487 raise util.Abort(_('repository is not empty'))
1488
1488
1489 # determine number of revs in DAG
1489 # determine number of revs in DAG
1490 total = 0
1490 total = 0
1491 for type, data in dagparser.parsedag(text):
1491 for type, data in dagparser.parsedag(text):
1492 if type == 'n':
1492 if type == 'n':
1493 total += 1
1493 total += 1
1494
1494
1495 if mergeable_file:
1495 if mergeable_file:
1496 linesperrev = 2
1496 linesperrev = 2
1497 # make a file with k lines per rev
1497 # make a file with k lines per rev
1498 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1498 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1499 initialmergedlines.append("")
1499 initialmergedlines.append("")
1500
1500
1501 tags = []
1501 tags = []
1502
1502
1503 lock = tr = None
1503 lock = tr = None
1504 try:
1504 try:
1505 lock = repo.lock()
1505 lock = repo.lock()
1506 tr = repo.transaction("builddag")
1506 tr = repo.transaction("builddag")
1507
1507
1508 at = -1
1508 at = -1
1509 atbranch = 'default'
1509 atbranch = 'default'
1510 nodeids = []
1510 nodeids = []
1511 id = 0
1511 id = 0
1512 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1512 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1513 for type, data in dagparser.parsedag(text):
1513 for type, data in dagparser.parsedag(text):
1514 if type == 'n':
1514 if type == 'n':
1515 ui.note('node %s\n' % str(data))
1515 ui.note('node %s\n' % str(data))
1516 id, ps = data
1516 id, ps = data
1517
1517
1518 files = []
1518 files = []
1519 fctxs = {}
1519 fctxs = {}
1520
1520
1521 p2 = None
1521 p2 = None
1522 if mergeable_file:
1522 if mergeable_file:
1523 fn = "mf"
1523 fn = "mf"
1524 p1 = repo[ps[0]]
1524 p1 = repo[ps[0]]
1525 if len(ps) > 1:
1525 if len(ps) > 1:
1526 p2 = repo[ps[1]]
1526 p2 = repo[ps[1]]
1527 pa = p1.ancestor(p2)
1527 pa = p1.ancestor(p2)
1528 base, local, other = [x[fn].data() for x in pa, p1, p2]
1528 base, local, other = [x[fn].data() for x in pa, p1, p2]
1529 m3 = simplemerge.Merge3Text(base, local, other)
1529 m3 = simplemerge.Merge3Text(base, local, other)
1530 ml = [l.strip() for l in m3.merge_lines()]
1530 ml = [l.strip() for l in m3.merge_lines()]
1531 ml.append("")
1531 ml.append("")
1532 elif at > 0:
1532 elif at > 0:
1533 ml = p1[fn].data().split("\n")
1533 ml = p1[fn].data().split("\n")
1534 else:
1534 else:
1535 ml = initialmergedlines
1535 ml = initialmergedlines
1536 ml[id * linesperrev] += " r%i" % id
1536 ml[id * linesperrev] += " r%i" % id
1537 mergedtext = "\n".join(ml)
1537 mergedtext = "\n".join(ml)
1538 files.append(fn)
1538 files.append(fn)
1539 fctxs[fn] = context.memfilectx(fn, mergedtext)
1539 fctxs[fn] = context.memfilectx(fn, mergedtext)
1540
1540
1541 if overwritten_file:
1541 if overwritten_file:
1542 fn = "of"
1542 fn = "of"
1543 files.append(fn)
1543 files.append(fn)
1544 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1544 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1545
1545
1546 if new_file:
1546 if new_file:
1547 fn = "nf%i" % id
1547 fn = "nf%i" % id
1548 files.append(fn)
1548 files.append(fn)
1549 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1549 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1550 if len(ps) > 1:
1550 if len(ps) > 1:
1551 if not p2:
1551 if not p2:
1552 p2 = repo[ps[1]]
1552 p2 = repo[ps[1]]
1553 for fn in p2:
1553 for fn in p2:
1554 if fn.startswith("nf"):
1554 if fn.startswith("nf"):
1555 files.append(fn)
1555 files.append(fn)
1556 fctxs[fn] = p2[fn]
1556 fctxs[fn] = p2[fn]
1557
1557
1558 def fctxfn(repo, cx, path):
1558 def fctxfn(repo, cx, path):
1559 return fctxs.get(path)
1559 return fctxs.get(path)
1560
1560
1561 if len(ps) == 0 or ps[0] < 0:
1561 if len(ps) == 0 or ps[0] < 0:
1562 pars = [None, None]
1562 pars = [None, None]
1563 elif len(ps) == 1:
1563 elif len(ps) == 1:
1564 pars = [nodeids[ps[0]], None]
1564 pars = [nodeids[ps[0]], None]
1565 else:
1565 else:
1566 pars = [nodeids[p] for p in ps]
1566 pars = [nodeids[p] for p in ps]
1567 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1567 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1568 date=(id, 0),
1568 date=(id, 0),
1569 user="debugbuilddag",
1569 user="debugbuilddag",
1570 extra={'branch': atbranch})
1570 extra={'branch': atbranch})
1571 nodeid = repo.commitctx(cx)
1571 nodeid = repo.commitctx(cx)
1572 nodeids.append(nodeid)
1572 nodeids.append(nodeid)
1573 at = id
1573 at = id
1574 elif type == 'l':
1574 elif type == 'l':
1575 id, name = data
1575 id, name = data
1576 ui.note('tag %s\n' % name)
1576 ui.note('tag %s\n' % name)
1577 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1577 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1578 elif type == 'a':
1578 elif type == 'a':
1579 ui.note('branch %s\n' % data)
1579 ui.note('branch %s\n' % data)
1580 atbranch = data
1580 atbranch = data
1581 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1581 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1582 tr.close()
1582 tr.close()
1583
1583
1584 if tags:
1584 if tags:
1585 repo.opener.write("localtags", "".join(tags))
1585 repo.opener.write("localtags", "".join(tags))
1586 finally:
1586 finally:
1587 ui.progress(_('building'), None)
1587 ui.progress(_('building'), None)
1588 release(tr, lock)
1588 release(tr, lock)
1589
1589
1590 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1590 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1591 def debugbundle(ui, bundlepath, all=None, **opts):
1591 def debugbundle(ui, bundlepath, all=None, **opts):
1592 """lists the contents of a bundle"""
1592 """lists the contents of a bundle"""
1593 f = url.open(ui, bundlepath)
1593 f = hg.openpath(ui, bundlepath)
1594 try:
1594 try:
1595 gen = changegroup.readbundle(f, bundlepath)
1595 gen = changegroup.readbundle(f, bundlepath)
1596 if all:
1596 if all:
1597 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1597 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1598
1598
1599 def showchunks(named):
1599 def showchunks(named):
1600 ui.write("\n%s\n" % named)
1600 ui.write("\n%s\n" % named)
1601 chain = None
1601 chain = None
1602 while True:
1602 while True:
1603 chunkdata = gen.deltachunk(chain)
1603 chunkdata = gen.deltachunk(chain)
1604 if not chunkdata:
1604 if not chunkdata:
1605 break
1605 break
1606 node = chunkdata['node']
1606 node = chunkdata['node']
1607 p1 = chunkdata['p1']
1607 p1 = chunkdata['p1']
1608 p2 = chunkdata['p2']
1608 p2 = chunkdata['p2']
1609 cs = chunkdata['cs']
1609 cs = chunkdata['cs']
1610 deltabase = chunkdata['deltabase']
1610 deltabase = chunkdata['deltabase']
1611 delta = chunkdata['delta']
1611 delta = chunkdata['delta']
1612 ui.write("%s %s %s %s %s %s\n" %
1612 ui.write("%s %s %s %s %s %s\n" %
1613 (hex(node), hex(p1), hex(p2),
1613 (hex(node), hex(p1), hex(p2),
1614 hex(cs), hex(deltabase), len(delta)))
1614 hex(cs), hex(deltabase), len(delta)))
1615 chain = node
1615 chain = node
1616
1616
1617 chunkdata = gen.changelogheader()
1617 chunkdata = gen.changelogheader()
1618 showchunks("changelog")
1618 showchunks("changelog")
1619 chunkdata = gen.manifestheader()
1619 chunkdata = gen.manifestheader()
1620 showchunks("manifest")
1620 showchunks("manifest")
1621 while True:
1621 while True:
1622 chunkdata = gen.filelogheader()
1622 chunkdata = gen.filelogheader()
1623 if not chunkdata:
1623 if not chunkdata:
1624 break
1624 break
1625 fname = chunkdata['filename']
1625 fname = chunkdata['filename']
1626 showchunks(fname)
1626 showchunks(fname)
1627 else:
1627 else:
1628 chunkdata = gen.changelogheader()
1628 chunkdata = gen.changelogheader()
1629 chain = None
1629 chain = None
1630 while True:
1630 while True:
1631 chunkdata = gen.deltachunk(chain)
1631 chunkdata = gen.deltachunk(chain)
1632 if not chunkdata:
1632 if not chunkdata:
1633 break
1633 break
1634 node = chunkdata['node']
1634 node = chunkdata['node']
1635 ui.write("%s\n" % hex(node))
1635 ui.write("%s\n" % hex(node))
1636 chain = node
1636 chain = node
1637 finally:
1637 finally:
1638 f.close()
1638 f.close()
1639
1639
1640 @command('debugcheckstate', [], '')
1640 @command('debugcheckstate', [], '')
1641 def debugcheckstate(ui, repo):
1641 def debugcheckstate(ui, repo):
1642 """validate the correctness of the current dirstate"""
1642 """validate the correctness of the current dirstate"""
1643 parent1, parent2 = repo.dirstate.parents()
1643 parent1, parent2 = repo.dirstate.parents()
1644 m1 = repo[parent1].manifest()
1644 m1 = repo[parent1].manifest()
1645 m2 = repo[parent2].manifest()
1645 m2 = repo[parent2].manifest()
1646 errors = 0
1646 errors = 0
1647 for f in repo.dirstate:
1647 for f in repo.dirstate:
1648 state = repo.dirstate[f]
1648 state = repo.dirstate[f]
1649 if state in "nr" and f not in m1:
1649 if state in "nr" and f not in m1:
1650 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1650 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1651 errors += 1
1651 errors += 1
1652 if state in "a" and f in m1:
1652 if state in "a" and f in m1:
1653 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1653 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1654 errors += 1
1654 errors += 1
1655 if state in "m" and f not in m1 and f not in m2:
1655 if state in "m" and f not in m1 and f not in m2:
1656 ui.warn(_("%s in state %s, but not in either manifest\n") %
1656 ui.warn(_("%s in state %s, but not in either manifest\n") %
1657 (f, state))
1657 (f, state))
1658 errors += 1
1658 errors += 1
1659 for f in m1:
1659 for f in m1:
1660 state = repo.dirstate[f]
1660 state = repo.dirstate[f]
1661 if state not in "nrm":
1661 if state not in "nrm":
1662 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1662 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1663 errors += 1
1663 errors += 1
1664 if errors:
1664 if errors:
1665 error = _(".hg/dirstate inconsistent with current parent's manifest")
1665 error = _(".hg/dirstate inconsistent with current parent's manifest")
1666 raise util.Abort(error)
1666 raise util.Abort(error)
1667
1667
1668 @command('debugcommands', [], _('[COMMAND]'))
1668 @command('debugcommands', [], _('[COMMAND]'))
1669 def debugcommands(ui, cmd='', *args):
1669 def debugcommands(ui, cmd='', *args):
1670 """list all available commands and options"""
1670 """list all available commands and options"""
1671 for cmd, vals in sorted(table.iteritems()):
1671 for cmd, vals in sorted(table.iteritems()):
1672 cmd = cmd.split('|')[0].strip('^')
1672 cmd = cmd.split('|')[0].strip('^')
1673 opts = ', '.join([i[1] for i in vals[1]])
1673 opts = ', '.join([i[1] for i in vals[1]])
1674 ui.write('%s: %s\n' % (cmd, opts))
1674 ui.write('%s: %s\n' % (cmd, opts))
1675
1675
1676 @command('debugcomplete',
1676 @command('debugcomplete',
1677 [('o', 'options', None, _('show the command options'))],
1677 [('o', 'options', None, _('show the command options'))],
1678 _('[-o] CMD'))
1678 _('[-o] CMD'))
1679 def debugcomplete(ui, cmd='', **opts):
1679 def debugcomplete(ui, cmd='', **opts):
1680 """returns the completion list associated with the given command"""
1680 """returns the completion list associated with the given command"""
1681
1681
1682 if opts.get('options'):
1682 if opts.get('options'):
1683 options = []
1683 options = []
1684 otables = [globalopts]
1684 otables = [globalopts]
1685 if cmd:
1685 if cmd:
1686 aliases, entry = cmdutil.findcmd(cmd, table, False)
1686 aliases, entry = cmdutil.findcmd(cmd, table, False)
1687 otables.append(entry[1])
1687 otables.append(entry[1])
1688 for t in otables:
1688 for t in otables:
1689 for o in t:
1689 for o in t:
1690 if "(DEPRECATED)" in o[3]:
1690 if "(DEPRECATED)" in o[3]:
1691 continue
1691 continue
1692 if o[0]:
1692 if o[0]:
1693 options.append('-%s' % o[0])
1693 options.append('-%s' % o[0])
1694 options.append('--%s' % o[1])
1694 options.append('--%s' % o[1])
1695 ui.write("%s\n" % "\n".join(options))
1695 ui.write("%s\n" % "\n".join(options))
1696 return
1696 return
1697
1697
1698 cmdlist = cmdutil.findpossible(cmd, table)
1698 cmdlist = cmdutil.findpossible(cmd, table)
1699 if ui.verbose:
1699 if ui.verbose:
1700 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1700 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1701 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1701 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1702
1702
1703 @command('debugdag',
1703 @command('debugdag',
1704 [('t', 'tags', None, _('use tags as labels')),
1704 [('t', 'tags', None, _('use tags as labels')),
1705 ('b', 'branches', None, _('annotate with branch names')),
1705 ('b', 'branches', None, _('annotate with branch names')),
1706 ('', 'dots', None, _('use dots for runs')),
1706 ('', 'dots', None, _('use dots for runs')),
1707 ('s', 'spaces', None, _('separate elements by spaces'))],
1707 ('s', 'spaces', None, _('separate elements by spaces'))],
1708 _('[OPTION]... [FILE [REV]...]'))
1708 _('[OPTION]... [FILE [REV]...]'))
1709 def debugdag(ui, repo, file_=None, *revs, **opts):
1709 def debugdag(ui, repo, file_=None, *revs, **opts):
1710 """format the changelog or an index DAG as a concise textual description
1710 """format the changelog or an index DAG as a concise textual description
1711
1711
1712 If you pass a revlog index, the revlog's DAG is emitted. If you list
1712 If you pass a revlog index, the revlog's DAG is emitted. If you list
1713 revision numbers, they get labeled in the output as rN.
1713 revision numbers, they get labeled in the output as rN.
1714
1714
1715 Otherwise, the changelog DAG of the current repo is emitted.
1715 Otherwise, the changelog DAG of the current repo is emitted.
1716 """
1716 """
1717 spaces = opts.get('spaces')
1717 spaces = opts.get('spaces')
1718 dots = opts.get('dots')
1718 dots = opts.get('dots')
1719 if file_:
1719 if file_:
1720 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1720 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1721 revs = set((int(r) for r in revs))
1721 revs = set((int(r) for r in revs))
1722 def events():
1722 def events():
1723 for r in rlog:
1723 for r in rlog:
1724 yield 'n', (r, list(set(p for p in rlog.parentrevs(r)
1724 yield 'n', (r, list(set(p for p in rlog.parentrevs(r)
1725 if p != -1)))
1725 if p != -1)))
1726 if r in revs:
1726 if r in revs:
1727 yield 'l', (r, "r%i" % r)
1727 yield 'l', (r, "r%i" % r)
1728 elif repo:
1728 elif repo:
1729 cl = repo.changelog
1729 cl = repo.changelog
1730 tags = opts.get('tags')
1730 tags = opts.get('tags')
1731 branches = opts.get('branches')
1731 branches = opts.get('branches')
1732 if tags:
1732 if tags:
1733 labels = {}
1733 labels = {}
1734 for l, n in repo.tags().items():
1734 for l, n in repo.tags().items():
1735 labels.setdefault(cl.rev(n), []).append(l)
1735 labels.setdefault(cl.rev(n), []).append(l)
1736 def events():
1736 def events():
1737 b = "default"
1737 b = "default"
1738 for r in cl:
1738 for r in cl:
1739 if branches:
1739 if branches:
1740 newb = cl.read(cl.node(r))[5]['branch']
1740 newb = cl.read(cl.node(r))[5]['branch']
1741 if newb != b:
1741 if newb != b:
1742 yield 'a', newb
1742 yield 'a', newb
1743 b = newb
1743 b = newb
1744 yield 'n', (r, list(set(p for p in cl.parentrevs(r)
1744 yield 'n', (r, list(set(p for p in cl.parentrevs(r)
1745 if p != -1)))
1745 if p != -1)))
1746 if tags:
1746 if tags:
1747 ls = labels.get(r)
1747 ls = labels.get(r)
1748 if ls:
1748 if ls:
1749 for l in ls:
1749 for l in ls:
1750 yield 'l', (r, l)
1750 yield 'l', (r, l)
1751 else:
1751 else:
1752 raise util.Abort(_('need repo for changelog dag'))
1752 raise util.Abort(_('need repo for changelog dag'))
1753
1753
1754 for line in dagparser.dagtextlines(events(),
1754 for line in dagparser.dagtextlines(events(),
1755 addspaces=spaces,
1755 addspaces=spaces,
1756 wraplabels=True,
1756 wraplabels=True,
1757 wrapannotations=True,
1757 wrapannotations=True,
1758 wrapnonlinear=dots,
1758 wrapnonlinear=dots,
1759 usedots=dots,
1759 usedots=dots,
1760 maxlinewidth=70):
1760 maxlinewidth=70):
1761 ui.write(line)
1761 ui.write(line)
1762 ui.write("\n")
1762 ui.write("\n")
1763
1763
1764 @command('debugdata',
1764 @command('debugdata',
1765 [('c', 'changelog', False, _('open changelog')),
1765 [('c', 'changelog', False, _('open changelog')),
1766 ('m', 'manifest', False, _('open manifest'))],
1766 ('m', 'manifest', False, _('open manifest'))],
1767 _('-c|-m|FILE REV'))
1767 _('-c|-m|FILE REV'))
1768 def debugdata(ui, repo, file_, rev = None, **opts):
1768 def debugdata(ui, repo, file_, rev = None, **opts):
1769 """dump the contents of a data file revision"""
1769 """dump the contents of a data file revision"""
1770 if opts.get('changelog') or opts.get('manifest'):
1770 if opts.get('changelog') or opts.get('manifest'):
1771 file_, rev = None, file_
1771 file_, rev = None, file_
1772 elif rev is None:
1772 elif rev is None:
1773 raise error.CommandError('debugdata', _('invalid arguments'))
1773 raise error.CommandError('debugdata', _('invalid arguments'))
1774 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1774 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1775 try:
1775 try:
1776 ui.write(r.revision(r.lookup(rev)))
1776 ui.write(r.revision(r.lookup(rev)))
1777 except KeyError:
1777 except KeyError:
1778 raise util.Abort(_('invalid revision identifier %s') % rev)
1778 raise util.Abort(_('invalid revision identifier %s') % rev)
1779
1779
1780 @command('debugdate',
1780 @command('debugdate',
1781 [('e', 'extended', None, _('try extended date formats'))],
1781 [('e', 'extended', None, _('try extended date formats'))],
1782 _('[-e] DATE [RANGE]'))
1782 _('[-e] DATE [RANGE]'))
1783 def debugdate(ui, date, range=None, **opts):
1783 def debugdate(ui, date, range=None, **opts):
1784 """parse and display a date"""
1784 """parse and display a date"""
1785 if opts["extended"]:
1785 if opts["extended"]:
1786 d = util.parsedate(date, util.extendeddateformats)
1786 d = util.parsedate(date, util.extendeddateformats)
1787 else:
1787 else:
1788 d = util.parsedate(date)
1788 d = util.parsedate(date)
1789 ui.write("internal: %s %s\n" % d)
1789 ui.write("internal: %s %s\n" % d)
1790 ui.write("standard: %s\n" % util.datestr(d))
1790 ui.write("standard: %s\n" % util.datestr(d))
1791 if range:
1791 if range:
1792 m = util.matchdate(range)
1792 m = util.matchdate(range)
1793 ui.write("match: %s\n" % m(d[0]))
1793 ui.write("match: %s\n" % m(d[0]))
1794
1794
1795 @command('debugdiscovery',
1795 @command('debugdiscovery',
1796 [('', 'old', None, _('use old-style discovery')),
1796 [('', 'old', None, _('use old-style discovery')),
1797 ('', 'nonheads', None,
1797 ('', 'nonheads', None,
1798 _('use old-style discovery with non-heads included')),
1798 _('use old-style discovery with non-heads included')),
1799 ] + remoteopts,
1799 ] + remoteopts,
1800 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1800 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1801 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1801 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1802 """runs the changeset discovery protocol in isolation"""
1802 """runs the changeset discovery protocol in isolation"""
1803 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1803 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
1804 opts.get('branch'))
1804 opts.get('branch'))
1805 remote = hg.peer(repo, opts, remoteurl)
1805 remote = hg.peer(repo, opts, remoteurl)
1806 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1806 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1807
1807
1808 # make sure tests are repeatable
1808 # make sure tests are repeatable
1809 random.seed(12323)
1809 random.seed(12323)
1810
1810
1811 def doit(localheads, remoteheads, remote=remote):
1811 def doit(localheads, remoteheads, remote=remote):
1812 if opts.get('old'):
1812 if opts.get('old'):
1813 if localheads:
1813 if localheads:
1814 raise util.Abort('cannot use localheads with old style '
1814 raise util.Abort('cannot use localheads with old style '
1815 'discovery')
1815 'discovery')
1816 if not util.safehasattr(remote, 'branches'):
1816 if not util.safehasattr(remote, 'branches'):
1817 # enable in-client legacy support
1817 # enable in-client legacy support
1818 remote = localrepo.locallegacypeer(remote.local())
1818 remote = localrepo.locallegacypeer(remote.local())
1819 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1819 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1820 force=True)
1820 force=True)
1821 common = set(common)
1821 common = set(common)
1822 if not opts.get('nonheads'):
1822 if not opts.get('nonheads'):
1823 ui.write("unpruned common: %s\n" % " ".join([short(n)
1823 ui.write("unpruned common: %s\n" % " ".join([short(n)
1824 for n in common]))
1824 for n in common]))
1825 dag = dagutil.revlogdag(repo.changelog)
1825 dag = dagutil.revlogdag(repo.changelog)
1826 all = dag.ancestorset(dag.internalizeall(common))
1826 all = dag.ancestorset(dag.internalizeall(common))
1827 common = dag.externalizeall(dag.headsetofconnecteds(all))
1827 common = dag.externalizeall(dag.headsetofconnecteds(all))
1828 else:
1828 else:
1829 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1829 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1830 common = set(common)
1830 common = set(common)
1831 rheads = set(hds)
1831 rheads = set(hds)
1832 lheads = set(repo.heads())
1832 lheads = set(repo.heads())
1833 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1833 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1834 if lheads <= common:
1834 if lheads <= common:
1835 ui.write("local is subset\n")
1835 ui.write("local is subset\n")
1836 elif rheads <= common:
1836 elif rheads <= common:
1837 ui.write("remote is subset\n")
1837 ui.write("remote is subset\n")
1838
1838
1839 serverlogs = opts.get('serverlog')
1839 serverlogs = opts.get('serverlog')
1840 if serverlogs:
1840 if serverlogs:
1841 for filename in serverlogs:
1841 for filename in serverlogs:
1842 logfile = open(filename, 'r')
1842 logfile = open(filename, 'r')
1843 try:
1843 try:
1844 line = logfile.readline()
1844 line = logfile.readline()
1845 while line:
1845 while line:
1846 parts = line.strip().split(';')
1846 parts = line.strip().split(';')
1847 op = parts[1]
1847 op = parts[1]
1848 if op == 'cg':
1848 if op == 'cg':
1849 pass
1849 pass
1850 elif op == 'cgss':
1850 elif op == 'cgss':
1851 doit(parts[2].split(' '), parts[3].split(' '))
1851 doit(parts[2].split(' '), parts[3].split(' '))
1852 elif op == 'unb':
1852 elif op == 'unb':
1853 doit(parts[3].split(' '), parts[2].split(' '))
1853 doit(parts[3].split(' '), parts[2].split(' '))
1854 line = logfile.readline()
1854 line = logfile.readline()
1855 finally:
1855 finally:
1856 logfile.close()
1856 logfile.close()
1857
1857
1858 else:
1858 else:
1859 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1859 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1860 opts.get('remote_head'))
1860 opts.get('remote_head'))
1861 localrevs = opts.get('local_head')
1861 localrevs = opts.get('local_head')
1862 doit(localrevs, remoterevs)
1862 doit(localrevs, remoterevs)
1863
1863
1864 @command('debugfileset',
1864 @command('debugfileset',
1865 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
1865 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
1866 _('[-r REV] FILESPEC'))
1866 _('[-r REV] FILESPEC'))
1867 def debugfileset(ui, repo, expr, **opts):
1867 def debugfileset(ui, repo, expr, **opts):
1868 '''parse and apply a fileset specification'''
1868 '''parse and apply a fileset specification'''
1869 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
1869 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
1870 if ui.verbose:
1870 if ui.verbose:
1871 tree = fileset.parse(expr)[0]
1871 tree = fileset.parse(expr)[0]
1872 ui.note(tree, "\n")
1872 ui.note(tree, "\n")
1873
1873
1874 for f in fileset.getfileset(ctx, expr):
1874 for f in fileset.getfileset(ctx, expr):
1875 ui.write("%s\n" % f)
1875 ui.write("%s\n" % f)
1876
1876
1877 @command('debugfsinfo', [], _('[PATH]'))
1877 @command('debugfsinfo', [], _('[PATH]'))
1878 def debugfsinfo(ui, path = "."):
1878 def debugfsinfo(ui, path = "."):
1879 """show information detected about current filesystem"""
1879 """show information detected about current filesystem"""
1880 util.writefile('.debugfsinfo', '')
1880 util.writefile('.debugfsinfo', '')
1881 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1881 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1882 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1882 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1883 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1883 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1884 and 'yes' or 'no'))
1884 and 'yes' or 'no'))
1885 os.unlink('.debugfsinfo')
1885 os.unlink('.debugfsinfo')
1886
1886
1887 @command('debuggetbundle',
1887 @command('debuggetbundle',
1888 [('H', 'head', [], _('id of head node'), _('ID')),
1888 [('H', 'head', [], _('id of head node'), _('ID')),
1889 ('C', 'common', [], _('id of common node'), _('ID')),
1889 ('C', 'common', [], _('id of common node'), _('ID')),
1890 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1890 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1891 _('REPO FILE [-H|-C ID]...'))
1891 _('REPO FILE [-H|-C ID]...'))
1892 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1892 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1893 """retrieves a bundle from a repo
1893 """retrieves a bundle from a repo
1894
1894
1895 Every ID must be a full-length hex node id string. Saves the bundle to the
1895 Every ID must be a full-length hex node id string. Saves the bundle to the
1896 given file.
1896 given file.
1897 """
1897 """
1898 repo = hg.peer(ui, opts, repopath)
1898 repo = hg.peer(ui, opts, repopath)
1899 if not repo.capable('getbundle'):
1899 if not repo.capable('getbundle'):
1900 raise util.Abort("getbundle() not supported by target repository")
1900 raise util.Abort("getbundle() not supported by target repository")
1901 args = {}
1901 args = {}
1902 if common:
1902 if common:
1903 args['common'] = [bin(s) for s in common]
1903 args['common'] = [bin(s) for s in common]
1904 if head:
1904 if head:
1905 args['heads'] = [bin(s) for s in head]
1905 args['heads'] = [bin(s) for s in head]
1906 bundle = repo.getbundle('debug', **args)
1906 bundle = repo.getbundle('debug', **args)
1907
1907
1908 bundletype = opts.get('type', 'bzip2').lower()
1908 bundletype = opts.get('type', 'bzip2').lower()
1909 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1909 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1910 bundletype = btypes.get(bundletype)
1910 bundletype = btypes.get(bundletype)
1911 if bundletype not in changegroup.bundletypes:
1911 if bundletype not in changegroup.bundletypes:
1912 raise util.Abort(_('unknown bundle type specified with --type'))
1912 raise util.Abort(_('unknown bundle type specified with --type'))
1913 changegroup.writebundle(bundle, bundlepath, bundletype)
1913 changegroup.writebundle(bundle, bundlepath, bundletype)
1914
1914
1915 @command('debugignore', [], '')
1915 @command('debugignore', [], '')
1916 def debugignore(ui, repo, *values, **opts):
1916 def debugignore(ui, repo, *values, **opts):
1917 """display the combined ignore pattern"""
1917 """display the combined ignore pattern"""
1918 ignore = repo.dirstate._ignore
1918 ignore = repo.dirstate._ignore
1919 includepat = getattr(ignore, 'includepat', None)
1919 includepat = getattr(ignore, 'includepat', None)
1920 if includepat is not None:
1920 if includepat is not None:
1921 ui.write("%s\n" % includepat)
1921 ui.write("%s\n" % includepat)
1922 else:
1922 else:
1923 raise util.Abort(_("no ignore patterns found"))
1923 raise util.Abort(_("no ignore patterns found"))
1924
1924
1925 @command('debugindex',
1925 @command('debugindex',
1926 [('c', 'changelog', False, _('open changelog')),
1926 [('c', 'changelog', False, _('open changelog')),
1927 ('m', 'manifest', False, _('open manifest')),
1927 ('m', 'manifest', False, _('open manifest')),
1928 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1928 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1929 _('[-f FORMAT] -c|-m|FILE'))
1929 _('[-f FORMAT] -c|-m|FILE'))
1930 def debugindex(ui, repo, file_ = None, **opts):
1930 def debugindex(ui, repo, file_ = None, **opts):
1931 """dump the contents of an index file"""
1931 """dump the contents of an index file"""
1932 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1932 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1933 format = opts.get('format', 0)
1933 format = opts.get('format', 0)
1934 if format not in (0, 1):
1934 if format not in (0, 1):
1935 raise util.Abort(_("unknown format %d") % format)
1935 raise util.Abort(_("unknown format %d") % format)
1936
1936
1937 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1937 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1938 if generaldelta:
1938 if generaldelta:
1939 basehdr = ' delta'
1939 basehdr = ' delta'
1940 else:
1940 else:
1941 basehdr = ' base'
1941 basehdr = ' base'
1942
1942
1943 if format == 0:
1943 if format == 0:
1944 ui.write(" rev offset length " + basehdr + " linkrev"
1944 ui.write(" rev offset length " + basehdr + " linkrev"
1945 " nodeid p1 p2\n")
1945 " nodeid p1 p2\n")
1946 elif format == 1:
1946 elif format == 1:
1947 ui.write(" rev flag offset length"
1947 ui.write(" rev flag offset length"
1948 " size " + basehdr + " link p1 p2"
1948 " size " + basehdr + " link p1 p2"
1949 " nodeid\n")
1949 " nodeid\n")
1950
1950
1951 for i in r:
1951 for i in r:
1952 node = r.node(i)
1952 node = r.node(i)
1953 if generaldelta:
1953 if generaldelta:
1954 base = r.deltaparent(i)
1954 base = r.deltaparent(i)
1955 else:
1955 else:
1956 base = r.chainbase(i)
1956 base = r.chainbase(i)
1957 if format == 0:
1957 if format == 0:
1958 try:
1958 try:
1959 pp = r.parents(node)
1959 pp = r.parents(node)
1960 except Exception:
1960 except Exception:
1961 pp = [nullid, nullid]
1961 pp = [nullid, nullid]
1962 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1962 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1963 i, r.start(i), r.length(i), base, r.linkrev(i),
1963 i, r.start(i), r.length(i), base, r.linkrev(i),
1964 short(node), short(pp[0]), short(pp[1])))
1964 short(node), short(pp[0]), short(pp[1])))
1965 elif format == 1:
1965 elif format == 1:
1966 pr = r.parentrevs(i)
1966 pr = r.parentrevs(i)
1967 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1967 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1968 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1968 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1969 base, r.linkrev(i), pr[0], pr[1], short(node)))
1969 base, r.linkrev(i), pr[0], pr[1], short(node)))
1970
1970
1971 @command('debugindexdot', [], _('FILE'))
1971 @command('debugindexdot', [], _('FILE'))
1972 def debugindexdot(ui, repo, file_):
1972 def debugindexdot(ui, repo, file_):
1973 """dump an index DAG as a graphviz dot file"""
1973 """dump an index DAG as a graphviz dot file"""
1974 r = None
1974 r = None
1975 if repo:
1975 if repo:
1976 filelog = repo.file(file_)
1976 filelog = repo.file(file_)
1977 if len(filelog):
1977 if len(filelog):
1978 r = filelog
1978 r = filelog
1979 if not r:
1979 if not r:
1980 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1980 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1981 ui.write("digraph G {\n")
1981 ui.write("digraph G {\n")
1982 for i in r:
1982 for i in r:
1983 node = r.node(i)
1983 node = r.node(i)
1984 pp = r.parents(node)
1984 pp = r.parents(node)
1985 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1985 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1986 if pp[1] != nullid:
1986 if pp[1] != nullid:
1987 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1987 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1988 ui.write("}\n")
1988 ui.write("}\n")
1989
1989
1990 @command('debuginstall', [], '')
1990 @command('debuginstall', [], '')
1991 def debuginstall(ui):
1991 def debuginstall(ui):
1992 '''test Mercurial installation
1992 '''test Mercurial installation
1993
1993
1994 Returns 0 on success.
1994 Returns 0 on success.
1995 '''
1995 '''
1996
1996
1997 def writetemp(contents):
1997 def writetemp(contents):
1998 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1998 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1999 f = os.fdopen(fd, "wb")
1999 f = os.fdopen(fd, "wb")
2000 f.write(contents)
2000 f.write(contents)
2001 f.close()
2001 f.close()
2002 return name
2002 return name
2003
2003
2004 problems = 0
2004 problems = 0
2005
2005
2006 # encoding
2006 # encoding
2007 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2007 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2008 try:
2008 try:
2009 encoding.fromlocal("test")
2009 encoding.fromlocal("test")
2010 except util.Abort, inst:
2010 except util.Abort, inst:
2011 ui.write(" %s\n" % inst)
2011 ui.write(" %s\n" % inst)
2012 ui.write(_(" (check that your locale is properly set)\n"))
2012 ui.write(_(" (check that your locale is properly set)\n"))
2013 problems += 1
2013 problems += 1
2014
2014
2015 # Python lib
2015 # Python lib
2016 ui.status(_("checking Python lib (%s)...\n")
2016 ui.status(_("checking Python lib (%s)...\n")
2017 % os.path.dirname(os.__file__))
2017 % os.path.dirname(os.__file__))
2018
2018
2019 # compiled modules
2019 # compiled modules
2020 ui.status(_("checking installed modules (%s)...\n")
2020 ui.status(_("checking installed modules (%s)...\n")
2021 % os.path.dirname(__file__))
2021 % os.path.dirname(__file__))
2022 try:
2022 try:
2023 import bdiff, mpatch, base85, osutil
2023 import bdiff, mpatch, base85, osutil
2024 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2024 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2025 except Exception, inst:
2025 except Exception, inst:
2026 ui.write(" %s\n" % inst)
2026 ui.write(" %s\n" % inst)
2027 ui.write(_(" One or more extensions could not be found"))
2027 ui.write(_(" One or more extensions could not be found"))
2028 ui.write(_(" (check that you compiled the extensions)\n"))
2028 ui.write(_(" (check that you compiled the extensions)\n"))
2029 problems += 1
2029 problems += 1
2030
2030
2031 # templates
2031 # templates
2032 import templater
2032 import templater
2033 p = templater.templatepath()
2033 p = templater.templatepath()
2034 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2034 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2035 try:
2035 try:
2036 templater.templater(templater.templatepath("map-cmdline.default"))
2036 templater.templater(templater.templatepath("map-cmdline.default"))
2037 except Exception, inst:
2037 except Exception, inst:
2038 ui.write(" %s\n" % inst)
2038 ui.write(" %s\n" % inst)
2039 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2039 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2040 problems += 1
2040 problems += 1
2041
2041
2042 # editor
2042 # editor
2043 ui.status(_("checking commit editor...\n"))
2043 ui.status(_("checking commit editor...\n"))
2044 editor = ui.geteditor()
2044 editor = ui.geteditor()
2045 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
2045 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
2046 if not cmdpath:
2046 if not cmdpath:
2047 if editor == 'vi':
2047 if editor == 'vi':
2048 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2048 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2049 ui.write(_(" (specify a commit editor in your configuration"
2049 ui.write(_(" (specify a commit editor in your configuration"
2050 " file)\n"))
2050 " file)\n"))
2051 else:
2051 else:
2052 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2052 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2053 ui.write(_(" (specify a commit editor in your configuration"
2053 ui.write(_(" (specify a commit editor in your configuration"
2054 " file)\n"))
2054 " file)\n"))
2055 problems += 1
2055 problems += 1
2056
2056
2057 # check username
2057 # check username
2058 ui.status(_("checking username...\n"))
2058 ui.status(_("checking username...\n"))
2059 try:
2059 try:
2060 ui.username()
2060 ui.username()
2061 except util.Abort, e:
2061 except util.Abort, e:
2062 ui.write(" %s\n" % e)
2062 ui.write(" %s\n" % e)
2063 ui.write(_(" (specify a username in your configuration file)\n"))
2063 ui.write(_(" (specify a username in your configuration file)\n"))
2064 problems += 1
2064 problems += 1
2065
2065
2066 if not problems:
2066 if not problems:
2067 ui.status(_("no problems detected\n"))
2067 ui.status(_("no problems detected\n"))
2068 else:
2068 else:
2069 ui.write(_("%s problems detected,"
2069 ui.write(_("%s problems detected,"
2070 " please check your install!\n") % problems)
2070 " please check your install!\n") % problems)
2071
2071
2072 return problems
2072 return problems
2073
2073
2074 @command('debugknown', [], _('REPO ID...'))
2074 @command('debugknown', [], _('REPO ID...'))
2075 def debugknown(ui, repopath, *ids, **opts):
2075 def debugknown(ui, repopath, *ids, **opts):
2076 """test whether node ids are known to a repo
2076 """test whether node ids are known to a repo
2077
2077
2078 Every ID must be a full-length hex node id string. Returns a list of 0s
2078 Every ID must be a full-length hex node id string. Returns a list of 0s
2079 and 1s indicating unknown/known.
2079 and 1s indicating unknown/known.
2080 """
2080 """
2081 repo = hg.peer(ui, opts, repopath)
2081 repo = hg.peer(ui, opts, repopath)
2082 if not repo.capable('known'):
2082 if not repo.capable('known'):
2083 raise util.Abort("known() not supported by target repository")
2083 raise util.Abort("known() not supported by target repository")
2084 flags = repo.known([bin(s) for s in ids])
2084 flags = repo.known([bin(s) for s in ids])
2085 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2085 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2086
2086
2087 @command('debugobsolete',
2087 @command('debugobsolete',
2088 [('', 'flags', 0, _('markers flag')),
2088 [('', 'flags', 0, _('markers flag')),
2089 ] + commitopts2,
2089 ] + commitopts2,
2090 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2090 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2091 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2091 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2092 """create arbitrary obsolete marker"""
2092 """create arbitrary obsolete marker"""
2093 def parsenodeid(s):
2093 def parsenodeid(s):
2094 try:
2094 try:
2095 # We do not use revsingle/revrange functions here to accept
2095 # We do not use revsingle/revrange functions here to accept
2096 # arbitrary node identifiers, possibly not present in the
2096 # arbitrary node identifiers, possibly not present in the
2097 # local repository.
2097 # local repository.
2098 n = bin(s)
2098 n = bin(s)
2099 if len(n) != len(nullid):
2099 if len(n) != len(nullid):
2100 raise TypeError()
2100 raise TypeError()
2101 return n
2101 return n
2102 except TypeError:
2102 except TypeError:
2103 raise util.Abort('changeset references must be full hexadecimal '
2103 raise util.Abort('changeset references must be full hexadecimal '
2104 'node identifiers')
2104 'node identifiers')
2105
2105
2106 if precursor is not None:
2106 if precursor is not None:
2107 metadata = {}
2107 metadata = {}
2108 if 'date' in opts:
2108 if 'date' in opts:
2109 metadata['date'] = opts['date']
2109 metadata['date'] = opts['date']
2110 metadata['user'] = opts['user'] or ui.username()
2110 metadata['user'] = opts['user'] or ui.username()
2111 succs = tuple(parsenodeid(succ) for succ in successors)
2111 succs = tuple(parsenodeid(succ) for succ in successors)
2112 l = repo.lock()
2112 l = repo.lock()
2113 try:
2113 try:
2114 tr = repo.transaction('debugobsolete')
2114 tr = repo.transaction('debugobsolete')
2115 try:
2115 try:
2116 repo.obsstore.create(tr, parsenodeid(precursor), succs,
2116 repo.obsstore.create(tr, parsenodeid(precursor), succs,
2117 opts['flags'], metadata)
2117 opts['flags'], metadata)
2118 tr.close()
2118 tr.close()
2119 finally:
2119 finally:
2120 tr.release()
2120 tr.release()
2121 finally:
2121 finally:
2122 l.release()
2122 l.release()
2123 else:
2123 else:
2124 for m in obsolete.allmarkers(repo):
2124 for m in obsolete.allmarkers(repo):
2125 ui.write(hex(m.precnode()))
2125 ui.write(hex(m.precnode()))
2126 for repl in m.succnodes():
2126 for repl in m.succnodes():
2127 ui.write(' ')
2127 ui.write(' ')
2128 ui.write(hex(repl))
2128 ui.write(hex(repl))
2129 ui.write(' %X ' % m._data[2])
2129 ui.write(' %X ' % m._data[2])
2130 ui.write(m.metadata())
2130 ui.write(m.metadata())
2131 ui.write('\n')
2131 ui.write('\n')
2132
2132
2133 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
2133 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
2134 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2134 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2135 '''access the pushkey key/value protocol
2135 '''access the pushkey key/value protocol
2136
2136
2137 With two args, list the keys in the given namespace.
2137 With two args, list the keys in the given namespace.
2138
2138
2139 With five args, set a key to new if it currently is set to old.
2139 With five args, set a key to new if it currently is set to old.
2140 Reports success or failure.
2140 Reports success or failure.
2141 '''
2141 '''
2142
2142
2143 target = hg.peer(ui, {}, repopath)
2143 target = hg.peer(ui, {}, repopath)
2144 if keyinfo:
2144 if keyinfo:
2145 key, old, new = keyinfo
2145 key, old, new = keyinfo
2146 r = target.pushkey(namespace, key, old, new)
2146 r = target.pushkey(namespace, key, old, new)
2147 ui.status(str(r) + '\n')
2147 ui.status(str(r) + '\n')
2148 return not r
2148 return not r
2149 else:
2149 else:
2150 for k, v in target.listkeys(namespace).iteritems():
2150 for k, v in target.listkeys(namespace).iteritems():
2151 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2151 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2152 v.encode('string-escape')))
2152 v.encode('string-escape')))
2153
2153
2154 @command('debugpvec', [], _('A B'))
2154 @command('debugpvec', [], _('A B'))
2155 def debugpvec(ui, repo, a, b=None):
2155 def debugpvec(ui, repo, a, b=None):
2156 ca = scmutil.revsingle(repo, a)
2156 ca = scmutil.revsingle(repo, a)
2157 cb = scmutil.revsingle(repo, b)
2157 cb = scmutil.revsingle(repo, b)
2158 pa = pvec.ctxpvec(ca)
2158 pa = pvec.ctxpvec(ca)
2159 pb = pvec.ctxpvec(cb)
2159 pb = pvec.ctxpvec(cb)
2160 if pa == pb:
2160 if pa == pb:
2161 rel = "="
2161 rel = "="
2162 elif pa > pb:
2162 elif pa > pb:
2163 rel = ">"
2163 rel = ">"
2164 elif pa < pb:
2164 elif pa < pb:
2165 rel = "<"
2165 rel = "<"
2166 elif pa | pb:
2166 elif pa | pb:
2167 rel = "|"
2167 rel = "|"
2168 ui.write(_("a: %s\n") % pa)
2168 ui.write(_("a: %s\n") % pa)
2169 ui.write(_("b: %s\n") % pb)
2169 ui.write(_("b: %s\n") % pb)
2170 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2170 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2171 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2171 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2172 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2172 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2173 pa.distance(pb), rel))
2173 pa.distance(pb), rel))
2174
2174
2175 @command('debugrebuildstate',
2175 @command('debugrebuildstate',
2176 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2176 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2177 _('[-r REV] [REV]'))
2177 _('[-r REV] [REV]'))
2178 def debugrebuildstate(ui, repo, rev="tip"):
2178 def debugrebuildstate(ui, repo, rev="tip"):
2179 """rebuild the dirstate as it would look like for the given revision"""
2179 """rebuild the dirstate as it would look like for the given revision"""
2180 ctx = scmutil.revsingle(repo, rev)
2180 ctx = scmutil.revsingle(repo, rev)
2181 wlock = repo.wlock()
2181 wlock = repo.wlock()
2182 try:
2182 try:
2183 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2183 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2184 finally:
2184 finally:
2185 wlock.release()
2185 wlock.release()
2186
2186
2187 @command('debugrename',
2187 @command('debugrename',
2188 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2188 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2189 _('[-r REV] FILE'))
2189 _('[-r REV] FILE'))
2190 def debugrename(ui, repo, file1, *pats, **opts):
2190 def debugrename(ui, repo, file1, *pats, **opts):
2191 """dump rename information"""
2191 """dump rename information"""
2192
2192
2193 ctx = scmutil.revsingle(repo, opts.get('rev'))
2193 ctx = scmutil.revsingle(repo, opts.get('rev'))
2194 m = scmutil.match(ctx, (file1,) + pats, opts)
2194 m = scmutil.match(ctx, (file1,) + pats, opts)
2195 for abs in ctx.walk(m):
2195 for abs in ctx.walk(m):
2196 fctx = ctx[abs]
2196 fctx = ctx[abs]
2197 o = fctx.filelog().renamed(fctx.filenode())
2197 o = fctx.filelog().renamed(fctx.filenode())
2198 rel = m.rel(abs)
2198 rel = m.rel(abs)
2199 if o:
2199 if o:
2200 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2200 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2201 else:
2201 else:
2202 ui.write(_("%s not renamed\n") % rel)
2202 ui.write(_("%s not renamed\n") % rel)
2203
2203
2204 @command('debugrevlog',
2204 @command('debugrevlog',
2205 [('c', 'changelog', False, _('open changelog')),
2205 [('c', 'changelog', False, _('open changelog')),
2206 ('m', 'manifest', False, _('open manifest')),
2206 ('m', 'manifest', False, _('open manifest')),
2207 ('d', 'dump', False, _('dump index data'))],
2207 ('d', 'dump', False, _('dump index data'))],
2208 _('-c|-m|FILE'))
2208 _('-c|-m|FILE'))
2209 def debugrevlog(ui, repo, file_ = None, **opts):
2209 def debugrevlog(ui, repo, file_ = None, **opts):
2210 """show data and statistics about a revlog"""
2210 """show data and statistics about a revlog"""
2211 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2211 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2212
2212
2213 if opts.get("dump"):
2213 if opts.get("dump"):
2214 numrevs = len(r)
2214 numrevs = len(r)
2215 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2215 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2216 " rawsize totalsize compression heads\n")
2216 " rawsize totalsize compression heads\n")
2217 ts = 0
2217 ts = 0
2218 heads = set()
2218 heads = set()
2219 for rev in xrange(numrevs):
2219 for rev in xrange(numrevs):
2220 dbase = r.deltaparent(rev)
2220 dbase = r.deltaparent(rev)
2221 if dbase == -1:
2221 if dbase == -1:
2222 dbase = rev
2222 dbase = rev
2223 cbase = r.chainbase(rev)
2223 cbase = r.chainbase(rev)
2224 p1, p2 = r.parentrevs(rev)
2224 p1, p2 = r.parentrevs(rev)
2225 rs = r.rawsize(rev)
2225 rs = r.rawsize(rev)
2226 ts = ts + rs
2226 ts = ts + rs
2227 heads -= set(r.parentrevs(rev))
2227 heads -= set(r.parentrevs(rev))
2228 heads.add(rev)
2228 heads.add(rev)
2229 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2229 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
2230 (rev, p1, p2, r.start(rev), r.end(rev),
2230 (rev, p1, p2, r.start(rev), r.end(rev),
2231 r.start(dbase), r.start(cbase),
2231 r.start(dbase), r.start(cbase),
2232 r.start(p1), r.start(p2),
2232 r.start(p1), r.start(p2),
2233 rs, ts, ts / r.end(rev), len(heads)))
2233 rs, ts, ts / r.end(rev), len(heads)))
2234 return 0
2234 return 0
2235
2235
2236 v = r.version
2236 v = r.version
2237 format = v & 0xFFFF
2237 format = v & 0xFFFF
2238 flags = []
2238 flags = []
2239 gdelta = False
2239 gdelta = False
2240 if v & revlog.REVLOGNGINLINEDATA:
2240 if v & revlog.REVLOGNGINLINEDATA:
2241 flags.append('inline')
2241 flags.append('inline')
2242 if v & revlog.REVLOGGENERALDELTA:
2242 if v & revlog.REVLOGGENERALDELTA:
2243 gdelta = True
2243 gdelta = True
2244 flags.append('generaldelta')
2244 flags.append('generaldelta')
2245 if not flags:
2245 if not flags:
2246 flags = ['(none)']
2246 flags = ['(none)']
2247
2247
2248 nummerges = 0
2248 nummerges = 0
2249 numfull = 0
2249 numfull = 0
2250 numprev = 0
2250 numprev = 0
2251 nump1 = 0
2251 nump1 = 0
2252 nump2 = 0
2252 nump2 = 0
2253 numother = 0
2253 numother = 0
2254 nump1prev = 0
2254 nump1prev = 0
2255 nump2prev = 0
2255 nump2prev = 0
2256 chainlengths = []
2256 chainlengths = []
2257
2257
2258 datasize = [None, 0, 0L]
2258 datasize = [None, 0, 0L]
2259 fullsize = [None, 0, 0L]
2259 fullsize = [None, 0, 0L]
2260 deltasize = [None, 0, 0L]
2260 deltasize = [None, 0, 0L]
2261
2261
2262 def addsize(size, l):
2262 def addsize(size, l):
2263 if l[0] is None or size < l[0]:
2263 if l[0] is None or size < l[0]:
2264 l[0] = size
2264 l[0] = size
2265 if size > l[1]:
2265 if size > l[1]:
2266 l[1] = size
2266 l[1] = size
2267 l[2] += size
2267 l[2] += size
2268
2268
2269 numrevs = len(r)
2269 numrevs = len(r)
2270 for rev in xrange(numrevs):
2270 for rev in xrange(numrevs):
2271 p1, p2 = r.parentrevs(rev)
2271 p1, p2 = r.parentrevs(rev)
2272 delta = r.deltaparent(rev)
2272 delta = r.deltaparent(rev)
2273 if format > 0:
2273 if format > 0:
2274 addsize(r.rawsize(rev), datasize)
2274 addsize(r.rawsize(rev), datasize)
2275 if p2 != nullrev:
2275 if p2 != nullrev:
2276 nummerges += 1
2276 nummerges += 1
2277 size = r.length(rev)
2277 size = r.length(rev)
2278 if delta == nullrev:
2278 if delta == nullrev:
2279 chainlengths.append(0)
2279 chainlengths.append(0)
2280 numfull += 1
2280 numfull += 1
2281 addsize(size, fullsize)
2281 addsize(size, fullsize)
2282 else:
2282 else:
2283 chainlengths.append(chainlengths[delta] + 1)
2283 chainlengths.append(chainlengths[delta] + 1)
2284 addsize(size, deltasize)
2284 addsize(size, deltasize)
2285 if delta == rev - 1:
2285 if delta == rev - 1:
2286 numprev += 1
2286 numprev += 1
2287 if delta == p1:
2287 if delta == p1:
2288 nump1prev += 1
2288 nump1prev += 1
2289 elif delta == p2:
2289 elif delta == p2:
2290 nump2prev += 1
2290 nump2prev += 1
2291 elif delta == p1:
2291 elif delta == p1:
2292 nump1 += 1
2292 nump1 += 1
2293 elif delta == p2:
2293 elif delta == p2:
2294 nump2 += 1
2294 nump2 += 1
2295 elif delta != nullrev:
2295 elif delta != nullrev:
2296 numother += 1
2296 numother += 1
2297
2297
2298 # Adjust size min value for empty cases
2298 # Adjust size min value for empty cases
2299 for size in (datasize, fullsize, deltasize):
2299 for size in (datasize, fullsize, deltasize):
2300 if size[0] is None:
2300 if size[0] is None:
2301 size[0] = 0
2301 size[0] = 0
2302
2302
2303 numdeltas = numrevs - numfull
2303 numdeltas = numrevs - numfull
2304 numoprev = numprev - nump1prev - nump2prev
2304 numoprev = numprev - nump1prev - nump2prev
2305 totalrawsize = datasize[2]
2305 totalrawsize = datasize[2]
2306 datasize[2] /= numrevs
2306 datasize[2] /= numrevs
2307 fulltotal = fullsize[2]
2307 fulltotal = fullsize[2]
2308 fullsize[2] /= numfull
2308 fullsize[2] /= numfull
2309 deltatotal = deltasize[2]
2309 deltatotal = deltasize[2]
2310 if numrevs - numfull > 0:
2310 if numrevs - numfull > 0:
2311 deltasize[2] /= numrevs - numfull
2311 deltasize[2] /= numrevs - numfull
2312 totalsize = fulltotal + deltatotal
2312 totalsize = fulltotal + deltatotal
2313 avgchainlen = sum(chainlengths) / numrevs
2313 avgchainlen = sum(chainlengths) / numrevs
2314 compratio = totalrawsize / totalsize
2314 compratio = totalrawsize / totalsize
2315
2315
2316 basedfmtstr = '%%%dd\n'
2316 basedfmtstr = '%%%dd\n'
2317 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2317 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2318
2318
2319 def dfmtstr(max):
2319 def dfmtstr(max):
2320 return basedfmtstr % len(str(max))
2320 return basedfmtstr % len(str(max))
2321 def pcfmtstr(max, padding=0):
2321 def pcfmtstr(max, padding=0):
2322 return basepcfmtstr % (len(str(max)), ' ' * padding)
2322 return basepcfmtstr % (len(str(max)), ' ' * padding)
2323
2323
2324 def pcfmt(value, total):
2324 def pcfmt(value, total):
2325 return (value, 100 * float(value) / total)
2325 return (value, 100 * float(value) / total)
2326
2326
2327 ui.write('format : %d\n' % format)
2327 ui.write('format : %d\n' % format)
2328 ui.write('flags : %s\n' % ', '.join(flags))
2328 ui.write('flags : %s\n' % ', '.join(flags))
2329
2329
2330 ui.write('\n')
2330 ui.write('\n')
2331 fmt = pcfmtstr(totalsize)
2331 fmt = pcfmtstr(totalsize)
2332 fmt2 = dfmtstr(totalsize)
2332 fmt2 = dfmtstr(totalsize)
2333 ui.write('revisions : ' + fmt2 % numrevs)
2333 ui.write('revisions : ' + fmt2 % numrevs)
2334 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2334 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
2335 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2335 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
2336 ui.write('revisions : ' + fmt2 % numrevs)
2336 ui.write('revisions : ' + fmt2 % numrevs)
2337 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2337 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2338 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2338 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2339 ui.write('revision size : ' + fmt2 % totalsize)
2339 ui.write('revision size : ' + fmt2 % totalsize)
2340 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2340 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2341 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2341 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2342
2342
2343 ui.write('\n')
2343 ui.write('\n')
2344 fmt = dfmtstr(max(avgchainlen, compratio))
2344 fmt = dfmtstr(max(avgchainlen, compratio))
2345 ui.write('avg chain length : ' + fmt % avgchainlen)
2345 ui.write('avg chain length : ' + fmt % avgchainlen)
2346 ui.write('compression ratio : ' + fmt % compratio)
2346 ui.write('compression ratio : ' + fmt % compratio)
2347
2347
2348 if format > 0:
2348 if format > 0:
2349 ui.write('\n')
2349 ui.write('\n')
2350 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2350 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2351 % tuple(datasize))
2351 % tuple(datasize))
2352 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2352 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2353 % tuple(fullsize))
2353 % tuple(fullsize))
2354 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2354 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2355 % tuple(deltasize))
2355 % tuple(deltasize))
2356
2356
2357 if numdeltas > 0:
2357 if numdeltas > 0:
2358 ui.write('\n')
2358 ui.write('\n')
2359 fmt = pcfmtstr(numdeltas)
2359 fmt = pcfmtstr(numdeltas)
2360 fmt2 = pcfmtstr(numdeltas, 4)
2360 fmt2 = pcfmtstr(numdeltas, 4)
2361 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2361 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2362 if numprev > 0:
2362 if numprev > 0:
2363 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev,
2363 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev,
2364 numprev))
2364 numprev))
2365 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev,
2365 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev,
2366 numprev))
2366 numprev))
2367 ui.write(' other : ' + fmt2 % pcfmt(numoprev,
2367 ui.write(' other : ' + fmt2 % pcfmt(numoprev,
2368 numprev))
2368 numprev))
2369 if gdelta:
2369 if gdelta:
2370 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2370 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2371 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2371 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2372 ui.write('deltas against other : ' + fmt % pcfmt(numother,
2372 ui.write('deltas against other : ' + fmt % pcfmt(numother,
2373 numdeltas))
2373 numdeltas))
2374
2374
2375 @command('debugrevspec', [], ('REVSPEC'))
2375 @command('debugrevspec', [], ('REVSPEC'))
2376 def debugrevspec(ui, repo, expr):
2376 def debugrevspec(ui, repo, expr):
2377 """parse and apply a revision specification
2377 """parse and apply a revision specification
2378
2378
2379 Use --verbose to print the parsed tree before and after aliases
2379 Use --verbose to print the parsed tree before and after aliases
2380 expansion.
2380 expansion.
2381 """
2381 """
2382 if ui.verbose:
2382 if ui.verbose:
2383 tree = revset.parse(expr)[0]
2383 tree = revset.parse(expr)[0]
2384 ui.note(revset.prettyformat(tree), "\n")
2384 ui.note(revset.prettyformat(tree), "\n")
2385 newtree = revset.findaliases(ui, tree)
2385 newtree = revset.findaliases(ui, tree)
2386 if newtree != tree:
2386 if newtree != tree:
2387 ui.note(revset.prettyformat(newtree), "\n")
2387 ui.note(revset.prettyformat(newtree), "\n")
2388 func = revset.match(ui, expr)
2388 func = revset.match(ui, expr)
2389 for c in func(repo, range(len(repo))):
2389 for c in func(repo, range(len(repo))):
2390 ui.write("%s\n" % c)
2390 ui.write("%s\n" % c)
2391
2391
2392 @command('debugsetparents', [], _('REV1 [REV2]'))
2392 @command('debugsetparents', [], _('REV1 [REV2]'))
2393 def debugsetparents(ui, repo, rev1, rev2=None):
2393 def debugsetparents(ui, repo, rev1, rev2=None):
2394 """manually set the parents of the current working directory
2394 """manually set the parents of the current working directory
2395
2395
2396 This is useful for writing repository conversion tools, but should
2396 This is useful for writing repository conversion tools, but should
2397 be used with care.
2397 be used with care.
2398
2398
2399 Returns 0 on success.
2399 Returns 0 on success.
2400 """
2400 """
2401
2401
2402 r1 = scmutil.revsingle(repo, rev1).node()
2402 r1 = scmutil.revsingle(repo, rev1).node()
2403 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2403 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2404
2404
2405 wlock = repo.wlock()
2405 wlock = repo.wlock()
2406 try:
2406 try:
2407 repo.setparents(r1, r2)
2407 repo.setparents(r1, r2)
2408 finally:
2408 finally:
2409 wlock.release()
2409 wlock.release()
2410
2410
2411 @command('debugstate',
2411 @command('debugstate',
2412 [('', 'nodates', None, _('do not display the saved mtime')),
2412 [('', 'nodates', None, _('do not display the saved mtime')),
2413 ('', 'datesort', None, _('sort by saved mtime'))],
2413 ('', 'datesort', None, _('sort by saved mtime'))],
2414 _('[OPTION]...'))
2414 _('[OPTION]...'))
2415 def debugstate(ui, repo, nodates=None, datesort=None):
2415 def debugstate(ui, repo, nodates=None, datesort=None):
2416 """show the contents of the current dirstate"""
2416 """show the contents of the current dirstate"""
2417 timestr = ""
2417 timestr = ""
2418 showdate = not nodates
2418 showdate = not nodates
2419 if datesort:
2419 if datesort:
2420 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2420 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2421 else:
2421 else:
2422 keyfunc = None # sort by filename
2422 keyfunc = None # sort by filename
2423 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2423 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2424 if showdate:
2424 if showdate:
2425 if ent[3] == -1:
2425 if ent[3] == -1:
2426 # Pad or slice to locale representation
2426 # Pad or slice to locale representation
2427 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2427 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2428 time.localtime(0)))
2428 time.localtime(0)))
2429 timestr = 'unset'
2429 timestr = 'unset'
2430 timestr = (timestr[:locale_len] +
2430 timestr = (timestr[:locale_len] +
2431 ' ' * (locale_len - len(timestr)))
2431 ' ' * (locale_len - len(timestr)))
2432 else:
2432 else:
2433 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2433 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2434 time.localtime(ent[3]))
2434 time.localtime(ent[3]))
2435 if ent[1] & 020000:
2435 if ent[1] & 020000:
2436 mode = 'lnk'
2436 mode = 'lnk'
2437 else:
2437 else:
2438 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2438 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2439 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2439 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2440 for f in repo.dirstate.copies():
2440 for f in repo.dirstate.copies():
2441 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2441 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2442
2442
2443 @command('debugsub',
2443 @command('debugsub',
2444 [('r', 'rev', '',
2444 [('r', 'rev', '',
2445 _('revision to check'), _('REV'))],
2445 _('revision to check'), _('REV'))],
2446 _('[-r REV] [REV]'))
2446 _('[-r REV] [REV]'))
2447 def debugsub(ui, repo, rev=None):
2447 def debugsub(ui, repo, rev=None):
2448 ctx = scmutil.revsingle(repo, rev, None)
2448 ctx = scmutil.revsingle(repo, rev, None)
2449 for k, v in sorted(ctx.substate.items()):
2449 for k, v in sorted(ctx.substate.items()):
2450 ui.write('path %s\n' % k)
2450 ui.write('path %s\n' % k)
2451 ui.write(' source %s\n' % v[0])
2451 ui.write(' source %s\n' % v[0])
2452 ui.write(' revision %s\n' % v[1])
2452 ui.write(' revision %s\n' % v[1])
2453
2453
2454 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2454 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2455 def debugwalk(ui, repo, *pats, **opts):
2455 def debugwalk(ui, repo, *pats, **opts):
2456 """show how files match on given patterns"""
2456 """show how files match on given patterns"""
2457 m = scmutil.match(repo[None], pats, opts)
2457 m = scmutil.match(repo[None], pats, opts)
2458 items = list(repo.walk(m))
2458 items = list(repo.walk(m))
2459 if not items:
2459 if not items:
2460 return
2460 return
2461 f = lambda fn: fn
2461 f = lambda fn: fn
2462 if ui.configbool('ui', 'slash') and os.sep != '/':
2462 if ui.configbool('ui', 'slash') and os.sep != '/':
2463 f = lambda fn: util.normpath(fn)
2463 f = lambda fn: util.normpath(fn)
2464 fmt = 'f %%-%ds %%-%ds %%s' % (
2464 fmt = 'f %%-%ds %%-%ds %%s' % (
2465 max([len(abs) for abs in items]),
2465 max([len(abs) for abs in items]),
2466 max([len(m.rel(abs)) for abs in items]))
2466 max([len(m.rel(abs)) for abs in items]))
2467 for abs in items:
2467 for abs in items:
2468 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2468 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2469 ui.write("%s\n" % line.rstrip())
2469 ui.write("%s\n" % line.rstrip())
2470
2470
2471 @command('debugwireargs',
2471 @command('debugwireargs',
2472 [('', 'three', '', 'three'),
2472 [('', 'three', '', 'three'),
2473 ('', 'four', '', 'four'),
2473 ('', 'four', '', 'four'),
2474 ('', 'five', '', 'five'),
2474 ('', 'five', '', 'five'),
2475 ] + remoteopts,
2475 ] + remoteopts,
2476 _('REPO [OPTIONS]... [ONE [TWO]]'))
2476 _('REPO [OPTIONS]... [ONE [TWO]]'))
2477 def debugwireargs(ui, repopath, *vals, **opts):
2477 def debugwireargs(ui, repopath, *vals, **opts):
2478 repo = hg.peer(ui, opts, repopath)
2478 repo = hg.peer(ui, opts, repopath)
2479 for opt in remoteopts:
2479 for opt in remoteopts:
2480 del opts[opt[1]]
2480 del opts[opt[1]]
2481 args = {}
2481 args = {}
2482 for k, v in opts.iteritems():
2482 for k, v in opts.iteritems():
2483 if v:
2483 if v:
2484 args[k] = v
2484 args[k] = v
2485 # run twice to check that we don't mess up the stream for the next command
2485 # run twice to check that we don't mess up the stream for the next command
2486 res1 = repo.debugwireargs(*vals, **args)
2486 res1 = repo.debugwireargs(*vals, **args)
2487 res2 = repo.debugwireargs(*vals, **args)
2487 res2 = repo.debugwireargs(*vals, **args)
2488 ui.write("%s\n" % res1)
2488 ui.write("%s\n" % res1)
2489 if res1 != res2:
2489 if res1 != res2:
2490 ui.warn("%s\n" % res2)
2490 ui.warn("%s\n" % res2)
2491
2491
2492 @command('^diff',
2492 @command('^diff',
2493 [('r', 'rev', [], _('revision'), _('REV')),
2493 [('r', 'rev', [], _('revision'), _('REV')),
2494 ('c', 'change', '', _('change made by revision'), _('REV'))
2494 ('c', 'change', '', _('change made by revision'), _('REV'))
2495 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2495 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2496 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2496 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2497 def diff(ui, repo, *pats, **opts):
2497 def diff(ui, repo, *pats, **opts):
2498 """diff repository (or selected files)
2498 """diff repository (or selected files)
2499
2499
2500 Show differences between revisions for the specified files.
2500 Show differences between revisions for the specified files.
2501
2501
2502 Differences between files are shown using the unified diff format.
2502 Differences between files are shown using the unified diff format.
2503
2503
2504 .. note::
2504 .. note::
2505 diff may generate unexpected results for merges, as it will
2505 diff may generate unexpected results for merges, as it will
2506 default to comparing against the working directory's first
2506 default to comparing against the working directory's first
2507 parent changeset if no revisions are specified.
2507 parent changeset if no revisions are specified.
2508
2508
2509 When two revision arguments are given, then changes are shown
2509 When two revision arguments are given, then changes are shown
2510 between those revisions. If only one revision is specified then
2510 between those revisions. If only one revision is specified then
2511 that revision is compared to the working directory, and, when no
2511 that revision is compared to the working directory, and, when no
2512 revisions are specified, the working directory files are compared
2512 revisions are specified, the working directory files are compared
2513 to its parent.
2513 to its parent.
2514
2514
2515 Alternatively you can specify -c/--change with a revision to see
2515 Alternatively you can specify -c/--change with a revision to see
2516 the changes in that changeset relative to its first parent.
2516 the changes in that changeset relative to its first parent.
2517
2517
2518 Without the -a/--text option, diff will avoid generating diffs of
2518 Without the -a/--text option, diff will avoid generating diffs of
2519 files it detects as binary. With -a, diff will generate a diff
2519 files it detects as binary. With -a, diff will generate a diff
2520 anyway, probably with undesirable results.
2520 anyway, probably with undesirable results.
2521
2521
2522 Use the -g/--git option to generate diffs in the git extended diff
2522 Use the -g/--git option to generate diffs in the git extended diff
2523 format. For more information, read :hg:`help diffs`.
2523 format. For more information, read :hg:`help diffs`.
2524
2524
2525 .. container:: verbose
2525 .. container:: verbose
2526
2526
2527 Examples:
2527 Examples:
2528
2528
2529 - compare a file in the current working directory to its parent::
2529 - compare a file in the current working directory to its parent::
2530
2530
2531 hg diff foo.c
2531 hg diff foo.c
2532
2532
2533 - compare two historical versions of a directory, with rename info::
2533 - compare two historical versions of a directory, with rename info::
2534
2534
2535 hg diff --git -r 1.0:1.2 lib/
2535 hg diff --git -r 1.0:1.2 lib/
2536
2536
2537 - get change stats relative to the last change on some date::
2537 - get change stats relative to the last change on some date::
2538
2538
2539 hg diff --stat -r "date('may 2')"
2539 hg diff --stat -r "date('may 2')"
2540
2540
2541 - diff all newly-added files that contain a keyword::
2541 - diff all newly-added files that contain a keyword::
2542
2542
2543 hg diff "set:added() and grep(GNU)"
2543 hg diff "set:added() and grep(GNU)"
2544
2544
2545 - compare a revision and its parents::
2545 - compare a revision and its parents::
2546
2546
2547 hg diff -c 9353 # compare against first parent
2547 hg diff -c 9353 # compare against first parent
2548 hg diff -r 9353^:9353 # same using revset syntax
2548 hg diff -r 9353^:9353 # same using revset syntax
2549 hg diff -r 9353^2:9353 # compare against the second parent
2549 hg diff -r 9353^2:9353 # compare against the second parent
2550
2550
2551 Returns 0 on success.
2551 Returns 0 on success.
2552 """
2552 """
2553
2553
2554 revs = opts.get('rev')
2554 revs = opts.get('rev')
2555 change = opts.get('change')
2555 change = opts.get('change')
2556 stat = opts.get('stat')
2556 stat = opts.get('stat')
2557 reverse = opts.get('reverse')
2557 reverse = opts.get('reverse')
2558
2558
2559 if revs and change:
2559 if revs and change:
2560 msg = _('cannot specify --rev and --change at the same time')
2560 msg = _('cannot specify --rev and --change at the same time')
2561 raise util.Abort(msg)
2561 raise util.Abort(msg)
2562 elif change:
2562 elif change:
2563 node2 = scmutil.revsingle(repo, change, None).node()
2563 node2 = scmutil.revsingle(repo, change, None).node()
2564 node1 = repo[node2].p1().node()
2564 node1 = repo[node2].p1().node()
2565 else:
2565 else:
2566 node1, node2 = scmutil.revpair(repo, revs)
2566 node1, node2 = scmutil.revpair(repo, revs)
2567
2567
2568 if reverse:
2568 if reverse:
2569 node1, node2 = node2, node1
2569 node1, node2 = node2, node1
2570
2570
2571 diffopts = patch.diffopts(ui, opts)
2571 diffopts = patch.diffopts(ui, opts)
2572 m = scmutil.match(repo[node2], pats, opts)
2572 m = scmutil.match(repo[node2], pats, opts)
2573 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2573 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2574 listsubrepos=opts.get('subrepos'))
2574 listsubrepos=opts.get('subrepos'))
2575
2575
2576 @command('^export',
2576 @command('^export',
2577 [('o', 'output', '',
2577 [('o', 'output', '',
2578 _('print output to file with formatted name'), _('FORMAT')),
2578 _('print output to file with formatted name'), _('FORMAT')),
2579 ('', 'switch-parent', None, _('diff against the second parent')),
2579 ('', 'switch-parent', None, _('diff against the second parent')),
2580 ('r', 'rev', [], _('revisions to export'), _('REV')),
2580 ('r', 'rev', [], _('revisions to export'), _('REV')),
2581 ] + diffopts,
2581 ] + diffopts,
2582 _('[OPTION]... [-o OUTFILESPEC] [-r] REV...'))
2582 _('[OPTION]... [-o OUTFILESPEC] [-r] REV...'))
2583 def export(ui, repo, *changesets, **opts):
2583 def export(ui, repo, *changesets, **opts):
2584 """dump the header and diffs for one or more changesets
2584 """dump the header and diffs for one or more changesets
2585
2585
2586 Print the changeset header and diffs for one or more revisions.
2586 Print the changeset header and diffs for one or more revisions.
2587
2587
2588 The information shown in the changeset header is: author, date,
2588 The information shown in the changeset header is: author, date,
2589 branch name (if non-default), changeset hash, parent(s) and commit
2589 branch name (if non-default), changeset hash, parent(s) and commit
2590 comment.
2590 comment.
2591
2591
2592 .. note::
2592 .. note::
2593 export may generate unexpected diff output for merge
2593 export may generate unexpected diff output for merge
2594 changesets, as it will compare the merge changeset against its
2594 changesets, as it will compare the merge changeset against its
2595 first parent only.
2595 first parent only.
2596
2596
2597 Output may be to a file, in which case the name of the file is
2597 Output may be to a file, in which case the name of the file is
2598 given using a format string. The formatting rules are as follows:
2598 given using a format string. The formatting rules are as follows:
2599
2599
2600 :``%%``: literal "%" character
2600 :``%%``: literal "%" character
2601 :``%H``: changeset hash (40 hexadecimal digits)
2601 :``%H``: changeset hash (40 hexadecimal digits)
2602 :``%N``: number of patches being generated
2602 :``%N``: number of patches being generated
2603 :``%R``: changeset revision number
2603 :``%R``: changeset revision number
2604 :``%b``: basename of the exporting repository
2604 :``%b``: basename of the exporting repository
2605 :``%h``: short-form changeset hash (12 hexadecimal digits)
2605 :``%h``: short-form changeset hash (12 hexadecimal digits)
2606 :``%m``: first line of the commit message (only alphanumeric characters)
2606 :``%m``: first line of the commit message (only alphanumeric characters)
2607 :``%n``: zero-padded sequence number, starting at 1
2607 :``%n``: zero-padded sequence number, starting at 1
2608 :``%r``: zero-padded changeset revision number
2608 :``%r``: zero-padded changeset revision number
2609
2609
2610 Without the -a/--text option, export will avoid generating diffs
2610 Without the -a/--text option, export will avoid generating diffs
2611 of files it detects as binary. With -a, export will generate a
2611 of files it detects as binary. With -a, export will generate a
2612 diff anyway, probably with undesirable results.
2612 diff anyway, probably with undesirable results.
2613
2613
2614 Use the -g/--git option to generate diffs in the git extended diff
2614 Use the -g/--git option to generate diffs in the git extended diff
2615 format. See :hg:`help diffs` for more information.
2615 format. See :hg:`help diffs` for more information.
2616
2616
2617 With the --switch-parent option, the diff will be against the
2617 With the --switch-parent option, the diff will be against the
2618 second parent. It can be useful to review a merge.
2618 second parent. It can be useful to review a merge.
2619
2619
2620 .. container:: verbose
2620 .. container:: verbose
2621
2621
2622 Examples:
2622 Examples:
2623
2623
2624 - use export and import to transplant a bugfix to the current
2624 - use export and import to transplant a bugfix to the current
2625 branch::
2625 branch::
2626
2626
2627 hg export -r 9353 | hg import -
2627 hg export -r 9353 | hg import -
2628
2628
2629 - export all the changesets between two revisions to a file with
2629 - export all the changesets between two revisions to a file with
2630 rename information::
2630 rename information::
2631
2631
2632 hg export --git -r 123:150 > changes.txt
2632 hg export --git -r 123:150 > changes.txt
2633
2633
2634 - split outgoing changes into a series of patches with
2634 - split outgoing changes into a series of patches with
2635 descriptive names::
2635 descriptive names::
2636
2636
2637 hg export -r "outgoing()" -o "%n-%m.patch"
2637 hg export -r "outgoing()" -o "%n-%m.patch"
2638
2638
2639 Returns 0 on success.
2639 Returns 0 on success.
2640 """
2640 """
2641 changesets += tuple(opts.get('rev', []))
2641 changesets += tuple(opts.get('rev', []))
2642 revs = scmutil.revrange(repo, changesets)
2642 revs = scmutil.revrange(repo, changesets)
2643 if not revs:
2643 if not revs:
2644 raise util.Abort(_("export requires at least one changeset"))
2644 raise util.Abort(_("export requires at least one changeset"))
2645 if len(revs) > 1:
2645 if len(revs) > 1:
2646 ui.note(_('exporting patches:\n'))
2646 ui.note(_('exporting patches:\n'))
2647 else:
2647 else:
2648 ui.note(_('exporting patch:\n'))
2648 ui.note(_('exporting patch:\n'))
2649 cmdutil.export(repo, revs, template=opts.get('output'),
2649 cmdutil.export(repo, revs, template=opts.get('output'),
2650 switch_parent=opts.get('switch_parent'),
2650 switch_parent=opts.get('switch_parent'),
2651 opts=patch.diffopts(ui, opts))
2651 opts=patch.diffopts(ui, opts))
2652
2652
2653 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2653 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2654 def forget(ui, repo, *pats, **opts):
2654 def forget(ui, repo, *pats, **opts):
2655 """forget the specified files on the next commit
2655 """forget the specified files on the next commit
2656
2656
2657 Mark the specified files so they will no longer be tracked
2657 Mark the specified files so they will no longer be tracked
2658 after the next commit.
2658 after the next commit.
2659
2659
2660 This only removes files from the current branch, not from the
2660 This only removes files from the current branch, not from the
2661 entire project history, and it does not delete them from the
2661 entire project history, and it does not delete them from the
2662 working directory.
2662 working directory.
2663
2663
2664 To undo a forget before the next commit, see :hg:`add`.
2664 To undo a forget before the next commit, see :hg:`add`.
2665
2665
2666 .. container:: verbose
2666 .. container:: verbose
2667
2667
2668 Examples:
2668 Examples:
2669
2669
2670 - forget newly-added binary files::
2670 - forget newly-added binary files::
2671
2671
2672 hg forget "set:added() and binary()"
2672 hg forget "set:added() and binary()"
2673
2673
2674 - forget files that would be excluded by .hgignore::
2674 - forget files that would be excluded by .hgignore::
2675
2675
2676 hg forget "set:hgignore()"
2676 hg forget "set:hgignore()"
2677
2677
2678 Returns 0 on success.
2678 Returns 0 on success.
2679 """
2679 """
2680
2680
2681 if not pats:
2681 if not pats:
2682 raise util.Abort(_('no files specified'))
2682 raise util.Abort(_('no files specified'))
2683
2683
2684 m = scmutil.match(repo[None], pats, opts)
2684 m = scmutil.match(repo[None], pats, opts)
2685 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2685 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
2686 return rejected and 1 or 0
2686 return rejected and 1 or 0
2687
2687
2688 @command(
2688 @command(
2689 'graft',
2689 'graft',
2690 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2690 [('r', 'rev', [], _('revisions to graft'), _('REV')),
2691 ('c', 'continue', False, _('resume interrupted graft')),
2691 ('c', 'continue', False, _('resume interrupted graft')),
2692 ('e', 'edit', False, _('invoke editor on commit messages')),
2692 ('e', 'edit', False, _('invoke editor on commit messages')),
2693 ('', 'log', None, _('append graft info to log message')),
2693 ('', 'log', None, _('append graft info to log message')),
2694 ('D', 'currentdate', False,
2694 ('D', 'currentdate', False,
2695 _('record the current date as commit date')),
2695 _('record the current date as commit date')),
2696 ('U', 'currentuser', False,
2696 ('U', 'currentuser', False,
2697 _('record the current user as committer'), _('DATE'))]
2697 _('record the current user as committer'), _('DATE'))]
2698 + commitopts2 + mergetoolopts + dryrunopts,
2698 + commitopts2 + mergetoolopts + dryrunopts,
2699 _('[OPTION]... [-r] REV...'))
2699 _('[OPTION]... [-r] REV...'))
2700 def graft(ui, repo, *revs, **opts):
2700 def graft(ui, repo, *revs, **opts):
2701 '''copy changes from other branches onto the current branch
2701 '''copy changes from other branches onto the current branch
2702
2702
2703 This command uses Mercurial's merge logic to copy individual
2703 This command uses Mercurial's merge logic to copy individual
2704 changes from other branches without merging branches in the
2704 changes from other branches without merging branches in the
2705 history graph. This is sometimes known as 'backporting' or
2705 history graph. This is sometimes known as 'backporting' or
2706 'cherry-picking'. By default, graft will copy user, date, and
2706 'cherry-picking'. By default, graft will copy user, date, and
2707 description from the source changesets.
2707 description from the source changesets.
2708
2708
2709 Changesets that are ancestors of the current revision, that have
2709 Changesets that are ancestors of the current revision, that have
2710 already been grafted, or that are merges will be skipped.
2710 already been grafted, or that are merges will be skipped.
2711
2711
2712 If --log is specified, log messages will have a comment appended
2712 If --log is specified, log messages will have a comment appended
2713 of the form::
2713 of the form::
2714
2714
2715 (grafted from CHANGESETHASH)
2715 (grafted from CHANGESETHASH)
2716
2716
2717 If a graft merge results in conflicts, the graft process is
2717 If a graft merge results in conflicts, the graft process is
2718 interrupted so that the current merge can be manually resolved.
2718 interrupted so that the current merge can be manually resolved.
2719 Once all conflicts are addressed, the graft process can be
2719 Once all conflicts are addressed, the graft process can be
2720 continued with the -c/--continue option.
2720 continued with the -c/--continue option.
2721
2721
2722 .. note::
2722 .. note::
2723 The -c/--continue option does not reapply earlier options.
2723 The -c/--continue option does not reapply earlier options.
2724
2724
2725 .. container:: verbose
2725 .. container:: verbose
2726
2726
2727 Examples:
2727 Examples:
2728
2728
2729 - copy a single change to the stable branch and edit its description::
2729 - copy a single change to the stable branch and edit its description::
2730
2730
2731 hg update stable
2731 hg update stable
2732 hg graft --edit 9393
2732 hg graft --edit 9393
2733
2733
2734 - graft a range of changesets with one exception, updating dates::
2734 - graft a range of changesets with one exception, updating dates::
2735
2735
2736 hg graft -D "2085::2093 and not 2091"
2736 hg graft -D "2085::2093 and not 2091"
2737
2737
2738 - continue a graft after resolving conflicts::
2738 - continue a graft after resolving conflicts::
2739
2739
2740 hg graft -c
2740 hg graft -c
2741
2741
2742 - show the source of a grafted changeset::
2742 - show the source of a grafted changeset::
2743
2743
2744 hg log --debug -r tip
2744 hg log --debug -r tip
2745
2745
2746 Returns 0 on successful completion.
2746 Returns 0 on successful completion.
2747 '''
2747 '''
2748
2748
2749 revs = list(revs)
2749 revs = list(revs)
2750 revs.extend(opts['rev'])
2750 revs.extend(opts['rev'])
2751
2751
2752 if not opts.get('user') and opts.get('currentuser'):
2752 if not opts.get('user') and opts.get('currentuser'):
2753 opts['user'] = ui.username()
2753 opts['user'] = ui.username()
2754 if not opts.get('date') and opts.get('currentdate'):
2754 if not opts.get('date') and opts.get('currentdate'):
2755 opts['date'] = "%d %d" % util.makedate()
2755 opts['date'] = "%d %d" % util.makedate()
2756
2756
2757 editor = None
2757 editor = None
2758 if opts.get('edit'):
2758 if opts.get('edit'):
2759 editor = cmdutil.commitforceeditor
2759 editor = cmdutil.commitforceeditor
2760
2760
2761 cont = False
2761 cont = False
2762 if opts['continue']:
2762 if opts['continue']:
2763 cont = True
2763 cont = True
2764 if revs:
2764 if revs:
2765 raise util.Abort(_("can't specify --continue and revisions"))
2765 raise util.Abort(_("can't specify --continue and revisions"))
2766 # read in unfinished revisions
2766 # read in unfinished revisions
2767 try:
2767 try:
2768 nodes = repo.opener.read('graftstate').splitlines()
2768 nodes = repo.opener.read('graftstate').splitlines()
2769 revs = [repo[node].rev() for node in nodes]
2769 revs = [repo[node].rev() for node in nodes]
2770 except IOError, inst:
2770 except IOError, inst:
2771 if inst.errno != errno.ENOENT:
2771 if inst.errno != errno.ENOENT:
2772 raise
2772 raise
2773 raise util.Abort(_("no graft state found, can't continue"))
2773 raise util.Abort(_("no graft state found, can't continue"))
2774 else:
2774 else:
2775 cmdutil.bailifchanged(repo)
2775 cmdutil.bailifchanged(repo)
2776 if not revs:
2776 if not revs:
2777 raise util.Abort(_('no revisions specified'))
2777 raise util.Abort(_('no revisions specified'))
2778 revs = scmutil.revrange(repo, revs)
2778 revs = scmutil.revrange(repo, revs)
2779
2779
2780 # check for merges
2780 # check for merges
2781 for rev in repo.revs('%ld and merge()', revs):
2781 for rev in repo.revs('%ld and merge()', revs):
2782 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2782 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
2783 revs.remove(rev)
2783 revs.remove(rev)
2784 if not revs:
2784 if not revs:
2785 return -1
2785 return -1
2786
2786
2787 # check for ancestors of dest branch
2787 # check for ancestors of dest branch
2788 for rev in repo.revs('::. and %ld', revs):
2788 for rev in repo.revs('::. and %ld', revs):
2789 ui.warn(_('skipping ancestor revision %s\n') % rev)
2789 ui.warn(_('skipping ancestor revision %s\n') % rev)
2790 revs.remove(rev)
2790 revs.remove(rev)
2791 if not revs:
2791 if not revs:
2792 return -1
2792 return -1
2793
2793
2794 # analyze revs for earlier grafts
2794 # analyze revs for earlier grafts
2795 ids = {}
2795 ids = {}
2796 for ctx in repo.set("%ld", revs):
2796 for ctx in repo.set("%ld", revs):
2797 ids[ctx.hex()] = ctx.rev()
2797 ids[ctx.hex()] = ctx.rev()
2798 n = ctx.extra().get('source')
2798 n = ctx.extra().get('source')
2799 if n:
2799 if n:
2800 ids[n] = ctx.rev()
2800 ids[n] = ctx.rev()
2801
2801
2802 # check ancestors for earlier grafts
2802 # check ancestors for earlier grafts
2803 ui.debug('scanning for duplicate grafts\n')
2803 ui.debug('scanning for duplicate grafts\n')
2804 for ctx in repo.set("::. - ::%ld", revs):
2804 for ctx in repo.set("::. - ::%ld", revs):
2805 n = ctx.extra().get('source')
2805 n = ctx.extra().get('source')
2806 if n in ids:
2806 if n in ids:
2807 r = repo[n].rev()
2807 r = repo[n].rev()
2808 if r in revs:
2808 if r in revs:
2809 ui.warn(_('skipping already grafted revision %s\n') % r)
2809 ui.warn(_('skipping already grafted revision %s\n') % r)
2810 revs.remove(r)
2810 revs.remove(r)
2811 elif ids[n] in revs:
2811 elif ids[n] in revs:
2812 ui.warn(_('skipping already grafted revision %s '
2812 ui.warn(_('skipping already grafted revision %s '
2813 '(same origin %d)\n') % (ids[n], r))
2813 '(same origin %d)\n') % (ids[n], r))
2814 revs.remove(ids[n])
2814 revs.remove(ids[n])
2815 elif ctx.hex() in ids:
2815 elif ctx.hex() in ids:
2816 r = ids[ctx.hex()]
2816 r = ids[ctx.hex()]
2817 ui.warn(_('skipping already grafted revision %s '
2817 ui.warn(_('skipping already grafted revision %s '
2818 '(was grafted from %d)\n') % (r, ctx.rev()))
2818 '(was grafted from %d)\n') % (r, ctx.rev()))
2819 revs.remove(r)
2819 revs.remove(r)
2820 if not revs:
2820 if not revs:
2821 return -1
2821 return -1
2822
2822
2823 wlock = repo.wlock()
2823 wlock = repo.wlock()
2824 try:
2824 try:
2825 for pos, ctx in enumerate(repo.set("%ld", revs)):
2825 for pos, ctx in enumerate(repo.set("%ld", revs)):
2826 current = repo['.']
2826 current = repo['.']
2827
2827
2828 ui.status(_('grafting revision %s\n') % ctx.rev())
2828 ui.status(_('grafting revision %s\n') % ctx.rev())
2829 if opts.get('dry_run'):
2829 if opts.get('dry_run'):
2830 continue
2830 continue
2831
2831
2832 # we don't merge the first commit when continuing
2832 # we don't merge the first commit when continuing
2833 if not cont:
2833 if not cont:
2834 # perform the graft merge with p1(rev) as 'ancestor'
2834 # perform the graft merge with p1(rev) as 'ancestor'
2835 try:
2835 try:
2836 # ui.forcemerge is an internal variable, do not document
2836 # ui.forcemerge is an internal variable, do not document
2837 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2837 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2838 stats = mergemod.update(repo, ctx.node(), True, True, False,
2838 stats = mergemod.update(repo, ctx.node(), True, True, False,
2839 ctx.p1().node())
2839 ctx.p1().node())
2840 finally:
2840 finally:
2841 repo.ui.setconfig('ui', 'forcemerge', '')
2841 repo.ui.setconfig('ui', 'forcemerge', '')
2842 # report any conflicts
2842 # report any conflicts
2843 if stats and stats[3] > 0:
2843 if stats and stats[3] > 0:
2844 # write out state for --continue
2844 # write out state for --continue
2845 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2845 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
2846 repo.opener.write('graftstate', ''.join(nodelines))
2846 repo.opener.write('graftstate', ''.join(nodelines))
2847 raise util.Abort(
2847 raise util.Abort(
2848 _("unresolved conflicts, can't continue"),
2848 _("unresolved conflicts, can't continue"),
2849 hint=_('use hg resolve and hg graft --continue'))
2849 hint=_('use hg resolve and hg graft --continue'))
2850 else:
2850 else:
2851 cont = False
2851 cont = False
2852
2852
2853 # drop the second merge parent
2853 # drop the second merge parent
2854 repo.setparents(current.node(), nullid)
2854 repo.setparents(current.node(), nullid)
2855 repo.dirstate.write()
2855 repo.dirstate.write()
2856 # fix up dirstate for copies and renames
2856 # fix up dirstate for copies and renames
2857 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
2857 cmdutil.duplicatecopies(repo, ctx.rev(), ctx.p1().rev())
2858
2858
2859 # commit
2859 # commit
2860 source = ctx.extra().get('source')
2860 source = ctx.extra().get('source')
2861 if not source:
2861 if not source:
2862 source = ctx.hex()
2862 source = ctx.hex()
2863 extra = {'source': source}
2863 extra = {'source': source}
2864 user = ctx.user()
2864 user = ctx.user()
2865 if opts.get('user'):
2865 if opts.get('user'):
2866 user = opts['user']
2866 user = opts['user']
2867 date = ctx.date()
2867 date = ctx.date()
2868 if opts.get('date'):
2868 if opts.get('date'):
2869 date = opts['date']
2869 date = opts['date']
2870 message = ctx.description()
2870 message = ctx.description()
2871 if opts.get('log'):
2871 if opts.get('log'):
2872 message += '\n(grafted from %s)' % ctx.hex()
2872 message += '\n(grafted from %s)' % ctx.hex()
2873 node = repo.commit(text=message, user=user,
2873 node = repo.commit(text=message, user=user,
2874 date=date, extra=extra, editor=editor)
2874 date=date, extra=extra, editor=editor)
2875 if node is None:
2875 if node is None:
2876 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
2876 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
2877 finally:
2877 finally:
2878 wlock.release()
2878 wlock.release()
2879
2879
2880 # remove state when we complete successfully
2880 # remove state when we complete successfully
2881 if not opts.get('dry_run') and os.path.exists(repo.join('graftstate')):
2881 if not opts.get('dry_run') and os.path.exists(repo.join('graftstate')):
2882 util.unlinkpath(repo.join('graftstate'))
2882 util.unlinkpath(repo.join('graftstate'))
2883
2883
2884 return 0
2884 return 0
2885
2885
2886 @command('grep',
2886 @command('grep',
2887 [('0', 'print0', None, _('end fields with NUL')),
2887 [('0', 'print0', None, _('end fields with NUL')),
2888 ('', 'all', None, _('print all revisions that match')),
2888 ('', 'all', None, _('print all revisions that match')),
2889 ('a', 'text', None, _('treat all files as text')),
2889 ('a', 'text', None, _('treat all files as text')),
2890 ('f', 'follow', None,
2890 ('f', 'follow', None,
2891 _('follow changeset history,'
2891 _('follow changeset history,'
2892 ' or file history across copies and renames')),
2892 ' or file history across copies and renames')),
2893 ('i', 'ignore-case', None, _('ignore case when matching')),
2893 ('i', 'ignore-case', None, _('ignore case when matching')),
2894 ('l', 'files-with-matches', None,
2894 ('l', 'files-with-matches', None,
2895 _('print only filenames and revisions that match')),
2895 _('print only filenames and revisions that match')),
2896 ('n', 'line-number', None, _('print matching line numbers')),
2896 ('n', 'line-number', None, _('print matching line numbers')),
2897 ('r', 'rev', [],
2897 ('r', 'rev', [],
2898 _('only search files changed within revision range'), _('REV')),
2898 _('only search files changed within revision range'), _('REV')),
2899 ('u', 'user', None, _('list the author (long with -v)')),
2899 ('u', 'user', None, _('list the author (long with -v)')),
2900 ('d', 'date', None, _('list the date (short with -q)')),
2900 ('d', 'date', None, _('list the date (short with -q)')),
2901 ] + walkopts,
2901 ] + walkopts,
2902 _('[OPTION]... PATTERN [FILE]...'))
2902 _('[OPTION]... PATTERN [FILE]...'))
2903 def grep(ui, repo, pattern, *pats, **opts):
2903 def grep(ui, repo, pattern, *pats, **opts):
2904 """search for a pattern in specified files and revisions
2904 """search for a pattern in specified files and revisions
2905
2905
2906 Search revisions of files for a regular expression.
2906 Search revisions of files for a regular expression.
2907
2907
2908 This command behaves differently than Unix grep. It only accepts
2908 This command behaves differently than Unix grep. It only accepts
2909 Python/Perl regexps. It searches repository history, not the
2909 Python/Perl regexps. It searches repository history, not the
2910 working directory. It always prints the revision number in which a
2910 working directory. It always prints the revision number in which a
2911 match appears.
2911 match appears.
2912
2912
2913 By default, grep only prints output for the first revision of a
2913 By default, grep only prints output for the first revision of a
2914 file in which it finds a match. To get it to print every revision
2914 file in which it finds a match. To get it to print every revision
2915 that contains a change in match status ("-" for a match that
2915 that contains a change in match status ("-" for a match that
2916 becomes a non-match, or "+" for a non-match that becomes a match),
2916 becomes a non-match, or "+" for a non-match that becomes a match),
2917 use the --all flag.
2917 use the --all flag.
2918
2918
2919 Returns 0 if a match is found, 1 otherwise.
2919 Returns 0 if a match is found, 1 otherwise.
2920 """
2920 """
2921 reflags = re.M
2921 reflags = re.M
2922 if opts.get('ignore_case'):
2922 if opts.get('ignore_case'):
2923 reflags |= re.I
2923 reflags |= re.I
2924 try:
2924 try:
2925 regexp = re.compile(pattern, reflags)
2925 regexp = re.compile(pattern, reflags)
2926 except re.error, inst:
2926 except re.error, inst:
2927 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2927 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2928 return 1
2928 return 1
2929 sep, eol = ':', '\n'
2929 sep, eol = ':', '\n'
2930 if opts.get('print0'):
2930 if opts.get('print0'):
2931 sep = eol = '\0'
2931 sep = eol = '\0'
2932
2932
2933 getfile = util.lrucachefunc(repo.file)
2933 getfile = util.lrucachefunc(repo.file)
2934
2934
2935 def matchlines(body):
2935 def matchlines(body):
2936 begin = 0
2936 begin = 0
2937 linenum = 0
2937 linenum = 0
2938 while True:
2938 while True:
2939 match = regexp.search(body, begin)
2939 match = regexp.search(body, begin)
2940 if not match:
2940 if not match:
2941 break
2941 break
2942 mstart, mend = match.span()
2942 mstart, mend = match.span()
2943 linenum += body.count('\n', begin, mstart) + 1
2943 linenum += body.count('\n', begin, mstart) + 1
2944 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2944 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2945 begin = body.find('\n', mend) + 1 or len(body) + 1
2945 begin = body.find('\n', mend) + 1 or len(body) + 1
2946 lend = begin - 1
2946 lend = begin - 1
2947 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2947 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2948
2948
2949 class linestate(object):
2949 class linestate(object):
2950 def __init__(self, line, linenum, colstart, colend):
2950 def __init__(self, line, linenum, colstart, colend):
2951 self.line = line
2951 self.line = line
2952 self.linenum = linenum
2952 self.linenum = linenum
2953 self.colstart = colstart
2953 self.colstart = colstart
2954 self.colend = colend
2954 self.colend = colend
2955
2955
2956 def __hash__(self):
2956 def __hash__(self):
2957 return hash((self.linenum, self.line))
2957 return hash((self.linenum, self.line))
2958
2958
2959 def __eq__(self, other):
2959 def __eq__(self, other):
2960 return self.line == other.line
2960 return self.line == other.line
2961
2961
2962 matches = {}
2962 matches = {}
2963 copies = {}
2963 copies = {}
2964 def grepbody(fn, rev, body):
2964 def grepbody(fn, rev, body):
2965 matches[rev].setdefault(fn, [])
2965 matches[rev].setdefault(fn, [])
2966 m = matches[rev][fn]
2966 m = matches[rev][fn]
2967 for lnum, cstart, cend, line in matchlines(body):
2967 for lnum, cstart, cend, line in matchlines(body):
2968 s = linestate(line, lnum, cstart, cend)
2968 s = linestate(line, lnum, cstart, cend)
2969 m.append(s)
2969 m.append(s)
2970
2970
2971 def difflinestates(a, b):
2971 def difflinestates(a, b):
2972 sm = difflib.SequenceMatcher(None, a, b)
2972 sm = difflib.SequenceMatcher(None, a, b)
2973 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2973 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2974 if tag == 'insert':
2974 if tag == 'insert':
2975 for i in xrange(blo, bhi):
2975 for i in xrange(blo, bhi):
2976 yield ('+', b[i])
2976 yield ('+', b[i])
2977 elif tag == 'delete':
2977 elif tag == 'delete':
2978 for i in xrange(alo, ahi):
2978 for i in xrange(alo, ahi):
2979 yield ('-', a[i])
2979 yield ('-', a[i])
2980 elif tag == 'replace':
2980 elif tag == 'replace':
2981 for i in xrange(alo, ahi):
2981 for i in xrange(alo, ahi):
2982 yield ('-', a[i])
2982 yield ('-', a[i])
2983 for i in xrange(blo, bhi):
2983 for i in xrange(blo, bhi):
2984 yield ('+', b[i])
2984 yield ('+', b[i])
2985
2985
2986 def display(fn, ctx, pstates, states):
2986 def display(fn, ctx, pstates, states):
2987 rev = ctx.rev()
2987 rev = ctx.rev()
2988 datefunc = ui.quiet and util.shortdate or util.datestr
2988 datefunc = ui.quiet and util.shortdate or util.datestr
2989 found = False
2989 found = False
2990 filerevmatches = {}
2990 filerevmatches = {}
2991 def binary():
2991 def binary():
2992 flog = getfile(fn)
2992 flog = getfile(fn)
2993 return util.binary(flog.read(ctx.filenode(fn)))
2993 return util.binary(flog.read(ctx.filenode(fn)))
2994
2994
2995 if opts.get('all'):
2995 if opts.get('all'):
2996 iter = difflinestates(pstates, states)
2996 iter = difflinestates(pstates, states)
2997 else:
2997 else:
2998 iter = [('', l) for l in states]
2998 iter = [('', l) for l in states]
2999 for change, l in iter:
2999 for change, l in iter:
3000 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3000 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3001 before, match, after = None, None, None
3001 before, match, after = None, None, None
3002
3002
3003 if opts.get('line_number'):
3003 if opts.get('line_number'):
3004 cols.append((str(l.linenum), 'grep.linenumber'))
3004 cols.append((str(l.linenum), 'grep.linenumber'))
3005 if opts.get('all'):
3005 if opts.get('all'):
3006 cols.append((change, 'grep.change'))
3006 cols.append((change, 'grep.change'))
3007 if opts.get('user'):
3007 if opts.get('user'):
3008 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3008 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3009 if opts.get('date'):
3009 if opts.get('date'):
3010 cols.append((datefunc(ctx.date()), 'grep.date'))
3010 cols.append((datefunc(ctx.date()), 'grep.date'))
3011 if opts.get('files_with_matches'):
3011 if opts.get('files_with_matches'):
3012 c = (fn, rev)
3012 c = (fn, rev)
3013 if c in filerevmatches:
3013 if c in filerevmatches:
3014 continue
3014 continue
3015 filerevmatches[c] = 1
3015 filerevmatches[c] = 1
3016 else:
3016 else:
3017 before = l.line[:l.colstart]
3017 before = l.line[:l.colstart]
3018 match = l.line[l.colstart:l.colend]
3018 match = l.line[l.colstart:l.colend]
3019 after = l.line[l.colend:]
3019 after = l.line[l.colend:]
3020 for col, label in cols[:-1]:
3020 for col, label in cols[:-1]:
3021 ui.write(col, label=label)
3021 ui.write(col, label=label)
3022 ui.write(sep, label='grep.sep')
3022 ui.write(sep, label='grep.sep')
3023 ui.write(cols[-1][0], label=cols[-1][1])
3023 ui.write(cols[-1][0], label=cols[-1][1])
3024 if before is not None:
3024 if before is not None:
3025 ui.write(sep, label='grep.sep')
3025 ui.write(sep, label='grep.sep')
3026 if not opts.get('text') and binary():
3026 if not opts.get('text') and binary():
3027 ui.write(" Binary file matches")
3027 ui.write(" Binary file matches")
3028 else:
3028 else:
3029 ui.write(before)
3029 ui.write(before)
3030 ui.write(match, label='grep.match')
3030 ui.write(match, label='grep.match')
3031 ui.write(after)
3031 ui.write(after)
3032 ui.write(eol)
3032 ui.write(eol)
3033 found = True
3033 found = True
3034 return found
3034 return found
3035
3035
3036 skip = {}
3036 skip = {}
3037 revfiles = {}
3037 revfiles = {}
3038 matchfn = scmutil.match(repo[None], pats, opts)
3038 matchfn = scmutil.match(repo[None], pats, opts)
3039 found = False
3039 found = False
3040 follow = opts.get('follow')
3040 follow = opts.get('follow')
3041
3041
3042 def prep(ctx, fns):
3042 def prep(ctx, fns):
3043 rev = ctx.rev()
3043 rev = ctx.rev()
3044 pctx = ctx.p1()
3044 pctx = ctx.p1()
3045 parent = pctx.rev()
3045 parent = pctx.rev()
3046 matches.setdefault(rev, {})
3046 matches.setdefault(rev, {})
3047 matches.setdefault(parent, {})
3047 matches.setdefault(parent, {})
3048 files = revfiles.setdefault(rev, [])
3048 files = revfiles.setdefault(rev, [])
3049 for fn in fns:
3049 for fn in fns:
3050 flog = getfile(fn)
3050 flog = getfile(fn)
3051 try:
3051 try:
3052 fnode = ctx.filenode(fn)
3052 fnode = ctx.filenode(fn)
3053 except error.LookupError:
3053 except error.LookupError:
3054 continue
3054 continue
3055
3055
3056 copied = flog.renamed(fnode)
3056 copied = flog.renamed(fnode)
3057 copy = follow and copied and copied[0]
3057 copy = follow and copied and copied[0]
3058 if copy:
3058 if copy:
3059 copies.setdefault(rev, {})[fn] = copy
3059 copies.setdefault(rev, {})[fn] = copy
3060 if fn in skip:
3060 if fn in skip:
3061 if copy:
3061 if copy:
3062 skip[copy] = True
3062 skip[copy] = True
3063 continue
3063 continue
3064 files.append(fn)
3064 files.append(fn)
3065
3065
3066 if fn not in matches[rev]:
3066 if fn not in matches[rev]:
3067 grepbody(fn, rev, flog.read(fnode))
3067 grepbody(fn, rev, flog.read(fnode))
3068
3068
3069 pfn = copy or fn
3069 pfn = copy or fn
3070 if pfn not in matches[parent]:
3070 if pfn not in matches[parent]:
3071 try:
3071 try:
3072 fnode = pctx.filenode(pfn)
3072 fnode = pctx.filenode(pfn)
3073 grepbody(pfn, parent, flog.read(fnode))
3073 grepbody(pfn, parent, flog.read(fnode))
3074 except error.LookupError:
3074 except error.LookupError:
3075 pass
3075 pass
3076
3076
3077 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3077 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3078 rev = ctx.rev()
3078 rev = ctx.rev()
3079 parent = ctx.p1().rev()
3079 parent = ctx.p1().rev()
3080 for fn in sorted(revfiles.get(rev, [])):
3080 for fn in sorted(revfiles.get(rev, [])):
3081 states = matches[rev][fn]
3081 states = matches[rev][fn]
3082 copy = copies.get(rev, {}).get(fn)
3082 copy = copies.get(rev, {}).get(fn)
3083 if fn in skip:
3083 if fn in skip:
3084 if copy:
3084 if copy:
3085 skip[copy] = True
3085 skip[copy] = True
3086 continue
3086 continue
3087 pstates = matches.get(parent, {}).get(copy or fn, [])
3087 pstates = matches.get(parent, {}).get(copy or fn, [])
3088 if pstates or states:
3088 if pstates or states:
3089 r = display(fn, ctx, pstates, states)
3089 r = display(fn, ctx, pstates, states)
3090 found = found or r
3090 found = found or r
3091 if r and not opts.get('all'):
3091 if r and not opts.get('all'):
3092 skip[fn] = True
3092 skip[fn] = True
3093 if copy:
3093 if copy:
3094 skip[copy] = True
3094 skip[copy] = True
3095 del matches[rev]
3095 del matches[rev]
3096 del revfiles[rev]
3096 del revfiles[rev]
3097
3097
3098 return not found
3098 return not found
3099
3099
3100 @command('heads',
3100 @command('heads',
3101 [('r', 'rev', '',
3101 [('r', 'rev', '',
3102 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3102 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3103 ('t', 'topo', False, _('show topological heads only')),
3103 ('t', 'topo', False, _('show topological heads only')),
3104 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3104 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3105 ('c', 'closed', False, _('show normal and closed branch heads')),
3105 ('c', 'closed', False, _('show normal and closed branch heads')),
3106 ] + templateopts,
3106 ] + templateopts,
3107 _('[-ct] [-r STARTREV] [REV]...'))
3107 _('[-ct] [-r STARTREV] [REV]...'))
3108 def heads(ui, repo, *branchrevs, **opts):
3108 def heads(ui, repo, *branchrevs, **opts):
3109 """show current repository heads or show branch heads
3109 """show current repository heads or show branch heads
3110
3110
3111 With no arguments, show all repository branch heads.
3111 With no arguments, show all repository branch heads.
3112
3112
3113 Repository "heads" are changesets with no child changesets. They are
3113 Repository "heads" are changesets with no child changesets. They are
3114 where development generally takes place and are the usual targets
3114 where development generally takes place and are the usual targets
3115 for update and merge operations. Branch heads are changesets that have
3115 for update and merge operations. Branch heads are changesets that have
3116 no child changeset on the same branch.
3116 no child changeset on the same branch.
3117
3117
3118 If one or more REVs are given, only branch heads on the branches
3118 If one or more REVs are given, only branch heads on the branches
3119 associated with the specified changesets are shown. This means
3119 associated with the specified changesets are shown. This means
3120 that you can use :hg:`heads foo` to see the heads on a branch
3120 that you can use :hg:`heads foo` to see the heads on a branch
3121 named ``foo``.
3121 named ``foo``.
3122
3122
3123 If -c/--closed is specified, also show branch heads marked closed
3123 If -c/--closed is specified, also show branch heads marked closed
3124 (see :hg:`commit --close-branch`).
3124 (see :hg:`commit --close-branch`).
3125
3125
3126 If STARTREV is specified, only those heads that are descendants of
3126 If STARTREV is specified, only those heads that are descendants of
3127 STARTREV will be displayed.
3127 STARTREV will be displayed.
3128
3128
3129 If -t/--topo is specified, named branch mechanics will be ignored and only
3129 If -t/--topo is specified, named branch mechanics will be ignored and only
3130 changesets without children will be shown.
3130 changesets without children will be shown.
3131
3131
3132 Returns 0 if matching heads are found, 1 if not.
3132 Returns 0 if matching heads are found, 1 if not.
3133 """
3133 """
3134
3134
3135 start = None
3135 start = None
3136 if 'rev' in opts:
3136 if 'rev' in opts:
3137 start = scmutil.revsingle(repo, opts['rev'], None).node()
3137 start = scmutil.revsingle(repo, opts['rev'], None).node()
3138
3138
3139 if opts.get('topo'):
3139 if opts.get('topo'):
3140 heads = [repo[h] for h in repo.heads(start)]
3140 heads = [repo[h] for h in repo.heads(start)]
3141 else:
3141 else:
3142 heads = []
3142 heads = []
3143 for branch in repo.branchmap():
3143 for branch in repo.branchmap():
3144 heads += repo.branchheads(branch, start, opts.get('closed'))
3144 heads += repo.branchheads(branch, start, opts.get('closed'))
3145 heads = [repo[h] for h in heads]
3145 heads = [repo[h] for h in heads]
3146
3146
3147 if branchrevs:
3147 if branchrevs:
3148 branches = set(repo[br].branch() for br in branchrevs)
3148 branches = set(repo[br].branch() for br in branchrevs)
3149 heads = [h for h in heads if h.branch() in branches]
3149 heads = [h for h in heads if h.branch() in branches]
3150
3150
3151 if opts.get('active') and branchrevs:
3151 if opts.get('active') and branchrevs:
3152 dagheads = repo.heads(start)
3152 dagheads = repo.heads(start)
3153 heads = [h for h in heads if h.node() in dagheads]
3153 heads = [h for h in heads if h.node() in dagheads]
3154
3154
3155 if branchrevs:
3155 if branchrevs:
3156 haveheads = set(h.branch() for h in heads)
3156 haveheads = set(h.branch() for h in heads)
3157 if branches - haveheads:
3157 if branches - haveheads:
3158 headless = ', '.join(b for b in branches - haveheads)
3158 headless = ', '.join(b for b in branches - haveheads)
3159 msg = _('no open branch heads found on branches %s')
3159 msg = _('no open branch heads found on branches %s')
3160 if opts.get('rev'):
3160 if opts.get('rev'):
3161 msg += _(' (started at %s)') % opts['rev']
3161 msg += _(' (started at %s)') % opts['rev']
3162 ui.warn((msg + '\n') % headless)
3162 ui.warn((msg + '\n') % headless)
3163
3163
3164 if not heads:
3164 if not heads:
3165 return 1
3165 return 1
3166
3166
3167 heads = sorted(heads, key=lambda x: -x.rev())
3167 heads = sorted(heads, key=lambda x: -x.rev())
3168 displayer = cmdutil.show_changeset(ui, repo, opts)
3168 displayer = cmdutil.show_changeset(ui, repo, opts)
3169 for ctx in heads:
3169 for ctx in heads:
3170 displayer.show(ctx)
3170 displayer.show(ctx)
3171 displayer.close()
3171 displayer.close()
3172
3172
3173 @command('help',
3173 @command('help',
3174 [('e', 'extension', None, _('show only help for extensions')),
3174 [('e', 'extension', None, _('show only help for extensions')),
3175 ('c', 'command', None, _('show only help for commands')),
3175 ('c', 'command', None, _('show only help for commands')),
3176 ('k', 'keyword', '', _('show topics matching keyword')),
3176 ('k', 'keyword', '', _('show topics matching keyword')),
3177 ],
3177 ],
3178 _('[-ec] [TOPIC]'))
3178 _('[-ec] [TOPIC]'))
3179 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
3179 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
3180 """show help for a given topic or a help overview
3180 """show help for a given topic or a help overview
3181
3181
3182 With no arguments, print a list of commands with short help messages.
3182 With no arguments, print a list of commands with short help messages.
3183
3183
3184 Given a topic, extension, or command name, print help for that
3184 Given a topic, extension, or command name, print help for that
3185 topic.
3185 topic.
3186
3186
3187 Returns 0 if successful.
3187 Returns 0 if successful.
3188 """
3188 """
3189
3189
3190 textwidth = min(ui.termwidth(), 80) - 2
3190 textwidth = min(ui.termwidth(), 80) - 2
3191
3191
3192 def helpcmd(name):
3192 def helpcmd(name):
3193 try:
3193 try:
3194 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3194 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
3195 except error.AmbiguousCommand, inst:
3195 except error.AmbiguousCommand, inst:
3196 # py3k fix: except vars can't be used outside the scope of the
3196 # py3k fix: except vars can't be used outside the scope of the
3197 # except block, nor can be used inside a lambda. python issue4617
3197 # except block, nor can be used inside a lambda. python issue4617
3198 prefix = inst.args[0]
3198 prefix = inst.args[0]
3199 select = lambda c: c.lstrip('^').startswith(prefix)
3199 select = lambda c: c.lstrip('^').startswith(prefix)
3200 rst = helplist(select)
3200 rst = helplist(select)
3201 return rst
3201 return rst
3202
3202
3203 rst = []
3203 rst = []
3204
3204
3205 # check if it's an invalid alias and display its error if it is
3205 # check if it's an invalid alias and display its error if it is
3206 if getattr(entry[0], 'badalias', False):
3206 if getattr(entry[0], 'badalias', False):
3207 if not unknowncmd:
3207 if not unknowncmd:
3208 ui.pushbuffer()
3208 ui.pushbuffer()
3209 entry[0](ui)
3209 entry[0](ui)
3210 rst.append(ui.popbuffer())
3210 rst.append(ui.popbuffer())
3211 return rst
3211 return rst
3212
3212
3213 # synopsis
3213 # synopsis
3214 if len(entry) > 2:
3214 if len(entry) > 2:
3215 if entry[2].startswith('hg'):
3215 if entry[2].startswith('hg'):
3216 rst.append("%s\n" % entry[2])
3216 rst.append("%s\n" % entry[2])
3217 else:
3217 else:
3218 rst.append('hg %s %s\n' % (aliases[0], entry[2]))
3218 rst.append('hg %s %s\n' % (aliases[0], entry[2]))
3219 else:
3219 else:
3220 rst.append('hg %s\n' % aliases[0])
3220 rst.append('hg %s\n' % aliases[0])
3221 # aliases
3221 # aliases
3222 if full and not ui.quiet and len(aliases) > 1:
3222 if full and not ui.quiet and len(aliases) > 1:
3223 rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:]))
3223 rst.append(_("\naliases: %s\n") % ', '.join(aliases[1:]))
3224 rst.append('\n')
3224 rst.append('\n')
3225
3225
3226 # description
3226 # description
3227 doc = gettext(entry[0].__doc__)
3227 doc = gettext(entry[0].__doc__)
3228 if not doc:
3228 if not doc:
3229 doc = _("(no help text available)")
3229 doc = _("(no help text available)")
3230 if util.safehasattr(entry[0], 'definition'): # aliased command
3230 if util.safehasattr(entry[0], 'definition'): # aliased command
3231 if entry[0].definition.startswith('!'): # shell alias
3231 if entry[0].definition.startswith('!'): # shell alias
3232 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3232 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
3233 else:
3233 else:
3234 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3234 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
3235 doc = doc.splitlines(True)
3235 doc = doc.splitlines(True)
3236 if ui.quiet or not full:
3236 if ui.quiet or not full:
3237 rst.append(doc[0])
3237 rst.append(doc[0])
3238 else:
3238 else:
3239 rst.extend(doc)
3239 rst.extend(doc)
3240 rst.append('\n')
3240 rst.append('\n')
3241
3241
3242 # check if this command shadows a non-trivial (multi-line)
3242 # check if this command shadows a non-trivial (multi-line)
3243 # extension help text
3243 # extension help text
3244 try:
3244 try:
3245 mod = extensions.find(name)
3245 mod = extensions.find(name)
3246 doc = gettext(mod.__doc__) or ''
3246 doc = gettext(mod.__doc__) or ''
3247 if '\n' in doc.strip():
3247 if '\n' in doc.strip():
3248 msg = _('use "hg help -e %s" to show help for '
3248 msg = _('use "hg help -e %s" to show help for '
3249 'the %s extension') % (name, name)
3249 'the %s extension') % (name, name)
3250 rst.append('\n%s\n' % msg)
3250 rst.append('\n%s\n' % msg)
3251 except KeyError:
3251 except KeyError:
3252 pass
3252 pass
3253
3253
3254 # options
3254 # options
3255 if not ui.quiet and entry[1]:
3255 if not ui.quiet and entry[1]:
3256 rst.append('\n%s\n\n' % _("options:"))
3256 rst.append('\n%s\n\n' % _("options:"))
3257 rst.append(help.optrst(entry[1], ui.verbose))
3257 rst.append(help.optrst(entry[1], ui.verbose))
3258
3258
3259 if ui.verbose:
3259 if ui.verbose:
3260 rst.append('\n%s\n\n' % _("global options:"))
3260 rst.append('\n%s\n\n' % _("global options:"))
3261 rst.append(help.optrst(globalopts, ui.verbose))
3261 rst.append(help.optrst(globalopts, ui.verbose))
3262
3262
3263 if not ui.verbose:
3263 if not ui.verbose:
3264 if not full:
3264 if not full:
3265 rst.append(_('\nuse "hg help %s" to show the full help text\n')
3265 rst.append(_('\nuse "hg help %s" to show the full help text\n')
3266 % name)
3266 % name)
3267 elif not ui.quiet:
3267 elif not ui.quiet:
3268 omitted = _('use "hg -v help %s" to show more complete'
3268 omitted = _('use "hg -v help %s" to show more complete'
3269 ' help and the global options') % name
3269 ' help and the global options') % name
3270 notomitted = _('use "hg -v help %s" to show'
3270 notomitted = _('use "hg -v help %s" to show'
3271 ' the global options') % name
3271 ' the global options') % name
3272 help.indicateomitted(rst, omitted, notomitted)
3272 help.indicateomitted(rst, omitted, notomitted)
3273
3273
3274 return rst
3274 return rst
3275
3275
3276
3276
3277 def helplist(select=None):
3277 def helplist(select=None):
3278 # list of commands
3278 # list of commands
3279 if name == "shortlist":
3279 if name == "shortlist":
3280 header = _('basic commands:\n\n')
3280 header = _('basic commands:\n\n')
3281 else:
3281 else:
3282 header = _('list of commands:\n\n')
3282 header = _('list of commands:\n\n')
3283
3283
3284 h = {}
3284 h = {}
3285 cmds = {}
3285 cmds = {}
3286 for c, e in table.iteritems():
3286 for c, e in table.iteritems():
3287 f = c.split("|", 1)[0]
3287 f = c.split("|", 1)[0]
3288 if select and not select(f):
3288 if select and not select(f):
3289 continue
3289 continue
3290 if (not select and name != 'shortlist' and
3290 if (not select and name != 'shortlist' and
3291 e[0].__module__ != __name__):
3291 e[0].__module__ != __name__):
3292 continue
3292 continue
3293 if name == "shortlist" and not f.startswith("^"):
3293 if name == "shortlist" and not f.startswith("^"):
3294 continue
3294 continue
3295 f = f.lstrip("^")
3295 f = f.lstrip("^")
3296 if not ui.debugflag and f.startswith("debug"):
3296 if not ui.debugflag and f.startswith("debug"):
3297 continue
3297 continue
3298 doc = e[0].__doc__
3298 doc = e[0].__doc__
3299 if doc and 'DEPRECATED' in doc and not ui.verbose:
3299 if doc and 'DEPRECATED' in doc and not ui.verbose:
3300 continue
3300 continue
3301 doc = gettext(doc)
3301 doc = gettext(doc)
3302 if not doc:
3302 if not doc:
3303 doc = _("(no help text available)")
3303 doc = _("(no help text available)")
3304 h[f] = doc.splitlines()[0].rstrip()
3304 h[f] = doc.splitlines()[0].rstrip()
3305 cmds[f] = c.lstrip("^")
3305 cmds[f] = c.lstrip("^")
3306
3306
3307 rst = []
3307 rst = []
3308 if not h:
3308 if not h:
3309 if not ui.quiet:
3309 if not ui.quiet:
3310 rst.append(_('no commands defined\n'))
3310 rst.append(_('no commands defined\n'))
3311 return rst
3311 return rst
3312
3312
3313 if not ui.quiet:
3313 if not ui.quiet:
3314 rst.append(header)
3314 rst.append(header)
3315 fns = sorted(h)
3315 fns = sorted(h)
3316 for f in fns:
3316 for f in fns:
3317 if ui.verbose:
3317 if ui.verbose:
3318 commands = cmds[f].replace("|",", ")
3318 commands = cmds[f].replace("|",", ")
3319 rst.append(" :%s: %s\n" % (commands, h[f]))
3319 rst.append(" :%s: %s\n" % (commands, h[f]))
3320 else:
3320 else:
3321 rst.append(' :%s: %s\n' % (f, h[f]))
3321 rst.append(' :%s: %s\n' % (f, h[f]))
3322
3322
3323 if not name:
3323 if not name:
3324 exts = help.listexts(_('enabled extensions:'), extensions.enabled())
3324 exts = help.listexts(_('enabled extensions:'), extensions.enabled())
3325 if exts:
3325 if exts:
3326 rst.append('\n')
3326 rst.append('\n')
3327 rst.extend(exts)
3327 rst.extend(exts)
3328
3328
3329 rst.append(_("\nadditional help topics:\n\n"))
3329 rst.append(_("\nadditional help topics:\n\n"))
3330 topics = []
3330 topics = []
3331 for names, header, doc in help.helptable:
3331 for names, header, doc in help.helptable:
3332 topics.append((names[0], header))
3332 topics.append((names[0], header))
3333 for t, desc in topics:
3333 for t, desc in topics:
3334 rst.append(" :%s: %s\n" % (t, desc))
3334 rst.append(" :%s: %s\n" % (t, desc))
3335
3335
3336 optlist = []
3336 optlist = []
3337 if not ui.quiet:
3337 if not ui.quiet:
3338 if ui.verbose:
3338 if ui.verbose:
3339 optlist.append((_("global options:"), globalopts))
3339 optlist.append((_("global options:"), globalopts))
3340 if name == 'shortlist':
3340 if name == 'shortlist':
3341 optlist.append((_('use "hg help" for the full list '
3341 optlist.append((_('use "hg help" for the full list '
3342 'of commands'), ()))
3342 'of commands'), ()))
3343 else:
3343 else:
3344 if name == 'shortlist':
3344 if name == 'shortlist':
3345 msg = _('use "hg help" for the full list of commands '
3345 msg = _('use "hg help" for the full list of commands '
3346 'or "hg -v" for details')
3346 'or "hg -v" for details')
3347 elif name and not full:
3347 elif name and not full:
3348 msg = _('use "hg help %s" to show the full help '
3348 msg = _('use "hg help %s" to show the full help '
3349 'text') % name
3349 'text') % name
3350 else:
3350 else:
3351 msg = _('use "hg -v help%s" to show builtin aliases and '
3351 msg = _('use "hg -v help%s" to show builtin aliases and '
3352 'global options') % (name and " " + name or "")
3352 'global options') % (name and " " + name or "")
3353 optlist.append((msg, ()))
3353 optlist.append((msg, ()))
3354
3354
3355 if optlist:
3355 if optlist:
3356 for title, options in optlist:
3356 for title, options in optlist:
3357 rst.append('\n%s\n' % title)
3357 rst.append('\n%s\n' % title)
3358 if options:
3358 if options:
3359 rst.append('\n%s\n' % help.optrst(options, ui.verbose))
3359 rst.append('\n%s\n' % help.optrst(options, ui.verbose))
3360 return rst
3360 return rst
3361
3361
3362 def helptopic(name):
3362 def helptopic(name):
3363 for names, header, doc in help.helptable:
3363 for names, header, doc in help.helptable:
3364 if name in names:
3364 if name in names:
3365 break
3365 break
3366 else:
3366 else:
3367 raise error.UnknownCommand(name)
3367 raise error.UnknownCommand(name)
3368
3368
3369 rst = ["%s\n\n" % header]
3369 rst = ["%s\n\n" % header]
3370 # description
3370 # description
3371 if not doc:
3371 if not doc:
3372 rst.append(" %s\n" % _("(no help text available)"))
3372 rst.append(" %s\n" % _("(no help text available)"))
3373 if util.safehasattr(doc, '__call__'):
3373 if util.safehasattr(doc, '__call__'):
3374 rst += [" %s\n" % l for l in doc().splitlines()]
3374 rst += [" %s\n" % l for l in doc().splitlines()]
3375
3375
3376 if not ui.verbose:
3376 if not ui.verbose:
3377 omitted = (_('use "hg help -v %s" to show more complete help') %
3377 omitted = (_('use "hg help -v %s" to show more complete help') %
3378 name)
3378 name)
3379 help.indicateomitted(rst, omitted)
3379 help.indicateomitted(rst, omitted)
3380
3380
3381 try:
3381 try:
3382 cmdutil.findcmd(name, table)
3382 cmdutil.findcmd(name, table)
3383 rst.append(_('\nuse "hg help -c %s" to see help for '
3383 rst.append(_('\nuse "hg help -c %s" to see help for '
3384 'the %s command\n') % (name, name))
3384 'the %s command\n') % (name, name))
3385 except error.UnknownCommand:
3385 except error.UnknownCommand:
3386 pass
3386 pass
3387 return rst
3387 return rst
3388
3388
3389 def helpext(name):
3389 def helpext(name):
3390 try:
3390 try:
3391 mod = extensions.find(name)
3391 mod = extensions.find(name)
3392 doc = gettext(mod.__doc__) or _('no help text available')
3392 doc = gettext(mod.__doc__) or _('no help text available')
3393 except KeyError:
3393 except KeyError:
3394 mod = None
3394 mod = None
3395 doc = extensions.disabledext(name)
3395 doc = extensions.disabledext(name)
3396 if not doc:
3396 if not doc:
3397 raise error.UnknownCommand(name)
3397 raise error.UnknownCommand(name)
3398
3398
3399 if '\n' not in doc:
3399 if '\n' not in doc:
3400 head, tail = doc, ""
3400 head, tail = doc, ""
3401 else:
3401 else:
3402 head, tail = doc.split('\n', 1)
3402 head, tail = doc.split('\n', 1)
3403 rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)]
3403 rst = [_('%s extension - %s\n\n') % (name.split('.')[-1], head)]
3404 if tail:
3404 if tail:
3405 rst.extend(tail.splitlines(True))
3405 rst.extend(tail.splitlines(True))
3406 rst.append('\n')
3406 rst.append('\n')
3407
3407
3408 if not ui.verbose:
3408 if not ui.verbose:
3409 omitted = (_('use "hg help -v %s" to show more complete help') %
3409 omitted = (_('use "hg help -v %s" to show more complete help') %
3410 name)
3410 name)
3411 help.indicateomitted(rst, omitted)
3411 help.indicateomitted(rst, omitted)
3412
3412
3413 if mod:
3413 if mod:
3414 try:
3414 try:
3415 ct = mod.cmdtable
3415 ct = mod.cmdtable
3416 except AttributeError:
3416 except AttributeError:
3417 ct = {}
3417 ct = {}
3418 modcmds = set([c.split('|', 1)[0] for c in ct])
3418 modcmds = set([c.split('|', 1)[0] for c in ct])
3419 rst.extend(helplist(modcmds.__contains__))
3419 rst.extend(helplist(modcmds.__contains__))
3420 else:
3420 else:
3421 rst.append(_('use "hg help extensions" for information on enabling '
3421 rst.append(_('use "hg help extensions" for information on enabling '
3422 'extensions\n'))
3422 'extensions\n'))
3423 return rst
3423 return rst
3424
3424
3425 def helpextcmd(name):
3425 def helpextcmd(name):
3426 cmd, ext, mod = extensions.disabledcmd(ui, name,
3426 cmd, ext, mod = extensions.disabledcmd(ui, name,
3427 ui.configbool('ui', 'strict'))
3427 ui.configbool('ui', 'strict'))
3428 doc = gettext(mod.__doc__).splitlines()[0]
3428 doc = gettext(mod.__doc__).splitlines()[0]
3429
3429
3430 rst = help.listexts(_("'%s' is provided by the following "
3430 rst = help.listexts(_("'%s' is provided by the following "
3431 "extension:") % cmd, {ext: doc}, indent=4)
3431 "extension:") % cmd, {ext: doc}, indent=4)
3432 rst.append('\n')
3432 rst.append('\n')
3433 rst.append(_('use "hg help extensions" for information on enabling '
3433 rst.append(_('use "hg help extensions" for information on enabling '
3434 'extensions\n'))
3434 'extensions\n'))
3435 return rst
3435 return rst
3436
3436
3437
3437
3438 rst = []
3438 rst = []
3439 kw = opts.get('keyword')
3439 kw = opts.get('keyword')
3440 if kw:
3440 if kw:
3441 matches = help.topicmatch(kw)
3441 matches = help.topicmatch(kw)
3442 for t, title in (('topics', _('Topics')),
3442 for t, title in (('topics', _('Topics')),
3443 ('commands', _('Commands')),
3443 ('commands', _('Commands')),
3444 ('extensions', _('Extensions')),
3444 ('extensions', _('Extensions')),
3445 ('extensioncommands', _('Extension Commands'))):
3445 ('extensioncommands', _('Extension Commands'))):
3446 if matches[t]:
3446 if matches[t]:
3447 rst.append('%s:\n\n' % title)
3447 rst.append('%s:\n\n' % title)
3448 rst.extend(minirst.maketable(sorted(matches[t]), 1))
3448 rst.extend(minirst.maketable(sorted(matches[t]), 1))
3449 rst.append('\n')
3449 rst.append('\n')
3450 elif name and name != 'shortlist':
3450 elif name and name != 'shortlist':
3451 i = None
3451 i = None
3452 if unknowncmd:
3452 if unknowncmd:
3453 queries = (helpextcmd,)
3453 queries = (helpextcmd,)
3454 elif opts.get('extension'):
3454 elif opts.get('extension'):
3455 queries = (helpext,)
3455 queries = (helpext,)
3456 elif opts.get('command'):
3456 elif opts.get('command'):
3457 queries = (helpcmd,)
3457 queries = (helpcmd,)
3458 else:
3458 else:
3459 queries = (helptopic, helpcmd, helpext, helpextcmd)
3459 queries = (helptopic, helpcmd, helpext, helpextcmd)
3460 for f in queries:
3460 for f in queries:
3461 try:
3461 try:
3462 rst = f(name)
3462 rst = f(name)
3463 i = None
3463 i = None
3464 break
3464 break
3465 except error.UnknownCommand, inst:
3465 except error.UnknownCommand, inst:
3466 i = inst
3466 i = inst
3467 if i:
3467 if i:
3468 raise i
3468 raise i
3469 else:
3469 else:
3470 # program name
3470 # program name
3471 if not ui.quiet:
3471 if not ui.quiet:
3472 rst = [_("Mercurial Distributed SCM\n"), '\n']
3472 rst = [_("Mercurial Distributed SCM\n"), '\n']
3473 rst.extend(helplist())
3473 rst.extend(helplist())
3474
3474
3475 keep = ui.verbose and ['verbose'] or []
3475 keep = ui.verbose and ['verbose'] or []
3476 text = ''.join(rst)
3476 text = ''.join(rst)
3477 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3477 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3478 if 'verbose' in pruned:
3478 if 'verbose' in pruned:
3479 keep.append('omitted')
3479 keep.append('omitted')
3480 else:
3480 else:
3481 keep.append('notomitted')
3481 keep.append('notomitted')
3482 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3482 formatted, pruned = minirst.format(text, textwidth, keep=keep)
3483 ui.write(formatted)
3483 ui.write(formatted)
3484
3484
3485
3485
3486 @command('identify|id',
3486 @command('identify|id',
3487 [('r', 'rev', '',
3487 [('r', 'rev', '',
3488 _('identify the specified revision'), _('REV')),
3488 _('identify the specified revision'), _('REV')),
3489 ('n', 'num', None, _('show local revision number')),
3489 ('n', 'num', None, _('show local revision number')),
3490 ('i', 'id', None, _('show global revision id')),
3490 ('i', 'id', None, _('show global revision id')),
3491 ('b', 'branch', None, _('show branch')),
3491 ('b', 'branch', None, _('show branch')),
3492 ('t', 'tags', None, _('show tags')),
3492 ('t', 'tags', None, _('show tags')),
3493 ('B', 'bookmarks', None, _('show bookmarks')),
3493 ('B', 'bookmarks', None, _('show bookmarks')),
3494 ] + remoteopts,
3494 ] + remoteopts,
3495 _('[-nibtB] [-r REV] [SOURCE]'))
3495 _('[-nibtB] [-r REV] [SOURCE]'))
3496 def identify(ui, repo, source=None, rev=None,
3496 def identify(ui, repo, source=None, rev=None,
3497 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3497 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3498 """identify the working copy or specified revision
3498 """identify the working copy or specified revision
3499
3499
3500 Print a summary identifying the repository state at REV using one or
3500 Print a summary identifying the repository state at REV using one or
3501 two parent hash identifiers, followed by a "+" if the working
3501 two parent hash identifiers, followed by a "+" if the working
3502 directory has uncommitted changes, the branch name (if not default),
3502 directory has uncommitted changes, the branch name (if not default),
3503 a list of tags, and a list of bookmarks.
3503 a list of tags, and a list of bookmarks.
3504
3504
3505 When REV is not given, print a summary of the current state of the
3505 When REV is not given, print a summary of the current state of the
3506 repository.
3506 repository.
3507
3507
3508 Specifying a path to a repository root or Mercurial bundle will
3508 Specifying a path to a repository root or Mercurial bundle will
3509 cause lookup to operate on that repository/bundle.
3509 cause lookup to operate on that repository/bundle.
3510
3510
3511 .. container:: verbose
3511 .. container:: verbose
3512
3512
3513 Examples:
3513 Examples:
3514
3514
3515 - generate a build identifier for the working directory::
3515 - generate a build identifier for the working directory::
3516
3516
3517 hg id --id > build-id.dat
3517 hg id --id > build-id.dat
3518
3518
3519 - find the revision corresponding to a tag::
3519 - find the revision corresponding to a tag::
3520
3520
3521 hg id -n -r 1.3
3521 hg id -n -r 1.3
3522
3522
3523 - check the most recent revision of a remote repository::
3523 - check the most recent revision of a remote repository::
3524
3524
3525 hg id -r tip http://selenic.com/hg/
3525 hg id -r tip http://selenic.com/hg/
3526
3526
3527 Returns 0 if successful.
3527 Returns 0 if successful.
3528 """
3528 """
3529
3529
3530 if not repo and not source:
3530 if not repo and not source:
3531 raise util.Abort(_("there is no Mercurial repository here "
3531 raise util.Abort(_("there is no Mercurial repository here "
3532 "(.hg not found)"))
3532 "(.hg not found)"))
3533
3533
3534 hexfunc = ui.debugflag and hex or short
3534 hexfunc = ui.debugflag and hex or short
3535 default = not (num or id or branch or tags or bookmarks)
3535 default = not (num or id or branch or tags or bookmarks)
3536 output = []
3536 output = []
3537 revs = []
3537 revs = []
3538
3538
3539 if source:
3539 if source:
3540 source, branches = hg.parseurl(ui.expandpath(source))
3540 source, branches = hg.parseurl(ui.expandpath(source))
3541 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3541 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3542 repo = peer.local()
3542 repo = peer.local()
3543 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3543 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3544
3544
3545 if not repo:
3545 if not repo:
3546 if num or branch or tags:
3546 if num or branch or tags:
3547 raise util.Abort(
3547 raise util.Abort(
3548 _("can't query remote revision number, branch, or tags"))
3548 _("can't query remote revision number, branch, or tags"))
3549 if not rev and revs:
3549 if not rev and revs:
3550 rev = revs[0]
3550 rev = revs[0]
3551 if not rev:
3551 if not rev:
3552 rev = "tip"
3552 rev = "tip"
3553
3553
3554 remoterev = peer.lookup(rev)
3554 remoterev = peer.lookup(rev)
3555 if default or id:
3555 if default or id:
3556 output = [hexfunc(remoterev)]
3556 output = [hexfunc(remoterev)]
3557
3557
3558 def getbms():
3558 def getbms():
3559 bms = []
3559 bms = []
3560
3560
3561 if 'bookmarks' in peer.listkeys('namespaces'):
3561 if 'bookmarks' in peer.listkeys('namespaces'):
3562 hexremoterev = hex(remoterev)
3562 hexremoterev = hex(remoterev)
3563 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3563 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3564 if bmr == hexremoterev]
3564 if bmr == hexremoterev]
3565
3565
3566 return bms
3566 return bms
3567
3567
3568 if bookmarks:
3568 if bookmarks:
3569 output.extend(getbms())
3569 output.extend(getbms())
3570 elif default and not ui.quiet:
3570 elif default and not ui.quiet:
3571 # multiple bookmarks for a single parent separated by '/'
3571 # multiple bookmarks for a single parent separated by '/'
3572 bm = '/'.join(getbms())
3572 bm = '/'.join(getbms())
3573 if bm:
3573 if bm:
3574 output.append(bm)
3574 output.append(bm)
3575 else:
3575 else:
3576 if not rev:
3576 if not rev:
3577 ctx = repo[None]
3577 ctx = repo[None]
3578 parents = ctx.parents()
3578 parents = ctx.parents()
3579 changed = ""
3579 changed = ""
3580 if default or id or num:
3580 if default or id or num:
3581 if (util.any(repo.status())
3581 if (util.any(repo.status())
3582 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
3582 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
3583 changed = '+'
3583 changed = '+'
3584 if default or id:
3584 if default or id:
3585 output = ["%s%s" %
3585 output = ["%s%s" %
3586 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3586 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3587 if num:
3587 if num:
3588 output.append("%s%s" %
3588 output.append("%s%s" %
3589 ('+'.join([str(p.rev()) for p in parents]), changed))
3589 ('+'.join([str(p.rev()) for p in parents]), changed))
3590 else:
3590 else:
3591 ctx = scmutil.revsingle(repo, rev)
3591 ctx = scmutil.revsingle(repo, rev)
3592 if default or id:
3592 if default or id:
3593 output = [hexfunc(ctx.node())]
3593 output = [hexfunc(ctx.node())]
3594 if num:
3594 if num:
3595 output.append(str(ctx.rev()))
3595 output.append(str(ctx.rev()))
3596
3596
3597 if default and not ui.quiet:
3597 if default and not ui.quiet:
3598 b = ctx.branch()
3598 b = ctx.branch()
3599 if b != 'default':
3599 if b != 'default':
3600 output.append("(%s)" % b)
3600 output.append("(%s)" % b)
3601
3601
3602 # multiple tags for a single parent separated by '/'
3602 # multiple tags for a single parent separated by '/'
3603 t = '/'.join(ctx.tags())
3603 t = '/'.join(ctx.tags())
3604 if t:
3604 if t:
3605 output.append(t)
3605 output.append(t)
3606
3606
3607 # multiple bookmarks for a single parent separated by '/'
3607 # multiple bookmarks for a single parent separated by '/'
3608 bm = '/'.join(ctx.bookmarks())
3608 bm = '/'.join(ctx.bookmarks())
3609 if bm:
3609 if bm:
3610 output.append(bm)
3610 output.append(bm)
3611 else:
3611 else:
3612 if branch:
3612 if branch:
3613 output.append(ctx.branch())
3613 output.append(ctx.branch())
3614
3614
3615 if tags:
3615 if tags:
3616 output.extend(ctx.tags())
3616 output.extend(ctx.tags())
3617
3617
3618 if bookmarks:
3618 if bookmarks:
3619 output.extend(ctx.bookmarks())
3619 output.extend(ctx.bookmarks())
3620
3620
3621 ui.write("%s\n" % ' '.join(output))
3621 ui.write("%s\n" % ' '.join(output))
3622
3622
3623 @command('import|patch',
3623 @command('import|patch',
3624 [('p', 'strip', 1,
3624 [('p', 'strip', 1,
3625 _('directory strip option for patch. This has the same '
3625 _('directory strip option for patch. This has the same '
3626 'meaning as the corresponding patch option'), _('NUM')),
3626 'meaning as the corresponding patch option'), _('NUM')),
3627 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3627 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3628 ('e', 'edit', False, _('invoke editor on commit messages')),
3628 ('e', 'edit', False, _('invoke editor on commit messages')),
3629 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3629 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3630 ('', 'no-commit', None,
3630 ('', 'no-commit', None,
3631 _("don't commit, just update the working directory")),
3631 _("don't commit, just update the working directory")),
3632 ('', 'bypass', None,
3632 ('', 'bypass', None,
3633 _("apply patch without touching the working directory")),
3633 _("apply patch without touching the working directory")),
3634 ('', 'exact', None,
3634 ('', 'exact', None,
3635 _('apply patch to the nodes from which it was generated')),
3635 _('apply patch to the nodes from which it was generated')),
3636 ('', 'import-branch', None,
3636 ('', 'import-branch', None,
3637 _('use any branch information in patch (implied by --exact)'))] +
3637 _('use any branch information in patch (implied by --exact)'))] +
3638 commitopts + commitopts2 + similarityopts,
3638 commitopts + commitopts2 + similarityopts,
3639 _('[OPTION]... PATCH...'))
3639 _('[OPTION]... PATCH...'))
3640 def import_(ui, repo, patch1=None, *patches, **opts):
3640 def import_(ui, repo, patch1=None, *patches, **opts):
3641 """import an ordered set of patches
3641 """import an ordered set of patches
3642
3642
3643 Import a list of patches and commit them individually (unless
3643 Import a list of patches and commit them individually (unless
3644 --no-commit is specified).
3644 --no-commit is specified).
3645
3645
3646 If there are outstanding changes in the working directory, import
3646 If there are outstanding changes in the working directory, import
3647 will abort unless given the -f/--force flag.
3647 will abort unless given the -f/--force flag.
3648
3648
3649 You can import a patch straight from a mail message. Even patches
3649 You can import a patch straight from a mail message. Even patches
3650 as attachments work (to use the body part, it must have type
3650 as attachments work (to use the body part, it must have type
3651 text/plain or text/x-patch). From and Subject headers of email
3651 text/plain or text/x-patch). From and Subject headers of email
3652 message are used as default committer and commit message. All
3652 message are used as default committer and commit message. All
3653 text/plain body parts before first diff are added to commit
3653 text/plain body parts before first diff are added to commit
3654 message.
3654 message.
3655
3655
3656 If the imported patch was generated by :hg:`export`, user and
3656 If the imported patch was generated by :hg:`export`, user and
3657 description from patch override values from message headers and
3657 description from patch override values from message headers and
3658 body. Values given on command line with -m/--message and -u/--user
3658 body. Values given on command line with -m/--message and -u/--user
3659 override these.
3659 override these.
3660
3660
3661 If --exact is specified, import will set the working directory to
3661 If --exact is specified, import will set the working directory to
3662 the parent of each patch before applying it, and will abort if the
3662 the parent of each patch before applying it, and will abort if the
3663 resulting changeset has a different ID than the one recorded in
3663 resulting changeset has a different ID than the one recorded in
3664 the patch. This may happen due to character set problems or other
3664 the patch. This may happen due to character set problems or other
3665 deficiencies in the text patch format.
3665 deficiencies in the text patch format.
3666
3666
3667 Use --bypass to apply and commit patches directly to the
3667 Use --bypass to apply and commit patches directly to the
3668 repository, not touching the working directory. Without --exact,
3668 repository, not touching the working directory. Without --exact,
3669 patches will be applied on top of the working directory parent
3669 patches will be applied on top of the working directory parent
3670 revision.
3670 revision.
3671
3671
3672 With -s/--similarity, hg will attempt to discover renames and
3672 With -s/--similarity, hg will attempt to discover renames and
3673 copies in the patch in the same way as :hg:`addremove`.
3673 copies in the patch in the same way as :hg:`addremove`.
3674
3674
3675 To read a patch from standard input, use "-" as the patch name. If
3675 To read a patch from standard input, use "-" as the patch name. If
3676 a URL is specified, the patch will be downloaded from it.
3676 a URL is specified, the patch will be downloaded from it.
3677 See :hg:`help dates` for a list of formats valid for -d/--date.
3677 See :hg:`help dates` for a list of formats valid for -d/--date.
3678
3678
3679 .. container:: verbose
3679 .. container:: verbose
3680
3680
3681 Examples:
3681 Examples:
3682
3682
3683 - import a traditional patch from a website and detect renames::
3683 - import a traditional patch from a website and detect renames::
3684
3684
3685 hg import -s 80 http://example.com/bugfix.patch
3685 hg import -s 80 http://example.com/bugfix.patch
3686
3686
3687 - import a changeset from an hgweb server::
3687 - import a changeset from an hgweb server::
3688
3688
3689 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3689 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
3690
3690
3691 - import all the patches in an Unix-style mbox::
3691 - import all the patches in an Unix-style mbox::
3692
3692
3693 hg import incoming-patches.mbox
3693 hg import incoming-patches.mbox
3694
3694
3695 - attempt to exactly restore an exported changeset (not always
3695 - attempt to exactly restore an exported changeset (not always
3696 possible)::
3696 possible)::
3697
3697
3698 hg import --exact proposed-fix.patch
3698 hg import --exact proposed-fix.patch
3699
3699
3700 Returns 0 on success.
3700 Returns 0 on success.
3701 """
3701 """
3702
3702
3703 if not patch1:
3703 if not patch1:
3704 raise util.Abort(_('need at least one patch to import'))
3704 raise util.Abort(_('need at least one patch to import'))
3705
3705
3706 patches = (patch1,) + patches
3706 patches = (patch1,) + patches
3707
3707
3708 date = opts.get('date')
3708 date = opts.get('date')
3709 if date:
3709 if date:
3710 opts['date'] = util.parsedate(date)
3710 opts['date'] = util.parsedate(date)
3711
3711
3712 editor = cmdutil.commiteditor
3712 editor = cmdutil.commiteditor
3713 if opts.get('edit'):
3713 if opts.get('edit'):
3714 editor = cmdutil.commitforceeditor
3714 editor = cmdutil.commitforceeditor
3715
3715
3716 update = not opts.get('bypass')
3716 update = not opts.get('bypass')
3717 if not update and opts.get('no_commit'):
3717 if not update and opts.get('no_commit'):
3718 raise util.Abort(_('cannot use --no-commit with --bypass'))
3718 raise util.Abort(_('cannot use --no-commit with --bypass'))
3719 try:
3719 try:
3720 sim = float(opts.get('similarity') or 0)
3720 sim = float(opts.get('similarity') or 0)
3721 except ValueError:
3721 except ValueError:
3722 raise util.Abort(_('similarity must be a number'))
3722 raise util.Abort(_('similarity must be a number'))
3723 if sim < 0 or sim > 100:
3723 if sim < 0 or sim > 100:
3724 raise util.Abort(_('similarity must be between 0 and 100'))
3724 raise util.Abort(_('similarity must be between 0 and 100'))
3725 if sim and not update:
3725 if sim and not update:
3726 raise util.Abort(_('cannot use --similarity with --bypass'))
3726 raise util.Abort(_('cannot use --similarity with --bypass'))
3727
3727
3728 if (opts.get('exact') or not opts.get('force')) and update:
3728 if (opts.get('exact') or not opts.get('force')) and update:
3729 cmdutil.bailifchanged(repo)
3729 cmdutil.bailifchanged(repo)
3730
3730
3731 base = opts["base"]
3731 base = opts["base"]
3732 strip = opts["strip"]
3732 strip = opts["strip"]
3733 wlock = lock = tr = None
3733 wlock = lock = tr = None
3734 msgs = []
3734 msgs = []
3735
3735
3736 def checkexact(repo, n, nodeid):
3736 def checkexact(repo, n, nodeid):
3737 if opts.get('exact') and hex(n) != nodeid:
3737 if opts.get('exact') and hex(n) != nodeid:
3738 repo.rollback()
3738 repo.rollback()
3739 raise util.Abort(_('patch is damaged or loses information'))
3739 raise util.Abort(_('patch is damaged or loses information'))
3740
3740
3741 def tryone(ui, hunk, parents):
3741 def tryone(ui, hunk, parents):
3742 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3742 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3743 patch.extract(ui, hunk)
3743 patch.extract(ui, hunk)
3744
3744
3745 if not tmpname:
3745 if not tmpname:
3746 return (None, None)
3746 return (None, None)
3747 msg = _('applied to working directory')
3747 msg = _('applied to working directory')
3748
3748
3749 try:
3749 try:
3750 cmdline_message = cmdutil.logmessage(ui, opts)
3750 cmdline_message = cmdutil.logmessage(ui, opts)
3751 if cmdline_message:
3751 if cmdline_message:
3752 # pickup the cmdline msg
3752 # pickup the cmdline msg
3753 message = cmdline_message
3753 message = cmdline_message
3754 elif message:
3754 elif message:
3755 # pickup the patch msg
3755 # pickup the patch msg
3756 message = message.strip()
3756 message = message.strip()
3757 else:
3757 else:
3758 # launch the editor
3758 # launch the editor
3759 message = None
3759 message = None
3760 ui.debug('message:\n%s\n' % message)
3760 ui.debug('message:\n%s\n' % message)
3761
3761
3762 if len(parents) == 1:
3762 if len(parents) == 1:
3763 parents.append(repo[nullid])
3763 parents.append(repo[nullid])
3764 if opts.get('exact'):
3764 if opts.get('exact'):
3765 if not nodeid or not p1:
3765 if not nodeid or not p1:
3766 raise util.Abort(_('not a Mercurial patch'))
3766 raise util.Abort(_('not a Mercurial patch'))
3767 p1 = repo[p1]
3767 p1 = repo[p1]
3768 p2 = repo[p2 or nullid]
3768 p2 = repo[p2 or nullid]
3769 elif p2:
3769 elif p2:
3770 try:
3770 try:
3771 p1 = repo[p1]
3771 p1 = repo[p1]
3772 p2 = repo[p2]
3772 p2 = repo[p2]
3773 # Without any options, consider p2 only if the
3773 # Without any options, consider p2 only if the
3774 # patch is being applied on top of the recorded
3774 # patch is being applied on top of the recorded
3775 # first parent.
3775 # first parent.
3776 if p1 != parents[0]:
3776 if p1 != parents[0]:
3777 p1 = parents[0]
3777 p1 = parents[0]
3778 p2 = repo[nullid]
3778 p2 = repo[nullid]
3779 except error.RepoError:
3779 except error.RepoError:
3780 p1, p2 = parents
3780 p1, p2 = parents
3781 else:
3781 else:
3782 p1, p2 = parents
3782 p1, p2 = parents
3783
3783
3784 n = None
3784 n = None
3785 if update:
3785 if update:
3786 if p1 != parents[0]:
3786 if p1 != parents[0]:
3787 hg.clean(repo, p1.node())
3787 hg.clean(repo, p1.node())
3788 if p2 != parents[1]:
3788 if p2 != parents[1]:
3789 repo.setparents(p1.node(), p2.node())
3789 repo.setparents(p1.node(), p2.node())
3790
3790
3791 if opts.get('exact') or opts.get('import_branch'):
3791 if opts.get('exact') or opts.get('import_branch'):
3792 repo.dirstate.setbranch(branch or 'default')
3792 repo.dirstate.setbranch(branch or 'default')
3793
3793
3794 files = set()
3794 files = set()
3795 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3795 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3796 eolmode=None, similarity=sim / 100.0)
3796 eolmode=None, similarity=sim / 100.0)
3797 files = list(files)
3797 files = list(files)
3798 if opts.get('no_commit'):
3798 if opts.get('no_commit'):
3799 if message:
3799 if message:
3800 msgs.append(message)
3800 msgs.append(message)
3801 else:
3801 else:
3802 if opts.get('exact') or p2:
3802 if opts.get('exact') or p2:
3803 # If you got here, you either use --force and know what
3803 # If you got here, you either use --force and know what
3804 # you are doing or used --exact or a merge patch while
3804 # you are doing or used --exact or a merge patch while
3805 # being updated to its first parent.
3805 # being updated to its first parent.
3806 m = None
3806 m = None
3807 else:
3807 else:
3808 m = scmutil.matchfiles(repo, files or [])
3808 m = scmutil.matchfiles(repo, files or [])
3809 n = repo.commit(message, opts.get('user') or user,
3809 n = repo.commit(message, opts.get('user') or user,
3810 opts.get('date') or date, match=m,
3810 opts.get('date') or date, match=m,
3811 editor=editor)
3811 editor=editor)
3812 checkexact(repo, n, nodeid)
3812 checkexact(repo, n, nodeid)
3813 else:
3813 else:
3814 if opts.get('exact') or opts.get('import_branch'):
3814 if opts.get('exact') or opts.get('import_branch'):
3815 branch = branch or 'default'
3815 branch = branch or 'default'
3816 else:
3816 else:
3817 branch = p1.branch()
3817 branch = p1.branch()
3818 store = patch.filestore()
3818 store = patch.filestore()
3819 try:
3819 try:
3820 files = set()
3820 files = set()
3821 try:
3821 try:
3822 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3822 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3823 files, eolmode=None)
3823 files, eolmode=None)
3824 except patch.PatchError, e:
3824 except patch.PatchError, e:
3825 raise util.Abort(str(e))
3825 raise util.Abort(str(e))
3826 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3826 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3827 message,
3827 message,
3828 opts.get('user') or user,
3828 opts.get('user') or user,
3829 opts.get('date') or date,
3829 opts.get('date') or date,
3830 branch, files, store,
3830 branch, files, store,
3831 editor=cmdutil.commiteditor)
3831 editor=cmdutil.commiteditor)
3832 repo.savecommitmessage(memctx.description())
3832 repo.savecommitmessage(memctx.description())
3833 n = memctx.commit()
3833 n = memctx.commit()
3834 checkexact(repo, n, nodeid)
3834 checkexact(repo, n, nodeid)
3835 finally:
3835 finally:
3836 store.close()
3836 store.close()
3837 if n:
3837 if n:
3838 # i18n: refers to a short changeset id
3838 # i18n: refers to a short changeset id
3839 msg = _('created %s') % short(n)
3839 msg = _('created %s') % short(n)
3840 return (msg, n)
3840 return (msg, n)
3841 finally:
3841 finally:
3842 os.unlink(tmpname)
3842 os.unlink(tmpname)
3843
3843
3844 try:
3844 try:
3845 try:
3845 try:
3846 wlock = repo.wlock()
3846 wlock = repo.wlock()
3847 if not opts.get('no_commit'):
3847 if not opts.get('no_commit'):
3848 lock = repo.lock()
3848 lock = repo.lock()
3849 tr = repo.transaction('import')
3849 tr = repo.transaction('import')
3850 parents = repo.parents()
3850 parents = repo.parents()
3851 for patchurl in patches:
3851 for patchurl in patches:
3852 if patchurl == '-':
3852 if patchurl == '-':
3853 ui.status(_('applying patch from stdin\n'))
3853 ui.status(_('applying patch from stdin\n'))
3854 patchfile = ui.fin
3854 patchfile = ui.fin
3855 patchurl = 'stdin' # for error message
3855 patchurl = 'stdin' # for error message
3856 else:
3856 else:
3857 patchurl = os.path.join(base, patchurl)
3857 patchurl = os.path.join(base, patchurl)
3858 ui.status(_('applying %s\n') % patchurl)
3858 ui.status(_('applying %s\n') % patchurl)
3859 patchfile = url.open(ui, patchurl)
3859 patchfile = hg.openpath(ui, patchurl)
3860
3860
3861 haspatch = False
3861 haspatch = False
3862 for hunk in patch.split(patchfile):
3862 for hunk in patch.split(patchfile):
3863 (msg, node) = tryone(ui, hunk, parents)
3863 (msg, node) = tryone(ui, hunk, parents)
3864 if msg:
3864 if msg:
3865 haspatch = True
3865 haspatch = True
3866 ui.note(msg + '\n')
3866 ui.note(msg + '\n')
3867 if update or opts.get('exact'):
3867 if update or opts.get('exact'):
3868 parents = repo.parents()
3868 parents = repo.parents()
3869 else:
3869 else:
3870 parents = [repo[node]]
3870 parents = [repo[node]]
3871
3871
3872 if not haspatch:
3872 if not haspatch:
3873 raise util.Abort(_('%s: no diffs found') % patchurl)
3873 raise util.Abort(_('%s: no diffs found') % patchurl)
3874
3874
3875 if tr:
3875 if tr:
3876 tr.close()
3876 tr.close()
3877 if msgs:
3877 if msgs:
3878 repo.savecommitmessage('\n* * *\n'.join(msgs))
3878 repo.savecommitmessage('\n* * *\n'.join(msgs))
3879 except: # re-raises
3879 except: # re-raises
3880 # wlock.release() indirectly calls dirstate.write(): since
3880 # wlock.release() indirectly calls dirstate.write(): since
3881 # we're crashing, we do not want to change the working dir
3881 # we're crashing, we do not want to change the working dir
3882 # parent after all, so make sure it writes nothing
3882 # parent after all, so make sure it writes nothing
3883 repo.dirstate.invalidate()
3883 repo.dirstate.invalidate()
3884 raise
3884 raise
3885 finally:
3885 finally:
3886 if tr:
3886 if tr:
3887 tr.release()
3887 tr.release()
3888 release(lock, wlock)
3888 release(lock, wlock)
3889
3889
3890 @command('incoming|in',
3890 @command('incoming|in',
3891 [('f', 'force', None,
3891 [('f', 'force', None,
3892 _('run even if remote repository is unrelated')),
3892 _('run even if remote repository is unrelated')),
3893 ('n', 'newest-first', None, _('show newest record first')),
3893 ('n', 'newest-first', None, _('show newest record first')),
3894 ('', 'bundle', '',
3894 ('', 'bundle', '',
3895 _('file to store the bundles into'), _('FILE')),
3895 _('file to store the bundles into'), _('FILE')),
3896 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3896 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3897 ('B', 'bookmarks', False, _("compare bookmarks")),
3897 ('B', 'bookmarks', False, _("compare bookmarks")),
3898 ('b', 'branch', [],
3898 ('b', 'branch', [],
3899 _('a specific branch you would like to pull'), _('BRANCH')),
3899 _('a specific branch you would like to pull'), _('BRANCH')),
3900 ] + logopts + remoteopts + subrepoopts,
3900 ] + logopts + remoteopts + subrepoopts,
3901 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3901 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3902 def incoming(ui, repo, source="default", **opts):
3902 def incoming(ui, repo, source="default", **opts):
3903 """show new changesets found in source
3903 """show new changesets found in source
3904
3904
3905 Show new changesets found in the specified path/URL or the default
3905 Show new changesets found in the specified path/URL or the default
3906 pull location. These are the changesets that would have been pulled
3906 pull location. These are the changesets that would have been pulled
3907 if a pull at the time you issued this command.
3907 if a pull at the time you issued this command.
3908
3908
3909 For remote repository, using --bundle avoids downloading the
3909 For remote repository, using --bundle avoids downloading the
3910 changesets twice if the incoming is followed by a pull.
3910 changesets twice if the incoming is followed by a pull.
3911
3911
3912 See pull for valid source format details.
3912 See pull for valid source format details.
3913
3913
3914 Returns 0 if there are incoming changes, 1 otherwise.
3914 Returns 0 if there are incoming changes, 1 otherwise.
3915 """
3915 """
3916 if opts.get('graph'):
3916 if opts.get('graph'):
3917 cmdutil.checkunsupportedgraphflags([], opts)
3917 cmdutil.checkunsupportedgraphflags([], opts)
3918 def display(other, chlist, displayer):
3918 def display(other, chlist, displayer):
3919 revdag = cmdutil.graphrevs(other, chlist, opts)
3919 revdag = cmdutil.graphrevs(other, chlist, opts)
3920 showparents = [ctx.node() for ctx in repo[None].parents()]
3920 showparents = [ctx.node() for ctx in repo[None].parents()]
3921 cmdutil.displaygraph(ui, revdag, displayer, showparents,
3921 cmdutil.displaygraph(ui, revdag, displayer, showparents,
3922 graphmod.asciiedges)
3922 graphmod.asciiedges)
3923
3923
3924 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3924 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
3925 return 0
3925 return 0
3926
3926
3927 if opts.get('bundle') and opts.get('subrepos'):
3927 if opts.get('bundle') and opts.get('subrepos'):
3928 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3928 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3929
3929
3930 if opts.get('bookmarks'):
3930 if opts.get('bookmarks'):
3931 source, branches = hg.parseurl(ui.expandpath(source),
3931 source, branches = hg.parseurl(ui.expandpath(source),
3932 opts.get('branch'))
3932 opts.get('branch'))
3933 other = hg.peer(repo, opts, source)
3933 other = hg.peer(repo, opts, source)
3934 if 'bookmarks' not in other.listkeys('namespaces'):
3934 if 'bookmarks' not in other.listkeys('namespaces'):
3935 ui.warn(_("remote doesn't support bookmarks\n"))
3935 ui.warn(_("remote doesn't support bookmarks\n"))
3936 return 0
3936 return 0
3937 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3937 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3938 return bookmarks.diff(ui, repo, other)
3938 return bookmarks.diff(ui, repo, other)
3939
3939
3940 repo._subtoppath = ui.expandpath(source)
3940 repo._subtoppath = ui.expandpath(source)
3941 try:
3941 try:
3942 return hg.incoming(ui, repo, source, opts)
3942 return hg.incoming(ui, repo, source, opts)
3943 finally:
3943 finally:
3944 del repo._subtoppath
3944 del repo._subtoppath
3945
3945
3946
3946
3947 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3947 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3948 def init(ui, dest=".", **opts):
3948 def init(ui, dest=".", **opts):
3949 """create a new repository in the given directory
3949 """create a new repository in the given directory
3950
3950
3951 Initialize a new repository in the given directory. If the given
3951 Initialize a new repository in the given directory. If the given
3952 directory does not exist, it will be created.
3952 directory does not exist, it will be created.
3953
3953
3954 If no directory is given, the current directory is used.
3954 If no directory is given, the current directory is used.
3955
3955
3956 It is possible to specify an ``ssh://`` URL as the destination.
3956 It is possible to specify an ``ssh://`` URL as the destination.
3957 See :hg:`help urls` for more information.
3957 See :hg:`help urls` for more information.
3958
3958
3959 Returns 0 on success.
3959 Returns 0 on success.
3960 """
3960 """
3961 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3961 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3962
3962
3963 @command('locate',
3963 @command('locate',
3964 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3964 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3965 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3965 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3966 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3966 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3967 ] + walkopts,
3967 ] + walkopts,
3968 _('[OPTION]... [PATTERN]...'))
3968 _('[OPTION]... [PATTERN]...'))
3969 def locate(ui, repo, *pats, **opts):
3969 def locate(ui, repo, *pats, **opts):
3970 """locate files matching specific patterns
3970 """locate files matching specific patterns
3971
3971
3972 Print files under Mercurial control in the working directory whose
3972 Print files under Mercurial control in the working directory whose
3973 names match the given patterns.
3973 names match the given patterns.
3974
3974
3975 By default, this command searches all directories in the working
3975 By default, this command searches all directories in the working
3976 directory. To search just the current directory and its
3976 directory. To search just the current directory and its
3977 subdirectories, use "--include .".
3977 subdirectories, use "--include .".
3978
3978
3979 If no patterns are given to match, this command prints the names
3979 If no patterns are given to match, this command prints the names
3980 of all files under Mercurial control in the working directory.
3980 of all files under Mercurial control in the working directory.
3981
3981
3982 If you want to feed the output of this command into the "xargs"
3982 If you want to feed the output of this command into the "xargs"
3983 command, use the -0 option to both this command and "xargs". This
3983 command, use the -0 option to both this command and "xargs". This
3984 will avoid the problem of "xargs" treating single filenames that
3984 will avoid the problem of "xargs" treating single filenames that
3985 contain whitespace as multiple filenames.
3985 contain whitespace as multiple filenames.
3986
3986
3987 Returns 0 if a match is found, 1 otherwise.
3987 Returns 0 if a match is found, 1 otherwise.
3988 """
3988 """
3989 end = opts.get('print0') and '\0' or '\n'
3989 end = opts.get('print0') and '\0' or '\n'
3990 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3990 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3991
3991
3992 ret = 1
3992 ret = 1
3993 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3993 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3994 m.bad = lambda x, y: False
3994 m.bad = lambda x, y: False
3995 for abs in repo[rev].walk(m):
3995 for abs in repo[rev].walk(m):
3996 if not rev and abs not in repo.dirstate:
3996 if not rev and abs not in repo.dirstate:
3997 continue
3997 continue
3998 if opts.get('fullpath'):
3998 if opts.get('fullpath'):
3999 ui.write(repo.wjoin(abs), end)
3999 ui.write(repo.wjoin(abs), end)
4000 else:
4000 else:
4001 ui.write(((pats and m.rel(abs)) or abs), end)
4001 ui.write(((pats and m.rel(abs)) or abs), end)
4002 ret = 0
4002 ret = 0
4003
4003
4004 return ret
4004 return ret
4005
4005
4006 @command('^log|history',
4006 @command('^log|history',
4007 [('f', 'follow', None,
4007 [('f', 'follow', None,
4008 _('follow changeset history, or file history across copies and renames')),
4008 _('follow changeset history, or file history across copies and renames')),
4009 ('', 'follow-first', None,
4009 ('', 'follow-first', None,
4010 _('only follow the first parent of merge changesets (DEPRECATED)')),
4010 _('only follow the first parent of merge changesets (DEPRECATED)')),
4011 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4011 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4012 ('C', 'copies', None, _('show copied files')),
4012 ('C', 'copies', None, _('show copied files')),
4013 ('k', 'keyword', [],
4013 ('k', 'keyword', [],
4014 _('do case-insensitive search for a given text'), _('TEXT')),
4014 _('do case-insensitive search for a given text'), _('TEXT')),
4015 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
4015 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
4016 ('', 'removed', None, _('include revisions where files were removed')),
4016 ('', 'removed', None, _('include revisions where files were removed')),
4017 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4017 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4018 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4018 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4019 ('', 'only-branch', [],
4019 ('', 'only-branch', [],
4020 _('show only changesets within the given named branch (DEPRECATED)'),
4020 _('show only changesets within the given named branch (DEPRECATED)'),
4021 _('BRANCH')),
4021 _('BRANCH')),
4022 ('b', 'branch', [],
4022 ('b', 'branch', [],
4023 _('show changesets within the given named branch'), _('BRANCH')),
4023 _('show changesets within the given named branch'), _('BRANCH')),
4024 ('P', 'prune', [],
4024 ('P', 'prune', [],
4025 _('do not display revision or any of its ancestors'), _('REV')),
4025 _('do not display revision or any of its ancestors'), _('REV')),
4026 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
4026 ('', 'hidden', False, _('show hidden changesets (DEPRECATED)')),
4027 ] + logopts + walkopts,
4027 ] + logopts + walkopts,
4028 _('[OPTION]... [FILE]'))
4028 _('[OPTION]... [FILE]'))
4029 def log(ui, repo, *pats, **opts):
4029 def log(ui, repo, *pats, **opts):
4030 """show revision history of entire repository or files
4030 """show revision history of entire repository or files
4031
4031
4032 Print the revision history of the specified files or the entire
4032 Print the revision history of the specified files or the entire
4033 project.
4033 project.
4034
4034
4035 If no revision range is specified, the default is ``tip:0`` unless
4035 If no revision range is specified, the default is ``tip:0`` unless
4036 --follow is set, in which case the working directory parent is
4036 --follow is set, in which case the working directory parent is
4037 used as the starting revision.
4037 used as the starting revision.
4038
4038
4039 File history is shown without following rename or copy history of
4039 File history is shown without following rename or copy history of
4040 files. Use -f/--follow with a filename to follow history across
4040 files. Use -f/--follow with a filename to follow history across
4041 renames and copies. --follow without a filename will only show
4041 renames and copies. --follow without a filename will only show
4042 ancestors or descendants of the starting revision.
4042 ancestors or descendants of the starting revision.
4043
4043
4044 By default this command prints revision number and changeset id,
4044 By default this command prints revision number and changeset id,
4045 tags, non-trivial parents, user, date and time, and a summary for
4045 tags, non-trivial parents, user, date and time, and a summary for
4046 each commit. When the -v/--verbose switch is used, the list of
4046 each commit. When the -v/--verbose switch is used, the list of
4047 changed files and full commit message are shown.
4047 changed files and full commit message are shown.
4048
4048
4049 .. note::
4049 .. note::
4050 log -p/--patch may generate unexpected diff output for merge
4050 log -p/--patch may generate unexpected diff output for merge
4051 changesets, as it will only compare the merge changeset against
4051 changesets, as it will only compare the merge changeset against
4052 its first parent. Also, only files different from BOTH parents
4052 its first parent. Also, only files different from BOTH parents
4053 will appear in files:.
4053 will appear in files:.
4054
4054
4055 .. note::
4055 .. note::
4056 for performance reasons, log FILE may omit duplicate changes
4056 for performance reasons, log FILE may omit duplicate changes
4057 made on branches and will not show deletions. To see all
4057 made on branches and will not show deletions. To see all
4058 changes including duplicates and deletions, use the --removed
4058 changes including duplicates and deletions, use the --removed
4059 switch.
4059 switch.
4060
4060
4061 .. container:: verbose
4061 .. container:: verbose
4062
4062
4063 Some examples:
4063 Some examples:
4064
4064
4065 - changesets with full descriptions and file lists::
4065 - changesets with full descriptions and file lists::
4066
4066
4067 hg log -v
4067 hg log -v
4068
4068
4069 - changesets ancestral to the working directory::
4069 - changesets ancestral to the working directory::
4070
4070
4071 hg log -f
4071 hg log -f
4072
4072
4073 - last 10 commits on the current branch::
4073 - last 10 commits on the current branch::
4074
4074
4075 hg log -l 10 -b .
4075 hg log -l 10 -b .
4076
4076
4077 - changesets showing all modifications of a file, including removals::
4077 - changesets showing all modifications of a file, including removals::
4078
4078
4079 hg log --removed file.c
4079 hg log --removed file.c
4080
4080
4081 - all changesets that touch a directory, with diffs, excluding merges::
4081 - all changesets that touch a directory, with diffs, excluding merges::
4082
4082
4083 hg log -Mp lib/
4083 hg log -Mp lib/
4084
4084
4085 - all revision numbers that match a keyword::
4085 - all revision numbers that match a keyword::
4086
4086
4087 hg log -k bug --template "{rev}\\n"
4087 hg log -k bug --template "{rev}\\n"
4088
4088
4089 - check if a given changeset is included is a tagged release::
4089 - check if a given changeset is included is a tagged release::
4090
4090
4091 hg log -r "a21ccf and ancestor(1.9)"
4091 hg log -r "a21ccf and ancestor(1.9)"
4092
4092
4093 - find all changesets by some user in a date range::
4093 - find all changesets by some user in a date range::
4094
4094
4095 hg log -k alice -d "may 2008 to jul 2008"
4095 hg log -k alice -d "may 2008 to jul 2008"
4096
4096
4097 - summary of all changesets after the last tag::
4097 - summary of all changesets after the last tag::
4098
4098
4099 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4099 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4100
4100
4101 See :hg:`help dates` for a list of formats valid for -d/--date.
4101 See :hg:`help dates` for a list of formats valid for -d/--date.
4102
4102
4103 See :hg:`help revisions` and :hg:`help revsets` for more about
4103 See :hg:`help revisions` and :hg:`help revsets` for more about
4104 specifying revisions.
4104 specifying revisions.
4105
4105
4106 See :hg:`help templates` for more about pre-packaged styles and
4106 See :hg:`help templates` for more about pre-packaged styles and
4107 specifying custom templates.
4107 specifying custom templates.
4108
4108
4109 Returns 0 on success.
4109 Returns 0 on success.
4110 """
4110 """
4111 if opts.get('graph'):
4111 if opts.get('graph'):
4112 return cmdutil.graphlog(ui, repo, *pats, **opts)
4112 return cmdutil.graphlog(ui, repo, *pats, **opts)
4113
4113
4114 matchfn = scmutil.match(repo[None], pats, opts)
4114 matchfn = scmutil.match(repo[None], pats, opts)
4115 limit = cmdutil.loglimit(opts)
4115 limit = cmdutil.loglimit(opts)
4116 count = 0
4116 count = 0
4117
4117
4118 getrenamed, endrev = None, None
4118 getrenamed, endrev = None, None
4119 if opts.get('copies'):
4119 if opts.get('copies'):
4120 if opts.get('rev'):
4120 if opts.get('rev'):
4121 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
4121 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
4122 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4122 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4123
4123
4124 df = False
4124 df = False
4125 if opts.get("date"):
4125 if opts.get("date"):
4126 df = util.matchdate(opts["date"])
4126 df = util.matchdate(opts["date"])
4127
4127
4128 branches = opts.get('branch', []) + opts.get('only_branch', [])
4128 branches = opts.get('branch', []) + opts.get('only_branch', [])
4129 opts['branch'] = [repo.lookupbranch(b) for b in branches]
4129 opts['branch'] = [repo.lookupbranch(b) for b in branches]
4130
4130
4131 displayer = cmdutil.show_changeset(ui, repo, opts, True)
4131 displayer = cmdutil.show_changeset(ui, repo, opts, True)
4132 def prep(ctx, fns):
4132 def prep(ctx, fns):
4133 rev = ctx.rev()
4133 rev = ctx.rev()
4134 parents = [p for p in repo.changelog.parentrevs(rev)
4134 parents = [p for p in repo.changelog.parentrevs(rev)
4135 if p != nullrev]
4135 if p != nullrev]
4136 if opts.get('no_merges') and len(parents) == 2:
4136 if opts.get('no_merges') and len(parents) == 2:
4137 return
4137 return
4138 if opts.get('only_merges') and len(parents) != 2:
4138 if opts.get('only_merges') and len(parents) != 2:
4139 return
4139 return
4140 if opts.get('branch') and ctx.branch() not in opts['branch']:
4140 if opts.get('branch') and ctx.branch() not in opts['branch']:
4141 return
4141 return
4142 if not opts.get('hidden') and ctx.hidden():
4142 if not opts.get('hidden') and ctx.hidden():
4143 return
4143 return
4144 if df and not df(ctx.date()[0]):
4144 if df and not df(ctx.date()[0]):
4145 return
4145 return
4146
4146
4147 lower = encoding.lower
4147 lower = encoding.lower
4148 if opts.get('user'):
4148 if opts.get('user'):
4149 luser = lower(ctx.user())
4149 luser = lower(ctx.user())
4150 for k in [lower(x) for x in opts['user']]:
4150 for k in [lower(x) for x in opts['user']]:
4151 if (k in luser):
4151 if (k in luser):
4152 break
4152 break
4153 else:
4153 else:
4154 return
4154 return
4155 if opts.get('keyword'):
4155 if opts.get('keyword'):
4156 luser = lower(ctx.user())
4156 luser = lower(ctx.user())
4157 ldesc = lower(ctx.description())
4157 ldesc = lower(ctx.description())
4158 lfiles = lower(" ".join(ctx.files()))
4158 lfiles = lower(" ".join(ctx.files()))
4159 for k in [lower(x) for x in opts['keyword']]:
4159 for k in [lower(x) for x in opts['keyword']]:
4160 if (k in luser or k in ldesc or k in lfiles):
4160 if (k in luser or k in ldesc or k in lfiles):
4161 break
4161 break
4162 else:
4162 else:
4163 return
4163 return
4164
4164
4165 copies = None
4165 copies = None
4166 if getrenamed is not None and rev:
4166 if getrenamed is not None and rev:
4167 copies = []
4167 copies = []
4168 for fn in ctx.files():
4168 for fn in ctx.files():
4169 rename = getrenamed(fn, rev)
4169 rename = getrenamed(fn, rev)
4170 if rename:
4170 if rename:
4171 copies.append((fn, rename[0]))
4171 copies.append((fn, rename[0]))
4172
4172
4173 revmatchfn = None
4173 revmatchfn = None
4174 if opts.get('patch') or opts.get('stat'):
4174 if opts.get('patch') or opts.get('stat'):
4175 if opts.get('follow') or opts.get('follow_first'):
4175 if opts.get('follow') or opts.get('follow_first'):
4176 # note: this might be wrong when following through merges
4176 # note: this might be wrong when following through merges
4177 revmatchfn = scmutil.match(repo[None], fns, default='path')
4177 revmatchfn = scmutil.match(repo[None], fns, default='path')
4178 else:
4178 else:
4179 revmatchfn = matchfn
4179 revmatchfn = matchfn
4180
4180
4181 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4181 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4182
4182
4183 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4183 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
4184 if count == limit:
4184 if count == limit:
4185 break
4185 break
4186 if displayer.flush(ctx.rev()):
4186 if displayer.flush(ctx.rev()):
4187 count += 1
4187 count += 1
4188 displayer.close()
4188 displayer.close()
4189
4189
4190 @command('manifest',
4190 @command('manifest',
4191 [('r', 'rev', '', _('revision to display'), _('REV')),
4191 [('r', 'rev', '', _('revision to display'), _('REV')),
4192 ('', 'all', False, _("list files from all revisions"))],
4192 ('', 'all', False, _("list files from all revisions"))],
4193 _('[-r REV]'))
4193 _('[-r REV]'))
4194 def manifest(ui, repo, node=None, rev=None, **opts):
4194 def manifest(ui, repo, node=None, rev=None, **opts):
4195 """output the current or given revision of the project manifest
4195 """output the current or given revision of the project manifest
4196
4196
4197 Print a list of version controlled files for the given revision.
4197 Print a list of version controlled files for the given revision.
4198 If no revision is given, the first parent of the working directory
4198 If no revision is given, the first parent of the working directory
4199 is used, or the null revision if no revision is checked out.
4199 is used, or the null revision if no revision is checked out.
4200
4200
4201 With -v, print file permissions, symlink and executable bits.
4201 With -v, print file permissions, symlink and executable bits.
4202 With --debug, print file revision hashes.
4202 With --debug, print file revision hashes.
4203
4203
4204 If option --all is specified, the list of all files from all revisions
4204 If option --all is specified, the list of all files from all revisions
4205 is printed. This includes deleted and renamed files.
4205 is printed. This includes deleted and renamed files.
4206
4206
4207 Returns 0 on success.
4207 Returns 0 on success.
4208 """
4208 """
4209 if opts.get('all'):
4209 if opts.get('all'):
4210 if rev or node:
4210 if rev or node:
4211 raise util.Abort(_("can't specify a revision with --all"))
4211 raise util.Abort(_("can't specify a revision with --all"))
4212
4212
4213 res = []
4213 res = []
4214 prefix = "data/"
4214 prefix = "data/"
4215 suffix = ".i"
4215 suffix = ".i"
4216 plen = len(prefix)
4216 plen = len(prefix)
4217 slen = len(suffix)
4217 slen = len(suffix)
4218 lock = repo.lock()
4218 lock = repo.lock()
4219 try:
4219 try:
4220 for fn, b, size in repo.store.datafiles():
4220 for fn, b, size in repo.store.datafiles():
4221 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4221 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4222 res.append(fn[plen:-slen])
4222 res.append(fn[plen:-slen])
4223 finally:
4223 finally:
4224 lock.release()
4224 lock.release()
4225 for f in res:
4225 for f in res:
4226 ui.write("%s\n" % f)
4226 ui.write("%s\n" % f)
4227 return
4227 return
4228
4228
4229 if rev and node:
4229 if rev and node:
4230 raise util.Abort(_("please specify just one revision"))
4230 raise util.Abort(_("please specify just one revision"))
4231
4231
4232 if not node:
4232 if not node:
4233 node = rev
4233 node = rev
4234
4234
4235 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
4235 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
4236 ctx = scmutil.revsingle(repo, node)
4236 ctx = scmutil.revsingle(repo, node)
4237 for f in ctx:
4237 for f in ctx:
4238 if ui.debugflag:
4238 if ui.debugflag:
4239 ui.write("%40s " % hex(ctx.manifest()[f]))
4239 ui.write("%40s " % hex(ctx.manifest()[f]))
4240 if ui.verbose:
4240 if ui.verbose:
4241 ui.write(decor[ctx.flags(f)])
4241 ui.write(decor[ctx.flags(f)])
4242 ui.write("%s\n" % f)
4242 ui.write("%s\n" % f)
4243
4243
4244 @command('^merge',
4244 @command('^merge',
4245 [('f', 'force', None, _('force a merge with outstanding changes')),
4245 [('f', 'force', None, _('force a merge with outstanding changes')),
4246 ('r', 'rev', '', _('revision to merge'), _('REV')),
4246 ('r', 'rev', '', _('revision to merge'), _('REV')),
4247 ('P', 'preview', None,
4247 ('P', 'preview', None,
4248 _('review revisions to merge (no merge is performed)'))
4248 _('review revisions to merge (no merge is performed)'))
4249 ] + mergetoolopts,
4249 ] + mergetoolopts,
4250 _('[-P] [-f] [[-r] REV]'))
4250 _('[-P] [-f] [[-r] REV]'))
4251 def merge(ui, repo, node=None, **opts):
4251 def merge(ui, repo, node=None, **opts):
4252 """merge working directory with another revision
4252 """merge working directory with another revision
4253
4253
4254 The current working directory is updated with all changes made in
4254 The current working directory is updated with all changes made in
4255 the requested revision since the last common predecessor revision.
4255 the requested revision since the last common predecessor revision.
4256
4256
4257 Files that changed between either parent are marked as changed for
4257 Files that changed between either parent are marked as changed for
4258 the next commit and a commit must be performed before any further
4258 the next commit and a commit must be performed before any further
4259 updates to the repository are allowed. The next commit will have
4259 updates to the repository are allowed. The next commit will have
4260 two parents.
4260 two parents.
4261
4261
4262 ``--tool`` can be used to specify the merge tool used for file
4262 ``--tool`` can be used to specify the merge tool used for file
4263 merges. It overrides the HGMERGE environment variable and your
4263 merges. It overrides the HGMERGE environment variable and your
4264 configuration files. See :hg:`help merge-tools` for options.
4264 configuration files. See :hg:`help merge-tools` for options.
4265
4265
4266 If no revision is specified, the working directory's parent is a
4266 If no revision is specified, the working directory's parent is a
4267 head revision, and the current branch contains exactly one other
4267 head revision, and the current branch contains exactly one other
4268 head, the other head is merged with by default. Otherwise, an
4268 head, the other head is merged with by default. Otherwise, an
4269 explicit revision with which to merge with must be provided.
4269 explicit revision with which to merge with must be provided.
4270
4270
4271 :hg:`resolve` must be used to resolve unresolved files.
4271 :hg:`resolve` must be used to resolve unresolved files.
4272
4272
4273 To undo an uncommitted merge, use :hg:`update --clean .` which
4273 To undo an uncommitted merge, use :hg:`update --clean .` which
4274 will check out a clean copy of the original merge parent, losing
4274 will check out a clean copy of the original merge parent, losing
4275 all changes.
4275 all changes.
4276
4276
4277 Returns 0 on success, 1 if there are unresolved files.
4277 Returns 0 on success, 1 if there are unresolved files.
4278 """
4278 """
4279
4279
4280 if opts.get('rev') and node:
4280 if opts.get('rev') and node:
4281 raise util.Abort(_("please specify just one revision"))
4281 raise util.Abort(_("please specify just one revision"))
4282 if not node:
4282 if not node:
4283 node = opts.get('rev')
4283 node = opts.get('rev')
4284
4284
4285 if node:
4285 if node:
4286 node = scmutil.revsingle(repo, node).node()
4286 node = scmutil.revsingle(repo, node).node()
4287
4287
4288 if not node and repo._bookmarkcurrent:
4288 if not node and repo._bookmarkcurrent:
4289 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4289 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4290 curhead = repo[repo._bookmarkcurrent]
4290 curhead = repo[repo._bookmarkcurrent]
4291 if len(bmheads) == 2:
4291 if len(bmheads) == 2:
4292 if curhead == bmheads[0]:
4292 if curhead == bmheads[0]:
4293 node = bmheads[1]
4293 node = bmheads[1]
4294 else:
4294 else:
4295 node = bmheads[0]
4295 node = bmheads[0]
4296 elif len(bmheads) > 2:
4296 elif len(bmheads) > 2:
4297 raise util.Abort(_("multiple matching bookmarks to merge - "
4297 raise util.Abort(_("multiple matching bookmarks to merge - "
4298 "please merge with an explicit rev or bookmark"),
4298 "please merge with an explicit rev or bookmark"),
4299 hint=_("run 'hg heads' to see all heads"))
4299 hint=_("run 'hg heads' to see all heads"))
4300 elif len(bmheads) <= 1:
4300 elif len(bmheads) <= 1:
4301 raise util.Abort(_("no matching bookmark to merge - "
4301 raise util.Abort(_("no matching bookmark to merge - "
4302 "please merge with an explicit rev or bookmark"),
4302 "please merge with an explicit rev or bookmark"),
4303 hint=_("run 'hg heads' to see all heads"))
4303 hint=_("run 'hg heads' to see all heads"))
4304
4304
4305 if not node and not repo._bookmarkcurrent:
4305 if not node and not repo._bookmarkcurrent:
4306 branch = repo[None].branch()
4306 branch = repo[None].branch()
4307 bheads = repo.branchheads(branch)
4307 bheads = repo.branchheads(branch)
4308 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4308 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4309
4309
4310 if len(nbhs) > 2:
4310 if len(nbhs) > 2:
4311 raise util.Abort(_("branch '%s' has %d heads - "
4311 raise util.Abort(_("branch '%s' has %d heads - "
4312 "please merge with an explicit rev")
4312 "please merge with an explicit rev")
4313 % (branch, len(bheads)),
4313 % (branch, len(bheads)),
4314 hint=_("run 'hg heads .' to see heads"))
4314 hint=_("run 'hg heads .' to see heads"))
4315
4315
4316 parent = repo.dirstate.p1()
4316 parent = repo.dirstate.p1()
4317 if len(nbhs) <= 1:
4317 if len(nbhs) <= 1:
4318 if len(bheads) > 1:
4318 if len(bheads) > 1:
4319 raise util.Abort(_("heads are bookmarked - "
4319 raise util.Abort(_("heads are bookmarked - "
4320 "please merge with an explicit rev"),
4320 "please merge with an explicit rev"),
4321 hint=_("run 'hg heads' to see all heads"))
4321 hint=_("run 'hg heads' to see all heads"))
4322 if len(repo.heads()) > 1:
4322 if len(repo.heads()) > 1:
4323 raise util.Abort(_("branch '%s' has one head - "
4323 raise util.Abort(_("branch '%s' has one head - "
4324 "please merge with an explicit rev")
4324 "please merge with an explicit rev")
4325 % branch,
4325 % branch,
4326 hint=_("run 'hg heads' to see all heads"))
4326 hint=_("run 'hg heads' to see all heads"))
4327 msg, hint = _('nothing to merge'), None
4327 msg, hint = _('nothing to merge'), None
4328 if parent != repo.lookup(branch):
4328 if parent != repo.lookup(branch):
4329 hint = _("use 'hg update' instead")
4329 hint = _("use 'hg update' instead")
4330 raise util.Abort(msg, hint=hint)
4330 raise util.Abort(msg, hint=hint)
4331
4331
4332 if parent not in bheads:
4332 if parent not in bheads:
4333 raise util.Abort(_('working directory not at a head revision'),
4333 raise util.Abort(_('working directory not at a head revision'),
4334 hint=_("use 'hg update' or merge with an "
4334 hint=_("use 'hg update' or merge with an "
4335 "explicit revision"))
4335 "explicit revision"))
4336 if parent == nbhs[0]:
4336 if parent == nbhs[0]:
4337 node = nbhs[-1]
4337 node = nbhs[-1]
4338 else:
4338 else:
4339 node = nbhs[0]
4339 node = nbhs[0]
4340
4340
4341 if opts.get('preview'):
4341 if opts.get('preview'):
4342 # find nodes that are ancestors of p2 but not of p1
4342 # find nodes that are ancestors of p2 but not of p1
4343 p1 = repo.lookup('.')
4343 p1 = repo.lookup('.')
4344 p2 = repo.lookup(node)
4344 p2 = repo.lookup(node)
4345 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4345 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4346
4346
4347 displayer = cmdutil.show_changeset(ui, repo, opts)
4347 displayer = cmdutil.show_changeset(ui, repo, opts)
4348 for node in nodes:
4348 for node in nodes:
4349 displayer.show(repo[node])
4349 displayer.show(repo[node])
4350 displayer.close()
4350 displayer.close()
4351 return 0
4351 return 0
4352
4352
4353 try:
4353 try:
4354 # ui.forcemerge is an internal variable, do not document
4354 # ui.forcemerge is an internal variable, do not document
4355 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4355 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4356 return hg.merge(repo, node, force=opts.get('force'))
4356 return hg.merge(repo, node, force=opts.get('force'))
4357 finally:
4357 finally:
4358 ui.setconfig('ui', 'forcemerge', '')
4358 ui.setconfig('ui', 'forcemerge', '')
4359
4359
4360 @command('outgoing|out',
4360 @command('outgoing|out',
4361 [('f', 'force', None, _('run even when the destination is unrelated')),
4361 [('f', 'force', None, _('run even when the destination is unrelated')),
4362 ('r', 'rev', [],
4362 ('r', 'rev', [],
4363 _('a changeset intended to be included in the destination'), _('REV')),
4363 _('a changeset intended to be included in the destination'), _('REV')),
4364 ('n', 'newest-first', None, _('show newest record first')),
4364 ('n', 'newest-first', None, _('show newest record first')),
4365 ('B', 'bookmarks', False, _('compare bookmarks')),
4365 ('B', 'bookmarks', False, _('compare bookmarks')),
4366 ('b', 'branch', [], _('a specific branch you would like to push'),
4366 ('b', 'branch', [], _('a specific branch you would like to push'),
4367 _('BRANCH')),
4367 _('BRANCH')),
4368 ] + logopts + remoteopts + subrepoopts,
4368 ] + logopts + remoteopts + subrepoopts,
4369 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4369 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4370 def outgoing(ui, repo, dest=None, **opts):
4370 def outgoing(ui, repo, dest=None, **opts):
4371 """show changesets not found in the destination
4371 """show changesets not found in the destination
4372
4372
4373 Show changesets not found in the specified destination repository
4373 Show changesets not found in the specified destination repository
4374 or the default push location. These are the changesets that would
4374 or the default push location. These are the changesets that would
4375 be pushed if a push was requested.
4375 be pushed if a push was requested.
4376
4376
4377 See pull for details of valid destination formats.
4377 See pull for details of valid destination formats.
4378
4378
4379 Returns 0 if there are outgoing changes, 1 otherwise.
4379 Returns 0 if there are outgoing changes, 1 otherwise.
4380 """
4380 """
4381 if opts.get('graph'):
4381 if opts.get('graph'):
4382 cmdutil.checkunsupportedgraphflags([], opts)
4382 cmdutil.checkunsupportedgraphflags([], opts)
4383 o = hg._outgoing(ui, repo, dest, opts)
4383 o = hg._outgoing(ui, repo, dest, opts)
4384 if o is None:
4384 if o is None:
4385 return
4385 return
4386
4386
4387 revdag = cmdutil.graphrevs(repo, o, opts)
4387 revdag = cmdutil.graphrevs(repo, o, opts)
4388 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4388 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4389 showparents = [ctx.node() for ctx in repo[None].parents()]
4389 showparents = [ctx.node() for ctx in repo[None].parents()]
4390 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4390 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4391 graphmod.asciiedges)
4391 graphmod.asciiedges)
4392 return 0
4392 return 0
4393
4393
4394 if opts.get('bookmarks'):
4394 if opts.get('bookmarks'):
4395 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4395 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4396 dest, branches = hg.parseurl(dest, opts.get('branch'))
4396 dest, branches = hg.parseurl(dest, opts.get('branch'))
4397 other = hg.peer(repo, opts, dest)
4397 other = hg.peer(repo, opts, dest)
4398 if 'bookmarks' not in other.listkeys('namespaces'):
4398 if 'bookmarks' not in other.listkeys('namespaces'):
4399 ui.warn(_("remote doesn't support bookmarks\n"))
4399 ui.warn(_("remote doesn't support bookmarks\n"))
4400 return 0
4400 return 0
4401 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4401 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4402 return bookmarks.diff(ui, other, repo)
4402 return bookmarks.diff(ui, other, repo)
4403
4403
4404 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4404 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4405 try:
4405 try:
4406 return hg.outgoing(ui, repo, dest, opts)
4406 return hg.outgoing(ui, repo, dest, opts)
4407 finally:
4407 finally:
4408 del repo._subtoppath
4408 del repo._subtoppath
4409
4409
4410 @command('parents',
4410 @command('parents',
4411 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4411 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4412 ] + templateopts,
4412 ] + templateopts,
4413 _('[-r REV] [FILE]'))
4413 _('[-r REV] [FILE]'))
4414 def parents(ui, repo, file_=None, **opts):
4414 def parents(ui, repo, file_=None, **opts):
4415 """show the parents of the working directory or revision
4415 """show the parents of the working directory or revision
4416
4416
4417 Print the working directory's parent revisions. If a revision is
4417 Print the working directory's parent revisions. If a revision is
4418 given via -r/--rev, the parent of that revision will be printed.
4418 given via -r/--rev, the parent of that revision will be printed.
4419 If a file argument is given, the revision in which the file was
4419 If a file argument is given, the revision in which the file was
4420 last changed (before the working directory revision or the
4420 last changed (before the working directory revision or the
4421 argument to --rev if given) is printed.
4421 argument to --rev if given) is printed.
4422
4422
4423 Returns 0 on success.
4423 Returns 0 on success.
4424 """
4424 """
4425
4425
4426 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4426 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4427
4427
4428 if file_:
4428 if file_:
4429 m = scmutil.match(ctx, (file_,), opts)
4429 m = scmutil.match(ctx, (file_,), opts)
4430 if m.anypats() or len(m.files()) != 1:
4430 if m.anypats() or len(m.files()) != 1:
4431 raise util.Abort(_('can only specify an explicit filename'))
4431 raise util.Abort(_('can only specify an explicit filename'))
4432 file_ = m.files()[0]
4432 file_ = m.files()[0]
4433 filenodes = []
4433 filenodes = []
4434 for cp in ctx.parents():
4434 for cp in ctx.parents():
4435 if not cp:
4435 if not cp:
4436 continue
4436 continue
4437 try:
4437 try:
4438 filenodes.append(cp.filenode(file_))
4438 filenodes.append(cp.filenode(file_))
4439 except error.LookupError:
4439 except error.LookupError:
4440 pass
4440 pass
4441 if not filenodes:
4441 if not filenodes:
4442 raise util.Abort(_("'%s' not found in manifest!") % file_)
4442 raise util.Abort(_("'%s' not found in manifest!") % file_)
4443 fl = repo.file(file_)
4443 fl = repo.file(file_)
4444 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4444 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
4445 else:
4445 else:
4446 p = [cp.node() for cp in ctx.parents()]
4446 p = [cp.node() for cp in ctx.parents()]
4447
4447
4448 displayer = cmdutil.show_changeset(ui, repo, opts)
4448 displayer = cmdutil.show_changeset(ui, repo, opts)
4449 for n in p:
4449 for n in p:
4450 if n != nullid:
4450 if n != nullid:
4451 displayer.show(repo[n])
4451 displayer.show(repo[n])
4452 displayer.close()
4452 displayer.close()
4453
4453
4454 @command('paths', [], _('[NAME]'))
4454 @command('paths', [], _('[NAME]'))
4455 def paths(ui, repo, search=None):
4455 def paths(ui, repo, search=None):
4456 """show aliases for remote repositories
4456 """show aliases for remote repositories
4457
4457
4458 Show definition of symbolic path name NAME. If no name is given,
4458 Show definition of symbolic path name NAME. If no name is given,
4459 show definition of all available names.
4459 show definition of all available names.
4460
4460
4461 Option -q/--quiet suppresses all output when searching for NAME
4461 Option -q/--quiet suppresses all output when searching for NAME
4462 and shows only the path names when listing all definitions.
4462 and shows only the path names when listing all definitions.
4463
4463
4464 Path names are defined in the [paths] section of your
4464 Path names are defined in the [paths] section of your
4465 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4465 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4466 repository, ``.hg/hgrc`` is used, too.
4466 repository, ``.hg/hgrc`` is used, too.
4467
4467
4468 The path names ``default`` and ``default-push`` have a special
4468 The path names ``default`` and ``default-push`` have a special
4469 meaning. When performing a push or pull operation, they are used
4469 meaning. When performing a push or pull operation, they are used
4470 as fallbacks if no location is specified on the command-line.
4470 as fallbacks if no location is specified on the command-line.
4471 When ``default-push`` is set, it will be used for push and
4471 When ``default-push`` is set, it will be used for push and
4472 ``default`` will be used for pull; otherwise ``default`` is used
4472 ``default`` will be used for pull; otherwise ``default`` is used
4473 as the fallback for both. When cloning a repository, the clone
4473 as the fallback for both. When cloning a repository, the clone
4474 source is written as ``default`` in ``.hg/hgrc``. Note that
4474 source is written as ``default`` in ``.hg/hgrc``. Note that
4475 ``default`` and ``default-push`` apply to all inbound (e.g.
4475 ``default`` and ``default-push`` apply to all inbound (e.g.
4476 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4476 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4477 :hg:`bundle`) operations.
4477 :hg:`bundle`) operations.
4478
4478
4479 See :hg:`help urls` for more information.
4479 See :hg:`help urls` for more information.
4480
4480
4481 Returns 0 on success.
4481 Returns 0 on success.
4482 """
4482 """
4483 if search:
4483 if search:
4484 for name, path in ui.configitems("paths"):
4484 for name, path in ui.configitems("paths"):
4485 if name == search:
4485 if name == search:
4486 ui.status("%s\n" % util.hidepassword(path))
4486 ui.status("%s\n" % util.hidepassword(path))
4487 return
4487 return
4488 if not ui.quiet:
4488 if not ui.quiet:
4489 ui.warn(_("not found!\n"))
4489 ui.warn(_("not found!\n"))
4490 return 1
4490 return 1
4491 else:
4491 else:
4492 for name, path in ui.configitems("paths"):
4492 for name, path in ui.configitems("paths"):
4493 if ui.quiet:
4493 if ui.quiet:
4494 ui.write("%s\n" % name)
4494 ui.write("%s\n" % name)
4495 else:
4495 else:
4496 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4496 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4497
4497
4498 @command('^phase',
4498 @command('^phase',
4499 [('p', 'public', False, _('set changeset phase to public')),
4499 [('p', 'public', False, _('set changeset phase to public')),
4500 ('d', 'draft', False, _('set changeset phase to draft')),
4500 ('d', 'draft', False, _('set changeset phase to draft')),
4501 ('s', 'secret', False, _('set changeset phase to secret')),
4501 ('s', 'secret', False, _('set changeset phase to secret')),
4502 ('f', 'force', False, _('allow to move boundary backward')),
4502 ('f', 'force', False, _('allow to move boundary backward')),
4503 ('r', 'rev', [], _('target revision'), _('REV')),
4503 ('r', 'rev', [], _('target revision'), _('REV')),
4504 ],
4504 ],
4505 _('[-p|-d|-s] [-f] [-r] REV...'))
4505 _('[-p|-d|-s] [-f] [-r] REV...'))
4506 def phase(ui, repo, *revs, **opts):
4506 def phase(ui, repo, *revs, **opts):
4507 """set or show the current phase name
4507 """set or show the current phase name
4508
4508
4509 With no argument, show the phase name of specified revisions.
4509 With no argument, show the phase name of specified revisions.
4510
4510
4511 With one of -p/--public, -d/--draft or -s/--secret, change the
4511 With one of -p/--public, -d/--draft or -s/--secret, change the
4512 phase value of the specified revisions.
4512 phase value of the specified revisions.
4513
4513
4514 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4514 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4515 lower phase to an higher phase. Phases are ordered as follows::
4515 lower phase to an higher phase. Phases are ordered as follows::
4516
4516
4517 public < draft < secret
4517 public < draft < secret
4518
4518
4519 Return 0 on success, 1 if no phases were changed or some could not
4519 Return 0 on success, 1 if no phases were changed or some could not
4520 be changed.
4520 be changed.
4521 """
4521 """
4522 # search for a unique phase argument
4522 # search for a unique phase argument
4523 targetphase = None
4523 targetphase = None
4524 for idx, name in enumerate(phases.phasenames):
4524 for idx, name in enumerate(phases.phasenames):
4525 if opts[name]:
4525 if opts[name]:
4526 if targetphase is not None:
4526 if targetphase is not None:
4527 raise util.Abort(_('only one phase can be specified'))
4527 raise util.Abort(_('only one phase can be specified'))
4528 targetphase = idx
4528 targetphase = idx
4529
4529
4530 # look for specified revision
4530 # look for specified revision
4531 revs = list(revs)
4531 revs = list(revs)
4532 revs.extend(opts['rev'])
4532 revs.extend(opts['rev'])
4533 if not revs:
4533 if not revs:
4534 raise util.Abort(_('no revisions specified'))
4534 raise util.Abort(_('no revisions specified'))
4535
4535
4536 revs = scmutil.revrange(repo, revs)
4536 revs = scmutil.revrange(repo, revs)
4537
4537
4538 lock = None
4538 lock = None
4539 ret = 0
4539 ret = 0
4540 if targetphase is None:
4540 if targetphase is None:
4541 # display
4541 # display
4542 for r in revs:
4542 for r in revs:
4543 ctx = repo[r]
4543 ctx = repo[r]
4544 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4544 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4545 else:
4545 else:
4546 lock = repo.lock()
4546 lock = repo.lock()
4547 try:
4547 try:
4548 # set phase
4548 # set phase
4549 if not revs:
4549 if not revs:
4550 raise util.Abort(_('empty revision set'))
4550 raise util.Abort(_('empty revision set'))
4551 nodes = [repo[r].node() for r in revs]
4551 nodes = [repo[r].node() for r in revs]
4552 olddata = repo._phasecache.getphaserevs(repo)[:]
4552 olddata = repo._phasecache.getphaserevs(repo)[:]
4553 phases.advanceboundary(repo, targetphase, nodes)
4553 phases.advanceboundary(repo, targetphase, nodes)
4554 if opts['force']:
4554 if opts['force']:
4555 phases.retractboundary(repo, targetphase, nodes)
4555 phases.retractboundary(repo, targetphase, nodes)
4556 finally:
4556 finally:
4557 lock.release()
4557 lock.release()
4558 newdata = repo._phasecache.getphaserevs(repo)
4558 newdata = repo._phasecache.getphaserevs(repo)
4559 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4559 changes = sum(o != newdata[i] for i, o in enumerate(olddata))
4560 rejected = [n for n in nodes
4560 rejected = [n for n in nodes
4561 if newdata[repo[n].rev()] < targetphase]
4561 if newdata[repo[n].rev()] < targetphase]
4562 if rejected:
4562 if rejected:
4563 ui.warn(_('cannot move %i changesets to a more permissive '
4563 ui.warn(_('cannot move %i changesets to a more permissive '
4564 'phase, use --force\n') % len(rejected))
4564 'phase, use --force\n') % len(rejected))
4565 ret = 1
4565 ret = 1
4566 if changes:
4566 if changes:
4567 msg = _('phase changed for %i changesets\n') % changes
4567 msg = _('phase changed for %i changesets\n') % changes
4568 if ret:
4568 if ret:
4569 ui.status(msg)
4569 ui.status(msg)
4570 else:
4570 else:
4571 ui.note(msg)
4571 ui.note(msg)
4572 else:
4572 else:
4573 ui.warn(_('no phases changed\n'))
4573 ui.warn(_('no phases changed\n'))
4574 ret = 1
4574 ret = 1
4575 return ret
4575 return ret
4576
4576
4577 def postincoming(ui, repo, modheads, optupdate, checkout):
4577 def postincoming(ui, repo, modheads, optupdate, checkout):
4578 if modheads == 0:
4578 if modheads == 0:
4579 return
4579 return
4580 if optupdate:
4580 if optupdate:
4581 movemarkfrom = repo['.'].node()
4581 movemarkfrom = repo['.'].node()
4582 try:
4582 try:
4583 ret = hg.update(repo, checkout)
4583 ret = hg.update(repo, checkout)
4584 except util.Abort, inst:
4584 except util.Abort, inst:
4585 ui.warn(_("not updating: %s\n") % str(inst))
4585 ui.warn(_("not updating: %s\n") % str(inst))
4586 return 0
4586 return 0
4587 if not ret and not checkout:
4587 if not ret and not checkout:
4588 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4588 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4589 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4589 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4590 return ret
4590 return ret
4591 if modheads > 1:
4591 if modheads > 1:
4592 currentbranchheads = len(repo.branchheads())
4592 currentbranchheads = len(repo.branchheads())
4593 if currentbranchheads == modheads:
4593 if currentbranchheads == modheads:
4594 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4594 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4595 elif currentbranchheads > 1:
4595 elif currentbranchheads > 1:
4596 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4596 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4597 "merge)\n"))
4597 "merge)\n"))
4598 else:
4598 else:
4599 ui.status(_("(run 'hg heads' to see heads)\n"))
4599 ui.status(_("(run 'hg heads' to see heads)\n"))
4600 else:
4600 else:
4601 ui.status(_("(run 'hg update' to get a working copy)\n"))
4601 ui.status(_("(run 'hg update' to get a working copy)\n"))
4602
4602
4603 @command('^pull',
4603 @command('^pull',
4604 [('u', 'update', None,
4604 [('u', 'update', None,
4605 _('update to new branch head if changesets were pulled')),
4605 _('update to new branch head if changesets were pulled')),
4606 ('f', 'force', None, _('run even when remote repository is unrelated')),
4606 ('f', 'force', None, _('run even when remote repository is unrelated')),
4607 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4607 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4608 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4608 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4609 ('b', 'branch', [], _('a specific branch you would like to pull'),
4609 ('b', 'branch', [], _('a specific branch you would like to pull'),
4610 _('BRANCH')),
4610 _('BRANCH')),
4611 ] + remoteopts,
4611 ] + remoteopts,
4612 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4612 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4613 def pull(ui, repo, source="default", **opts):
4613 def pull(ui, repo, source="default", **opts):
4614 """pull changes from the specified source
4614 """pull changes from the specified source
4615
4615
4616 Pull changes from a remote repository to a local one.
4616 Pull changes from a remote repository to a local one.
4617
4617
4618 This finds all changes from the repository at the specified path
4618 This finds all changes from the repository at the specified path
4619 or URL and adds them to a local repository (the current one unless
4619 or URL and adds them to a local repository (the current one unless
4620 -R is specified). By default, this does not update the copy of the
4620 -R is specified). By default, this does not update the copy of the
4621 project in the working directory.
4621 project in the working directory.
4622
4622
4623 Use :hg:`incoming` if you want to see what would have been added
4623 Use :hg:`incoming` if you want to see what would have been added
4624 by a pull at the time you issued this command. If you then decide
4624 by a pull at the time you issued this command. If you then decide
4625 to add those changes to the repository, you should use :hg:`pull
4625 to add those changes to the repository, you should use :hg:`pull
4626 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4626 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4627
4627
4628 If SOURCE is omitted, the 'default' path will be used.
4628 If SOURCE is omitted, the 'default' path will be used.
4629 See :hg:`help urls` for more information.
4629 See :hg:`help urls` for more information.
4630
4630
4631 Returns 0 on success, 1 if an update had unresolved files.
4631 Returns 0 on success, 1 if an update had unresolved files.
4632 """
4632 """
4633 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4633 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4634 other = hg.peer(repo, opts, source)
4634 other = hg.peer(repo, opts, source)
4635 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4635 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4636 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4636 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4637
4637
4638 if opts.get('bookmark'):
4638 if opts.get('bookmark'):
4639 if not revs:
4639 if not revs:
4640 revs = []
4640 revs = []
4641 rb = other.listkeys('bookmarks')
4641 rb = other.listkeys('bookmarks')
4642 for b in opts['bookmark']:
4642 for b in opts['bookmark']:
4643 if b not in rb:
4643 if b not in rb:
4644 raise util.Abort(_('remote bookmark %s not found!') % b)
4644 raise util.Abort(_('remote bookmark %s not found!') % b)
4645 revs.append(rb[b])
4645 revs.append(rb[b])
4646
4646
4647 if revs:
4647 if revs:
4648 try:
4648 try:
4649 revs = [other.lookup(rev) for rev in revs]
4649 revs = [other.lookup(rev) for rev in revs]
4650 except error.CapabilityError:
4650 except error.CapabilityError:
4651 err = _("other repository doesn't support revision lookup, "
4651 err = _("other repository doesn't support revision lookup, "
4652 "so a rev cannot be specified.")
4652 "so a rev cannot be specified.")
4653 raise util.Abort(err)
4653 raise util.Abort(err)
4654
4654
4655 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4655 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
4656 bookmarks.updatefromremote(ui, repo, other, source)
4656 bookmarks.updatefromremote(ui, repo, other, source)
4657 if checkout:
4657 if checkout:
4658 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4658 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4659 repo._subtoppath = source
4659 repo._subtoppath = source
4660 try:
4660 try:
4661 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4661 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4662
4662
4663 finally:
4663 finally:
4664 del repo._subtoppath
4664 del repo._subtoppath
4665
4665
4666 # update specified bookmarks
4666 # update specified bookmarks
4667 if opts.get('bookmark'):
4667 if opts.get('bookmark'):
4668 for b in opts['bookmark']:
4668 for b in opts['bookmark']:
4669 # explicit pull overrides local bookmark if any
4669 # explicit pull overrides local bookmark if any
4670 ui.status(_("importing bookmark %s\n") % b)
4670 ui.status(_("importing bookmark %s\n") % b)
4671 repo._bookmarks[b] = repo[rb[b]].node()
4671 repo._bookmarks[b] = repo[rb[b]].node()
4672 bookmarks.write(repo)
4672 bookmarks.write(repo)
4673
4673
4674 return ret
4674 return ret
4675
4675
4676 @command('^push',
4676 @command('^push',
4677 [('f', 'force', None, _('force push')),
4677 [('f', 'force', None, _('force push')),
4678 ('r', 'rev', [],
4678 ('r', 'rev', [],
4679 _('a changeset intended to be included in the destination'),
4679 _('a changeset intended to be included in the destination'),
4680 _('REV')),
4680 _('REV')),
4681 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4681 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4682 ('b', 'branch', [],
4682 ('b', 'branch', [],
4683 _('a specific branch you would like to push'), _('BRANCH')),
4683 _('a specific branch you would like to push'), _('BRANCH')),
4684 ('', 'new-branch', False, _('allow pushing a new branch')),
4684 ('', 'new-branch', False, _('allow pushing a new branch')),
4685 ] + remoteopts,
4685 ] + remoteopts,
4686 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4686 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4687 def push(ui, repo, dest=None, **opts):
4687 def push(ui, repo, dest=None, **opts):
4688 """push changes to the specified destination
4688 """push changes to the specified destination
4689
4689
4690 Push changesets from the local repository to the specified
4690 Push changesets from the local repository to the specified
4691 destination.
4691 destination.
4692
4692
4693 This operation is symmetrical to pull: it is identical to a pull
4693 This operation is symmetrical to pull: it is identical to a pull
4694 in the destination repository from the current one.
4694 in the destination repository from the current one.
4695
4695
4696 By default, push will not allow creation of new heads at the
4696 By default, push will not allow creation of new heads at the
4697 destination, since multiple heads would make it unclear which head
4697 destination, since multiple heads would make it unclear which head
4698 to use. In this situation, it is recommended to pull and merge
4698 to use. In this situation, it is recommended to pull and merge
4699 before pushing.
4699 before pushing.
4700
4700
4701 Use --new-branch if you want to allow push to create a new named
4701 Use --new-branch if you want to allow push to create a new named
4702 branch that is not present at the destination. This allows you to
4702 branch that is not present at the destination. This allows you to
4703 only create a new branch without forcing other changes.
4703 only create a new branch without forcing other changes.
4704
4704
4705 Use -f/--force to override the default behavior and push all
4705 Use -f/--force to override the default behavior and push all
4706 changesets on all branches.
4706 changesets on all branches.
4707
4707
4708 If -r/--rev is used, the specified revision and all its ancestors
4708 If -r/--rev is used, the specified revision and all its ancestors
4709 will be pushed to the remote repository.
4709 will be pushed to the remote repository.
4710
4710
4711 If -B/--bookmark is used, the specified bookmarked revision, its
4711 If -B/--bookmark is used, the specified bookmarked revision, its
4712 ancestors, and the bookmark will be pushed to the remote
4712 ancestors, and the bookmark will be pushed to the remote
4713 repository.
4713 repository.
4714
4714
4715 Please see :hg:`help urls` for important details about ``ssh://``
4715 Please see :hg:`help urls` for important details about ``ssh://``
4716 URLs. If DESTINATION is omitted, a default path will be used.
4716 URLs. If DESTINATION is omitted, a default path will be used.
4717
4717
4718 Returns 0 if push was successful, 1 if nothing to push.
4718 Returns 0 if push was successful, 1 if nothing to push.
4719 """
4719 """
4720
4720
4721 if opts.get('bookmark'):
4721 if opts.get('bookmark'):
4722 for b in opts['bookmark']:
4722 for b in opts['bookmark']:
4723 # translate -B options to -r so changesets get pushed
4723 # translate -B options to -r so changesets get pushed
4724 if b in repo._bookmarks:
4724 if b in repo._bookmarks:
4725 opts.setdefault('rev', []).append(b)
4725 opts.setdefault('rev', []).append(b)
4726 else:
4726 else:
4727 # if we try to push a deleted bookmark, translate it to null
4727 # if we try to push a deleted bookmark, translate it to null
4728 # this lets simultaneous -r, -b options continue working
4728 # this lets simultaneous -r, -b options continue working
4729 opts.setdefault('rev', []).append("null")
4729 opts.setdefault('rev', []).append("null")
4730
4730
4731 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4731 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4732 dest, branches = hg.parseurl(dest, opts.get('branch'))
4732 dest, branches = hg.parseurl(dest, opts.get('branch'))
4733 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4733 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
4734 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4734 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
4735 other = hg.peer(repo, opts, dest)
4735 other = hg.peer(repo, opts, dest)
4736 if revs:
4736 if revs:
4737 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4737 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
4738
4738
4739 repo._subtoppath = dest
4739 repo._subtoppath = dest
4740 try:
4740 try:
4741 # push subrepos depth-first for coherent ordering
4741 # push subrepos depth-first for coherent ordering
4742 c = repo['']
4742 c = repo['']
4743 subs = c.substate # only repos that are committed
4743 subs = c.substate # only repos that are committed
4744 for s in sorted(subs):
4744 for s in sorted(subs):
4745 if c.sub(s).push(opts) == 0:
4745 if c.sub(s).push(opts) == 0:
4746 return False
4746 return False
4747 finally:
4747 finally:
4748 del repo._subtoppath
4748 del repo._subtoppath
4749 result = repo.push(other, opts.get('force'), revs=revs,
4749 result = repo.push(other, opts.get('force'), revs=revs,
4750 newbranch=opts.get('new_branch'))
4750 newbranch=opts.get('new_branch'))
4751
4751
4752 result = not result
4752 result = not result
4753
4753
4754 if opts.get('bookmark'):
4754 if opts.get('bookmark'):
4755 rb = other.listkeys('bookmarks')
4755 rb = other.listkeys('bookmarks')
4756 for b in opts['bookmark']:
4756 for b in opts['bookmark']:
4757 # explicit push overrides remote bookmark if any
4757 # explicit push overrides remote bookmark if any
4758 if b in repo._bookmarks:
4758 if b in repo._bookmarks:
4759 ui.status(_("exporting bookmark %s\n") % b)
4759 ui.status(_("exporting bookmark %s\n") % b)
4760 new = repo[b].hex()
4760 new = repo[b].hex()
4761 elif b in rb:
4761 elif b in rb:
4762 ui.status(_("deleting remote bookmark %s\n") % b)
4762 ui.status(_("deleting remote bookmark %s\n") % b)
4763 new = '' # delete
4763 new = '' # delete
4764 else:
4764 else:
4765 ui.warn(_('bookmark %s does not exist on the local '
4765 ui.warn(_('bookmark %s does not exist on the local '
4766 'or remote repository!\n') % b)
4766 'or remote repository!\n') % b)
4767 return 2
4767 return 2
4768 old = rb.get(b, '')
4768 old = rb.get(b, '')
4769 r = other.pushkey('bookmarks', b, old, new)
4769 r = other.pushkey('bookmarks', b, old, new)
4770 if not r:
4770 if not r:
4771 ui.warn(_('updating bookmark %s failed!\n') % b)
4771 ui.warn(_('updating bookmark %s failed!\n') % b)
4772 if not result:
4772 if not result:
4773 result = 2
4773 result = 2
4774
4774
4775 return result
4775 return result
4776
4776
4777 @command('recover', [])
4777 @command('recover', [])
4778 def recover(ui, repo):
4778 def recover(ui, repo):
4779 """roll back an interrupted transaction
4779 """roll back an interrupted transaction
4780
4780
4781 Recover from an interrupted commit or pull.
4781 Recover from an interrupted commit or pull.
4782
4782
4783 This command tries to fix the repository status after an
4783 This command tries to fix the repository status after an
4784 interrupted operation. It should only be necessary when Mercurial
4784 interrupted operation. It should only be necessary when Mercurial
4785 suggests it.
4785 suggests it.
4786
4786
4787 Returns 0 if successful, 1 if nothing to recover or verify fails.
4787 Returns 0 if successful, 1 if nothing to recover or verify fails.
4788 """
4788 """
4789 if repo.recover():
4789 if repo.recover():
4790 return hg.verify(repo)
4790 return hg.verify(repo)
4791 return 1
4791 return 1
4792
4792
4793 @command('^remove|rm',
4793 @command('^remove|rm',
4794 [('A', 'after', None, _('record delete for missing files')),
4794 [('A', 'after', None, _('record delete for missing files')),
4795 ('f', 'force', None,
4795 ('f', 'force', None,
4796 _('remove (and delete) file even if added or modified')),
4796 _('remove (and delete) file even if added or modified')),
4797 ] + walkopts,
4797 ] + walkopts,
4798 _('[OPTION]... FILE...'))
4798 _('[OPTION]... FILE...'))
4799 def remove(ui, repo, *pats, **opts):
4799 def remove(ui, repo, *pats, **opts):
4800 """remove the specified files on the next commit
4800 """remove the specified files on the next commit
4801
4801
4802 Schedule the indicated files for removal from the current branch.
4802 Schedule the indicated files for removal from the current branch.
4803
4803
4804 This command schedules the files to be removed at the next commit.
4804 This command schedules the files to be removed at the next commit.
4805 To undo a remove before that, see :hg:`revert`. To undo added
4805 To undo a remove before that, see :hg:`revert`. To undo added
4806 files, see :hg:`forget`.
4806 files, see :hg:`forget`.
4807
4807
4808 .. container:: verbose
4808 .. container:: verbose
4809
4809
4810 -A/--after can be used to remove only files that have already
4810 -A/--after can be used to remove only files that have already
4811 been deleted, -f/--force can be used to force deletion, and -Af
4811 been deleted, -f/--force can be used to force deletion, and -Af
4812 can be used to remove files from the next revision without
4812 can be used to remove files from the next revision without
4813 deleting them from the working directory.
4813 deleting them from the working directory.
4814
4814
4815 The following table details the behavior of remove for different
4815 The following table details the behavior of remove for different
4816 file states (columns) and option combinations (rows). The file
4816 file states (columns) and option combinations (rows). The file
4817 states are Added [A], Clean [C], Modified [M] and Missing [!]
4817 states are Added [A], Clean [C], Modified [M] and Missing [!]
4818 (as reported by :hg:`status`). The actions are Warn, Remove
4818 (as reported by :hg:`status`). The actions are Warn, Remove
4819 (from branch) and Delete (from disk):
4819 (from branch) and Delete (from disk):
4820
4820
4821 ======= == == == ==
4821 ======= == == == ==
4822 A C M !
4822 A C M !
4823 ======= == == == ==
4823 ======= == == == ==
4824 none W RD W R
4824 none W RD W R
4825 -f R RD RD R
4825 -f R RD RD R
4826 -A W W W R
4826 -A W W W R
4827 -Af R R R R
4827 -Af R R R R
4828 ======= == == == ==
4828 ======= == == == ==
4829
4829
4830 Note that remove never deletes files in Added [A] state from the
4830 Note that remove never deletes files in Added [A] state from the
4831 working directory, not even if option --force is specified.
4831 working directory, not even if option --force is specified.
4832
4832
4833 Returns 0 on success, 1 if any warnings encountered.
4833 Returns 0 on success, 1 if any warnings encountered.
4834 """
4834 """
4835
4835
4836 ret = 0
4836 ret = 0
4837 after, force = opts.get('after'), opts.get('force')
4837 after, force = opts.get('after'), opts.get('force')
4838 if not pats and not after:
4838 if not pats and not after:
4839 raise util.Abort(_('no files specified'))
4839 raise util.Abort(_('no files specified'))
4840
4840
4841 m = scmutil.match(repo[None], pats, opts)
4841 m = scmutil.match(repo[None], pats, opts)
4842 s = repo.status(match=m, clean=True)
4842 s = repo.status(match=m, clean=True)
4843 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4843 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
4844
4844
4845 # warn about failure to delete explicit files/dirs
4845 # warn about failure to delete explicit files/dirs
4846 wctx = repo[None]
4846 wctx = repo[None]
4847 for f in m.files():
4847 for f in m.files():
4848 if f in repo.dirstate or f in wctx.dirs():
4848 if f in repo.dirstate or f in wctx.dirs():
4849 continue
4849 continue
4850 if os.path.exists(m.rel(f)):
4850 if os.path.exists(m.rel(f)):
4851 if os.path.isdir(m.rel(f)):
4851 if os.path.isdir(m.rel(f)):
4852 ui.warn(_('not removing %s: no tracked files\n') % m.rel(f))
4852 ui.warn(_('not removing %s: no tracked files\n') % m.rel(f))
4853 else:
4853 else:
4854 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4854 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
4855 # missing files will generate a warning elsewhere
4855 # missing files will generate a warning elsewhere
4856 ret = 1
4856 ret = 1
4857
4857
4858 if force:
4858 if force:
4859 list = modified + deleted + clean + added
4859 list = modified + deleted + clean + added
4860 elif after:
4860 elif after:
4861 list = deleted
4861 list = deleted
4862 for f in modified + added + clean:
4862 for f in modified + added + clean:
4863 ui.warn(_('not removing %s: file still exists (use -f'
4863 ui.warn(_('not removing %s: file still exists (use -f'
4864 ' to force removal)\n') % m.rel(f))
4864 ' to force removal)\n') % m.rel(f))
4865 ret = 1
4865 ret = 1
4866 else:
4866 else:
4867 list = deleted + clean
4867 list = deleted + clean
4868 for f in modified:
4868 for f in modified:
4869 ui.warn(_('not removing %s: file is modified (use -f'
4869 ui.warn(_('not removing %s: file is modified (use -f'
4870 ' to force removal)\n') % m.rel(f))
4870 ' to force removal)\n') % m.rel(f))
4871 ret = 1
4871 ret = 1
4872 for f in added:
4872 for f in added:
4873 ui.warn(_('not removing %s: file has been marked for add'
4873 ui.warn(_('not removing %s: file has been marked for add'
4874 ' (use forget to undo)\n') % m.rel(f))
4874 ' (use forget to undo)\n') % m.rel(f))
4875 ret = 1
4875 ret = 1
4876
4876
4877 for f in sorted(list):
4877 for f in sorted(list):
4878 if ui.verbose or not m.exact(f):
4878 if ui.verbose or not m.exact(f):
4879 ui.status(_('removing %s\n') % m.rel(f))
4879 ui.status(_('removing %s\n') % m.rel(f))
4880
4880
4881 wlock = repo.wlock()
4881 wlock = repo.wlock()
4882 try:
4882 try:
4883 if not after:
4883 if not after:
4884 for f in list:
4884 for f in list:
4885 if f in added:
4885 if f in added:
4886 continue # we never unlink added files on remove
4886 continue # we never unlink added files on remove
4887 try:
4887 try:
4888 util.unlinkpath(repo.wjoin(f))
4888 util.unlinkpath(repo.wjoin(f))
4889 except OSError, inst:
4889 except OSError, inst:
4890 if inst.errno != errno.ENOENT:
4890 if inst.errno != errno.ENOENT:
4891 raise
4891 raise
4892 repo[None].forget(list)
4892 repo[None].forget(list)
4893 finally:
4893 finally:
4894 wlock.release()
4894 wlock.release()
4895
4895
4896 return ret
4896 return ret
4897
4897
4898 @command('rename|move|mv',
4898 @command('rename|move|mv',
4899 [('A', 'after', None, _('record a rename that has already occurred')),
4899 [('A', 'after', None, _('record a rename that has already occurred')),
4900 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4900 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4901 ] + walkopts + dryrunopts,
4901 ] + walkopts + dryrunopts,
4902 _('[OPTION]... SOURCE... DEST'))
4902 _('[OPTION]... SOURCE... DEST'))
4903 def rename(ui, repo, *pats, **opts):
4903 def rename(ui, repo, *pats, **opts):
4904 """rename files; equivalent of copy + remove
4904 """rename files; equivalent of copy + remove
4905
4905
4906 Mark dest as copies of sources; mark sources for deletion. If dest
4906 Mark dest as copies of sources; mark sources for deletion. If dest
4907 is a directory, copies are put in that directory. If dest is a
4907 is a directory, copies are put in that directory. If dest is a
4908 file, there can only be one source.
4908 file, there can only be one source.
4909
4909
4910 By default, this command copies the contents of files as they
4910 By default, this command copies the contents of files as they
4911 exist in the working directory. If invoked with -A/--after, the
4911 exist in the working directory. If invoked with -A/--after, the
4912 operation is recorded, but no copying is performed.
4912 operation is recorded, but no copying is performed.
4913
4913
4914 This command takes effect at the next commit. To undo a rename
4914 This command takes effect at the next commit. To undo a rename
4915 before that, see :hg:`revert`.
4915 before that, see :hg:`revert`.
4916
4916
4917 Returns 0 on success, 1 if errors are encountered.
4917 Returns 0 on success, 1 if errors are encountered.
4918 """
4918 """
4919 wlock = repo.wlock(False)
4919 wlock = repo.wlock(False)
4920 try:
4920 try:
4921 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4921 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4922 finally:
4922 finally:
4923 wlock.release()
4923 wlock.release()
4924
4924
4925 @command('resolve',
4925 @command('resolve',
4926 [('a', 'all', None, _('select all unresolved files')),
4926 [('a', 'all', None, _('select all unresolved files')),
4927 ('l', 'list', None, _('list state of files needing merge')),
4927 ('l', 'list', None, _('list state of files needing merge')),
4928 ('m', 'mark', None, _('mark files as resolved')),
4928 ('m', 'mark', None, _('mark files as resolved')),
4929 ('u', 'unmark', None, _('mark files as unresolved')),
4929 ('u', 'unmark', None, _('mark files as unresolved')),
4930 ('n', 'no-status', None, _('hide status prefix'))]
4930 ('n', 'no-status', None, _('hide status prefix'))]
4931 + mergetoolopts + walkopts,
4931 + mergetoolopts + walkopts,
4932 _('[OPTION]... [FILE]...'))
4932 _('[OPTION]... [FILE]...'))
4933 def resolve(ui, repo, *pats, **opts):
4933 def resolve(ui, repo, *pats, **opts):
4934 """redo merges or set/view the merge status of files
4934 """redo merges or set/view the merge status of files
4935
4935
4936 Merges with unresolved conflicts are often the result of
4936 Merges with unresolved conflicts are often the result of
4937 non-interactive merging using the ``internal:merge`` configuration
4937 non-interactive merging using the ``internal:merge`` configuration
4938 setting, or a command-line merge tool like ``diff3``. The resolve
4938 setting, or a command-line merge tool like ``diff3``. The resolve
4939 command is used to manage the files involved in a merge, after
4939 command is used to manage the files involved in a merge, after
4940 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4940 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4941 working directory must have two parents). See :hg:`help
4941 working directory must have two parents). See :hg:`help
4942 merge-tools` for information on configuring merge tools.
4942 merge-tools` for information on configuring merge tools.
4943
4943
4944 The resolve command can be used in the following ways:
4944 The resolve command can be used in the following ways:
4945
4945
4946 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4946 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4947 files, discarding any previous merge attempts. Re-merging is not
4947 files, discarding any previous merge attempts. Re-merging is not
4948 performed for files already marked as resolved. Use ``--all/-a``
4948 performed for files already marked as resolved. Use ``--all/-a``
4949 to select all unresolved files. ``--tool`` can be used to specify
4949 to select all unresolved files. ``--tool`` can be used to specify
4950 the merge tool used for the given files. It overrides the HGMERGE
4950 the merge tool used for the given files. It overrides the HGMERGE
4951 environment variable and your configuration files. Previous file
4951 environment variable and your configuration files. Previous file
4952 contents are saved with a ``.orig`` suffix.
4952 contents are saved with a ``.orig`` suffix.
4953
4953
4954 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4954 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4955 (e.g. after having manually fixed-up the files). The default is
4955 (e.g. after having manually fixed-up the files). The default is
4956 to mark all unresolved files.
4956 to mark all unresolved files.
4957
4957
4958 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4958 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4959 default is to mark all resolved files.
4959 default is to mark all resolved files.
4960
4960
4961 - :hg:`resolve -l`: list files which had or still have conflicts.
4961 - :hg:`resolve -l`: list files which had or still have conflicts.
4962 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4962 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4963
4963
4964 Note that Mercurial will not let you commit files with unresolved
4964 Note that Mercurial will not let you commit files with unresolved
4965 merge conflicts. You must use :hg:`resolve -m ...` before you can
4965 merge conflicts. You must use :hg:`resolve -m ...` before you can
4966 commit after a conflicting merge.
4966 commit after a conflicting merge.
4967
4967
4968 Returns 0 on success, 1 if any files fail a resolve attempt.
4968 Returns 0 on success, 1 if any files fail a resolve attempt.
4969 """
4969 """
4970
4970
4971 all, mark, unmark, show, nostatus = \
4971 all, mark, unmark, show, nostatus = \
4972 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4972 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4973
4973
4974 if (show and (mark or unmark)) or (mark and unmark):
4974 if (show and (mark or unmark)) or (mark and unmark):
4975 raise util.Abort(_("too many options specified"))
4975 raise util.Abort(_("too many options specified"))
4976 if pats and all:
4976 if pats and all:
4977 raise util.Abort(_("can't specify --all and patterns"))
4977 raise util.Abort(_("can't specify --all and patterns"))
4978 if not (all or pats or show or mark or unmark):
4978 if not (all or pats or show or mark or unmark):
4979 raise util.Abort(_('no files or directories specified; '
4979 raise util.Abort(_('no files or directories specified; '
4980 'use --all to remerge all files'))
4980 'use --all to remerge all files'))
4981
4981
4982 ms = mergemod.mergestate(repo)
4982 ms = mergemod.mergestate(repo)
4983 m = scmutil.match(repo[None], pats, opts)
4983 m = scmutil.match(repo[None], pats, opts)
4984 ret = 0
4984 ret = 0
4985
4985
4986 for f in ms:
4986 for f in ms:
4987 if m(f):
4987 if m(f):
4988 if show:
4988 if show:
4989 if nostatus:
4989 if nostatus:
4990 ui.write("%s\n" % f)
4990 ui.write("%s\n" % f)
4991 else:
4991 else:
4992 ui.write("%s %s\n" % (ms[f].upper(), f),
4992 ui.write("%s %s\n" % (ms[f].upper(), f),
4993 label='resolve.' +
4993 label='resolve.' +
4994 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4994 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4995 elif mark:
4995 elif mark:
4996 ms.mark(f, "r")
4996 ms.mark(f, "r")
4997 elif unmark:
4997 elif unmark:
4998 ms.mark(f, "u")
4998 ms.mark(f, "u")
4999 else:
4999 else:
5000 wctx = repo[None]
5000 wctx = repo[None]
5001 mctx = wctx.parents()[-1]
5001 mctx = wctx.parents()[-1]
5002
5002
5003 # backup pre-resolve (merge uses .orig for its own purposes)
5003 # backup pre-resolve (merge uses .orig for its own purposes)
5004 a = repo.wjoin(f)
5004 a = repo.wjoin(f)
5005 util.copyfile(a, a + ".resolve")
5005 util.copyfile(a, a + ".resolve")
5006
5006
5007 try:
5007 try:
5008 # resolve file
5008 # resolve file
5009 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
5009 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
5010 if ms.resolve(f, wctx, mctx):
5010 if ms.resolve(f, wctx, mctx):
5011 ret = 1
5011 ret = 1
5012 finally:
5012 finally:
5013 ui.setconfig('ui', 'forcemerge', '')
5013 ui.setconfig('ui', 'forcemerge', '')
5014 ms.commit()
5014 ms.commit()
5015
5015
5016 # replace filemerge's .orig file with our resolve file
5016 # replace filemerge's .orig file with our resolve file
5017 util.rename(a + ".resolve", a + ".orig")
5017 util.rename(a + ".resolve", a + ".orig")
5018
5018
5019 ms.commit()
5019 ms.commit()
5020 return ret
5020 return ret
5021
5021
5022 @command('revert',
5022 @command('revert',
5023 [('a', 'all', None, _('revert all changes when no arguments given')),
5023 [('a', 'all', None, _('revert all changes when no arguments given')),
5024 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5024 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5025 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5025 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5026 ('C', 'no-backup', None, _('do not save backup copies of files')),
5026 ('C', 'no-backup', None, _('do not save backup copies of files')),
5027 ] + walkopts + dryrunopts,
5027 ] + walkopts + dryrunopts,
5028 _('[OPTION]... [-r REV] [NAME]...'))
5028 _('[OPTION]... [-r REV] [NAME]...'))
5029 def revert(ui, repo, *pats, **opts):
5029 def revert(ui, repo, *pats, **opts):
5030 """restore files to their checkout state
5030 """restore files to their checkout state
5031
5031
5032 .. note::
5032 .. note::
5033
5033
5034 To check out earlier revisions, you should use :hg:`update REV`.
5034 To check out earlier revisions, you should use :hg:`update REV`.
5035 To cancel an uncommitted merge (and lose your changes), use
5035 To cancel an uncommitted merge (and lose your changes), use
5036 :hg:`update --clean .`.
5036 :hg:`update --clean .`.
5037
5037
5038 With no revision specified, revert the specified files or directories
5038 With no revision specified, revert the specified files or directories
5039 to the contents they had in the parent of the working directory.
5039 to the contents they had in the parent of the working directory.
5040 This restores the contents of files to an unmodified
5040 This restores the contents of files to an unmodified
5041 state and unschedules adds, removes, copies, and renames. If the
5041 state and unschedules adds, removes, copies, and renames. If the
5042 working directory has two parents, you must explicitly specify a
5042 working directory has two parents, you must explicitly specify a
5043 revision.
5043 revision.
5044
5044
5045 Using the -r/--rev or -d/--date options, revert the given files or
5045 Using the -r/--rev or -d/--date options, revert the given files or
5046 directories to their states as of a specific revision. Because
5046 directories to their states as of a specific revision. Because
5047 revert does not change the working directory parents, this will
5047 revert does not change the working directory parents, this will
5048 cause these files to appear modified. This can be helpful to "back
5048 cause these files to appear modified. This can be helpful to "back
5049 out" some or all of an earlier change. See :hg:`backout` for a
5049 out" some or all of an earlier change. See :hg:`backout` for a
5050 related method.
5050 related method.
5051
5051
5052 Modified files are saved with a .orig suffix before reverting.
5052 Modified files are saved with a .orig suffix before reverting.
5053 To disable these backups, use --no-backup.
5053 To disable these backups, use --no-backup.
5054
5054
5055 See :hg:`help dates` for a list of formats valid for -d/--date.
5055 See :hg:`help dates` for a list of formats valid for -d/--date.
5056
5056
5057 Returns 0 on success.
5057 Returns 0 on success.
5058 """
5058 """
5059
5059
5060 if opts.get("date"):
5060 if opts.get("date"):
5061 if opts.get("rev"):
5061 if opts.get("rev"):
5062 raise util.Abort(_("you can't specify a revision and a date"))
5062 raise util.Abort(_("you can't specify a revision and a date"))
5063 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5063 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5064
5064
5065 parent, p2 = repo.dirstate.parents()
5065 parent, p2 = repo.dirstate.parents()
5066 if not opts.get('rev') and p2 != nullid:
5066 if not opts.get('rev') and p2 != nullid:
5067 # revert after merge is a trap for new users (issue2915)
5067 # revert after merge is a trap for new users (issue2915)
5068 raise util.Abort(_('uncommitted merge with no revision specified'),
5068 raise util.Abort(_('uncommitted merge with no revision specified'),
5069 hint=_('use "hg update" or see "hg help revert"'))
5069 hint=_('use "hg update" or see "hg help revert"'))
5070
5070
5071 ctx = scmutil.revsingle(repo, opts.get('rev'))
5071 ctx = scmutil.revsingle(repo, opts.get('rev'))
5072
5072
5073 if not pats and not opts.get('all'):
5073 if not pats and not opts.get('all'):
5074 msg = _("no files or directories specified")
5074 msg = _("no files or directories specified")
5075 if p2 != nullid:
5075 if p2 != nullid:
5076 hint = _("uncommitted merge, use --all to discard all changes,"
5076 hint = _("uncommitted merge, use --all to discard all changes,"
5077 " or 'hg update -C .' to abort the merge")
5077 " or 'hg update -C .' to abort the merge")
5078 raise util.Abort(msg, hint=hint)
5078 raise util.Abort(msg, hint=hint)
5079 dirty = util.any(repo.status())
5079 dirty = util.any(repo.status())
5080 node = ctx.node()
5080 node = ctx.node()
5081 if node != parent:
5081 if node != parent:
5082 if dirty:
5082 if dirty:
5083 hint = _("uncommitted changes, use --all to discard all"
5083 hint = _("uncommitted changes, use --all to discard all"
5084 " changes, or 'hg update %s' to update") % ctx.rev()
5084 " changes, or 'hg update %s' to update") % ctx.rev()
5085 else:
5085 else:
5086 hint = _("use --all to revert all files,"
5086 hint = _("use --all to revert all files,"
5087 " or 'hg update %s' to update") % ctx.rev()
5087 " or 'hg update %s' to update") % ctx.rev()
5088 elif dirty:
5088 elif dirty:
5089 hint = _("uncommitted changes, use --all to discard all changes")
5089 hint = _("uncommitted changes, use --all to discard all changes")
5090 else:
5090 else:
5091 hint = _("use --all to revert all files")
5091 hint = _("use --all to revert all files")
5092 raise util.Abort(msg, hint=hint)
5092 raise util.Abort(msg, hint=hint)
5093
5093
5094 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5094 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5095
5095
5096 @command('rollback', dryrunopts +
5096 @command('rollback', dryrunopts +
5097 [('f', 'force', False, _('ignore safety measures'))])
5097 [('f', 'force', False, _('ignore safety measures'))])
5098 def rollback(ui, repo, **opts):
5098 def rollback(ui, repo, **opts):
5099 """roll back the last transaction (dangerous)
5099 """roll back the last transaction (dangerous)
5100
5100
5101 This command should be used with care. There is only one level of
5101 This command should be used with care. There is only one level of
5102 rollback, and there is no way to undo a rollback. It will also
5102 rollback, and there is no way to undo a rollback. It will also
5103 restore the dirstate at the time of the last transaction, losing
5103 restore the dirstate at the time of the last transaction, losing
5104 any dirstate changes since that time. This command does not alter
5104 any dirstate changes since that time. This command does not alter
5105 the working directory.
5105 the working directory.
5106
5106
5107 Transactions are used to encapsulate the effects of all commands
5107 Transactions are used to encapsulate the effects of all commands
5108 that create new changesets or propagate existing changesets into a
5108 that create new changesets or propagate existing changesets into a
5109 repository.
5109 repository.
5110
5110
5111 .. container:: verbose
5111 .. container:: verbose
5112
5112
5113 For example, the following commands are transactional, and their
5113 For example, the following commands are transactional, and their
5114 effects can be rolled back:
5114 effects can be rolled back:
5115
5115
5116 - commit
5116 - commit
5117 - import
5117 - import
5118 - pull
5118 - pull
5119 - push (with this repository as the destination)
5119 - push (with this repository as the destination)
5120 - unbundle
5120 - unbundle
5121
5121
5122 To avoid permanent data loss, rollback will refuse to rollback a
5122 To avoid permanent data loss, rollback will refuse to rollback a
5123 commit transaction if it isn't checked out. Use --force to
5123 commit transaction if it isn't checked out. Use --force to
5124 override this protection.
5124 override this protection.
5125
5125
5126 This command is not intended for use on public repositories. Once
5126 This command is not intended for use on public repositories. Once
5127 changes are visible for pull by other users, rolling a transaction
5127 changes are visible for pull by other users, rolling a transaction
5128 back locally is ineffective (someone else may already have pulled
5128 back locally is ineffective (someone else may already have pulled
5129 the changes). Furthermore, a race is possible with readers of the
5129 the changes). Furthermore, a race is possible with readers of the
5130 repository; for example an in-progress pull from the repository
5130 repository; for example an in-progress pull from the repository
5131 may fail if a rollback is performed.
5131 may fail if a rollback is performed.
5132
5132
5133 Returns 0 on success, 1 if no rollback data is available.
5133 Returns 0 on success, 1 if no rollback data is available.
5134 """
5134 """
5135 return repo.rollback(dryrun=opts.get('dry_run'),
5135 return repo.rollback(dryrun=opts.get('dry_run'),
5136 force=opts.get('force'))
5136 force=opts.get('force'))
5137
5137
5138 @command('root', [])
5138 @command('root', [])
5139 def root(ui, repo):
5139 def root(ui, repo):
5140 """print the root (top) of the current working directory
5140 """print the root (top) of the current working directory
5141
5141
5142 Print the root directory of the current repository.
5142 Print the root directory of the current repository.
5143
5143
5144 Returns 0 on success.
5144 Returns 0 on success.
5145 """
5145 """
5146 ui.write(repo.root + "\n")
5146 ui.write(repo.root + "\n")
5147
5147
5148 @command('^serve',
5148 @command('^serve',
5149 [('A', 'accesslog', '', _('name of access log file to write to'),
5149 [('A', 'accesslog', '', _('name of access log file to write to'),
5150 _('FILE')),
5150 _('FILE')),
5151 ('d', 'daemon', None, _('run server in background')),
5151 ('d', 'daemon', None, _('run server in background')),
5152 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
5152 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
5153 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5153 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5154 # use string type, then we can check if something was passed
5154 # use string type, then we can check if something was passed
5155 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5155 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5156 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5156 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5157 _('ADDR')),
5157 _('ADDR')),
5158 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5158 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5159 _('PREFIX')),
5159 _('PREFIX')),
5160 ('n', 'name', '',
5160 ('n', 'name', '',
5161 _('name to show in web pages (default: working directory)'), _('NAME')),
5161 _('name to show in web pages (default: working directory)'), _('NAME')),
5162 ('', 'web-conf', '',
5162 ('', 'web-conf', '',
5163 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5163 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5164 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5164 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5165 _('FILE')),
5165 _('FILE')),
5166 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5166 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5167 ('', 'stdio', None, _('for remote clients')),
5167 ('', 'stdio', None, _('for remote clients')),
5168 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5168 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5169 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5169 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5170 ('', 'style', '', _('template style to use'), _('STYLE')),
5170 ('', 'style', '', _('template style to use'), _('STYLE')),
5171 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5171 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5172 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5172 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5173 _('[OPTION]...'))
5173 _('[OPTION]...'))
5174 def serve(ui, repo, **opts):
5174 def serve(ui, repo, **opts):
5175 """start stand-alone webserver
5175 """start stand-alone webserver
5176
5176
5177 Start a local HTTP repository browser and pull server. You can use
5177 Start a local HTTP repository browser and pull server. You can use
5178 this for ad-hoc sharing and browsing of repositories. It is
5178 this for ad-hoc sharing and browsing of repositories. It is
5179 recommended to use a real web server to serve a repository for
5179 recommended to use a real web server to serve a repository for
5180 longer periods of time.
5180 longer periods of time.
5181
5181
5182 Please note that the server does not implement access control.
5182 Please note that the server does not implement access control.
5183 This means that, by default, anybody can read from the server and
5183 This means that, by default, anybody can read from the server and
5184 nobody can write to it by default. Set the ``web.allow_push``
5184 nobody can write to it by default. Set the ``web.allow_push``
5185 option to ``*`` to allow everybody to push to the server. You
5185 option to ``*`` to allow everybody to push to the server. You
5186 should use a real web server if you need to authenticate users.
5186 should use a real web server if you need to authenticate users.
5187
5187
5188 By default, the server logs accesses to stdout and errors to
5188 By default, the server logs accesses to stdout and errors to
5189 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5189 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5190 files.
5190 files.
5191
5191
5192 To have the server choose a free port number to listen on, specify
5192 To have the server choose a free port number to listen on, specify
5193 a port number of 0; in this case, the server will print the port
5193 a port number of 0; in this case, the server will print the port
5194 number it uses.
5194 number it uses.
5195
5195
5196 Returns 0 on success.
5196 Returns 0 on success.
5197 """
5197 """
5198
5198
5199 if opts["stdio"] and opts["cmdserver"]:
5199 if opts["stdio"] and opts["cmdserver"]:
5200 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5200 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5201
5201
5202 def checkrepo():
5202 def checkrepo():
5203 if repo is None:
5203 if repo is None:
5204 raise error.RepoError(_("there is no Mercurial repository here"
5204 raise error.RepoError(_("there is no Mercurial repository here"
5205 " (.hg not found)"))
5205 " (.hg not found)"))
5206
5206
5207 if opts["stdio"]:
5207 if opts["stdio"]:
5208 checkrepo()
5208 checkrepo()
5209 s = sshserver.sshserver(ui, repo)
5209 s = sshserver.sshserver(ui, repo)
5210 s.serve_forever()
5210 s.serve_forever()
5211
5211
5212 if opts["cmdserver"]:
5212 if opts["cmdserver"]:
5213 checkrepo()
5213 checkrepo()
5214 s = commandserver.server(ui, repo, opts["cmdserver"])
5214 s = commandserver.server(ui, repo, opts["cmdserver"])
5215 return s.serve()
5215 return s.serve()
5216
5216
5217 # this way we can check if something was given in the command-line
5217 # this way we can check if something was given in the command-line
5218 if opts.get('port'):
5218 if opts.get('port'):
5219 opts['port'] = util.getport(opts.get('port'))
5219 opts['port'] = util.getport(opts.get('port'))
5220
5220
5221 baseui = repo and repo.baseui or ui
5221 baseui = repo and repo.baseui or ui
5222 optlist = ("name templates style address port prefix ipv6"
5222 optlist = ("name templates style address port prefix ipv6"
5223 " accesslog errorlog certificate encoding")
5223 " accesslog errorlog certificate encoding")
5224 for o in optlist.split():
5224 for o in optlist.split():
5225 val = opts.get(o, '')
5225 val = opts.get(o, '')
5226 if val in (None, ''): # should check against default options instead
5226 if val in (None, ''): # should check against default options instead
5227 continue
5227 continue
5228 baseui.setconfig("web", o, val)
5228 baseui.setconfig("web", o, val)
5229 if repo and repo.ui != baseui:
5229 if repo and repo.ui != baseui:
5230 repo.ui.setconfig("web", o, val)
5230 repo.ui.setconfig("web", o, val)
5231
5231
5232 o = opts.get('web_conf') or opts.get('webdir_conf')
5232 o = opts.get('web_conf') or opts.get('webdir_conf')
5233 if not o:
5233 if not o:
5234 if not repo:
5234 if not repo:
5235 raise error.RepoError(_("there is no Mercurial repository"
5235 raise error.RepoError(_("there is no Mercurial repository"
5236 " here (.hg not found)"))
5236 " here (.hg not found)"))
5237 o = repo.root
5237 o = repo.root
5238
5238
5239 app = hgweb.hgweb(o, baseui=ui)
5239 app = hgweb.hgweb(o, baseui=ui)
5240
5240
5241 class service(object):
5241 class service(object):
5242 def init(self):
5242 def init(self):
5243 util.setsignalhandler()
5243 util.setsignalhandler()
5244 self.httpd = hgweb.server.create_server(ui, app)
5244 self.httpd = hgweb.server.create_server(ui, app)
5245
5245
5246 if opts['port'] and not ui.verbose:
5246 if opts['port'] and not ui.verbose:
5247 return
5247 return
5248
5248
5249 if self.httpd.prefix:
5249 if self.httpd.prefix:
5250 prefix = self.httpd.prefix.strip('/') + '/'
5250 prefix = self.httpd.prefix.strip('/') + '/'
5251 else:
5251 else:
5252 prefix = ''
5252 prefix = ''
5253
5253
5254 port = ':%d' % self.httpd.port
5254 port = ':%d' % self.httpd.port
5255 if port == ':80':
5255 if port == ':80':
5256 port = ''
5256 port = ''
5257
5257
5258 bindaddr = self.httpd.addr
5258 bindaddr = self.httpd.addr
5259 if bindaddr == '0.0.0.0':
5259 if bindaddr == '0.0.0.0':
5260 bindaddr = '*'
5260 bindaddr = '*'
5261 elif ':' in bindaddr: # IPv6
5261 elif ':' in bindaddr: # IPv6
5262 bindaddr = '[%s]' % bindaddr
5262 bindaddr = '[%s]' % bindaddr
5263
5263
5264 fqaddr = self.httpd.fqaddr
5264 fqaddr = self.httpd.fqaddr
5265 if ':' in fqaddr:
5265 if ':' in fqaddr:
5266 fqaddr = '[%s]' % fqaddr
5266 fqaddr = '[%s]' % fqaddr
5267 if opts['port']:
5267 if opts['port']:
5268 write = ui.status
5268 write = ui.status
5269 else:
5269 else:
5270 write = ui.write
5270 write = ui.write
5271 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5271 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5272 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5272 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5273
5273
5274 def run(self):
5274 def run(self):
5275 self.httpd.serve_forever()
5275 self.httpd.serve_forever()
5276
5276
5277 service = service()
5277 service = service()
5278
5278
5279 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5279 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5280
5280
5281 @command('showconfig|debugconfig',
5281 @command('showconfig|debugconfig',
5282 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5282 [('u', 'untrusted', None, _('show untrusted configuration options'))],
5283 _('[-u] [NAME]...'))
5283 _('[-u] [NAME]...'))
5284 def showconfig(ui, repo, *values, **opts):
5284 def showconfig(ui, repo, *values, **opts):
5285 """show combined config settings from all hgrc files
5285 """show combined config settings from all hgrc files
5286
5286
5287 With no arguments, print names and values of all config items.
5287 With no arguments, print names and values of all config items.
5288
5288
5289 With one argument of the form section.name, print just the value
5289 With one argument of the form section.name, print just the value
5290 of that config item.
5290 of that config item.
5291
5291
5292 With multiple arguments, print names and values of all config
5292 With multiple arguments, print names and values of all config
5293 items with matching section names.
5293 items with matching section names.
5294
5294
5295 With --debug, the source (filename and line number) is printed
5295 With --debug, the source (filename and line number) is printed
5296 for each config item.
5296 for each config item.
5297
5297
5298 Returns 0 on success.
5298 Returns 0 on success.
5299 """
5299 """
5300
5300
5301 for f in scmutil.rcpath():
5301 for f in scmutil.rcpath():
5302 ui.debug('read config from: %s\n' % f)
5302 ui.debug('read config from: %s\n' % f)
5303 untrusted = bool(opts.get('untrusted'))
5303 untrusted = bool(opts.get('untrusted'))
5304 if values:
5304 if values:
5305 sections = [v for v in values if '.' not in v]
5305 sections = [v for v in values if '.' not in v]
5306 items = [v for v in values if '.' in v]
5306 items = [v for v in values if '.' in v]
5307 if len(items) > 1 or items and sections:
5307 if len(items) > 1 or items and sections:
5308 raise util.Abort(_('only one config item permitted'))
5308 raise util.Abort(_('only one config item permitted'))
5309 for section, name, value in ui.walkconfig(untrusted=untrusted):
5309 for section, name, value in ui.walkconfig(untrusted=untrusted):
5310 value = str(value).replace('\n', '\\n')
5310 value = str(value).replace('\n', '\\n')
5311 sectname = section + '.' + name
5311 sectname = section + '.' + name
5312 if values:
5312 if values:
5313 for v in values:
5313 for v in values:
5314 if v == section:
5314 if v == section:
5315 ui.debug('%s: ' %
5315 ui.debug('%s: ' %
5316 ui.configsource(section, name, untrusted))
5316 ui.configsource(section, name, untrusted))
5317 ui.write('%s=%s\n' % (sectname, value))
5317 ui.write('%s=%s\n' % (sectname, value))
5318 elif v == sectname:
5318 elif v == sectname:
5319 ui.debug('%s: ' %
5319 ui.debug('%s: ' %
5320 ui.configsource(section, name, untrusted))
5320 ui.configsource(section, name, untrusted))
5321 ui.write(value, '\n')
5321 ui.write(value, '\n')
5322 else:
5322 else:
5323 ui.debug('%s: ' %
5323 ui.debug('%s: ' %
5324 ui.configsource(section, name, untrusted))
5324 ui.configsource(section, name, untrusted))
5325 ui.write('%s=%s\n' % (sectname, value))
5325 ui.write('%s=%s\n' % (sectname, value))
5326
5326
5327 @command('^status|st',
5327 @command('^status|st',
5328 [('A', 'all', None, _('show status of all files')),
5328 [('A', 'all', None, _('show status of all files')),
5329 ('m', 'modified', None, _('show only modified files')),
5329 ('m', 'modified', None, _('show only modified files')),
5330 ('a', 'added', None, _('show only added files')),
5330 ('a', 'added', None, _('show only added files')),
5331 ('r', 'removed', None, _('show only removed files')),
5331 ('r', 'removed', None, _('show only removed files')),
5332 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5332 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5333 ('c', 'clean', None, _('show only files without changes')),
5333 ('c', 'clean', None, _('show only files without changes')),
5334 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5334 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5335 ('i', 'ignored', None, _('show only ignored files')),
5335 ('i', 'ignored', None, _('show only ignored files')),
5336 ('n', 'no-status', None, _('hide status prefix')),
5336 ('n', 'no-status', None, _('hide status prefix')),
5337 ('C', 'copies', None, _('show source of copied files')),
5337 ('C', 'copies', None, _('show source of copied files')),
5338 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5338 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5339 ('', 'rev', [], _('show difference from revision'), _('REV')),
5339 ('', 'rev', [], _('show difference from revision'), _('REV')),
5340 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5340 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5341 ] + walkopts + subrepoopts,
5341 ] + walkopts + subrepoopts,
5342 _('[OPTION]... [FILE]...'))
5342 _('[OPTION]... [FILE]...'))
5343 def status(ui, repo, *pats, **opts):
5343 def status(ui, repo, *pats, **opts):
5344 """show changed files in the working directory
5344 """show changed files in the working directory
5345
5345
5346 Show status of files in the repository. If names are given, only
5346 Show status of files in the repository. If names are given, only
5347 files that match are shown. Files that are clean or ignored or
5347 files that match are shown. Files that are clean or ignored or
5348 the source of a copy/move operation, are not listed unless
5348 the source of a copy/move operation, are not listed unless
5349 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5349 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5350 Unless options described with "show only ..." are given, the
5350 Unless options described with "show only ..." are given, the
5351 options -mardu are used.
5351 options -mardu are used.
5352
5352
5353 Option -q/--quiet hides untracked (unknown and ignored) files
5353 Option -q/--quiet hides untracked (unknown and ignored) files
5354 unless explicitly requested with -u/--unknown or -i/--ignored.
5354 unless explicitly requested with -u/--unknown or -i/--ignored.
5355
5355
5356 .. note::
5356 .. note::
5357 status may appear to disagree with diff if permissions have
5357 status may appear to disagree with diff if permissions have
5358 changed or a merge has occurred. The standard diff format does
5358 changed or a merge has occurred. The standard diff format does
5359 not report permission changes and diff only reports changes
5359 not report permission changes and diff only reports changes
5360 relative to one merge parent.
5360 relative to one merge parent.
5361
5361
5362 If one revision is given, it is used as the base revision.
5362 If one revision is given, it is used as the base revision.
5363 If two revisions are given, the differences between them are
5363 If two revisions are given, the differences between them are
5364 shown. The --change option can also be used as a shortcut to list
5364 shown. The --change option can also be used as a shortcut to list
5365 the changed files of a revision from its first parent.
5365 the changed files of a revision from its first parent.
5366
5366
5367 The codes used to show the status of files are::
5367 The codes used to show the status of files are::
5368
5368
5369 M = modified
5369 M = modified
5370 A = added
5370 A = added
5371 R = removed
5371 R = removed
5372 C = clean
5372 C = clean
5373 ! = missing (deleted by non-hg command, but still tracked)
5373 ! = missing (deleted by non-hg command, but still tracked)
5374 ? = not tracked
5374 ? = not tracked
5375 I = ignored
5375 I = ignored
5376 = origin of the previous file listed as A (added)
5376 = origin of the previous file listed as A (added)
5377
5377
5378 .. container:: verbose
5378 .. container:: verbose
5379
5379
5380 Examples:
5380 Examples:
5381
5381
5382 - show changes in the working directory relative to a
5382 - show changes in the working directory relative to a
5383 changeset::
5383 changeset::
5384
5384
5385 hg status --rev 9353
5385 hg status --rev 9353
5386
5386
5387 - show all changes including copies in an existing changeset::
5387 - show all changes including copies in an existing changeset::
5388
5388
5389 hg status --copies --change 9353
5389 hg status --copies --change 9353
5390
5390
5391 - get a NUL separated list of added files, suitable for xargs::
5391 - get a NUL separated list of added files, suitable for xargs::
5392
5392
5393 hg status -an0
5393 hg status -an0
5394
5394
5395 Returns 0 on success.
5395 Returns 0 on success.
5396 """
5396 """
5397
5397
5398 revs = opts.get('rev')
5398 revs = opts.get('rev')
5399 change = opts.get('change')
5399 change = opts.get('change')
5400
5400
5401 if revs and change:
5401 if revs and change:
5402 msg = _('cannot specify --rev and --change at the same time')
5402 msg = _('cannot specify --rev and --change at the same time')
5403 raise util.Abort(msg)
5403 raise util.Abort(msg)
5404 elif change:
5404 elif change:
5405 node2 = scmutil.revsingle(repo, change, None).node()
5405 node2 = scmutil.revsingle(repo, change, None).node()
5406 node1 = repo[node2].p1().node()
5406 node1 = repo[node2].p1().node()
5407 else:
5407 else:
5408 node1, node2 = scmutil.revpair(repo, revs)
5408 node1, node2 = scmutil.revpair(repo, revs)
5409
5409
5410 cwd = (pats and repo.getcwd()) or ''
5410 cwd = (pats and repo.getcwd()) or ''
5411 end = opts.get('print0') and '\0' or '\n'
5411 end = opts.get('print0') and '\0' or '\n'
5412 copy = {}
5412 copy = {}
5413 states = 'modified added removed deleted unknown ignored clean'.split()
5413 states = 'modified added removed deleted unknown ignored clean'.split()
5414 show = [k for k in states if opts.get(k)]
5414 show = [k for k in states if opts.get(k)]
5415 if opts.get('all'):
5415 if opts.get('all'):
5416 show += ui.quiet and (states[:4] + ['clean']) or states
5416 show += ui.quiet and (states[:4] + ['clean']) or states
5417 if not show:
5417 if not show:
5418 show = ui.quiet and states[:4] or states[:5]
5418 show = ui.quiet and states[:4] or states[:5]
5419
5419
5420 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5420 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5421 'ignored' in show, 'clean' in show, 'unknown' in show,
5421 'ignored' in show, 'clean' in show, 'unknown' in show,
5422 opts.get('subrepos'))
5422 opts.get('subrepos'))
5423 changestates = zip(states, 'MAR!?IC', stat)
5423 changestates = zip(states, 'MAR!?IC', stat)
5424
5424
5425 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5425 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5426 copy = copies.pathcopies(repo[node1], repo[node2])
5426 copy = copies.pathcopies(repo[node1], repo[node2])
5427
5427
5428 fm = ui.formatter('status', opts)
5428 fm = ui.formatter('status', opts)
5429 format = '%s %s' + end
5429 format = '%s %s' + end
5430 if opts.get('no_status'):
5430 if opts.get('no_status'):
5431 format = '%.0s%s' + end
5431 format = '%.0s%s' + end
5432
5432
5433 for state, char, files in changestates:
5433 for state, char, files in changestates:
5434 if state in show:
5434 if state in show:
5435 label = 'status.' + state
5435 label = 'status.' + state
5436 for f in files:
5436 for f in files:
5437 fm.startitem()
5437 fm.startitem()
5438 fm.write("status path", format, char,
5438 fm.write("status path", format, char,
5439 repo.pathto(f, cwd), label=label)
5439 repo.pathto(f, cwd), label=label)
5440 if f in copy:
5440 if f in copy:
5441 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5441 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5442 label='status.copied')
5442 label='status.copied')
5443 fm.end()
5443 fm.end()
5444
5444
5445 @command('^summary|sum',
5445 @command('^summary|sum',
5446 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5446 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5447 def summary(ui, repo, **opts):
5447 def summary(ui, repo, **opts):
5448 """summarize working directory state
5448 """summarize working directory state
5449
5449
5450 This generates a brief summary of the working directory state,
5450 This generates a brief summary of the working directory state,
5451 including parents, branch, commit status, and available updates.
5451 including parents, branch, commit status, and available updates.
5452
5452
5453 With the --remote option, this will check the default paths for
5453 With the --remote option, this will check the default paths for
5454 incoming and outgoing changes. This can be time-consuming.
5454 incoming and outgoing changes. This can be time-consuming.
5455
5455
5456 Returns 0 on success.
5456 Returns 0 on success.
5457 """
5457 """
5458
5458
5459 ctx = repo[None]
5459 ctx = repo[None]
5460 parents = ctx.parents()
5460 parents = ctx.parents()
5461 pnode = parents[0].node()
5461 pnode = parents[0].node()
5462 marks = []
5462 marks = []
5463
5463
5464 for p in parents:
5464 for p in parents:
5465 # label with log.changeset (instead of log.parent) since this
5465 # label with log.changeset (instead of log.parent) since this
5466 # shows a working directory parent *changeset*:
5466 # shows a working directory parent *changeset*:
5467 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5467 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5468 label='log.changeset changeset.%s' % p.phasestr())
5468 label='log.changeset changeset.%s' % p.phasestr())
5469 ui.write(' '.join(p.tags()), label='log.tag')
5469 ui.write(' '.join(p.tags()), label='log.tag')
5470 if p.bookmarks():
5470 if p.bookmarks():
5471 marks.extend(p.bookmarks())
5471 marks.extend(p.bookmarks())
5472 if p.rev() == -1:
5472 if p.rev() == -1:
5473 if not len(repo):
5473 if not len(repo):
5474 ui.write(_(' (empty repository)'))
5474 ui.write(_(' (empty repository)'))
5475 else:
5475 else:
5476 ui.write(_(' (no revision checked out)'))
5476 ui.write(_(' (no revision checked out)'))
5477 ui.write('\n')
5477 ui.write('\n')
5478 if p.description():
5478 if p.description():
5479 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5479 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5480 label='log.summary')
5480 label='log.summary')
5481
5481
5482 branch = ctx.branch()
5482 branch = ctx.branch()
5483 bheads = repo.branchheads(branch)
5483 bheads = repo.branchheads(branch)
5484 m = _('branch: %s\n') % branch
5484 m = _('branch: %s\n') % branch
5485 if branch != 'default':
5485 if branch != 'default':
5486 ui.write(m, label='log.branch')
5486 ui.write(m, label='log.branch')
5487 else:
5487 else:
5488 ui.status(m, label='log.branch')
5488 ui.status(m, label='log.branch')
5489
5489
5490 if marks:
5490 if marks:
5491 current = repo._bookmarkcurrent
5491 current = repo._bookmarkcurrent
5492 ui.write(_('bookmarks:'), label='log.bookmark')
5492 ui.write(_('bookmarks:'), label='log.bookmark')
5493 if current is not None:
5493 if current is not None:
5494 try:
5494 try:
5495 marks.remove(current)
5495 marks.remove(current)
5496 ui.write(' *' + current, label='bookmarks.current')
5496 ui.write(' *' + current, label='bookmarks.current')
5497 except ValueError:
5497 except ValueError:
5498 # current bookmark not in parent ctx marks
5498 # current bookmark not in parent ctx marks
5499 pass
5499 pass
5500 for m in marks:
5500 for m in marks:
5501 ui.write(' ' + m, label='log.bookmark')
5501 ui.write(' ' + m, label='log.bookmark')
5502 ui.write('\n', label='log.bookmark')
5502 ui.write('\n', label='log.bookmark')
5503
5503
5504 st = list(repo.status(unknown=True))[:6]
5504 st = list(repo.status(unknown=True))[:6]
5505
5505
5506 c = repo.dirstate.copies()
5506 c = repo.dirstate.copies()
5507 copied, renamed = [], []
5507 copied, renamed = [], []
5508 for d, s in c.iteritems():
5508 for d, s in c.iteritems():
5509 if s in st[2]:
5509 if s in st[2]:
5510 st[2].remove(s)
5510 st[2].remove(s)
5511 renamed.append(d)
5511 renamed.append(d)
5512 else:
5512 else:
5513 copied.append(d)
5513 copied.append(d)
5514 if d in st[1]:
5514 if d in st[1]:
5515 st[1].remove(d)
5515 st[1].remove(d)
5516 st.insert(3, renamed)
5516 st.insert(3, renamed)
5517 st.insert(4, copied)
5517 st.insert(4, copied)
5518
5518
5519 ms = mergemod.mergestate(repo)
5519 ms = mergemod.mergestate(repo)
5520 st.append([f for f in ms if ms[f] == 'u'])
5520 st.append([f for f in ms if ms[f] == 'u'])
5521
5521
5522 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5522 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5523 st.append(subs)
5523 st.append(subs)
5524
5524
5525 labels = [ui.label(_('%d modified'), 'status.modified'),
5525 labels = [ui.label(_('%d modified'), 'status.modified'),
5526 ui.label(_('%d added'), 'status.added'),
5526 ui.label(_('%d added'), 'status.added'),
5527 ui.label(_('%d removed'), 'status.removed'),
5527 ui.label(_('%d removed'), 'status.removed'),
5528 ui.label(_('%d renamed'), 'status.copied'),
5528 ui.label(_('%d renamed'), 'status.copied'),
5529 ui.label(_('%d copied'), 'status.copied'),
5529 ui.label(_('%d copied'), 'status.copied'),
5530 ui.label(_('%d deleted'), 'status.deleted'),
5530 ui.label(_('%d deleted'), 'status.deleted'),
5531 ui.label(_('%d unknown'), 'status.unknown'),
5531 ui.label(_('%d unknown'), 'status.unknown'),
5532 ui.label(_('%d ignored'), 'status.ignored'),
5532 ui.label(_('%d ignored'), 'status.ignored'),
5533 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5533 ui.label(_('%d unresolved'), 'resolve.unresolved'),
5534 ui.label(_('%d subrepos'), 'status.modified')]
5534 ui.label(_('%d subrepos'), 'status.modified')]
5535 t = []
5535 t = []
5536 for s, l in zip(st, labels):
5536 for s, l in zip(st, labels):
5537 if s:
5537 if s:
5538 t.append(l % len(s))
5538 t.append(l % len(s))
5539
5539
5540 t = ', '.join(t)
5540 t = ', '.join(t)
5541 cleanworkdir = False
5541 cleanworkdir = False
5542
5542
5543 if len(parents) > 1:
5543 if len(parents) > 1:
5544 t += _(' (merge)')
5544 t += _(' (merge)')
5545 elif branch != parents[0].branch():
5545 elif branch != parents[0].branch():
5546 t += _(' (new branch)')
5546 t += _(' (new branch)')
5547 elif (parents[0].closesbranch() and
5547 elif (parents[0].closesbranch() and
5548 pnode in repo.branchheads(branch, closed=True)):
5548 pnode in repo.branchheads(branch, closed=True)):
5549 t += _(' (head closed)')
5549 t += _(' (head closed)')
5550 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5550 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
5551 t += _(' (clean)')
5551 t += _(' (clean)')
5552 cleanworkdir = True
5552 cleanworkdir = True
5553 elif pnode not in bheads:
5553 elif pnode not in bheads:
5554 t += _(' (new branch head)')
5554 t += _(' (new branch head)')
5555
5555
5556 if cleanworkdir:
5556 if cleanworkdir:
5557 ui.status(_('commit: %s\n') % t.strip())
5557 ui.status(_('commit: %s\n') % t.strip())
5558 else:
5558 else:
5559 ui.write(_('commit: %s\n') % t.strip())
5559 ui.write(_('commit: %s\n') % t.strip())
5560
5560
5561 # all ancestors of branch heads - all ancestors of parent = new csets
5561 # all ancestors of branch heads - all ancestors of parent = new csets
5562 new = [0] * len(repo)
5562 new = [0] * len(repo)
5563 cl = repo.changelog
5563 cl = repo.changelog
5564 for a in [cl.rev(n) for n in bheads]:
5564 for a in [cl.rev(n) for n in bheads]:
5565 new[a] = 1
5565 new[a] = 1
5566 for a in cl.ancestors([cl.rev(n) for n in bheads]):
5566 for a in cl.ancestors([cl.rev(n) for n in bheads]):
5567 new[a] = 1
5567 new[a] = 1
5568 for a in [p.rev() for p in parents]:
5568 for a in [p.rev() for p in parents]:
5569 if a >= 0:
5569 if a >= 0:
5570 new[a] = 0
5570 new[a] = 0
5571 for a in cl.ancestors([p.rev() for p in parents]):
5571 for a in cl.ancestors([p.rev() for p in parents]):
5572 new[a] = 0
5572 new[a] = 0
5573 new = sum(new)
5573 new = sum(new)
5574
5574
5575 if new == 0:
5575 if new == 0:
5576 ui.status(_('update: (current)\n'))
5576 ui.status(_('update: (current)\n'))
5577 elif pnode not in bheads:
5577 elif pnode not in bheads:
5578 ui.write(_('update: %d new changesets (update)\n') % new)
5578 ui.write(_('update: %d new changesets (update)\n') % new)
5579 else:
5579 else:
5580 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5580 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5581 (new, len(bheads)))
5581 (new, len(bheads)))
5582
5582
5583 if opts.get('remote'):
5583 if opts.get('remote'):
5584 t = []
5584 t = []
5585 source, branches = hg.parseurl(ui.expandpath('default'))
5585 source, branches = hg.parseurl(ui.expandpath('default'))
5586 other = hg.peer(repo, {}, source)
5586 other = hg.peer(repo, {}, source)
5587 revs, checkout = hg.addbranchrevs(repo, other, branches,
5587 revs, checkout = hg.addbranchrevs(repo, other, branches,
5588 opts.get('rev'))
5588 opts.get('rev'))
5589 ui.debug('comparing with %s\n' % util.hidepassword(source))
5589 ui.debug('comparing with %s\n' % util.hidepassword(source))
5590 repo.ui.pushbuffer()
5590 repo.ui.pushbuffer()
5591 commoninc = discovery.findcommonincoming(repo, other)
5591 commoninc = discovery.findcommonincoming(repo, other)
5592 _common, incoming, _rheads = commoninc
5592 _common, incoming, _rheads = commoninc
5593 repo.ui.popbuffer()
5593 repo.ui.popbuffer()
5594 if incoming:
5594 if incoming:
5595 t.append(_('1 or more incoming'))
5595 t.append(_('1 or more incoming'))
5596
5596
5597 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5597 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5598 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5598 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5599 if source != dest:
5599 if source != dest:
5600 other = hg.peer(repo, {}, dest)
5600 other = hg.peer(repo, {}, dest)
5601 commoninc = None
5601 commoninc = None
5602 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5602 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5603 repo.ui.pushbuffer()
5603 repo.ui.pushbuffer()
5604 outgoing = discovery.findcommonoutgoing(repo, other,
5604 outgoing = discovery.findcommonoutgoing(repo, other,
5605 commoninc=commoninc)
5605 commoninc=commoninc)
5606 repo.ui.popbuffer()
5606 repo.ui.popbuffer()
5607 o = outgoing.missing
5607 o = outgoing.missing
5608 if o:
5608 if o:
5609 t.append(_('%d outgoing') % len(o))
5609 t.append(_('%d outgoing') % len(o))
5610 if 'bookmarks' in other.listkeys('namespaces'):
5610 if 'bookmarks' in other.listkeys('namespaces'):
5611 lmarks = repo.listkeys('bookmarks')
5611 lmarks = repo.listkeys('bookmarks')
5612 rmarks = other.listkeys('bookmarks')
5612 rmarks = other.listkeys('bookmarks')
5613 diff = set(rmarks) - set(lmarks)
5613 diff = set(rmarks) - set(lmarks)
5614 if len(diff) > 0:
5614 if len(diff) > 0:
5615 t.append(_('%d incoming bookmarks') % len(diff))
5615 t.append(_('%d incoming bookmarks') % len(diff))
5616 diff = set(lmarks) - set(rmarks)
5616 diff = set(lmarks) - set(rmarks)
5617 if len(diff) > 0:
5617 if len(diff) > 0:
5618 t.append(_('%d outgoing bookmarks') % len(diff))
5618 t.append(_('%d outgoing bookmarks') % len(diff))
5619
5619
5620 if t:
5620 if t:
5621 ui.write(_('remote: %s\n') % (', '.join(t)))
5621 ui.write(_('remote: %s\n') % (', '.join(t)))
5622 else:
5622 else:
5623 ui.status(_('remote: (synced)\n'))
5623 ui.status(_('remote: (synced)\n'))
5624
5624
5625 @command('tag',
5625 @command('tag',
5626 [('f', 'force', None, _('force tag')),
5626 [('f', 'force', None, _('force tag')),
5627 ('l', 'local', None, _('make the tag local')),
5627 ('l', 'local', None, _('make the tag local')),
5628 ('r', 'rev', '', _('revision to tag'), _('REV')),
5628 ('r', 'rev', '', _('revision to tag'), _('REV')),
5629 ('', 'remove', None, _('remove a tag')),
5629 ('', 'remove', None, _('remove a tag')),
5630 # -l/--local is already there, commitopts cannot be used
5630 # -l/--local is already there, commitopts cannot be used
5631 ('e', 'edit', None, _('edit commit message')),
5631 ('e', 'edit', None, _('edit commit message')),
5632 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5632 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
5633 ] + commitopts2,
5633 ] + commitopts2,
5634 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5634 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5635 def tag(ui, repo, name1, *names, **opts):
5635 def tag(ui, repo, name1, *names, **opts):
5636 """add one or more tags for the current or given revision
5636 """add one or more tags for the current or given revision
5637
5637
5638 Name a particular revision using <name>.
5638 Name a particular revision using <name>.
5639
5639
5640 Tags are used to name particular revisions of the repository and are
5640 Tags are used to name particular revisions of the repository and are
5641 very useful to compare different revisions, to go back to significant
5641 very useful to compare different revisions, to go back to significant
5642 earlier versions or to mark branch points as releases, etc. Changing
5642 earlier versions or to mark branch points as releases, etc. Changing
5643 an existing tag is normally disallowed; use -f/--force to override.
5643 an existing tag is normally disallowed; use -f/--force to override.
5644
5644
5645 If no revision is given, the parent of the working directory is
5645 If no revision is given, the parent of the working directory is
5646 used, or tip if no revision is checked out.
5646 used, or tip if no revision is checked out.
5647
5647
5648 To facilitate version control, distribution, and merging of tags,
5648 To facilitate version control, distribution, and merging of tags,
5649 they are stored as a file named ".hgtags" which is managed similarly
5649 they are stored as a file named ".hgtags" which is managed similarly
5650 to other project files and can be hand-edited if necessary. This
5650 to other project files and can be hand-edited if necessary. This
5651 also means that tagging creates a new commit. The file
5651 also means that tagging creates a new commit. The file
5652 ".hg/localtags" is used for local tags (not shared among
5652 ".hg/localtags" is used for local tags (not shared among
5653 repositories).
5653 repositories).
5654
5654
5655 Tag commits are usually made at the head of a branch. If the parent
5655 Tag commits are usually made at the head of a branch. If the parent
5656 of the working directory is not a branch head, :hg:`tag` aborts; use
5656 of the working directory is not a branch head, :hg:`tag` aborts; use
5657 -f/--force to force the tag commit to be based on a non-head
5657 -f/--force to force the tag commit to be based on a non-head
5658 changeset.
5658 changeset.
5659
5659
5660 See :hg:`help dates` for a list of formats valid for -d/--date.
5660 See :hg:`help dates` for a list of formats valid for -d/--date.
5661
5661
5662 Since tag names have priority over branch names during revision
5662 Since tag names have priority over branch names during revision
5663 lookup, using an existing branch name as a tag name is discouraged.
5663 lookup, using an existing branch name as a tag name is discouraged.
5664
5664
5665 Returns 0 on success.
5665 Returns 0 on success.
5666 """
5666 """
5667 wlock = lock = None
5667 wlock = lock = None
5668 try:
5668 try:
5669 wlock = repo.wlock()
5669 wlock = repo.wlock()
5670 lock = repo.lock()
5670 lock = repo.lock()
5671 rev_ = "."
5671 rev_ = "."
5672 names = [t.strip() for t in (name1,) + names]
5672 names = [t.strip() for t in (name1,) + names]
5673 if len(names) != len(set(names)):
5673 if len(names) != len(set(names)):
5674 raise util.Abort(_('tag names must be unique'))
5674 raise util.Abort(_('tag names must be unique'))
5675 for n in names:
5675 for n in names:
5676 scmutil.checknewlabel(repo, n, 'tag')
5676 scmutil.checknewlabel(repo, n, 'tag')
5677 if not n:
5677 if not n:
5678 raise util.Abort(_('tag names cannot consist entirely of '
5678 raise util.Abort(_('tag names cannot consist entirely of '
5679 'whitespace'))
5679 'whitespace'))
5680 if opts.get('rev') and opts.get('remove'):
5680 if opts.get('rev') and opts.get('remove'):
5681 raise util.Abort(_("--rev and --remove are incompatible"))
5681 raise util.Abort(_("--rev and --remove are incompatible"))
5682 if opts.get('rev'):
5682 if opts.get('rev'):
5683 rev_ = opts['rev']
5683 rev_ = opts['rev']
5684 message = opts.get('message')
5684 message = opts.get('message')
5685 if opts.get('remove'):
5685 if opts.get('remove'):
5686 expectedtype = opts.get('local') and 'local' or 'global'
5686 expectedtype = opts.get('local') and 'local' or 'global'
5687 for n in names:
5687 for n in names:
5688 if not repo.tagtype(n):
5688 if not repo.tagtype(n):
5689 raise util.Abort(_("tag '%s' does not exist") % n)
5689 raise util.Abort(_("tag '%s' does not exist") % n)
5690 if repo.tagtype(n) != expectedtype:
5690 if repo.tagtype(n) != expectedtype:
5691 if expectedtype == 'global':
5691 if expectedtype == 'global':
5692 raise util.Abort(_("tag '%s' is not a global tag") % n)
5692 raise util.Abort(_("tag '%s' is not a global tag") % n)
5693 else:
5693 else:
5694 raise util.Abort(_("tag '%s' is not a local tag") % n)
5694 raise util.Abort(_("tag '%s' is not a local tag") % n)
5695 rev_ = nullid
5695 rev_ = nullid
5696 if not message:
5696 if not message:
5697 # we don't translate commit messages
5697 # we don't translate commit messages
5698 message = 'Removed tag %s' % ', '.join(names)
5698 message = 'Removed tag %s' % ', '.join(names)
5699 elif not opts.get('force'):
5699 elif not opts.get('force'):
5700 for n in names:
5700 for n in names:
5701 if n in repo.tags():
5701 if n in repo.tags():
5702 raise util.Abort(_("tag '%s' already exists "
5702 raise util.Abort(_("tag '%s' already exists "
5703 "(use -f to force)") % n)
5703 "(use -f to force)") % n)
5704 if not opts.get('local'):
5704 if not opts.get('local'):
5705 p1, p2 = repo.dirstate.parents()
5705 p1, p2 = repo.dirstate.parents()
5706 if p2 != nullid:
5706 if p2 != nullid:
5707 raise util.Abort(_('uncommitted merge'))
5707 raise util.Abort(_('uncommitted merge'))
5708 bheads = repo.branchheads()
5708 bheads = repo.branchheads()
5709 if not opts.get('force') and bheads and p1 not in bheads:
5709 if not opts.get('force') and bheads and p1 not in bheads:
5710 raise util.Abort(_('not at a branch head (use -f to force)'))
5710 raise util.Abort(_('not at a branch head (use -f to force)'))
5711 r = scmutil.revsingle(repo, rev_).node()
5711 r = scmutil.revsingle(repo, rev_).node()
5712
5712
5713 if not message:
5713 if not message:
5714 # we don't translate commit messages
5714 # we don't translate commit messages
5715 message = ('Added tag %s for changeset %s' %
5715 message = ('Added tag %s for changeset %s' %
5716 (', '.join(names), short(r)))
5716 (', '.join(names), short(r)))
5717
5717
5718 date = opts.get('date')
5718 date = opts.get('date')
5719 if date:
5719 if date:
5720 date = util.parsedate(date)
5720 date = util.parsedate(date)
5721
5721
5722 if opts.get('edit'):
5722 if opts.get('edit'):
5723 message = ui.edit(message, ui.username())
5723 message = ui.edit(message, ui.username())
5724
5724
5725 # don't allow tagging the null rev
5725 # don't allow tagging the null rev
5726 if (not opts.get('remove') and
5726 if (not opts.get('remove') and
5727 scmutil.revsingle(repo, rev_).rev() == nullrev):
5727 scmutil.revsingle(repo, rev_).rev() == nullrev):
5728 raise util.Abort(_("null revision specified"))
5728 raise util.Abort(_("null revision specified"))
5729
5729
5730 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5730 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
5731 finally:
5731 finally:
5732 release(lock, wlock)
5732 release(lock, wlock)
5733
5733
5734 @command('tags', [], '')
5734 @command('tags', [], '')
5735 def tags(ui, repo):
5735 def tags(ui, repo):
5736 """list repository tags
5736 """list repository tags
5737
5737
5738 This lists both regular and local tags. When the -v/--verbose
5738 This lists both regular and local tags. When the -v/--verbose
5739 switch is used, a third column "local" is printed for local tags.
5739 switch is used, a third column "local" is printed for local tags.
5740
5740
5741 Returns 0 on success.
5741 Returns 0 on success.
5742 """
5742 """
5743
5743
5744 hexfunc = ui.debugflag and hex or short
5744 hexfunc = ui.debugflag and hex or short
5745 tagtype = ""
5745 tagtype = ""
5746
5746
5747 for t, n in reversed(repo.tagslist()):
5747 for t, n in reversed(repo.tagslist()):
5748 if ui.quiet:
5748 if ui.quiet:
5749 ui.write("%s\n" % t, label='tags.normal')
5749 ui.write("%s\n" % t, label='tags.normal')
5750 continue
5750 continue
5751
5751
5752 hn = hexfunc(n)
5752 hn = hexfunc(n)
5753 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5753 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5754 rev = ui.label(r, 'log.changeset changeset.%s' % repo[n].phasestr())
5754 rev = ui.label(r, 'log.changeset changeset.%s' % repo[n].phasestr())
5755 spaces = " " * (30 - encoding.colwidth(t))
5755 spaces = " " * (30 - encoding.colwidth(t))
5756
5756
5757 tag = ui.label(t, 'tags.normal')
5757 tag = ui.label(t, 'tags.normal')
5758 if ui.verbose:
5758 if ui.verbose:
5759 if repo.tagtype(t) == 'local':
5759 if repo.tagtype(t) == 'local':
5760 tagtype = " local"
5760 tagtype = " local"
5761 tag = ui.label(t, 'tags.local')
5761 tag = ui.label(t, 'tags.local')
5762 else:
5762 else:
5763 tagtype = ""
5763 tagtype = ""
5764 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5764 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5765
5765
5766 @command('tip',
5766 @command('tip',
5767 [('p', 'patch', None, _('show patch')),
5767 [('p', 'patch', None, _('show patch')),
5768 ('g', 'git', None, _('use git extended diff format')),
5768 ('g', 'git', None, _('use git extended diff format')),
5769 ] + templateopts,
5769 ] + templateopts,
5770 _('[-p] [-g]'))
5770 _('[-p] [-g]'))
5771 def tip(ui, repo, **opts):
5771 def tip(ui, repo, **opts):
5772 """show the tip revision
5772 """show the tip revision
5773
5773
5774 The tip revision (usually just called the tip) is the changeset
5774 The tip revision (usually just called the tip) is the changeset
5775 most recently added to the repository (and therefore the most
5775 most recently added to the repository (and therefore the most
5776 recently changed head).
5776 recently changed head).
5777
5777
5778 If you have just made a commit, that commit will be the tip. If
5778 If you have just made a commit, that commit will be the tip. If
5779 you have just pulled changes from another repository, the tip of
5779 you have just pulled changes from another repository, the tip of
5780 that repository becomes the current tip. The "tip" tag is special
5780 that repository becomes the current tip. The "tip" tag is special
5781 and cannot be renamed or assigned to a different changeset.
5781 and cannot be renamed or assigned to a different changeset.
5782
5782
5783 Returns 0 on success.
5783 Returns 0 on success.
5784 """
5784 """
5785 displayer = cmdutil.show_changeset(ui, repo, opts)
5785 displayer = cmdutil.show_changeset(ui, repo, opts)
5786 displayer.show(repo[len(repo) - 1])
5786 displayer.show(repo[len(repo) - 1])
5787 displayer.close()
5787 displayer.close()
5788
5788
5789 @command('unbundle',
5789 @command('unbundle',
5790 [('u', 'update', None,
5790 [('u', 'update', None,
5791 _('update to new branch head if changesets were unbundled'))],
5791 _('update to new branch head if changesets were unbundled'))],
5792 _('[-u] FILE...'))
5792 _('[-u] FILE...'))
5793 def unbundle(ui, repo, fname1, *fnames, **opts):
5793 def unbundle(ui, repo, fname1, *fnames, **opts):
5794 """apply one or more changegroup files
5794 """apply one or more changegroup files
5795
5795
5796 Apply one or more compressed changegroup files generated by the
5796 Apply one or more compressed changegroup files generated by the
5797 bundle command.
5797 bundle command.
5798
5798
5799 Returns 0 on success, 1 if an update has unresolved files.
5799 Returns 0 on success, 1 if an update has unresolved files.
5800 """
5800 """
5801 fnames = (fname1,) + fnames
5801 fnames = (fname1,) + fnames
5802
5802
5803 lock = repo.lock()
5803 lock = repo.lock()
5804 wc = repo['.']
5804 wc = repo['.']
5805 try:
5805 try:
5806 for fname in fnames:
5806 for fname in fnames:
5807 f = url.open(ui, fname)
5807 f = hg.openpath(ui, fname)
5808 gen = changegroup.readbundle(f, fname)
5808 gen = changegroup.readbundle(f, fname)
5809 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5809 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
5810 finally:
5810 finally:
5811 lock.release()
5811 lock.release()
5812 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5812 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5813 return postincoming(ui, repo, modheads, opts.get('update'), None)
5813 return postincoming(ui, repo, modheads, opts.get('update'), None)
5814
5814
5815 @command('^update|up|checkout|co',
5815 @command('^update|up|checkout|co',
5816 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5816 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5817 ('c', 'check', None,
5817 ('c', 'check', None,
5818 _('update across branches if no uncommitted changes')),
5818 _('update across branches if no uncommitted changes')),
5819 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5819 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5820 ('r', 'rev', '', _('revision'), _('REV'))],
5820 ('r', 'rev', '', _('revision'), _('REV'))],
5821 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5821 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5822 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5822 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5823 """update working directory (or switch revisions)
5823 """update working directory (or switch revisions)
5824
5824
5825 Update the repository's working directory to the specified
5825 Update the repository's working directory to the specified
5826 changeset. If no changeset is specified, update to the tip of the
5826 changeset. If no changeset is specified, update to the tip of the
5827 current named branch and move the current bookmark (see :hg:`help
5827 current named branch and move the current bookmark (see :hg:`help
5828 bookmarks`).
5828 bookmarks`).
5829
5829
5830 Update sets the working directory's parent revision to the specified
5830 Update sets the working directory's parent revision to the specified
5831 changeset (see :hg:`help parents`).
5831 changeset (see :hg:`help parents`).
5832
5832
5833 If the changeset is not a descendant or ancestor of the working
5833 If the changeset is not a descendant or ancestor of the working
5834 directory's parent, the update is aborted. With the -c/--check
5834 directory's parent, the update is aborted. With the -c/--check
5835 option, the working directory is checked for uncommitted changes; if
5835 option, the working directory is checked for uncommitted changes; if
5836 none are found, the working directory is updated to the specified
5836 none are found, the working directory is updated to the specified
5837 changeset.
5837 changeset.
5838
5838
5839 .. container:: verbose
5839 .. container:: verbose
5840
5840
5841 The following rules apply when the working directory contains
5841 The following rules apply when the working directory contains
5842 uncommitted changes:
5842 uncommitted changes:
5843
5843
5844 1. If neither -c/--check nor -C/--clean is specified, and if
5844 1. If neither -c/--check nor -C/--clean is specified, and if
5845 the requested changeset is an ancestor or descendant of
5845 the requested changeset is an ancestor or descendant of
5846 the working directory's parent, the uncommitted changes
5846 the working directory's parent, the uncommitted changes
5847 are merged into the requested changeset and the merged
5847 are merged into the requested changeset and the merged
5848 result is left uncommitted. If the requested changeset is
5848 result is left uncommitted. If the requested changeset is
5849 not an ancestor or descendant (that is, it is on another
5849 not an ancestor or descendant (that is, it is on another
5850 branch), the update is aborted and the uncommitted changes
5850 branch), the update is aborted and the uncommitted changes
5851 are preserved.
5851 are preserved.
5852
5852
5853 2. With the -c/--check option, the update is aborted and the
5853 2. With the -c/--check option, the update is aborted and the
5854 uncommitted changes are preserved.
5854 uncommitted changes are preserved.
5855
5855
5856 3. With the -C/--clean option, uncommitted changes are discarded and
5856 3. With the -C/--clean option, uncommitted changes are discarded and
5857 the working directory is updated to the requested changeset.
5857 the working directory is updated to the requested changeset.
5858
5858
5859 To cancel an uncommitted merge (and lose your changes), use
5859 To cancel an uncommitted merge (and lose your changes), use
5860 :hg:`update --clean .`.
5860 :hg:`update --clean .`.
5861
5861
5862 Use null as the changeset to remove the working directory (like
5862 Use null as the changeset to remove the working directory (like
5863 :hg:`clone -U`).
5863 :hg:`clone -U`).
5864
5864
5865 If you want to revert just one file to an older revision, use
5865 If you want to revert just one file to an older revision, use
5866 :hg:`revert [-r REV] NAME`.
5866 :hg:`revert [-r REV] NAME`.
5867
5867
5868 See :hg:`help dates` for a list of formats valid for -d/--date.
5868 See :hg:`help dates` for a list of formats valid for -d/--date.
5869
5869
5870 Returns 0 on success, 1 if there are unresolved files.
5870 Returns 0 on success, 1 if there are unresolved files.
5871 """
5871 """
5872 if rev and node:
5872 if rev and node:
5873 raise util.Abort(_("please specify just one revision"))
5873 raise util.Abort(_("please specify just one revision"))
5874
5874
5875 if rev is None or rev == '':
5875 if rev is None or rev == '':
5876 rev = node
5876 rev = node
5877
5877
5878 # with no argument, we also move the current bookmark, if any
5878 # with no argument, we also move the current bookmark, if any
5879 movemarkfrom = None
5879 movemarkfrom = None
5880 if rev is None:
5880 if rev is None:
5881 movemarkfrom = repo['.'].node()
5881 movemarkfrom = repo['.'].node()
5882
5882
5883 # if we defined a bookmark, we have to remember the original bookmark name
5883 # if we defined a bookmark, we have to remember the original bookmark name
5884 brev = rev
5884 brev = rev
5885 rev = scmutil.revsingle(repo, rev, rev).rev()
5885 rev = scmutil.revsingle(repo, rev, rev).rev()
5886
5886
5887 if check and clean:
5887 if check and clean:
5888 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5888 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5889
5889
5890 if date:
5890 if date:
5891 if rev is not None:
5891 if rev is not None:
5892 raise util.Abort(_("you can't specify a revision and a date"))
5892 raise util.Abort(_("you can't specify a revision and a date"))
5893 rev = cmdutil.finddate(ui, repo, date)
5893 rev = cmdutil.finddate(ui, repo, date)
5894
5894
5895 if check:
5895 if check:
5896 c = repo[None]
5896 c = repo[None]
5897 if c.dirty(merge=False, branch=False, missing=True):
5897 if c.dirty(merge=False, branch=False, missing=True):
5898 raise util.Abort(_("uncommitted local changes"))
5898 raise util.Abort(_("uncommitted local changes"))
5899 if rev is None:
5899 if rev is None:
5900 rev = repo[repo[None].branch()].rev()
5900 rev = repo[repo[None].branch()].rev()
5901 mergemod._checkunknown(repo, repo[None], repo[rev])
5901 mergemod._checkunknown(repo, repo[None], repo[rev])
5902
5902
5903 if clean:
5903 if clean:
5904 ret = hg.clean(repo, rev)
5904 ret = hg.clean(repo, rev)
5905 else:
5905 else:
5906 ret = hg.update(repo, rev)
5906 ret = hg.update(repo, rev)
5907
5907
5908 if not ret and movemarkfrom:
5908 if not ret and movemarkfrom:
5909 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5909 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
5910 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5910 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
5911 elif brev in repo._bookmarks:
5911 elif brev in repo._bookmarks:
5912 bookmarks.setcurrent(repo, brev)
5912 bookmarks.setcurrent(repo, brev)
5913 elif brev:
5913 elif brev:
5914 bookmarks.unsetcurrent(repo)
5914 bookmarks.unsetcurrent(repo)
5915
5915
5916 return ret
5916 return ret
5917
5917
5918 @command('verify', [])
5918 @command('verify', [])
5919 def verify(ui, repo):
5919 def verify(ui, repo):
5920 """verify the integrity of the repository
5920 """verify the integrity of the repository
5921
5921
5922 Verify the integrity of the current repository.
5922 Verify the integrity of the current repository.
5923
5923
5924 This will perform an extensive check of the repository's
5924 This will perform an extensive check of the repository's
5925 integrity, validating the hashes and checksums of each entry in
5925 integrity, validating the hashes and checksums of each entry in
5926 the changelog, manifest, and tracked files, as well as the
5926 the changelog, manifest, and tracked files, as well as the
5927 integrity of their crosslinks and indices.
5927 integrity of their crosslinks and indices.
5928
5928
5929 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
5929 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
5930 for more information about recovery from corruption of the
5930 for more information about recovery from corruption of the
5931 repository.
5931 repository.
5932
5932
5933 Returns 0 on success, 1 if errors are encountered.
5933 Returns 0 on success, 1 if errors are encountered.
5934 """
5934 """
5935 return hg.verify(repo)
5935 return hg.verify(repo)
5936
5936
5937 @command('version', [])
5937 @command('version', [])
5938 def version_(ui):
5938 def version_(ui):
5939 """output version and copyright information"""
5939 """output version and copyright information"""
5940 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5940 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5941 % util.version())
5941 % util.version())
5942 ui.status(_(
5942 ui.status(_(
5943 "(see http://mercurial.selenic.com for more information)\n"
5943 "(see http://mercurial.selenic.com for more information)\n"
5944 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
5944 "\nCopyright (C) 2005-2012 Matt Mackall and others\n"
5945 "This is free software; see the source for copying conditions. "
5945 "This is free software; see the source for copying conditions. "
5946 "There is NO\nwarranty; "
5946 "There is NO\nwarranty; "
5947 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5947 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5948 ))
5948 ))
5949
5949
5950 norepo = ("clone init version help debugcommands debugcomplete"
5950 norepo = ("clone init version help debugcommands debugcomplete"
5951 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5951 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5952 " debugknown debuggetbundle debugbundle")
5952 " debugknown debuggetbundle debugbundle")
5953 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5953 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5954 " debugdata debugindex debugindexdot debugrevlog")
5954 " debugdata debugindex debugindexdot debugrevlog")
5955 inferrepo = ("add addremove annotate cat commit diff grep forget log parents"
5955 inferrepo = ("add addremove annotate cat commit diff grep forget log parents"
5956 " remove resolve status debugwalk")
5956 " remove resolve status debugwalk")
@@ -1,627 +1,634 b''
1 # hg.py - repository classes for mercurial
1 # hg.py - repository classes for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 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 i18n import _
9 from i18n import _
10 from lock import release
10 from lock import release
11 from node import hex, nullid
11 from node import hex, nullid
12 import localrepo, bundlerepo, httppeer, sshpeer, statichttprepo, bookmarks
12 import localrepo, bundlerepo, httppeer, sshpeer, statichttprepo, bookmarks
13 import lock, util, extensions, error, node, scmutil, phases
13 import lock, util, extensions, error, node, scmutil, phases, url
14 import cmdutil, discovery
14 import cmdutil, discovery
15 import merge as mergemod
15 import merge as mergemod
16 import verify as verifymod
16 import verify as verifymod
17 import errno, os, shutil
17 import errno, os, shutil
18
18
19 def _local(path):
19 def _local(path):
20 path = util.expandpath(util.urllocalpath(path))
20 path = util.expandpath(util.urllocalpath(path))
21 return (os.path.isfile(path) and bundlerepo or localrepo)
21 return (os.path.isfile(path) and bundlerepo or localrepo)
22
22
23 def addbranchrevs(lrepo, other, branches, revs):
23 def addbranchrevs(lrepo, other, branches, revs):
24 peer = other.peer() # a courtesy to callers using a localrepo for other
24 peer = other.peer() # a courtesy to callers using a localrepo for other
25 hashbranch, branches = branches
25 hashbranch, branches = branches
26 if not hashbranch and not branches:
26 if not hashbranch and not branches:
27 return revs or None, revs and revs[0] or None
27 return revs or None, revs and revs[0] or None
28 revs = revs and list(revs) or []
28 revs = revs and list(revs) or []
29 if not peer.capable('branchmap'):
29 if not peer.capable('branchmap'):
30 if branches:
30 if branches:
31 raise util.Abort(_("remote branch lookup not supported"))
31 raise util.Abort(_("remote branch lookup not supported"))
32 revs.append(hashbranch)
32 revs.append(hashbranch)
33 return revs, revs[0]
33 return revs, revs[0]
34 branchmap = peer.branchmap()
34 branchmap = peer.branchmap()
35
35
36 def primary(branch):
36 def primary(branch):
37 if branch == '.':
37 if branch == '.':
38 if not lrepo:
38 if not lrepo:
39 raise util.Abort(_("dirstate branch not accessible"))
39 raise util.Abort(_("dirstate branch not accessible"))
40 branch = lrepo.dirstate.branch()
40 branch = lrepo.dirstate.branch()
41 if branch in branchmap:
41 if branch in branchmap:
42 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
42 revs.extend(node.hex(r) for r in reversed(branchmap[branch]))
43 return True
43 return True
44 else:
44 else:
45 return False
45 return False
46
46
47 for branch in branches:
47 for branch in branches:
48 if not primary(branch):
48 if not primary(branch):
49 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
49 raise error.RepoLookupError(_("unknown branch '%s'") % branch)
50 if hashbranch:
50 if hashbranch:
51 if not primary(hashbranch):
51 if not primary(hashbranch):
52 revs.append(hashbranch)
52 revs.append(hashbranch)
53 return revs, revs[0]
53 return revs, revs[0]
54
54
55 def parseurl(path, branches=None):
55 def parseurl(path, branches=None):
56 '''parse url#branch, returning (url, (branch, branches))'''
56 '''parse url#branch, returning (url, (branch, branches))'''
57
57
58 u = util.url(path)
58 u = util.url(path)
59 branch = None
59 branch = None
60 if u.fragment:
60 if u.fragment:
61 branch = u.fragment
61 branch = u.fragment
62 u.fragment = None
62 u.fragment = None
63 return str(u), (branch, branches or [])
63 return str(u), (branch, branches or [])
64
64
65 schemes = {
65 schemes = {
66 'bundle': bundlerepo,
66 'bundle': bundlerepo,
67 'file': _local,
67 'file': _local,
68 'http': httppeer,
68 'http': httppeer,
69 'https': httppeer,
69 'https': httppeer,
70 'ssh': sshpeer,
70 'ssh': sshpeer,
71 'static-http': statichttprepo,
71 'static-http': statichttprepo,
72 }
72 }
73
73
74 def _peerlookup(path):
74 def _peerlookup(path):
75 u = util.url(path)
75 u = util.url(path)
76 scheme = u.scheme or 'file'
76 scheme = u.scheme or 'file'
77 thing = schemes.get(scheme) or schemes['file']
77 thing = schemes.get(scheme) or schemes['file']
78 try:
78 try:
79 return thing(path)
79 return thing(path)
80 except TypeError:
80 except TypeError:
81 return thing
81 return thing
82
82
83 def islocal(repo):
83 def islocal(repo):
84 '''return true if repo or path is local'''
84 '''return true if repo or path is local'''
85 if isinstance(repo, str):
85 if isinstance(repo, str):
86 try:
86 try:
87 return _peerlookup(repo).islocal(repo)
87 return _peerlookup(repo).islocal(repo)
88 except AttributeError:
88 except AttributeError:
89 return False
89 return False
90 return repo.local()
90 return repo.local()
91
91
92 def openpath(ui, path):
93 '''open path with open if local, url.open if remote'''
94 if islocal(path):
95 return open(util.urllocalpath(path))
96 else:
97 return url.open(ui, path)
98
92 def _peerorrepo(ui, path, create=False):
99 def _peerorrepo(ui, path, create=False):
93 """return a repository object for the specified path"""
100 """return a repository object for the specified path"""
94 obj = _peerlookup(path).instance(ui, path, create)
101 obj = _peerlookup(path).instance(ui, path, create)
95 ui = getattr(obj, "ui", ui)
102 ui = getattr(obj, "ui", ui)
96 for name, module in extensions.extensions():
103 for name, module in extensions.extensions():
97 hook = getattr(module, 'reposetup', None)
104 hook = getattr(module, 'reposetup', None)
98 if hook:
105 if hook:
99 hook(ui, obj)
106 hook(ui, obj)
100 return obj
107 return obj
101
108
102 def repository(ui, path='', create=False):
109 def repository(ui, path='', create=False):
103 """return a repository object for the specified path"""
110 """return a repository object for the specified path"""
104 peer = _peerorrepo(ui, path, create)
111 peer = _peerorrepo(ui, path, create)
105 repo = peer.local()
112 repo = peer.local()
106 if not repo:
113 if not repo:
107 raise util.Abort(_("repository '%s' is not local") %
114 raise util.Abort(_("repository '%s' is not local") %
108 (path or peer.url()))
115 (path or peer.url()))
109 return repo
116 return repo
110
117
111 def peer(uiorrepo, opts, path, create=False):
118 def peer(uiorrepo, opts, path, create=False):
112 '''return a repository peer for the specified path'''
119 '''return a repository peer for the specified path'''
113 rui = remoteui(uiorrepo, opts)
120 rui = remoteui(uiorrepo, opts)
114 return _peerorrepo(rui, path, create).peer()
121 return _peerorrepo(rui, path, create).peer()
115
122
116 def defaultdest(source):
123 def defaultdest(source):
117 '''return default destination of clone if none is given'''
124 '''return default destination of clone if none is given'''
118 return os.path.basename(os.path.normpath(util.url(source).path))
125 return os.path.basename(os.path.normpath(util.url(source).path))
119
126
120 def share(ui, source, dest=None, update=True):
127 def share(ui, source, dest=None, update=True):
121 '''create a shared repository'''
128 '''create a shared repository'''
122
129
123 if not islocal(source):
130 if not islocal(source):
124 raise util.Abort(_('can only share local repositories'))
131 raise util.Abort(_('can only share local repositories'))
125
132
126 if not dest:
133 if not dest:
127 dest = defaultdest(source)
134 dest = defaultdest(source)
128 else:
135 else:
129 dest = ui.expandpath(dest)
136 dest = ui.expandpath(dest)
130
137
131 if isinstance(source, str):
138 if isinstance(source, str):
132 origsource = ui.expandpath(source)
139 origsource = ui.expandpath(source)
133 source, branches = parseurl(origsource)
140 source, branches = parseurl(origsource)
134 srcrepo = repository(ui, source)
141 srcrepo = repository(ui, source)
135 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
142 rev, checkout = addbranchrevs(srcrepo, srcrepo, branches, None)
136 else:
143 else:
137 srcrepo = source.local()
144 srcrepo = source.local()
138 origsource = source = srcrepo.url()
145 origsource = source = srcrepo.url()
139 checkout = None
146 checkout = None
140
147
141 sharedpath = srcrepo.sharedpath # if our source is already sharing
148 sharedpath = srcrepo.sharedpath # if our source is already sharing
142
149
143 root = os.path.realpath(dest)
150 root = os.path.realpath(dest)
144 roothg = os.path.join(root, '.hg')
151 roothg = os.path.join(root, '.hg')
145
152
146 if os.path.exists(roothg):
153 if os.path.exists(roothg):
147 raise util.Abort(_('destination already exists'))
154 raise util.Abort(_('destination already exists'))
148
155
149 if not os.path.isdir(root):
156 if not os.path.isdir(root):
150 os.mkdir(root)
157 os.mkdir(root)
151 util.makedir(roothg, notindexed=True)
158 util.makedir(roothg, notindexed=True)
152
159
153 requirements = ''
160 requirements = ''
154 try:
161 try:
155 requirements = srcrepo.opener.read('requires')
162 requirements = srcrepo.opener.read('requires')
156 except IOError, inst:
163 except IOError, inst:
157 if inst.errno != errno.ENOENT:
164 if inst.errno != errno.ENOENT:
158 raise
165 raise
159
166
160 requirements += 'shared\n'
167 requirements += 'shared\n'
161 util.writefile(os.path.join(roothg, 'requires'), requirements)
168 util.writefile(os.path.join(roothg, 'requires'), requirements)
162 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
169 util.writefile(os.path.join(roothg, 'sharedpath'), sharedpath)
163
170
164 r = repository(ui, root)
171 r = repository(ui, root)
165
172
166 default = srcrepo.ui.config('paths', 'default')
173 default = srcrepo.ui.config('paths', 'default')
167 if default:
174 if default:
168 fp = r.opener("hgrc", "w", text=True)
175 fp = r.opener("hgrc", "w", text=True)
169 fp.write("[paths]\n")
176 fp.write("[paths]\n")
170 fp.write("default = %s\n" % default)
177 fp.write("default = %s\n" % default)
171 fp.close()
178 fp.close()
172
179
173 if update:
180 if update:
174 r.ui.status(_("updating working directory\n"))
181 r.ui.status(_("updating working directory\n"))
175 if update is not True:
182 if update is not True:
176 checkout = update
183 checkout = update
177 for test in (checkout, 'default', 'tip'):
184 for test in (checkout, 'default', 'tip'):
178 if test is None:
185 if test is None:
179 continue
186 continue
180 try:
187 try:
181 uprev = r.lookup(test)
188 uprev = r.lookup(test)
182 break
189 break
183 except error.RepoLookupError:
190 except error.RepoLookupError:
184 continue
191 continue
185 _update(r, uprev)
192 _update(r, uprev)
186
193
187 def copystore(ui, srcrepo, destpath):
194 def copystore(ui, srcrepo, destpath):
188 '''copy files from store of srcrepo in destpath
195 '''copy files from store of srcrepo in destpath
189
196
190 returns destlock
197 returns destlock
191 '''
198 '''
192 destlock = None
199 destlock = None
193 try:
200 try:
194 hardlink = None
201 hardlink = None
195 num = 0
202 num = 0
196 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
203 srcpublishing = srcrepo.ui.configbool('phases', 'publish', True)
197 for f in srcrepo.store.copylist():
204 for f in srcrepo.store.copylist():
198 if srcpublishing and f.endswith('phaseroots'):
205 if srcpublishing and f.endswith('phaseroots'):
199 continue
206 continue
200 src = os.path.join(srcrepo.sharedpath, f)
207 src = os.path.join(srcrepo.sharedpath, f)
201 dst = os.path.join(destpath, f)
208 dst = os.path.join(destpath, f)
202 dstbase = os.path.dirname(dst)
209 dstbase = os.path.dirname(dst)
203 if dstbase and not os.path.exists(dstbase):
210 if dstbase and not os.path.exists(dstbase):
204 os.mkdir(dstbase)
211 os.mkdir(dstbase)
205 if os.path.exists(src):
212 if os.path.exists(src):
206 if dst.endswith('data'):
213 if dst.endswith('data'):
207 # lock to avoid premature writing to the target
214 # lock to avoid premature writing to the target
208 destlock = lock.lock(os.path.join(dstbase, "lock"))
215 destlock = lock.lock(os.path.join(dstbase, "lock"))
209 hardlink, n = util.copyfiles(src, dst, hardlink)
216 hardlink, n = util.copyfiles(src, dst, hardlink)
210 num += n
217 num += n
211 if hardlink:
218 if hardlink:
212 ui.debug("linked %d files\n" % num)
219 ui.debug("linked %d files\n" % num)
213 else:
220 else:
214 ui.debug("copied %d files\n" % num)
221 ui.debug("copied %d files\n" % num)
215 return destlock
222 return destlock
216 except: # re-raises
223 except: # re-raises
217 release(destlock)
224 release(destlock)
218 raise
225 raise
219
226
220 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
227 def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
221 update=True, stream=False, branch=None):
228 update=True, stream=False, branch=None):
222 """Make a copy of an existing repository.
229 """Make a copy of an existing repository.
223
230
224 Create a copy of an existing repository in a new directory. The
231 Create a copy of an existing repository in a new directory. The
225 source and destination are URLs, as passed to the repository
232 source and destination are URLs, as passed to the repository
226 function. Returns a pair of repository peers, the source and
233 function. Returns a pair of repository peers, the source and
227 newly created destination.
234 newly created destination.
228
235
229 The location of the source is added to the new repository's
236 The location of the source is added to the new repository's
230 .hg/hgrc file, as the default to be used for future pulls and
237 .hg/hgrc file, as the default to be used for future pulls and
231 pushes.
238 pushes.
232
239
233 If an exception is raised, the partly cloned/updated destination
240 If an exception is raised, the partly cloned/updated destination
234 repository will be deleted.
241 repository will be deleted.
235
242
236 Arguments:
243 Arguments:
237
244
238 source: repository object or URL
245 source: repository object or URL
239
246
240 dest: URL of destination repository to create (defaults to base
247 dest: URL of destination repository to create (defaults to base
241 name of source repository)
248 name of source repository)
242
249
243 pull: always pull from source repository, even in local case
250 pull: always pull from source repository, even in local case
244
251
245 stream: stream raw data uncompressed from repository (fast over
252 stream: stream raw data uncompressed from repository (fast over
246 LAN, slow over WAN)
253 LAN, slow over WAN)
247
254
248 rev: revision to clone up to (implies pull=True)
255 rev: revision to clone up to (implies pull=True)
249
256
250 update: update working directory after clone completes, if
257 update: update working directory after clone completes, if
251 destination is local repository (True means update to default rev,
258 destination is local repository (True means update to default rev,
252 anything else is treated as a revision)
259 anything else is treated as a revision)
253
260
254 branch: branches to clone
261 branch: branches to clone
255 """
262 """
256
263
257 if isinstance(source, str):
264 if isinstance(source, str):
258 origsource = ui.expandpath(source)
265 origsource = ui.expandpath(source)
259 source, branch = parseurl(origsource, branch)
266 source, branch = parseurl(origsource, branch)
260 srcpeer = peer(ui, peeropts, source)
267 srcpeer = peer(ui, peeropts, source)
261 else:
268 else:
262 srcpeer = source.peer() # in case we were called with a localrepo
269 srcpeer = source.peer() # in case we were called with a localrepo
263 branch = (None, branch or [])
270 branch = (None, branch or [])
264 origsource = source = srcpeer.url()
271 origsource = source = srcpeer.url()
265 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
272 rev, checkout = addbranchrevs(srcpeer, srcpeer, branch, rev)
266
273
267 if dest is None:
274 if dest is None:
268 dest = defaultdest(source)
275 dest = defaultdest(source)
269 ui.status(_("destination directory: %s\n") % dest)
276 ui.status(_("destination directory: %s\n") % dest)
270 else:
277 else:
271 dest = ui.expandpath(dest)
278 dest = ui.expandpath(dest)
272
279
273 dest = util.urllocalpath(dest)
280 dest = util.urllocalpath(dest)
274 source = util.urllocalpath(source)
281 source = util.urllocalpath(source)
275
282
276 if not dest:
283 if not dest:
277 raise util.Abort(_("empty destination path is not valid"))
284 raise util.Abort(_("empty destination path is not valid"))
278 if os.path.exists(dest):
285 if os.path.exists(dest):
279 if not os.path.isdir(dest):
286 if not os.path.isdir(dest):
280 raise util.Abort(_("destination '%s' already exists") % dest)
287 raise util.Abort(_("destination '%s' already exists") % dest)
281 elif os.listdir(dest):
288 elif os.listdir(dest):
282 raise util.Abort(_("destination '%s' is not empty") % dest)
289 raise util.Abort(_("destination '%s' is not empty") % dest)
283
290
284 class DirCleanup(object):
291 class DirCleanup(object):
285 def __init__(self, dir_):
292 def __init__(self, dir_):
286 self.rmtree = shutil.rmtree
293 self.rmtree = shutil.rmtree
287 self.dir_ = dir_
294 self.dir_ = dir_
288 def close(self):
295 def close(self):
289 self.dir_ = None
296 self.dir_ = None
290 def cleanup(self):
297 def cleanup(self):
291 if self.dir_:
298 if self.dir_:
292 self.rmtree(self.dir_, True)
299 self.rmtree(self.dir_, True)
293
300
294 srclock = destlock = dircleanup = None
301 srclock = destlock = dircleanup = None
295 srcrepo = srcpeer.local()
302 srcrepo = srcpeer.local()
296 try:
303 try:
297 abspath = origsource
304 abspath = origsource
298 if islocal(origsource):
305 if islocal(origsource):
299 abspath = os.path.abspath(util.urllocalpath(origsource))
306 abspath = os.path.abspath(util.urllocalpath(origsource))
300
307
301 if islocal(dest):
308 if islocal(dest):
302 dircleanup = DirCleanup(dest)
309 dircleanup = DirCleanup(dest)
303
310
304 copy = False
311 copy = False
305 if (srcrepo and srcrepo.cancopy() and islocal(dest)
312 if (srcrepo and srcrepo.cancopy() and islocal(dest)
306 and not phases.hassecret(srcrepo)):
313 and not phases.hassecret(srcrepo)):
307 copy = not pull and not rev
314 copy = not pull and not rev
308
315
309 if copy:
316 if copy:
310 try:
317 try:
311 # we use a lock here because if we race with commit, we
318 # we use a lock here because if we race with commit, we
312 # can end up with extra data in the cloned revlogs that's
319 # can end up with extra data in the cloned revlogs that's
313 # not pointed to by changesets, thus causing verify to
320 # not pointed to by changesets, thus causing verify to
314 # fail
321 # fail
315 srclock = srcrepo.lock(wait=False)
322 srclock = srcrepo.lock(wait=False)
316 except error.LockError:
323 except error.LockError:
317 copy = False
324 copy = False
318
325
319 if copy:
326 if copy:
320 srcrepo.hook('preoutgoing', throw=True, source='clone')
327 srcrepo.hook('preoutgoing', throw=True, source='clone')
321 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
328 hgdir = os.path.realpath(os.path.join(dest, ".hg"))
322 if not os.path.exists(dest):
329 if not os.path.exists(dest):
323 os.mkdir(dest)
330 os.mkdir(dest)
324 else:
331 else:
325 # only clean up directories we create ourselves
332 # only clean up directories we create ourselves
326 dircleanup.dir_ = hgdir
333 dircleanup.dir_ = hgdir
327 try:
334 try:
328 destpath = hgdir
335 destpath = hgdir
329 util.makedir(destpath, notindexed=True)
336 util.makedir(destpath, notindexed=True)
330 except OSError, inst:
337 except OSError, inst:
331 if inst.errno == errno.EEXIST:
338 if inst.errno == errno.EEXIST:
332 dircleanup.close()
339 dircleanup.close()
333 raise util.Abort(_("destination '%s' already exists")
340 raise util.Abort(_("destination '%s' already exists")
334 % dest)
341 % dest)
335 raise
342 raise
336
343
337 destlock = copystore(ui, srcrepo, destpath)
344 destlock = copystore(ui, srcrepo, destpath)
338
345
339 # Recomputing branch cache might be slow on big repos,
346 # Recomputing branch cache might be slow on big repos,
340 # so just copy it
347 # so just copy it
341 dstcachedir = os.path.join(destpath, 'cache')
348 dstcachedir = os.path.join(destpath, 'cache')
342 srcbranchcache = srcrepo.sjoin('cache/branchheads')
349 srcbranchcache = srcrepo.sjoin('cache/branchheads')
343 dstbranchcache = os.path.join(dstcachedir, 'branchheads')
350 dstbranchcache = os.path.join(dstcachedir, 'branchheads')
344 if os.path.exists(srcbranchcache):
351 if os.path.exists(srcbranchcache):
345 if not os.path.exists(dstcachedir):
352 if not os.path.exists(dstcachedir):
346 os.mkdir(dstcachedir)
353 os.mkdir(dstcachedir)
347 util.copyfile(srcbranchcache, dstbranchcache)
354 util.copyfile(srcbranchcache, dstbranchcache)
348
355
349 # we need to re-init the repo after manually copying the data
356 # we need to re-init the repo after manually copying the data
350 # into it
357 # into it
351 destpeer = peer(srcrepo, peeropts, dest)
358 destpeer = peer(srcrepo, peeropts, dest)
352 srcrepo.hook('outgoing', source='clone',
359 srcrepo.hook('outgoing', source='clone',
353 node=node.hex(node.nullid))
360 node=node.hex(node.nullid))
354 else:
361 else:
355 try:
362 try:
356 destpeer = peer(srcrepo or ui, peeropts, dest, create=True)
363 destpeer = peer(srcrepo or ui, peeropts, dest, create=True)
357 # only pass ui when no srcrepo
364 # only pass ui when no srcrepo
358 except OSError, inst:
365 except OSError, inst:
359 if inst.errno == errno.EEXIST:
366 if inst.errno == errno.EEXIST:
360 dircleanup.close()
367 dircleanup.close()
361 raise util.Abort(_("destination '%s' already exists")
368 raise util.Abort(_("destination '%s' already exists")
362 % dest)
369 % dest)
363 raise
370 raise
364
371
365 revs = None
372 revs = None
366 if rev:
373 if rev:
367 if not srcpeer.capable('lookup'):
374 if not srcpeer.capable('lookup'):
368 raise util.Abort(_("src repository does not support "
375 raise util.Abort(_("src repository does not support "
369 "revision lookup and so doesn't "
376 "revision lookup and so doesn't "
370 "support clone by revision"))
377 "support clone by revision"))
371 revs = [srcpeer.lookup(r) for r in rev]
378 revs = [srcpeer.lookup(r) for r in rev]
372 checkout = revs[0]
379 checkout = revs[0]
373 if destpeer.local():
380 if destpeer.local():
374 destpeer.local().clone(srcpeer, heads=revs, stream=stream)
381 destpeer.local().clone(srcpeer, heads=revs, stream=stream)
375 elif srcrepo:
382 elif srcrepo:
376 srcrepo.push(destpeer, revs=revs)
383 srcrepo.push(destpeer, revs=revs)
377 else:
384 else:
378 raise util.Abort(_("clone from remote to remote not supported"))
385 raise util.Abort(_("clone from remote to remote not supported"))
379
386
380 if dircleanup:
387 if dircleanup:
381 dircleanup.close()
388 dircleanup.close()
382
389
383 # clone all bookmarks except divergent ones
390 # clone all bookmarks except divergent ones
384 destrepo = destpeer.local()
391 destrepo = destpeer.local()
385 if destrepo and srcpeer.capable("pushkey"):
392 if destrepo and srcpeer.capable("pushkey"):
386 rb = srcpeer.listkeys('bookmarks')
393 rb = srcpeer.listkeys('bookmarks')
387 for k, n in rb.iteritems():
394 for k, n in rb.iteritems():
388 try:
395 try:
389 m = destrepo.lookup(n)
396 m = destrepo.lookup(n)
390 destrepo._bookmarks[k] = m
397 destrepo._bookmarks[k] = m
391 except error.RepoLookupError:
398 except error.RepoLookupError:
392 pass
399 pass
393 if rb:
400 if rb:
394 bookmarks.write(destrepo)
401 bookmarks.write(destrepo)
395 elif srcrepo and destpeer.capable("pushkey"):
402 elif srcrepo and destpeer.capable("pushkey"):
396 for k, n in srcrepo._bookmarks.iteritems():
403 for k, n in srcrepo._bookmarks.iteritems():
397 destpeer.pushkey('bookmarks', k, '', hex(n))
404 destpeer.pushkey('bookmarks', k, '', hex(n))
398
405
399 if destrepo:
406 if destrepo:
400 fp = destrepo.opener("hgrc", "w", text=True)
407 fp = destrepo.opener("hgrc", "w", text=True)
401 fp.write("[paths]\n")
408 fp.write("[paths]\n")
402 u = util.url(abspath)
409 u = util.url(abspath)
403 u.passwd = None
410 u.passwd = None
404 defaulturl = str(u)
411 defaulturl = str(u)
405 fp.write("default = %s\n" % defaulturl)
412 fp.write("default = %s\n" % defaulturl)
406 fp.close()
413 fp.close()
407
414
408 destrepo.ui.setconfig('paths', 'default', defaulturl)
415 destrepo.ui.setconfig('paths', 'default', defaulturl)
409
416
410 if update:
417 if update:
411 if update is not True:
418 if update is not True:
412 checkout = srcpeer.lookup(update)
419 checkout = srcpeer.lookup(update)
413 uprev = None
420 uprev = None
414 status = None
421 status = None
415 if checkout is not None:
422 if checkout is not None:
416 try:
423 try:
417 uprev = destrepo.lookup(checkout)
424 uprev = destrepo.lookup(checkout)
418 except error.RepoLookupError:
425 except error.RepoLookupError:
419 pass
426 pass
420 if uprev is None:
427 if uprev is None:
421 try:
428 try:
422 uprev = destrepo._bookmarks['@']
429 uprev = destrepo._bookmarks['@']
423 update = '@'
430 update = '@'
424 bn = destrepo[uprev].branch()
431 bn = destrepo[uprev].branch()
425 if bn == 'default':
432 if bn == 'default':
426 status = _("updating to bookmark @\n")
433 status = _("updating to bookmark @\n")
427 else:
434 else:
428 status = _("updating to bookmark @ on branch %s\n"
435 status = _("updating to bookmark @ on branch %s\n"
429 % bn)
436 % bn)
430 except KeyError:
437 except KeyError:
431 try:
438 try:
432 uprev = destrepo.branchtip('default')
439 uprev = destrepo.branchtip('default')
433 except error.RepoLookupError:
440 except error.RepoLookupError:
434 uprev = destrepo.lookup('tip')
441 uprev = destrepo.lookup('tip')
435 if not status:
442 if not status:
436 bn = destrepo[uprev].branch()
443 bn = destrepo[uprev].branch()
437 status = _("updating to branch %s\n") % bn
444 status = _("updating to branch %s\n") % bn
438 destrepo.ui.status(status)
445 destrepo.ui.status(status)
439 _update(destrepo, uprev)
446 _update(destrepo, uprev)
440 if update in destrepo._bookmarks:
447 if update in destrepo._bookmarks:
441 bookmarks.setcurrent(destrepo, update)
448 bookmarks.setcurrent(destrepo, update)
442
449
443 return srcpeer, destpeer
450 return srcpeer, destpeer
444 finally:
451 finally:
445 release(srclock, destlock)
452 release(srclock, destlock)
446 if dircleanup is not None:
453 if dircleanup is not None:
447 dircleanup.cleanup()
454 dircleanup.cleanup()
448 if srcpeer is not None:
455 if srcpeer is not None:
449 srcpeer.close()
456 srcpeer.close()
450
457
451 def _showstats(repo, stats):
458 def _showstats(repo, stats):
452 repo.ui.status(_("%d files updated, %d files merged, "
459 repo.ui.status(_("%d files updated, %d files merged, "
453 "%d files removed, %d files unresolved\n") % stats)
460 "%d files removed, %d files unresolved\n") % stats)
454
461
455 def update(repo, node):
462 def update(repo, node):
456 """update the working directory to node, merging linear changes"""
463 """update the working directory to node, merging linear changes"""
457 stats = mergemod.update(repo, node, False, False, None)
464 stats = mergemod.update(repo, node, False, False, None)
458 _showstats(repo, stats)
465 _showstats(repo, stats)
459 if stats[3]:
466 if stats[3]:
460 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
467 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
461 return stats[3] > 0
468 return stats[3] > 0
462
469
463 # naming conflict in clone()
470 # naming conflict in clone()
464 _update = update
471 _update = update
465
472
466 def clean(repo, node, show_stats=True):
473 def clean(repo, node, show_stats=True):
467 """forcibly switch the working directory to node, clobbering changes"""
474 """forcibly switch the working directory to node, clobbering changes"""
468 stats = mergemod.update(repo, node, False, True, None)
475 stats = mergemod.update(repo, node, False, True, None)
469 if show_stats:
476 if show_stats:
470 _showstats(repo, stats)
477 _showstats(repo, stats)
471 return stats[3] > 0
478 return stats[3] > 0
472
479
473 def merge(repo, node, force=None, remind=True):
480 def merge(repo, node, force=None, remind=True):
474 """Branch merge with node, resolving changes. Return true if any
481 """Branch merge with node, resolving changes. Return true if any
475 unresolved conflicts."""
482 unresolved conflicts."""
476 stats = mergemod.update(repo, node, True, force, False)
483 stats = mergemod.update(repo, node, True, force, False)
477 _showstats(repo, stats)
484 _showstats(repo, stats)
478 if stats[3]:
485 if stats[3]:
479 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
486 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
480 "or 'hg update -C .' to abandon\n"))
487 "or 'hg update -C .' to abandon\n"))
481 elif remind:
488 elif remind:
482 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
489 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
483 return stats[3] > 0
490 return stats[3] > 0
484
491
485 def _incoming(displaychlist, subreporecurse, ui, repo, source,
492 def _incoming(displaychlist, subreporecurse, ui, repo, source,
486 opts, buffered=False):
493 opts, buffered=False):
487 """
494 """
488 Helper for incoming / gincoming.
495 Helper for incoming / gincoming.
489 displaychlist gets called with
496 displaychlist gets called with
490 (remoterepo, incomingchangesetlist, displayer) parameters,
497 (remoterepo, incomingchangesetlist, displayer) parameters,
491 and is supposed to contain only code that can't be unified.
498 and is supposed to contain only code that can't be unified.
492 """
499 """
493 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
500 source, branches = parseurl(ui.expandpath(source), opts.get('branch'))
494 other = peer(repo, opts, source)
501 other = peer(repo, opts, source)
495 ui.status(_('comparing with %s\n') % util.hidepassword(source))
502 ui.status(_('comparing with %s\n') % util.hidepassword(source))
496 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
503 revs, checkout = addbranchrevs(repo, other, branches, opts.get('rev'))
497
504
498 if revs:
505 if revs:
499 revs = [other.lookup(rev) for rev in revs]
506 revs = [other.lookup(rev) for rev in revs]
500 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
507 other, chlist, cleanupfn = bundlerepo.getremotechanges(ui, repo, other,
501 revs, opts["bundle"], opts["force"])
508 revs, opts["bundle"], opts["force"])
502 try:
509 try:
503 if not chlist:
510 if not chlist:
504 ui.status(_("no changes found\n"))
511 ui.status(_("no changes found\n"))
505 return subreporecurse()
512 return subreporecurse()
506
513
507 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
514 displayer = cmdutil.show_changeset(ui, other, opts, buffered)
508
515
509 # XXX once graphlog extension makes it into core,
516 # XXX once graphlog extension makes it into core,
510 # should be replaced by a if graph/else
517 # should be replaced by a if graph/else
511 displaychlist(other, chlist, displayer)
518 displaychlist(other, chlist, displayer)
512
519
513 displayer.close()
520 displayer.close()
514 finally:
521 finally:
515 cleanupfn()
522 cleanupfn()
516 subreporecurse()
523 subreporecurse()
517 return 0 # exit code is zero since we found incoming changes
524 return 0 # exit code is zero since we found incoming changes
518
525
519 def incoming(ui, repo, source, opts):
526 def incoming(ui, repo, source, opts):
520 def subreporecurse():
527 def subreporecurse():
521 ret = 1
528 ret = 1
522 if opts.get('subrepos'):
529 if opts.get('subrepos'):
523 ctx = repo[None]
530 ctx = repo[None]
524 for subpath in sorted(ctx.substate):
531 for subpath in sorted(ctx.substate):
525 sub = ctx.sub(subpath)
532 sub = ctx.sub(subpath)
526 ret = min(ret, sub.incoming(ui, source, opts))
533 ret = min(ret, sub.incoming(ui, source, opts))
527 return ret
534 return ret
528
535
529 def display(other, chlist, displayer):
536 def display(other, chlist, displayer):
530 limit = cmdutil.loglimit(opts)
537 limit = cmdutil.loglimit(opts)
531 if opts.get('newest_first'):
538 if opts.get('newest_first'):
532 chlist.reverse()
539 chlist.reverse()
533 count = 0
540 count = 0
534 for n in chlist:
541 for n in chlist:
535 if limit is not None and count >= limit:
542 if limit is not None and count >= limit:
536 break
543 break
537 parents = [p for p in other.changelog.parents(n) if p != nullid]
544 parents = [p for p in other.changelog.parents(n) if p != nullid]
538 if opts.get('no_merges') and len(parents) == 2:
545 if opts.get('no_merges') and len(parents) == 2:
539 continue
546 continue
540 count += 1
547 count += 1
541 displayer.show(other[n])
548 displayer.show(other[n])
542 return _incoming(display, subreporecurse, ui, repo, source, opts)
549 return _incoming(display, subreporecurse, ui, repo, source, opts)
543
550
544 def _outgoing(ui, repo, dest, opts):
551 def _outgoing(ui, repo, dest, opts):
545 dest = ui.expandpath(dest or 'default-push', dest or 'default')
552 dest = ui.expandpath(dest or 'default-push', dest or 'default')
546 dest, branches = parseurl(dest, opts.get('branch'))
553 dest, branches = parseurl(dest, opts.get('branch'))
547 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
554 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
548 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
555 revs, checkout = addbranchrevs(repo, repo, branches, opts.get('rev'))
549 if revs:
556 if revs:
550 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
557 revs = [repo.lookup(rev) for rev in scmutil.revrange(repo, revs)]
551
558
552 other = peer(repo, opts, dest)
559 other = peer(repo, opts, dest)
553 outgoing = discovery.findcommonoutgoing(repo, other, revs,
560 outgoing = discovery.findcommonoutgoing(repo, other, revs,
554 force=opts.get('force'))
561 force=opts.get('force'))
555 o = outgoing.missing
562 o = outgoing.missing
556 if not o:
563 if not o:
557 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
564 scmutil.nochangesfound(repo.ui, repo, outgoing.excluded)
558 return None
565 return None
559 return o
566 return o
560
567
561 def outgoing(ui, repo, dest, opts):
568 def outgoing(ui, repo, dest, opts):
562 def recurse():
569 def recurse():
563 ret = 1
570 ret = 1
564 if opts.get('subrepos'):
571 if opts.get('subrepos'):
565 ctx = repo[None]
572 ctx = repo[None]
566 for subpath in sorted(ctx.substate):
573 for subpath in sorted(ctx.substate):
567 sub = ctx.sub(subpath)
574 sub = ctx.sub(subpath)
568 ret = min(ret, sub.outgoing(ui, dest, opts))
575 ret = min(ret, sub.outgoing(ui, dest, opts))
569 return ret
576 return ret
570
577
571 limit = cmdutil.loglimit(opts)
578 limit = cmdutil.loglimit(opts)
572 o = _outgoing(ui, repo, dest, opts)
579 o = _outgoing(ui, repo, dest, opts)
573 if o is None:
580 if o is None:
574 return recurse()
581 return recurse()
575
582
576 if opts.get('newest_first'):
583 if opts.get('newest_first'):
577 o.reverse()
584 o.reverse()
578 displayer = cmdutil.show_changeset(ui, repo, opts)
585 displayer = cmdutil.show_changeset(ui, repo, opts)
579 count = 0
586 count = 0
580 for n in o:
587 for n in o:
581 if limit is not None and count >= limit:
588 if limit is not None and count >= limit:
582 break
589 break
583 parents = [p for p in repo.changelog.parents(n) if p != nullid]
590 parents = [p for p in repo.changelog.parents(n) if p != nullid]
584 if opts.get('no_merges') and len(parents) == 2:
591 if opts.get('no_merges') and len(parents) == 2:
585 continue
592 continue
586 count += 1
593 count += 1
587 displayer.show(repo[n])
594 displayer.show(repo[n])
588 displayer.close()
595 displayer.close()
589 recurse()
596 recurse()
590 return 0 # exit code is zero since we found outgoing changes
597 return 0 # exit code is zero since we found outgoing changes
591
598
592 def revert(repo, node, choose):
599 def revert(repo, node, choose):
593 """revert changes to revision in node without updating dirstate"""
600 """revert changes to revision in node without updating dirstate"""
594 return mergemod.update(repo, node, False, True, choose)[3] > 0
601 return mergemod.update(repo, node, False, True, choose)[3] > 0
595
602
596 def verify(repo):
603 def verify(repo):
597 """verify the consistency of a repository"""
604 """verify the consistency of a repository"""
598 return verifymod.verify(repo)
605 return verifymod.verify(repo)
599
606
600 def remoteui(src, opts):
607 def remoteui(src, opts):
601 'build a remote ui from ui or repo and opts'
608 'build a remote ui from ui or repo and opts'
602 if util.safehasattr(src, 'baseui'): # looks like a repository
609 if util.safehasattr(src, 'baseui'): # looks like a repository
603 dst = src.baseui.copy() # drop repo-specific config
610 dst = src.baseui.copy() # drop repo-specific config
604 src = src.ui # copy target options from repo
611 src = src.ui # copy target options from repo
605 else: # assume it's a global ui object
612 else: # assume it's a global ui object
606 dst = src.copy() # keep all global options
613 dst = src.copy() # keep all global options
607
614
608 # copy ssh-specific options
615 # copy ssh-specific options
609 for o in 'ssh', 'remotecmd':
616 for o in 'ssh', 'remotecmd':
610 v = opts.get(o) or src.config('ui', o)
617 v = opts.get(o) or src.config('ui', o)
611 if v:
618 if v:
612 dst.setconfig("ui", o, v)
619 dst.setconfig("ui", o, v)
613
620
614 # copy bundle-specific options
621 # copy bundle-specific options
615 r = src.config('bundle', 'mainreporoot')
622 r = src.config('bundle', 'mainreporoot')
616 if r:
623 if r:
617 dst.setconfig('bundle', 'mainreporoot', r)
624 dst.setconfig('bundle', 'mainreporoot', r)
618
625
619 # copy selected local settings to the remote ui
626 # copy selected local settings to the remote ui
620 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
627 for sect in ('auth', 'hostfingerprints', 'http_proxy'):
621 for key, val in src.configitems(sect):
628 for key, val in src.configitems(sect):
622 dst.setconfig(sect, key, val)
629 dst.setconfig(sect, key, val)
623 v = src.config('web', 'cacerts')
630 v = src.config('web', 'cacerts')
624 if v:
631 if v:
625 dst.setconfig('web', 'cacerts', util.expandpath(v))
632 dst.setconfig('web', 'cacerts', util.expandpath(v))
626
633
627 return dst
634 return dst
General Comments 0
You need to be logged in to leave comments. Login now