##// END OF EJS Templates
rename util.unlink to unlinkpath
Adrian Buehlmann -
r13235:6bf39d88 default
parent child Browse files
Show More
@@ -1,3253 +1,3253 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 You will by default be managing a patch queue named "patches". You can
41 You will by default be managing a patch queue named "patches". You can
42 create other, independent patch queues with the :hg:`qqueue` command.
42 create other, independent patch queues with the :hg:`qqueue` command.
43 '''
43 '''
44
44
45 from mercurial.i18n import _
45 from mercurial.i18n import _
46 from mercurial.node import bin, hex, short, nullid, nullrev
46 from mercurial.node import bin, hex, short, nullid, nullrev
47 from mercurial.lock import release
47 from mercurial.lock import release
48 from mercurial import commands, cmdutil, hg, patch, util
48 from mercurial import commands, cmdutil, hg, patch, util
49 from mercurial import repair, extensions, url, error
49 from mercurial import repair, extensions, url, error
50 import os, sys, re, errno, shutil
50 import os, sys, re, errno, shutil
51
51
52 commands.norepo += " qclone"
52 commands.norepo += " qclone"
53
53
54 # Patch names looks like unix-file names.
54 # Patch names looks like unix-file names.
55 # They must be joinable with queue directory and result in the patch path.
55 # They must be joinable with queue directory and result in the patch path.
56 normname = util.normpath
56 normname = util.normpath
57
57
58 class statusentry(object):
58 class statusentry(object):
59 def __init__(self, node, name):
59 def __init__(self, node, name):
60 self.node, self.name = node, name
60 self.node, self.name = node, name
61 def __repr__(self):
61 def __repr__(self):
62 return hex(self.node) + ':' + self.name
62 return hex(self.node) + ':' + self.name
63
63
64 class patchheader(object):
64 class patchheader(object):
65 def __init__(self, pf, plainmode=False):
65 def __init__(self, pf, plainmode=False):
66 def eatdiff(lines):
66 def eatdiff(lines):
67 while lines:
67 while lines:
68 l = lines[-1]
68 l = lines[-1]
69 if (l.startswith("diff -") or
69 if (l.startswith("diff -") or
70 l.startswith("Index:") or
70 l.startswith("Index:") or
71 l.startswith("===========")):
71 l.startswith("===========")):
72 del lines[-1]
72 del lines[-1]
73 else:
73 else:
74 break
74 break
75 def eatempty(lines):
75 def eatempty(lines):
76 while lines:
76 while lines:
77 if not lines[-1].strip():
77 if not lines[-1].strip():
78 del lines[-1]
78 del lines[-1]
79 else:
79 else:
80 break
80 break
81
81
82 message = []
82 message = []
83 comments = []
83 comments = []
84 user = None
84 user = None
85 date = None
85 date = None
86 parent = None
86 parent = None
87 format = None
87 format = None
88 subject = None
88 subject = None
89 branch = None
89 branch = None
90 nodeid = None
90 nodeid = None
91 diffstart = 0
91 diffstart = 0
92
92
93 for line in file(pf):
93 for line in file(pf):
94 line = line.rstrip()
94 line = line.rstrip()
95 if (line.startswith('diff --git')
95 if (line.startswith('diff --git')
96 or (diffstart and line.startswith('+++ '))):
96 or (diffstart and line.startswith('+++ '))):
97 diffstart = 2
97 diffstart = 2
98 break
98 break
99 diffstart = 0 # reset
99 diffstart = 0 # reset
100 if line.startswith("--- "):
100 if line.startswith("--- "):
101 diffstart = 1
101 diffstart = 1
102 continue
102 continue
103 elif format == "hgpatch":
103 elif format == "hgpatch":
104 # parse values when importing the result of an hg export
104 # parse values when importing the result of an hg export
105 if line.startswith("# User "):
105 if line.startswith("# User "):
106 user = line[7:]
106 user = line[7:]
107 elif line.startswith("# Date "):
107 elif line.startswith("# Date "):
108 date = line[7:]
108 date = line[7:]
109 elif line.startswith("# Parent "):
109 elif line.startswith("# Parent "):
110 parent = line[9:]
110 parent = line[9:]
111 elif line.startswith("# Branch "):
111 elif line.startswith("# Branch "):
112 branch = line[9:]
112 branch = line[9:]
113 elif line.startswith("# Node ID "):
113 elif line.startswith("# Node ID "):
114 nodeid = line[10:]
114 nodeid = line[10:]
115 elif not line.startswith("# ") and line:
115 elif not line.startswith("# ") and line:
116 message.append(line)
116 message.append(line)
117 format = None
117 format = None
118 elif line == '# HG changeset patch':
118 elif line == '# HG changeset patch':
119 message = []
119 message = []
120 format = "hgpatch"
120 format = "hgpatch"
121 elif (format != "tagdone" and (line.startswith("Subject: ") or
121 elif (format != "tagdone" and (line.startswith("Subject: ") or
122 line.startswith("subject: "))):
122 line.startswith("subject: "))):
123 subject = line[9:]
123 subject = line[9:]
124 format = "tag"
124 format = "tag"
125 elif (format != "tagdone" and (line.startswith("From: ") or
125 elif (format != "tagdone" and (line.startswith("From: ") or
126 line.startswith("from: "))):
126 line.startswith("from: "))):
127 user = line[6:]
127 user = line[6:]
128 format = "tag"
128 format = "tag"
129 elif (format != "tagdone" and (line.startswith("Date: ") or
129 elif (format != "tagdone" and (line.startswith("Date: ") or
130 line.startswith("date: "))):
130 line.startswith("date: "))):
131 date = line[6:]
131 date = line[6:]
132 format = "tag"
132 format = "tag"
133 elif format == "tag" and line == "":
133 elif format == "tag" and line == "":
134 # when looking for tags (subject: from: etc) they
134 # when looking for tags (subject: from: etc) they
135 # end once you find a blank line in the source
135 # end once you find a blank line in the source
136 format = "tagdone"
136 format = "tagdone"
137 elif message or line:
137 elif message or line:
138 message.append(line)
138 message.append(line)
139 comments.append(line)
139 comments.append(line)
140
140
141 eatdiff(message)
141 eatdiff(message)
142 eatdiff(comments)
142 eatdiff(comments)
143 # Remember the exact starting line of the patch diffs before consuming
143 # Remember the exact starting line of the patch diffs before consuming
144 # empty lines, for external use by TortoiseHg and others
144 # empty lines, for external use by TortoiseHg and others
145 self.diffstartline = len(comments)
145 self.diffstartline = len(comments)
146 eatempty(message)
146 eatempty(message)
147 eatempty(comments)
147 eatempty(comments)
148
148
149 # make sure message isn't empty
149 # make sure message isn't empty
150 if format and format.startswith("tag") and subject:
150 if format and format.startswith("tag") and subject:
151 message.insert(0, "")
151 message.insert(0, "")
152 message.insert(0, subject)
152 message.insert(0, subject)
153
153
154 self.message = message
154 self.message = message
155 self.comments = comments
155 self.comments = comments
156 self.user = user
156 self.user = user
157 self.date = date
157 self.date = date
158 self.parent = parent
158 self.parent = parent
159 # nodeid and branch are for external use by TortoiseHg and others
159 # nodeid and branch are for external use by TortoiseHg and others
160 self.nodeid = nodeid
160 self.nodeid = nodeid
161 self.branch = branch
161 self.branch = branch
162 self.haspatch = diffstart > 1
162 self.haspatch = diffstart > 1
163 self.plainmode = plainmode
163 self.plainmode = plainmode
164
164
165 def setuser(self, user):
165 def setuser(self, user):
166 if not self.updateheader(['From: ', '# User '], user):
166 if not self.updateheader(['From: ', '# User '], user):
167 try:
167 try:
168 patchheaderat = self.comments.index('# HG changeset patch')
168 patchheaderat = self.comments.index('# HG changeset patch')
169 self.comments.insert(patchheaderat + 1, '# User ' + user)
169 self.comments.insert(patchheaderat + 1, '# User ' + user)
170 except ValueError:
170 except ValueError:
171 if self.plainmode or self._hasheader(['Date: ']):
171 if self.plainmode or self._hasheader(['Date: ']):
172 self.comments = ['From: ' + user] + self.comments
172 self.comments = ['From: ' + user] + self.comments
173 else:
173 else:
174 tmp = ['# HG changeset patch', '# User ' + user, '']
174 tmp = ['# HG changeset patch', '# User ' + user, '']
175 self.comments = tmp + self.comments
175 self.comments = tmp + self.comments
176 self.user = user
176 self.user = user
177
177
178 def setdate(self, date):
178 def setdate(self, date):
179 if not self.updateheader(['Date: ', '# Date '], date):
179 if not self.updateheader(['Date: ', '# Date '], date):
180 try:
180 try:
181 patchheaderat = self.comments.index('# HG changeset patch')
181 patchheaderat = self.comments.index('# HG changeset patch')
182 self.comments.insert(patchheaderat + 1, '# Date ' + date)
182 self.comments.insert(patchheaderat + 1, '# Date ' + date)
183 except ValueError:
183 except ValueError:
184 if self.plainmode or self._hasheader(['From: ']):
184 if self.plainmode or self._hasheader(['From: ']):
185 self.comments = ['Date: ' + date] + self.comments
185 self.comments = ['Date: ' + date] + self.comments
186 else:
186 else:
187 tmp = ['# HG changeset patch', '# Date ' + date, '']
187 tmp = ['# HG changeset patch', '# Date ' + date, '']
188 self.comments = tmp + self.comments
188 self.comments = tmp + self.comments
189 self.date = date
189 self.date = date
190
190
191 def setparent(self, parent):
191 def setparent(self, parent):
192 if not self.updateheader(['# Parent '], parent):
192 if not self.updateheader(['# Parent '], parent):
193 try:
193 try:
194 patchheaderat = self.comments.index('# HG changeset patch')
194 patchheaderat = self.comments.index('# HG changeset patch')
195 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
195 self.comments.insert(patchheaderat + 1, '# Parent ' + parent)
196 except ValueError:
196 except ValueError:
197 pass
197 pass
198 self.parent = parent
198 self.parent = parent
199
199
200 def setmessage(self, message):
200 def setmessage(self, message):
201 if self.comments:
201 if self.comments:
202 self._delmsg()
202 self._delmsg()
203 self.message = [message]
203 self.message = [message]
204 self.comments += self.message
204 self.comments += self.message
205
205
206 def updateheader(self, prefixes, new):
206 def updateheader(self, prefixes, new):
207 '''Update all references to a field in the patch header.
207 '''Update all references to a field in the patch header.
208 Return whether the field is present.'''
208 Return whether the field is present.'''
209 res = False
209 res = False
210 for prefix in prefixes:
210 for prefix in prefixes:
211 for i in xrange(len(self.comments)):
211 for i in xrange(len(self.comments)):
212 if self.comments[i].startswith(prefix):
212 if self.comments[i].startswith(prefix):
213 self.comments[i] = prefix + new
213 self.comments[i] = prefix + new
214 res = True
214 res = True
215 break
215 break
216 return res
216 return res
217
217
218 def _hasheader(self, prefixes):
218 def _hasheader(self, prefixes):
219 '''Check if a header starts with any of the given prefixes.'''
219 '''Check if a header starts with any of the given prefixes.'''
220 for prefix in prefixes:
220 for prefix in prefixes:
221 for comment in self.comments:
221 for comment in self.comments:
222 if comment.startswith(prefix):
222 if comment.startswith(prefix):
223 return True
223 return True
224 return False
224 return False
225
225
226 def __str__(self):
226 def __str__(self):
227 if not self.comments:
227 if not self.comments:
228 return ''
228 return ''
229 return '\n'.join(self.comments) + '\n\n'
229 return '\n'.join(self.comments) + '\n\n'
230
230
231 def _delmsg(self):
231 def _delmsg(self):
232 '''Remove existing message, keeping the rest of the comments fields.
232 '''Remove existing message, keeping the rest of the comments fields.
233 If comments contains 'subject: ', message will prepend
233 If comments contains 'subject: ', message will prepend
234 the field and a blank line.'''
234 the field and a blank line.'''
235 if self.message:
235 if self.message:
236 subj = 'subject: ' + self.message[0].lower()
236 subj = 'subject: ' + self.message[0].lower()
237 for i in xrange(len(self.comments)):
237 for i in xrange(len(self.comments)):
238 if subj == self.comments[i].lower():
238 if subj == self.comments[i].lower():
239 del self.comments[i]
239 del self.comments[i]
240 self.message = self.message[2:]
240 self.message = self.message[2:]
241 break
241 break
242 ci = 0
242 ci = 0
243 for mi in self.message:
243 for mi in self.message:
244 while mi != self.comments[ci]:
244 while mi != self.comments[ci]:
245 ci += 1
245 ci += 1
246 del self.comments[ci]
246 del self.comments[ci]
247
247
248 class queue(object):
248 class queue(object):
249 def __init__(self, ui, path, patchdir=None):
249 def __init__(self, ui, path, patchdir=None):
250 self.basepath = path
250 self.basepath = path
251 try:
251 try:
252 fh = open(os.path.join(path, 'patches.queue'))
252 fh = open(os.path.join(path, 'patches.queue'))
253 cur = fh.read().rstrip()
253 cur = fh.read().rstrip()
254 if not cur:
254 if not cur:
255 curpath = os.path.join(path, 'patches')
255 curpath = os.path.join(path, 'patches')
256 else:
256 else:
257 curpath = os.path.join(path, 'patches-' + cur)
257 curpath = os.path.join(path, 'patches-' + cur)
258 except IOError:
258 except IOError:
259 curpath = os.path.join(path, 'patches')
259 curpath = os.path.join(path, 'patches')
260 self.path = patchdir or curpath
260 self.path = patchdir or curpath
261 self.opener = util.opener(self.path)
261 self.opener = util.opener(self.path)
262 self.ui = ui
262 self.ui = ui
263 self.applied_dirty = 0
263 self.applied_dirty = 0
264 self.series_dirty = 0
264 self.series_dirty = 0
265 self.added = []
265 self.added = []
266 self.series_path = "series"
266 self.series_path = "series"
267 self.status_path = "status"
267 self.status_path = "status"
268 self.guards_path = "guards"
268 self.guards_path = "guards"
269 self.active_guards = None
269 self.active_guards = None
270 self.guards_dirty = False
270 self.guards_dirty = False
271 # Handle mq.git as a bool with extended values
271 # Handle mq.git as a bool with extended values
272 try:
272 try:
273 gitmode = ui.configbool('mq', 'git', None)
273 gitmode = ui.configbool('mq', 'git', None)
274 if gitmode is None:
274 if gitmode is None:
275 raise error.ConfigError()
275 raise error.ConfigError()
276 self.gitmode = gitmode and 'yes' or 'no'
276 self.gitmode = gitmode and 'yes' or 'no'
277 except error.ConfigError:
277 except error.ConfigError:
278 self.gitmode = ui.config('mq', 'git', 'auto').lower()
278 self.gitmode = ui.config('mq', 'git', 'auto').lower()
279 self.plainmode = ui.configbool('mq', 'plain', False)
279 self.plainmode = ui.configbool('mq', 'plain', False)
280
280
281 @util.propertycache
281 @util.propertycache
282 def applied(self):
282 def applied(self):
283 if os.path.exists(self.join(self.status_path)):
283 if os.path.exists(self.join(self.status_path)):
284 def parse(l):
284 def parse(l):
285 n, name = l.split(':', 1)
285 n, name = l.split(':', 1)
286 return statusentry(bin(n), name)
286 return statusentry(bin(n), name)
287 lines = self.opener(self.status_path).read().splitlines()
287 lines = self.opener(self.status_path).read().splitlines()
288 return [parse(l) for l in lines]
288 return [parse(l) for l in lines]
289 return []
289 return []
290
290
291 @util.propertycache
291 @util.propertycache
292 def full_series(self):
292 def full_series(self):
293 if os.path.exists(self.join(self.series_path)):
293 if os.path.exists(self.join(self.series_path)):
294 return self.opener(self.series_path).read().splitlines()
294 return self.opener(self.series_path).read().splitlines()
295 return []
295 return []
296
296
297 @util.propertycache
297 @util.propertycache
298 def series(self):
298 def series(self):
299 self.parse_series()
299 self.parse_series()
300 return self.series
300 return self.series
301
301
302 @util.propertycache
302 @util.propertycache
303 def series_guards(self):
303 def series_guards(self):
304 self.parse_series()
304 self.parse_series()
305 return self.series_guards
305 return self.series_guards
306
306
307 def invalidate(self):
307 def invalidate(self):
308 for a in 'applied full_series series series_guards'.split():
308 for a in 'applied full_series series series_guards'.split():
309 if a in self.__dict__:
309 if a in self.__dict__:
310 delattr(self, a)
310 delattr(self, a)
311 self.applied_dirty = 0
311 self.applied_dirty = 0
312 self.series_dirty = 0
312 self.series_dirty = 0
313 self.guards_dirty = False
313 self.guards_dirty = False
314 self.active_guards = None
314 self.active_guards = None
315
315
316 def diffopts(self, opts={}, patchfn=None):
316 def diffopts(self, opts={}, patchfn=None):
317 diffopts = patch.diffopts(self.ui, opts)
317 diffopts = patch.diffopts(self.ui, opts)
318 if self.gitmode == 'auto':
318 if self.gitmode == 'auto':
319 diffopts.upgrade = True
319 diffopts.upgrade = True
320 elif self.gitmode == 'keep':
320 elif self.gitmode == 'keep':
321 pass
321 pass
322 elif self.gitmode in ('yes', 'no'):
322 elif self.gitmode in ('yes', 'no'):
323 diffopts.git = self.gitmode == 'yes'
323 diffopts.git = self.gitmode == 'yes'
324 else:
324 else:
325 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
325 raise util.Abort(_('mq.git option can be auto/keep/yes/no'
326 ' got %s') % self.gitmode)
326 ' got %s') % self.gitmode)
327 if patchfn:
327 if patchfn:
328 diffopts = self.patchopts(diffopts, patchfn)
328 diffopts = self.patchopts(diffopts, patchfn)
329 return diffopts
329 return diffopts
330
330
331 def patchopts(self, diffopts, *patches):
331 def patchopts(self, diffopts, *patches):
332 """Return a copy of input diff options with git set to true if
332 """Return a copy of input diff options with git set to true if
333 referenced patch is a git patch and should be preserved as such.
333 referenced patch is a git patch and should be preserved as such.
334 """
334 """
335 diffopts = diffopts.copy()
335 diffopts = diffopts.copy()
336 if not diffopts.git and self.gitmode == 'keep':
336 if not diffopts.git and self.gitmode == 'keep':
337 for patchfn in patches:
337 for patchfn in patches:
338 patchf = self.opener(patchfn, 'r')
338 patchf = self.opener(patchfn, 'r')
339 # if the patch was a git patch, refresh it as a git patch
339 # if the patch was a git patch, refresh it as a git patch
340 for line in patchf:
340 for line in patchf:
341 if line.startswith('diff --git'):
341 if line.startswith('diff --git'):
342 diffopts.git = True
342 diffopts.git = True
343 break
343 break
344 patchf.close()
344 patchf.close()
345 return diffopts
345 return diffopts
346
346
347 def join(self, *p):
347 def join(self, *p):
348 return os.path.join(self.path, *p)
348 return os.path.join(self.path, *p)
349
349
350 def find_series(self, patch):
350 def find_series(self, patch):
351 def matchpatch(l):
351 def matchpatch(l):
352 l = l.split('#', 1)[0]
352 l = l.split('#', 1)[0]
353 return l.strip() == patch
353 return l.strip() == patch
354 for index, l in enumerate(self.full_series):
354 for index, l in enumerate(self.full_series):
355 if matchpatch(l):
355 if matchpatch(l):
356 return index
356 return index
357 return None
357 return None
358
358
359 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
359 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
360
360
361 def parse_series(self):
361 def parse_series(self):
362 self.series = []
362 self.series = []
363 self.series_guards = []
363 self.series_guards = []
364 for l in self.full_series:
364 for l in self.full_series:
365 h = l.find('#')
365 h = l.find('#')
366 if h == -1:
366 if h == -1:
367 patch = l
367 patch = l
368 comment = ''
368 comment = ''
369 elif h == 0:
369 elif h == 0:
370 continue
370 continue
371 else:
371 else:
372 patch = l[:h]
372 patch = l[:h]
373 comment = l[h:]
373 comment = l[h:]
374 patch = patch.strip()
374 patch = patch.strip()
375 if patch:
375 if patch:
376 if patch in self.series:
376 if patch in self.series:
377 raise util.Abort(_('%s appears more than once in %s') %
377 raise util.Abort(_('%s appears more than once in %s') %
378 (patch, self.join(self.series_path)))
378 (patch, self.join(self.series_path)))
379 self.series.append(patch)
379 self.series.append(patch)
380 self.series_guards.append(self.guard_re.findall(comment))
380 self.series_guards.append(self.guard_re.findall(comment))
381
381
382 def check_guard(self, guard):
382 def check_guard(self, guard):
383 if not guard:
383 if not guard:
384 return _('guard cannot be an empty string')
384 return _('guard cannot be an empty string')
385 bad_chars = '# \t\r\n\f'
385 bad_chars = '# \t\r\n\f'
386 first = guard[0]
386 first = guard[0]
387 if first in '-+':
387 if first in '-+':
388 return (_('guard %r starts with invalid character: %r') %
388 return (_('guard %r starts with invalid character: %r') %
389 (guard, first))
389 (guard, first))
390 for c in bad_chars:
390 for c in bad_chars:
391 if c in guard:
391 if c in guard:
392 return _('invalid character in guard %r: %r') % (guard, c)
392 return _('invalid character in guard %r: %r') % (guard, c)
393
393
394 def set_active(self, guards):
394 def set_active(self, guards):
395 for guard in guards:
395 for guard in guards:
396 bad = self.check_guard(guard)
396 bad = self.check_guard(guard)
397 if bad:
397 if bad:
398 raise util.Abort(bad)
398 raise util.Abort(bad)
399 guards = sorted(set(guards))
399 guards = sorted(set(guards))
400 self.ui.debug('active guards: %s\n' % ' '.join(guards))
400 self.ui.debug('active guards: %s\n' % ' '.join(guards))
401 self.active_guards = guards
401 self.active_guards = guards
402 self.guards_dirty = True
402 self.guards_dirty = True
403
403
404 def active(self):
404 def active(self):
405 if self.active_guards is None:
405 if self.active_guards is None:
406 self.active_guards = []
406 self.active_guards = []
407 try:
407 try:
408 guards = self.opener(self.guards_path).read().split()
408 guards = self.opener(self.guards_path).read().split()
409 except IOError, err:
409 except IOError, err:
410 if err.errno != errno.ENOENT:
410 if err.errno != errno.ENOENT:
411 raise
411 raise
412 guards = []
412 guards = []
413 for i, guard in enumerate(guards):
413 for i, guard in enumerate(guards):
414 bad = self.check_guard(guard)
414 bad = self.check_guard(guard)
415 if bad:
415 if bad:
416 self.ui.warn('%s:%d: %s\n' %
416 self.ui.warn('%s:%d: %s\n' %
417 (self.join(self.guards_path), i + 1, bad))
417 (self.join(self.guards_path), i + 1, bad))
418 else:
418 else:
419 self.active_guards.append(guard)
419 self.active_guards.append(guard)
420 return self.active_guards
420 return self.active_guards
421
421
422 def set_guards(self, idx, guards):
422 def set_guards(self, idx, guards):
423 for g in guards:
423 for g in guards:
424 if len(g) < 2:
424 if len(g) < 2:
425 raise util.Abort(_('guard %r too short') % g)
425 raise util.Abort(_('guard %r too short') % g)
426 if g[0] not in '-+':
426 if g[0] not in '-+':
427 raise util.Abort(_('guard %r starts with invalid char') % g)
427 raise util.Abort(_('guard %r starts with invalid char') % g)
428 bad = self.check_guard(g[1:])
428 bad = self.check_guard(g[1:])
429 if bad:
429 if bad:
430 raise util.Abort(bad)
430 raise util.Abort(bad)
431 drop = self.guard_re.sub('', self.full_series[idx])
431 drop = self.guard_re.sub('', self.full_series[idx])
432 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
432 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
433 self.parse_series()
433 self.parse_series()
434 self.series_dirty = True
434 self.series_dirty = True
435
435
436 def pushable(self, idx):
436 def pushable(self, idx):
437 if isinstance(idx, str):
437 if isinstance(idx, str):
438 idx = self.series.index(idx)
438 idx = self.series.index(idx)
439 patchguards = self.series_guards[idx]
439 patchguards = self.series_guards[idx]
440 if not patchguards:
440 if not patchguards:
441 return True, None
441 return True, None
442 guards = self.active()
442 guards = self.active()
443 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
443 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
444 if exactneg:
444 if exactneg:
445 return False, exactneg[0]
445 return False, exactneg[0]
446 pos = [g for g in patchguards if g[0] == '+']
446 pos = [g for g in patchguards if g[0] == '+']
447 exactpos = [g for g in pos if g[1:] in guards]
447 exactpos = [g for g in pos if g[1:] in guards]
448 if pos:
448 if pos:
449 if exactpos:
449 if exactpos:
450 return True, exactpos[0]
450 return True, exactpos[0]
451 return False, pos
451 return False, pos
452 return True, ''
452 return True, ''
453
453
454 def explain_pushable(self, idx, all_patches=False):
454 def explain_pushable(self, idx, all_patches=False):
455 write = all_patches and self.ui.write or self.ui.warn
455 write = all_patches and self.ui.write or self.ui.warn
456 if all_patches or self.ui.verbose:
456 if all_patches or self.ui.verbose:
457 if isinstance(idx, str):
457 if isinstance(idx, str):
458 idx = self.series.index(idx)
458 idx = self.series.index(idx)
459 pushable, why = self.pushable(idx)
459 pushable, why = self.pushable(idx)
460 if all_patches and pushable:
460 if all_patches and pushable:
461 if why is None:
461 if why is None:
462 write(_('allowing %s - no guards in effect\n') %
462 write(_('allowing %s - no guards in effect\n') %
463 self.series[idx])
463 self.series[idx])
464 else:
464 else:
465 if not why:
465 if not why:
466 write(_('allowing %s - no matching negative guards\n') %
466 write(_('allowing %s - no matching negative guards\n') %
467 self.series[idx])
467 self.series[idx])
468 else:
468 else:
469 write(_('allowing %s - guarded by %r\n') %
469 write(_('allowing %s - guarded by %r\n') %
470 (self.series[idx], why))
470 (self.series[idx], why))
471 if not pushable:
471 if not pushable:
472 if why:
472 if why:
473 write(_('skipping %s - guarded by %r\n') %
473 write(_('skipping %s - guarded by %r\n') %
474 (self.series[idx], why))
474 (self.series[idx], why))
475 else:
475 else:
476 write(_('skipping %s - no matching guards\n') %
476 write(_('skipping %s - no matching guards\n') %
477 self.series[idx])
477 self.series[idx])
478
478
479 def save_dirty(self):
479 def save_dirty(self):
480 def write_list(items, path):
480 def write_list(items, path):
481 fp = self.opener(path, 'w')
481 fp = self.opener(path, 'w')
482 for i in items:
482 for i in items:
483 fp.write("%s\n" % i)
483 fp.write("%s\n" % i)
484 fp.close()
484 fp.close()
485 if self.applied_dirty:
485 if self.applied_dirty:
486 write_list(map(str, self.applied), self.status_path)
486 write_list(map(str, self.applied), self.status_path)
487 if self.series_dirty:
487 if self.series_dirty:
488 write_list(self.full_series, self.series_path)
488 write_list(self.full_series, self.series_path)
489 if self.guards_dirty:
489 if self.guards_dirty:
490 write_list(self.active_guards, self.guards_path)
490 write_list(self.active_guards, self.guards_path)
491 if self.added:
491 if self.added:
492 qrepo = self.qrepo()
492 qrepo = self.qrepo()
493 if qrepo:
493 if qrepo:
494 qrepo[None].add(f for f in self.added if f not in qrepo[None])
494 qrepo[None].add(f for f in self.added if f not in qrepo[None])
495 self.added = []
495 self.added = []
496
496
497 def removeundo(self, repo):
497 def removeundo(self, repo):
498 undo = repo.sjoin('undo')
498 undo = repo.sjoin('undo')
499 if not os.path.exists(undo):
499 if not os.path.exists(undo):
500 return
500 return
501 try:
501 try:
502 os.unlink(undo)
502 os.unlink(undo)
503 except OSError, inst:
503 except OSError, inst:
504 self.ui.warn(_('error removing undo: %s\n') % str(inst))
504 self.ui.warn(_('error removing undo: %s\n') % str(inst))
505
505
506 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
506 def printdiff(self, repo, diffopts, node1, node2=None, files=None,
507 fp=None, changes=None, opts={}):
507 fp=None, changes=None, opts={}):
508 stat = opts.get('stat')
508 stat = opts.get('stat')
509 m = cmdutil.match(repo, files, opts)
509 m = cmdutil.match(repo, files, opts)
510 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
510 cmdutil.diffordiffstat(self.ui, repo, diffopts, node1, node2, m,
511 changes, stat, fp)
511 changes, stat, fp)
512
512
513 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
513 def mergeone(self, repo, mergeq, head, patch, rev, diffopts):
514 # first try just applying the patch
514 # first try just applying the patch
515 (err, n) = self.apply(repo, [patch], update_status=False,
515 (err, n) = self.apply(repo, [patch], update_status=False,
516 strict=True, merge=rev)
516 strict=True, merge=rev)
517
517
518 if err == 0:
518 if err == 0:
519 return (err, n)
519 return (err, n)
520
520
521 if n is None:
521 if n is None:
522 raise util.Abort(_("apply failed for patch %s") % patch)
522 raise util.Abort(_("apply failed for patch %s") % patch)
523
523
524 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
524 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
525
525
526 # apply failed, strip away that rev and merge.
526 # apply failed, strip away that rev and merge.
527 hg.clean(repo, head)
527 hg.clean(repo, head)
528 self.strip(repo, [n], update=False, backup='strip')
528 self.strip(repo, [n], update=False, backup='strip')
529
529
530 ctx = repo[rev]
530 ctx = repo[rev]
531 ret = hg.merge(repo, rev)
531 ret = hg.merge(repo, rev)
532 if ret:
532 if ret:
533 raise util.Abort(_("update returned %d") % ret)
533 raise util.Abort(_("update returned %d") % ret)
534 n = repo.commit(ctx.description(), ctx.user(), force=True)
534 n = repo.commit(ctx.description(), ctx.user(), force=True)
535 if n is None:
535 if n is None:
536 raise util.Abort(_("repo commit failed"))
536 raise util.Abort(_("repo commit failed"))
537 try:
537 try:
538 ph = patchheader(mergeq.join(patch), self.plainmode)
538 ph = patchheader(mergeq.join(patch), self.plainmode)
539 except:
539 except:
540 raise util.Abort(_("unable to read %s") % patch)
540 raise util.Abort(_("unable to read %s") % patch)
541
541
542 diffopts = self.patchopts(diffopts, patch)
542 diffopts = self.patchopts(diffopts, patch)
543 patchf = self.opener(patch, "w")
543 patchf = self.opener(patch, "w")
544 comments = str(ph)
544 comments = str(ph)
545 if comments:
545 if comments:
546 patchf.write(comments)
546 patchf.write(comments)
547 self.printdiff(repo, diffopts, head, n, fp=patchf)
547 self.printdiff(repo, diffopts, head, n, fp=patchf)
548 patchf.close()
548 patchf.close()
549 self.removeundo(repo)
549 self.removeundo(repo)
550 return (0, n)
550 return (0, n)
551
551
552 def qparents(self, repo, rev=None):
552 def qparents(self, repo, rev=None):
553 if rev is None:
553 if rev is None:
554 (p1, p2) = repo.dirstate.parents()
554 (p1, p2) = repo.dirstate.parents()
555 if p2 == nullid:
555 if p2 == nullid:
556 return p1
556 return p1
557 if not self.applied:
557 if not self.applied:
558 return None
558 return None
559 return self.applied[-1].node
559 return self.applied[-1].node
560 p1, p2 = repo.changelog.parents(rev)
560 p1, p2 = repo.changelog.parents(rev)
561 if p2 != nullid and p2 in [x.node for x in self.applied]:
561 if p2 != nullid and p2 in [x.node for x in self.applied]:
562 return p2
562 return p2
563 return p1
563 return p1
564
564
565 def mergepatch(self, repo, mergeq, series, diffopts):
565 def mergepatch(self, repo, mergeq, series, diffopts):
566 if not self.applied:
566 if not self.applied:
567 # each of the patches merged in will have two parents. This
567 # each of the patches merged in will have two parents. This
568 # can confuse the qrefresh, qdiff, and strip code because it
568 # can confuse the qrefresh, qdiff, and strip code because it
569 # needs to know which parent is actually in the patch queue.
569 # needs to know which parent is actually in the patch queue.
570 # so, we insert a merge marker with only one parent. This way
570 # so, we insert a merge marker with only one parent. This way
571 # the first patch in the queue is never a merge patch
571 # the first patch in the queue is never a merge patch
572 #
572 #
573 pname = ".hg.patches.merge.marker"
573 pname = ".hg.patches.merge.marker"
574 n = repo.commit('[mq]: merge marker', force=True)
574 n = repo.commit('[mq]: merge marker', force=True)
575 self.removeundo(repo)
575 self.removeundo(repo)
576 self.applied.append(statusentry(n, pname))
576 self.applied.append(statusentry(n, pname))
577 self.applied_dirty = 1
577 self.applied_dirty = 1
578
578
579 head = self.qparents(repo)
579 head = self.qparents(repo)
580
580
581 for patch in series:
581 for patch in series:
582 patch = mergeq.lookup(patch, strict=True)
582 patch = mergeq.lookup(patch, strict=True)
583 if not patch:
583 if not patch:
584 self.ui.warn(_("patch %s does not exist\n") % patch)
584 self.ui.warn(_("patch %s does not exist\n") % patch)
585 return (1, None)
585 return (1, None)
586 pushable, reason = self.pushable(patch)
586 pushable, reason = self.pushable(patch)
587 if not pushable:
587 if not pushable:
588 self.explain_pushable(patch, all_patches=True)
588 self.explain_pushable(patch, all_patches=True)
589 continue
589 continue
590 info = mergeq.isapplied(patch)
590 info = mergeq.isapplied(patch)
591 if not info:
591 if not info:
592 self.ui.warn(_("patch %s is not applied\n") % patch)
592 self.ui.warn(_("patch %s is not applied\n") % patch)
593 return (1, None)
593 return (1, None)
594 rev = info[1]
594 rev = info[1]
595 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
595 err, head = self.mergeone(repo, mergeq, head, patch, rev, diffopts)
596 if head:
596 if head:
597 self.applied.append(statusentry(head, patch))
597 self.applied.append(statusentry(head, patch))
598 self.applied_dirty = 1
598 self.applied_dirty = 1
599 if err:
599 if err:
600 return (err, head)
600 return (err, head)
601 self.save_dirty()
601 self.save_dirty()
602 return (0, head)
602 return (0, head)
603
603
604 def patch(self, repo, patchfile):
604 def patch(self, repo, patchfile):
605 '''Apply patchfile to the working directory.
605 '''Apply patchfile to the working directory.
606 patchfile: name of patch file'''
606 patchfile: name of patch file'''
607 files = {}
607 files = {}
608 try:
608 try:
609 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
609 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
610 files=files, eolmode=None)
610 files=files, eolmode=None)
611 except Exception, inst:
611 except Exception, inst:
612 self.ui.note(str(inst) + '\n')
612 self.ui.note(str(inst) + '\n')
613 if not self.ui.verbose:
613 if not self.ui.verbose:
614 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
614 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
615 return (False, files, False)
615 return (False, files, False)
616
616
617 return (True, files, fuzz)
617 return (True, files, fuzz)
618
618
619 def apply(self, repo, series, list=False, update_status=True,
619 def apply(self, repo, series, list=False, update_status=True,
620 strict=False, patchdir=None, merge=None, all_files=None):
620 strict=False, patchdir=None, merge=None, all_files=None):
621 wlock = lock = tr = None
621 wlock = lock = tr = None
622 try:
622 try:
623 wlock = repo.wlock()
623 wlock = repo.wlock()
624 lock = repo.lock()
624 lock = repo.lock()
625 tr = repo.transaction("qpush")
625 tr = repo.transaction("qpush")
626 try:
626 try:
627 ret = self._apply(repo, series, list, update_status,
627 ret = self._apply(repo, series, list, update_status,
628 strict, patchdir, merge, all_files=all_files)
628 strict, patchdir, merge, all_files=all_files)
629 tr.close()
629 tr.close()
630 self.save_dirty()
630 self.save_dirty()
631 return ret
631 return ret
632 except:
632 except:
633 try:
633 try:
634 tr.abort()
634 tr.abort()
635 finally:
635 finally:
636 repo.invalidate()
636 repo.invalidate()
637 repo.dirstate.invalidate()
637 repo.dirstate.invalidate()
638 raise
638 raise
639 finally:
639 finally:
640 release(tr, lock, wlock)
640 release(tr, lock, wlock)
641 self.removeundo(repo)
641 self.removeundo(repo)
642
642
643 def _apply(self, repo, series, list=False, update_status=True,
643 def _apply(self, repo, series, list=False, update_status=True,
644 strict=False, patchdir=None, merge=None, all_files=None):
644 strict=False, patchdir=None, merge=None, all_files=None):
645 '''returns (error, hash)
645 '''returns (error, hash)
646 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
646 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
647 # TODO unify with commands.py
647 # TODO unify with commands.py
648 if not patchdir:
648 if not patchdir:
649 patchdir = self.path
649 patchdir = self.path
650 err = 0
650 err = 0
651 n = None
651 n = None
652 for patchname in series:
652 for patchname in series:
653 pushable, reason = self.pushable(patchname)
653 pushable, reason = self.pushable(patchname)
654 if not pushable:
654 if not pushable:
655 self.explain_pushable(patchname, all_patches=True)
655 self.explain_pushable(patchname, all_patches=True)
656 continue
656 continue
657 self.ui.status(_("applying %s\n") % patchname)
657 self.ui.status(_("applying %s\n") % patchname)
658 pf = os.path.join(patchdir, patchname)
658 pf = os.path.join(patchdir, patchname)
659
659
660 try:
660 try:
661 ph = patchheader(self.join(patchname), self.plainmode)
661 ph = patchheader(self.join(patchname), self.plainmode)
662 except:
662 except:
663 self.ui.warn(_("unable to read %s\n") % patchname)
663 self.ui.warn(_("unable to read %s\n") % patchname)
664 err = 1
664 err = 1
665 break
665 break
666
666
667 message = ph.message
667 message = ph.message
668 if not message:
668 if not message:
669 # The commit message should not be translated
669 # The commit message should not be translated
670 message = "imported patch %s\n" % patchname
670 message = "imported patch %s\n" % patchname
671 else:
671 else:
672 if list:
672 if list:
673 # The commit message should not be translated
673 # The commit message should not be translated
674 message.append("\nimported patch %s" % patchname)
674 message.append("\nimported patch %s" % patchname)
675 message = '\n'.join(message)
675 message = '\n'.join(message)
676
676
677 if ph.haspatch:
677 if ph.haspatch:
678 (patcherr, files, fuzz) = self.patch(repo, pf)
678 (patcherr, files, fuzz) = self.patch(repo, pf)
679 if all_files is not None:
679 if all_files is not None:
680 all_files.update(files)
680 all_files.update(files)
681 patcherr = not patcherr
681 patcherr = not patcherr
682 else:
682 else:
683 self.ui.warn(_("patch %s is empty\n") % patchname)
683 self.ui.warn(_("patch %s is empty\n") % patchname)
684 patcherr, files, fuzz = 0, [], 0
684 patcherr, files, fuzz = 0, [], 0
685
685
686 if merge and files:
686 if merge and files:
687 # Mark as removed/merged and update dirstate parent info
687 # Mark as removed/merged and update dirstate parent info
688 removed = []
688 removed = []
689 merged = []
689 merged = []
690 for f in files:
690 for f in files:
691 if os.path.lexists(repo.wjoin(f)):
691 if os.path.lexists(repo.wjoin(f)):
692 merged.append(f)
692 merged.append(f)
693 else:
693 else:
694 removed.append(f)
694 removed.append(f)
695 for f in removed:
695 for f in removed:
696 repo.dirstate.remove(f)
696 repo.dirstate.remove(f)
697 for f in merged:
697 for f in merged:
698 repo.dirstate.merge(f)
698 repo.dirstate.merge(f)
699 p1, p2 = repo.dirstate.parents()
699 p1, p2 = repo.dirstate.parents()
700 repo.dirstate.setparents(p1, merge)
700 repo.dirstate.setparents(p1, merge)
701
701
702 files = cmdutil.updatedir(self.ui, repo, files)
702 files = cmdutil.updatedir(self.ui, repo, files)
703 match = cmdutil.matchfiles(repo, files or [])
703 match = cmdutil.matchfiles(repo, files or [])
704 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
704 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
705
705
706 if n is None:
706 if n is None:
707 raise util.Abort(_("repository commit failed"))
707 raise util.Abort(_("repository commit failed"))
708
708
709 if update_status:
709 if update_status:
710 self.applied.append(statusentry(n, patchname))
710 self.applied.append(statusentry(n, patchname))
711
711
712 if patcherr:
712 if patcherr:
713 self.ui.warn(_("patch failed, rejects left in working dir\n"))
713 self.ui.warn(_("patch failed, rejects left in working dir\n"))
714 err = 2
714 err = 2
715 break
715 break
716
716
717 if fuzz and strict:
717 if fuzz and strict:
718 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
718 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
719 err = 3
719 err = 3
720 break
720 break
721 return (err, n)
721 return (err, n)
722
722
723 def _cleanup(self, patches, numrevs, keep=False):
723 def _cleanup(self, patches, numrevs, keep=False):
724 if not keep:
724 if not keep:
725 r = self.qrepo()
725 r = self.qrepo()
726 if r:
726 if r:
727 r[None].remove(patches, True)
727 r[None].remove(patches, True)
728 else:
728 else:
729 for p in patches:
729 for p in patches:
730 os.unlink(self.join(p))
730 os.unlink(self.join(p))
731
731
732 if numrevs:
732 if numrevs:
733 del self.applied[:numrevs]
733 del self.applied[:numrevs]
734 self.applied_dirty = 1
734 self.applied_dirty = 1
735
735
736 for i in sorted([self.find_series(p) for p in patches], reverse=True):
736 for i in sorted([self.find_series(p) for p in patches], reverse=True):
737 del self.full_series[i]
737 del self.full_series[i]
738 self.parse_series()
738 self.parse_series()
739 self.series_dirty = 1
739 self.series_dirty = 1
740
740
741 def _revpatches(self, repo, revs):
741 def _revpatches(self, repo, revs):
742 firstrev = repo[self.applied[0].node].rev()
742 firstrev = repo[self.applied[0].node].rev()
743 patches = []
743 patches = []
744 for i, rev in enumerate(revs):
744 for i, rev in enumerate(revs):
745
745
746 if rev < firstrev:
746 if rev < firstrev:
747 raise util.Abort(_('revision %d is not managed') % rev)
747 raise util.Abort(_('revision %d is not managed') % rev)
748
748
749 ctx = repo[rev]
749 ctx = repo[rev]
750 base = self.applied[i].node
750 base = self.applied[i].node
751 if ctx.node() != base:
751 if ctx.node() != base:
752 msg = _('cannot delete revision %d above applied patches')
752 msg = _('cannot delete revision %d above applied patches')
753 raise util.Abort(msg % rev)
753 raise util.Abort(msg % rev)
754
754
755 patch = self.applied[i].name
755 patch = self.applied[i].name
756 for fmt in ('[mq]: %s', 'imported patch %s'):
756 for fmt in ('[mq]: %s', 'imported patch %s'):
757 if ctx.description() == fmt % patch:
757 if ctx.description() == fmt % patch:
758 msg = _('patch %s finalized without changeset message\n')
758 msg = _('patch %s finalized without changeset message\n')
759 repo.ui.status(msg % patch)
759 repo.ui.status(msg % patch)
760 break
760 break
761
761
762 patches.append(patch)
762 patches.append(patch)
763 return patches
763 return patches
764
764
765 def finish(self, repo, revs):
765 def finish(self, repo, revs):
766 patches = self._revpatches(repo, sorted(revs))
766 patches = self._revpatches(repo, sorted(revs))
767 self._cleanup(patches, len(patches))
767 self._cleanup(patches, len(patches))
768
768
769 def delete(self, repo, patches, opts):
769 def delete(self, repo, patches, opts):
770 if not patches and not opts.get('rev'):
770 if not patches and not opts.get('rev'):
771 raise util.Abort(_('qdelete requires at least one revision or '
771 raise util.Abort(_('qdelete requires at least one revision or '
772 'patch name'))
772 'patch name'))
773
773
774 realpatches = []
774 realpatches = []
775 for patch in patches:
775 for patch in patches:
776 patch = self.lookup(patch, strict=True)
776 patch = self.lookup(patch, strict=True)
777 info = self.isapplied(patch)
777 info = self.isapplied(patch)
778 if info:
778 if info:
779 raise util.Abort(_("cannot delete applied patch %s") % patch)
779 raise util.Abort(_("cannot delete applied patch %s") % patch)
780 if patch not in self.series:
780 if patch not in self.series:
781 raise util.Abort(_("patch %s not in series file") % patch)
781 raise util.Abort(_("patch %s not in series file") % patch)
782 if patch not in realpatches:
782 if patch not in realpatches:
783 realpatches.append(patch)
783 realpatches.append(patch)
784
784
785 numrevs = 0
785 numrevs = 0
786 if opts.get('rev'):
786 if opts.get('rev'):
787 if not self.applied:
787 if not self.applied:
788 raise util.Abort(_('no patches applied'))
788 raise util.Abort(_('no patches applied'))
789 revs = cmdutil.revrange(repo, opts.get('rev'))
789 revs = cmdutil.revrange(repo, opts.get('rev'))
790 if len(revs) > 1 and revs[0] > revs[1]:
790 if len(revs) > 1 and revs[0] > revs[1]:
791 revs.reverse()
791 revs.reverse()
792 revpatches = self._revpatches(repo, revs)
792 revpatches = self._revpatches(repo, revs)
793 realpatches += revpatches
793 realpatches += revpatches
794 numrevs = len(revpatches)
794 numrevs = len(revpatches)
795
795
796 self._cleanup(realpatches, numrevs, opts.get('keep'))
796 self._cleanup(realpatches, numrevs, opts.get('keep'))
797
797
798 def check_toppatch(self, repo):
798 def check_toppatch(self, repo):
799 if self.applied:
799 if self.applied:
800 top = self.applied[-1].node
800 top = self.applied[-1].node
801 patch = self.applied[-1].name
801 patch = self.applied[-1].name
802 pp = repo.dirstate.parents()
802 pp = repo.dirstate.parents()
803 if top not in pp:
803 if top not in pp:
804 raise util.Abort(_("working directory revision is not qtip"))
804 raise util.Abort(_("working directory revision is not qtip"))
805 return top, patch
805 return top, patch
806 return None, None
806 return None, None
807
807
808 def check_substate(self, repo):
808 def check_substate(self, repo):
809 '''return list of subrepos at a different revision than substate.
809 '''return list of subrepos at a different revision than substate.
810 Abort if any subrepos have uncommitted changes.'''
810 Abort if any subrepos have uncommitted changes.'''
811 inclsubs = []
811 inclsubs = []
812 wctx = repo[None]
812 wctx = repo[None]
813 for s in wctx.substate:
813 for s in wctx.substate:
814 if wctx.sub(s).dirty(True):
814 if wctx.sub(s).dirty(True):
815 raise util.Abort(
815 raise util.Abort(
816 _("uncommitted changes in subrepository %s") % s)
816 _("uncommitted changes in subrepository %s") % s)
817 elif wctx.sub(s).dirty():
817 elif wctx.sub(s).dirty():
818 inclsubs.append(s)
818 inclsubs.append(s)
819 return inclsubs
819 return inclsubs
820
820
821 def check_localchanges(self, repo, force=False, refresh=True):
821 def check_localchanges(self, repo, force=False, refresh=True):
822 m, a, r, d = repo.status()[:4]
822 m, a, r, d = repo.status()[:4]
823 if (m or a or r or d) and not force:
823 if (m or a or r or d) and not force:
824 if refresh:
824 if refresh:
825 raise util.Abort(_("local changes found, refresh first"))
825 raise util.Abort(_("local changes found, refresh first"))
826 else:
826 else:
827 raise util.Abort(_("local changes found"))
827 raise util.Abort(_("local changes found"))
828 return m, a, r, d
828 return m, a, r, d
829
829
830 _reserved = ('series', 'status', 'guards')
830 _reserved = ('series', 'status', 'guards')
831 def check_reserved_name(self, name):
831 def check_reserved_name(self, name):
832 if (name in self._reserved or name.startswith('.hg')
832 if (name in self._reserved or name.startswith('.hg')
833 or name.startswith('.mq') or '#' in name or ':' in name):
833 or name.startswith('.mq') or '#' in name or ':' in name):
834 raise util.Abort(_('"%s" cannot be used as the name of a patch')
834 raise util.Abort(_('"%s" cannot be used as the name of a patch')
835 % name)
835 % name)
836
836
837 def new(self, repo, patchfn, *pats, **opts):
837 def new(self, repo, patchfn, *pats, **opts):
838 """options:
838 """options:
839 msg: a string or a no-argument function returning a string
839 msg: a string or a no-argument function returning a string
840 """
840 """
841 msg = opts.get('msg')
841 msg = opts.get('msg')
842 user = opts.get('user')
842 user = opts.get('user')
843 date = opts.get('date')
843 date = opts.get('date')
844 if date:
844 if date:
845 date = util.parsedate(date)
845 date = util.parsedate(date)
846 diffopts = self.diffopts({'git': opts.get('git')})
846 diffopts = self.diffopts({'git': opts.get('git')})
847 self.check_reserved_name(patchfn)
847 self.check_reserved_name(patchfn)
848 if os.path.exists(self.join(patchfn)):
848 if os.path.exists(self.join(patchfn)):
849 if os.path.isdir(self.join(patchfn)):
849 if os.path.isdir(self.join(patchfn)):
850 raise util.Abort(_('"%s" already exists as a directory')
850 raise util.Abort(_('"%s" already exists as a directory')
851 % patchfn)
851 % patchfn)
852 else:
852 else:
853 raise util.Abort(_('patch "%s" already exists') % patchfn)
853 raise util.Abort(_('patch "%s" already exists') % patchfn)
854
854
855 inclsubs = self.check_substate(repo)
855 inclsubs = self.check_substate(repo)
856 if inclsubs:
856 if inclsubs:
857 inclsubs.append('.hgsubstate')
857 inclsubs.append('.hgsubstate')
858 if opts.get('include') or opts.get('exclude') or pats:
858 if opts.get('include') or opts.get('exclude') or pats:
859 if inclsubs:
859 if inclsubs:
860 pats = list(pats or []) + inclsubs
860 pats = list(pats or []) + inclsubs
861 match = cmdutil.match(repo, pats, opts)
861 match = cmdutil.match(repo, pats, opts)
862 # detect missing files in pats
862 # detect missing files in pats
863 def badfn(f, msg):
863 def badfn(f, msg):
864 if f != '.hgsubstate': # .hgsubstate is auto-created
864 if f != '.hgsubstate': # .hgsubstate is auto-created
865 raise util.Abort('%s: %s' % (f, msg))
865 raise util.Abort('%s: %s' % (f, msg))
866 match.bad = badfn
866 match.bad = badfn
867 m, a, r, d = repo.status(match=match)[:4]
867 m, a, r, d = repo.status(match=match)[:4]
868 else:
868 else:
869 m, a, r, d = self.check_localchanges(repo, force=True)
869 m, a, r, d = self.check_localchanges(repo, force=True)
870 match = cmdutil.matchfiles(repo, m + a + r + inclsubs)
870 match = cmdutil.matchfiles(repo, m + a + r + inclsubs)
871 if len(repo[None].parents()) > 1:
871 if len(repo[None].parents()) > 1:
872 raise util.Abort(_('cannot manage merge changesets'))
872 raise util.Abort(_('cannot manage merge changesets'))
873 commitfiles = m + a + r
873 commitfiles = m + a + r
874 self.check_toppatch(repo)
874 self.check_toppatch(repo)
875 insert = self.full_series_end()
875 insert = self.full_series_end()
876 wlock = repo.wlock()
876 wlock = repo.wlock()
877 try:
877 try:
878 try:
878 try:
879 # if patch file write fails, abort early
879 # if patch file write fails, abort early
880 p = self.opener(patchfn, "w")
880 p = self.opener(patchfn, "w")
881 except IOError, e:
881 except IOError, e:
882 raise util.Abort(_('cannot write patch "%s": %s')
882 raise util.Abort(_('cannot write patch "%s": %s')
883 % (patchfn, e.strerror))
883 % (patchfn, e.strerror))
884 try:
884 try:
885 if self.plainmode:
885 if self.plainmode:
886 if user:
886 if user:
887 p.write("From: " + user + "\n")
887 p.write("From: " + user + "\n")
888 if not date:
888 if not date:
889 p.write("\n")
889 p.write("\n")
890 if date:
890 if date:
891 p.write("Date: %d %d\n\n" % date)
891 p.write("Date: %d %d\n\n" % date)
892 else:
892 else:
893 p.write("# HG changeset patch\n")
893 p.write("# HG changeset patch\n")
894 p.write("# Parent "
894 p.write("# Parent "
895 + hex(repo[None].parents()[0].node()) + "\n")
895 + hex(repo[None].parents()[0].node()) + "\n")
896 if user:
896 if user:
897 p.write("# User " + user + "\n")
897 p.write("# User " + user + "\n")
898 if date:
898 if date:
899 p.write("# Date %s %s\n\n" % date)
899 p.write("# Date %s %s\n\n" % date)
900 if hasattr(msg, '__call__'):
900 if hasattr(msg, '__call__'):
901 msg = msg()
901 msg = msg()
902 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
902 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
903 n = repo.commit(commitmsg, user, date, match=match, force=True)
903 n = repo.commit(commitmsg, user, date, match=match, force=True)
904 if n is None:
904 if n is None:
905 raise util.Abort(_("repo commit failed"))
905 raise util.Abort(_("repo commit failed"))
906 try:
906 try:
907 self.full_series[insert:insert] = [patchfn]
907 self.full_series[insert:insert] = [patchfn]
908 self.applied.append(statusentry(n, patchfn))
908 self.applied.append(statusentry(n, patchfn))
909 self.parse_series()
909 self.parse_series()
910 self.series_dirty = 1
910 self.series_dirty = 1
911 self.applied_dirty = 1
911 self.applied_dirty = 1
912 if msg:
912 if msg:
913 msg = msg + "\n\n"
913 msg = msg + "\n\n"
914 p.write(msg)
914 p.write(msg)
915 if commitfiles:
915 if commitfiles:
916 parent = self.qparents(repo, n)
916 parent = self.qparents(repo, n)
917 chunks = patch.diff(repo, node1=parent, node2=n,
917 chunks = patch.diff(repo, node1=parent, node2=n,
918 match=match, opts=diffopts)
918 match=match, opts=diffopts)
919 for chunk in chunks:
919 for chunk in chunks:
920 p.write(chunk)
920 p.write(chunk)
921 p.close()
921 p.close()
922 wlock.release()
922 wlock.release()
923 wlock = None
923 wlock = None
924 r = self.qrepo()
924 r = self.qrepo()
925 if r:
925 if r:
926 r[None].add([patchfn])
926 r[None].add([patchfn])
927 except:
927 except:
928 repo.rollback()
928 repo.rollback()
929 raise
929 raise
930 except Exception:
930 except Exception:
931 patchpath = self.join(patchfn)
931 patchpath = self.join(patchfn)
932 try:
932 try:
933 os.unlink(patchpath)
933 os.unlink(patchpath)
934 except:
934 except:
935 self.ui.warn(_('error unlinking %s\n') % patchpath)
935 self.ui.warn(_('error unlinking %s\n') % patchpath)
936 raise
936 raise
937 self.removeundo(repo)
937 self.removeundo(repo)
938 finally:
938 finally:
939 release(wlock)
939 release(wlock)
940
940
941 def strip(self, repo, revs, update=True, backup="all", force=None):
941 def strip(self, repo, revs, update=True, backup="all", force=None):
942 wlock = lock = None
942 wlock = lock = None
943 try:
943 try:
944 wlock = repo.wlock()
944 wlock = repo.wlock()
945 lock = repo.lock()
945 lock = repo.lock()
946
946
947 if update:
947 if update:
948 self.check_localchanges(repo, force=force, refresh=False)
948 self.check_localchanges(repo, force=force, refresh=False)
949 urev = self.qparents(repo, revs[0])
949 urev = self.qparents(repo, revs[0])
950 hg.clean(repo, urev)
950 hg.clean(repo, urev)
951 repo.dirstate.write()
951 repo.dirstate.write()
952
952
953 self.removeundo(repo)
953 self.removeundo(repo)
954 for rev in revs:
954 for rev in revs:
955 repair.strip(self.ui, repo, rev, backup)
955 repair.strip(self.ui, repo, rev, backup)
956 # strip may have unbundled a set of backed up revisions after
956 # strip may have unbundled a set of backed up revisions after
957 # the actual strip
957 # the actual strip
958 self.removeundo(repo)
958 self.removeundo(repo)
959 finally:
959 finally:
960 release(lock, wlock)
960 release(lock, wlock)
961
961
962 def isapplied(self, patch):
962 def isapplied(self, patch):
963 """returns (index, rev, patch)"""
963 """returns (index, rev, patch)"""
964 for i, a in enumerate(self.applied):
964 for i, a in enumerate(self.applied):
965 if a.name == patch:
965 if a.name == patch:
966 return (i, a.node, a.name)
966 return (i, a.node, a.name)
967 return None
967 return None
968
968
969 # if the exact patch name does not exist, we try a few
969 # if the exact patch name does not exist, we try a few
970 # variations. If strict is passed, we try only #1
970 # variations. If strict is passed, we try only #1
971 #
971 #
972 # 1) a number to indicate an offset in the series file
972 # 1) a number to indicate an offset in the series file
973 # 2) a unique substring of the patch name was given
973 # 2) a unique substring of the patch name was given
974 # 3) patchname[-+]num to indicate an offset in the series file
974 # 3) patchname[-+]num to indicate an offset in the series file
975 def lookup(self, patch, strict=False):
975 def lookup(self, patch, strict=False):
976 patch = patch and str(patch)
976 patch = patch and str(patch)
977
977
978 def partial_name(s):
978 def partial_name(s):
979 if s in self.series:
979 if s in self.series:
980 return s
980 return s
981 matches = [x for x in self.series if s in x]
981 matches = [x for x in self.series if s in x]
982 if len(matches) > 1:
982 if len(matches) > 1:
983 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
983 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
984 for m in matches:
984 for m in matches:
985 self.ui.warn(' %s\n' % m)
985 self.ui.warn(' %s\n' % m)
986 return None
986 return None
987 if matches:
987 if matches:
988 return matches[0]
988 return matches[0]
989 if self.series and self.applied:
989 if self.series and self.applied:
990 if s == 'qtip':
990 if s == 'qtip':
991 return self.series[self.series_end(True)-1]
991 return self.series[self.series_end(True)-1]
992 if s == 'qbase':
992 if s == 'qbase':
993 return self.series[0]
993 return self.series[0]
994 return None
994 return None
995
995
996 if patch is None:
996 if patch is None:
997 return None
997 return None
998 if patch in self.series:
998 if patch in self.series:
999 return patch
999 return patch
1000
1000
1001 if not os.path.isfile(self.join(patch)):
1001 if not os.path.isfile(self.join(patch)):
1002 try:
1002 try:
1003 sno = int(patch)
1003 sno = int(patch)
1004 except (ValueError, OverflowError):
1004 except (ValueError, OverflowError):
1005 pass
1005 pass
1006 else:
1006 else:
1007 if -len(self.series) <= sno < len(self.series):
1007 if -len(self.series) <= sno < len(self.series):
1008 return self.series[sno]
1008 return self.series[sno]
1009
1009
1010 if not strict:
1010 if not strict:
1011 res = partial_name(patch)
1011 res = partial_name(patch)
1012 if res:
1012 if res:
1013 return res
1013 return res
1014 minus = patch.rfind('-')
1014 minus = patch.rfind('-')
1015 if minus >= 0:
1015 if minus >= 0:
1016 res = partial_name(patch[:minus])
1016 res = partial_name(patch[:minus])
1017 if res:
1017 if res:
1018 i = self.series.index(res)
1018 i = self.series.index(res)
1019 try:
1019 try:
1020 off = int(patch[minus + 1:] or 1)
1020 off = int(patch[minus + 1:] or 1)
1021 except (ValueError, OverflowError):
1021 except (ValueError, OverflowError):
1022 pass
1022 pass
1023 else:
1023 else:
1024 if i - off >= 0:
1024 if i - off >= 0:
1025 return self.series[i - off]
1025 return self.series[i - off]
1026 plus = patch.rfind('+')
1026 plus = patch.rfind('+')
1027 if plus >= 0:
1027 if plus >= 0:
1028 res = partial_name(patch[:plus])
1028 res = partial_name(patch[:plus])
1029 if res:
1029 if res:
1030 i = self.series.index(res)
1030 i = self.series.index(res)
1031 try:
1031 try:
1032 off = int(patch[plus + 1:] or 1)
1032 off = int(patch[plus + 1:] or 1)
1033 except (ValueError, OverflowError):
1033 except (ValueError, OverflowError):
1034 pass
1034 pass
1035 else:
1035 else:
1036 if i + off < len(self.series):
1036 if i + off < len(self.series):
1037 return self.series[i + off]
1037 return self.series[i + off]
1038 raise util.Abort(_("patch %s not in series") % patch)
1038 raise util.Abort(_("patch %s not in series") % patch)
1039
1039
1040 def push(self, repo, patch=None, force=False, list=False,
1040 def push(self, repo, patch=None, force=False, list=False,
1041 mergeq=None, all=False, move=False, exact=False):
1041 mergeq=None, all=False, move=False, exact=False):
1042 diffopts = self.diffopts()
1042 diffopts = self.diffopts()
1043 wlock = repo.wlock()
1043 wlock = repo.wlock()
1044 try:
1044 try:
1045 heads = []
1045 heads = []
1046 for b, ls in repo.branchmap().iteritems():
1046 for b, ls in repo.branchmap().iteritems():
1047 heads += ls
1047 heads += ls
1048 if not heads:
1048 if not heads:
1049 heads = [nullid]
1049 heads = [nullid]
1050 if repo.dirstate.parents()[0] not in heads and not exact:
1050 if repo.dirstate.parents()[0] not in heads and not exact:
1051 self.ui.status(_("(working directory not at a head)\n"))
1051 self.ui.status(_("(working directory not at a head)\n"))
1052
1052
1053 if not self.series:
1053 if not self.series:
1054 self.ui.warn(_('no patches in series\n'))
1054 self.ui.warn(_('no patches in series\n'))
1055 return 0
1055 return 0
1056
1056
1057 patch = self.lookup(patch)
1057 patch = self.lookup(patch)
1058 # Suppose our series file is: A B C and the current 'top'
1058 # Suppose our series file is: A B C and the current 'top'
1059 # patch is B. qpush C should be performed (moving forward)
1059 # patch is B. qpush C should be performed (moving forward)
1060 # qpush B is a NOP (no change) qpush A is an error (can't
1060 # qpush B is a NOP (no change) qpush A is an error (can't
1061 # go backwards with qpush)
1061 # go backwards with qpush)
1062 if patch:
1062 if patch:
1063 info = self.isapplied(patch)
1063 info = self.isapplied(patch)
1064 if info:
1064 if info:
1065 if info[0] < len(self.applied) - 1:
1065 if info[0] < len(self.applied) - 1:
1066 raise util.Abort(
1066 raise util.Abort(
1067 _("cannot push to a previous patch: %s") % patch)
1067 _("cannot push to a previous patch: %s") % patch)
1068 self.ui.warn(
1068 self.ui.warn(
1069 _('qpush: %s is already at the top\n') % patch)
1069 _('qpush: %s is already at the top\n') % patch)
1070 return 0
1070 return 0
1071 pushable, reason = self.pushable(patch)
1071 pushable, reason = self.pushable(patch)
1072 if not pushable:
1072 if not pushable:
1073 if reason:
1073 if reason:
1074 reason = _('guarded by %r') % reason
1074 reason = _('guarded by %r') % reason
1075 else:
1075 else:
1076 reason = _('no matching guards')
1076 reason = _('no matching guards')
1077 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1077 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
1078 return 1
1078 return 1
1079 elif all:
1079 elif all:
1080 patch = self.series[-1]
1080 patch = self.series[-1]
1081 if self.isapplied(patch):
1081 if self.isapplied(patch):
1082 self.ui.warn(_('all patches are currently applied\n'))
1082 self.ui.warn(_('all patches are currently applied\n'))
1083 return 0
1083 return 0
1084
1084
1085 # Following the above example, starting at 'top' of B:
1085 # Following the above example, starting at 'top' of B:
1086 # qpush should be performed (pushes C), but a subsequent
1086 # qpush should be performed (pushes C), but a subsequent
1087 # qpush without an argument is an error (nothing to
1087 # qpush without an argument is an error (nothing to
1088 # apply). This allows a loop of "...while hg qpush..." to
1088 # apply). This allows a loop of "...while hg qpush..." to
1089 # work as it detects an error when done
1089 # work as it detects an error when done
1090 start = self.series_end()
1090 start = self.series_end()
1091 if start == len(self.series):
1091 if start == len(self.series):
1092 self.ui.warn(_('patch series already fully applied\n'))
1092 self.ui.warn(_('patch series already fully applied\n'))
1093 return 1
1093 return 1
1094 if not force:
1094 if not force:
1095 self.check_localchanges(repo)
1095 self.check_localchanges(repo)
1096
1096
1097 if exact:
1097 if exact:
1098 if move:
1098 if move:
1099 raise util.Abort(_("cannot use --exact and --move together"))
1099 raise util.Abort(_("cannot use --exact and --move together"))
1100 if self.applied:
1100 if self.applied:
1101 raise util.Abort(_("cannot push --exact with applied patches"))
1101 raise util.Abort(_("cannot push --exact with applied patches"))
1102 root = self.series[start]
1102 root = self.series[start]
1103 target = patchheader(self.join(root), self.plainmode).parent
1103 target = patchheader(self.join(root), self.plainmode).parent
1104 if not target:
1104 if not target:
1105 raise util.Abort(_("%s does not have a parent recorded" % root))
1105 raise util.Abort(_("%s does not have a parent recorded" % root))
1106 if not repo[target] == repo['.']:
1106 if not repo[target] == repo['.']:
1107 hg.update(repo, target)
1107 hg.update(repo, target)
1108
1108
1109 if move:
1109 if move:
1110 if not patch:
1110 if not patch:
1111 raise util.Abort(_("please specify the patch to move"))
1111 raise util.Abort(_("please specify the patch to move"))
1112 for i, rpn in enumerate(self.full_series[start:]):
1112 for i, rpn in enumerate(self.full_series[start:]):
1113 # strip markers for patch guards
1113 # strip markers for patch guards
1114 if self.guard_re.split(rpn, 1)[0] == patch:
1114 if self.guard_re.split(rpn, 1)[0] == patch:
1115 break
1115 break
1116 index = start + i
1116 index = start + i
1117 assert index < len(self.full_series)
1117 assert index < len(self.full_series)
1118 fullpatch = self.full_series[index]
1118 fullpatch = self.full_series[index]
1119 del self.full_series[index]
1119 del self.full_series[index]
1120 self.full_series.insert(start, fullpatch)
1120 self.full_series.insert(start, fullpatch)
1121 self.parse_series()
1121 self.parse_series()
1122 self.series_dirty = 1
1122 self.series_dirty = 1
1123
1123
1124 self.applied_dirty = 1
1124 self.applied_dirty = 1
1125 if start > 0:
1125 if start > 0:
1126 self.check_toppatch(repo)
1126 self.check_toppatch(repo)
1127 if not patch:
1127 if not patch:
1128 patch = self.series[start]
1128 patch = self.series[start]
1129 end = start + 1
1129 end = start + 1
1130 else:
1130 else:
1131 end = self.series.index(patch, start) + 1
1131 end = self.series.index(patch, start) + 1
1132
1132
1133 s = self.series[start:end]
1133 s = self.series[start:end]
1134 all_files = set()
1134 all_files = set()
1135 try:
1135 try:
1136 if mergeq:
1136 if mergeq:
1137 ret = self.mergepatch(repo, mergeq, s, diffopts)
1137 ret = self.mergepatch(repo, mergeq, s, diffopts)
1138 else:
1138 else:
1139 ret = self.apply(repo, s, list, all_files=all_files)
1139 ret = self.apply(repo, s, list, all_files=all_files)
1140 except:
1140 except:
1141 self.ui.warn(_('cleaning up working directory...'))
1141 self.ui.warn(_('cleaning up working directory...'))
1142 node = repo.dirstate.parents()[0]
1142 node = repo.dirstate.parents()[0]
1143 hg.revert(repo, node, None)
1143 hg.revert(repo, node, None)
1144 # only remove unknown files that we know we touched or
1144 # only remove unknown files that we know we touched or
1145 # created while patching
1145 # created while patching
1146 for f in all_files:
1146 for f in all_files:
1147 if f not in repo.dirstate:
1147 if f not in repo.dirstate:
1148 try:
1148 try:
1149 util.unlink(repo.wjoin(f))
1149 util.unlinkpath(repo.wjoin(f))
1150 except OSError, inst:
1150 except OSError, inst:
1151 if inst.errno != errno.ENOENT:
1151 if inst.errno != errno.ENOENT:
1152 raise
1152 raise
1153 self.ui.warn(_('done\n'))
1153 self.ui.warn(_('done\n'))
1154 raise
1154 raise
1155
1155
1156 if not self.applied:
1156 if not self.applied:
1157 return ret[0]
1157 return ret[0]
1158 top = self.applied[-1].name
1158 top = self.applied[-1].name
1159 if ret[0] and ret[0] > 1:
1159 if ret[0] and ret[0] > 1:
1160 msg = _("errors during apply, please fix and refresh %s\n")
1160 msg = _("errors during apply, please fix and refresh %s\n")
1161 self.ui.write(msg % top)
1161 self.ui.write(msg % top)
1162 else:
1162 else:
1163 self.ui.write(_("now at: %s\n") % top)
1163 self.ui.write(_("now at: %s\n") % top)
1164 return ret[0]
1164 return ret[0]
1165
1165
1166 finally:
1166 finally:
1167 wlock.release()
1167 wlock.release()
1168
1168
1169 def pop(self, repo, patch=None, force=False, update=True, all=False):
1169 def pop(self, repo, patch=None, force=False, update=True, all=False):
1170 wlock = repo.wlock()
1170 wlock = repo.wlock()
1171 try:
1171 try:
1172 if patch:
1172 if patch:
1173 # index, rev, patch
1173 # index, rev, patch
1174 info = self.isapplied(patch)
1174 info = self.isapplied(patch)
1175 if not info:
1175 if not info:
1176 patch = self.lookup(patch)
1176 patch = self.lookup(patch)
1177 info = self.isapplied(patch)
1177 info = self.isapplied(patch)
1178 if not info:
1178 if not info:
1179 raise util.Abort(_("patch %s is not applied") % patch)
1179 raise util.Abort(_("patch %s is not applied") % patch)
1180
1180
1181 if not self.applied:
1181 if not self.applied:
1182 # Allow qpop -a to work repeatedly,
1182 # Allow qpop -a to work repeatedly,
1183 # but not qpop without an argument
1183 # but not qpop without an argument
1184 self.ui.warn(_("no patches applied\n"))
1184 self.ui.warn(_("no patches applied\n"))
1185 return not all
1185 return not all
1186
1186
1187 if all:
1187 if all:
1188 start = 0
1188 start = 0
1189 elif patch:
1189 elif patch:
1190 start = info[0] + 1
1190 start = info[0] + 1
1191 else:
1191 else:
1192 start = len(self.applied) - 1
1192 start = len(self.applied) - 1
1193
1193
1194 if start >= len(self.applied):
1194 if start >= len(self.applied):
1195 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1195 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1196 return
1196 return
1197
1197
1198 if not update:
1198 if not update:
1199 parents = repo.dirstate.parents()
1199 parents = repo.dirstate.parents()
1200 rr = [x.node for x in self.applied]
1200 rr = [x.node for x in self.applied]
1201 for p in parents:
1201 for p in parents:
1202 if p in rr:
1202 if p in rr:
1203 self.ui.warn(_("qpop: forcing dirstate update\n"))
1203 self.ui.warn(_("qpop: forcing dirstate update\n"))
1204 update = True
1204 update = True
1205 else:
1205 else:
1206 parents = [p.node() for p in repo[None].parents()]
1206 parents = [p.node() for p in repo[None].parents()]
1207 needupdate = False
1207 needupdate = False
1208 for entry in self.applied[start:]:
1208 for entry in self.applied[start:]:
1209 if entry.node in parents:
1209 if entry.node in parents:
1210 needupdate = True
1210 needupdate = True
1211 break
1211 break
1212 update = needupdate
1212 update = needupdate
1213
1213
1214 if not force and update:
1214 if not force and update:
1215 self.check_localchanges(repo)
1215 self.check_localchanges(repo)
1216
1216
1217 self.applied_dirty = 1
1217 self.applied_dirty = 1
1218 end = len(self.applied)
1218 end = len(self.applied)
1219 rev = self.applied[start].node
1219 rev = self.applied[start].node
1220 if update:
1220 if update:
1221 top = self.check_toppatch(repo)[0]
1221 top = self.check_toppatch(repo)[0]
1222
1222
1223 try:
1223 try:
1224 heads = repo.changelog.heads(rev)
1224 heads = repo.changelog.heads(rev)
1225 except error.LookupError:
1225 except error.LookupError:
1226 node = short(rev)
1226 node = short(rev)
1227 raise util.Abort(_('trying to pop unknown node %s') % node)
1227 raise util.Abort(_('trying to pop unknown node %s') % node)
1228
1228
1229 if heads != [self.applied[-1].node]:
1229 if heads != [self.applied[-1].node]:
1230 raise util.Abort(_("popping would remove a revision not "
1230 raise util.Abort(_("popping would remove a revision not "
1231 "managed by this patch queue"))
1231 "managed by this patch queue"))
1232
1232
1233 # we know there are no local changes, so we can make a simplified
1233 # we know there are no local changes, so we can make a simplified
1234 # form of hg.update.
1234 # form of hg.update.
1235 if update:
1235 if update:
1236 qp = self.qparents(repo, rev)
1236 qp = self.qparents(repo, rev)
1237 ctx = repo[qp]
1237 ctx = repo[qp]
1238 m, a, r, d = repo.status(qp, top)[:4]
1238 m, a, r, d = repo.status(qp, top)[:4]
1239 if d:
1239 if d:
1240 raise util.Abort(_("deletions found between repo revs"))
1240 raise util.Abort(_("deletions found between repo revs"))
1241 for f in a:
1241 for f in a:
1242 try:
1242 try:
1243 util.unlink(repo.wjoin(f))
1243 util.unlinkpath(repo.wjoin(f))
1244 except OSError, e:
1244 except OSError, e:
1245 if e.errno != errno.ENOENT:
1245 if e.errno != errno.ENOENT:
1246 raise
1246 raise
1247 repo.dirstate.forget(f)
1247 repo.dirstate.forget(f)
1248 for f in m + r:
1248 for f in m + r:
1249 fctx = ctx[f]
1249 fctx = ctx[f]
1250 repo.wwrite(f, fctx.data(), fctx.flags())
1250 repo.wwrite(f, fctx.data(), fctx.flags())
1251 repo.dirstate.normal(f)
1251 repo.dirstate.normal(f)
1252 repo.dirstate.setparents(qp, nullid)
1252 repo.dirstate.setparents(qp, nullid)
1253 for patch in reversed(self.applied[start:end]):
1253 for patch in reversed(self.applied[start:end]):
1254 self.ui.status(_("popping %s\n") % patch.name)
1254 self.ui.status(_("popping %s\n") % patch.name)
1255 del self.applied[start:end]
1255 del self.applied[start:end]
1256 self.strip(repo, [rev], update=False, backup='strip')
1256 self.strip(repo, [rev], update=False, backup='strip')
1257 if self.applied:
1257 if self.applied:
1258 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1258 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1259 else:
1259 else:
1260 self.ui.write(_("patch queue now empty\n"))
1260 self.ui.write(_("patch queue now empty\n"))
1261 finally:
1261 finally:
1262 wlock.release()
1262 wlock.release()
1263
1263
1264 def diff(self, repo, pats, opts):
1264 def diff(self, repo, pats, opts):
1265 top, patch = self.check_toppatch(repo)
1265 top, patch = self.check_toppatch(repo)
1266 if not top:
1266 if not top:
1267 self.ui.write(_("no patches applied\n"))
1267 self.ui.write(_("no patches applied\n"))
1268 return
1268 return
1269 qp = self.qparents(repo, top)
1269 qp = self.qparents(repo, top)
1270 if opts.get('reverse'):
1270 if opts.get('reverse'):
1271 node1, node2 = None, qp
1271 node1, node2 = None, qp
1272 else:
1272 else:
1273 node1, node2 = qp, None
1273 node1, node2 = qp, None
1274 diffopts = self.diffopts(opts, patch)
1274 diffopts = self.diffopts(opts, patch)
1275 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1275 self.printdiff(repo, diffopts, node1, node2, files=pats, opts=opts)
1276
1276
1277 def refresh(self, repo, pats=None, **opts):
1277 def refresh(self, repo, pats=None, **opts):
1278 if not self.applied:
1278 if not self.applied:
1279 self.ui.write(_("no patches applied\n"))
1279 self.ui.write(_("no patches applied\n"))
1280 return 1
1280 return 1
1281 msg = opts.get('msg', '').rstrip()
1281 msg = opts.get('msg', '').rstrip()
1282 newuser = opts.get('user')
1282 newuser = opts.get('user')
1283 newdate = opts.get('date')
1283 newdate = opts.get('date')
1284 if newdate:
1284 if newdate:
1285 newdate = '%d %d' % util.parsedate(newdate)
1285 newdate = '%d %d' % util.parsedate(newdate)
1286 wlock = repo.wlock()
1286 wlock = repo.wlock()
1287
1287
1288 try:
1288 try:
1289 self.check_toppatch(repo)
1289 self.check_toppatch(repo)
1290 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1290 (top, patchfn) = (self.applied[-1].node, self.applied[-1].name)
1291 if repo.changelog.heads(top) != [top]:
1291 if repo.changelog.heads(top) != [top]:
1292 raise util.Abort(_("cannot refresh a revision with children"))
1292 raise util.Abort(_("cannot refresh a revision with children"))
1293
1293
1294 inclsubs = self.check_substate(repo)
1294 inclsubs = self.check_substate(repo)
1295
1295
1296 cparents = repo.changelog.parents(top)
1296 cparents = repo.changelog.parents(top)
1297 patchparent = self.qparents(repo, top)
1297 patchparent = self.qparents(repo, top)
1298 ph = patchheader(self.join(patchfn), self.plainmode)
1298 ph = patchheader(self.join(patchfn), self.plainmode)
1299 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1299 diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
1300 if msg:
1300 if msg:
1301 ph.setmessage(msg)
1301 ph.setmessage(msg)
1302 if newuser:
1302 if newuser:
1303 ph.setuser(newuser)
1303 ph.setuser(newuser)
1304 if newdate:
1304 if newdate:
1305 ph.setdate(newdate)
1305 ph.setdate(newdate)
1306 ph.setparent(hex(patchparent))
1306 ph.setparent(hex(patchparent))
1307
1307
1308 # only commit new patch when write is complete
1308 # only commit new patch when write is complete
1309 patchf = self.opener(patchfn, 'w', atomictemp=True)
1309 patchf = self.opener(patchfn, 'w', atomictemp=True)
1310
1310
1311 comments = str(ph)
1311 comments = str(ph)
1312 if comments:
1312 if comments:
1313 patchf.write(comments)
1313 patchf.write(comments)
1314
1314
1315 # update the dirstate in place, strip off the qtip commit
1315 # update the dirstate in place, strip off the qtip commit
1316 # and then commit.
1316 # and then commit.
1317 #
1317 #
1318 # this should really read:
1318 # this should really read:
1319 # mm, dd, aa = repo.status(top, patchparent)[:3]
1319 # mm, dd, aa = repo.status(top, patchparent)[:3]
1320 # but we do it backwards to take advantage of manifest/chlog
1320 # but we do it backwards to take advantage of manifest/chlog
1321 # caching against the next repo.status call
1321 # caching against the next repo.status call
1322 mm, aa, dd = repo.status(patchparent, top)[:3]
1322 mm, aa, dd = repo.status(patchparent, top)[:3]
1323 changes = repo.changelog.read(top)
1323 changes = repo.changelog.read(top)
1324 man = repo.manifest.read(changes[0])
1324 man = repo.manifest.read(changes[0])
1325 aaa = aa[:]
1325 aaa = aa[:]
1326 matchfn = cmdutil.match(repo, pats, opts)
1326 matchfn = cmdutil.match(repo, pats, opts)
1327 # in short mode, we only diff the files included in the
1327 # in short mode, we only diff the files included in the
1328 # patch already plus specified files
1328 # patch already plus specified files
1329 if opts.get('short'):
1329 if opts.get('short'):
1330 # if amending a patch, we start with existing
1330 # if amending a patch, we start with existing
1331 # files plus specified files - unfiltered
1331 # files plus specified files - unfiltered
1332 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1332 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1333 # filter with inc/exl options
1333 # filter with inc/exl options
1334 matchfn = cmdutil.match(repo, opts=opts)
1334 matchfn = cmdutil.match(repo, opts=opts)
1335 else:
1335 else:
1336 match = cmdutil.matchall(repo)
1336 match = cmdutil.matchall(repo)
1337 m, a, r, d = repo.status(match=match)[:4]
1337 m, a, r, d = repo.status(match=match)[:4]
1338 mm = set(mm)
1338 mm = set(mm)
1339 aa = set(aa)
1339 aa = set(aa)
1340 dd = set(dd)
1340 dd = set(dd)
1341
1341
1342 # we might end up with files that were added between
1342 # we might end up with files that were added between
1343 # qtip and the dirstate parent, but then changed in the
1343 # qtip and the dirstate parent, but then changed in the
1344 # local dirstate. in this case, we want them to only
1344 # local dirstate. in this case, we want them to only
1345 # show up in the added section
1345 # show up in the added section
1346 for x in m:
1346 for x in m:
1347 if x not in aa:
1347 if x not in aa:
1348 mm.add(x)
1348 mm.add(x)
1349 # we might end up with files added by the local dirstate that
1349 # we might end up with files added by the local dirstate that
1350 # were deleted by the patch. In this case, they should only
1350 # were deleted by the patch. In this case, they should only
1351 # show up in the changed section.
1351 # show up in the changed section.
1352 for x in a:
1352 for x in a:
1353 if x in dd:
1353 if x in dd:
1354 dd.remove(x)
1354 dd.remove(x)
1355 mm.add(x)
1355 mm.add(x)
1356 else:
1356 else:
1357 aa.add(x)
1357 aa.add(x)
1358 # make sure any files deleted in the local dirstate
1358 # make sure any files deleted in the local dirstate
1359 # are not in the add or change column of the patch
1359 # are not in the add or change column of the patch
1360 forget = []
1360 forget = []
1361 for x in d + r:
1361 for x in d + r:
1362 if x in aa:
1362 if x in aa:
1363 aa.remove(x)
1363 aa.remove(x)
1364 forget.append(x)
1364 forget.append(x)
1365 continue
1365 continue
1366 else:
1366 else:
1367 mm.discard(x)
1367 mm.discard(x)
1368 dd.add(x)
1368 dd.add(x)
1369
1369
1370 m = list(mm)
1370 m = list(mm)
1371 r = list(dd)
1371 r = list(dd)
1372 a = list(aa)
1372 a = list(aa)
1373 c = [filter(matchfn, l) for l in (m, a, r)]
1373 c = [filter(matchfn, l) for l in (m, a, r)]
1374 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1374 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2] + inclsubs))
1375 chunks = patch.diff(repo, patchparent, match=match,
1375 chunks = patch.diff(repo, patchparent, match=match,
1376 changes=c, opts=diffopts)
1376 changes=c, opts=diffopts)
1377 for chunk in chunks:
1377 for chunk in chunks:
1378 patchf.write(chunk)
1378 patchf.write(chunk)
1379
1379
1380 try:
1380 try:
1381 if diffopts.git or diffopts.upgrade:
1381 if diffopts.git or diffopts.upgrade:
1382 copies = {}
1382 copies = {}
1383 for dst in a:
1383 for dst in a:
1384 src = repo.dirstate.copied(dst)
1384 src = repo.dirstate.copied(dst)
1385 # during qfold, the source file for copies may
1385 # during qfold, the source file for copies may
1386 # be removed. Treat this as a simple add.
1386 # be removed. Treat this as a simple add.
1387 if src is not None and src in repo.dirstate:
1387 if src is not None and src in repo.dirstate:
1388 copies.setdefault(src, []).append(dst)
1388 copies.setdefault(src, []).append(dst)
1389 repo.dirstate.add(dst)
1389 repo.dirstate.add(dst)
1390 # remember the copies between patchparent and qtip
1390 # remember the copies between patchparent and qtip
1391 for dst in aaa:
1391 for dst in aaa:
1392 f = repo.file(dst)
1392 f = repo.file(dst)
1393 src = f.renamed(man[dst])
1393 src = f.renamed(man[dst])
1394 if src:
1394 if src:
1395 copies.setdefault(src[0], []).extend(
1395 copies.setdefault(src[0], []).extend(
1396 copies.get(dst, []))
1396 copies.get(dst, []))
1397 if dst in a:
1397 if dst in a:
1398 copies[src[0]].append(dst)
1398 copies[src[0]].append(dst)
1399 # we can't copy a file created by the patch itself
1399 # we can't copy a file created by the patch itself
1400 if dst in copies:
1400 if dst in copies:
1401 del copies[dst]
1401 del copies[dst]
1402 for src, dsts in copies.iteritems():
1402 for src, dsts in copies.iteritems():
1403 for dst in dsts:
1403 for dst in dsts:
1404 repo.dirstate.copy(src, dst)
1404 repo.dirstate.copy(src, dst)
1405 else:
1405 else:
1406 for dst in a:
1406 for dst in a:
1407 repo.dirstate.add(dst)
1407 repo.dirstate.add(dst)
1408 # Drop useless copy information
1408 # Drop useless copy information
1409 for f in list(repo.dirstate.copies()):
1409 for f in list(repo.dirstate.copies()):
1410 repo.dirstate.copy(None, f)
1410 repo.dirstate.copy(None, f)
1411 for f in r:
1411 for f in r:
1412 repo.dirstate.remove(f)
1412 repo.dirstate.remove(f)
1413 # if the patch excludes a modified file, mark that
1413 # if the patch excludes a modified file, mark that
1414 # file with mtime=0 so status can see it.
1414 # file with mtime=0 so status can see it.
1415 mm = []
1415 mm = []
1416 for i in xrange(len(m)-1, -1, -1):
1416 for i in xrange(len(m)-1, -1, -1):
1417 if not matchfn(m[i]):
1417 if not matchfn(m[i]):
1418 mm.append(m[i])
1418 mm.append(m[i])
1419 del m[i]
1419 del m[i]
1420 for f in m:
1420 for f in m:
1421 repo.dirstate.normal(f)
1421 repo.dirstate.normal(f)
1422 for f in mm:
1422 for f in mm:
1423 repo.dirstate.normallookup(f)
1423 repo.dirstate.normallookup(f)
1424 for f in forget:
1424 for f in forget:
1425 repo.dirstate.forget(f)
1425 repo.dirstate.forget(f)
1426
1426
1427 if not msg:
1427 if not msg:
1428 if not ph.message:
1428 if not ph.message:
1429 message = "[mq]: %s\n" % patchfn
1429 message = "[mq]: %s\n" % patchfn
1430 else:
1430 else:
1431 message = "\n".join(ph.message)
1431 message = "\n".join(ph.message)
1432 else:
1432 else:
1433 message = msg
1433 message = msg
1434
1434
1435 user = ph.user or changes[1]
1435 user = ph.user or changes[1]
1436
1436
1437 # assumes strip can roll itself back if interrupted
1437 # assumes strip can roll itself back if interrupted
1438 repo.dirstate.setparents(*cparents)
1438 repo.dirstate.setparents(*cparents)
1439 self.applied.pop()
1439 self.applied.pop()
1440 self.applied_dirty = 1
1440 self.applied_dirty = 1
1441 self.strip(repo, [top], update=False,
1441 self.strip(repo, [top], update=False,
1442 backup='strip')
1442 backup='strip')
1443 except:
1443 except:
1444 repo.dirstate.invalidate()
1444 repo.dirstate.invalidate()
1445 raise
1445 raise
1446
1446
1447 try:
1447 try:
1448 # might be nice to attempt to roll back strip after this
1448 # might be nice to attempt to roll back strip after this
1449 patchf.rename()
1449 patchf.rename()
1450 n = repo.commit(message, user, ph.date, match=match,
1450 n = repo.commit(message, user, ph.date, match=match,
1451 force=True)
1451 force=True)
1452 self.applied.append(statusentry(n, patchfn))
1452 self.applied.append(statusentry(n, patchfn))
1453 except:
1453 except:
1454 ctx = repo[cparents[0]]
1454 ctx = repo[cparents[0]]
1455 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1455 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1456 self.save_dirty()
1456 self.save_dirty()
1457 self.ui.warn(_('refresh interrupted while patch was popped! '
1457 self.ui.warn(_('refresh interrupted while patch was popped! '
1458 '(revert --all, qpush to recover)\n'))
1458 '(revert --all, qpush to recover)\n'))
1459 raise
1459 raise
1460 finally:
1460 finally:
1461 wlock.release()
1461 wlock.release()
1462 self.removeundo(repo)
1462 self.removeundo(repo)
1463
1463
1464 def init(self, repo, create=False):
1464 def init(self, repo, create=False):
1465 if not create and os.path.isdir(self.path):
1465 if not create and os.path.isdir(self.path):
1466 raise util.Abort(_("patch queue directory already exists"))
1466 raise util.Abort(_("patch queue directory already exists"))
1467 try:
1467 try:
1468 os.mkdir(self.path)
1468 os.mkdir(self.path)
1469 except OSError, inst:
1469 except OSError, inst:
1470 if inst.errno != errno.EEXIST or not create:
1470 if inst.errno != errno.EEXIST or not create:
1471 raise
1471 raise
1472 if create:
1472 if create:
1473 return self.qrepo(create=True)
1473 return self.qrepo(create=True)
1474
1474
1475 def unapplied(self, repo, patch=None):
1475 def unapplied(self, repo, patch=None):
1476 if patch and patch not in self.series:
1476 if patch and patch not in self.series:
1477 raise util.Abort(_("patch %s is not in series file") % patch)
1477 raise util.Abort(_("patch %s is not in series file") % patch)
1478 if not patch:
1478 if not patch:
1479 start = self.series_end()
1479 start = self.series_end()
1480 else:
1480 else:
1481 start = self.series.index(patch) + 1
1481 start = self.series.index(patch) + 1
1482 unapplied = []
1482 unapplied = []
1483 for i in xrange(start, len(self.series)):
1483 for i in xrange(start, len(self.series)):
1484 pushable, reason = self.pushable(i)
1484 pushable, reason = self.pushable(i)
1485 if pushable:
1485 if pushable:
1486 unapplied.append((i, self.series[i]))
1486 unapplied.append((i, self.series[i]))
1487 self.explain_pushable(i)
1487 self.explain_pushable(i)
1488 return unapplied
1488 return unapplied
1489
1489
1490 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1490 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1491 summary=False):
1491 summary=False):
1492 def displayname(pfx, patchname, state):
1492 def displayname(pfx, patchname, state):
1493 if pfx:
1493 if pfx:
1494 self.ui.write(pfx)
1494 self.ui.write(pfx)
1495 if summary:
1495 if summary:
1496 ph = patchheader(self.join(patchname), self.plainmode)
1496 ph = patchheader(self.join(patchname), self.plainmode)
1497 msg = ph.message and ph.message[0] or ''
1497 msg = ph.message and ph.message[0] or ''
1498 if self.ui.formatted():
1498 if self.ui.formatted():
1499 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1499 width = self.ui.termwidth() - len(pfx) - len(patchname) - 2
1500 if width > 0:
1500 if width > 0:
1501 msg = util.ellipsis(msg, width)
1501 msg = util.ellipsis(msg, width)
1502 else:
1502 else:
1503 msg = ''
1503 msg = ''
1504 self.ui.write(patchname, label='qseries.' + state)
1504 self.ui.write(patchname, label='qseries.' + state)
1505 self.ui.write(': ')
1505 self.ui.write(': ')
1506 self.ui.write(msg, label='qseries.message.' + state)
1506 self.ui.write(msg, label='qseries.message.' + state)
1507 else:
1507 else:
1508 self.ui.write(patchname, label='qseries.' + state)
1508 self.ui.write(patchname, label='qseries.' + state)
1509 self.ui.write('\n')
1509 self.ui.write('\n')
1510
1510
1511 applied = set([p.name for p in self.applied])
1511 applied = set([p.name for p in self.applied])
1512 if length is None:
1512 if length is None:
1513 length = len(self.series) - start
1513 length = len(self.series) - start
1514 if not missing:
1514 if not missing:
1515 if self.ui.verbose:
1515 if self.ui.verbose:
1516 idxwidth = len(str(start + length - 1))
1516 idxwidth = len(str(start + length - 1))
1517 for i in xrange(start, start + length):
1517 for i in xrange(start, start + length):
1518 patch = self.series[i]
1518 patch = self.series[i]
1519 if patch in applied:
1519 if patch in applied:
1520 char, state = 'A', 'applied'
1520 char, state = 'A', 'applied'
1521 elif self.pushable(i)[0]:
1521 elif self.pushable(i)[0]:
1522 char, state = 'U', 'unapplied'
1522 char, state = 'U', 'unapplied'
1523 else:
1523 else:
1524 char, state = 'G', 'guarded'
1524 char, state = 'G', 'guarded'
1525 pfx = ''
1525 pfx = ''
1526 if self.ui.verbose:
1526 if self.ui.verbose:
1527 pfx = '%*d %s ' % (idxwidth, i, char)
1527 pfx = '%*d %s ' % (idxwidth, i, char)
1528 elif status and status != char:
1528 elif status and status != char:
1529 continue
1529 continue
1530 displayname(pfx, patch, state)
1530 displayname(pfx, patch, state)
1531 else:
1531 else:
1532 msng_list = []
1532 msng_list = []
1533 for root, dirs, files in os.walk(self.path):
1533 for root, dirs, files in os.walk(self.path):
1534 d = root[len(self.path) + 1:]
1534 d = root[len(self.path) + 1:]
1535 for f in files:
1535 for f in files:
1536 fl = os.path.join(d, f)
1536 fl = os.path.join(d, f)
1537 if (fl not in self.series and
1537 if (fl not in self.series and
1538 fl not in (self.status_path, self.series_path,
1538 fl not in (self.status_path, self.series_path,
1539 self.guards_path)
1539 self.guards_path)
1540 and not fl.startswith('.')):
1540 and not fl.startswith('.')):
1541 msng_list.append(fl)
1541 msng_list.append(fl)
1542 for x in sorted(msng_list):
1542 for x in sorted(msng_list):
1543 pfx = self.ui.verbose and ('D ') or ''
1543 pfx = self.ui.verbose and ('D ') or ''
1544 displayname(pfx, x, 'missing')
1544 displayname(pfx, x, 'missing')
1545
1545
1546 def issaveline(self, l):
1546 def issaveline(self, l):
1547 if l.name == '.hg.patches.save.line':
1547 if l.name == '.hg.patches.save.line':
1548 return True
1548 return True
1549
1549
1550 def qrepo(self, create=False):
1550 def qrepo(self, create=False):
1551 ui = self.ui.copy()
1551 ui = self.ui.copy()
1552 ui.setconfig('paths', 'default', '', overlay=False)
1552 ui.setconfig('paths', 'default', '', overlay=False)
1553 ui.setconfig('paths', 'default-push', '', overlay=False)
1553 ui.setconfig('paths', 'default-push', '', overlay=False)
1554 if create or os.path.isdir(self.join(".hg")):
1554 if create or os.path.isdir(self.join(".hg")):
1555 return hg.repository(ui, path=self.path, create=create)
1555 return hg.repository(ui, path=self.path, create=create)
1556
1556
1557 def restore(self, repo, rev, delete=None, qupdate=None):
1557 def restore(self, repo, rev, delete=None, qupdate=None):
1558 desc = repo[rev].description().strip()
1558 desc = repo[rev].description().strip()
1559 lines = desc.splitlines()
1559 lines = desc.splitlines()
1560 i = 0
1560 i = 0
1561 datastart = None
1561 datastart = None
1562 series = []
1562 series = []
1563 applied = []
1563 applied = []
1564 qpp = None
1564 qpp = None
1565 for i, line in enumerate(lines):
1565 for i, line in enumerate(lines):
1566 if line == 'Patch Data:':
1566 if line == 'Patch Data:':
1567 datastart = i + 1
1567 datastart = i + 1
1568 elif line.startswith('Dirstate:'):
1568 elif line.startswith('Dirstate:'):
1569 l = line.rstrip()
1569 l = line.rstrip()
1570 l = l[10:].split(' ')
1570 l = l[10:].split(' ')
1571 qpp = [bin(x) for x in l]
1571 qpp = [bin(x) for x in l]
1572 elif datastart is not None:
1572 elif datastart is not None:
1573 l = line.rstrip()
1573 l = line.rstrip()
1574 n, name = l.split(':', 1)
1574 n, name = l.split(':', 1)
1575 if n:
1575 if n:
1576 applied.append(statusentry(bin(n), name))
1576 applied.append(statusentry(bin(n), name))
1577 else:
1577 else:
1578 series.append(l)
1578 series.append(l)
1579 if datastart is None:
1579 if datastart is None:
1580 self.ui.warn(_("No saved patch data found\n"))
1580 self.ui.warn(_("No saved patch data found\n"))
1581 return 1
1581 return 1
1582 self.ui.warn(_("restoring status: %s\n") % lines[0])
1582 self.ui.warn(_("restoring status: %s\n") % lines[0])
1583 self.full_series = series
1583 self.full_series = series
1584 self.applied = applied
1584 self.applied = applied
1585 self.parse_series()
1585 self.parse_series()
1586 self.series_dirty = 1
1586 self.series_dirty = 1
1587 self.applied_dirty = 1
1587 self.applied_dirty = 1
1588 heads = repo.changelog.heads()
1588 heads = repo.changelog.heads()
1589 if delete:
1589 if delete:
1590 if rev not in heads:
1590 if rev not in heads:
1591 self.ui.warn(_("save entry has children, leaving it alone\n"))
1591 self.ui.warn(_("save entry has children, leaving it alone\n"))
1592 else:
1592 else:
1593 self.ui.warn(_("removing save entry %s\n") % short(rev))
1593 self.ui.warn(_("removing save entry %s\n") % short(rev))
1594 pp = repo.dirstate.parents()
1594 pp = repo.dirstate.parents()
1595 if rev in pp:
1595 if rev in pp:
1596 update = True
1596 update = True
1597 else:
1597 else:
1598 update = False
1598 update = False
1599 self.strip(repo, [rev], update=update, backup='strip')
1599 self.strip(repo, [rev], update=update, backup='strip')
1600 if qpp:
1600 if qpp:
1601 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1601 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1602 (short(qpp[0]), short(qpp[1])))
1602 (short(qpp[0]), short(qpp[1])))
1603 if qupdate:
1603 if qupdate:
1604 self.ui.status(_("updating queue directory\n"))
1604 self.ui.status(_("updating queue directory\n"))
1605 r = self.qrepo()
1605 r = self.qrepo()
1606 if not r:
1606 if not r:
1607 self.ui.warn(_("Unable to load queue repository\n"))
1607 self.ui.warn(_("Unable to load queue repository\n"))
1608 return 1
1608 return 1
1609 hg.clean(r, qpp[0])
1609 hg.clean(r, qpp[0])
1610
1610
1611 def save(self, repo, msg=None):
1611 def save(self, repo, msg=None):
1612 if not self.applied:
1612 if not self.applied:
1613 self.ui.warn(_("save: no patches applied, exiting\n"))
1613 self.ui.warn(_("save: no patches applied, exiting\n"))
1614 return 1
1614 return 1
1615 if self.issaveline(self.applied[-1]):
1615 if self.issaveline(self.applied[-1]):
1616 self.ui.warn(_("status is already saved\n"))
1616 self.ui.warn(_("status is already saved\n"))
1617 return 1
1617 return 1
1618
1618
1619 if not msg:
1619 if not msg:
1620 msg = _("hg patches saved state")
1620 msg = _("hg patches saved state")
1621 else:
1621 else:
1622 msg = "hg patches: " + msg.rstrip('\r\n')
1622 msg = "hg patches: " + msg.rstrip('\r\n')
1623 r = self.qrepo()
1623 r = self.qrepo()
1624 if r:
1624 if r:
1625 pp = r.dirstate.parents()
1625 pp = r.dirstate.parents()
1626 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1626 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1627 msg += "\n\nPatch Data:\n"
1627 msg += "\n\nPatch Data:\n"
1628 msg += ''.join('%s\n' % x for x in self.applied)
1628 msg += ''.join('%s\n' % x for x in self.applied)
1629 msg += ''.join(':%s\n' % x for x in self.full_series)
1629 msg += ''.join(':%s\n' % x for x in self.full_series)
1630 n = repo.commit(msg, force=True)
1630 n = repo.commit(msg, force=True)
1631 if not n:
1631 if not n:
1632 self.ui.warn(_("repo commit failed\n"))
1632 self.ui.warn(_("repo commit failed\n"))
1633 return 1
1633 return 1
1634 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1634 self.applied.append(statusentry(n, '.hg.patches.save.line'))
1635 self.applied_dirty = 1
1635 self.applied_dirty = 1
1636 self.removeundo(repo)
1636 self.removeundo(repo)
1637
1637
1638 def full_series_end(self):
1638 def full_series_end(self):
1639 if self.applied:
1639 if self.applied:
1640 p = self.applied[-1].name
1640 p = self.applied[-1].name
1641 end = self.find_series(p)
1641 end = self.find_series(p)
1642 if end is None:
1642 if end is None:
1643 return len(self.full_series)
1643 return len(self.full_series)
1644 return end + 1
1644 return end + 1
1645 return 0
1645 return 0
1646
1646
1647 def series_end(self, all_patches=False):
1647 def series_end(self, all_patches=False):
1648 """If all_patches is False, return the index of the next pushable patch
1648 """If all_patches is False, return the index of the next pushable patch
1649 in the series, or the series length. If all_patches is True, return the
1649 in the series, or the series length. If all_patches is True, return the
1650 index of the first patch past the last applied one.
1650 index of the first patch past the last applied one.
1651 """
1651 """
1652 end = 0
1652 end = 0
1653 def next(start):
1653 def next(start):
1654 if all_patches or start >= len(self.series):
1654 if all_patches or start >= len(self.series):
1655 return start
1655 return start
1656 for i in xrange(start, len(self.series)):
1656 for i in xrange(start, len(self.series)):
1657 p, reason = self.pushable(i)
1657 p, reason = self.pushable(i)
1658 if p:
1658 if p:
1659 break
1659 break
1660 self.explain_pushable(i)
1660 self.explain_pushable(i)
1661 return i
1661 return i
1662 if self.applied:
1662 if self.applied:
1663 p = self.applied[-1].name
1663 p = self.applied[-1].name
1664 try:
1664 try:
1665 end = self.series.index(p)
1665 end = self.series.index(p)
1666 except ValueError:
1666 except ValueError:
1667 return 0
1667 return 0
1668 return next(end + 1)
1668 return next(end + 1)
1669 return next(end)
1669 return next(end)
1670
1670
1671 def appliedname(self, index):
1671 def appliedname(self, index):
1672 pname = self.applied[index].name
1672 pname = self.applied[index].name
1673 if not self.ui.verbose:
1673 if not self.ui.verbose:
1674 p = pname
1674 p = pname
1675 else:
1675 else:
1676 p = str(self.series.index(pname)) + " " + pname
1676 p = str(self.series.index(pname)) + " " + pname
1677 return p
1677 return p
1678
1678
1679 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1679 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1680 force=None, git=False):
1680 force=None, git=False):
1681 def checkseries(patchname):
1681 def checkseries(patchname):
1682 if patchname in self.series:
1682 if patchname in self.series:
1683 raise util.Abort(_('patch %s is already in the series file')
1683 raise util.Abort(_('patch %s is already in the series file')
1684 % patchname)
1684 % patchname)
1685 def checkfile(patchname):
1685 def checkfile(patchname):
1686 if not force and os.path.exists(self.join(patchname)):
1686 if not force and os.path.exists(self.join(patchname)):
1687 raise util.Abort(_('patch "%s" already exists')
1687 raise util.Abort(_('patch "%s" already exists')
1688 % patchname)
1688 % patchname)
1689
1689
1690 if rev:
1690 if rev:
1691 if files:
1691 if files:
1692 raise util.Abort(_('option "-r" not valid when importing '
1692 raise util.Abort(_('option "-r" not valid when importing '
1693 'files'))
1693 'files'))
1694 rev = cmdutil.revrange(repo, rev)
1694 rev = cmdutil.revrange(repo, rev)
1695 rev.sort(reverse=True)
1695 rev.sort(reverse=True)
1696 if (len(files) > 1 or len(rev) > 1) and patchname:
1696 if (len(files) > 1 or len(rev) > 1) and patchname:
1697 raise util.Abort(_('option "-n" not valid when importing multiple '
1697 raise util.Abort(_('option "-n" not valid when importing multiple '
1698 'patches'))
1698 'patches'))
1699 if rev:
1699 if rev:
1700 # If mq patches are applied, we can only import revisions
1700 # If mq patches are applied, we can only import revisions
1701 # that form a linear path to qbase.
1701 # that form a linear path to qbase.
1702 # Otherwise, they should form a linear path to a head.
1702 # Otherwise, they should form a linear path to a head.
1703 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1703 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1704 if len(heads) > 1:
1704 if len(heads) > 1:
1705 raise util.Abort(_('revision %d is the root of more than one '
1705 raise util.Abort(_('revision %d is the root of more than one '
1706 'branch') % rev[-1])
1706 'branch') % rev[-1])
1707 if self.applied:
1707 if self.applied:
1708 base = repo.changelog.node(rev[0])
1708 base = repo.changelog.node(rev[0])
1709 if base in [n.node for n in self.applied]:
1709 if base in [n.node for n in self.applied]:
1710 raise util.Abort(_('revision %d is already managed')
1710 raise util.Abort(_('revision %d is already managed')
1711 % rev[0])
1711 % rev[0])
1712 if heads != [self.applied[-1].node]:
1712 if heads != [self.applied[-1].node]:
1713 raise util.Abort(_('revision %d is not the parent of '
1713 raise util.Abort(_('revision %d is not the parent of '
1714 'the queue') % rev[0])
1714 'the queue') % rev[0])
1715 base = repo.changelog.rev(self.applied[0].node)
1715 base = repo.changelog.rev(self.applied[0].node)
1716 lastparent = repo.changelog.parentrevs(base)[0]
1716 lastparent = repo.changelog.parentrevs(base)[0]
1717 else:
1717 else:
1718 if heads != [repo.changelog.node(rev[0])]:
1718 if heads != [repo.changelog.node(rev[0])]:
1719 raise util.Abort(_('revision %d has unmanaged children')
1719 raise util.Abort(_('revision %d has unmanaged children')
1720 % rev[0])
1720 % rev[0])
1721 lastparent = None
1721 lastparent = None
1722
1722
1723 diffopts = self.diffopts({'git': git})
1723 diffopts = self.diffopts({'git': git})
1724 for r in rev:
1724 for r in rev:
1725 p1, p2 = repo.changelog.parentrevs(r)
1725 p1, p2 = repo.changelog.parentrevs(r)
1726 n = repo.changelog.node(r)
1726 n = repo.changelog.node(r)
1727 if p2 != nullrev:
1727 if p2 != nullrev:
1728 raise util.Abort(_('cannot import merge revision %d') % r)
1728 raise util.Abort(_('cannot import merge revision %d') % r)
1729 if lastparent and lastparent != r:
1729 if lastparent and lastparent != r:
1730 raise util.Abort(_('revision %d is not the parent of %d')
1730 raise util.Abort(_('revision %d is not the parent of %d')
1731 % (r, lastparent))
1731 % (r, lastparent))
1732 lastparent = p1
1732 lastparent = p1
1733
1733
1734 if not patchname:
1734 if not patchname:
1735 patchname = normname('%d.diff' % r)
1735 patchname = normname('%d.diff' % r)
1736 self.check_reserved_name(patchname)
1736 self.check_reserved_name(patchname)
1737 checkseries(patchname)
1737 checkseries(patchname)
1738 checkfile(patchname)
1738 checkfile(patchname)
1739 self.full_series.insert(0, patchname)
1739 self.full_series.insert(0, patchname)
1740
1740
1741 patchf = self.opener(patchname, "w")
1741 patchf = self.opener(patchname, "w")
1742 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1742 cmdutil.export(repo, [n], fp=patchf, opts=diffopts)
1743 patchf.close()
1743 patchf.close()
1744
1744
1745 se = statusentry(n, patchname)
1745 se = statusentry(n, patchname)
1746 self.applied.insert(0, se)
1746 self.applied.insert(0, se)
1747
1747
1748 self.added.append(patchname)
1748 self.added.append(patchname)
1749 patchname = None
1749 patchname = None
1750 self.parse_series()
1750 self.parse_series()
1751 self.applied_dirty = 1
1751 self.applied_dirty = 1
1752 self.series_dirty = True
1752 self.series_dirty = True
1753
1753
1754 for i, filename in enumerate(files):
1754 for i, filename in enumerate(files):
1755 if existing:
1755 if existing:
1756 if filename == '-':
1756 if filename == '-':
1757 raise util.Abort(_('-e is incompatible with import from -'))
1757 raise util.Abort(_('-e is incompatible with import from -'))
1758 filename = normname(filename)
1758 filename = normname(filename)
1759 self.check_reserved_name(filename)
1759 self.check_reserved_name(filename)
1760 originpath = self.join(filename)
1760 originpath = self.join(filename)
1761 if not os.path.isfile(originpath):
1761 if not os.path.isfile(originpath):
1762 raise util.Abort(_("patch %s does not exist") % filename)
1762 raise util.Abort(_("patch %s does not exist") % filename)
1763
1763
1764 if patchname:
1764 if patchname:
1765 self.check_reserved_name(patchname)
1765 self.check_reserved_name(patchname)
1766 checkfile(patchname)
1766 checkfile(patchname)
1767
1767
1768 self.ui.write(_('renaming %s to %s\n')
1768 self.ui.write(_('renaming %s to %s\n')
1769 % (filename, patchname))
1769 % (filename, patchname))
1770 util.rename(originpath, self.join(patchname))
1770 util.rename(originpath, self.join(patchname))
1771 else:
1771 else:
1772 patchname = filename
1772 patchname = filename
1773
1773
1774 else:
1774 else:
1775 try:
1775 try:
1776 if filename == '-':
1776 if filename == '-':
1777 if not patchname:
1777 if not patchname:
1778 raise util.Abort(
1778 raise util.Abort(
1779 _('need --name to import a patch from -'))
1779 _('need --name to import a patch from -'))
1780 text = sys.stdin.read()
1780 text = sys.stdin.read()
1781 else:
1781 else:
1782 text = url.open(self.ui, filename).read()
1782 text = url.open(self.ui, filename).read()
1783 except (OSError, IOError):
1783 except (OSError, IOError):
1784 raise util.Abort(_("unable to read file %s") % filename)
1784 raise util.Abort(_("unable to read file %s") % filename)
1785 if not patchname:
1785 if not patchname:
1786 patchname = normname(os.path.basename(filename))
1786 patchname = normname(os.path.basename(filename))
1787 self.check_reserved_name(patchname)
1787 self.check_reserved_name(patchname)
1788 checkfile(patchname)
1788 checkfile(patchname)
1789 patchf = self.opener(patchname, "w")
1789 patchf = self.opener(patchname, "w")
1790 patchf.write(text)
1790 patchf.write(text)
1791 if not force:
1791 if not force:
1792 checkseries(patchname)
1792 checkseries(patchname)
1793 if patchname not in self.series:
1793 if patchname not in self.series:
1794 index = self.full_series_end() + i
1794 index = self.full_series_end() + i
1795 self.full_series[index:index] = [patchname]
1795 self.full_series[index:index] = [patchname]
1796 self.parse_series()
1796 self.parse_series()
1797 self.series_dirty = True
1797 self.series_dirty = True
1798 self.ui.warn(_("adding %s to series file\n") % patchname)
1798 self.ui.warn(_("adding %s to series file\n") % patchname)
1799 self.added.append(patchname)
1799 self.added.append(patchname)
1800 patchname = None
1800 patchname = None
1801
1801
1802 def delete(ui, repo, *patches, **opts):
1802 def delete(ui, repo, *patches, **opts):
1803 """remove patches from queue
1803 """remove patches from queue
1804
1804
1805 The patches must not be applied, and at least one patch is required. With
1805 The patches must not be applied, and at least one patch is required. With
1806 -k/--keep, the patch files are preserved in the patch directory.
1806 -k/--keep, the patch files are preserved in the patch directory.
1807
1807
1808 To stop managing a patch and move it into permanent history,
1808 To stop managing a patch and move it into permanent history,
1809 use the :hg:`qfinish` command."""
1809 use the :hg:`qfinish` command."""
1810 q = repo.mq
1810 q = repo.mq
1811 q.delete(repo, patches, opts)
1811 q.delete(repo, patches, opts)
1812 q.save_dirty()
1812 q.save_dirty()
1813 return 0
1813 return 0
1814
1814
1815 def applied(ui, repo, patch=None, **opts):
1815 def applied(ui, repo, patch=None, **opts):
1816 """print the patches already applied
1816 """print the patches already applied
1817
1817
1818 Returns 0 on success."""
1818 Returns 0 on success."""
1819
1819
1820 q = repo.mq
1820 q = repo.mq
1821
1821
1822 if patch:
1822 if patch:
1823 if patch not in q.series:
1823 if patch not in q.series:
1824 raise util.Abort(_("patch %s is not in series file") % patch)
1824 raise util.Abort(_("patch %s is not in series file") % patch)
1825 end = q.series.index(patch) + 1
1825 end = q.series.index(patch) + 1
1826 else:
1826 else:
1827 end = q.series_end(True)
1827 end = q.series_end(True)
1828
1828
1829 if opts.get('last') and not end:
1829 if opts.get('last') and not end:
1830 ui.write(_("no patches applied\n"))
1830 ui.write(_("no patches applied\n"))
1831 return 1
1831 return 1
1832 elif opts.get('last') and end == 1:
1832 elif opts.get('last') and end == 1:
1833 ui.write(_("only one patch applied\n"))
1833 ui.write(_("only one patch applied\n"))
1834 return 1
1834 return 1
1835 elif opts.get('last'):
1835 elif opts.get('last'):
1836 start = end - 2
1836 start = end - 2
1837 end = 1
1837 end = 1
1838 else:
1838 else:
1839 start = 0
1839 start = 0
1840
1840
1841 q.qseries(repo, length=end, start=start, status='A',
1841 q.qseries(repo, length=end, start=start, status='A',
1842 summary=opts.get('summary'))
1842 summary=opts.get('summary'))
1843
1843
1844
1844
1845 def unapplied(ui, repo, patch=None, **opts):
1845 def unapplied(ui, repo, patch=None, **opts):
1846 """print the patches not yet applied
1846 """print the patches not yet applied
1847
1847
1848 Returns 0 on success."""
1848 Returns 0 on success."""
1849
1849
1850 q = repo.mq
1850 q = repo.mq
1851 if patch:
1851 if patch:
1852 if patch not in q.series:
1852 if patch not in q.series:
1853 raise util.Abort(_("patch %s is not in series file") % patch)
1853 raise util.Abort(_("patch %s is not in series file") % patch)
1854 start = q.series.index(patch) + 1
1854 start = q.series.index(patch) + 1
1855 else:
1855 else:
1856 start = q.series_end(True)
1856 start = q.series_end(True)
1857
1857
1858 if start == len(q.series) and opts.get('first'):
1858 if start == len(q.series) and opts.get('first'):
1859 ui.write(_("all patches applied\n"))
1859 ui.write(_("all patches applied\n"))
1860 return 1
1860 return 1
1861
1861
1862 length = opts.get('first') and 1 or None
1862 length = opts.get('first') and 1 or None
1863 q.qseries(repo, start=start, length=length, status='U',
1863 q.qseries(repo, start=start, length=length, status='U',
1864 summary=opts.get('summary'))
1864 summary=opts.get('summary'))
1865
1865
1866 def qimport(ui, repo, *filename, **opts):
1866 def qimport(ui, repo, *filename, **opts):
1867 """import a patch
1867 """import a patch
1868
1868
1869 The patch is inserted into the series after the last applied
1869 The patch is inserted into the series after the last applied
1870 patch. If no patches have been applied, qimport prepends the patch
1870 patch. If no patches have been applied, qimport prepends the patch
1871 to the series.
1871 to the series.
1872
1872
1873 The patch will have the same name as its source file unless you
1873 The patch will have the same name as its source file unless you
1874 give it a new one with -n/--name.
1874 give it a new one with -n/--name.
1875
1875
1876 You can register an existing patch inside the patch directory with
1876 You can register an existing patch inside the patch directory with
1877 the -e/--existing flag.
1877 the -e/--existing flag.
1878
1878
1879 With -f/--force, an existing patch of the same name will be
1879 With -f/--force, an existing patch of the same name will be
1880 overwritten.
1880 overwritten.
1881
1881
1882 An existing changeset may be placed under mq control with -r/--rev
1882 An existing changeset may be placed under mq control with -r/--rev
1883 (e.g. qimport --rev tip -n patch will place tip under mq control).
1883 (e.g. qimport --rev tip -n patch will place tip under mq control).
1884 With -g/--git, patches imported with --rev will use the git diff
1884 With -g/--git, patches imported with --rev will use the git diff
1885 format. See the diffs help topic for information on why this is
1885 format. See the diffs help topic for information on why this is
1886 important for preserving rename/copy information and permission
1886 important for preserving rename/copy information and permission
1887 changes.
1887 changes.
1888
1888
1889 To import a patch from standard input, pass - as the patch file.
1889 To import a patch from standard input, pass - as the patch file.
1890 When importing from standard input, a patch name must be specified
1890 When importing from standard input, a patch name must be specified
1891 using the --name flag.
1891 using the --name flag.
1892
1892
1893 To import an existing patch while renaming it::
1893 To import an existing patch while renaming it::
1894
1894
1895 hg qimport -e existing-patch -n new-name
1895 hg qimport -e existing-patch -n new-name
1896
1896
1897 Returns 0 if import succeeded.
1897 Returns 0 if import succeeded.
1898 """
1898 """
1899 q = repo.mq
1899 q = repo.mq
1900 try:
1900 try:
1901 q.qimport(repo, filename, patchname=opts.get('name'),
1901 q.qimport(repo, filename, patchname=opts.get('name'),
1902 existing=opts.get('existing'), force=opts.get('force'),
1902 existing=opts.get('existing'), force=opts.get('force'),
1903 rev=opts.get('rev'), git=opts.get('git'))
1903 rev=opts.get('rev'), git=opts.get('git'))
1904 finally:
1904 finally:
1905 q.save_dirty()
1905 q.save_dirty()
1906
1906
1907 if opts.get('push') and not opts.get('rev'):
1907 if opts.get('push') and not opts.get('rev'):
1908 return q.push(repo, None)
1908 return q.push(repo, None)
1909 return 0
1909 return 0
1910
1910
1911 def qinit(ui, repo, create):
1911 def qinit(ui, repo, create):
1912 """initialize a new queue repository
1912 """initialize a new queue repository
1913
1913
1914 This command also creates a series file for ordering patches, and
1914 This command also creates a series file for ordering patches, and
1915 an mq-specific .hgignore file in the queue repository, to exclude
1915 an mq-specific .hgignore file in the queue repository, to exclude
1916 the status and guards files (these contain mostly transient state).
1916 the status and guards files (these contain mostly transient state).
1917
1917
1918 Returns 0 if initialization succeeded."""
1918 Returns 0 if initialization succeeded."""
1919 q = repo.mq
1919 q = repo.mq
1920 r = q.init(repo, create)
1920 r = q.init(repo, create)
1921 q.save_dirty()
1921 q.save_dirty()
1922 if r:
1922 if r:
1923 if not os.path.exists(r.wjoin('.hgignore')):
1923 if not os.path.exists(r.wjoin('.hgignore')):
1924 fp = r.wopener('.hgignore', 'w')
1924 fp = r.wopener('.hgignore', 'w')
1925 fp.write('^\\.hg\n')
1925 fp.write('^\\.hg\n')
1926 fp.write('^\\.mq\n')
1926 fp.write('^\\.mq\n')
1927 fp.write('syntax: glob\n')
1927 fp.write('syntax: glob\n')
1928 fp.write('status\n')
1928 fp.write('status\n')
1929 fp.write('guards\n')
1929 fp.write('guards\n')
1930 fp.close()
1930 fp.close()
1931 if not os.path.exists(r.wjoin('series')):
1931 if not os.path.exists(r.wjoin('series')):
1932 r.wopener('series', 'w').close()
1932 r.wopener('series', 'w').close()
1933 r[None].add(['.hgignore', 'series'])
1933 r[None].add(['.hgignore', 'series'])
1934 commands.add(ui, r)
1934 commands.add(ui, r)
1935 return 0
1935 return 0
1936
1936
1937 def init(ui, repo, **opts):
1937 def init(ui, repo, **opts):
1938 """init a new queue repository (DEPRECATED)
1938 """init a new queue repository (DEPRECATED)
1939
1939
1940 The queue repository is unversioned by default. If
1940 The queue repository is unversioned by default. If
1941 -c/--create-repo is specified, qinit will create a separate nested
1941 -c/--create-repo is specified, qinit will create a separate nested
1942 repository for patches (qinit -c may also be run later to convert
1942 repository for patches (qinit -c may also be run later to convert
1943 an unversioned patch repository into a versioned one). You can use
1943 an unversioned patch repository into a versioned one). You can use
1944 qcommit to commit changes to this queue repository.
1944 qcommit to commit changes to this queue repository.
1945
1945
1946 This command is deprecated. Without -c, it's implied by other relevant
1946 This command is deprecated. Without -c, it's implied by other relevant
1947 commands. With -c, use :hg:`init --mq` instead."""
1947 commands. With -c, use :hg:`init --mq` instead."""
1948 return qinit(ui, repo, create=opts.get('create_repo'))
1948 return qinit(ui, repo, create=opts.get('create_repo'))
1949
1949
1950 def clone(ui, source, dest=None, **opts):
1950 def clone(ui, source, dest=None, **opts):
1951 '''clone main and patch repository at same time
1951 '''clone main and patch repository at same time
1952
1952
1953 If source is local, destination will have no patches applied. If
1953 If source is local, destination will have no patches applied. If
1954 source is remote, this command can not check if patches are
1954 source is remote, this command can not check if patches are
1955 applied in source, so cannot guarantee that patches are not
1955 applied in source, so cannot guarantee that patches are not
1956 applied in destination. If you clone remote repository, be sure
1956 applied in destination. If you clone remote repository, be sure
1957 before that it has no patches applied.
1957 before that it has no patches applied.
1958
1958
1959 Source patch repository is looked for in <src>/.hg/patches by
1959 Source patch repository is looked for in <src>/.hg/patches by
1960 default. Use -p <url> to change.
1960 default. Use -p <url> to change.
1961
1961
1962 The patch directory must be a nested Mercurial repository, as
1962 The patch directory must be a nested Mercurial repository, as
1963 would be created by :hg:`init --mq`.
1963 would be created by :hg:`init --mq`.
1964
1964
1965 Return 0 on success.
1965 Return 0 on success.
1966 '''
1966 '''
1967 def patchdir(repo):
1967 def patchdir(repo):
1968 url = repo.url()
1968 url = repo.url()
1969 if url.endswith('/'):
1969 if url.endswith('/'):
1970 url = url[:-1]
1970 url = url[:-1]
1971 return url + '/.hg/patches'
1971 return url + '/.hg/patches'
1972 if dest is None:
1972 if dest is None:
1973 dest = hg.defaultdest(source)
1973 dest = hg.defaultdest(source)
1974 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
1974 sr = hg.repository(hg.remoteui(ui, opts), ui.expandpath(source))
1975 if opts.get('patches'):
1975 if opts.get('patches'):
1976 patchespath = ui.expandpath(opts.get('patches'))
1976 patchespath = ui.expandpath(opts.get('patches'))
1977 else:
1977 else:
1978 patchespath = patchdir(sr)
1978 patchespath = patchdir(sr)
1979 try:
1979 try:
1980 hg.repository(ui, patchespath)
1980 hg.repository(ui, patchespath)
1981 except error.RepoError:
1981 except error.RepoError:
1982 raise util.Abort(_('versioned patch repository not found'
1982 raise util.Abort(_('versioned patch repository not found'
1983 ' (see init --mq)'))
1983 ' (see init --mq)'))
1984 qbase, destrev = None, None
1984 qbase, destrev = None, None
1985 if sr.local():
1985 if sr.local():
1986 if sr.mq.applied:
1986 if sr.mq.applied:
1987 qbase = sr.mq.applied[0].node
1987 qbase = sr.mq.applied[0].node
1988 if not hg.islocal(dest):
1988 if not hg.islocal(dest):
1989 heads = set(sr.heads())
1989 heads = set(sr.heads())
1990 destrev = list(heads.difference(sr.heads(qbase)))
1990 destrev = list(heads.difference(sr.heads(qbase)))
1991 destrev.append(sr.changelog.parents(qbase)[0])
1991 destrev.append(sr.changelog.parents(qbase)[0])
1992 elif sr.capable('lookup'):
1992 elif sr.capable('lookup'):
1993 try:
1993 try:
1994 qbase = sr.lookup('qbase')
1994 qbase = sr.lookup('qbase')
1995 except error.RepoError:
1995 except error.RepoError:
1996 pass
1996 pass
1997 ui.note(_('cloning main repository\n'))
1997 ui.note(_('cloning main repository\n'))
1998 sr, dr = hg.clone(ui, sr.url(), dest,
1998 sr, dr = hg.clone(ui, sr.url(), dest,
1999 pull=opts.get('pull'),
1999 pull=opts.get('pull'),
2000 rev=destrev,
2000 rev=destrev,
2001 update=False,
2001 update=False,
2002 stream=opts.get('uncompressed'))
2002 stream=opts.get('uncompressed'))
2003 ui.note(_('cloning patch repository\n'))
2003 ui.note(_('cloning patch repository\n'))
2004 hg.clone(ui, opts.get('patches') or patchdir(sr), patchdir(dr),
2004 hg.clone(ui, opts.get('patches') or patchdir(sr), patchdir(dr),
2005 pull=opts.get('pull'), update=not opts.get('noupdate'),
2005 pull=opts.get('pull'), update=not opts.get('noupdate'),
2006 stream=opts.get('uncompressed'))
2006 stream=opts.get('uncompressed'))
2007 if dr.local():
2007 if dr.local():
2008 if qbase:
2008 if qbase:
2009 ui.note(_('stripping applied patches from destination '
2009 ui.note(_('stripping applied patches from destination '
2010 'repository\n'))
2010 'repository\n'))
2011 dr.mq.strip(dr, [qbase], update=False, backup=None)
2011 dr.mq.strip(dr, [qbase], update=False, backup=None)
2012 if not opts.get('noupdate'):
2012 if not opts.get('noupdate'):
2013 ui.note(_('updating destination repository\n'))
2013 ui.note(_('updating destination repository\n'))
2014 hg.update(dr, dr.changelog.tip())
2014 hg.update(dr, dr.changelog.tip())
2015
2015
2016 def commit(ui, repo, *pats, **opts):
2016 def commit(ui, repo, *pats, **opts):
2017 """commit changes in the queue repository (DEPRECATED)
2017 """commit changes in the queue repository (DEPRECATED)
2018
2018
2019 This command is deprecated; use :hg:`commit --mq` instead."""
2019 This command is deprecated; use :hg:`commit --mq` instead."""
2020 q = repo.mq
2020 q = repo.mq
2021 r = q.qrepo()
2021 r = q.qrepo()
2022 if not r:
2022 if not r:
2023 raise util.Abort('no queue repository')
2023 raise util.Abort('no queue repository')
2024 commands.commit(r.ui, r, *pats, **opts)
2024 commands.commit(r.ui, r, *pats, **opts)
2025
2025
2026 def series(ui, repo, **opts):
2026 def series(ui, repo, **opts):
2027 """print the entire series file
2027 """print the entire series file
2028
2028
2029 Returns 0 on success."""
2029 Returns 0 on success."""
2030 repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
2030 repo.mq.qseries(repo, missing=opts.get('missing'), summary=opts.get('summary'))
2031 return 0
2031 return 0
2032
2032
2033 def top(ui, repo, **opts):
2033 def top(ui, repo, **opts):
2034 """print the name of the current patch
2034 """print the name of the current patch
2035
2035
2036 Returns 0 on success."""
2036 Returns 0 on success."""
2037 q = repo.mq
2037 q = repo.mq
2038 t = q.applied and q.series_end(True) or 0
2038 t = q.applied and q.series_end(True) or 0
2039 if t:
2039 if t:
2040 q.qseries(repo, start=t - 1, length=1, status='A',
2040 q.qseries(repo, start=t - 1, length=1, status='A',
2041 summary=opts.get('summary'))
2041 summary=opts.get('summary'))
2042 else:
2042 else:
2043 ui.write(_("no patches applied\n"))
2043 ui.write(_("no patches applied\n"))
2044 return 1
2044 return 1
2045
2045
2046 def next(ui, repo, **opts):
2046 def next(ui, repo, **opts):
2047 """print the name of the next patch
2047 """print the name of the next patch
2048
2048
2049 Returns 0 on success."""
2049 Returns 0 on success."""
2050 q = repo.mq
2050 q = repo.mq
2051 end = q.series_end()
2051 end = q.series_end()
2052 if end == len(q.series):
2052 if end == len(q.series):
2053 ui.write(_("all patches applied\n"))
2053 ui.write(_("all patches applied\n"))
2054 return 1
2054 return 1
2055 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2055 q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
2056
2056
2057 def prev(ui, repo, **opts):
2057 def prev(ui, repo, **opts):
2058 """print the name of the previous patch
2058 """print the name of the previous patch
2059
2059
2060 Returns 0 on success."""
2060 Returns 0 on success."""
2061 q = repo.mq
2061 q = repo.mq
2062 l = len(q.applied)
2062 l = len(q.applied)
2063 if l == 1:
2063 if l == 1:
2064 ui.write(_("only one patch applied\n"))
2064 ui.write(_("only one patch applied\n"))
2065 return 1
2065 return 1
2066 if not l:
2066 if not l:
2067 ui.write(_("no patches applied\n"))
2067 ui.write(_("no patches applied\n"))
2068 return 1
2068 return 1
2069 q.qseries(repo, start=l - 2, length=1, status='A',
2069 q.qseries(repo, start=l - 2, length=1, status='A',
2070 summary=opts.get('summary'))
2070 summary=opts.get('summary'))
2071
2071
2072 def setupheaderopts(ui, opts):
2072 def setupheaderopts(ui, opts):
2073 if not opts.get('user') and opts.get('currentuser'):
2073 if not opts.get('user') and opts.get('currentuser'):
2074 opts['user'] = ui.username()
2074 opts['user'] = ui.username()
2075 if not opts.get('date') and opts.get('currentdate'):
2075 if not opts.get('date') and opts.get('currentdate'):
2076 opts['date'] = "%d %d" % util.makedate()
2076 opts['date'] = "%d %d" % util.makedate()
2077
2077
2078 def new(ui, repo, patch, *args, **opts):
2078 def new(ui, repo, patch, *args, **opts):
2079 """create a new patch
2079 """create a new patch
2080
2080
2081 qnew creates a new patch on top of the currently-applied patch (if
2081 qnew creates a new patch on top of the currently-applied patch (if
2082 any). The patch will be initialized with any outstanding changes
2082 any). The patch will be initialized with any outstanding changes
2083 in the working directory. You may also use -I/--include,
2083 in the working directory. You may also use -I/--include,
2084 -X/--exclude, and/or a list of files after the patch name to add
2084 -X/--exclude, and/or a list of files after the patch name to add
2085 only changes to matching files to the new patch, leaving the rest
2085 only changes to matching files to the new patch, leaving the rest
2086 as uncommitted modifications.
2086 as uncommitted modifications.
2087
2087
2088 -u/--user and -d/--date can be used to set the (given) user and
2088 -u/--user and -d/--date can be used to set the (given) user and
2089 date, respectively. -U/--currentuser and -D/--currentdate set user
2089 date, respectively. -U/--currentuser and -D/--currentdate set user
2090 to current user and date to current date.
2090 to current user and date to current date.
2091
2091
2092 -e/--edit, -m/--message or -l/--logfile set the patch header as
2092 -e/--edit, -m/--message or -l/--logfile set the patch header as
2093 well as the commit message. If none is specified, the header is
2093 well as the commit message. If none is specified, the header is
2094 empty and the commit message is '[mq]: PATCH'.
2094 empty and the commit message is '[mq]: PATCH'.
2095
2095
2096 Use the -g/--git option to keep the patch in the git extended diff
2096 Use the -g/--git option to keep the patch in the git extended diff
2097 format. Read the diffs help topic for more information on why this
2097 format. Read the diffs help topic for more information on why this
2098 is important for preserving permission changes and copy/rename
2098 is important for preserving permission changes and copy/rename
2099 information.
2099 information.
2100
2100
2101 Returns 0 on successful creation of a new patch.
2101 Returns 0 on successful creation of a new patch.
2102 """
2102 """
2103 msg = cmdutil.logmessage(opts)
2103 msg = cmdutil.logmessage(opts)
2104 def getmsg():
2104 def getmsg():
2105 return ui.edit(msg, opts.get('user') or ui.username())
2105 return ui.edit(msg, opts.get('user') or ui.username())
2106 q = repo.mq
2106 q = repo.mq
2107 opts['msg'] = msg
2107 opts['msg'] = msg
2108 if opts.get('edit'):
2108 if opts.get('edit'):
2109 opts['msg'] = getmsg
2109 opts['msg'] = getmsg
2110 else:
2110 else:
2111 opts['msg'] = msg
2111 opts['msg'] = msg
2112 setupheaderopts(ui, opts)
2112 setupheaderopts(ui, opts)
2113 q.new(repo, patch, *args, **opts)
2113 q.new(repo, patch, *args, **opts)
2114 q.save_dirty()
2114 q.save_dirty()
2115 return 0
2115 return 0
2116
2116
2117 def refresh(ui, repo, *pats, **opts):
2117 def refresh(ui, repo, *pats, **opts):
2118 """update the current patch
2118 """update the current patch
2119
2119
2120 If any file patterns are provided, the refreshed patch will
2120 If any file patterns are provided, the refreshed patch will
2121 contain only the modifications that match those patterns; the
2121 contain only the modifications that match those patterns; the
2122 remaining modifications will remain in the working directory.
2122 remaining modifications will remain in the working directory.
2123
2123
2124 If -s/--short is specified, files currently included in the patch
2124 If -s/--short is specified, files currently included in the patch
2125 will be refreshed just like matched files and remain in the patch.
2125 will be refreshed just like matched files and remain in the patch.
2126
2126
2127 If -e/--edit is specified, Mercurial will start your configured editor for
2127 If -e/--edit is specified, Mercurial will start your configured editor for
2128 you to enter a message. In case qrefresh fails, you will find a backup of
2128 you to enter a message. In case qrefresh fails, you will find a backup of
2129 your message in ``.hg/last-message.txt``.
2129 your message in ``.hg/last-message.txt``.
2130
2130
2131 hg add/remove/copy/rename work as usual, though you might want to
2131 hg add/remove/copy/rename work as usual, though you might want to
2132 use git-style patches (-g/--git or [diff] git=1) to track copies
2132 use git-style patches (-g/--git or [diff] git=1) to track copies
2133 and renames. See the diffs help topic for more information on the
2133 and renames. See the diffs help topic for more information on the
2134 git diff format.
2134 git diff format.
2135
2135
2136 Returns 0 on success.
2136 Returns 0 on success.
2137 """
2137 """
2138 q = repo.mq
2138 q = repo.mq
2139 message = cmdutil.logmessage(opts)
2139 message = cmdutil.logmessage(opts)
2140 if opts.get('edit'):
2140 if opts.get('edit'):
2141 if not q.applied:
2141 if not q.applied:
2142 ui.write(_("no patches applied\n"))
2142 ui.write(_("no patches applied\n"))
2143 return 1
2143 return 1
2144 if message:
2144 if message:
2145 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2145 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2146 patch = q.applied[-1].name
2146 patch = q.applied[-1].name
2147 ph = patchheader(q.join(patch), q.plainmode)
2147 ph = patchheader(q.join(patch), q.plainmode)
2148 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2148 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
2149 # We don't want to lose the patch message if qrefresh fails (issue2062)
2149 # We don't want to lose the patch message if qrefresh fails (issue2062)
2150 msgfile = repo.opener('last-message.txt', 'wb')
2150 msgfile = repo.opener('last-message.txt', 'wb')
2151 msgfile.write(message)
2151 msgfile.write(message)
2152 msgfile.close()
2152 msgfile.close()
2153 setupheaderopts(ui, opts)
2153 setupheaderopts(ui, opts)
2154 ret = q.refresh(repo, pats, msg=message, **opts)
2154 ret = q.refresh(repo, pats, msg=message, **opts)
2155 q.save_dirty()
2155 q.save_dirty()
2156 return ret
2156 return ret
2157
2157
2158 def diff(ui, repo, *pats, **opts):
2158 def diff(ui, repo, *pats, **opts):
2159 """diff of the current patch and subsequent modifications
2159 """diff of the current patch and subsequent modifications
2160
2160
2161 Shows a diff which includes the current patch as well as any
2161 Shows a diff which includes the current patch as well as any
2162 changes which have been made in the working directory since the
2162 changes which have been made in the working directory since the
2163 last refresh (thus showing what the current patch would become
2163 last refresh (thus showing what the current patch would become
2164 after a qrefresh).
2164 after a qrefresh).
2165
2165
2166 Use :hg:`diff` if you only want to see the changes made since the
2166 Use :hg:`diff` if you only want to see the changes made since the
2167 last qrefresh, or :hg:`export qtip` if you want to see changes
2167 last qrefresh, or :hg:`export qtip` if you want to see changes
2168 made by the current patch without including changes made since the
2168 made by the current patch without including changes made since the
2169 qrefresh.
2169 qrefresh.
2170
2170
2171 Returns 0 on success.
2171 Returns 0 on success.
2172 """
2172 """
2173 repo.mq.diff(repo, pats, opts)
2173 repo.mq.diff(repo, pats, opts)
2174 return 0
2174 return 0
2175
2175
2176 def fold(ui, repo, *files, **opts):
2176 def fold(ui, repo, *files, **opts):
2177 """fold the named patches into the current patch
2177 """fold the named patches into the current patch
2178
2178
2179 Patches must not yet be applied. Each patch will be successively
2179 Patches must not yet be applied. Each patch will be successively
2180 applied to the current patch in the order given. If all the
2180 applied to the current patch in the order given. If all the
2181 patches apply successfully, the current patch will be refreshed
2181 patches apply successfully, the current patch will be refreshed
2182 with the new cumulative patch, and the folded patches will be
2182 with the new cumulative patch, and the folded patches will be
2183 deleted. With -k/--keep, the folded patch files will not be
2183 deleted. With -k/--keep, the folded patch files will not be
2184 removed afterwards.
2184 removed afterwards.
2185
2185
2186 The header for each folded patch will be concatenated with the
2186 The header for each folded patch will be concatenated with the
2187 current patch header, separated by a line of ``* * *``.
2187 current patch header, separated by a line of ``* * *``.
2188
2188
2189 Returns 0 on success."""
2189 Returns 0 on success."""
2190
2190
2191 q = repo.mq
2191 q = repo.mq
2192
2192
2193 if not files:
2193 if not files:
2194 raise util.Abort(_('qfold requires at least one patch name'))
2194 raise util.Abort(_('qfold requires at least one patch name'))
2195 if not q.check_toppatch(repo)[0]:
2195 if not q.check_toppatch(repo)[0]:
2196 raise util.Abort(_('no patches applied'))
2196 raise util.Abort(_('no patches applied'))
2197 q.check_localchanges(repo)
2197 q.check_localchanges(repo)
2198
2198
2199 message = cmdutil.logmessage(opts)
2199 message = cmdutil.logmessage(opts)
2200 if opts.get('edit'):
2200 if opts.get('edit'):
2201 if message:
2201 if message:
2202 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2202 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
2203
2203
2204 parent = q.lookup('qtip')
2204 parent = q.lookup('qtip')
2205 patches = []
2205 patches = []
2206 messages = []
2206 messages = []
2207 for f in files:
2207 for f in files:
2208 p = q.lookup(f)
2208 p = q.lookup(f)
2209 if p in patches or p == parent:
2209 if p in patches or p == parent:
2210 ui.warn(_('Skipping already folded patch %s\n') % p)
2210 ui.warn(_('Skipping already folded patch %s\n') % p)
2211 if q.isapplied(p):
2211 if q.isapplied(p):
2212 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2212 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
2213 patches.append(p)
2213 patches.append(p)
2214
2214
2215 for p in patches:
2215 for p in patches:
2216 if not message:
2216 if not message:
2217 ph = patchheader(q.join(p), q.plainmode)
2217 ph = patchheader(q.join(p), q.plainmode)
2218 if ph.message:
2218 if ph.message:
2219 messages.append(ph.message)
2219 messages.append(ph.message)
2220 pf = q.join(p)
2220 pf = q.join(p)
2221 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2221 (patchsuccess, files, fuzz) = q.patch(repo, pf)
2222 if not patchsuccess:
2222 if not patchsuccess:
2223 raise util.Abort(_('error folding patch %s') % p)
2223 raise util.Abort(_('error folding patch %s') % p)
2224 cmdutil.updatedir(ui, repo, files)
2224 cmdutil.updatedir(ui, repo, files)
2225
2225
2226 if not message:
2226 if not message:
2227 ph = patchheader(q.join(parent), q.plainmode)
2227 ph = patchheader(q.join(parent), q.plainmode)
2228 message, user = ph.message, ph.user
2228 message, user = ph.message, ph.user
2229 for msg in messages:
2229 for msg in messages:
2230 message.append('* * *')
2230 message.append('* * *')
2231 message.extend(msg)
2231 message.extend(msg)
2232 message = '\n'.join(message)
2232 message = '\n'.join(message)
2233
2233
2234 if opts.get('edit'):
2234 if opts.get('edit'):
2235 message = ui.edit(message, user or ui.username())
2235 message = ui.edit(message, user or ui.username())
2236
2236
2237 diffopts = q.patchopts(q.diffopts(), *patches)
2237 diffopts = q.patchopts(q.diffopts(), *patches)
2238 q.refresh(repo, msg=message, git=diffopts.git)
2238 q.refresh(repo, msg=message, git=diffopts.git)
2239 q.delete(repo, patches, opts)
2239 q.delete(repo, patches, opts)
2240 q.save_dirty()
2240 q.save_dirty()
2241
2241
2242 def goto(ui, repo, patch, **opts):
2242 def goto(ui, repo, patch, **opts):
2243 '''push or pop patches until named patch is at top of stack
2243 '''push or pop patches until named patch is at top of stack
2244
2244
2245 Returns 0 on success.'''
2245 Returns 0 on success.'''
2246 q = repo.mq
2246 q = repo.mq
2247 patch = q.lookup(patch)
2247 patch = q.lookup(patch)
2248 if q.isapplied(patch):
2248 if q.isapplied(patch):
2249 ret = q.pop(repo, patch, force=opts.get('force'))
2249 ret = q.pop(repo, patch, force=opts.get('force'))
2250 else:
2250 else:
2251 ret = q.push(repo, patch, force=opts.get('force'))
2251 ret = q.push(repo, patch, force=opts.get('force'))
2252 q.save_dirty()
2252 q.save_dirty()
2253 return ret
2253 return ret
2254
2254
2255 def guard(ui, repo, *args, **opts):
2255 def guard(ui, repo, *args, **opts):
2256 '''set or print guards for a patch
2256 '''set or print guards for a patch
2257
2257
2258 Guards control whether a patch can be pushed. A patch with no
2258 Guards control whether a patch can be pushed. A patch with no
2259 guards is always pushed. A patch with a positive guard ("+foo") is
2259 guards is always pushed. A patch with a positive guard ("+foo") is
2260 pushed only if the :hg:`qselect` command has activated it. A patch with
2260 pushed only if the :hg:`qselect` command has activated it. A patch with
2261 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2261 a negative guard ("-foo") is never pushed if the :hg:`qselect` command
2262 has activated it.
2262 has activated it.
2263
2263
2264 With no arguments, print the currently active guards.
2264 With no arguments, print the currently active guards.
2265 With arguments, set guards for the named patch.
2265 With arguments, set guards for the named patch.
2266
2266
2267 .. note::
2267 .. note::
2268 Specifying negative guards now requires '--'.
2268 Specifying negative guards now requires '--'.
2269
2269
2270 To set guards on another patch::
2270 To set guards on another patch::
2271
2271
2272 hg qguard other.patch -- +2.6.17 -stable
2272 hg qguard other.patch -- +2.6.17 -stable
2273
2273
2274 Returns 0 on success.
2274 Returns 0 on success.
2275 '''
2275 '''
2276 def status(idx):
2276 def status(idx):
2277 guards = q.series_guards[idx] or ['unguarded']
2277 guards = q.series_guards[idx] or ['unguarded']
2278 if q.series[idx] in applied:
2278 if q.series[idx] in applied:
2279 state = 'applied'
2279 state = 'applied'
2280 elif q.pushable(idx)[0]:
2280 elif q.pushable(idx)[0]:
2281 state = 'unapplied'
2281 state = 'unapplied'
2282 else:
2282 else:
2283 state = 'guarded'
2283 state = 'guarded'
2284 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2284 label = 'qguard.patch qguard.%s qseries.%s' % (state, state)
2285 ui.write('%s: ' % ui.label(q.series[idx], label))
2285 ui.write('%s: ' % ui.label(q.series[idx], label))
2286
2286
2287 for i, guard in enumerate(guards):
2287 for i, guard in enumerate(guards):
2288 if guard.startswith('+'):
2288 if guard.startswith('+'):
2289 ui.write(guard, label='qguard.positive')
2289 ui.write(guard, label='qguard.positive')
2290 elif guard.startswith('-'):
2290 elif guard.startswith('-'):
2291 ui.write(guard, label='qguard.negative')
2291 ui.write(guard, label='qguard.negative')
2292 else:
2292 else:
2293 ui.write(guard, label='qguard.unguarded')
2293 ui.write(guard, label='qguard.unguarded')
2294 if i != len(guards) - 1:
2294 if i != len(guards) - 1:
2295 ui.write(' ')
2295 ui.write(' ')
2296 ui.write('\n')
2296 ui.write('\n')
2297 q = repo.mq
2297 q = repo.mq
2298 applied = set(p.name for p in q.applied)
2298 applied = set(p.name for p in q.applied)
2299 patch = None
2299 patch = None
2300 args = list(args)
2300 args = list(args)
2301 if opts.get('list'):
2301 if opts.get('list'):
2302 if args or opts.get('none'):
2302 if args or opts.get('none'):
2303 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2303 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2304 for i in xrange(len(q.series)):
2304 for i in xrange(len(q.series)):
2305 status(i)
2305 status(i)
2306 return
2306 return
2307 if not args or args[0][0:1] in '-+':
2307 if not args or args[0][0:1] in '-+':
2308 if not q.applied:
2308 if not q.applied:
2309 raise util.Abort(_('no patches applied'))
2309 raise util.Abort(_('no patches applied'))
2310 patch = q.applied[-1].name
2310 patch = q.applied[-1].name
2311 if patch is None and args[0][0:1] not in '-+':
2311 if patch is None and args[0][0:1] not in '-+':
2312 patch = args.pop(0)
2312 patch = args.pop(0)
2313 if patch is None:
2313 if patch is None:
2314 raise util.Abort(_('no patch to work with'))
2314 raise util.Abort(_('no patch to work with'))
2315 if args or opts.get('none'):
2315 if args or opts.get('none'):
2316 idx = q.find_series(patch)
2316 idx = q.find_series(patch)
2317 if idx is None:
2317 if idx is None:
2318 raise util.Abort(_('no patch named %s') % patch)
2318 raise util.Abort(_('no patch named %s') % patch)
2319 q.set_guards(idx, args)
2319 q.set_guards(idx, args)
2320 q.save_dirty()
2320 q.save_dirty()
2321 else:
2321 else:
2322 status(q.series.index(q.lookup(patch)))
2322 status(q.series.index(q.lookup(patch)))
2323
2323
2324 def header(ui, repo, patch=None):
2324 def header(ui, repo, patch=None):
2325 """print the header of the topmost or specified patch
2325 """print the header of the topmost or specified patch
2326
2326
2327 Returns 0 on success."""
2327 Returns 0 on success."""
2328 q = repo.mq
2328 q = repo.mq
2329
2329
2330 if patch:
2330 if patch:
2331 patch = q.lookup(patch)
2331 patch = q.lookup(patch)
2332 else:
2332 else:
2333 if not q.applied:
2333 if not q.applied:
2334 ui.write(_('no patches applied\n'))
2334 ui.write(_('no patches applied\n'))
2335 return 1
2335 return 1
2336 patch = q.lookup('qtip')
2336 patch = q.lookup('qtip')
2337 ph = patchheader(q.join(patch), q.plainmode)
2337 ph = patchheader(q.join(patch), q.plainmode)
2338
2338
2339 ui.write('\n'.join(ph.message) + '\n')
2339 ui.write('\n'.join(ph.message) + '\n')
2340
2340
2341 def lastsavename(path):
2341 def lastsavename(path):
2342 (directory, base) = os.path.split(path)
2342 (directory, base) = os.path.split(path)
2343 names = os.listdir(directory)
2343 names = os.listdir(directory)
2344 namere = re.compile("%s.([0-9]+)" % base)
2344 namere = re.compile("%s.([0-9]+)" % base)
2345 maxindex = None
2345 maxindex = None
2346 maxname = None
2346 maxname = None
2347 for f in names:
2347 for f in names:
2348 m = namere.match(f)
2348 m = namere.match(f)
2349 if m:
2349 if m:
2350 index = int(m.group(1))
2350 index = int(m.group(1))
2351 if maxindex is None or index > maxindex:
2351 if maxindex is None or index > maxindex:
2352 maxindex = index
2352 maxindex = index
2353 maxname = f
2353 maxname = f
2354 if maxname:
2354 if maxname:
2355 return (os.path.join(directory, maxname), maxindex)
2355 return (os.path.join(directory, maxname), maxindex)
2356 return (None, None)
2356 return (None, None)
2357
2357
2358 def savename(path):
2358 def savename(path):
2359 (last, index) = lastsavename(path)
2359 (last, index) = lastsavename(path)
2360 if last is None:
2360 if last is None:
2361 index = 0
2361 index = 0
2362 newpath = path + ".%d" % (index + 1)
2362 newpath = path + ".%d" % (index + 1)
2363 return newpath
2363 return newpath
2364
2364
2365 def push(ui, repo, patch=None, **opts):
2365 def push(ui, repo, patch=None, **opts):
2366 """push the next patch onto the stack
2366 """push the next patch onto the stack
2367
2367
2368 When -f/--force is applied, all local changes in patched files
2368 When -f/--force is applied, all local changes in patched files
2369 will be lost.
2369 will be lost.
2370
2370
2371 Return 0 on succces.
2371 Return 0 on succces.
2372 """
2372 """
2373 q = repo.mq
2373 q = repo.mq
2374 mergeq = None
2374 mergeq = None
2375
2375
2376 if opts.get('merge'):
2376 if opts.get('merge'):
2377 if opts.get('name'):
2377 if opts.get('name'):
2378 newpath = repo.join(opts.get('name'))
2378 newpath = repo.join(opts.get('name'))
2379 else:
2379 else:
2380 newpath, i = lastsavename(q.path)
2380 newpath, i = lastsavename(q.path)
2381 if not newpath:
2381 if not newpath:
2382 ui.warn(_("no saved queues found, please use -n\n"))
2382 ui.warn(_("no saved queues found, please use -n\n"))
2383 return 1
2383 return 1
2384 mergeq = queue(ui, repo.join(""), newpath)
2384 mergeq = queue(ui, repo.join(""), newpath)
2385 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2385 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2386 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2386 ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
2387 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2387 mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
2388 exact=opts.get('exact'))
2388 exact=opts.get('exact'))
2389 return ret
2389 return ret
2390
2390
2391 def pop(ui, repo, patch=None, **opts):
2391 def pop(ui, repo, patch=None, **opts):
2392 """pop the current patch off the stack
2392 """pop the current patch off the stack
2393
2393
2394 By default, pops off the top of the patch stack. If given a patch
2394 By default, pops off the top of the patch stack. If given a patch
2395 name, keeps popping off patches until the named patch is at the
2395 name, keeps popping off patches until the named patch is at the
2396 top of the stack.
2396 top of the stack.
2397
2397
2398 Return 0 on success.
2398 Return 0 on success.
2399 """
2399 """
2400 localupdate = True
2400 localupdate = True
2401 if opts.get('name'):
2401 if opts.get('name'):
2402 q = queue(ui, repo.join(""), repo.join(opts.get('name')))
2402 q = queue(ui, repo.join(""), repo.join(opts.get('name')))
2403 ui.warn(_('using patch queue: %s\n') % q.path)
2403 ui.warn(_('using patch queue: %s\n') % q.path)
2404 localupdate = False
2404 localupdate = False
2405 else:
2405 else:
2406 q = repo.mq
2406 q = repo.mq
2407 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2407 ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
2408 all=opts.get('all'))
2408 all=opts.get('all'))
2409 q.save_dirty()
2409 q.save_dirty()
2410 return ret
2410 return ret
2411
2411
2412 def rename(ui, repo, patch, name=None, **opts):
2412 def rename(ui, repo, patch, name=None, **opts):
2413 """rename a patch
2413 """rename a patch
2414
2414
2415 With one argument, renames the current patch to PATCH1.
2415 With one argument, renames the current patch to PATCH1.
2416 With two arguments, renames PATCH1 to PATCH2.
2416 With two arguments, renames PATCH1 to PATCH2.
2417
2417
2418 Returns 0 on success."""
2418 Returns 0 on success."""
2419
2419
2420 q = repo.mq
2420 q = repo.mq
2421
2421
2422 if not name:
2422 if not name:
2423 name = patch
2423 name = patch
2424 patch = None
2424 patch = None
2425
2425
2426 if patch:
2426 if patch:
2427 patch = q.lookup(patch)
2427 patch = q.lookup(patch)
2428 else:
2428 else:
2429 if not q.applied:
2429 if not q.applied:
2430 ui.write(_('no patches applied\n'))
2430 ui.write(_('no patches applied\n'))
2431 return
2431 return
2432 patch = q.lookup('qtip')
2432 patch = q.lookup('qtip')
2433 absdest = q.join(name)
2433 absdest = q.join(name)
2434 if os.path.isdir(absdest):
2434 if os.path.isdir(absdest):
2435 name = normname(os.path.join(name, os.path.basename(patch)))
2435 name = normname(os.path.join(name, os.path.basename(patch)))
2436 absdest = q.join(name)
2436 absdest = q.join(name)
2437 if os.path.exists(absdest):
2437 if os.path.exists(absdest):
2438 raise util.Abort(_('%s already exists') % absdest)
2438 raise util.Abort(_('%s already exists') % absdest)
2439
2439
2440 if name in q.series:
2440 if name in q.series:
2441 raise util.Abort(
2441 raise util.Abort(
2442 _('A patch named %s already exists in the series file') % name)
2442 _('A patch named %s already exists in the series file') % name)
2443
2443
2444 ui.note(_('renaming %s to %s\n') % (patch, name))
2444 ui.note(_('renaming %s to %s\n') % (patch, name))
2445 i = q.find_series(patch)
2445 i = q.find_series(patch)
2446 guards = q.guard_re.findall(q.full_series[i])
2446 guards = q.guard_re.findall(q.full_series[i])
2447 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2447 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2448 q.parse_series()
2448 q.parse_series()
2449 q.series_dirty = 1
2449 q.series_dirty = 1
2450
2450
2451 info = q.isapplied(patch)
2451 info = q.isapplied(patch)
2452 if info:
2452 if info:
2453 q.applied[info[0]] = statusentry(info[1], name)
2453 q.applied[info[0]] = statusentry(info[1], name)
2454 q.applied_dirty = 1
2454 q.applied_dirty = 1
2455
2455
2456 destdir = os.path.dirname(absdest)
2456 destdir = os.path.dirname(absdest)
2457 if not os.path.isdir(destdir):
2457 if not os.path.isdir(destdir):
2458 os.makedirs(destdir)
2458 os.makedirs(destdir)
2459 util.rename(q.join(patch), absdest)
2459 util.rename(q.join(patch), absdest)
2460 r = q.qrepo()
2460 r = q.qrepo()
2461 if r and patch in r.dirstate:
2461 if r and patch in r.dirstate:
2462 wctx = r[None]
2462 wctx = r[None]
2463 wlock = r.wlock()
2463 wlock = r.wlock()
2464 try:
2464 try:
2465 if r.dirstate[patch] == 'a':
2465 if r.dirstate[patch] == 'a':
2466 r.dirstate.forget(patch)
2466 r.dirstate.forget(patch)
2467 r.dirstate.add(name)
2467 r.dirstate.add(name)
2468 else:
2468 else:
2469 if r.dirstate[name] == 'r':
2469 if r.dirstate[name] == 'r':
2470 wctx.undelete([name])
2470 wctx.undelete([name])
2471 wctx.copy(patch, name)
2471 wctx.copy(patch, name)
2472 wctx.remove([patch], False)
2472 wctx.remove([patch], False)
2473 finally:
2473 finally:
2474 wlock.release()
2474 wlock.release()
2475
2475
2476 q.save_dirty()
2476 q.save_dirty()
2477
2477
2478 def restore(ui, repo, rev, **opts):
2478 def restore(ui, repo, rev, **opts):
2479 """restore the queue state saved by a revision (DEPRECATED)
2479 """restore the queue state saved by a revision (DEPRECATED)
2480
2480
2481 This command is deprecated, use :hg:`rebase` instead."""
2481 This command is deprecated, use :hg:`rebase` instead."""
2482 rev = repo.lookup(rev)
2482 rev = repo.lookup(rev)
2483 q = repo.mq
2483 q = repo.mq
2484 q.restore(repo, rev, delete=opts.get('delete'),
2484 q.restore(repo, rev, delete=opts.get('delete'),
2485 qupdate=opts.get('update'))
2485 qupdate=opts.get('update'))
2486 q.save_dirty()
2486 q.save_dirty()
2487 return 0
2487 return 0
2488
2488
2489 def save(ui, repo, **opts):
2489 def save(ui, repo, **opts):
2490 """save current queue state (DEPRECATED)
2490 """save current queue state (DEPRECATED)
2491
2491
2492 This command is deprecated, use :hg:`rebase` instead."""
2492 This command is deprecated, use :hg:`rebase` instead."""
2493 q = repo.mq
2493 q = repo.mq
2494 message = cmdutil.logmessage(opts)
2494 message = cmdutil.logmessage(opts)
2495 ret = q.save(repo, msg=message)
2495 ret = q.save(repo, msg=message)
2496 if ret:
2496 if ret:
2497 return ret
2497 return ret
2498 q.save_dirty()
2498 q.save_dirty()
2499 if opts.get('copy'):
2499 if opts.get('copy'):
2500 path = q.path
2500 path = q.path
2501 if opts.get('name'):
2501 if opts.get('name'):
2502 newpath = os.path.join(q.basepath, opts.get('name'))
2502 newpath = os.path.join(q.basepath, opts.get('name'))
2503 if os.path.exists(newpath):
2503 if os.path.exists(newpath):
2504 if not os.path.isdir(newpath):
2504 if not os.path.isdir(newpath):
2505 raise util.Abort(_('destination %s exists and is not '
2505 raise util.Abort(_('destination %s exists and is not '
2506 'a directory') % newpath)
2506 'a directory') % newpath)
2507 if not opts.get('force'):
2507 if not opts.get('force'):
2508 raise util.Abort(_('destination %s exists, '
2508 raise util.Abort(_('destination %s exists, '
2509 'use -f to force') % newpath)
2509 'use -f to force') % newpath)
2510 else:
2510 else:
2511 newpath = savename(path)
2511 newpath = savename(path)
2512 ui.warn(_("copy %s to %s\n") % (path, newpath))
2512 ui.warn(_("copy %s to %s\n") % (path, newpath))
2513 util.copyfiles(path, newpath)
2513 util.copyfiles(path, newpath)
2514 if opts.get('empty'):
2514 if opts.get('empty'):
2515 try:
2515 try:
2516 os.unlink(q.join(q.status_path))
2516 os.unlink(q.join(q.status_path))
2517 except:
2517 except:
2518 pass
2518 pass
2519 return 0
2519 return 0
2520
2520
2521 def strip(ui, repo, *revs, **opts):
2521 def strip(ui, repo, *revs, **opts):
2522 """strip changesets and all their descendants from the repository
2522 """strip changesets and all their descendants from the repository
2523
2523
2524 The strip command removes the specified changesets and all their
2524 The strip command removes the specified changesets and all their
2525 descendants. If the working directory has uncommitted changes,
2525 descendants. If the working directory has uncommitted changes,
2526 the operation is aborted unless the --force flag is supplied.
2526 the operation is aborted unless the --force flag is supplied.
2527
2527
2528 If a parent of the working directory is stripped, then the working
2528 If a parent of the working directory is stripped, then the working
2529 directory will automatically be updated to the most recent
2529 directory will automatically be updated to the most recent
2530 available ancestor of the stripped parent after the operation
2530 available ancestor of the stripped parent after the operation
2531 completes.
2531 completes.
2532
2532
2533 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2533 Any stripped changesets are stored in ``.hg/strip-backup`` as a
2534 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2534 bundle (see :hg:`help bundle` and :hg:`help unbundle`). They can
2535 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2535 be restored by running :hg:`unbundle .hg/strip-backup/BUNDLE`,
2536 where BUNDLE is the bundle file created by the strip. Note that
2536 where BUNDLE is the bundle file created by the strip. Note that
2537 the local revision numbers will in general be different after the
2537 the local revision numbers will in general be different after the
2538 restore.
2538 restore.
2539
2539
2540 Use the --no-backup option to discard the backup bundle once the
2540 Use the --no-backup option to discard the backup bundle once the
2541 operation completes.
2541 operation completes.
2542
2542
2543 Return 0 on success.
2543 Return 0 on success.
2544 """
2544 """
2545 backup = 'all'
2545 backup = 'all'
2546 if opts.get('backup'):
2546 if opts.get('backup'):
2547 backup = 'strip'
2547 backup = 'strip'
2548 elif opts.get('no_backup') or opts.get('nobackup'):
2548 elif opts.get('no_backup') or opts.get('nobackup'):
2549 backup = 'none'
2549 backup = 'none'
2550
2550
2551 cl = repo.changelog
2551 cl = repo.changelog
2552 revs = set(cmdutil.revrange(repo, revs))
2552 revs = set(cmdutil.revrange(repo, revs))
2553 if not revs:
2553 if not revs:
2554 raise util.Abort(_('empty revision set'))
2554 raise util.Abort(_('empty revision set'))
2555
2555
2556 descendants = set(cl.descendants(*revs))
2556 descendants = set(cl.descendants(*revs))
2557 strippedrevs = revs.union(descendants)
2557 strippedrevs = revs.union(descendants)
2558 roots = revs.difference(descendants)
2558 roots = revs.difference(descendants)
2559
2559
2560 update = False
2560 update = False
2561 # if one of the wdir parent is stripped we'll need
2561 # if one of the wdir parent is stripped we'll need
2562 # to update away to an earlier revision
2562 # to update away to an earlier revision
2563 for p in repo.dirstate.parents():
2563 for p in repo.dirstate.parents():
2564 if p != nullid and cl.rev(p) in strippedrevs:
2564 if p != nullid and cl.rev(p) in strippedrevs:
2565 update = True
2565 update = True
2566 break
2566 break
2567
2567
2568 rootnodes = set(cl.node(r) for r in roots)
2568 rootnodes = set(cl.node(r) for r in roots)
2569
2569
2570 q = repo.mq
2570 q = repo.mq
2571 if q.applied:
2571 if q.applied:
2572 # refresh queue state if we're about to strip
2572 # refresh queue state if we're about to strip
2573 # applied patches
2573 # applied patches
2574 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2574 if cl.rev(repo.lookup('qtip')) in strippedrevs:
2575 q.applied_dirty = True
2575 q.applied_dirty = True
2576 start = 0
2576 start = 0
2577 end = len(q.applied)
2577 end = len(q.applied)
2578 for i, statusentry in enumerate(q.applied):
2578 for i, statusentry in enumerate(q.applied):
2579 if statusentry.node in rootnodes:
2579 if statusentry.node in rootnodes:
2580 # if one of the stripped roots is an applied
2580 # if one of the stripped roots is an applied
2581 # patch, only part of the queue is stripped
2581 # patch, only part of the queue is stripped
2582 start = i
2582 start = i
2583 break
2583 break
2584 del q.applied[start:end]
2584 del q.applied[start:end]
2585 q.save_dirty()
2585 q.save_dirty()
2586
2586
2587 revs = list(rootnodes)
2587 revs = list(rootnodes)
2588 if update and opts.get('keep'):
2588 if update and opts.get('keep'):
2589 wlock = repo.wlock()
2589 wlock = repo.wlock()
2590 try:
2590 try:
2591 urev = repo.mq.qparents(repo, revs[0])
2591 urev = repo.mq.qparents(repo, revs[0])
2592 repo.dirstate.rebuild(urev, repo[urev].manifest())
2592 repo.dirstate.rebuild(urev, repo[urev].manifest())
2593 repo.dirstate.write()
2593 repo.dirstate.write()
2594 update = False
2594 update = False
2595 finally:
2595 finally:
2596 wlock.release()
2596 wlock.release()
2597
2597
2598 repo.mq.strip(repo, revs, backup=backup, update=update,
2598 repo.mq.strip(repo, revs, backup=backup, update=update,
2599 force=opts.get('force'))
2599 force=opts.get('force'))
2600 return 0
2600 return 0
2601
2601
2602 def select(ui, repo, *args, **opts):
2602 def select(ui, repo, *args, **opts):
2603 '''set or print guarded patches to push
2603 '''set or print guarded patches to push
2604
2604
2605 Use the :hg:`qguard` command to set or print guards on patch, then use
2605 Use the :hg:`qguard` command to set or print guards on patch, then use
2606 qselect to tell mq which guards to use. A patch will be pushed if
2606 qselect to tell mq which guards to use. A patch will be pushed if
2607 it has no guards or any positive guards match the currently
2607 it has no guards or any positive guards match the currently
2608 selected guard, but will not be pushed if any negative guards
2608 selected guard, but will not be pushed if any negative guards
2609 match the current guard. For example::
2609 match the current guard. For example::
2610
2610
2611 qguard foo.patch -stable (negative guard)
2611 qguard foo.patch -stable (negative guard)
2612 qguard bar.patch +stable (positive guard)
2612 qguard bar.patch +stable (positive guard)
2613 qselect stable
2613 qselect stable
2614
2614
2615 This activates the "stable" guard. mq will skip foo.patch (because
2615 This activates the "stable" guard. mq will skip foo.patch (because
2616 it has a negative match) but push bar.patch (because it has a
2616 it has a negative match) but push bar.patch (because it has a
2617 positive match).
2617 positive match).
2618
2618
2619 With no arguments, prints the currently active guards.
2619 With no arguments, prints the currently active guards.
2620 With one argument, sets the active guard.
2620 With one argument, sets the active guard.
2621
2621
2622 Use -n/--none to deactivate guards (no other arguments needed).
2622 Use -n/--none to deactivate guards (no other arguments needed).
2623 When no guards are active, patches with positive guards are
2623 When no guards are active, patches with positive guards are
2624 skipped and patches with negative guards are pushed.
2624 skipped and patches with negative guards are pushed.
2625
2625
2626 qselect can change the guards on applied patches. It does not pop
2626 qselect can change the guards on applied patches. It does not pop
2627 guarded patches by default. Use --pop to pop back to the last
2627 guarded patches by default. Use --pop to pop back to the last
2628 applied patch that is not guarded. Use --reapply (which implies
2628 applied patch that is not guarded. Use --reapply (which implies
2629 --pop) to push back to the current patch afterwards, but skip
2629 --pop) to push back to the current patch afterwards, but skip
2630 guarded patches.
2630 guarded patches.
2631
2631
2632 Use -s/--series to print a list of all guards in the series file
2632 Use -s/--series to print a list of all guards in the series file
2633 (no other arguments needed). Use -v for more information.
2633 (no other arguments needed). Use -v for more information.
2634
2634
2635 Returns 0 on success.'''
2635 Returns 0 on success.'''
2636
2636
2637 q = repo.mq
2637 q = repo.mq
2638 guards = q.active()
2638 guards = q.active()
2639 if args or opts.get('none'):
2639 if args or opts.get('none'):
2640 old_unapplied = q.unapplied(repo)
2640 old_unapplied = q.unapplied(repo)
2641 old_guarded = [i for i in xrange(len(q.applied)) if
2641 old_guarded = [i for i in xrange(len(q.applied)) if
2642 not q.pushable(i)[0]]
2642 not q.pushable(i)[0]]
2643 q.set_active(args)
2643 q.set_active(args)
2644 q.save_dirty()
2644 q.save_dirty()
2645 if not args:
2645 if not args:
2646 ui.status(_('guards deactivated\n'))
2646 ui.status(_('guards deactivated\n'))
2647 if not opts.get('pop') and not opts.get('reapply'):
2647 if not opts.get('pop') and not opts.get('reapply'):
2648 unapplied = q.unapplied(repo)
2648 unapplied = q.unapplied(repo)
2649 guarded = [i for i in xrange(len(q.applied))
2649 guarded = [i for i in xrange(len(q.applied))
2650 if not q.pushable(i)[0]]
2650 if not q.pushable(i)[0]]
2651 if len(unapplied) != len(old_unapplied):
2651 if len(unapplied) != len(old_unapplied):
2652 ui.status(_('number of unguarded, unapplied patches has '
2652 ui.status(_('number of unguarded, unapplied patches has '
2653 'changed from %d to %d\n') %
2653 'changed from %d to %d\n') %
2654 (len(old_unapplied), len(unapplied)))
2654 (len(old_unapplied), len(unapplied)))
2655 if len(guarded) != len(old_guarded):
2655 if len(guarded) != len(old_guarded):
2656 ui.status(_('number of guarded, applied patches has changed '
2656 ui.status(_('number of guarded, applied patches has changed '
2657 'from %d to %d\n') %
2657 'from %d to %d\n') %
2658 (len(old_guarded), len(guarded)))
2658 (len(old_guarded), len(guarded)))
2659 elif opts.get('series'):
2659 elif opts.get('series'):
2660 guards = {}
2660 guards = {}
2661 noguards = 0
2661 noguards = 0
2662 for gs in q.series_guards:
2662 for gs in q.series_guards:
2663 if not gs:
2663 if not gs:
2664 noguards += 1
2664 noguards += 1
2665 for g in gs:
2665 for g in gs:
2666 guards.setdefault(g, 0)
2666 guards.setdefault(g, 0)
2667 guards[g] += 1
2667 guards[g] += 1
2668 if ui.verbose:
2668 if ui.verbose:
2669 guards['NONE'] = noguards
2669 guards['NONE'] = noguards
2670 guards = guards.items()
2670 guards = guards.items()
2671 guards.sort(key=lambda x: x[0][1:])
2671 guards.sort(key=lambda x: x[0][1:])
2672 if guards:
2672 if guards:
2673 ui.note(_('guards in series file:\n'))
2673 ui.note(_('guards in series file:\n'))
2674 for guard, count in guards:
2674 for guard, count in guards:
2675 ui.note('%2d ' % count)
2675 ui.note('%2d ' % count)
2676 ui.write(guard, '\n')
2676 ui.write(guard, '\n')
2677 else:
2677 else:
2678 ui.note(_('no guards in series file\n'))
2678 ui.note(_('no guards in series file\n'))
2679 else:
2679 else:
2680 if guards:
2680 if guards:
2681 ui.note(_('active guards:\n'))
2681 ui.note(_('active guards:\n'))
2682 for g in guards:
2682 for g in guards:
2683 ui.write(g, '\n')
2683 ui.write(g, '\n')
2684 else:
2684 else:
2685 ui.write(_('no active guards\n'))
2685 ui.write(_('no active guards\n'))
2686 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
2686 reapply = opts.get('reapply') and q.applied and q.appliedname(-1)
2687 popped = False
2687 popped = False
2688 if opts.get('pop') or opts.get('reapply'):
2688 if opts.get('pop') or opts.get('reapply'):
2689 for i in xrange(len(q.applied)):
2689 for i in xrange(len(q.applied)):
2690 pushable, reason = q.pushable(i)
2690 pushable, reason = q.pushable(i)
2691 if not pushable:
2691 if not pushable:
2692 ui.status(_('popping guarded patches\n'))
2692 ui.status(_('popping guarded patches\n'))
2693 popped = True
2693 popped = True
2694 if i == 0:
2694 if i == 0:
2695 q.pop(repo, all=True)
2695 q.pop(repo, all=True)
2696 else:
2696 else:
2697 q.pop(repo, i - 1)
2697 q.pop(repo, i - 1)
2698 break
2698 break
2699 if popped:
2699 if popped:
2700 try:
2700 try:
2701 if reapply:
2701 if reapply:
2702 ui.status(_('reapplying unguarded patches\n'))
2702 ui.status(_('reapplying unguarded patches\n'))
2703 q.push(repo, reapply)
2703 q.push(repo, reapply)
2704 finally:
2704 finally:
2705 q.save_dirty()
2705 q.save_dirty()
2706
2706
2707 def finish(ui, repo, *revrange, **opts):
2707 def finish(ui, repo, *revrange, **opts):
2708 """move applied patches into repository history
2708 """move applied patches into repository history
2709
2709
2710 Finishes the specified revisions (corresponding to applied
2710 Finishes the specified revisions (corresponding to applied
2711 patches) by moving them out of mq control into regular repository
2711 patches) by moving them out of mq control into regular repository
2712 history.
2712 history.
2713
2713
2714 Accepts a revision range or the -a/--applied option. If --applied
2714 Accepts a revision range or the -a/--applied option. If --applied
2715 is specified, all applied mq revisions are removed from mq
2715 is specified, all applied mq revisions are removed from mq
2716 control. Otherwise, the given revisions must be at the base of the
2716 control. Otherwise, the given revisions must be at the base of the
2717 stack of applied patches.
2717 stack of applied patches.
2718
2718
2719 This can be especially useful if your changes have been applied to
2719 This can be especially useful if your changes have been applied to
2720 an upstream repository, or if you are about to push your changes
2720 an upstream repository, or if you are about to push your changes
2721 to upstream.
2721 to upstream.
2722
2722
2723 Returns 0 on success.
2723 Returns 0 on success.
2724 """
2724 """
2725 if not opts.get('applied') and not revrange:
2725 if not opts.get('applied') and not revrange:
2726 raise util.Abort(_('no revisions specified'))
2726 raise util.Abort(_('no revisions specified'))
2727 elif opts.get('applied'):
2727 elif opts.get('applied'):
2728 revrange = ('qbase::qtip',) + revrange
2728 revrange = ('qbase::qtip',) + revrange
2729
2729
2730 q = repo.mq
2730 q = repo.mq
2731 if not q.applied:
2731 if not q.applied:
2732 ui.status(_('no patches applied\n'))
2732 ui.status(_('no patches applied\n'))
2733 return 0
2733 return 0
2734
2734
2735 revs = cmdutil.revrange(repo, revrange)
2735 revs = cmdutil.revrange(repo, revrange)
2736 q.finish(repo, revs)
2736 q.finish(repo, revs)
2737 q.save_dirty()
2737 q.save_dirty()
2738 return 0
2738 return 0
2739
2739
2740 def qqueue(ui, repo, name=None, **opts):
2740 def qqueue(ui, repo, name=None, **opts):
2741 '''manage multiple patch queues
2741 '''manage multiple patch queues
2742
2742
2743 Supports switching between different patch queues, as well as creating
2743 Supports switching between different patch queues, as well as creating
2744 new patch queues and deleting existing ones.
2744 new patch queues and deleting existing ones.
2745
2745
2746 Omitting a queue name or specifying -l/--list will show you the registered
2746 Omitting a queue name or specifying -l/--list will show you the registered
2747 queues - by default the "normal" patches queue is registered. The currently
2747 queues - by default the "normal" patches queue is registered. The currently
2748 active queue will be marked with "(active)".
2748 active queue will be marked with "(active)".
2749
2749
2750 To create a new queue, use -c/--create. The queue is automatically made
2750 To create a new queue, use -c/--create. The queue is automatically made
2751 active, except in the case where there are applied patches from the
2751 active, except in the case where there are applied patches from the
2752 currently active queue in the repository. Then the queue will only be
2752 currently active queue in the repository. Then the queue will only be
2753 created and switching will fail.
2753 created and switching will fail.
2754
2754
2755 To delete an existing queue, use --delete. You cannot delete the currently
2755 To delete an existing queue, use --delete. You cannot delete the currently
2756 active queue.
2756 active queue.
2757
2757
2758 Returns 0 on success.
2758 Returns 0 on success.
2759 '''
2759 '''
2760
2760
2761 q = repo.mq
2761 q = repo.mq
2762
2762
2763 _defaultqueue = 'patches'
2763 _defaultqueue = 'patches'
2764 _allqueues = 'patches.queues'
2764 _allqueues = 'patches.queues'
2765 _activequeue = 'patches.queue'
2765 _activequeue = 'patches.queue'
2766
2766
2767 def _getcurrent():
2767 def _getcurrent():
2768 cur = os.path.basename(q.path)
2768 cur = os.path.basename(q.path)
2769 if cur.startswith('patches-'):
2769 if cur.startswith('patches-'):
2770 cur = cur[8:]
2770 cur = cur[8:]
2771 return cur
2771 return cur
2772
2772
2773 def _noqueues():
2773 def _noqueues():
2774 try:
2774 try:
2775 fh = repo.opener(_allqueues, 'r')
2775 fh = repo.opener(_allqueues, 'r')
2776 fh.close()
2776 fh.close()
2777 except IOError:
2777 except IOError:
2778 return True
2778 return True
2779
2779
2780 return False
2780 return False
2781
2781
2782 def _getqueues():
2782 def _getqueues():
2783 current = _getcurrent()
2783 current = _getcurrent()
2784
2784
2785 try:
2785 try:
2786 fh = repo.opener(_allqueues, 'r')
2786 fh = repo.opener(_allqueues, 'r')
2787 queues = [queue.strip() for queue in fh if queue.strip()]
2787 queues = [queue.strip() for queue in fh if queue.strip()]
2788 if current not in queues:
2788 if current not in queues:
2789 queues.append(current)
2789 queues.append(current)
2790 except IOError:
2790 except IOError:
2791 queues = [_defaultqueue]
2791 queues = [_defaultqueue]
2792
2792
2793 return sorted(queues)
2793 return sorted(queues)
2794
2794
2795 def _setactive(name):
2795 def _setactive(name):
2796 if q.applied:
2796 if q.applied:
2797 raise util.Abort(_('patches applied - cannot set new queue active'))
2797 raise util.Abort(_('patches applied - cannot set new queue active'))
2798 _setactivenocheck(name)
2798 _setactivenocheck(name)
2799
2799
2800 def _setactivenocheck(name):
2800 def _setactivenocheck(name):
2801 fh = repo.opener(_activequeue, 'w')
2801 fh = repo.opener(_activequeue, 'w')
2802 if name != 'patches':
2802 if name != 'patches':
2803 fh.write(name)
2803 fh.write(name)
2804 fh.close()
2804 fh.close()
2805
2805
2806 def _addqueue(name):
2806 def _addqueue(name):
2807 fh = repo.opener(_allqueues, 'a')
2807 fh = repo.opener(_allqueues, 'a')
2808 fh.write('%s\n' % (name,))
2808 fh.write('%s\n' % (name,))
2809 fh.close()
2809 fh.close()
2810
2810
2811 def _queuedir(name):
2811 def _queuedir(name):
2812 if name == 'patches':
2812 if name == 'patches':
2813 return repo.join('patches')
2813 return repo.join('patches')
2814 else:
2814 else:
2815 return repo.join('patches-' + name)
2815 return repo.join('patches-' + name)
2816
2816
2817 def _validname(name):
2817 def _validname(name):
2818 for n in name:
2818 for n in name:
2819 if n in ':\\/.':
2819 if n in ':\\/.':
2820 return False
2820 return False
2821 return True
2821 return True
2822
2822
2823 def _delete(name):
2823 def _delete(name):
2824 if name not in existing:
2824 if name not in existing:
2825 raise util.Abort(_('cannot delete queue that does not exist'))
2825 raise util.Abort(_('cannot delete queue that does not exist'))
2826
2826
2827 current = _getcurrent()
2827 current = _getcurrent()
2828
2828
2829 if name == current:
2829 if name == current:
2830 raise util.Abort(_('cannot delete currently active queue'))
2830 raise util.Abort(_('cannot delete currently active queue'))
2831
2831
2832 fh = repo.opener('patches.queues.new', 'w')
2832 fh = repo.opener('patches.queues.new', 'w')
2833 for queue in existing:
2833 for queue in existing:
2834 if queue == name:
2834 if queue == name:
2835 continue
2835 continue
2836 fh.write('%s\n' % (queue,))
2836 fh.write('%s\n' % (queue,))
2837 fh.close()
2837 fh.close()
2838 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2838 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2839
2839
2840 if not name or opts.get('list'):
2840 if not name or opts.get('list'):
2841 current = _getcurrent()
2841 current = _getcurrent()
2842 for queue in _getqueues():
2842 for queue in _getqueues():
2843 ui.write('%s' % (queue,))
2843 ui.write('%s' % (queue,))
2844 if queue == current and not ui.quiet:
2844 if queue == current and not ui.quiet:
2845 ui.write(_(' (active)\n'))
2845 ui.write(_(' (active)\n'))
2846 else:
2846 else:
2847 ui.write('\n')
2847 ui.write('\n')
2848 return
2848 return
2849
2849
2850 if not _validname(name):
2850 if not _validname(name):
2851 raise util.Abort(
2851 raise util.Abort(
2852 _('invalid queue name, may not contain the characters ":\\/."'))
2852 _('invalid queue name, may not contain the characters ":\\/."'))
2853
2853
2854 existing = _getqueues()
2854 existing = _getqueues()
2855
2855
2856 if opts.get('create'):
2856 if opts.get('create'):
2857 if name in existing:
2857 if name in existing:
2858 raise util.Abort(_('queue "%s" already exists') % name)
2858 raise util.Abort(_('queue "%s" already exists') % name)
2859 if _noqueues():
2859 if _noqueues():
2860 _addqueue(_defaultqueue)
2860 _addqueue(_defaultqueue)
2861 _addqueue(name)
2861 _addqueue(name)
2862 _setactive(name)
2862 _setactive(name)
2863 elif opts.get('rename'):
2863 elif opts.get('rename'):
2864 current = _getcurrent()
2864 current = _getcurrent()
2865 if name == current:
2865 if name == current:
2866 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
2866 raise util.Abort(_('can\'t rename "%s" to its current name') % name)
2867 if name in existing:
2867 if name in existing:
2868 raise util.Abort(_('queue "%s" already exists') % name)
2868 raise util.Abort(_('queue "%s" already exists') % name)
2869
2869
2870 olddir = _queuedir(current)
2870 olddir = _queuedir(current)
2871 newdir = _queuedir(name)
2871 newdir = _queuedir(name)
2872
2872
2873 if os.path.exists(newdir):
2873 if os.path.exists(newdir):
2874 raise util.Abort(_('non-queue directory "%s" already exists') %
2874 raise util.Abort(_('non-queue directory "%s" already exists') %
2875 newdir)
2875 newdir)
2876
2876
2877 fh = repo.opener('patches.queues.new', 'w')
2877 fh = repo.opener('patches.queues.new', 'w')
2878 for queue in existing:
2878 for queue in existing:
2879 if queue == current:
2879 if queue == current:
2880 fh.write('%s\n' % (name,))
2880 fh.write('%s\n' % (name,))
2881 if os.path.exists(olddir):
2881 if os.path.exists(olddir):
2882 util.rename(olddir, newdir)
2882 util.rename(olddir, newdir)
2883 else:
2883 else:
2884 fh.write('%s\n' % (queue,))
2884 fh.write('%s\n' % (queue,))
2885 fh.close()
2885 fh.close()
2886 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2886 util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
2887 _setactivenocheck(name)
2887 _setactivenocheck(name)
2888 elif opts.get('delete'):
2888 elif opts.get('delete'):
2889 _delete(name)
2889 _delete(name)
2890 elif opts.get('purge'):
2890 elif opts.get('purge'):
2891 if name in existing:
2891 if name in existing:
2892 _delete(name)
2892 _delete(name)
2893 qdir = _queuedir(name)
2893 qdir = _queuedir(name)
2894 if os.path.exists(qdir):
2894 if os.path.exists(qdir):
2895 shutil.rmtree(qdir)
2895 shutil.rmtree(qdir)
2896 else:
2896 else:
2897 if name not in existing:
2897 if name not in existing:
2898 raise util.Abort(_('use --create to create a new queue'))
2898 raise util.Abort(_('use --create to create a new queue'))
2899 _setactive(name)
2899 _setactive(name)
2900
2900
2901 def reposetup(ui, repo):
2901 def reposetup(ui, repo):
2902 class mqrepo(repo.__class__):
2902 class mqrepo(repo.__class__):
2903 @util.propertycache
2903 @util.propertycache
2904 def mq(self):
2904 def mq(self):
2905 return queue(self.ui, self.join(""))
2905 return queue(self.ui, self.join(""))
2906
2906
2907 def abort_if_wdir_patched(self, errmsg, force=False):
2907 def abort_if_wdir_patched(self, errmsg, force=False):
2908 if self.mq.applied and not force:
2908 if self.mq.applied and not force:
2909 parent = self.dirstate.parents()[0]
2909 parent = self.dirstate.parents()[0]
2910 if parent in [s.node for s in self.mq.applied]:
2910 if parent in [s.node for s in self.mq.applied]:
2911 raise util.Abort(errmsg)
2911 raise util.Abort(errmsg)
2912
2912
2913 def commit(self, text="", user=None, date=None, match=None,
2913 def commit(self, text="", user=None, date=None, match=None,
2914 force=False, editor=False, extra={}):
2914 force=False, editor=False, extra={}):
2915 self.abort_if_wdir_patched(
2915 self.abort_if_wdir_patched(
2916 _('cannot commit over an applied mq patch'),
2916 _('cannot commit over an applied mq patch'),
2917 force)
2917 force)
2918
2918
2919 return super(mqrepo, self).commit(text, user, date, match, force,
2919 return super(mqrepo, self).commit(text, user, date, match, force,
2920 editor, extra)
2920 editor, extra)
2921
2921
2922 def push(self, remote, force=False, revs=None, newbranch=False):
2922 def push(self, remote, force=False, revs=None, newbranch=False):
2923 if self.mq.applied and not force:
2923 if self.mq.applied and not force:
2924 haspatches = True
2924 haspatches = True
2925 if revs:
2925 if revs:
2926 # Assume applied patches have no non-patch descendants
2926 # Assume applied patches have no non-patch descendants
2927 # and are not on remote already. If they appear in the
2927 # and are not on remote already. If they appear in the
2928 # set of resolved 'revs', bail out.
2928 # set of resolved 'revs', bail out.
2929 applied = set(e.node for e in self.mq.applied)
2929 applied = set(e.node for e in self.mq.applied)
2930 haspatches = bool([n for n in revs if n in applied])
2930 haspatches = bool([n for n in revs if n in applied])
2931 if haspatches:
2931 if haspatches:
2932 raise util.Abort(_('source has mq patches applied'))
2932 raise util.Abort(_('source has mq patches applied'))
2933 return super(mqrepo, self).push(remote, force, revs, newbranch)
2933 return super(mqrepo, self).push(remote, force, revs, newbranch)
2934
2934
2935 def _findtags(self):
2935 def _findtags(self):
2936 '''augment tags from base class with patch tags'''
2936 '''augment tags from base class with patch tags'''
2937 result = super(mqrepo, self)._findtags()
2937 result = super(mqrepo, self)._findtags()
2938
2938
2939 q = self.mq
2939 q = self.mq
2940 if not q.applied:
2940 if not q.applied:
2941 return result
2941 return result
2942
2942
2943 mqtags = [(patch.node, patch.name) for patch in q.applied]
2943 mqtags = [(patch.node, patch.name) for patch in q.applied]
2944
2944
2945 if mqtags[-1][0] not in self.changelog.nodemap:
2945 if mqtags[-1][0] not in self.changelog.nodemap:
2946 self.ui.warn(_('mq status file refers to unknown node %s\n')
2946 self.ui.warn(_('mq status file refers to unknown node %s\n')
2947 % short(mqtags[-1][0]))
2947 % short(mqtags[-1][0]))
2948 return result
2948 return result
2949
2949
2950 mqtags.append((mqtags[-1][0], 'qtip'))
2950 mqtags.append((mqtags[-1][0], 'qtip'))
2951 mqtags.append((mqtags[0][0], 'qbase'))
2951 mqtags.append((mqtags[0][0], 'qbase'))
2952 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2952 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2953 tags = result[0]
2953 tags = result[0]
2954 for patch in mqtags:
2954 for patch in mqtags:
2955 if patch[1] in tags:
2955 if patch[1] in tags:
2956 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2956 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2957 % patch[1])
2957 % patch[1])
2958 else:
2958 else:
2959 tags[patch[1]] = patch[0]
2959 tags[patch[1]] = patch[0]
2960
2960
2961 return result
2961 return result
2962
2962
2963 def _branchtags(self, partial, lrev):
2963 def _branchtags(self, partial, lrev):
2964 q = self.mq
2964 q = self.mq
2965 if not q.applied:
2965 if not q.applied:
2966 return super(mqrepo, self)._branchtags(partial, lrev)
2966 return super(mqrepo, self)._branchtags(partial, lrev)
2967
2967
2968 cl = self.changelog
2968 cl = self.changelog
2969 qbasenode = q.applied[0].node
2969 qbasenode = q.applied[0].node
2970 if qbasenode not in cl.nodemap:
2970 if qbasenode not in cl.nodemap:
2971 self.ui.warn(_('mq status file refers to unknown node %s\n')
2971 self.ui.warn(_('mq status file refers to unknown node %s\n')
2972 % short(qbasenode))
2972 % short(qbasenode))
2973 return super(mqrepo, self)._branchtags(partial, lrev)
2973 return super(mqrepo, self)._branchtags(partial, lrev)
2974
2974
2975 qbase = cl.rev(qbasenode)
2975 qbase = cl.rev(qbasenode)
2976 start = lrev + 1
2976 start = lrev + 1
2977 if start < qbase:
2977 if start < qbase:
2978 # update the cache (excluding the patches) and save it
2978 # update the cache (excluding the patches) and save it
2979 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
2979 ctxgen = (self[r] for r in xrange(lrev + 1, qbase))
2980 self._updatebranchcache(partial, ctxgen)
2980 self._updatebranchcache(partial, ctxgen)
2981 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
2981 self._writebranchcache(partial, cl.node(qbase - 1), qbase - 1)
2982 start = qbase
2982 start = qbase
2983 # if start = qbase, the cache is as updated as it should be.
2983 # if start = qbase, the cache is as updated as it should be.
2984 # if start > qbase, the cache includes (part of) the patches.
2984 # if start > qbase, the cache includes (part of) the patches.
2985 # we might as well use it, but we won't save it.
2985 # we might as well use it, but we won't save it.
2986
2986
2987 # update the cache up to the tip
2987 # update the cache up to the tip
2988 ctxgen = (self[r] for r in xrange(start, len(cl)))
2988 ctxgen = (self[r] for r in xrange(start, len(cl)))
2989 self._updatebranchcache(partial, ctxgen)
2989 self._updatebranchcache(partial, ctxgen)
2990
2990
2991 return partial
2991 return partial
2992
2992
2993 if repo.local():
2993 if repo.local():
2994 repo.__class__ = mqrepo
2994 repo.__class__ = mqrepo
2995
2995
2996 def mqimport(orig, ui, repo, *args, **kwargs):
2996 def mqimport(orig, ui, repo, *args, **kwargs):
2997 if (hasattr(repo, 'abort_if_wdir_patched')
2997 if (hasattr(repo, 'abort_if_wdir_patched')
2998 and not kwargs.get('no_commit', False)):
2998 and not kwargs.get('no_commit', False)):
2999 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2999 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
3000 kwargs.get('force'))
3000 kwargs.get('force'))
3001 return orig(ui, repo, *args, **kwargs)
3001 return orig(ui, repo, *args, **kwargs)
3002
3002
3003 def mqinit(orig, ui, *args, **kwargs):
3003 def mqinit(orig, ui, *args, **kwargs):
3004 mq = kwargs.pop('mq', None)
3004 mq = kwargs.pop('mq', None)
3005
3005
3006 if not mq:
3006 if not mq:
3007 return orig(ui, *args, **kwargs)
3007 return orig(ui, *args, **kwargs)
3008
3008
3009 if args:
3009 if args:
3010 repopath = args[0]
3010 repopath = args[0]
3011 if not hg.islocal(repopath):
3011 if not hg.islocal(repopath):
3012 raise util.Abort(_('only a local queue repository '
3012 raise util.Abort(_('only a local queue repository '
3013 'may be initialized'))
3013 'may be initialized'))
3014 else:
3014 else:
3015 repopath = cmdutil.findrepo(os.getcwd())
3015 repopath = cmdutil.findrepo(os.getcwd())
3016 if not repopath:
3016 if not repopath:
3017 raise util.Abort(_('there is no Mercurial repository here '
3017 raise util.Abort(_('there is no Mercurial repository here '
3018 '(.hg not found)'))
3018 '(.hg not found)'))
3019 repo = hg.repository(ui, repopath)
3019 repo = hg.repository(ui, repopath)
3020 return qinit(ui, repo, True)
3020 return qinit(ui, repo, True)
3021
3021
3022 def mqcommand(orig, ui, repo, *args, **kwargs):
3022 def mqcommand(orig, ui, repo, *args, **kwargs):
3023 """Add --mq option to operate on patch repository instead of main"""
3023 """Add --mq option to operate on patch repository instead of main"""
3024
3024
3025 # some commands do not like getting unknown options
3025 # some commands do not like getting unknown options
3026 mq = kwargs.pop('mq', None)
3026 mq = kwargs.pop('mq', None)
3027
3027
3028 if not mq:
3028 if not mq:
3029 return orig(ui, repo, *args, **kwargs)
3029 return orig(ui, repo, *args, **kwargs)
3030
3030
3031 q = repo.mq
3031 q = repo.mq
3032 r = q.qrepo()
3032 r = q.qrepo()
3033 if not r:
3033 if not r:
3034 raise util.Abort(_('no queue repository'))
3034 raise util.Abort(_('no queue repository'))
3035 return orig(r.ui, r, *args, **kwargs)
3035 return orig(r.ui, r, *args, **kwargs)
3036
3036
3037 def summary(orig, ui, repo, *args, **kwargs):
3037 def summary(orig, ui, repo, *args, **kwargs):
3038 r = orig(ui, repo, *args, **kwargs)
3038 r = orig(ui, repo, *args, **kwargs)
3039 q = repo.mq
3039 q = repo.mq
3040 m = []
3040 m = []
3041 a, u = len(q.applied), len(q.unapplied(repo))
3041 a, u = len(q.applied), len(q.unapplied(repo))
3042 if a:
3042 if a:
3043 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3043 m.append(ui.label(_("%d applied"), 'qseries.applied') % a)
3044 if u:
3044 if u:
3045 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3045 m.append(ui.label(_("%d unapplied"), 'qseries.unapplied') % u)
3046 if m:
3046 if m:
3047 ui.write("mq: %s\n" % ', '.join(m))
3047 ui.write("mq: %s\n" % ', '.join(m))
3048 else:
3048 else:
3049 ui.note(_("mq: (empty queue)\n"))
3049 ui.note(_("mq: (empty queue)\n"))
3050 return r
3050 return r
3051
3051
3052 def uisetup(ui):
3052 def uisetup(ui):
3053 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3053 mqopt = [('', 'mq', None, _("operate on patch repository"))]
3054
3054
3055 extensions.wrapcommand(commands.table, 'import', mqimport)
3055 extensions.wrapcommand(commands.table, 'import', mqimport)
3056 extensions.wrapcommand(commands.table, 'summary', summary)
3056 extensions.wrapcommand(commands.table, 'summary', summary)
3057
3057
3058 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3058 entry = extensions.wrapcommand(commands.table, 'init', mqinit)
3059 entry[1].extend(mqopt)
3059 entry[1].extend(mqopt)
3060
3060
3061 nowrap = set(commands.norepo.split(" ") + ['qrecord'])
3061 nowrap = set(commands.norepo.split(" ") + ['qrecord'])
3062
3062
3063 def dotable(cmdtable):
3063 def dotable(cmdtable):
3064 for cmd in cmdtable.keys():
3064 for cmd in cmdtable.keys():
3065 cmd = cmdutil.parsealiases(cmd)[0]
3065 cmd = cmdutil.parsealiases(cmd)[0]
3066 if cmd in nowrap:
3066 if cmd in nowrap:
3067 continue
3067 continue
3068 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3068 entry = extensions.wrapcommand(cmdtable, cmd, mqcommand)
3069 entry[1].extend(mqopt)
3069 entry[1].extend(mqopt)
3070
3070
3071 dotable(commands.table)
3071 dotable(commands.table)
3072
3072
3073 for extname, extmodule in extensions.extensions():
3073 for extname, extmodule in extensions.extensions():
3074 if extmodule.__file__ != __file__:
3074 if extmodule.__file__ != __file__:
3075 dotable(getattr(extmodule, 'cmdtable', {}))
3075 dotable(getattr(extmodule, 'cmdtable', {}))
3076
3076
3077 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
3077 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
3078
3078
3079 cmdtable = {
3079 cmdtable = {
3080 "qapplied":
3080 "qapplied":
3081 (applied,
3081 (applied,
3082 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
3082 [('1', 'last', None, _('show only the last patch'))] + seriesopts,
3083 _('hg qapplied [-1] [-s] [PATCH]')),
3083 _('hg qapplied [-1] [-s] [PATCH]')),
3084 "qclone":
3084 "qclone":
3085 (clone,
3085 (clone,
3086 [('', 'pull', None, _('use pull protocol to copy metadata')),
3086 [('', 'pull', None, _('use pull protocol to copy metadata')),
3087 ('U', 'noupdate', None, _('do not update the new working directories')),
3087 ('U', 'noupdate', None, _('do not update the new working directories')),
3088 ('', 'uncompressed', None,
3088 ('', 'uncompressed', None,
3089 _('use uncompressed transfer (fast over LAN)')),
3089 _('use uncompressed transfer (fast over LAN)')),
3090 ('p', 'patches', '',
3090 ('p', 'patches', '',
3091 _('location of source patch repository'), _('REPO')),
3091 _('location of source patch repository'), _('REPO')),
3092 ] + commands.remoteopts,
3092 ] + commands.remoteopts,
3093 _('hg qclone [OPTION]... SOURCE [DEST]')),
3093 _('hg qclone [OPTION]... SOURCE [DEST]')),
3094 "qcommit|qci":
3094 "qcommit|qci":
3095 (commit,
3095 (commit,
3096 commands.table["^commit|ci"][1],
3096 commands.table["^commit|ci"][1],
3097 _('hg qcommit [OPTION]... [FILE]...')),
3097 _('hg qcommit [OPTION]... [FILE]...')),
3098 "^qdiff":
3098 "^qdiff":
3099 (diff,
3099 (diff,
3100 commands.diffopts + commands.diffopts2 + commands.walkopts,
3100 commands.diffopts + commands.diffopts2 + commands.walkopts,
3101 _('hg qdiff [OPTION]... [FILE]...')),
3101 _('hg qdiff [OPTION]... [FILE]...')),
3102 "qdelete|qremove|qrm":
3102 "qdelete|qremove|qrm":
3103 (delete,
3103 (delete,
3104 [('k', 'keep', None, _('keep patch file')),
3104 [('k', 'keep', None, _('keep patch file')),
3105 ('r', 'rev', [],
3105 ('r', 'rev', [],
3106 _('stop managing a revision (DEPRECATED)'), _('REV'))],
3106 _('stop managing a revision (DEPRECATED)'), _('REV'))],
3107 _('hg qdelete [-k] [PATCH]...')),
3107 _('hg qdelete [-k] [PATCH]...')),
3108 'qfold':
3108 'qfold':
3109 (fold,
3109 (fold,
3110 [('e', 'edit', None, _('edit patch header')),
3110 [('e', 'edit', None, _('edit patch header')),
3111 ('k', 'keep', None, _('keep folded patch files')),
3111 ('k', 'keep', None, _('keep folded patch files')),
3112 ] + commands.commitopts,
3112 ] + commands.commitopts,
3113 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
3113 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
3114 'qgoto':
3114 'qgoto':
3115 (goto,
3115 (goto,
3116 [('f', 'force', None, _('overwrite any local changes'))],
3116 [('f', 'force', None, _('overwrite any local changes'))],
3117 _('hg qgoto [OPTION]... PATCH')),
3117 _('hg qgoto [OPTION]... PATCH')),
3118 'qguard':
3118 'qguard':
3119 (guard,
3119 (guard,
3120 [('l', 'list', None, _('list all patches and guards')),
3120 [('l', 'list', None, _('list all patches and guards')),
3121 ('n', 'none', None, _('drop all guards'))],
3121 ('n', 'none', None, _('drop all guards'))],
3122 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
3122 _('hg qguard [-l] [-n] [PATCH] [-- [+GUARD]... [-GUARD]...]')),
3123 'qheader': (header, [], _('hg qheader [PATCH]')),
3123 'qheader': (header, [], _('hg qheader [PATCH]')),
3124 "qimport":
3124 "qimport":
3125 (qimport,
3125 (qimport,
3126 [('e', 'existing', None, _('import file in patch directory')),
3126 [('e', 'existing', None, _('import file in patch directory')),
3127 ('n', 'name', '',
3127 ('n', 'name', '',
3128 _('name of patch file'), _('NAME')),
3128 _('name of patch file'), _('NAME')),
3129 ('f', 'force', None, _('overwrite existing files')),
3129 ('f', 'force', None, _('overwrite existing files')),
3130 ('r', 'rev', [],
3130 ('r', 'rev', [],
3131 _('place existing revisions under mq control'), _('REV')),
3131 _('place existing revisions under mq control'), _('REV')),
3132 ('g', 'git', None, _('use git extended diff format')),
3132 ('g', 'git', None, _('use git extended diff format')),
3133 ('P', 'push', None, _('qpush after importing'))],
3133 ('P', 'push', None, _('qpush after importing'))],
3134 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
3134 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
3135 "^qinit":
3135 "^qinit":
3136 (init,
3136 (init,
3137 [('c', 'create-repo', None, _('create queue repository'))],
3137 [('c', 'create-repo', None, _('create queue repository'))],
3138 _('hg qinit [-c]')),
3138 _('hg qinit [-c]')),
3139 "^qnew":
3139 "^qnew":
3140 (new,
3140 (new,
3141 [('e', 'edit', None, _('edit commit message')),
3141 [('e', 'edit', None, _('edit commit message')),
3142 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
3142 ('f', 'force', None, _('import uncommitted changes (DEPRECATED)')),
3143 ('g', 'git', None, _('use git extended diff format')),
3143 ('g', 'git', None, _('use git extended diff format')),
3144 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
3144 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
3145 ('u', 'user', '',
3145 ('u', 'user', '',
3146 _('add "From: <USER>" to patch'), _('USER')),
3146 _('add "From: <USER>" to patch'), _('USER')),
3147 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
3147 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
3148 ('d', 'date', '',
3148 ('d', 'date', '',
3149 _('add "Date: <DATE>" to patch'), _('DATE'))
3149 _('add "Date: <DATE>" to patch'), _('DATE'))
3150 ] + commands.walkopts + commands.commitopts,
3150 ] + commands.walkopts + commands.commitopts,
3151 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...')),
3151 _('hg qnew [-e] [-m TEXT] [-l FILE] PATCH [FILE]...')),
3152 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
3152 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
3153 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
3153 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
3154 "^qpop":
3154 "^qpop":
3155 (pop,
3155 (pop,
3156 [('a', 'all', None, _('pop all patches')),
3156 [('a', 'all', None, _('pop all patches')),
3157 ('n', 'name', '',
3157 ('n', 'name', '',
3158 _('queue name to pop (DEPRECATED)'), _('NAME')),
3158 _('queue name to pop (DEPRECATED)'), _('NAME')),
3159 ('f', 'force', None, _('forget any local changes to patched files'))],
3159 ('f', 'force', None, _('forget any local changes to patched files'))],
3160 _('hg qpop [-a] [-f] [PATCH | INDEX]')),
3160 _('hg qpop [-a] [-f] [PATCH | INDEX]')),
3161 "^qpush":
3161 "^qpush":
3162 (push,
3162 (push,
3163 [('f', 'force', None, _('apply on top of local changes')),
3163 [('f', 'force', None, _('apply on top of local changes')),
3164 ('e', 'exact', None, _('apply the target patch to its recorded parent')),
3164 ('e', 'exact', None, _('apply the target patch to its recorded parent')),
3165 ('l', 'list', None, _('list patch name in commit text')),
3165 ('l', 'list', None, _('list patch name in commit text')),
3166 ('a', 'all', None, _('apply all patches')),
3166 ('a', 'all', None, _('apply all patches')),
3167 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
3167 ('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
3168 ('n', 'name', '',
3168 ('n', 'name', '',
3169 _('merge queue name (DEPRECATED)'), _('NAME')),
3169 _('merge queue name (DEPRECATED)'), _('NAME')),
3170 ('', 'move', None, _('reorder patch series and apply only the patch'))],
3170 ('', 'move', None, _('reorder patch series and apply only the patch'))],
3171 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]')),
3171 _('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]')),
3172 "^qrefresh":
3172 "^qrefresh":
3173 (refresh,
3173 (refresh,
3174 [('e', 'edit', None, _('edit commit message')),
3174 [('e', 'edit', None, _('edit commit message')),
3175 ('g', 'git', None, _('use git extended diff format')),
3175 ('g', 'git', None, _('use git extended diff format')),
3176 ('s', 'short', None,
3176 ('s', 'short', None,
3177 _('refresh only files already in the patch and specified files')),
3177 _('refresh only files already in the patch and specified files')),
3178 ('U', 'currentuser', None,
3178 ('U', 'currentuser', None,
3179 _('add/update author field in patch with current user')),
3179 _('add/update author field in patch with current user')),
3180 ('u', 'user', '',
3180 ('u', 'user', '',
3181 _('add/update author field in patch with given user'), _('USER')),
3181 _('add/update author field in patch with given user'), _('USER')),
3182 ('D', 'currentdate', None,
3182 ('D', 'currentdate', None,
3183 _('add/update date field in patch with current date')),
3183 _('add/update date field in patch with current date')),
3184 ('d', 'date', '',
3184 ('d', 'date', '',
3185 _('add/update date field in patch with given date'), _('DATE'))
3185 _('add/update date field in patch with given date'), _('DATE'))
3186 ] + commands.walkopts + commands.commitopts,
3186 ] + commands.walkopts + commands.commitopts,
3187 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
3187 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
3188 'qrename|qmv':
3188 'qrename|qmv':
3189 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
3189 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
3190 "qrestore":
3190 "qrestore":
3191 (restore,
3191 (restore,
3192 [('d', 'delete', None, _('delete save entry')),
3192 [('d', 'delete', None, _('delete save entry')),
3193 ('u', 'update', None, _('update queue working directory'))],
3193 ('u', 'update', None, _('update queue working directory'))],
3194 _('hg qrestore [-d] [-u] REV')),
3194 _('hg qrestore [-d] [-u] REV')),
3195 "qsave":
3195 "qsave":
3196 (save,
3196 (save,
3197 [('c', 'copy', None, _('copy patch directory')),
3197 [('c', 'copy', None, _('copy patch directory')),
3198 ('n', 'name', '',
3198 ('n', 'name', '',
3199 _('copy directory name'), _('NAME')),
3199 _('copy directory name'), _('NAME')),
3200 ('e', 'empty', None, _('clear queue status file')),
3200 ('e', 'empty', None, _('clear queue status file')),
3201 ('f', 'force', None, _('force copy'))] + commands.commitopts,
3201 ('f', 'force', None, _('force copy'))] + commands.commitopts,
3202 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
3202 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
3203 "qselect":
3203 "qselect":
3204 (select,
3204 (select,
3205 [('n', 'none', None, _('disable all guards')),
3205 [('n', 'none', None, _('disable all guards')),
3206 ('s', 'series', None, _('list all guards in series file')),
3206 ('s', 'series', None, _('list all guards in series file')),
3207 ('', 'pop', None, _('pop to before first guarded applied patch')),
3207 ('', 'pop', None, _('pop to before first guarded applied patch')),
3208 ('', 'reapply', None, _('pop, then reapply patches'))],
3208 ('', 'reapply', None, _('pop, then reapply patches'))],
3209 _('hg qselect [OPTION]... [GUARD]...')),
3209 _('hg qselect [OPTION]... [GUARD]...')),
3210 "qseries":
3210 "qseries":
3211 (series,
3211 (series,
3212 [('m', 'missing', None, _('print patches not in series')),
3212 [('m', 'missing', None, _('print patches not in series')),
3213 ] + seriesopts,
3213 ] + seriesopts,
3214 _('hg qseries [-ms]')),
3214 _('hg qseries [-ms]')),
3215 "strip":
3215 "strip":
3216 (strip,
3216 (strip,
3217 [('f', 'force', None, _('force removal of changesets even if the '
3217 [('f', 'force', None, _('force removal of changesets even if the '
3218 'working directory has uncommitted changes')),
3218 'working directory has uncommitted changes')),
3219 ('b', 'backup', None, _('bundle only changesets with local revision'
3219 ('b', 'backup', None, _('bundle only changesets with local revision'
3220 ' number greater than REV which are not'
3220 ' number greater than REV which are not'
3221 ' descendants of REV (DEPRECATED)')),
3221 ' descendants of REV (DEPRECATED)')),
3222 ('n', 'no-backup', None, _('no backups')),
3222 ('n', 'no-backup', None, _('no backups')),
3223 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
3223 ('', 'nobackup', None, _('no backups (DEPRECATED)')),
3224 ('k', 'keep', None, _("do not modify working copy during strip"))],
3224 ('k', 'keep', None, _("do not modify working copy during strip"))],
3225 _('hg strip [-k] [-f] [-n] REV...')),
3225 _('hg strip [-k] [-f] [-n] REV...')),
3226 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
3226 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
3227 "qunapplied":
3227 "qunapplied":
3228 (unapplied,
3228 (unapplied,
3229 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
3229 [('1', 'first', None, _('show only the first patch'))] + seriesopts,
3230 _('hg qunapplied [-1] [-s] [PATCH]')),
3230 _('hg qunapplied [-1] [-s] [PATCH]')),
3231 "qfinish":
3231 "qfinish":
3232 (finish,
3232 (finish,
3233 [('a', 'applied', None, _('finish all applied changesets'))],
3233 [('a', 'applied', None, _('finish all applied changesets'))],
3234 _('hg qfinish [-a] [REV]...')),
3234 _('hg qfinish [-a] [REV]...')),
3235 'qqueue':
3235 'qqueue':
3236 (qqueue,
3236 (qqueue,
3237 [
3237 [
3238 ('l', 'list', False, _('list all available queues')),
3238 ('l', 'list', False, _('list all available queues')),
3239 ('c', 'create', False, _('create new queue')),
3239 ('c', 'create', False, _('create new queue')),
3240 ('', 'rename', False, _('rename active queue')),
3240 ('', 'rename', False, _('rename active queue')),
3241 ('', 'delete', False, _('delete reference to queue')),
3241 ('', 'delete', False, _('delete reference to queue')),
3242 ('', 'purge', False, _('delete queue, and remove patch dir')),
3242 ('', 'purge', False, _('delete queue, and remove patch dir')),
3243 ],
3243 ],
3244 _('[OPTION] [QUEUE]')),
3244 _('[OPTION] [QUEUE]')),
3245 }
3245 }
3246
3246
3247 colortable = {'qguard.negative': 'red',
3247 colortable = {'qguard.negative': 'red',
3248 'qguard.positive': 'yellow',
3248 'qguard.positive': 'yellow',
3249 'qguard.unguarded': 'green',
3249 'qguard.unguarded': 'green',
3250 'qseries.applied': 'blue bold underline',
3250 'qseries.applied': 'blue bold underline',
3251 'qseries.guarded': 'black bold',
3251 'qseries.guarded': 'black bold',
3252 'qseries.missing': 'red bold',
3252 'qseries.missing': 'red bold',
3253 'qseries.unapplied': 'black bold'}
3253 'qseries.unapplied': 'black bold'}
@@ -1,577 +1,577 b''
1 # rebase.py - rebasing feature for mercurial
1 # rebase.py - rebasing feature for mercurial
2 #
2 #
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot com>
3 # Copyright 2008 Stefano Tortarolo <stefano.tortarolo at gmail dot 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 '''command to move sets of revisions to a different ancestor
8 '''command to move sets of revisions to a different ancestor
9
9
10 This extension lets you rebase changesets in an existing Mercurial
10 This extension lets you rebase changesets in an existing Mercurial
11 repository.
11 repository.
12
12
13 For more information:
13 For more information:
14 http://mercurial.selenic.com/wiki/RebaseExtension
14 http://mercurial.selenic.com/wiki/RebaseExtension
15 '''
15 '''
16
16
17 from mercurial import hg, util, repair, merge, cmdutil, commands
17 from mercurial import hg, util, repair, merge, cmdutil, commands
18 from mercurial import extensions, ancestor, copies, patch
18 from mercurial import extensions, ancestor, copies, patch
19 from mercurial.commands import templateopts
19 from mercurial.commands import templateopts
20 from mercurial.node import nullrev
20 from mercurial.node import nullrev
21 from mercurial.lock import release
21 from mercurial.lock import release
22 from mercurial.i18n import _
22 from mercurial.i18n import _
23 import os, errno
23 import os, errno
24
24
25 nullmerge = -2
25 nullmerge = -2
26
26
27 def rebase(ui, repo, **opts):
27 def rebase(ui, repo, **opts):
28 """move changeset (and descendants) to a different branch
28 """move changeset (and descendants) to a different branch
29
29
30 Rebase uses repeated merging to graft changesets from one part of
30 Rebase uses repeated merging to graft changesets from one part of
31 history (the source) onto another (the destination). This can be
31 history (the source) onto another (the destination). This can be
32 useful for linearizing *local* changes relative to a master
32 useful for linearizing *local* changes relative to a master
33 development tree.
33 development tree.
34
34
35 You should not rebase changesets that have already been shared
35 You should not rebase changesets that have already been shared
36 with others. Doing so will force everybody else to perform the
36 with others. Doing so will force everybody else to perform the
37 same rebase or they will end up with duplicated changesets after
37 same rebase or they will end up with duplicated changesets after
38 pulling in your rebased changesets.
38 pulling in your rebased changesets.
39
39
40 If you don't specify a destination changeset (``-d/--dest``),
40 If you don't specify a destination changeset (``-d/--dest``),
41 rebase uses the tipmost head of the current named branch as the
41 rebase uses the tipmost head of the current named branch as the
42 destination. (The destination changeset is not modified by
42 destination. (The destination changeset is not modified by
43 rebasing, but new changesets are added as its descendants.)
43 rebasing, but new changesets are added as its descendants.)
44
44
45 You can specify which changesets to rebase in two ways: as a
45 You can specify which changesets to rebase in two ways: as a
46 "source" changeset or as a "base" changeset. Both are shorthand
46 "source" changeset or as a "base" changeset. Both are shorthand
47 for a topologically related set of changesets (the "source
47 for a topologically related set of changesets (the "source
48 branch"). If you specify source (``-s/--source``), rebase will
48 branch"). If you specify source (``-s/--source``), rebase will
49 rebase that changeset and all of its descendants onto dest. If you
49 rebase that changeset and all of its descendants onto dest. If you
50 specify base (``-b/--base``), rebase will select ancestors of base
50 specify base (``-b/--base``), rebase will select ancestors of base
51 back to but not including the common ancestor with dest. Thus,
51 back to but not including the common ancestor with dest. Thus,
52 ``-b`` is less precise but more convenient than ``-s``: you can
52 ``-b`` is less precise but more convenient than ``-s``: you can
53 specify any changeset in the source branch, and rebase will select
53 specify any changeset in the source branch, and rebase will select
54 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
54 the whole branch. If you specify neither ``-s`` nor ``-b``, rebase
55 uses the parent of the working directory as the base.
55 uses the parent of the working directory as the base.
56
56
57 By default, rebase recreates the changesets in the source branch
57 By default, rebase recreates the changesets in the source branch
58 as descendants of dest and then destroys the originals. Use
58 as descendants of dest and then destroys the originals. Use
59 ``--keep`` to preserve the original source changesets. Some
59 ``--keep`` to preserve the original source changesets. Some
60 changesets in the source branch (e.g. merges from the destination
60 changesets in the source branch (e.g. merges from the destination
61 branch) may be dropped if they no longer contribute any change.
61 branch) may be dropped if they no longer contribute any change.
62
62
63 One result of the rules for selecting the destination changeset
63 One result of the rules for selecting the destination changeset
64 and source branch is that, unlike ``merge``, rebase will do
64 and source branch is that, unlike ``merge``, rebase will do
65 nothing if you are at the latest (tipmost) head of a named branch
65 nothing if you are at the latest (tipmost) head of a named branch
66 with two heads. You need to explicitly specify source and/or
66 with two heads. You need to explicitly specify source and/or
67 destination (or ``update`` to the other head, if it's the head of
67 destination (or ``update`` to the other head, if it's the head of
68 the intended source branch).
68 the intended source branch).
69
69
70 If a rebase is interrupted to manually resolve a merge, it can be
70 If a rebase is interrupted to manually resolve a merge, it can be
71 continued with --continue/-c or aborted with --abort/-a.
71 continued with --continue/-c or aborted with --abort/-a.
72
72
73 Returns 0 on success, 1 if nothing to rebase.
73 Returns 0 on success, 1 if nothing to rebase.
74 """
74 """
75 originalwd = target = None
75 originalwd = target = None
76 external = nullrev
76 external = nullrev
77 state = {}
77 state = {}
78 skipped = set()
78 skipped = set()
79 targetancestors = set()
79 targetancestors = set()
80
80
81 lock = wlock = None
81 lock = wlock = None
82 try:
82 try:
83 lock = repo.lock()
83 lock = repo.lock()
84 wlock = repo.wlock()
84 wlock = repo.wlock()
85
85
86 # Validate input and define rebasing points
86 # Validate input and define rebasing points
87 destf = opts.get('dest', None)
87 destf = opts.get('dest', None)
88 srcf = opts.get('source', None)
88 srcf = opts.get('source', None)
89 basef = opts.get('base', None)
89 basef = opts.get('base', None)
90 contf = opts.get('continue')
90 contf = opts.get('continue')
91 abortf = opts.get('abort')
91 abortf = opts.get('abort')
92 collapsef = opts.get('collapse', False)
92 collapsef = opts.get('collapse', False)
93 extrafn = opts.get('extrafn')
93 extrafn = opts.get('extrafn')
94 keepf = opts.get('keep', False)
94 keepf = opts.get('keep', False)
95 keepbranchesf = opts.get('keepbranches', False)
95 keepbranchesf = opts.get('keepbranches', False)
96 detachf = opts.get('detach', False)
96 detachf = opts.get('detach', False)
97 # keepopen is not meant for use on the command line, but by
97 # keepopen is not meant for use on the command line, but by
98 # other extensions
98 # other extensions
99 keepopen = opts.get('keepopen', False)
99 keepopen = opts.get('keepopen', False)
100
100
101 if contf or abortf:
101 if contf or abortf:
102 if contf and abortf:
102 if contf and abortf:
103 raise util.Abort(_('cannot use both abort and continue'))
103 raise util.Abort(_('cannot use both abort and continue'))
104 if collapsef:
104 if collapsef:
105 raise util.Abort(
105 raise util.Abort(
106 _('cannot use collapse with continue or abort'))
106 _('cannot use collapse with continue or abort'))
107 if detachf:
107 if detachf:
108 raise util.Abort(_('cannot use detach with continue or abort'))
108 raise util.Abort(_('cannot use detach with continue or abort'))
109 if srcf or basef or destf:
109 if srcf or basef or destf:
110 raise util.Abort(
110 raise util.Abort(
111 _('abort and continue do not allow specifying revisions'))
111 _('abort and continue do not allow specifying revisions'))
112
112
113 (originalwd, target, state, skipped, collapsef, keepf,
113 (originalwd, target, state, skipped, collapsef, keepf,
114 keepbranchesf, external) = restorestatus(repo)
114 keepbranchesf, external) = restorestatus(repo)
115 if abortf:
115 if abortf:
116 return abort(repo, originalwd, target, state)
116 return abort(repo, originalwd, target, state)
117 else:
117 else:
118 if srcf and basef:
118 if srcf and basef:
119 raise util.Abort(_('cannot specify both a '
119 raise util.Abort(_('cannot specify both a '
120 'revision and a base'))
120 'revision and a base'))
121 if detachf:
121 if detachf:
122 if not srcf:
122 if not srcf:
123 raise util.Abort(
123 raise util.Abort(
124 _('detach requires a revision to be specified'))
124 _('detach requires a revision to be specified'))
125 if basef:
125 if basef:
126 raise util.Abort(_('cannot specify a base with detach'))
126 raise util.Abort(_('cannot specify a base with detach'))
127
127
128 cmdutil.bail_if_changed(repo)
128 cmdutil.bail_if_changed(repo)
129 result = buildstate(repo, destf, srcf, basef, detachf)
129 result = buildstate(repo, destf, srcf, basef, detachf)
130 if not result:
130 if not result:
131 # Empty state built, nothing to rebase
131 # Empty state built, nothing to rebase
132 ui.status(_('nothing to rebase\n'))
132 ui.status(_('nothing to rebase\n'))
133 return 1
133 return 1
134 else:
134 else:
135 originalwd, target, state = result
135 originalwd, target, state = result
136 if collapsef:
136 if collapsef:
137 targetancestors = set(repo.changelog.ancestors(target))
137 targetancestors = set(repo.changelog.ancestors(target))
138 external = checkexternal(repo, state, targetancestors)
138 external = checkexternal(repo, state, targetancestors)
139
139
140 if keepbranchesf:
140 if keepbranchesf:
141 if extrafn:
141 if extrafn:
142 raise util.Abort(_('cannot use both keepbranches and extrafn'))
142 raise util.Abort(_('cannot use both keepbranches and extrafn'))
143 def extrafn(ctx, extra):
143 def extrafn(ctx, extra):
144 extra['branch'] = ctx.branch()
144 extra['branch'] = ctx.branch()
145
145
146 # Rebase
146 # Rebase
147 if not targetancestors:
147 if not targetancestors:
148 targetancestors = set(repo.changelog.ancestors(target))
148 targetancestors = set(repo.changelog.ancestors(target))
149 targetancestors.add(target)
149 targetancestors.add(target)
150
150
151 sortedstate = sorted(state)
151 sortedstate = sorted(state)
152 total = len(sortedstate)
152 total = len(sortedstate)
153 pos = 0
153 pos = 0
154 for rev in sortedstate:
154 for rev in sortedstate:
155 pos += 1
155 pos += 1
156 if state[rev] == -1:
156 if state[rev] == -1:
157 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
157 ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, repo[rev])),
158 _('changesets'), total)
158 _('changesets'), total)
159 storestatus(repo, originalwd, target, state, collapsef, keepf,
159 storestatus(repo, originalwd, target, state, collapsef, keepf,
160 keepbranchesf, external)
160 keepbranchesf, external)
161 p1, p2 = defineparents(repo, rev, target, state,
161 p1, p2 = defineparents(repo, rev, target, state,
162 targetancestors)
162 targetancestors)
163 if len(repo.parents()) == 2:
163 if len(repo.parents()) == 2:
164 repo.ui.debug('resuming interrupted rebase\n')
164 repo.ui.debug('resuming interrupted rebase\n')
165 else:
165 else:
166 stats = rebasenode(repo, rev, p1, p2, state)
166 stats = rebasenode(repo, rev, p1, p2, state)
167 if stats and stats[3] > 0:
167 if stats and stats[3] > 0:
168 raise util.Abort(_('unresolved conflicts (see hg '
168 raise util.Abort(_('unresolved conflicts (see hg '
169 'resolve, then hg rebase --continue)'))
169 'resolve, then hg rebase --continue)'))
170 updatedirstate(repo, rev, target, p2)
170 updatedirstate(repo, rev, target, p2)
171 if not collapsef:
171 if not collapsef:
172 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn)
172 newrev = concludenode(repo, rev, p1, p2, extrafn=extrafn)
173 else:
173 else:
174 # Skip commit if we are collapsing
174 # Skip commit if we are collapsing
175 repo.dirstate.setparents(repo[p1].node())
175 repo.dirstate.setparents(repo[p1].node())
176 newrev = None
176 newrev = None
177 # Update the state
177 # Update the state
178 if newrev is not None:
178 if newrev is not None:
179 state[rev] = repo[newrev].rev()
179 state[rev] = repo[newrev].rev()
180 else:
180 else:
181 if not collapsef:
181 if not collapsef:
182 ui.note(_('no changes, revision %d skipped\n') % rev)
182 ui.note(_('no changes, revision %d skipped\n') % rev)
183 ui.debug('next revision set to %s\n' % p1)
183 ui.debug('next revision set to %s\n' % p1)
184 skipped.add(rev)
184 skipped.add(rev)
185 state[rev] = p1
185 state[rev] = p1
186
186
187 ui.progress(_('rebasing'), None)
187 ui.progress(_('rebasing'), None)
188 ui.note(_('rebase merging completed\n'))
188 ui.note(_('rebase merging completed\n'))
189
189
190 if collapsef and not keepopen:
190 if collapsef and not keepopen:
191 p1, p2 = defineparents(repo, min(state), target,
191 p1, p2 = defineparents(repo, min(state), target,
192 state, targetancestors)
192 state, targetancestors)
193 commitmsg = 'Collapsed revision'
193 commitmsg = 'Collapsed revision'
194 for rebased in state:
194 for rebased in state:
195 if rebased not in skipped and state[rebased] != nullmerge:
195 if rebased not in skipped and state[rebased] != nullmerge:
196 commitmsg += '\n* %s' % repo[rebased].description()
196 commitmsg += '\n* %s' % repo[rebased].description()
197 commitmsg = ui.edit(commitmsg, repo.ui.username())
197 commitmsg = ui.edit(commitmsg, repo.ui.username())
198 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
198 newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
199 extrafn=extrafn)
199 extrafn=extrafn)
200
200
201 if 'qtip' in repo.tags():
201 if 'qtip' in repo.tags():
202 updatemq(repo, state, skipped, **opts)
202 updatemq(repo, state, skipped, **opts)
203
203
204 if not keepf:
204 if not keepf:
205 # Remove no more useful revisions
205 # Remove no more useful revisions
206 rebased = [rev for rev in state if state[rev] != nullmerge]
206 rebased = [rev for rev in state if state[rev] != nullmerge]
207 if rebased:
207 if rebased:
208 if set(repo.changelog.descendants(min(rebased))) - set(state):
208 if set(repo.changelog.descendants(min(rebased))) - set(state):
209 ui.warn(_("warning: new changesets detected "
209 ui.warn(_("warning: new changesets detected "
210 "on source branch, not stripping\n"))
210 "on source branch, not stripping\n"))
211 else:
211 else:
212 # backup the old csets by default
212 # backup the old csets by default
213 repair.strip(ui, repo, repo[min(rebased)].node(), "all")
213 repair.strip(ui, repo, repo[min(rebased)].node(), "all")
214
214
215 clearstatus(repo)
215 clearstatus(repo)
216 ui.note(_("rebase completed\n"))
216 ui.note(_("rebase completed\n"))
217 if os.path.exists(repo.sjoin('undo')):
217 if os.path.exists(repo.sjoin('undo')):
218 util.unlink(repo.sjoin('undo'))
218 util.unlinkpath(repo.sjoin('undo'))
219 if skipped:
219 if skipped:
220 ui.note(_("%d revisions have been skipped\n") % len(skipped))
220 ui.note(_("%d revisions have been skipped\n") % len(skipped))
221 finally:
221 finally:
222 release(lock, wlock)
222 release(lock, wlock)
223
223
224 def rebasemerge(repo, rev, first=False):
224 def rebasemerge(repo, rev, first=False):
225 'return the correct ancestor'
225 'return the correct ancestor'
226 oldancestor = ancestor.ancestor
226 oldancestor = ancestor.ancestor
227
227
228 def newancestor(a, b, pfunc):
228 def newancestor(a, b, pfunc):
229 if b == rev:
229 if b == rev:
230 return repo[rev].parents()[0].rev()
230 return repo[rev].parents()[0].rev()
231 return oldancestor(a, b, pfunc)
231 return oldancestor(a, b, pfunc)
232
232
233 if not first:
233 if not first:
234 ancestor.ancestor = newancestor
234 ancestor.ancestor = newancestor
235 else:
235 else:
236 repo.ui.debug("first revision, do not change ancestor\n")
236 repo.ui.debug("first revision, do not change ancestor\n")
237 try:
237 try:
238 stats = merge.update(repo, rev, True, True, False)
238 stats = merge.update(repo, rev, True, True, False)
239 return stats
239 return stats
240 finally:
240 finally:
241 ancestor.ancestor = oldancestor
241 ancestor.ancestor = oldancestor
242
242
243 def checkexternal(repo, state, targetancestors):
243 def checkexternal(repo, state, targetancestors):
244 """Check whether one or more external revisions need to be taken in
244 """Check whether one or more external revisions need to be taken in
245 consideration. In the latter case, abort.
245 consideration. In the latter case, abort.
246 """
246 """
247 external = nullrev
247 external = nullrev
248 source = min(state)
248 source = min(state)
249 for rev in state:
249 for rev in state:
250 if rev == source:
250 if rev == source:
251 continue
251 continue
252 # Check externals and fail if there are more than one
252 # Check externals and fail if there are more than one
253 for p in repo[rev].parents():
253 for p in repo[rev].parents():
254 if (p.rev() not in state
254 if (p.rev() not in state
255 and p.rev() not in targetancestors):
255 and p.rev() not in targetancestors):
256 if external != nullrev:
256 if external != nullrev:
257 raise util.Abort(_('unable to collapse, there is more '
257 raise util.Abort(_('unable to collapse, there is more '
258 'than one external parent'))
258 'than one external parent'))
259 external = p.rev()
259 external = p.rev()
260 return external
260 return external
261
261
262 def updatedirstate(repo, rev, p1, p2):
262 def updatedirstate(repo, rev, p1, p2):
263 """Keep track of renamed files in the revision that is going to be rebased
263 """Keep track of renamed files in the revision that is going to be rebased
264 """
264 """
265 # Here we simulate the copies and renames in the source changeset
265 # Here we simulate the copies and renames in the source changeset
266 cop, diver = copies.copies(repo, repo[rev], repo[p1], repo[p2], True)
266 cop, diver = copies.copies(repo, repo[rev], repo[p1], repo[p2], True)
267 m1 = repo[rev].manifest()
267 m1 = repo[rev].manifest()
268 m2 = repo[p1].manifest()
268 m2 = repo[p1].manifest()
269 for k, v in cop.iteritems():
269 for k, v in cop.iteritems():
270 if k in m1:
270 if k in m1:
271 if v in m1 or v in m2:
271 if v in m1 or v in m2:
272 repo.dirstate.copy(v, k)
272 repo.dirstate.copy(v, k)
273 if v in m2 and v not in m1:
273 if v in m2 and v not in m1:
274 repo.dirstate.remove(v)
274 repo.dirstate.remove(v)
275
275
276 def concludenode(repo, rev, p1, p2, commitmsg=None, extrafn=None):
276 def concludenode(repo, rev, p1, p2, commitmsg=None, extrafn=None):
277 'Commit the changes and store useful information in extra'
277 'Commit the changes and store useful information in extra'
278 try:
278 try:
279 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
279 repo.dirstate.setparents(repo[p1].node(), repo[p2].node())
280 ctx = repo[rev]
280 ctx = repo[rev]
281 if commitmsg is None:
281 if commitmsg is None:
282 commitmsg = ctx.description()
282 commitmsg = ctx.description()
283 extra = {'rebase_source': ctx.hex()}
283 extra = {'rebase_source': ctx.hex()}
284 if extrafn:
284 if extrafn:
285 extrafn(ctx, extra)
285 extrafn(ctx, extra)
286 # Commit might fail if unresolved files exist
286 # Commit might fail if unresolved files exist
287 newrev = repo.commit(text=commitmsg, user=ctx.user(),
287 newrev = repo.commit(text=commitmsg, user=ctx.user(),
288 date=ctx.date(), extra=extra)
288 date=ctx.date(), extra=extra)
289 repo.dirstate.setbranch(repo[newrev].branch())
289 repo.dirstate.setbranch(repo[newrev].branch())
290 return newrev
290 return newrev
291 except util.Abort:
291 except util.Abort:
292 # Invalidate the previous setparents
292 # Invalidate the previous setparents
293 repo.dirstate.invalidate()
293 repo.dirstate.invalidate()
294 raise
294 raise
295
295
296 def rebasenode(repo, rev, p1, p2, state):
296 def rebasenode(repo, rev, p1, p2, state):
297 'Rebase a single revision'
297 'Rebase a single revision'
298 # Merge phase
298 # Merge phase
299 # Update to target and merge it with local
299 # Update to target and merge it with local
300 if repo['.'].rev() != repo[p1].rev():
300 if repo['.'].rev() != repo[p1].rev():
301 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
301 repo.ui.debug(" update to %d:%s\n" % (repo[p1].rev(), repo[p1]))
302 merge.update(repo, p1, False, True, False)
302 merge.update(repo, p1, False, True, False)
303 else:
303 else:
304 repo.ui.debug(" already in target\n")
304 repo.ui.debug(" already in target\n")
305 repo.dirstate.write()
305 repo.dirstate.write()
306 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
306 repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev]))
307 first = repo[rev].rev() == repo[min(state)].rev()
307 first = repo[rev].rev() == repo[min(state)].rev()
308 stats = rebasemerge(repo, rev, first)
308 stats = rebasemerge(repo, rev, first)
309 return stats
309 return stats
310
310
311 def defineparents(repo, rev, target, state, targetancestors):
311 def defineparents(repo, rev, target, state, targetancestors):
312 'Return the new parent relationship of the revision that will be rebased'
312 'Return the new parent relationship of the revision that will be rebased'
313 parents = repo[rev].parents()
313 parents = repo[rev].parents()
314 p1 = p2 = nullrev
314 p1 = p2 = nullrev
315
315
316 P1n = parents[0].rev()
316 P1n = parents[0].rev()
317 if P1n in targetancestors:
317 if P1n in targetancestors:
318 p1 = target
318 p1 = target
319 elif P1n in state:
319 elif P1n in state:
320 if state[P1n] == nullmerge:
320 if state[P1n] == nullmerge:
321 p1 = target
321 p1 = target
322 else:
322 else:
323 p1 = state[P1n]
323 p1 = state[P1n]
324 else: # P1n external
324 else: # P1n external
325 p1 = target
325 p1 = target
326 p2 = P1n
326 p2 = P1n
327
327
328 if len(parents) == 2 and parents[1].rev() not in targetancestors:
328 if len(parents) == 2 and parents[1].rev() not in targetancestors:
329 P2n = parents[1].rev()
329 P2n = parents[1].rev()
330 # interesting second parent
330 # interesting second parent
331 if P2n in state:
331 if P2n in state:
332 if p1 == target: # P1n in targetancestors or external
332 if p1 == target: # P1n in targetancestors or external
333 p1 = state[P2n]
333 p1 = state[P2n]
334 else:
334 else:
335 p2 = state[P2n]
335 p2 = state[P2n]
336 else: # P2n external
336 else: # P2n external
337 if p2 != nullrev: # P1n external too => rev is a merged revision
337 if p2 != nullrev: # P1n external too => rev is a merged revision
338 raise util.Abort(_('cannot use revision %d as base, result '
338 raise util.Abort(_('cannot use revision %d as base, result '
339 'would have 3 parents') % rev)
339 'would have 3 parents') % rev)
340 p2 = P2n
340 p2 = P2n
341 repo.ui.debug(" future parents are %d and %d\n" %
341 repo.ui.debug(" future parents are %d and %d\n" %
342 (repo[p1].rev(), repo[p2].rev()))
342 (repo[p1].rev(), repo[p2].rev()))
343 return p1, p2
343 return p1, p2
344
344
345 def isagitpatch(repo, patchname):
345 def isagitpatch(repo, patchname):
346 'Return true if the given patch is in git format'
346 'Return true if the given patch is in git format'
347 mqpatch = os.path.join(repo.mq.path, patchname)
347 mqpatch = os.path.join(repo.mq.path, patchname)
348 for line in patch.linereader(file(mqpatch, 'rb')):
348 for line in patch.linereader(file(mqpatch, 'rb')):
349 if line.startswith('diff --git'):
349 if line.startswith('diff --git'):
350 return True
350 return True
351 return False
351 return False
352
352
353 def updatemq(repo, state, skipped, **opts):
353 def updatemq(repo, state, skipped, **opts):
354 'Update rebased mq patches - finalize and then import them'
354 'Update rebased mq patches - finalize and then import them'
355 mqrebase = {}
355 mqrebase = {}
356 mq = repo.mq
356 mq = repo.mq
357 for p in mq.applied:
357 for p in mq.applied:
358 rev = repo[p.node].rev()
358 rev = repo[p.node].rev()
359 if rev in state:
359 if rev in state:
360 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
360 repo.ui.debug('revision %d is an mq patch (%s), finalize it.\n' %
361 (rev, p.name))
361 (rev, p.name))
362 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
362 mqrebase[rev] = (p.name, isagitpatch(repo, p.name))
363
363
364 if mqrebase:
364 if mqrebase:
365 mq.finish(repo, mqrebase.keys())
365 mq.finish(repo, mqrebase.keys())
366
366
367 # We must start import from the newest revision
367 # We must start import from the newest revision
368 for rev in sorted(mqrebase, reverse=True):
368 for rev in sorted(mqrebase, reverse=True):
369 if rev not in skipped:
369 if rev not in skipped:
370 name, isgit = mqrebase[rev]
370 name, isgit = mqrebase[rev]
371 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
371 repo.ui.debug('import mq patch %d (%s)\n' % (state[rev], name))
372 mq.qimport(repo, (), patchname=name, git=isgit,
372 mq.qimport(repo, (), patchname=name, git=isgit,
373 rev=[str(state[rev])])
373 rev=[str(state[rev])])
374 mq.save_dirty()
374 mq.save_dirty()
375
375
376 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
376 def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,
377 external):
377 external):
378 'Store the current status to allow recovery'
378 'Store the current status to allow recovery'
379 f = repo.opener("rebasestate", "w")
379 f = repo.opener("rebasestate", "w")
380 f.write(repo[originalwd].hex() + '\n')
380 f.write(repo[originalwd].hex() + '\n')
381 f.write(repo[target].hex() + '\n')
381 f.write(repo[target].hex() + '\n')
382 f.write(repo[external].hex() + '\n')
382 f.write(repo[external].hex() + '\n')
383 f.write('%d\n' % int(collapse))
383 f.write('%d\n' % int(collapse))
384 f.write('%d\n' % int(keep))
384 f.write('%d\n' % int(keep))
385 f.write('%d\n' % int(keepbranches))
385 f.write('%d\n' % int(keepbranches))
386 for d, v in state.iteritems():
386 for d, v in state.iteritems():
387 oldrev = repo[d].hex()
387 oldrev = repo[d].hex()
388 newrev = repo[v].hex()
388 newrev = repo[v].hex()
389 f.write("%s:%s\n" % (oldrev, newrev))
389 f.write("%s:%s\n" % (oldrev, newrev))
390 f.close()
390 f.close()
391 repo.ui.debug('rebase status stored\n')
391 repo.ui.debug('rebase status stored\n')
392
392
393 def clearstatus(repo):
393 def clearstatus(repo):
394 'Remove the status files'
394 'Remove the status files'
395 if os.path.exists(repo.join("rebasestate")):
395 if os.path.exists(repo.join("rebasestate")):
396 util.unlink(repo.join("rebasestate"))
396 util.unlinkpath(repo.join("rebasestate"))
397
397
398 def restorestatus(repo):
398 def restorestatus(repo):
399 'Restore a previously stored status'
399 'Restore a previously stored status'
400 try:
400 try:
401 target = None
401 target = None
402 collapse = False
402 collapse = False
403 external = nullrev
403 external = nullrev
404 state = {}
404 state = {}
405 f = repo.opener("rebasestate")
405 f = repo.opener("rebasestate")
406 for i, l in enumerate(f.read().splitlines()):
406 for i, l in enumerate(f.read().splitlines()):
407 if i == 0:
407 if i == 0:
408 originalwd = repo[l].rev()
408 originalwd = repo[l].rev()
409 elif i == 1:
409 elif i == 1:
410 target = repo[l].rev()
410 target = repo[l].rev()
411 elif i == 2:
411 elif i == 2:
412 external = repo[l].rev()
412 external = repo[l].rev()
413 elif i == 3:
413 elif i == 3:
414 collapse = bool(int(l))
414 collapse = bool(int(l))
415 elif i == 4:
415 elif i == 4:
416 keep = bool(int(l))
416 keep = bool(int(l))
417 elif i == 5:
417 elif i == 5:
418 keepbranches = bool(int(l))
418 keepbranches = bool(int(l))
419 else:
419 else:
420 oldrev, newrev = l.split(':')
420 oldrev, newrev = l.split(':')
421 state[repo[oldrev].rev()] = repo[newrev].rev()
421 state[repo[oldrev].rev()] = repo[newrev].rev()
422 skipped = set()
422 skipped = set()
423 # recompute the set of skipped revs
423 # recompute the set of skipped revs
424 if not collapse:
424 if not collapse:
425 seen = set([target])
425 seen = set([target])
426 for old, new in sorted(state.items()):
426 for old, new in sorted(state.items()):
427 if new != nullrev and new in seen:
427 if new != nullrev and new in seen:
428 skipped.add(old)
428 skipped.add(old)
429 seen.add(new)
429 seen.add(new)
430 repo.ui.debug('computed skipped revs: %s\n' % skipped)
430 repo.ui.debug('computed skipped revs: %s\n' % skipped)
431 repo.ui.debug('rebase status resumed\n')
431 repo.ui.debug('rebase status resumed\n')
432 return (originalwd, target, state, skipped,
432 return (originalwd, target, state, skipped,
433 collapse, keep, keepbranches, external)
433 collapse, keep, keepbranches, external)
434 except IOError, err:
434 except IOError, err:
435 if err.errno != errno.ENOENT:
435 if err.errno != errno.ENOENT:
436 raise
436 raise
437 raise util.Abort(_('no rebase in progress'))
437 raise util.Abort(_('no rebase in progress'))
438
438
439 def abort(repo, originalwd, target, state):
439 def abort(repo, originalwd, target, state):
440 'Restore the repository to its original state'
440 'Restore the repository to its original state'
441 if set(repo.changelog.descendants(target)) - set(state.values()):
441 if set(repo.changelog.descendants(target)) - set(state.values()):
442 repo.ui.warn(_("warning: new changesets detected on target branch, "
442 repo.ui.warn(_("warning: new changesets detected on target branch, "
443 "can't abort\n"))
443 "can't abort\n"))
444 return -1
444 return -1
445 else:
445 else:
446 # Strip from the first rebased revision
446 # Strip from the first rebased revision
447 merge.update(repo, repo[originalwd].rev(), False, True, False)
447 merge.update(repo, repo[originalwd].rev(), False, True, False)
448 rebased = filter(lambda x: x > -1 and x != target, state.values())
448 rebased = filter(lambda x: x > -1 and x != target, state.values())
449 if rebased:
449 if rebased:
450 strippoint = min(rebased)
450 strippoint = min(rebased)
451 # no backup of rebased cset versions needed
451 # no backup of rebased cset versions needed
452 repair.strip(repo.ui, repo, repo[strippoint].node())
452 repair.strip(repo.ui, repo, repo[strippoint].node())
453 clearstatus(repo)
453 clearstatus(repo)
454 repo.ui.warn(_('rebase aborted\n'))
454 repo.ui.warn(_('rebase aborted\n'))
455 return 0
455 return 0
456
456
457 def buildstate(repo, dest, src, base, detach):
457 def buildstate(repo, dest, src, base, detach):
458 'Define which revisions are going to be rebased and where'
458 'Define which revisions are going to be rebased and where'
459 targetancestors = set()
459 targetancestors = set()
460 detachset = set()
460 detachset = set()
461
461
462 if not dest:
462 if not dest:
463 # Destination defaults to the latest revision in the current branch
463 # Destination defaults to the latest revision in the current branch
464 branch = repo[None].branch()
464 branch = repo[None].branch()
465 dest = repo[branch].rev()
465 dest = repo[branch].rev()
466 else:
466 else:
467 dest = repo[dest].rev()
467 dest = repo[dest].rev()
468
468
469 # This check isn't strictly necessary, since mq detects commits over an
469 # This check isn't strictly necessary, since mq detects commits over an
470 # applied patch. But it prevents messing up the working directory when
470 # applied patch. But it prevents messing up the working directory when
471 # a partially completed rebase is blocked by mq.
471 # a partially completed rebase is blocked by mq.
472 if 'qtip' in repo.tags() and (repo[dest].node() in
472 if 'qtip' in repo.tags() and (repo[dest].node() in
473 [s.node for s in repo.mq.applied]):
473 [s.node for s in repo.mq.applied]):
474 raise util.Abort(_('cannot rebase onto an applied mq patch'))
474 raise util.Abort(_('cannot rebase onto an applied mq patch'))
475
475
476 if src:
476 if src:
477 commonbase = repo[src].ancestor(repo[dest])
477 commonbase = repo[src].ancestor(repo[dest])
478 if commonbase == repo[src]:
478 if commonbase == repo[src]:
479 raise util.Abort(_('source is ancestor of destination'))
479 raise util.Abort(_('source is ancestor of destination'))
480 if commonbase == repo[dest]:
480 if commonbase == repo[dest]:
481 raise util.Abort(_('source is descendant of destination'))
481 raise util.Abort(_('source is descendant of destination'))
482 source = repo[src].rev()
482 source = repo[src].rev()
483 if detach:
483 if detach:
484 # We need to keep track of source's ancestors up to the common base
484 # We need to keep track of source's ancestors up to the common base
485 srcancestors = set(repo.changelog.ancestors(source))
485 srcancestors = set(repo.changelog.ancestors(source))
486 baseancestors = set(repo.changelog.ancestors(commonbase.rev()))
486 baseancestors = set(repo.changelog.ancestors(commonbase.rev()))
487 detachset = srcancestors - baseancestors
487 detachset = srcancestors - baseancestors
488 detachset.discard(commonbase.rev())
488 detachset.discard(commonbase.rev())
489 else:
489 else:
490 if base:
490 if base:
491 cwd = repo[base].rev()
491 cwd = repo[base].rev()
492 else:
492 else:
493 cwd = repo['.'].rev()
493 cwd = repo['.'].rev()
494
494
495 if cwd == dest:
495 if cwd == dest:
496 repo.ui.debug('source and destination are the same\n')
496 repo.ui.debug('source and destination are the same\n')
497 return None
497 return None
498
498
499 targetancestors = set(repo.changelog.ancestors(dest))
499 targetancestors = set(repo.changelog.ancestors(dest))
500 if cwd in targetancestors:
500 if cwd in targetancestors:
501 repo.ui.debug('source is ancestor of destination\n')
501 repo.ui.debug('source is ancestor of destination\n')
502 return None
502 return None
503
503
504 cwdancestors = set(repo.changelog.ancestors(cwd))
504 cwdancestors = set(repo.changelog.ancestors(cwd))
505 if dest in cwdancestors:
505 if dest in cwdancestors:
506 repo.ui.debug('source is descendant of destination\n')
506 repo.ui.debug('source is descendant of destination\n')
507 return None
507 return None
508
508
509 cwdancestors.add(cwd)
509 cwdancestors.add(cwd)
510 rebasingbranch = cwdancestors - targetancestors
510 rebasingbranch = cwdancestors - targetancestors
511 source = min(rebasingbranch)
511 source = min(rebasingbranch)
512
512
513 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, source))
513 repo.ui.debug('rebase onto %d starting from %d\n' % (dest, source))
514 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
514 state = dict.fromkeys(repo.changelog.descendants(source), nullrev)
515 state.update(dict.fromkeys(detachset, nullmerge))
515 state.update(dict.fromkeys(detachset, nullmerge))
516 state[source] = nullrev
516 state[source] = nullrev
517 return repo['.'].rev(), repo[dest].rev(), state
517 return repo['.'].rev(), repo[dest].rev(), state
518
518
519 def pullrebase(orig, ui, repo, *args, **opts):
519 def pullrebase(orig, ui, repo, *args, **opts):
520 'Call rebase after pull if the latter has been invoked with --rebase'
520 'Call rebase after pull if the latter has been invoked with --rebase'
521 if opts.get('rebase'):
521 if opts.get('rebase'):
522 if opts.get('update'):
522 if opts.get('update'):
523 del opts['update']
523 del opts['update']
524 ui.debug('--update and --rebase are not compatible, ignoring '
524 ui.debug('--update and --rebase are not compatible, ignoring '
525 'the update flag\n')
525 'the update flag\n')
526
526
527 cmdutil.bail_if_changed(repo)
527 cmdutil.bail_if_changed(repo)
528 revsprepull = len(repo)
528 revsprepull = len(repo)
529 origpostincoming = commands.postincoming
529 origpostincoming = commands.postincoming
530 def _dummy(*args, **kwargs):
530 def _dummy(*args, **kwargs):
531 pass
531 pass
532 commands.postincoming = _dummy
532 commands.postincoming = _dummy
533 try:
533 try:
534 orig(ui, repo, *args, **opts)
534 orig(ui, repo, *args, **opts)
535 finally:
535 finally:
536 commands.postincoming = origpostincoming
536 commands.postincoming = origpostincoming
537 revspostpull = len(repo)
537 revspostpull = len(repo)
538 if revspostpull > revsprepull:
538 if revspostpull > revsprepull:
539 rebase(ui, repo, **opts)
539 rebase(ui, repo, **opts)
540 branch = repo[None].branch()
540 branch = repo[None].branch()
541 dest = repo[branch].rev()
541 dest = repo[branch].rev()
542 if dest != repo['.'].rev():
542 if dest != repo['.'].rev():
543 # there was nothing to rebase we force an update
543 # there was nothing to rebase we force an update
544 hg.update(repo, dest)
544 hg.update(repo, dest)
545 else:
545 else:
546 orig(ui, repo, *args, **opts)
546 orig(ui, repo, *args, **opts)
547
547
548 def uisetup(ui):
548 def uisetup(ui):
549 'Replace pull with a decorator to provide --rebase option'
549 'Replace pull with a decorator to provide --rebase option'
550 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
550 entry = extensions.wrapcommand(commands.table, 'pull', pullrebase)
551 entry[1].append(('', 'rebase', None,
551 entry[1].append(('', 'rebase', None,
552 _("rebase working directory to branch head"))
552 _("rebase working directory to branch head"))
553 )
553 )
554
554
555 cmdtable = {
555 cmdtable = {
556 "rebase":
556 "rebase":
557 (rebase,
557 (rebase,
558 [
558 [
559 ('s', 'source', '',
559 ('s', 'source', '',
560 _('rebase from the specified changeset'), _('REV')),
560 _('rebase from the specified changeset'), _('REV')),
561 ('b', 'base', '',
561 ('b', 'base', '',
562 _('rebase from the base of the specified changeset '
562 _('rebase from the base of the specified changeset '
563 '(up to greatest common ancestor of base and dest)'),
563 '(up to greatest common ancestor of base and dest)'),
564 _('REV')),
564 _('REV')),
565 ('d', 'dest', '',
565 ('d', 'dest', '',
566 _('rebase onto the specified changeset'), _('REV')),
566 _('rebase onto the specified changeset'), _('REV')),
567 ('', 'collapse', False, _('collapse the rebased changesets')),
567 ('', 'collapse', False, _('collapse the rebased changesets')),
568 ('', 'keep', False, _('keep original changesets')),
568 ('', 'keep', False, _('keep original changesets')),
569 ('', 'keepbranches', False, _('keep original branch names')),
569 ('', 'keepbranches', False, _('keep original branch names')),
570 ('', 'detach', False, _('force detaching of source from its original '
570 ('', 'detach', False, _('force detaching of source from its original '
571 'branch')),
571 'branch')),
572 ('c', 'continue', False, _('continue an interrupted rebase')),
572 ('c', 'continue', False, _('continue an interrupted rebase')),
573 ('a', 'abort', False, _('abort an interrupted rebase'))] +
573 ('a', 'abort', False, _('abort an interrupted rebase'))] +
574 templateopts,
574 templateopts,
575 _('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
575 _('hg rebase [-s REV | -b REV] [-d REV] [options]\n'
576 'hg rebase {-a|-c}'))
576 'hg rebase {-a|-c}'))
577 }
577 }
@@ -1,4535 +1,4535 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, nullid, nullrev, short
8 from node import hex, 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, sys, difflib, time, tempfile
11 import os, re, sys, difflib, time, tempfile
12 import hg, util, revlog, extensions, copies, error
12 import hg, util, revlog, extensions, copies, error
13 import patch, help, mdiff, url, encoding, templatekw, discovery
13 import patch, help, mdiff, url, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 import merge as mergemod
15 import merge as mergemod
16 import minirst, revset
16 import minirst, revset
17 import dagparser
17 import dagparser
18
18
19 # Commands start here, listed alphabetically
19 # Commands start here, listed alphabetically
20
20
21 def add(ui, repo, *pats, **opts):
21 def add(ui, repo, *pats, **opts):
22 """add the specified files on the next commit
22 """add the specified files on the next commit
23
23
24 Schedule files to be version controlled and added to the
24 Schedule files to be version controlled and added to the
25 repository.
25 repository.
26
26
27 The files will be added to the repository at the next commit. To
27 The files will be added to the repository at the next commit. To
28 undo an add before that, see :hg:`forget`.
28 undo an add before that, see :hg:`forget`.
29
29
30 If no names are given, add all files to the repository.
30 If no names are given, add all files to the repository.
31
31
32 .. container:: verbose
32 .. container:: verbose
33
33
34 An example showing how new (unknown) files are added
34 An example showing how new (unknown) files are added
35 automatically by :hg:`add`::
35 automatically by :hg:`add`::
36
36
37 $ ls
37 $ ls
38 foo.c
38 foo.c
39 $ hg status
39 $ hg status
40 ? foo.c
40 ? foo.c
41 $ hg add
41 $ hg add
42 adding foo.c
42 adding foo.c
43 $ hg status
43 $ hg status
44 A foo.c
44 A foo.c
45
45
46 Returns 0 if all files are successfully added.
46 Returns 0 if all files are successfully added.
47 """
47 """
48
48
49 m = cmdutil.match(repo, pats, opts)
49 m = cmdutil.match(repo, pats, opts)
50 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
50 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
51 opts.get('subrepos'), prefix="")
51 opts.get('subrepos'), prefix="")
52 return rejected and 1 or 0
52 return rejected and 1 or 0
53
53
54 def addremove(ui, repo, *pats, **opts):
54 def addremove(ui, repo, *pats, **opts):
55 """add all new files, delete all missing files
55 """add all new files, delete all missing files
56
56
57 Add all new files and remove all missing files from the
57 Add all new files and remove all missing files from the
58 repository.
58 repository.
59
59
60 New files are ignored if they match any of the patterns in
60 New files are ignored if they match any of the patterns in
61 .hgignore. As with add, these changes take effect at the next
61 .hgignore. As with add, these changes take effect at the next
62 commit.
62 commit.
63
63
64 Use the -s/--similarity option to detect renamed files. With a
64 Use the -s/--similarity option to detect renamed files. With a
65 parameter greater than 0, this compares every removed file with
65 parameter greater than 0, this compares every removed file with
66 every added file and records those similar enough as renames. This
66 every added file and records those similar enough as renames. This
67 option takes a percentage between 0 (disabled) and 100 (files must
67 option takes a percentage between 0 (disabled) and 100 (files must
68 be identical) as its parameter. Detecting renamed files this way
68 be identical) as its parameter. Detecting renamed files this way
69 can be expensive. After using this option, :hg:`status -C` can be
69 can be expensive. After using this option, :hg:`status -C` can be
70 used to check which files were identified as moved or renamed.
70 used to check which files were identified as moved or renamed.
71
71
72 Returns 0 if all files are successfully added.
72 Returns 0 if all files are successfully added.
73 """
73 """
74 try:
74 try:
75 sim = float(opts.get('similarity') or 100)
75 sim = float(opts.get('similarity') or 100)
76 except ValueError:
76 except ValueError:
77 raise util.Abort(_('similarity must be a number'))
77 raise util.Abort(_('similarity must be a number'))
78 if sim < 0 or sim > 100:
78 if sim < 0 or sim > 100:
79 raise util.Abort(_('similarity must be between 0 and 100'))
79 raise util.Abort(_('similarity must be between 0 and 100'))
80 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
80 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
81
81
82 def annotate(ui, repo, *pats, **opts):
82 def annotate(ui, repo, *pats, **opts):
83 """show changeset information by line for each file
83 """show changeset information by line for each file
84
84
85 List changes in files, showing the revision id responsible for
85 List changes in files, showing the revision id responsible for
86 each line
86 each line
87
87
88 This command is useful for discovering when a change was made and
88 This command is useful for discovering when a change was made and
89 by whom.
89 by whom.
90
90
91 Without the -a/--text option, annotate will avoid processing files
91 Without the -a/--text option, annotate will avoid processing files
92 it detects as binary. With -a, annotate will annotate the file
92 it detects as binary. With -a, annotate will annotate the file
93 anyway, although the results will probably be neither useful
93 anyway, although the results will probably be neither useful
94 nor desirable.
94 nor desirable.
95
95
96 Returns 0 on success.
96 Returns 0 on success.
97 """
97 """
98 if opts.get('follow'):
98 if opts.get('follow'):
99 # --follow is deprecated and now just an alias for -f/--file
99 # --follow is deprecated and now just an alias for -f/--file
100 # to mimic the behavior of Mercurial before version 1.5
100 # to mimic the behavior of Mercurial before version 1.5
101 opts['file'] = 1
101 opts['file'] = 1
102
102
103 datefunc = ui.quiet and util.shortdate or util.datestr
103 datefunc = ui.quiet and util.shortdate or util.datestr
104 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
104 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
105
105
106 if not pats:
106 if not pats:
107 raise util.Abort(_('at least one filename or pattern is required'))
107 raise util.Abort(_('at least one filename or pattern is required'))
108
108
109 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
109 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
110 ('number', lambda x: str(x[0].rev())),
110 ('number', lambda x: str(x[0].rev())),
111 ('changeset', lambda x: short(x[0].node())),
111 ('changeset', lambda x: short(x[0].node())),
112 ('date', getdate),
112 ('date', getdate),
113 ('file', lambda x: x[0].path()),
113 ('file', lambda x: x[0].path()),
114 ]
114 ]
115
115
116 if (not opts.get('user') and not opts.get('changeset')
116 if (not opts.get('user') and not opts.get('changeset')
117 and not opts.get('date') and not opts.get('file')):
117 and not opts.get('date') and not opts.get('file')):
118 opts['number'] = 1
118 opts['number'] = 1
119
119
120 linenumber = opts.get('line_number') is not None
120 linenumber = opts.get('line_number') is not None
121 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
121 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
122 raise util.Abort(_('at least one of -n/-c is required for -l'))
122 raise util.Abort(_('at least one of -n/-c is required for -l'))
123
123
124 funcmap = [func for op, func in opmap if opts.get(op)]
124 funcmap = [func for op, func in opmap if opts.get(op)]
125 if linenumber:
125 if linenumber:
126 lastfunc = funcmap[-1]
126 lastfunc = funcmap[-1]
127 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
127 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
128
128
129 ctx = cmdutil.revsingle(repo, opts.get('rev'))
129 ctx = cmdutil.revsingle(repo, opts.get('rev'))
130 m = cmdutil.match(repo, pats, opts)
130 m = cmdutil.match(repo, pats, opts)
131 follow = not opts.get('no_follow')
131 follow = not opts.get('no_follow')
132 for abs in ctx.walk(m):
132 for abs in ctx.walk(m):
133 fctx = ctx[abs]
133 fctx = ctx[abs]
134 if not opts.get('text') and util.binary(fctx.data()):
134 if not opts.get('text') and util.binary(fctx.data()):
135 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
135 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
136 continue
136 continue
137
137
138 lines = fctx.annotate(follow=follow, linenumber=linenumber)
138 lines = fctx.annotate(follow=follow, linenumber=linenumber)
139 pieces = []
139 pieces = []
140
140
141 for f in funcmap:
141 for f in funcmap:
142 l = [f(n) for n, dummy in lines]
142 l = [f(n) for n, dummy in lines]
143 if l:
143 if l:
144 sized = [(x, encoding.colwidth(x)) for x in l]
144 sized = [(x, encoding.colwidth(x)) for x in l]
145 ml = max([w for x, w in sized])
145 ml = max([w for x, w in sized])
146 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
146 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
147
147
148 if pieces:
148 if pieces:
149 for p, l in zip(zip(*pieces), lines):
149 for p, l in zip(zip(*pieces), lines):
150 ui.write("%s: %s" % (" ".join(p), l[1]))
150 ui.write("%s: %s" % (" ".join(p), l[1]))
151
151
152 def archive(ui, repo, dest, **opts):
152 def archive(ui, repo, dest, **opts):
153 '''create an unversioned archive of a repository revision
153 '''create an unversioned archive of a repository revision
154
154
155 By default, the revision used is the parent of the working
155 By default, the revision used is the parent of the working
156 directory; use -r/--rev to specify a different revision.
156 directory; use -r/--rev to specify a different revision.
157
157
158 The archive type is automatically detected based on file
158 The archive type is automatically detected based on file
159 extension (or override using -t/--type).
159 extension (or override using -t/--type).
160
160
161 Valid types are:
161 Valid types are:
162
162
163 :``files``: a directory full of files (default)
163 :``files``: a directory full of files (default)
164 :``tar``: tar archive, uncompressed
164 :``tar``: tar archive, uncompressed
165 :``tbz2``: tar archive, compressed using bzip2
165 :``tbz2``: tar archive, compressed using bzip2
166 :``tgz``: tar archive, compressed using gzip
166 :``tgz``: tar archive, compressed using gzip
167 :``uzip``: zip archive, uncompressed
167 :``uzip``: zip archive, uncompressed
168 :``zip``: zip archive, compressed using deflate
168 :``zip``: zip archive, compressed using deflate
169
169
170 The exact name of the destination archive or directory is given
170 The exact name of the destination archive or directory is given
171 using a format string; see :hg:`help export` for details.
171 using a format string; see :hg:`help export` for details.
172
172
173 Each member added to an archive file has a directory prefix
173 Each member added to an archive file has a directory prefix
174 prepended. Use -p/--prefix to specify a format string for the
174 prepended. Use -p/--prefix to specify a format string for the
175 prefix. The default is the basename of the archive, with suffixes
175 prefix. The default is the basename of the archive, with suffixes
176 removed.
176 removed.
177
177
178 Returns 0 on success.
178 Returns 0 on success.
179 '''
179 '''
180
180
181 ctx = cmdutil.revsingle(repo, opts.get('rev'))
181 ctx = cmdutil.revsingle(repo, opts.get('rev'))
182 if not ctx:
182 if not ctx:
183 raise util.Abort(_('no working directory: please specify a revision'))
183 raise util.Abort(_('no working directory: please specify a revision'))
184 node = ctx.node()
184 node = ctx.node()
185 dest = cmdutil.make_filename(repo, dest, node)
185 dest = cmdutil.make_filename(repo, dest, node)
186 if os.path.realpath(dest) == repo.root:
186 if os.path.realpath(dest) == repo.root:
187 raise util.Abort(_('repository root cannot be destination'))
187 raise util.Abort(_('repository root cannot be destination'))
188
188
189 kind = opts.get('type') or archival.guesskind(dest) or 'files'
189 kind = opts.get('type') or archival.guesskind(dest) or 'files'
190 prefix = opts.get('prefix')
190 prefix = opts.get('prefix')
191
191
192 if dest == '-':
192 if dest == '-':
193 if kind == 'files':
193 if kind == 'files':
194 raise util.Abort(_('cannot archive plain files to stdout'))
194 raise util.Abort(_('cannot archive plain files to stdout'))
195 dest = sys.stdout
195 dest = sys.stdout
196 if not prefix:
196 if not prefix:
197 prefix = os.path.basename(repo.root) + '-%h'
197 prefix = os.path.basename(repo.root) + '-%h'
198
198
199 prefix = cmdutil.make_filename(repo, prefix, node)
199 prefix = cmdutil.make_filename(repo, prefix, node)
200 matchfn = cmdutil.match(repo, [], opts)
200 matchfn = cmdutil.match(repo, [], opts)
201 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
201 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
202 matchfn, prefix, subrepos=opts.get('subrepos'))
202 matchfn, prefix, subrepos=opts.get('subrepos'))
203
203
204 def backout(ui, repo, node=None, rev=None, **opts):
204 def backout(ui, repo, node=None, rev=None, **opts):
205 '''reverse effect of earlier changeset
205 '''reverse effect of earlier changeset
206
206
207 The backout command merges the reverse effect of the reverted
207 The backout command merges the reverse effect of the reverted
208 changeset into the working directory.
208 changeset into the working directory.
209
209
210 With the --merge option, it first commits the reverted changes
210 With the --merge option, it first commits the reverted changes
211 as a new changeset. This new changeset is a child of the reverted
211 as a new changeset. This new changeset is a child of the reverted
212 changeset.
212 changeset.
213 The --merge option remembers the parent of the working directory
213 The --merge option remembers the parent of the working directory
214 before starting the backout, then merges the new head with that
214 before starting the backout, then merges the new head with that
215 changeset afterwards.
215 changeset afterwards.
216 This will result in an explicit merge in the history.
216 This will result in an explicit merge in the history.
217
217
218 If you backout a changeset other than the original parent of the
218 If you backout a changeset other than the original parent of the
219 working directory, the result of this merge is not committed,
219 working directory, the result of this merge is not committed,
220 as with a normal merge. Otherwise, no merge is needed and the
220 as with a normal merge. Otherwise, no merge is needed and the
221 commit is automatic.
221 commit is automatic.
222
222
223 Note that the default behavior (without --merge) has changed in
223 Note that the default behavior (without --merge) has changed in
224 version 1.7. To restore the previous default behavior, use
224 version 1.7. To restore the previous default behavior, use
225 :hg:`backout --merge` and then :hg:`update --clean .` to get rid of
225 :hg:`backout --merge` and then :hg:`update --clean .` to get rid of
226 the ongoing merge.
226 the ongoing merge.
227
227
228 See :hg:`help dates` for a list of formats valid for -d/--date.
228 See :hg:`help dates` for a list of formats valid for -d/--date.
229
229
230 Returns 0 on success.
230 Returns 0 on success.
231 '''
231 '''
232 if rev and node:
232 if rev and node:
233 raise util.Abort(_("please specify just one revision"))
233 raise util.Abort(_("please specify just one revision"))
234
234
235 if not rev:
235 if not rev:
236 rev = node
236 rev = node
237
237
238 if not rev:
238 if not rev:
239 raise util.Abort(_("please specify a revision to backout"))
239 raise util.Abort(_("please specify a revision to backout"))
240
240
241 date = opts.get('date')
241 date = opts.get('date')
242 if date:
242 if date:
243 opts['date'] = util.parsedate(date)
243 opts['date'] = util.parsedate(date)
244
244
245 cmdutil.bail_if_changed(repo)
245 cmdutil.bail_if_changed(repo)
246 node = cmdutil.revsingle(repo, rev).node()
246 node = cmdutil.revsingle(repo, rev).node()
247
247
248 op1, op2 = repo.dirstate.parents()
248 op1, op2 = repo.dirstate.parents()
249 a = repo.changelog.ancestor(op1, node)
249 a = repo.changelog.ancestor(op1, node)
250 if a != node:
250 if a != node:
251 raise util.Abort(_('cannot backout change on a different branch'))
251 raise util.Abort(_('cannot backout change on a different branch'))
252
252
253 p1, p2 = repo.changelog.parents(node)
253 p1, p2 = repo.changelog.parents(node)
254 if p1 == nullid:
254 if p1 == nullid:
255 raise util.Abort(_('cannot backout a change with no parents'))
255 raise util.Abort(_('cannot backout a change with no parents'))
256 if p2 != nullid:
256 if p2 != nullid:
257 if not opts.get('parent'):
257 if not opts.get('parent'):
258 raise util.Abort(_('cannot backout a merge changeset without '
258 raise util.Abort(_('cannot backout a merge changeset without '
259 '--parent'))
259 '--parent'))
260 p = repo.lookup(opts['parent'])
260 p = repo.lookup(opts['parent'])
261 if p not in (p1, p2):
261 if p not in (p1, p2):
262 raise util.Abort(_('%s is not a parent of %s') %
262 raise util.Abort(_('%s is not a parent of %s') %
263 (short(p), short(node)))
263 (short(p), short(node)))
264 parent = p
264 parent = p
265 else:
265 else:
266 if opts.get('parent'):
266 if opts.get('parent'):
267 raise util.Abort(_('cannot use --parent on non-merge changeset'))
267 raise util.Abort(_('cannot use --parent on non-merge changeset'))
268 parent = p1
268 parent = p1
269
269
270 # the backout should appear on the same branch
270 # the backout should appear on the same branch
271 branch = repo.dirstate.branch()
271 branch = repo.dirstate.branch()
272 hg.clean(repo, node, show_stats=False)
272 hg.clean(repo, node, show_stats=False)
273 repo.dirstate.setbranch(branch)
273 repo.dirstate.setbranch(branch)
274 revert_opts = opts.copy()
274 revert_opts = opts.copy()
275 revert_opts['date'] = None
275 revert_opts['date'] = None
276 revert_opts['all'] = True
276 revert_opts['all'] = True
277 revert_opts['rev'] = hex(parent)
277 revert_opts['rev'] = hex(parent)
278 revert_opts['no_backup'] = None
278 revert_opts['no_backup'] = None
279 revert(ui, repo, **revert_opts)
279 revert(ui, repo, **revert_opts)
280 if not opts.get('merge') and op1 != node:
280 if not opts.get('merge') and op1 != node:
281 try:
281 try:
282 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
282 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
283 return hg.update(repo, op1)
283 return hg.update(repo, op1)
284 finally:
284 finally:
285 ui.setconfig('ui', 'forcemerge', '')
285 ui.setconfig('ui', 'forcemerge', '')
286
286
287 commit_opts = opts.copy()
287 commit_opts = opts.copy()
288 commit_opts['addremove'] = False
288 commit_opts['addremove'] = False
289 if not commit_opts['message'] and not commit_opts['logfile']:
289 if not commit_opts['message'] and not commit_opts['logfile']:
290 # we don't translate commit messages
290 # we don't translate commit messages
291 commit_opts['message'] = "Backed out changeset %s" % short(node)
291 commit_opts['message'] = "Backed out changeset %s" % short(node)
292 commit_opts['force_editor'] = True
292 commit_opts['force_editor'] = True
293 commit(ui, repo, **commit_opts)
293 commit(ui, repo, **commit_opts)
294 def nice(node):
294 def nice(node):
295 return '%d:%s' % (repo.changelog.rev(node), short(node))
295 return '%d:%s' % (repo.changelog.rev(node), short(node))
296 ui.status(_('changeset %s backs out changeset %s\n') %
296 ui.status(_('changeset %s backs out changeset %s\n') %
297 (nice(repo.changelog.tip()), nice(node)))
297 (nice(repo.changelog.tip()), nice(node)))
298 if opts.get('merge') and op1 != node:
298 if opts.get('merge') and op1 != node:
299 hg.clean(repo, op1, show_stats=False)
299 hg.clean(repo, op1, show_stats=False)
300 ui.status(_('merging with changeset %s\n')
300 ui.status(_('merging with changeset %s\n')
301 % nice(repo.changelog.tip()))
301 % nice(repo.changelog.tip()))
302 try:
302 try:
303 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
303 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
304 return hg.merge(repo, hex(repo.changelog.tip()))
304 return hg.merge(repo, hex(repo.changelog.tip()))
305 finally:
305 finally:
306 ui.setconfig('ui', 'forcemerge', '')
306 ui.setconfig('ui', 'forcemerge', '')
307 return 0
307 return 0
308
308
309 def bisect(ui, repo, rev=None, extra=None, command=None,
309 def bisect(ui, repo, rev=None, extra=None, command=None,
310 reset=None, good=None, bad=None, skip=None, noupdate=None):
310 reset=None, good=None, bad=None, skip=None, noupdate=None):
311 """subdivision search of changesets
311 """subdivision search of changesets
312
312
313 This command helps to find changesets which introduce problems. To
313 This command helps to find changesets which introduce problems. To
314 use, mark the earliest changeset you know exhibits the problem as
314 use, mark the earliest changeset you know exhibits the problem as
315 bad, then mark the latest changeset which is free from the problem
315 bad, then mark the latest changeset which is free from the problem
316 as good. Bisect will update your working directory to a revision
316 as good. Bisect will update your working directory to a revision
317 for testing (unless the -U/--noupdate option is specified). Once
317 for testing (unless the -U/--noupdate option is specified). Once
318 you have performed tests, mark the working directory as good or
318 you have performed tests, mark the working directory as good or
319 bad, and bisect will either update to another candidate changeset
319 bad, and bisect will either update to another candidate changeset
320 or announce that it has found the bad revision.
320 or announce that it has found the bad revision.
321
321
322 As a shortcut, you can also use the revision argument to mark a
322 As a shortcut, you can also use the revision argument to mark a
323 revision as good or bad without checking it out first.
323 revision as good or bad without checking it out first.
324
324
325 If you supply a command, it will be used for automatic bisection.
325 If you supply a command, it will be used for automatic bisection.
326 Its exit status will be used to mark revisions as good or bad:
326 Its exit status will be used to mark revisions as good or bad:
327 status 0 means good, 125 means to skip the revision, 127
327 status 0 means good, 125 means to skip the revision, 127
328 (command not found) will abort the bisection, and any other
328 (command not found) will abort the bisection, and any other
329 non-zero exit status means the revision is bad.
329 non-zero exit status means the revision is bad.
330
330
331 Returns 0 on success.
331 Returns 0 on success.
332 """
332 """
333 def print_result(nodes, good):
333 def print_result(nodes, good):
334 displayer = cmdutil.show_changeset(ui, repo, {})
334 displayer = cmdutil.show_changeset(ui, repo, {})
335 if len(nodes) == 1:
335 if len(nodes) == 1:
336 # narrowed it down to a single revision
336 # narrowed it down to a single revision
337 if good:
337 if good:
338 ui.write(_("The first good revision is:\n"))
338 ui.write(_("The first good revision is:\n"))
339 else:
339 else:
340 ui.write(_("The first bad revision is:\n"))
340 ui.write(_("The first bad revision is:\n"))
341 displayer.show(repo[nodes[0]])
341 displayer.show(repo[nodes[0]])
342 parents = repo[nodes[0]].parents()
342 parents = repo[nodes[0]].parents()
343 if len(parents) > 1:
343 if len(parents) > 1:
344 side = good and state['bad'] or state['good']
344 side = good and state['bad'] or state['good']
345 num = len(set(i.node() for i in parents) & set(side))
345 num = len(set(i.node() for i in parents) & set(side))
346 if num == 1:
346 if num == 1:
347 common = parents[0].ancestor(parents[1])
347 common = parents[0].ancestor(parents[1])
348 ui.write(_('Not all ancestors of this changeset have been'
348 ui.write(_('Not all ancestors of this changeset have been'
349 ' checked.\nTo check the other ancestors, start'
349 ' checked.\nTo check the other ancestors, start'
350 ' from the common ancestor, %s.\n' % common))
350 ' from the common ancestor, %s.\n' % common))
351 else:
351 else:
352 # multiple possible revisions
352 # multiple possible revisions
353 if good:
353 if good:
354 ui.write(_("Due to skipped revisions, the first "
354 ui.write(_("Due to skipped revisions, the first "
355 "good revision could be any of:\n"))
355 "good revision could be any of:\n"))
356 else:
356 else:
357 ui.write(_("Due to skipped revisions, the first "
357 ui.write(_("Due to skipped revisions, the first "
358 "bad revision could be any of:\n"))
358 "bad revision could be any of:\n"))
359 for n in nodes:
359 for n in nodes:
360 displayer.show(repo[n])
360 displayer.show(repo[n])
361 displayer.close()
361 displayer.close()
362
362
363 def check_state(state, interactive=True):
363 def check_state(state, interactive=True):
364 if not state['good'] or not state['bad']:
364 if not state['good'] or not state['bad']:
365 if (good or bad or skip or reset) and interactive:
365 if (good or bad or skip or reset) and interactive:
366 return
366 return
367 if not state['good']:
367 if not state['good']:
368 raise util.Abort(_('cannot bisect (no known good revisions)'))
368 raise util.Abort(_('cannot bisect (no known good revisions)'))
369 else:
369 else:
370 raise util.Abort(_('cannot bisect (no known bad revisions)'))
370 raise util.Abort(_('cannot bisect (no known bad revisions)'))
371 return True
371 return True
372
372
373 # backward compatibility
373 # backward compatibility
374 if rev in "good bad reset init".split():
374 if rev in "good bad reset init".split():
375 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
375 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
376 cmd, rev, extra = rev, extra, None
376 cmd, rev, extra = rev, extra, None
377 if cmd == "good":
377 if cmd == "good":
378 good = True
378 good = True
379 elif cmd == "bad":
379 elif cmd == "bad":
380 bad = True
380 bad = True
381 else:
381 else:
382 reset = True
382 reset = True
383 elif extra or good + bad + skip + reset + bool(command) > 1:
383 elif extra or good + bad + skip + reset + bool(command) > 1:
384 raise util.Abort(_('incompatible arguments'))
384 raise util.Abort(_('incompatible arguments'))
385
385
386 if reset:
386 if reset:
387 p = repo.join("bisect.state")
387 p = repo.join("bisect.state")
388 if os.path.exists(p):
388 if os.path.exists(p):
389 os.unlink(p)
389 os.unlink(p)
390 return
390 return
391
391
392 state = hbisect.load_state(repo)
392 state = hbisect.load_state(repo)
393
393
394 if command:
394 if command:
395 changesets = 1
395 changesets = 1
396 try:
396 try:
397 while changesets:
397 while changesets:
398 # update state
398 # update state
399 status = util.system(command)
399 status = util.system(command)
400 if status == 125:
400 if status == 125:
401 transition = "skip"
401 transition = "skip"
402 elif status == 0:
402 elif status == 0:
403 transition = "good"
403 transition = "good"
404 # status < 0 means process was killed
404 # status < 0 means process was killed
405 elif status == 127:
405 elif status == 127:
406 raise util.Abort(_("failed to execute %s") % command)
406 raise util.Abort(_("failed to execute %s") % command)
407 elif status < 0:
407 elif status < 0:
408 raise util.Abort(_("%s killed") % command)
408 raise util.Abort(_("%s killed") % command)
409 else:
409 else:
410 transition = "bad"
410 transition = "bad"
411 ctx = cmdutil.revsingle(repo, rev)
411 ctx = cmdutil.revsingle(repo, rev)
412 rev = None # clear for future iterations
412 rev = None # clear for future iterations
413 state[transition].append(ctx.node())
413 state[transition].append(ctx.node())
414 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
414 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
415 check_state(state, interactive=False)
415 check_state(state, interactive=False)
416 # bisect
416 # bisect
417 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
417 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
418 # update to next check
418 # update to next check
419 cmdutil.bail_if_changed(repo)
419 cmdutil.bail_if_changed(repo)
420 hg.clean(repo, nodes[0], show_stats=False)
420 hg.clean(repo, nodes[0], show_stats=False)
421 finally:
421 finally:
422 hbisect.save_state(repo, state)
422 hbisect.save_state(repo, state)
423 print_result(nodes, good)
423 print_result(nodes, good)
424 return
424 return
425
425
426 # update state
426 # update state
427
427
428 if rev:
428 if rev:
429 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
429 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
430 else:
430 else:
431 nodes = [repo.lookup('.')]
431 nodes = [repo.lookup('.')]
432
432
433 if good or bad or skip:
433 if good or bad or skip:
434 if good:
434 if good:
435 state['good'] += nodes
435 state['good'] += nodes
436 elif bad:
436 elif bad:
437 state['bad'] += nodes
437 state['bad'] += nodes
438 elif skip:
438 elif skip:
439 state['skip'] += nodes
439 state['skip'] += nodes
440 hbisect.save_state(repo, state)
440 hbisect.save_state(repo, state)
441
441
442 if not check_state(state):
442 if not check_state(state):
443 return
443 return
444
444
445 # actually bisect
445 # actually bisect
446 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
446 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
447 if changesets == 0:
447 if changesets == 0:
448 print_result(nodes, good)
448 print_result(nodes, good)
449 else:
449 else:
450 assert len(nodes) == 1 # only a single node can be tested next
450 assert len(nodes) == 1 # only a single node can be tested next
451 node = nodes[0]
451 node = nodes[0]
452 # compute the approximate number of remaining tests
452 # compute the approximate number of remaining tests
453 tests, size = 0, 2
453 tests, size = 0, 2
454 while size <= changesets:
454 while size <= changesets:
455 tests, size = tests + 1, size * 2
455 tests, size = tests + 1, size * 2
456 rev = repo.changelog.rev(node)
456 rev = repo.changelog.rev(node)
457 ui.write(_("Testing changeset %d:%s "
457 ui.write(_("Testing changeset %d:%s "
458 "(%d changesets remaining, ~%d tests)\n")
458 "(%d changesets remaining, ~%d tests)\n")
459 % (rev, short(node), changesets, tests))
459 % (rev, short(node), changesets, tests))
460 if not noupdate:
460 if not noupdate:
461 cmdutil.bail_if_changed(repo)
461 cmdutil.bail_if_changed(repo)
462 return hg.clean(repo, node)
462 return hg.clean(repo, node)
463
463
464 def branch(ui, repo, label=None, **opts):
464 def branch(ui, repo, label=None, **opts):
465 """set or show the current branch name
465 """set or show the current branch name
466
466
467 With no argument, show the current branch name. With one argument,
467 With no argument, show the current branch name. With one argument,
468 set the working directory branch name (the branch will not exist
468 set the working directory branch name (the branch will not exist
469 in the repository until the next commit). Standard practice
469 in the repository until the next commit). Standard practice
470 recommends that primary development take place on the 'default'
470 recommends that primary development take place on the 'default'
471 branch.
471 branch.
472
472
473 Unless -f/--force is specified, branch will not let you set a
473 Unless -f/--force is specified, branch will not let you set a
474 branch name that already exists, even if it's inactive.
474 branch name that already exists, even if it's inactive.
475
475
476 Use -C/--clean to reset the working directory branch to that of
476 Use -C/--clean to reset the working directory branch to that of
477 the parent of the working directory, negating a previous branch
477 the parent of the working directory, negating a previous branch
478 change.
478 change.
479
479
480 Use the command :hg:`update` to switch to an existing branch. Use
480 Use the command :hg:`update` to switch to an existing branch. Use
481 :hg:`commit --close-branch` to mark this branch as closed.
481 :hg:`commit --close-branch` to mark this branch as closed.
482
482
483 Returns 0 on success.
483 Returns 0 on success.
484 """
484 """
485
485
486 if opts.get('clean'):
486 if opts.get('clean'):
487 label = repo[None].parents()[0].branch()
487 label = repo[None].parents()[0].branch()
488 repo.dirstate.setbranch(label)
488 repo.dirstate.setbranch(label)
489 ui.status(_('reset working directory to branch %s\n') % label)
489 ui.status(_('reset working directory to branch %s\n') % label)
490 elif label:
490 elif label:
491 if not opts.get('force') and label in repo.branchtags():
491 if not opts.get('force') and label in repo.branchtags():
492 if label not in [p.branch() for p in repo.parents()]:
492 if label not in [p.branch() for p in repo.parents()]:
493 raise util.Abort(_('a branch of the same name already exists'
493 raise util.Abort(_('a branch of the same name already exists'
494 " (use 'hg update' to switch to it)"))
494 " (use 'hg update' to switch to it)"))
495 repo.dirstate.setbranch(label)
495 repo.dirstate.setbranch(label)
496 ui.status(_('marked working directory as branch %s\n') % label)
496 ui.status(_('marked working directory as branch %s\n') % label)
497 else:
497 else:
498 ui.write("%s\n" % repo.dirstate.branch())
498 ui.write("%s\n" % repo.dirstate.branch())
499
499
500 def branches(ui, repo, active=False, closed=False):
500 def branches(ui, repo, active=False, closed=False):
501 """list repository named branches
501 """list repository named branches
502
502
503 List the repository's named branches, indicating which ones are
503 List the repository's named branches, indicating which ones are
504 inactive. If -c/--closed is specified, also list branches which have
504 inactive. If -c/--closed is specified, also list branches which have
505 been marked closed (see :hg:`commit --close-branch`).
505 been marked closed (see :hg:`commit --close-branch`).
506
506
507 If -a/--active is specified, only show active branches. A branch
507 If -a/--active is specified, only show active branches. A branch
508 is considered active if it contains repository heads.
508 is considered active if it contains repository heads.
509
509
510 Use the command :hg:`update` to switch to an existing branch.
510 Use the command :hg:`update` to switch to an existing branch.
511
511
512 Returns 0.
512 Returns 0.
513 """
513 """
514
514
515 hexfunc = ui.debugflag and hex or short
515 hexfunc = ui.debugflag and hex or short
516 activebranches = [repo[n].branch() for n in repo.heads()]
516 activebranches = [repo[n].branch() for n in repo.heads()]
517 def testactive(tag, node):
517 def testactive(tag, node):
518 realhead = tag in activebranches
518 realhead = tag in activebranches
519 open = node in repo.branchheads(tag, closed=False)
519 open = node in repo.branchheads(tag, closed=False)
520 return realhead and open
520 return realhead and open
521 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
521 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
522 for tag, node in repo.branchtags().items()],
522 for tag, node in repo.branchtags().items()],
523 reverse=True)
523 reverse=True)
524
524
525 for isactive, node, tag in branches:
525 for isactive, node, tag in branches:
526 if (not active) or isactive:
526 if (not active) or isactive:
527 if ui.quiet:
527 if ui.quiet:
528 ui.write("%s\n" % tag)
528 ui.write("%s\n" % tag)
529 else:
529 else:
530 hn = repo.lookup(node)
530 hn = repo.lookup(node)
531 if isactive:
531 if isactive:
532 label = 'branches.active'
532 label = 'branches.active'
533 notice = ''
533 notice = ''
534 elif hn not in repo.branchheads(tag, closed=False):
534 elif hn not in repo.branchheads(tag, closed=False):
535 if not closed:
535 if not closed:
536 continue
536 continue
537 label = 'branches.closed'
537 label = 'branches.closed'
538 notice = _(' (closed)')
538 notice = _(' (closed)')
539 else:
539 else:
540 label = 'branches.inactive'
540 label = 'branches.inactive'
541 notice = _(' (inactive)')
541 notice = _(' (inactive)')
542 if tag == repo.dirstate.branch():
542 if tag == repo.dirstate.branch():
543 label = 'branches.current'
543 label = 'branches.current'
544 rev = str(node).rjust(31 - encoding.colwidth(tag))
544 rev = str(node).rjust(31 - encoding.colwidth(tag))
545 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
545 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
546 tag = ui.label(tag, label)
546 tag = ui.label(tag, label)
547 ui.write("%s %s%s\n" % (tag, rev, notice))
547 ui.write("%s %s%s\n" % (tag, rev, notice))
548
548
549 def bundle(ui, repo, fname, dest=None, **opts):
549 def bundle(ui, repo, fname, dest=None, **opts):
550 """create a changegroup file
550 """create a changegroup file
551
551
552 Generate a compressed changegroup file collecting changesets not
552 Generate a compressed changegroup file collecting changesets not
553 known to be in another repository.
553 known to be in another repository.
554
554
555 If you omit the destination repository, then hg assumes the
555 If you omit the destination repository, then hg assumes the
556 destination will have all the nodes you specify with --base
556 destination will have all the nodes you specify with --base
557 parameters. To create a bundle containing all changesets, use
557 parameters. To create a bundle containing all changesets, use
558 -a/--all (or --base null).
558 -a/--all (or --base null).
559
559
560 You can change compression method with the -t/--type option.
560 You can change compression method with the -t/--type option.
561 The available compression methods are: none, bzip2, and
561 The available compression methods are: none, bzip2, and
562 gzip (by default, bundles are compressed using bzip2).
562 gzip (by default, bundles are compressed using bzip2).
563
563
564 The bundle file can then be transferred using conventional means
564 The bundle file can then be transferred using conventional means
565 and applied to another repository with the unbundle or pull
565 and applied to another repository with the unbundle or pull
566 command. This is useful when direct push and pull are not
566 command. This is useful when direct push and pull are not
567 available or when exporting an entire repository is undesirable.
567 available or when exporting an entire repository is undesirable.
568
568
569 Applying bundles preserves all changeset contents including
569 Applying bundles preserves all changeset contents including
570 permissions, copy/rename information, and revision history.
570 permissions, copy/rename information, and revision history.
571
571
572 Returns 0 on success, 1 if no changes found.
572 Returns 0 on success, 1 if no changes found.
573 """
573 """
574 revs = None
574 revs = None
575 if 'rev' in opts:
575 if 'rev' in opts:
576 revs = cmdutil.revrange(repo, opts['rev'])
576 revs = cmdutil.revrange(repo, opts['rev'])
577
577
578 if opts.get('all'):
578 if opts.get('all'):
579 base = ['null']
579 base = ['null']
580 else:
580 else:
581 base = cmdutil.revrange(repo, opts.get('base'))
581 base = cmdutil.revrange(repo, opts.get('base'))
582 if base:
582 if base:
583 if dest:
583 if dest:
584 raise util.Abort(_("--base is incompatible with specifying "
584 raise util.Abort(_("--base is incompatible with specifying "
585 "a destination"))
585 "a destination"))
586 base = [repo.lookup(rev) for rev in base]
586 base = [repo.lookup(rev) for rev in base]
587 # create the right base
587 # create the right base
588 # XXX: nodesbetween / changegroup* should be "fixed" instead
588 # XXX: nodesbetween / changegroup* should be "fixed" instead
589 o = []
589 o = []
590 has = set((nullid,))
590 has = set((nullid,))
591 for n in base:
591 for n in base:
592 has.update(repo.changelog.reachable(n))
592 has.update(repo.changelog.reachable(n))
593 if revs:
593 if revs:
594 revs = [repo.lookup(rev) for rev in revs]
594 revs = [repo.lookup(rev) for rev in revs]
595 visit = revs[:]
595 visit = revs[:]
596 has.difference_update(visit)
596 has.difference_update(visit)
597 else:
597 else:
598 visit = repo.changelog.heads()
598 visit = repo.changelog.heads()
599 seen = {}
599 seen = {}
600 while visit:
600 while visit:
601 n = visit.pop(0)
601 n = visit.pop(0)
602 parents = [p for p in repo.changelog.parents(n) if p not in has]
602 parents = [p for p in repo.changelog.parents(n) if p not in has]
603 if len(parents) == 0:
603 if len(parents) == 0:
604 if n not in has:
604 if n not in has:
605 o.append(n)
605 o.append(n)
606 else:
606 else:
607 for p in parents:
607 for p in parents:
608 if p not in seen:
608 if p not in seen:
609 seen[p] = 1
609 seen[p] = 1
610 visit.append(p)
610 visit.append(p)
611 else:
611 else:
612 dest = ui.expandpath(dest or 'default-push', dest or 'default')
612 dest = ui.expandpath(dest or 'default-push', dest or 'default')
613 dest, branches = hg.parseurl(dest, opts.get('branch'))
613 dest, branches = hg.parseurl(dest, opts.get('branch'))
614 other = hg.repository(hg.remoteui(repo, opts), dest)
614 other = hg.repository(hg.remoteui(repo, opts), dest)
615 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
615 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
616 if revs:
616 if revs:
617 revs = [repo.lookup(rev) for rev in revs]
617 revs = [repo.lookup(rev) for rev in revs]
618 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
618 o = discovery.findoutgoing(repo, other, force=opts.get('force'))
619
619
620 if not o:
620 if not o:
621 ui.status(_("no changes found\n"))
621 ui.status(_("no changes found\n"))
622 return 1
622 return 1
623
623
624 if revs:
624 if revs:
625 cg = repo.changegroupsubset(o, revs, 'bundle')
625 cg = repo.changegroupsubset(o, revs, 'bundle')
626 else:
626 else:
627 cg = repo.changegroup(o, 'bundle')
627 cg = repo.changegroup(o, 'bundle')
628
628
629 bundletype = opts.get('type', 'bzip2').lower()
629 bundletype = opts.get('type', 'bzip2').lower()
630 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
630 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
631 bundletype = btypes.get(bundletype)
631 bundletype = btypes.get(bundletype)
632 if bundletype not in changegroup.bundletypes:
632 if bundletype not in changegroup.bundletypes:
633 raise util.Abort(_('unknown bundle type specified with --type'))
633 raise util.Abort(_('unknown bundle type specified with --type'))
634
634
635 changegroup.writebundle(cg, fname, bundletype)
635 changegroup.writebundle(cg, fname, bundletype)
636
636
637 def cat(ui, repo, file1, *pats, **opts):
637 def cat(ui, repo, file1, *pats, **opts):
638 """output the current or given revision of files
638 """output the current or given revision of files
639
639
640 Print the specified files as they were at the given revision. If
640 Print the specified files as they were at the given revision. If
641 no revision is given, the parent of the working directory is used,
641 no revision is given, the parent of the working directory is used,
642 or tip if no revision is checked out.
642 or tip if no revision is checked out.
643
643
644 Output may be to a file, in which case the name of the file is
644 Output may be to a file, in which case the name of the file is
645 given using a format string. The formatting rules are the same as
645 given using a format string. The formatting rules are the same as
646 for the export command, with the following additions:
646 for the export command, with the following additions:
647
647
648 :``%s``: basename of file being printed
648 :``%s``: basename of file being printed
649 :``%d``: dirname of file being printed, or '.' if in repository root
649 :``%d``: dirname of file being printed, or '.' if in repository root
650 :``%p``: root-relative path name of file being printed
650 :``%p``: root-relative path name of file being printed
651
651
652 Returns 0 on success.
652 Returns 0 on success.
653 """
653 """
654 ctx = cmdutil.revsingle(repo, opts.get('rev'))
654 ctx = cmdutil.revsingle(repo, opts.get('rev'))
655 err = 1
655 err = 1
656 m = cmdutil.match(repo, (file1,) + pats, opts)
656 m = cmdutil.match(repo, (file1,) + pats, opts)
657 for abs in ctx.walk(m):
657 for abs in ctx.walk(m):
658 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
658 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
659 data = ctx[abs].data()
659 data = ctx[abs].data()
660 if opts.get('decode'):
660 if opts.get('decode'):
661 data = repo.wwritedata(abs, data)
661 data = repo.wwritedata(abs, data)
662 fp.write(data)
662 fp.write(data)
663 err = 0
663 err = 0
664 return err
664 return err
665
665
666 def clone(ui, source, dest=None, **opts):
666 def clone(ui, source, dest=None, **opts):
667 """make a copy of an existing repository
667 """make a copy of an existing repository
668
668
669 Create a copy of an existing repository in a new directory.
669 Create a copy of an existing repository in a new directory.
670
670
671 If no destination directory name is specified, it defaults to the
671 If no destination directory name is specified, it defaults to the
672 basename of the source.
672 basename of the source.
673
673
674 The location of the source is added to the new repository's
674 The location of the source is added to the new repository's
675 .hg/hgrc file, as the default to be used for future pulls.
675 .hg/hgrc file, as the default to be used for future pulls.
676
676
677 See :hg:`help urls` for valid source format details.
677 See :hg:`help urls` for valid source format details.
678
678
679 It is possible to specify an ``ssh://`` URL as the destination, but no
679 It is possible to specify an ``ssh://`` URL as the destination, but no
680 .hg/hgrc and working directory will be created on the remote side.
680 .hg/hgrc and working directory will be created on the remote side.
681 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
681 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
682
682
683 A set of changesets (tags, or branch names) to pull may be specified
683 A set of changesets (tags, or branch names) to pull may be specified
684 by listing each changeset (tag, or branch name) with -r/--rev.
684 by listing each changeset (tag, or branch name) with -r/--rev.
685 If -r/--rev is used, the cloned repository will contain only a subset
685 If -r/--rev is used, the cloned repository will contain only a subset
686 of the changesets of the source repository. Only the set of changesets
686 of the changesets of the source repository. Only the set of changesets
687 defined by all -r/--rev options (including all their ancestors)
687 defined by all -r/--rev options (including all their ancestors)
688 will be pulled into the destination repository.
688 will be pulled into the destination repository.
689 No subsequent changesets (including subsequent tags) will be present
689 No subsequent changesets (including subsequent tags) will be present
690 in the destination.
690 in the destination.
691
691
692 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
692 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
693 local source repositories.
693 local source repositories.
694
694
695 For efficiency, hardlinks are used for cloning whenever the source
695 For efficiency, hardlinks are used for cloning whenever the source
696 and destination are on the same filesystem (note this applies only
696 and destination are on the same filesystem (note this applies only
697 to the repository data, not to the working directory). Some
697 to the repository data, not to the working directory). Some
698 filesystems, such as AFS, implement hardlinking incorrectly, but
698 filesystems, such as AFS, implement hardlinking incorrectly, but
699 do not report errors. In these cases, use the --pull option to
699 do not report errors. In these cases, use the --pull option to
700 avoid hardlinking.
700 avoid hardlinking.
701
701
702 In some cases, you can clone repositories and the working directory
702 In some cases, you can clone repositories and the working directory
703 using full hardlinks with ::
703 using full hardlinks with ::
704
704
705 $ cp -al REPO REPOCLONE
705 $ cp -al REPO REPOCLONE
706
706
707 This is the fastest way to clone, but it is not always safe. The
707 This is the fastest way to clone, but it is not always safe. The
708 operation is not atomic (making sure REPO is not modified during
708 operation is not atomic (making sure REPO is not modified during
709 the operation is up to you) and you have to make sure your editor
709 the operation is up to you) and you have to make sure your editor
710 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
710 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
711 this is not compatible with certain extensions that place their
711 this is not compatible with certain extensions that place their
712 metadata under the .hg directory, such as mq.
712 metadata under the .hg directory, such as mq.
713
713
714 Mercurial will update the working directory to the first applicable
714 Mercurial will update the working directory to the first applicable
715 revision from this list:
715 revision from this list:
716
716
717 a) null if -U or the source repository has no changesets
717 a) null if -U or the source repository has no changesets
718 b) if -u . and the source repository is local, the first parent of
718 b) if -u . and the source repository is local, the first parent of
719 the source repository's working directory
719 the source repository's working directory
720 c) the changeset specified with -u (if a branch name, this means the
720 c) the changeset specified with -u (if a branch name, this means the
721 latest head of that branch)
721 latest head of that branch)
722 d) the changeset specified with -r
722 d) the changeset specified with -r
723 e) the tipmost head specified with -b
723 e) the tipmost head specified with -b
724 f) the tipmost head specified with the url#branch source syntax
724 f) the tipmost head specified with the url#branch source syntax
725 g) the tipmost head of the default branch
725 g) the tipmost head of the default branch
726 h) tip
726 h) tip
727
727
728 Returns 0 on success.
728 Returns 0 on success.
729 """
729 """
730 if opts.get('noupdate') and opts.get('updaterev'):
730 if opts.get('noupdate') and opts.get('updaterev'):
731 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
731 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
732
732
733 r = hg.clone(hg.remoteui(ui, opts), source, dest,
733 r = hg.clone(hg.remoteui(ui, opts), source, dest,
734 pull=opts.get('pull'),
734 pull=opts.get('pull'),
735 stream=opts.get('uncompressed'),
735 stream=opts.get('uncompressed'),
736 rev=opts.get('rev'),
736 rev=opts.get('rev'),
737 update=opts.get('updaterev') or not opts.get('noupdate'),
737 update=opts.get('updaterev') or not opts.get('noupdate'),
738 branch=opts.get('branch'))
738 branch=opts.get('branch'))
739
739
740 return r is None
740 return r is None
741
741
742 def commit(ui, repo, *pats, **opts):
742 def commit(ui, repo, *pats, **opts):
743 """commit the specified files or all outstanding changes
743 """commit the specified files or all outstanding changes
744
744
745 Commit changes to the given files into the repository. Unlike a
745 Commit changes to the given files into the repository. Unlike a
746 centralized RCS, this operation is a local operation. See
746 centralized RCS, this operation is a local operation. See
747 :hg:`push` for a way to actively distribute your changes.
747 :hg:`push` for a way to actively distribute your changes.
748
748
749 If a list of files is omitted, all changes reported by :hg:`status`
749 If a list of files is omitted, all changes reported by :hg:`status`
750 will be committed.
750 will be committed.
751
751
752 If you are committing the result of a merge, do not provide any
752 If you are committing the result of a merge, do not provide any
753 filenames or -I/-X filters.
753 filenames or -I/-X filters.
754
754
755 If no commit message is specified, Mercurial starts your
755 If no commit message is specified, Mercurial starts your
756 configured editor where you can enter a message. In case your
756 configured editor where you can enter a message. In case your
757 commit fails, you will find a backup of your message in
757 commit fails, you will find a backup of your message in
758 ``.hg/last-message.txt``.
758 ``.hg/last-message.txt``.
759
759
760 See :hg:`help dates` for a list of formats valid for -d/--date.
760 See :hg:`help dates` for a list of formats valid for -d/--date.
761
761
762 Returns 0 on success, 1 if nothing changed.
762 Returns 0 on success, 1 if nothing changed.
763 """
763 """
764 extra = {}
764 extra = {}
765 if opts.get('close_branch'):
765 if opts.get('close_branch'):
766 if repo['.'].node() not in repo.branchheads():
766 if repo['.'].node() not in repo.branchheads():
767 # The topo heads set is included in the branch heads set of the
767 # The topo heads set is included in the branch heads set of the
768 # current branch, so it's sufficient to test branchheads
768 # current branch, so it's sufficient to test branchheads
769 raise util.Abort(_('can only close branch heads'))
769 raise util.Abort(_('can only close branch heads'))
770 extra['close'] = 1
770 extra['close'] = 1
771 e = cmdutil.commiteditor
771 e = cmdutil.commiteditor
772 if opts.get('force_editor'):
772 if opts.get('force_editor'):
773 e = cmdutil.commitforceeditor
773 e = cmdutil.commitforceeditor
774
774
775 def commitfunc(ui, repo, message, match, opts):
775 def commitfunc(ui, repo, message, match, opts):
776 return repo.commit(message, opts.get('user'), opts.get('date'), match,
776 return repo.commit(message, opts.get('user'), opts.get('date'), match,
777 editor=e, extra=extra)
777 editor=e, extra=extra)
778
778
779 branch = repo[None].branch()
779 branch = repo[None].branch()
780 bheads = repo.branchheads(branch)
780 bheads = repo.branchheads(branch)
781
781
782 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
782 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
783 if not node:
783 if not node:
784 ui.status(_("nothing changed\n"))
784 ui.status(_("nothing changed\n"))
785 return 1
785 return 1
786
786
787 ctx = repo[node]
787 ctx = repo[node]
788 parents = ctx.parents()
788 parents = ctx.parents()
789
789
790 if bheads and not [x for x in parents
790 if bheads and not [x for x in parents
791 if x.node() in bheads and x.branch() == branch]:
791 if x.node() in bheads and x.branch() == branch]:
792 ui.status(_('created new head\n'))
792 ui.status(_('created new head\n'))
793 # The message is not printed for initial roots. For the other
793 # The message is not printed for initial roots. For the other
794 # changesets, it is printed in the following situations:
794 # changesets, it is printed in the following situations:
795 #
795 #
796 # Par column: for the 2 parents with ...
796 # Par column: for the 2 parents with ...
797 # N: null or no parent
797 # N: null or no parent
798 # B: parent is on another named branch
798 # B: parent is on another named branch
799 # C: parent is a regular non head changeset
799 # C: parent is a regular non head changeset
800 # H: parent was a branch head of the current branch
800 # H: parent was a branch head of the current branch
801 # Msg column: whether we print "created new head" message
801 # Msg column: whether we print "created new head" message
802 # In the following, it is assumed that there already exists some
802 # In the following, it is assumed that there already exists some
803 # initial branch heads of the current branch, otherwise nothing is
803 # initial branch heads of the current branch, otherwise nothing is
804 # printed anyway.
804 # printed anyway.
805 #
805 #
806 # Par Msg Comment
806 # Par Msg Comment
807 # NN y additional topo root
807 # NN y additional topo root
808 #
808 #
809 # BN y additional branch root
809 # BN y additional branch root
810 # CN y additional topo head
810 # CN y additional topo head
811 # HN n usual case
811 # HN n usual case
812 #
812 #
813 # BB y weird additional branch root
813 # BB y weird additional branch root
814 # CB y branch merge
814 # CB y branch merge
815 # HB n merge with named branch
815 # HB n merge with named branch
816 #
816 #
817 # CC y additional head from merge
817 # CC y additional head from merge
818 # CH n merge with a head
818 # CH n merge with a head
819 #
819 #
820 # HH n head merge: head count decreases
820 # HH n head merge: head count decreases
821
821
822 if not opts.get('close_branch'):
822 if not opts.get('close_branch'):
823 for r in parents:
823 for r in parents:
824 if r.extra().get('close') and r.branch() == branch:
824 if r.extra().get('close') and r.branch() == branch:
825 ui.status(_('reopening closed branch head %d\n') % r)
825 ui.status(_('reopening closed branch head %d\n') % r)
826
826
827 if ui.debugflag:
827 if ui.debugflag:
828 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
828 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
829 elif ui.verbose:
829 elif ui.verbose:
830 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
830 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
831
831
832 def copy(ui, repo, *pats, **opts):
832 def copy(ui, repo, *pats, **opts):
833 """mark files as copied for the next commit
833 """mark files as copied for the next commit
834
834
835 Mark dest as having copies of source files. If dest is a
835 Mark dest as having copies of source files. If dest is a
836 directory, copies are put in that directory. If dest is a file,
836 directory, copies are put in that directory. If dest is a file,
837 the source must be a single file.
837 the source must be a single file.
838
838
839 By default, this command copies the contents of files as they
839 By default, this command copies the contents of files as they
840 exist in the working directory. If invoked with -A/--after, the
840 exist in the working directory. If invoked with -A/--after, the
841 operation is recorded, but no copying is performed.
841 operation is recorded, but no copying is performed.
842
842
843 This command takes effect with the next commit. To undo a copy
843 This command takes effect with the next commit. To undo a copy
844 before that, see :hg:`revert`.
844 before that, see :hg:`revert`.
845
845
846 Returns 0 on success, 1 if errors are encountered.
846 Returns 0 on success, 1 if errors are encountered.
847 """
847 """
848 wlock = repo.wlock(False)
848 wlock = repo.wlock(False)
849 try:
849 try:
850 return cmdutil.copy(ui, repo, pats, opts)
850 return cmdutil.copy(ui, repo, pats, opts)
851 finally:
851 finally:
852 wlock.release()
852 wlock.release()
853
853
854 def debugancestor(ui, repo, *args):
854 def debugancestor(ui, repo, *args):
855 """find the ancestor revision of two revisions in a given index"""
855 """find the ancestor revision of two revisions in a given index"""
856 if len(args) == 3:
856 if len(args) == 3:
857 index, rev1, rev2 = args
857 index, rev1, rev2 = args
858 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
858 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
859 lookup = r.lookup
859 lookup = r.lookup
860 elif len(args) == 2:
860 elif len(args) == 2:
861 if not repo:
861 if not repo:
862 raise util.Abort(_("there is no Mercurial repository here "
862 raise util.Abort(_("there is no Mercurial repository here "
863 "(.hg not found)"))
863 "(.hg not found)"))
864 rev1, rev2 = args
864 rev1, rev2 = args
865 r = repo.changelog
865 r = repo.changelog
866 lookup = repo.lookup
866 lookup = repo.lookup
867 else:
867 else:
868 raise util.Abort(_('either two or three arguments required'))
868 raise util.Abort(_('either two or three arguments required'))
869 a = r.ancestor(lookup(rev1), lookup(rev2))
869 a = r.ancestor(lookup(rev1), lookup(rev2))
870 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
870 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
871
871
872 def debugbuilddag(ui, repo, text,
872 def debugbuilddag(ui, repo, text,
873 mergeable_file=False,
873 mergeable_file=False,
874 appended_file=False,
874 appended_file=False,
875 overwritten_file=False,
875 overwritten_file=False,
876 new_file=False):
876 new_file=False):
877 """builds a repo with a given dag from scratch in the current empty repo
877 """builds a repo with a given dag from scratch in the current empty repo
878
878
879 Elements:
879 Elements:
880
880
881 - "+n" is a linear run of n nodes based on the current default parent
881 - "+n" is a linear run of n nodes based on the current default parent
882 - "." is a single node based on the current default parent
882 - "." is a single node based on the current default parent
883 - "$" resets the default parent to null (implied at the start);
883 - "$" resets the default parent to null (implied at the start);
884 otherwise the default parent is always the last node created
884 otherwise the default parent is always the last node created
885 - "<p" sets the default parent to the backref p
885 - "<p" sets the default parent to the backref p
886 - "*p" is a fork at parent p, which is a backref
886 - "*p" is a fork at parent p, which is a backref
887 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
887 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
888 - "/p2" is a merge of the preceding node and p2
888 - "/p2" is a merge of the preceding node and p2
889 - ":tag" defines a local tag for the preceding node
889 - ":tag" defines a local tag for the preceding node
890 - "@branch" sets the named branch for subsequent nodes
890 - "@branch" sets the named branch for subsequent nodes
891 - "!command" runs the command using your shell
891 - "!command" runs the command using your shell
892 - "!!my command\\n" is like "!", but to the end of the line
892 - "!!my command\\n" is like "!", but to the end of the line
893 - "#...\\n" is a comment up to the end of the line
893 - "#...\\n" is a comment up to the end of the line
894
894
895 Whitespace between the above elements is ignored.
895 Whitespace between the above elements is ignored.
896
896
897 A backref is either
897 A backref is either
898
898
899 - a number n, which references the node curr-n, where curr is the current
899 - a number n, which references the node curr-n, where curr is the current
900 node, or
900 node, or
901 - the name of a local tag you placed earlier using ":tag", or
901 - the name of a local tag you placed earlier using ":tag", or
902 - empty to denote the default parent.
902 - empty to denote the default parent.
903
903
904 All string valued-elements are either strictly alphanumeric, or must
904 All string valued-elements are either strictly alphanumeric, or must
905 be enclosed in double quotes ("..."), with "\\" as escape character.
905 be enclosed in double quotes ("..."), with "\\" as escape character.
906
906
907 Note that the --overwritten-file and --appended-file options imply the
907 Note that the --overwritten-file and --appended-file options imply the
908 use of "HGMERGE=internal:local" during DAG buildup.
908 use of "HGMERGE=internal:local" during DAG buildup.
909 """
909 """
910
910
911 if not (mergeable_file or appended_file or overwritten_file or new_file):
911 if not (mergeable_file or appended_file or overwritten_file or new_file):
912 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
912 raise util.Abort(_('need at least one of -m, -a, -o, -n'))
913
913
914 if len(repo.changelog) > 0:
914 if len(repo.changelog) > 0:
915 raise util.Abort(_('repository is not empty'))
915 raise util.Abort(_('repository is not empty'))
916
916
917 if overwritten_file or appended_file:
917 if overwritten_file or appended_file:
918 # we don't want to fail in merges during buildup
918 # we don't want to fail in merges during buildup
919 os.environ['HGMERGE'] = 'internal:local'
919 os.environ['HGMERGE'] = 'internal:local'
920
920
921 def writefile(fname, text, fmode="wb"):
921 def writefile(fname, text, fmode="wb"):
922 f = open(fname, fmode)
922 f = open(fname, fmode)
923 try:
923 try:
924 f.write(text)
924 f.write(text)
925 finally:
925 finally:
926 f.close()
926 f.close()
927
927
928 if mergeable_file:
928 if mergeable_file:
929 linesperrev = 2
929 linesperrev = 2
930 # determine number of revs in DAG
930 # determine number of revs in DAG
931 n = 0
931 n = 0
932 for type, data in dagparser.parsedag(text):
932 for type, data in dagparser.parsedag(text):
933 if type == 'n':
933 if type == 'n':
934 n += 1
934 n += 1
935 # make a file with k lines per rev
935 # make a file with k lines per rev
936 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
936 writefile("mf", "\n".join(str(i) for i in xrange(0, n * linesperrev))
937 + "\n")
937 + "\n")
938
938
939 at = -1
939 at = -1
940 atbranch = 'default'
940 atbranch = 'default'
941 for type, data in dagparser.parsedag(text):
941 for type, data in dagparser.parsedag(text):
942 if type == 'n':
942 if type == 'n':
943 ui.status('node %s\n' % str(data))
943 ui.status('node %s\n' % str(data))
944 id, ps = data
944 id, ps = data
945 p1 = ps[0]
945 p1 = ps[0]
946 if p1 != at:
946 if p1 != at:
947 update(ui, repo, node=str(p1), clean=True)
947 update(ui, repo, node=str(p1), clean=True)
948 at = p1
948 at = p1
949 if repo.dirstate.branch() != atbranch:
949 if repo.dirstate.branch() != atbranch:
950 branch(ui, repo, atbranch, force=True)
950 branch(ui, repo, atbranch, force=True)
951 if len(ps) > 1:
951 if len(ps) > 1:
952 p2 = ps[1]
952 p2 = ps[1]
953 merge(ui, repo, node=p2)
953 merge(ui, repo, node=p2)
954
954
955 if mergeable_file:
955 if mergeable_file:
956 f = open("mf", "rb+")
956 f = open("mf", "rb+")
957 try:
957 try:
958 lines = f.read().split("\n")
958 lines = f.read().split("\n")
959 lines[id * linesperrev] += " r%i" % id
959 lines[id * linesperrev] += " r%i" % id
960 f.seek(0)
960 f.seek(0)
961 f.write("\n".join(lines))
961 f.write("\n".join(lines))
962 finally:
962 finally:
963 f.close()
963 f.close()
964
964
965 if appended_file:
965 if appended_file:
966 writefile("af", "r%i\n" % id, "ab")
966 writefile("af", "r%i\n" % id, "ab")
967
967
968 if overwritten_file:
968 if overwritten_file:
969 writefile("of", "r%i\n" % id)
969 writefile("of", "r%i\n" % id)
970
970
971 if new_file:
971 if new_file:
972 writefile("nf%i" % id, "r%i\n" % id)
972 writefile("nf%i" % id, "r%i\n" % id)
973
973
974 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
974 commit(ui, repo, addremove=True, message="r%i" % id, date=(id, 0))
975 at = id
975 at = id
976 elif type == 'l':
976 elif type == 'l':
977 id, name = data
977 id, name = data
978 ui.status('tag %s\n' % name)
978 ui.status('tag %s\n' % name)
979 tag(ui, repo, name, local=True)
979 tag(ui, repo, name, local=True)
980 elif type == 'a':
980 elif type == 'a':
981 ui.status('branch %s\n' % data)
981 ui.status('branch %s\n' % data)
982 atbranch = data
982 atbranch = data
983 elif type in 'cC':
983 elif type in 'cC':
984 r = util.system(data, cwd=repo.root)
984 r = util.system(data, cwd=repo.root)
985 if r:
985 if r:
986 desc, r = util.explain_exit(r)
986 desc, r = util.explain_exit(r)
987 raise util.Abort(_('%s command %s') % (data, desc))
987 raise util.Abort(_('%s command %s') % (data, desc))
988
988
989 def debugcommands(ui, cmd='', *args):
989 def debugcommands(ui, cmd='', *args):
990 """list all available commands and options"""
990 """list all available commands and options"""
991 for cmd, vals in sorted(table.iteritems()):
991 for cmd, vals in sorted(table.iteritems()):
992 cmd = cmd.split('|')[0].strip('^')
992 cmd = cmd.split('|')[0].strip('^')
993 opts = ', '.join([i[1] for i in vals[1]])
993 opts = ', '.join([i[1] for i in vals[1]])
994 ui.write('%s: %s\n' % (cmd, opts))
994 ui.write('%s: %s\n' % (cmd, opts))
995
995
996 def debugcomplete(ui, cmd='', **opts):
996 def debugcomplete(ui, cmd='', **opts):
997 """returns the completion list associated with the given command"""
997 """returns the completion list associated with the given command"""
998
998
999 if opts.get('options'):
999 if opts.get('options'):
1000 options = []
1000 options = []
1001 otables = [globalopts]
1001 otables = [globalopts]
1002 if cmd:
1002 if cmd:
1003 aliases, entry = cmdutil.findcmd(cmd, table, False)
1003 aliases, entry = cmdutil.findcmd(cmd, table, False)
1004 otables.append(entry[1])
1004 otables.append(entry[1])
1005 for t in otables:
1005 for t in otables:
1006 for o in t:
1006 for o in t:
1007 if "(DEPRECATED)" in o[3]:
1007 if "(DEPRECATED)" in o[3]:
1008 continue
1008 continue
1009 if o[0]:
1009 if o[0]:
1010 options.append('-%s' % o[0])
1010 options.append('-%s' % o[0])
1011 options.append('--%s' % o[1])
1011 options.append('--%s' % o[1])
1012 ui.write("%s\n" % "\n".join(options))
1012 ui.write("%s\n" % "\n".join(options))
1013 return
1013 return
1014
1014
1015 cmdlist = cmdutil.findpossible(cmd, table)
1015 cmdlist = cmdutil.findpossible(cmd, table)
1016 if ui.verbose:
1016 if ui.verbose:
1017 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1017 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1018 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1018 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1019
1019
1020 def debugfsinfo(ui, path = "."):
1020 def debugfsinfo(ui, path = "."):
1021 """show information detected about current filesystem"""
1021 """show information detected about current filesystem"""
1022 open('.debugfsinfo', 'w').write('')
1022 open('.debugfsinfo', 'w').write('')
1023 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1023 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1024 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1024 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1025 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1025 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1026 and 'yes' or 'no'))
1026 and 'yes' or 'no'))
1027 os.unlink('.debugfsinfo')
1027 os.unlink('.debugfsinfo')
1028
1028
1029 def debugrebuildstate(ui, repo, rev="tip"):
1029 def debugrebuildstate(ui, repo, rev="tip"):
1030 """rebuild the dirstate as it would look like for the given revision"""
1030 """rebuild the dirstate as it would look like for the given revision"""
1031 ctx = cmdutil.revsingle(repo, rev)
1031 ctx = cmdutil.revsingle(repo, rev)
1032 wlock = repo.wlock()
1032 wlock = repo.wlock()
1033 try:
1033 try:
1034 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1034 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1035 finally:
1035 finally:
1036 wlock.release()
1036 wlock.release()
1037
1037
1038 def debugcheckstate(ui, repo):
1038 def debugcheckstate(ui, repo):
1039 """validate the correctness of the current dirstate"""
1039 """validate the correctness of the current dirstate"""
1040 parent1, parent2 = repo.dirstate.parents()
1040 parent1, parent2 = repo.dirstate.parents()
1041 m1 = repo[parent1].manifest()
1041 m1 = repo[parent1].manifest()
1042 m2 = repo[parent2].manifest()
1042 m2 = repo[parent2].manifest()
1043 errors = 0
1043 errors = 0
1044 for f in repo.dirstate:
1044 for f in repo.dirstate:
1045 state = repo.dirstate[f]
1045 state = repo.dirstate[f]
1046 if state in "nr" and f not in m1:
1046 if state in "nr" and f not in m1:
1047 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1047 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1048 errors += 1
1048 errors += 1
1049 if state in "a" and f in m1:
1049 if state in "a" and f in m1:
1050 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1050 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1051 errors += 1
1051 errors += 1
1052 if state in "m" and f not in m1 and f not in m2:
1052 if state in "m" and f not in m1 and f not in m2:
1053 ui.warn(_("%s in state %s, but not in either manifest\n") %
1053 ui.warn(_("%s in state %s, but not in either manifest\n") %
1054 (f, state))
1054 (f, state))
1055 errors += 1
1055 errors += 1
1056 for f in m1:
1056 for f in m1:
1057 state = repo.dirstate[f]
1057 state = repo.dirstate[f]
1058 if state not in "nrm":
1058 if state not in "nrm":
1059 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1059 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1060 errors += 1
1060 errors += 1
1061 if errors:
1061 if errors:
1062 error = _(".hg/dirstate inconsistent with current parent's manifest")
1062 error = _(".hg/dirstate inconsistent with current parent's manifest")
1063 raise util.Abort(error)
1063 raise util.Abort(error)
1064
1064
1065 def showconfig(ui, repo, *values, **opts):
1065 def showconfig(ui, repo, *values, **opts):
1066 """show combined config settings from all hgrc files
1066 """show combined config settings from all hgrc files
1067
1067
1068 With no arguments, print names and values of all config items.
1068 With no arguments, print names and values of all config items.
1069
1069
1070 With one argument of the form section.name, print just the value
1070 With one argument of the form section.name, print just the value
1071 of that config item.
1071 of that config item.
1072
1072
1073 With multiple arguments, print names and values of all config
1073 With multiple arguments, print names and values of all config
1074 items with matching section names.
1074 items with matching section names.
1075
1075
1076 With --debug, the source (filename and line number) is printed
1076 With --debug, the source (filename and line number) is printed
1077 for each config item.
1077 for each config item.
1078
1078
1079 Returns 0 on success.
1079 Returns 0 on success.
1080 """
1080 """
1081
1081
1082 for f in util.rcpath():
1082 for f in util.rcpath():
1083 ui.debug(_('read config from: %s\n') % f)
1083 ui.debug(_('read config from: %s\n') % f)
1084 untrusted = bool(opts.get('untrusted'))
1084 untrusted = bool(opts.get('untrusted'))
1085 if values:
1085 if values:
1086 sections = [v for v in values if '.' not in v]
1086 sections = [v for v in values if '.' not in v]
1087 items = [v for v in values if '.' in v]
1087 items = [v for v in values if '.' in v]
1088 if len(items) > 1 or items and sections:
1088 if len(items) > 1 or items and sections:
1089 raise util.Abort(_('only one config item permitted'))
1089 raise util.Abort(_('only one config item permitted'))
1090 for section, name, value in ui.walkconfig(untrusted=untrusted):
1090 for section, name, value in ui.walkconfig(untrusted=untrusted):
1091 sectname = section + '.' + name
1091 sectname = section + '.' + name
1092 if values:
1092 if values:
1093 for v in values:
1093 for v in values:
1094 if v == section:
1094 if v == section:
1095 ui.debug('%s: ' %
1095 ui.debug('%s: ' %
1096 ui.configsource(section, name, untrusted))
1096 ui.configsource(section, name, untrusted))
1097 ui.write('%s=%s\n' % (sectname, value))
1097 ui.write('%s=%s\n' % (sectname, value))
1098 elif v == sectname:
1098 elif v == sectname:
1099 ui.debug('%s: ' %
1099 ui.debug('%s: ' %
1100 ui.configsource(section, name, untrusted))
1100 ui.configsource(section, name, untrusted))
1101 ui.write(value, '\n')
1101 ui.write(value, '\n')
1102 else:
1102 else:
1103 ui.debug('%s: ' %
1103 ui.debug('%s: ' %
1104 ui.configsource(section, name, untrusted))
1104 ui.configsource(section, name, untrusted))
1105 ui.write('%s=%s\n' % (sectname, value))
1105 ui.write('%s=%s\n' % (sectname, value))
1106
1106
1107 def debugpushkey(ui, repopath, namespace, *keyinfo):
1107 def debugpushkey(ui, repopath, namespace, *keyinfo):
1108 '''access the pushkey key/value protocol
1108 '''access the pushkey key/value protocol
1109
1109
1110 With two args, list the keys in the given namespace.
1110 With two args, list the keys in the given namespace.
1111
1111
1112 With five args, set a key to new if it currently is set to old.
1112 With five args, set a key to new if it currently is set to old.
1113 Reports success or failure.
1113 Reports success or failure.
1114 '''
1114 '''
1115
1115
1116 target = hg.repository(ui, repopath)
1116 target = hg.repository(ui, repopath)
1117 if keyinfo:
1117 if keyinfo:
1118 key, old, new = keyinfo
1118 key, old, new = keyinfo
1119 r = target.pushkey(namespace, key, old, new)
1119 r = target.pushkey(namespace, key, old, new)
1120 ui.status(str(r) + '\n')
1120 ui.status(str(r) + '\n')
1121 return not r
1121 return not r
1122 else:
1122 else:
1123 for k, v in target.listkeys(namespace).iteritems():
1123 for k, v in target.listkeys(namespace).iteritems():
1124 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1124 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1125 v.encode('string-escape')))
1125 v.encode('string-escape')))
1126
1126
1127 def debugrevspec(ui, repo, expr):
1127 def debugrevspec(ui, repo, expr):
1128 '''parse and apply a revision specification'''
1128 '''parse and apply a revision specification'''
1129 if ui.verbose:
1129 if ui.verbose:
1130 tree = revset.parse(expr)
1130 tree = revset.parse(expr)
1131 ui.note(tree, "\n")
1131 ui.note(tree, "\n")
1132 func = revset.match(expr)
1132 func = revset.match(expr)
1133 for c in func(repo, range(len(repo))):
1133 for c in func(repo, range(len(repo))):
1134 ui.write("%s\n" % c)
1134 ui.write("%s\n" % c)
1135
1135
1136 def debugsetparents(ui, repo, rev1, rev2=None):
1136 def debugsetparents(ui, repo, rev1, rev2=None):
1137 """manually set the parents of the current working directory
1137 """manually set the parents of the current working directory
1138
1138
1139 This is useful for writing repository conversion tools, but should
1139 This is useful for writing repository conversion tools, but should
1140 be used with care.
1140 be used with care.
1141
1141
1142 Returns 0 on success.
1142 Returns 0 on success.
1143 """
1143 """
1144
1144
1145 r1 = cmdutil.revsingle(repo, rev1).node()
1145 r1 = cmdutil.revsingle(repo, rev1).node()
1146 r2 = cmdutil.revsingle(repo, rev2, 'null').node()
1146 r2 = cmdutil.revsingle(repo, rev2, 'null').node()
1147
1147
1148 wlock = repo.wlock()
1148 wlock = repo.wlock()
1149 try:
1149 try:
1150 repo.dirstate.setparents(r1, r2)
1150 repo.dirstate.setparents(r1, r2)
1151 finally:
1151 finally:
1152 wlock.release()
1152 wlock.release()
1153
1153
1154 def debugstate(ui, repo, nodates=None):
1154 def debugstate(ui, repo, nodates=None):
1155 """show the contents of the current dirstate"""
1155 """show the contents of the current dirstate"""
1156 timestr = ""
1156 timestr = ""
1157 showdate = not nodates
1157 showdate = not nodates
1158 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1158 for file_, ent in sorted(repo.dirstate._map.iteritems()):
1159 if showdate:
1159 if showdate:
1160 if ent[3] == -1:
1160 if ent[3] == -1:
1161 # Pad or slice to locale representation
1161 # Pad or slice to locale representation
1162 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1162 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
1163 time.localtime(0)))
1163 time.localtime(0)))
1164 timestr = 'unset'
1164 timestr = 'unset'
1165 timestr = (timestr[:locale_len] +
1165 timestr = (timestr[:locale_len] +
1166 ' ' * (locale_len - len(timestr)))
1166 ' ' * (locale_len - len(timestr)))
1167 else:
1167 else:
1168 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1168 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
1169 time.localtime(ent[3]))
1169 time.localtime(ent[3]))
1170 if ent[1] & 020000:
1170 if ent[1] & 020000:
1171 mode = 'lnk'
1171 mode = 'lnk'
1172 else:
1172 else:
1173 mode = '%3o' % (ent[1] & 0777)
1173 mode = '%3o' % (ent[1] & 0777)
1174 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1174 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
1175 for f in repo.dirstate.copies():
1175 for f in repo.dirstate.copies():
1176 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1176 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
1177
1177
1178 def debugsub(ui, repo, rev=None):
1178 def debugsub(ui, repo, rev=None):
1179 ctx = cmdutil.revsingle(repo, rev, None)
1179 ctx = cmdutil.revsingle(repo, rev, None)
1180 for k, v in sorted(ctx.substate.items()):
1180 for k, v in sorted(ctx.substate.items()):
1181 ui.write('path %s\n' % k)
1181 ui.write('path %s\n' % k)
1182 ui.write(' source %s\n' % v[0])
1182 ui.write(' source %s\n' % v[0])
1183 ui.write(' revision %s\n' % v[1])
1183 ui.write(' revision %s\n' % v[1])
1184
1184
1185 def debugdag(ui, repo, file_=None, *revs, **opts):
1185 def debugdag(ui, repo, file_=None, *revs, **opts):
1186 """format the changelog or an index DAG as a concise textual description
1186 """format the changelog or an index DAG as a concise textual description
1187
1187
1188 If you pass a revlog index, the revlog's DAG is emitted. If you list
1188 If you pass a revlog index, the revlog's DAG is emitted. If you list
1189 revision numbers, they get labelled in the output as rN.
1189 revision numbers, they get labelled in the output as rN.
1190
1190
1191 Otherwise, the changelog DAG of the current repo is emitted.
1191 Otherwise, the changelog DAG of the current repo is emitted.
1192 """
1192 """
1193 spaces = opts.get('spaces')
1193 spaces = opts.get('spaces')
1194 dots = opts.get('dots')
1194 dots = opts.get('dots')
1195 if file_:
1195 if file_:
1196 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1196 rlog = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1197 revs = set((int(r) for r in revs))
1197 revs = set((int(r) for r in revs))
1198 def events():
1198 def events():
1199 for r in rlog:
1199 for r in rlog:
1200 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1200 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1201 if r in revs:
1201 if r in revs:
1202 yield 'l', (r, "r%i" % r)
1202 yield 'l', (r, "r%i" % r)
1203 elif repo:
1203 elif repo:
1204 cl = repo.changelog
1204 cl = repo.changelog
1205 tags = opts.get('tags')
1205 tags = opts.get('tags')
1206 branches = opts.get('branches')
1206 branches = opts.get('branches')
1207 if tags:
1207 if tags:
1208 labels = {}
1208 labels = {}
1209 for l, n in repo.tags().items():
1209 for l, n in repo.tags().items():
1210 labels.setdefault(cl.rev(n), []).append(l)
1210 labels.setdefault(cl.rev(n), []).append(l)
1211 def events():
1211 def events():
1212 b = "default"
1212 b = "default"
1213 for r in cl:
1213 for r in cl:
1214 if branches:
1214 if branches:
1215 newb = cl.read(cl.node(r))[5]['branch']
1215 newb = cl.read(cl.node(r))[5]['branch']
1216 if newb != b:
1216 if newb != b:
1217 yield 'a', newb
1217 yield 'a', newb
1218 b = newb
1218 b = newb
1219 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1219 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1220 if tags:
1220 if tags:
1221 ls = labels.get(r)
1221 ls = labels.get(r)
1222 if ls:
1222 if ls:
1223 for l in ls:
1223 for l in ls:
1224 yield 'l', (r, l)
1224 yield 'l', (r, l)
1225 else:
1225 else:
1226 raise util.Abort(_('need repo for changelog dag'))
1226 raise util.Abort(_('need repo for changelog dag'))
1227
1227
1228 for line in dagparser.dagtextlines(events(),
1228 for line in dagparser.dagtextlines(events(),
1229 addspaces=spaces,
1229 addspaces=spaces,
1230 wraplabels=True,
1230 wraplabels=True,
1231 wrapannotations=True,
1231 wrapannotations=True,
1232 wrapnonlinear=dots,
1232 wrapnonlinear=dots,
1233 usedots=dots,
1233 usedots=dots,
1234 maxlinewidth=70):
1234 maxlinewidth=70):
1235 ui.write(line)
1235 ui.write(line)
1236 ui.write("\n")
1236 ui.write("\n")
1237
1237
1238 def debugdata(ui, repo, file_, rev):
1238 def debugdata(ui, repo, file_, rev):
1239 """dump the contents of a data file revision"""
1239 """dump the contents of a data file revision"""
1240 r = None
1240 r = None
1241 if repo:
1241 if repo:
1242 filelog = repo.file(file_)
1242 filelog = repo.file(file_)
1243 if len(filelog):
1243 if len(filelog):
1244 r = filelog
1244 r = filelog
1245 if not r:
1245 if not r:
1246 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1246 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
1247 try:
1247 try:
1248 ui.write(r.revision(r.lookup(rev)))
1248 ui.write(r.revision(r.lookup(rev)))
1249 except KeyError:
1249 except KeyError:
1250 raise util.Abort(_('invalid revision identifier %s') % rev)
1250 raise util.Abort(_('invalid revision identifier %s') % rev)
1251
1251
1252 def debugdate(ui, date, range=None, **opts):
1252 def debugdate(ui, date, range=None, **opts):
1253 """parse and display a date"""
1253 """parse and display a date"""
1254 if opts["extended"]:
1254 if opts["extended"]:
1255 d = util.parsedate(date, util.extendeddateformats)
1255 d = util.parsedate(date, util.extendeddateformats)
1256 else:
1256 else:
1257 d = util.parsedate(date)
1257 d = util.parsedate(date)
1258 ui.write("internal: %s %s\n" % d)
1258 ui.write("internal: %s %s\n" % d)
1259 ui.write("standard: %s\n" % util.datestr(d))
1259 ui.write("standard: %s\n" % util.datestr(d))
1260 if range:
1260 if range:
1261 m = util.matchdate(range)
1261 m = util.matchdate(range)
1262 ui.write("match: %s\n" % m(d[0]))
1262 ui.write("match: %s\n" % m(d[0]))
1263
1263
1264 def debugindex(ui, repo, file_, **opts):
1264 def debugindex(ui, repo, file_, **opts):
1265 """dump the contents of an index file"""
1265 """dump the contents of an index file"""
1266 r = None
1266 r = None
1267 if repo:
1267 if repo:
1268 filelog = repo.file(file_)
1268 filelog = repo.file(file_)
1269 if len(filelog):
1269 if len(filelog):
1270 r = filelog
1270 r = filelog
1271
1271
1272 format = opts.get('format', 0)
1272 format = opts.get('format', 0)
1273 if format not in (0, 1):
1273 if format not in (0, 1):
1274 raise util.Abort("unknown format %d" % format)
1274 raise util.Abort("unknown format %d" % format)
1275
1275
1276 if not r:
1276 if not r:
1277 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1277 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1278
1278
1279 if format == 0:
1279 if format == 0:
1280 ui.write(" rev offset length base linkrev"
1280 ui.write(" rev offset length base linkrev"
1281 " nodeid p1 p2\n")
1281 " nodeid p1 p2\n")
1282 elif format == 1:
1282 elif format == 1:
1283 ui.write(" rev flag offset length"
1283 ui.write(" rev flag offset length"
1284 " size base link p1 p2 nodeid\n")
1284 " size base link p1 p2 nodeid\n")
1285
1285
1286 for i in r:
1286 for i in r:
1287 node = r.node(i)
1287 node = r.node(i)
1288 if format == 0:
1288 if format == 0:
1289 try:
1289 try:
1290 pp = r.parents(node)
1290 pp = r.parents(node)
1291 except:
1291 except:
1292 pp = [nullid, nullid]
1292 pp = [nullid, nullid]
1293 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1293 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1294 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1294 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
1295 short(node), short(pp[0]), short(pp[1])))
1295 short(node), short(pp[0]), short(pp[1])))
1296 elif format == 1:
1296 elif format == 1:
1297 pr = r.parentrevs(i)
1297 pr = r.parentrevs(i)
1298 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1298 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1299 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1299 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1300 r.base(i), r.linkrev(i), pr[0], pr[1], short(node)))
1300 r.base(i), r.linkrev(i), pr[0], pr[1], short(node)))
1301
1301
1302 def debugindexdot(ui, repo, file_):
1302 def debugindexdot(ui, repo, file_):
1303 """dump an index DAG as a graphviz dot file"""
1303 """dump an index DAG as a graphviz dot file"""
1304 r = None
1304 r = None
1305 if repo:
1305 if repo:
1306 filelog = repo.file(file_)
1306 filelog = repo.file(file_)
1307 if len(filelog):
1307 if len(filelog):
1308 r = filelog
1308 r = filelog
1309 if not r:
1309 if not r:
1310 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1310 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
1311 ui.write("digraph G {\n")
1311 ui.write("digraph G {\n")
1312 for i in r:
1312 for i in r:
1313 node = r.node(i)
1313 node = r.node(i)
1314 pp = r.parents(node)
1314 pp = r.parents(node)
1315 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1315 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1316 if pp[1] != nullid:
1316 if pp[1] != nullid:
1317 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1317 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1318 ui.write("}\n")
1318 ui.write("}\n")
1319
1319
1320 def debuginstall(ui):
1320 def debuginstall(ui):
1321 '''test Mercurial installation
1321 '''test Mercurial installation
1322
1322
1323 Returns 0 on success.
1323 Returns 0 on success.
1324 '''
1324 '''
1325
1325
1326 def writetemp(contents):
1326 def writetemp(contents):
1327 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1327 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1328 f = os.fdopen(fd, "wb")
1328 f = os.fdopen(fd, "wb")
1329 f.write(contents)
1329 f.write(contents)
1330 f.close()
1330 f.close()
1331 return name
1331 return name
1332
1332
1333 problems = 0
1333 problems = 0
1334
1334
1335 # encoding
1335 # encoding
1336 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1336 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1337 try:
1337 try:
1338 encoding.fromlocal("test")
1338 encoding.fromlocal("test")
1339 except util.Abort, inst:
1339 except util.Abort, inst:
1340 ui.write(" %s\n" % inst)
1340 ui.write(" %s\n" % inst)
1341 ui.write(_(" (check that your locale is properly set)\n"))
1341 ui.write(_(" (check that your locale is properly set)\n"))
1342 problems += 1
1342 problems += 1
1343
1343
1344 # compiled modules
1344 # compiled modules
1345 ui.status(_("Checking installed modules (%s)...\n")
1345 ui.status(_("Checking installed modules (%s)...\n")
1346 % os.path.dirname(__file__))
1346 % os.path.dirname(__file__))
1347 try:
1347 try:
1348 import bdiff, mpatch, base85, osutil
1348 import bdiff, mpatch, base85, osutil
1349 except Exception, inst:
1349 except Exception, inst:
1350 ui.write(" %s\n" % inst)
1350 ui.write(" %s\n" % inst)
1351 ui.write(_(" One or more extensions could not be found"))
1351 ui.write(_(" One or more extensions could not be found"))
1352 ui.write(_(" (check that you compiled the extensions)\n"))
1352 ui.write(_(" (check that you compiled the extensions)\n"))
1353 problems += 1
1353 problems += 1
1354
1354
1355 # templates
1355 # templates
1356 ui.status(_("Checking templates...\n"))
1356 ui.status(_("Checking templates...\n"))
1357 try:
1357 try:
1358 import templater
1358 import templater
1359 templater.templater(templater.templatepath("map-cmdline.default"))
1359 templater.templater(templater.templatepath("map-cmdline.default"))
1360 except Exception, inst:
1360 except Exception, inst:
1361 ui.write(" %s\n" % inst)
1361 ui.write(" %s\n" % inst)
1362 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1362 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1363 problems += 1
1363 problems += 1
1364
1364
1365 # patch
1365 # patch
1366 ui.status(_("Checking patch...\n"))
1366 ui.status(_("Checking patch...\n"))
1367 patchproblems = 0
1367 patchproblems = 0
1368 a = "1\n2\n3\n4\n"
1368 a = "1\n2\n3\n4\n"
1369 b = "1\n2\n3\ninsert\n4\n"
1369 b = "1\n2\n3\ninsert\n4\n"
1370 fa = writetemp(a)
1370 fa = writetemp(a)
1371 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1371 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
1372 os.path.basename(fa))
1372 os.path.basename(fa))
1373 fd = writetemp(d)
1373 fd = writetemp(d)
1374
1374
1375 files = {}
1375 files = {}
1376 try:
1376 try:
1377 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1377 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
1378 except util.Abort, e:
1378 except util.Abort, e:
1379 ui.write(_(" patch call failed:\n"))
1379 ui.write(_(" patch call failed:\n"))
1380 ui.write(" " + str(e) + "\n")
1380 ui.write(" " + str(e) + "\n")
1381 patchproblems += 1
1381 patchproblems += 1
1382 else:
1382 else:
1383 if list(files) != [os.path.basename(fa)]:
1383 if list(files) != [os.path.basename(fa)]:
1384 ui.write(_(" unexpected patch output!\n"))
1384 ui.write(_(" unexpected patch output!\n"))
1385 patchproblems += 1
1385 patchproblems += 1
1386 a = open(fa).read()
1386 a = open(fa).read()
1387 if a != b:
1387 if a != b:
1388 ui.write(_(" patch test failed!\n"))
1388 ui.write(_(" patch test failed!\n"))
1389 patchproblems += 1
1389 patchproblems += 1
1390
1390
1391 if patchproblems:
1391 if patchproblems:
1392 if ui.config('ui', 'patch'):
1392 if ui.config('ui', 'patch'):
1393 ui.write(_(" (Current patch tool may be incompatible with patch,"
1393 ui.write(_(" (Current patch tool may be incompatible with patch,"
1394 " or misconfigured. Please check your configuration"
1394 " or misconfigured. Please check your configuration"
1395 " file)\n"))
1395 " file)\n"))
1396 else:
1396 else:
1397 ui.write(_(" Internal patcher failure, please report this error"
1397 ui.write(_(" Internal patcher failure, please report this error"
1398 " to http://mercurial.selenic.com/wiki/BugTracker\n"))
1398 " to http://mercurial.selenic.com/wiki/BugTracker\n"))
1399 problems += patchproblems
1399 problems += patchproblems
1400
1400
1401 os.unlink(fa)
1401 os.unlink(fa)
1402 os.unlink(fd)
1402 os.unlink(fd)
1403
1403
1404 # editor
1404 # editor
1405 ui.status(_("Checking commit editor...\n"))
1405 ui.status(_("Checking commit editor...\n"))
1406 editor = ui.geteditor()
1406 editor = ui.geteditor()
1407 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1407 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
1408 if not cmdpath:
1408 if not cmdpath:
1409 if editor == 'vi':
1409 if editor == 'vi':
1410 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1410 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1411 ui.write(_(" (specify a commit editor in your configuration"
1411 ui.write(_(" (specify a commit editor in your configuration"
1412 " file)\n"))
1412 " file)\n"))
1413 else:
1413 else:
1414 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1414 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1415 ui.write(_(" (specify a commit editor in your configuration"
1415 ui.write(_(" (specify a commit editor in your configuration"
1416 " file)\n"))
1416 " file)\n"))
1417 problems += 1
1417 problems += 1
1418
1418
1419 # check username
1419 # check username
1420 ui.status(_("Checking username...\n"))
1420 ui.status(_("Checking username...\n"))
1421 try:
1421 try:
1422 ui.username()
1422 ui.username()
1423 except util.Abort, e:
1423 except util.Abort, e:
1424 ui.write(" %s\n" % e)
1424 ui.write(" %s\n" % e)
1425 ui.write(_(" (specify a username in your configuration file)\n"))
1425 ui.write(_(" (specify a username in your configuration file)\n"))
1426 problems += 1
1426 problems += 1
1427
1427
1428 if not problems:
1428 if not problems:
1429 ui.status(_("No problems detected\n"))
1429 ui.status(_("No problems detected\n"))
1430 else:
1430 else:
1431 ui.write(_("%s problems detected,"
1431 ui.write(_("%s problems detected,"
1432 " please check your install!\n") % problems)
1432 " please check your install!\n") % problems)
1433
1433
1434 return problems
1434 return problems
1435
1435
1436 def debugrename(ui, repo, file1, *pats, **opts):
1436 def debugrename(ui, repo, file1, *pats, **opts):
1437 """dump rename information"""
1437 """dump rename information"""
1438
1438
1439 ctx = cmdutil.revsingle(repo, opts.get('rev'))
1439 ctx = cmdutil.revsingle(repo, opts.get('rev'))
1440 m = cmdutil.match(repo, (file1,) + pats, opts)
1440 m = cmdutil.match(repo, (file1,) + pats, opts)
1441 for abs in ctx.walk(m):
1441 for abs in ctx.walk(m):
1442 fctx = ctx[abs]
1442 fctx = ctx[abs]
1443 o = fctx.filelog().renamed(fctx.filenode())
1443 o = fctx.filelog().renamed(fctx.filenode())
1444 rel = m.rel(abs)
1444 rel = m.rel(abs)
1445 if o:
1445 if o:
1446 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1446 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1447 else:
1447 else:
1448 ui.write(_("%s not renamed\n") % rel)
1448 ui.write(_("%s not renamed\n") % rel)
1449
1449
1450 def debugwalk(ui, repo, *pats, **opts):
1450 def debugwalk(ui, repo, *pats, **opts):
1451 """show how files match on given patterns"""
1451 """show how files match on given patterns"""
1452 m = cmdutil.match(repo, pats, opts)
1452 m = cmdutil.match(repo, pats, opts)
1453 items = list(repo.walk(m))
1453 items = list(repo.walk(m))
1454 if not items:
1454 if not items:
1455 return
1455 return
1456 fmt = 'f %%-%ds %%-%ds %%s' % (
1456 fmt = 'f %%-%ds %%-%ds %%s' % (
1457 max([len(abs) for abs in items]),
1457 max([len(abs) for abs in items]),
1458 max([len(m.rel(abs)) for abs in items]))
1458 max([len(m.rel(abs)) for abs in items]))
1459 for abs in items:
1459 for abs in items:
1460 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1460 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1461 ui.write("%s\n" % line.rstrip())
1461 ui.write("%s\n" % line.rstrip())
1462
1462
1463 def diff(ui, repo, *pats, **opts):
1463 def diff(ui, repo, *pats, **opts):
1464 """diff repository (or selected files)
1464 """diff repository (or selected files)
1465
1465
1466 Show differences between revisions for the specified files.
1466 Show differences between revisions for the specified files.
1467
1467
1468 Differences between files are shown using the unified diff format.
1468 Differences between files are shown using the unified diff format.
1469
1469
1470 .. note::
1470 .. note::
1471 diff may generate unexpected results for merges, as it will
1471 diff may generate unexpected results for merges, as it will
1472 default to comparing against the working directory's first
1472 default to comparing against the working directory's first
1473 parent changeset if no revisions are specified.
1473 parent changeset if no revisions are specified.
1474
1474
1475 When two revision arguments are given, then changes are shown
1475 When two revision arguments are given, then changes are shown
1476 between those revisions. If only one revision is specified then
1476 between those revisions. If only one revision is specified then
1477 that revision is compared to the working directory, and, when no
1477 that revision is compared to the working directory, and, when no
1478 revisions are specified, the working directory files are compared
1478 revisions are specified, the working directory files are compared
1479 to its parent.
1479 to its parent.
1480
1480
1481 Alternatively you can specify -c/--change with a revision to see
1481 Alternatively you can specify -c/--change with a revision to see
1482 the changes in that changeset relative to its first parent.
1482 the changes in that changeset relative to its first parent.
1483
1483
1484 Without the -a/--text option, diff will avoid generating diffs of
1484 Without the -a/--text option, diff will avoid generating diffs of
1485 files it detects as binary. With -a, diff will generate a diff
1485 files it detects as binary. With -a, diff will generate a diff
1486 anyway, probably with undesirable results.
1486 anyway, probably with undesirable results.
1487
1487
1488 Use the -g/--git option to generate diffs in the git extended diff
1488 Use the -g/--git option to generate diffs in the git extended diff
1489 format. For more information, read :hg:`help diffs`.
1489 format. For more information, read :hg:`help diffs`.
1490
1490
1491 Returns 0 on success.
1491 Returns 0 on success.
1492 """
1492 """
1493
1493
1494 revs = opts.get('rev')
1494 revs = opts.get('rev')
1495 change = opts.get('change')
1495 change = opts.get('change')
1496 stat = opts.get('stat')
1496 stat = opts.get('stat')
1497 reverse = opts.get('reverse')
1497 reverse = opts.get('reverse')
1498
1498
1499 if revs and change:
1499 if revs and change:
1500 msg = _('cannot specify --rev and --change at the same time')
1500 msg = _('cannot specify --rev and --change at the same time')
1501 raise util.Abort(msg)
1501 raise util.Abort(msg)
1502 elif change:
1502 elif change:
1503 node2 = repo.lookup(change)
1503 node2 = repo.lookup(change)
1504 node1 = repo[node2].parents()[0].node()
1504 node1 = repo[node2].parents()[0].node()
1505 else:
1505 else:
1506 node1, node2 = cmdutil.revpair(repo, revs)
1506 node1, node2 = cmdutil.revpair(repo, revs)
1507
1507
1508 if reverse:
1508 if reverse:
1509 node1, node2 = node2, node1
1509 node1, node2 = node2, node1
1510
1510
1511 diffopts = patch.diffopts(ui, opts)
1511 diffopts = patch.diffopts(ui, opts)
1512 m = cmdutil.match(repo, pats, opts)
1512 m = cmdutil.match(repo, pats, opts)
1513 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1513 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
1514 listsubrepos=opts.get('subrepos'))
1514 listsubrepos=opts.get('subrepos'))
1515
1515
1516 def export(ui, repo, *changesets, **opts):
1516 def export(ui, repo, *changesets, **opts):
1517 """dump the header and diffs for one or more changesets
1517 """dump the header and diffs for one or more changesets
1518
1518
1519 Print the changeset header and diffs for one or more revisions.
1519 Print the changeset header and diffs for one or more revisions.
1520
1520
1521 The information shown in the changeset header is: author, date,
1521 The information shown in the changeset header is: author, date,
1522 branch name (if non-default), changeset hash, parent(s) and commit
1522 branch name (if non-default), changeset hash, parent(s) and commit
1523 comment.
1523 comment.
1524
1524
1525 .. note::
1525 .. note::
1526 export may generate unexpected diff output for merge
1526 export may generate unexpected diff output for merge
1527 changesets, as it will compare the merge changeset against its
1527 changesets, as it will compare the merge changeset against its
1528 first parent only.
1528 first parent only.
1529
1529
1530 Output may be to a file, in which case the name of the file is
1530 Output may be to a file, in which case the name of the file is
1531 given using a format string. The formatting rules are as follows:
1531 given using a format string. The formatting rules are as follows:
1532
1532
1533 :``%%``: literal "%" character
1533 :``%%``: literal "%" character
1534 :``%H``: changeset hash (40 hexadecimal digits)
1534 :``%H``: changeset hash (40 hexadecimal digits)
1535 :``%N``: number of patches being generated
1535 :``%N``: number of patches being generated
1536 :``%R``: changeset revision number
1536 :``%R``: changeset revision number
1537 :``%b``: basename of the exporting repository
1537 :``%b``: basename of the exporting repository
1538 :``%h``: short-form changeset hash (12 hexadecimal digits)
1538 :``%h``: short-form changeset hash (12 hexadecimal digits)
1539 :``%n``: zero-padded sequence number, starting at 1
1539 :``%n``: zero-padded sequence number, starting at 1
1540 :``%r``: zero-padded changeset revision number
1540 :``%r``: zero-padded changeset revision number
1541
1541
1542 Without the -a/--text option, export will avoid generating diffs
1542 Without the -a/--text option, export will avoid generating diffs
1543 of files it detects as binary. With -a, export will generate a
1543 of files it detects as binary. With -a, export will generate a
1544 diff anyway, probably with undesirable results.
1544 diff anyway, probably with undesirable results.
1545
1545
1546 Use the -g/--git option to generate diffs in the git extended diff
1546 Use the -g/--git option to generate diffs in the git extended diff
1547 format. See :hg:`help diffs` for more information.
1547 format. See :hg:`help diffs` for more information.
1548
1548
1549 With the --switch-parent option, the diff will be against the
1549 With the --switch-parent option, the diff will be against the
1550 second parent. It can be useful to review a merge.
1550 second parent. It can be useful to review a merge.
1551
1551
1552 Returns 0 on success.
1552 Returns 0 on success.
1553 """
1553 """
1554 changesets += tuple(opts.get('rev', []))
1554 changesets += tuple(opts.get('rev', []))
1555 if not changesets:
1555 if not changesets:
1556 raise util.Abort(_("export requires at least one changeset"))
1556 raise util.Abort(_("export requires at least one changeset"))
1557 revs = cmdutil.revrange(repo, changesets)
1557 revs = cmdutil.revrange(repo, changesets)
1558 if len(revs) > 1:
1558 if len(revs) > 1:
1559 ui.note(_('exporting patches:\n'))
1559 ui.note(_('exporting patches:\n'))
1560 else:
1560 else:
1561 ui.note(_('exporting patch:\n'))
1561 ui.note(_('exporting patch:\n'))
1562 cmdutil.export(repo, revs, template=opts.get('output'),
1562 cmdutil.export(repo, revs, template=opts.get('output'),
1563 switch_parent=opts.get('switch_parent'),
1563 switch_parent=opts.get('switch_parent'),
1564 opts=patch.diffopts(ui, opts))
1564 opts=patch.diffopts(ui, opts))
1565
1565
1566 def forget(ui, repo, *pats, **opts):
1566 def forget(ui, repo, *pats, **opts):
1567 """forget the specified files on the next commit
1567 """forget the specified files on the next commit
1568
1568
1569 Mark the specified files so they will no longer be tracked
1569 Mark the specified files so they will no longer be tracked
1570 after the next commit.
1570 after the next commit.
1571
1571
1572 This only removes files from the current branch, not from the
1572 This only removes files from the current branch, not from the
1573 entire project history, and it does not delete them from the
1573 entire project history, and it does not delete them from the
1574 working directory.
1574 working directory.
1575
1575
1576 To undo a forget before the next commit, see :hg:`add`.
1576 To undo a forget before the next commit, see :hg:`add`.
1577
1577
1578 Returns 0 on success.
1578 Returns 0 on success.
1579 """
1579 """
1580
1580
1581 if not pats:
1581 if not pats:
1582 raise util.Abort(_('no files specified'))
1582 raise util.Abort(_('no files specified'))
1583
1583
1584 m = cmdutil.match(repo, pats, opts)
1584 m = cmdutil.match(repo, pats, opts)
1585 s = repo.status(match=m, clean=True)
1585 s = repo.status(match=m, clean=True)
1586 forget = sorted(s[0] + s[1] + s[3] + s[6])
1586 forget = sorted(s[0] + s[1] + s[3] + s[6])
1587 errs = 0
1587 errs = 0
1588
1588
1589 for f in m.files():
1589 for f in m.files():
1590 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1590 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1591 ui.warn(_('not removing %s: file is already untracked\n')
1591 ui.warn(_('not removing %s: file is already untracked\n')
1592 % m.rel(f))
1592 % m.rel(f))
1593 errs = 1
1593 errs = 1
1594
1594
1595 for f in forget:
1595 for f in forget:
1596 if ui.verbose or not m.exact(f):
1596 if ui.verbose or not m.exact(f):
1597 ui.status(_('removing %s\n') % m.rel(f))
1597 ui.status(_('removing %s\n') % m.rel(f))
1598
1598
1599 repo[None].remove(forget, unlink=False)
1599 repo[None].remove(forget, unlink=False)
1600 return errs
1600 return errs
1601
1601
1602 def grep(ui, repo, pattern, *pats, **opts):
1602 def grep(ui, repo, pattern, *pats, **opts):
1603 """search for a pattern in specified files and revisions
1603 """search for a pattern in specified files and revisions
1604
1604
1605 Search revisions of files for a regular expression.
1605 Search revisions of files for a regular expression.
1606
1606
1607 This command behaves differently than Unix grep. It only accepts
1607 This command behaves differently than Unix grep. It only accepts
1608 Python/Perl regexps. It searches repository history, not the
1608 Python/Perl regexps. It searches repository history, not the
1609 working directory. It always prints the revision number in which a
1609 working directory. It always prints the revision number in which a
1610 match appears.
1610 match appears.
1611
1611
1612 By default, grep only prints output for the first revision of a
1612 By default, grep only prints output for the first revision of a
1613 file in which it finds a match. To get it to print every revision
1613 file in which it finds a match. To get it to print every revision
1614 that contains a change in match status ("-" for a match that
1614 that contains a change in match status ("-" for a match that
1615 becomes a non-match, or "+" for a non-match that becomes a match),
1615 becomes a non-match, or "+" for a non-match that becomes a match),
1616 use the --all flag.
1616 use the --all flag.
1617
1617
1618 Returns 0 if a match is found, 1 otherwise.
1618 Returns 0 if a match is found, 1 otherwise.
1619 """
1619 """
1620 reflags = 0
1620 reflags = 0
1621 if opts.get('ignore_case'):
1621 if opts.get('ignore_case'):
1622 reflags |= re.I
1622 reflags |= re.I
1623 try:
1623 try:
1624 regexp = re.compile(pattern, reflags)
1624 regexp = re.compile(pattern, reflags)
1625 except re.error, inst:
1625 except re.error, inst:
1626 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1626 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1627 return 1
1627 return 1
1628 sep, eol = ':', '\n'
1628 sep, eol = ':', '\n'
1629 if opts.get('print0'):
1629 if opts.get('print0'):
1630 sep = eol = '\0'
1630 sep = eol = '\0'
1631
1631
1632 getfile = util.lrucachefunc(repo.file)
1632 getfile = util.lrucachefunc(repo.file)
1633
1633
1634 def matchlines(body):
1634 def matchlines(body):
1635 begin = 0
1635 begin = 0
1636 linenum = 0
1636 linenum = 0
1637 while True:
1637 while True:
1638 match = regexp.search(body, begin)
1638 match = regexp.search(body, begin)
1639 if not match:
1639 if not match:
1640 break
1640 break
1641 mstart, mend = match.span()
1641 mstart, mend = match.span()
1642 linenum += body.count('\n', begin, mstart) + 1
1642 linenum += body.count('\n', begin, mstart) + 1
1643 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1643 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1644 begin = body.find('\n', mend) + 1 or len(body)
1644 begin = body.find('\n', mend) + 1 or len(body)
1645 lend = begin - 1
1645 lend = begin - 1
1646 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1646 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1647
1647
1648 class linestate(object):
1648 class linestate(object):
1649 def __init__(self, line, linenum, colstart, colend):
1649 def __init__(self, line, linenum, colstart, colend):
1650 self.line = line
1650 self.line = line
1651 self.linenum = linenum
1651 self.linenum = linenum
1652 self.colstart = colstart
1652 self.colstart = colstart
1653 self.colend = colend
1653 self.colend = colend
1654
1654
1655 def __hash__(self):
1655 def __hash__(self):
1656 return hash((self.linenum, self.line))
1656 return hash((self.linenum, self.line))
1657
1657
1658 def __eq__(self, other):
1658 def __eq__(self, other):
1659 return self.line == other.line
1659 return self.line == other.line
1660
1660
1661 matches = {}
1661 matches = {}
1662 copies = {}
1662 copies = {}
1663 def grepbody(fn, rev, body):
1663 def grepbody(fn, rev, body):
1664 matches[rev].setdefault(fn, [])
1664 matches[rev].setdefault(fn, [])
1665 m = matches[rev][fn]
1665 m = matches[rev][fn]
1666 for lnum, cstart, cend, line in matchlines(body):
1666 for lnum, cstart, cend, line in matchlines(body):
1667 s = linestate(line, lnum, cstart, cend)
1667 s = linestate(line, lnum, cstart, cend)
1668 m.append(s)
1668 m.append(s)
1669
1669
1670 def difflinestates(a, b):
1670 def difflinestates(a, b):
1671 sm = difflib.SequenceMatcher(None, a, b)
1671 sm = difflib.SequenceMatcher(None, a, b)
1672 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1672 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1673 if tag == 'insert':
1673 if tag == 'insert':
1674 for i in xrange(blo, bhi):
1674 for i in xrange(blo, bhi):
1675 yield ('+', b[i])
1675 yield ('+', b[i])
1676 elif tag == 'delete':
1676 elif tag == 'delete':
1677 for i in xrange(alo, ahi):
1677 for i in xrange(alo, ahi):
1678 yield ('-', a[i])
1678 yield ('-', a[i])
1679 elif tag == 'replace':
1679 elif tag == 'replace':
1680 for i in xrange(alo, ahi):
1680 for i in xrange(alo, ahi):
1681 yield ('-', a[i])
1681 yield ('-', a[i])
1682 for i in xrange(blo, bhi):
1682 for i in xrange(blo, bhi):
1683 yield ('+', b[i])
1683 yield ('+', b[i])
1684
1684
1685 def display(fn, ctx, pstates, states):
1685 def display(fn, ctx, pstates, states):
1686 rev = ctx.rev()
1686 rev = ctx.rev()
1687 datefunc = ui.quiet and util.shortdate or util.datestr
1687 datefunc = ui.quiet and util.shortdate or util.datestr
1688 found = False
1688 found = False
1689 filerevmatches = {}
1689 filerevmatches = {}
1690 if opts.get('all'):
1690 if opts.get('all'):
1691 iter = difflinestates(pstates, states)
1691 iter = difflinestates(pstates, states)
1692 else:
1692 else:
1693 iter = [('', l) for l in states]
1693 iter = [('', l) for l in states]
1694 for change, l in iter:
1694 for change, l in iter:
1695 cols = [fn, str(rev)]
1695 cols = [fn, str(rev)]
1696 before, match, after = None, None, None
1696 before, match, after = None, None, None
1697 if opts.get('line_number'):
1697 if opts.get('line_number'):
1698 cols.append(str(l.linenum))
1698 cols.append(str(l.linenum))
1699 if opts.get('all'):
1699 if opts.get('all'):
1700 cols.append(change)
1700 cols.append(change)
1701 if opts.get('user'):
1701 if opts.get('user'):
1702 cols.append(ui.shortuser(ctx.user()))
1702 cols.append(ui.shortuser(ctx.user()))
1703 if opts.get('date'):
1703 if opts.get('date'):
1704 cols.append(datefunc(ctx.date()))
1704 cols.append(datefunc(ctx.date()))
1705 if opts.get('files_with_matches'):
1705 if opts.get('files_with_matches'):
1706 c = (fn, rev)
1706 c = (fn, rev)
1707 if c in filerevmatches:
1707 if c in filerevmatches:
1708 continue
1708 continue
1709 filerevmatches[c] = 1
1709 filerevmatches[c] = 1
1710 else:
1710 else:
1711 before = l.line[:l.colstart]
1711 before = l.line[:l.colstart]
1712 match = l.line[l.colstart:l.colend]
1712 match = l.line[l.colstart:l.colend]
1713 after = l.line[l.colend:]
1713 after = l.line[l.colend:]
1714 ui.write(sep.join(cols))
1714 ui.write(sep.join(cols))
1715 if before is not None:
1715 if before is not None:
1716 ui.write(sep + before)
1716 ui.write(sep + before)
1717 ui.write(match, label='grep.match')
1717 ui.write(match, label='grep.match')
1718 ui.write(after)
1718 ui.write(after)
1719 ui.write(eol)
1719 ui.write(eol)
1720 found = True
1720 found = True
1721 return found
1721 return found
1722
1722
1723 skip = {}
1723 skip = {}
1724 revfiles = {}
1724 revfiles = {}
1725 matchfn = cmdutil.match(repo, pats, opts)
1725 matchfn = cmdutil.match(repo, pats, opts)
1726 found = False
1726 found = False
1727 follow = opts.get('follow')
1727 follow = opts.get('follow')
1728
1728
1729 def prep(ctx, fns):
1729 def prep(ctx, fns):
1730 rev = ctx.rev()
1730 rev = ctx.rev()
1731 pctx = ctx.parents()[0]
1731 pctx = ctx.parents()[0]
1732 parent = pctx.rev()
1732 parent = pctx.rev()
1733 matches.setdefault(rev, {})
1733 matches.setdefault(rev, {})
1734 matches.setdefault(parent, {})
1734 matches.setdefault(parent, {})
1735 files = revfiles.setdefault(rev, [])
1735 files = revfiles.setdefault(rev, [])
1736 for fn in fns:
1736 for fn in fns:
1737 flog = getfile(fn)
1737 flog = getfile(fn)
1738 try:
1738 try:
1739 fnode = ctx.filenode(fn)
1739 fnode = ctx.filenode(fn)
1740 except error.LookupError:
1740 except error.LookupError:
1741 continue
1741 continue
1742
1742
1743 copied = flog.renamed(fnode)
1743 copied = flog.renamed(fnode)
1744 copy = follow and copied and copied[0]
1744 copy = follow and copied and copied[0]
1745 if copy:
1745 if copy:
1746 copies.setdefault(rev, {})[fn] = copy
1746 copies.setdefault(rev, {})[fn] = copy
1747 if fn in skip:
1747 if fn in skip:
1748 if copy:
1748 if copy:
1749 skip[copy] = True
1749 skip[copy] = True
1750 continue
1750 continue
1751 files.append(fn)
1751 files.append(fn)
1752
1752
1753 if fn not in matches[rev]:
1753 if fn not in matches[rev]:
1754 grepbody(fn, rev, flog.read(fnode))
1754 grepbody(fn, rev, flog.read(fnode))
1755
1755
1756 pfn = copy or fn
1756 pfn = copy or fn
1757 if pfn not in matches[parent]:
1757 if pfn not in matches[parent]:
1758 try:
1758 try:
1759 fnode = pctx.filenode(pfn)
1759 fnode = pctx.filenode(pfn)
1760 grepbody(pfn, parent, flog.read(fnode))
1760 grepbody(pfn, parent, flog.read(fnode))
1761 except error.LookupError:
1761 except error.LookupError:
1762 pass
1762 pass
1763
1763
1764 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1764 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
1765 rev = ctx.rev()
1765 rev = ctx.rev()
1766 parent = ctx.parents()[0].rev()
1766 parent = ctx.parents()[0].rev()
1767 for fn in sorted(revfiles.get(rev, [])):
1767 for fn in sorted(revfiles.get(rev, [])):
1768 states = matches[rev][fn]
1768 states = matches[rev][fn]
1769 copy = copies.get(rev, {}).get(fn)
1769 copy = copies.get(rev, {}).get(fn)
1770 if fn in skip:
1770 if fn in skip:
1771 if copy:
1771 if copy:
1772 skip[copy] = True
1772 skip[copy] = True
1773 continue
1773 continue
1774 pstates = matches.get(parent, {}).get(copy or fn, [])
1774 pstates = matches.get(parent, {}).get(copy or fn, [])
1775 if pstates or states:
1775 if pstates or states:
1776 r = display(fn, ctx, pstates, states)
1776 r = display(fn, ctx, pstates, states)
1777 found = found or r
1777 found = found or r
1778 if r and not opts.get('all'):
1778 if r and not opts.get('all'):
1779 skip[fn] = True
1779 skip[fn] = True
1780 if copy:
1780 if copy:
1781 skip[copy] = True
1781 skip[copy] = True
1782 del matches[rev]
1782 del matches[rev]
1783 del revfiles[rev]
1783 del revfiles[rev]
1784
1784
1785 return not found
1785 return not found
1786
1786
1787 def heads(ui, repo, *branchrevs, **opts):
1787 def heads(ui, repo, *branchrevs, **opts):
1788 """show current repository heads or show branch heads
1788 """show current repository heads or show branch heads
1789
1789
1790 With no arguments, show all repository branch heads.
1790 With no arguments, show all repository branch heads.
1791
1791
1792 Repository "heads" are changesets with no child changesets. They are
1792 Repository "heads" are changesets with no child changesets. They are
1793 where development generally takes place and are the usual targets
1793 where development generally takes place and are the usual targets
1794 for update and merge operations. Branch heads are changesets that have
1794 for update and merge operations. Branch heads are changesets that have
1795 no child changeset on the same branch.
1795 no child changeset on the same branch.
1796
1796
1797 If one or more REVs are given, only branch heads on the branches
1797 If one or more REVs are given, only branch heads on the branches
1798 associated with the specified changesets are shown.
1798 associated with the specified changesets are shown.
1799
1799
1800 If -c/--closed is specified, also show branch heads marked closed
1800 If -c/--closed is specified, also show branch heads marked closed
1801 (see :hg:`commit --close-branch`).
1801 (see :hg:`commit --close-branch`).
1802
1802
1803 If STARTREV is specified, only those heads that are descendants of
1803 If STARTREV is specified, only those heads that are descendants of
1804 STARTREV will be displayed.
1804 STARTREV will be displayed.
1805
1805
1806 If -t/--topo is specified, named branch mechanics will be ignored and only
1806 If -t/--topo is specified, named branch mechanics will be ignored and only
1807 changesets without children will be shown.
1807 changesets without children will be shown.
1808
1808
1809 Returns 0 if matching heads are found, 1 if not.
1809 Returns 0 if matching heads are found, 1 if not.
1810 """
1810 """
1811
1811
1812 start = None
1812 start = None
1813 if 'rev' in opts:
1813 if 'rev' in opts:
1814 start = cmdutil.revsingle(repo, opts['rev'], None).node()
1814 start = cmdutil.revsingle(repo, opts['rev'], None).node()
1815
1815
1816 if opts.get('topo'):
1816 if opts.get('topo'):
1817 heads = [repo[h] for h in repo.heads(start)]
1817 heads = [repo[h] for h in repo.heads(start)]
1818 else:
1818 else:
1819 heads = []
1819 heads = []
1820 for b, ls in repo.branchmap().iteritems():
1820 for b, ls in repo.branchmap().iteritems():
1821 if start is None:
1821 if start is None:
1822 heads += [repo[h] for h in ls]
1822 heads += [repo[h] for h in ls]
1823 continue
1823 continue
1824 startrev = repo.changelog.rev(start)
1824 startrev = repo.changelog.rev(start)
1825 descendants = set(repo.changelog.descendants(startrev))
1825 descendants = set(repo.changelog.descendants(startrev))
1826 descendants.add(startrev)
1826 descendants.add(startrev)
1827 rev = repo.changelog.rev
1827 rev = repo.changelog.rev
1828 heads += [repo[h] for h in ls if rev(h) in descendants]
1828 heads += [repo[h] for h in ls if rev(h) in descendants]
1829
1829
1830 if branchrevs:
1830 if branchrevs:
1831 branches = set(repo[br].branch() for br in branchrevs)
1831 branches = set(repo[br].branch() for br in branchrevs)
1832 heads = [h for h in heads if h.branch() in branches]
1832 heads = [h for h in heads if h.branch() in branches]
1833
1833
1834 if not opts.get('closed'):
1834 if not opts.get('closed'):
1835 heads = [h for h in heads if not h.extra().get('close')]
1835 heads = [h for h in heads if not h.extra().get('close')]
1836
1836
1837 if opts.get('active') and branchrevs:
1837 if opts.get('active') and branchrevs:
1838 dagheads = repo.heads(start)
1838 dagheads = repo.heads(start)
1839 heads = [h for h in heads if h.node() in dagheads]
1839 heads = [h for h in heads if h.node() in dagheads]
1840
1840
1841 if branchrevs:
1841 if branchrevs:
1842 haveheads = set(h.branch() for h in heads)
1842 haveheads = set(h.branch() for h in heads)
1843 if branches - haveheads:
1843 if branches - haveheads:
1844 headless = ', '.join(b for b in branches - haveheads)
1844 headless = ', '.join(b for b in branches - haveheads)
1845 msg = _('no open branch heads found on branches %s')
1845 msg = _('no open branch heads found on branches %s')
1846 if opts.get('rev'):
1846 if opts.get('rev'):
1847 msg += _(' (started at %s)' % opts['rev'])
1847 msg += _(' (started at %s)' % opts['rev'])
1848 ui.warn((msg + '\n') % headless)
1848 ui.warn((msg + '\n') % headless)
1849
1849
1850 if not heads:
1850 if not heads:
1851 return 1
1851 return 1
1852
1852
1853 heads = sorted(heads, key=lambda x: -x.rev())
1853 heads = sorted(heads, key=lambda x: -x.rev())
1854 displayer = cmdutil.show_changeset(ui, repo, opts)
1854 displayer = cmdutil.show_changeset(ui, repo, opts)
1855 for ctx in heads:
1855 for ctx in heads:
1856 displayer.show(ctx)
1856 displayer.show(ctx)
1857 displayer.close()
1857 displayer.close()
1858
1858
1859 def help_(ui, name=None, with_version=False, unknowncmd=False):
1859 def help_(ui, name=None, with_version=False, unknowncmd=False):
1860 """show help for a given topic or a help overview
1860 """show help for a given topic or a help overview
1861
1861
1862 With no arguments, print a list of commands with short help messages.
1862 With no arguments, print a list of commands with short help messages.
1863
1863
1864 Given a topic, extension, or command name, print help for that
1864 Given a topic, extension, or command name, print help for that
1865 topic.
1865 topic.
1866
1866
1867 Returns 0 if successful.
1867 Returns 0 if successful.
1868 """
1868 """
1869 option_lists = []
1869 option_lists = []
1870 textwidth = ui.termwidth() - 2
1870 textwidth = ui.termwidth() - 2
1871
1871
1872 def addglobalopts(aliases):
1872 def addglobalopts(aliases):
1873 if ui.verbose:
1873 if ui.verbose:
1874 option_lists.append((_("global options:"), globalopts))
1874 option_lists.append((_("global options:"), globalopts))
1875 if name == 'shortlist':
1875 if name == 'shortlist':
1876 option_lists.append((_('use "hg help" for the full list '
1876 option_lists.append((_('use "hg help" for the full list '
1877 'of commands'), ()))
1877 'of commands'), ()))
1878 else:
1878 else:
1879 if name == 'shortlist':
1879 if name == 'shortlist':
1880 msg = _('use "hg help" for the full list of commands '
1880 msg = _('use "hg help" for the full list of commands '
1881 'or "hg -v" for details')
1881 'or "hg -v" for details')
1882 elif aliases:
1882 elif aliases:
1883 msg = _('use "hg -v help%s" to show builtin aliases and '
1883 msg = _('use "hg -v help%s" to show builtin aliases and '
1884 'global options') % (name and " " + name or "")
1884 'global options') % (name and " " + name or "")
1885 else:
1885 else:
1886 msg = _('use "hg -v help %s" to show global options') % name
1886 msg = _('use "hg -v help %s" to show global options') % name
1887 option_lists.append((msg, ()))
1887 option_lists.append((msg, ()))
1888
1888
1889 def helpcmd(name):
1889 def helpcmd(name):
1890 if with_version:
1890 if with_version:
1891 version_(ui)
1891 version_(ui)
1892 ui.write('\n')
1892 ui.write('\n')
1893
1893
1894 try:
1894 try:
1895 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1895 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
1896 except error.AmbiguousCommand, inst:
1896 except error.AmbiguousCommand, inst:
1897 # py3k fix: except vars can't be used outside the scope of the
1897 # py3k fix: except vars can't be used outside the scope of the
1898 # except block, nor can be used inside a lambda. python issue4617
1898 # except block, nor can be used inside a lambda. python issue4617
1899 prefix = inst.args[0]
1899 prefix = inst.args[0]
1900 select = lambda c: c.lstrip('^').startswith(prefix)
1900 select = lambda c: c.lstrip('^').startswith(prefix)
1901 helplist(_('list of commands:\n\n'), select)
1901 helplist(_('list of commands:\n\n'), select)
1902 return
1902 return
1903
1903
1904 # check if it's an invalid alias and display its error if it is
1904 # check if it's an invalid alias and display its error if it is
1905 if getattr(entry[0], 'badalias', False):
1905 if getattr(entry[0], 'badalias', False):
1906 if not unknowncmd:
1906 if not unknowncmd:
1907 entry[0](ui)
1907 entry[0](ui)
1908 return
1908 return
1909
1909
1910 # synopsis
1910 # synopsis
1911 if len(entry) > 2:
1911 if len(entry) > 2:
1912 if entry[2].startswith('hg'):
1912 if entry[2].startswith('hg'):
1913 ui.write("%s\n" % entry[2])
1913 ui.write("%s\n" % entry[2])
1914 else:
1914 else:
1915 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1915 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
1916 else:
1916 else:
1917 ui.write('hg %s\n' % aliases[0])
1917 ui.write('hg %s\n' % aliases[0])
1918
1918
1919 # aliases
1919 # aliases
1920 if not ui.quiet and len(aliases) > 1:
1920 if not ui.quiet and len(aliases) > 1:
1921 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1921 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1922
1922
1923 # description
1923 # description
1924 doc = gettext(entry[0].__doc__)
1924 doc = gettext(entry[0].__doc__)
1925 if not doc:
1925 if not doc:
1926 doc = _("(no help text available)")
1926 doc = _("(no help text available)")
1927 if hasattr(entry[0], 'definition'): # aliased command
1927 if hasattr(entry[0], 'definition'): # aliased command
1928 if entry[0].definition.startswith('!'): # shell alias
1928 if entry[0].definition.startswith('!'): # shell alias
1929 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
1929 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
1930 else:
1930 else:
1931 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1931 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
1932 if ui.quiet:
1932 if ui.quiet:
1933 doc = doc.splitlines()[0]
1933 doc = doc.splitlines()[0]
1934 keep = ui.verbose and ['verbose'] or []
1934 keep = ui.verbose and ['verbose'] or []
1935 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1935 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
1936 ui.write("\n%s\n" % formatted)
1936 ui.write("\n%s\n" % formatted)
1937 if pruned:
1937 if pruned:
1938 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1938 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
1939
1939
1940 if not ui.quiet:
1940 if not ui.quiet:
1941 # options
1941 # options
1942 if entry[1]:
1942 if entry[1]:
1943 option_lists.append((_("options:\n"), entry[1]))
1943 option_lists.append((_("options:\n"), entry[1]))
1944
1944
1945 addglobalopts(False)
1945 addglobalopts(False)
1946
1946
1947 def helplist(header, select=None):
1947 def helplist(header, select=None):
1948 h = {}
1948 h = {}
1949 cmds = {}
1949 cmds = {}
1950 for c, e in table.iteritems():
1950 for c, e in table.iteritems():
1951 f = c.split("|", 1)[0]
1951 f = c.split("|", 1)[0]
1952 if select and not select(f):
1952 if select and not select(f):
1953 continue
1953 continue
1954 if (not select and name != 'shortlist' and
1954 if (not select and name != 'shortlist' and
1955 e[0].__module__ != __name__):
1955 e[0].__module__ != __name__):
1956 continue
1956 continue
1957 if name == "shortlist" and not f.startswith("^"):
1957 if name == "shortlist" and not f.startswith("^"):
1958 continue
1958 continue
1959 f = f.lstrip("^")
1959 f = f.lstrip("^")
1960 if not ui.debugflag and f.startswith("debug"):
1960 if not ui.debugflag and f.startswith("debug"):
1961 continue
1961 continue
1962 doc = e[0].__doc__
1962 doc = e[0].__doc__
1963 if doc and 'DEPRECATED' in doc and not ui.verbose:
1963 if doc and 'DEPRECATED' in doc and not ui.verbose:
1964 continue
1964 continue
1965 doc = gettext(doc)
1965 doc = gettext(doc)
1966 if not doc:
1966 if not doc:
1967 doc = _("(no help text available)")
1967 doc = _("(no help text available)")
1968 h[f] = doc.splitlines()[0].rstrip()
1968 h[f] = doc.splitlines()[0].rstrip()
1969 cmds[f] = c.lstrip("^")
1969 cmds[f] = c.lstrip("^")
1970
1970
1971 if not h:
1971 if not h:
1972 ui.status(_('no commands defined\n'))
1972 ui.status(_('no commands defined\n'))
1973 return
1973 return
1974
1974
1975 ui.status(header)
1975 ui.status(header)
1976 fns = sorted(h)
1976 fns = sorted(h)
1977 m = max(map(len, fns))
1977 m = max(map(len, fns))
1978 for f in fns:
1978 for f in fns:
1979 if ui.verbose:
1979 if ui.verbose:
1980 commands = cmds[f].replace("|",", ")
1980 commands = cmds[f].replace("|",", ")
1981 ui.write(" %s:\n %s\n"%(commands, h[f]))
1981 ui.write(" %s:\n %s\n"%(commands, h[f]))
1982 else:
1982 else:
1983 ui.write('%s\n' % (util.wrap(h[f], textwidth,
1983 ui.write('%s\n' % (util.wrap(h[f], textwidth,
1984 initindent=' %-*s ' % (m, f),
1984 initindent=' %-*s ' % (m, f),
1985 hangindent=' ' * (m + 4))))
1985 hangindent=' ' * (m + 4))))
1986
1986
1987 if not ui.quiet:
1987 if not ui.quiet:
1988 addglobalopts(True)
1988 addglobalopts(True)
1989
1989
1990 def helptopic(name):
1990 def helptopic(name):
1991 for names, header, doc in help.helptable:
1991 for names, header, doc in help.helptable:
1992 if name in names:
1992 if name in names:
1993 break
1993 break
1994 else:
1994 else:
1995 raise error.UnknownCommand(name)
1995 raise error.UnknownCommand(name)
1996
1996
1997 # description
1997 # description
1998 if not doc:
1998 if not doc:
1999 doc = _("(no help text available)")
1999 doc = _("(no help text available)")
2000 if hasattr(doc, '__call__'):
2000 if hasattr(doc, '__call__'):
2001 doc = doc()
2001 doc = doc()
2002
2002
2003 ui.write("%s\n\n" % header)
2003 ui.write("%s\n\n" % header)
2004 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2004 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2005
2005
2006 def helpext(name):
2006 def helpext(name):
2007 try:
2007 try:
2008 mod = extensions.find(name)
2008 mod = extensions.find(name)
2009 doc = gettext(mod.__doc__) or _('no help text available')
2009 doc = gettext(mod.__doc__) or _('no help text available')
2010 except KeyError:
2010 except KeyError:
2011 mod = None
2011 mod = None
2012 doc = extensions.disabledext(name)
2012 doc = extensions.disabledext(name)
2013 if not doc:
2013 if not doc:
2014 raise error.UnknownCommand(name)
2014 raise error.UnknownCommand(name)
2015
2015
2016 if '\n' not in doc:
2016 if '\n' not in doc:
2017 head, tail = doc, ""
2017 head, tail = doc, ""
2018 else:
2018 else:
2019 head, tail = doc.split('\n', 1)
2019 head, tail = doc.split('\n', 1)
2020 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2020 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2021 if tail:
2021 if tail:
2022 ui.write(minirst.format(tail, textwidth))
2022 ui.write(minirst.format(tail, textwidth))
2023 ui.status('\n\n')
2023 ui.status('\n\n')
2024
2024
2025 if mod:
2025 if mod:
2026 try:
2026 try:
2027 ct = mod.cmdtable
2027 ct = mod.cmdtable
2028 except AttributeError:
2028 except AttributeError:
2029 ct = {}
2029 ct = {}
2030 modcmds = set([c.split('|', 1)[0] for c in ct])
2030 modcmds = set([c.split('|', 1)[0] for c in ct])
2031 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2031 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2032 else:
2032 else:
2033 ui.write(_('use "hg help extensions" for information on enabling '
2033 ui.write(_('use "hg help extensions" for information on enabling '
2034 'extensions\n'))
2034 'extensions\n'))
2035
2035
2036 def helpextcmd(name):
2036 def helpextcmd(name):
2037 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2037 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2038 doc = gettext(mod.__doc__).splitlines()[0]
2038 doc = gettext(mod.__doc__).splitlines()[0]
2039
2039
2040 msg = help.listexts(_("'%s' is provided by the following "
2040 msg = help.listexts(_("'%s' is provided by the following "
2041 "extension:") % cmd, {ext: doc}, len(ext),
2041 "extension:") % cmd, {ext: doc}, len(ext),
2042 indent=4)
2042 indent=4)
2043 ui.write(minirst.format(msg, textwidth))
2043 ui.write(minirst.format(msg, textwidth))
2044 ui.write('\n\n')
2044 ui.write('\n\n')
2045 ui.write(_('use "hg help extensions" for information on enabling '
2045 ui.write(_('use "hg help extensions" for information on enabling '
2046 'extensions\n'))
2046 'extensions\n'))
2047
2047
2048 help.addtopichook('revsets', revset.makedoc)
2048 help.addtopichook('revsets', revset.makedoc)
2049
2049
2050 if name and name != 'shortlist':
2050 if name and name != 'shortlist':
2051 i = None
2051 i = None
2052 if unknowncmd:
2052 if unknowncmd:
2053 queries = (helpextcmd,)
2053 queries = (helpextcmd,)
2054 else:
2054 else:
2055 queries = (helptopic, helpcmd, helpext, helpextcmd)
2055 queries = (helptopic, helpcmd, helpext, helpextcmd)
2056 for f in queries:
2056 for f in queries:
2057 try:
2057 try:
2058 f(name)
2058 f(name)
2059 i = None
2059 i = None
2060 break
2060 break
2061 except error.UnknownCommand, inst:
2061 except error.UnknownCommand, inst:
2062 i = inst
2062 i = inst
2063 if i:
2063 if i:
2064 raise i
2064 raise i
2065
2065
2066 else:
2066 else:
2067 # program name
2067 # program name
2068 if ui.verbose or with_version:
2068 if ui.verbose or with_version:
2069 version_(ui)
2069 version_(ui)
2070 else:
2070 else:
2071 ui.status(_("Mercurial Distributed SCM\n"))
2071 ui.status(_("Mercurial Distributed SCM\n"))
2072 ui.status('\n')
2072 ui.status('\n')
2073
2073
2074 # list of commands
2074 # list of commands
2075 if name == "shortlist":
2075 if name == "shortlist":
2076 header = _('basic commands:\n\n')
2076 header = _('basic commands:\n\n')
2077 else:
2077 else:
2078 header = _('list of commands:\n\n')
2078 header = _('list of commands:\n\n')
2079
2079
2080 helplist(header)
2080 helplist(header)
2081 if name != 'shortlist':
2081 if name != 'shortlist':
2082 exts, maxlength = extensions.enabled()
2082 exts, maxlength = extensions.enabled()
2083 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2083 text = help.listexts(_('enabled extensions:'), exts, maxlength)
2084 if text:
2084 if text:
2085 ui.write("\n%s\n" % minirst.format(text, textwidth))
2085 ui.write("\n%s\n" % minirst.format(text, textwidth))
2086
2086
2087 # list all option lists
2087 # list all option lists
2088 opt_output = []
2088 opt_output = []
2089 multioccur = False
2089 multioccur = False
2090 for title, options in option_lists:
2090 for title, options in option_lists:
2091 opt_output.append(("\n%s" % title, None))
2091 opt_output.append(("\n%s" % title, None))
2092 for option in options:
2092 for option in options:
2093 if len(option) == 5:
2093 if len(option) == 5:
2094 shortopt, longopt, default, desc, optlabel = option
2094 shortopt, longopt, default, desc, optlabel = option
2095 else:
2095 else:
2096 shortopt, longopt, default, desc = option
2096 shortopt, longopt, default, desc = option
2097 optlabel = _("VALUE") # default label
2097 optlabel = _("VALUE") # default label
2098
2098
2099 if _("DEPRECATED") in desc and not ui.verbose:
2099 if _("DEPRECATED") in desc and not ui.verbose:
2100 continue
2100 continue
2101 if isinstance(default, list):
2101 if isinstance(default, list):
2102 numqualifier = " %s [+]" % optlabel
2102 numqualifier = " %s [+]" % optlabel
2103 multioccur = True
2103 multioccur = True
2104 elif (default is not None) and not isinstance(default, bool):
2104 elif (default is not None) and not isinstance(default, bool):
2105 numqualifier = " %s" % optlabel
2105 numqualifier = " %s" % optlabel
2106 else:
2106 else:
2107 numqualifier = ""
2107 numqualifier = ""
2108 opt_output.append(("%2s%s" %
2108 opt_output.append(("%2s%s" %
2109 (shortopt and "-%s" % shortopt,
2109 (shortopt and "-%s" % shortopt,
2110 longopt and " --%s%s" %
2110 longopt and " --%s%s" %
2111 (longopt, numqualifier)),
2111 (longopt, numqualifier)),
2112 "%s%s" % (desc,
2112 "%s%s" % (desc,
2113 default
2113 default
2114 and _(" (default: %s)") % default
2114 and _(" (default: %s)") % default
2115 or "")))
2115 or "")))
2116 if multioccur:
2116 if multioccur:
2117 msg = _("\n[+] marked option can be specified multiple times")
2117 msg = _("\n[+] marked option can be specified multiple times")
2118 if ui.verbose and name != 'shortlist':
2118 if ui.verbose and name != 'shortlist':
2119 opt_output.append((msg, None))
2119 opt_output.append((msg, None))
2120 else:
2120 else:
2121 opt_output.insert(-1, (msg, None))
2121 opt_output.insert(-1, (msg, None))
2122
2122
2123 if not name:
2123 if not name:
2124 ui.write(_("\nadditional help topics:\n\n"))
2124 ui.write(_("\nadditional help topics:\n\n"))
2125 topics = []
2125 topics = []
2126 for names, header, doc in help.helptable:
2126 for names, header, doc in help.helptable:
2127 topics.append((sorted(names, key=len, reverse=True)[0], header))
2127 topics.append((sorted(names, key=len, reverse=True)[0], header))
2128 topics_len = max([len(s[0]) for s in topics])
2128 topics_len = max([len(s[0]) for s in topics])
2129 for t, desc in topics:
2129 for t, desc in topics:
2130 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2130 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2131
2131
2132 if opt_output:
2132 if opt_output:
2133 colwidth = encoding.colwidth
2133 colwidth = encoding.colwidth
2134 # normalize: (opt or message, desc or None, width of opt)
2134 # normalize: (opt or message, desc or None, width of opt)
2135 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2135 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2136 for opt, desc in opt_output]
2136 for opt, desc in opt_output]
2137 hanging = max([e[2] for e in entries])
2137 hanging = max([e[2] for e in entries])
2138 for opt, desc, width in entries:
2138 for opt, desc, width in entries:
2139 if desc:
2139 if desc:
2140 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2140 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2141 hangindent = ' ' * (hanging + 3)
2141 hangindent = ' ' * (hanging + 3)
2142 ui.write('%s\n' % (util.wrap(desc, textwidth,
2142 ui.write('%s\n' % (util.wrap(desc, textwidth,
2143 initindent=initindent,
2143 initindent=initindent,
2144 hangindent=hangindent)))
2144 hangindent=hangindent)))
2145 else:
2145 else:
2146 ui.write("%s\n" % opt)
2146 ui.write("%s\n" % opt)
2147
2147
2148 def identify(ui, repo, source=None,
2148 def identify(ui, repo, source=None,
2149 rev=None, num=None, id=None, branch=None, tags=None):
2149 rev=None, num=None, id=None, branch=None, tags=None):
2150 """identify the working copy or specified revision
2150 """identify the working copy or specified revision
2151
2151
2152 With no revision, print a summary of the current state of the
2152 With no revision, print a summary of the current state of the
2153 repository.
2153 repository.
2154
2154
2155 Specifying a path to a repository root or Mercurial bundle will
2155 Specifying a path to a repository root or Mercurial bundle will
2156 cause lookup to operate on that repository/bundle.
2156 cause lookup to operate on that repository/bundle.
2157
2157
2158 This summary identifies the repository state using one or two
2158 This summary identifies the repository state using one or two
2159 parent hash identifiers, followed by a "+" if there are
2159 parent hash identifiers, followed by a "+" if there are
2160 uncommitted changes in the working directory, a list of tags for
2160 uncommitted changes in the working directory, a list of tags for
2161 this revision and a branch name for non-default branches.
2161 this revision and a branch name for non-default branches.
2162
2162
2163 Returns 0 if successful.
2163 Returns 0 if successful.
2164 """
2164 """
2165
2165
2166 if not repo and not source:
2166 if not repo and not source:
2167 raise util.Abort(_("there is no Mercurial repository here "
2167 raise util.Abort(_("there is no Mercurial repository here "
2168 "(.hg not found)"))
2168 "(.hg not found)"))
2169
2169
2170 hexfunc = ui.debugflag and hex or short
2170 hexfunc = ui.debugflag and hex or short
2171 default = not (num or id or branch or tags)
2171 default = not (num or id or branch or tags)
2172 output = []
2172 output = []
2173
2173
2174 revs = []
2174 revs = []
2175 if source:
2175 if source:
2176 source, branches = hg.parseurl(ui.expandpath(source))
2176 source, branches = hg.parseurl(ui.expandpath(source))
2177 repo = hg.repository(ui, source)
2177 repo = hg.repository(ui, source)
2178 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2178 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2179
2179
2180 if not repo.local():
2180 if not repo.local():
2181 if not rev and revs:
2181 if not rev and revs:
2182 rev = revs[0]
2182 rev = revs[0]
2183 if not rev:
2183 if not rev:
2184 rev = "tip"
2184 rev = "tip"
2185 if num or branch or tags:
2185 if num or branch or tags:
2186 raise util.Abort(
2186 raise util.Abort(
2187 "can't query remote revision number, branch, or tags")
2187 "can't query remote revision number, branch, or tags")
2188 output = [hexfunc(repo.lookup(rev))]
2188 output = [hexfunc(repo.lookup(rev))]
2189 elif not rev:
2189 elif not rev:
2190 ctx = repo[None]
2190 ctx = repo[None]
2191 parents = ctx.parents()
2191 parents = ctx.parents()
2192 changed = False
2192 changed = False
2193 if default or id or num:
2193 if default or id or num:
2194 changed = util.any(repo.status())
2194 changed = util.any(repo.status())
2195 if default or id:
2195 if default or id:
2196 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2196 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
2197 (changed) and "+" or "")]
2197 (changed) and "+" or "")]
2198 if num:
2198 if num:
2199 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2199 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
2200 (changed) and "+" or ""))
2200 (changed) and "+" or ""))
2201 else:
2201 else:
2202 ctx = cmdutil.revsingle(repo, rev)
2202 ctx = cmdutil.revsingle(repo, rev)
2203 if default or id:
2203 if default or id:
2204 output = [hexfunc(ctx.node())]
2204 output = [hexfunc(ctx.node())]
2205 if num:
2205 if num:
2206 output.append(str(ctx.rev()))
2206 output.append(str(ctx.rev()))
2207
2207
2208 if repo.local() and default and not ui.quiet:
2208 if repo.local() and default and not ui.quiet:
2209 b = ctx.branch()
2209 b = ctx.branch()
2210 if b != 'default':
2210 if b != 'default':
2211 output.append("(%s)" % b)
2211 output.append("(%s)" % b)
2212
2212
2213 # multiple tags for a single parent separated by '/'
2213 # multiple tags for a single parent separated by '/'
2214 t = "/".join(ctx.tags())
2214 t = "/".join(ctx.tags())
2215 if t:
2215 if t:
2216 output.append(t)
2216 output.append(t)
2217
2217
2218 if branch:
2218 if branch:
2219 output.append(ctx.branch())
2219 output.append(ctx.branch())
2220
2220
2221 if tags:
2221 if tags:
2222 output.extend(ctx.tags())
2222 output.extend(ctx.tags())
2223
2223
2224 ui.write("%s\n" % ' '.join(output))
2224 ui.write("%s\n" % ' '.join(output))
2225
2225
2226 def import_(ui, repo, patch1, *patches, **opts):
2226 def import_(ui, repo, patch1, *patches, **opts):
2227 """import an ordered set of patches
2227 """import an ordered set of patches
2228
2228
2229 Import a list of patches and commit them individually (unless
2229 Import a list of patches and commit them individually (unless
2230 --no-commit is specified).
2230 --no-commit is specified).
2231
2231
2232 If there are outstanding changes in the working directory, import
2232 If there are outstanding changes in the working directory, import
2233 will abort unless given the -f/--force flag.
2233 will abort unless given the -f/--force flag.
2234
2234
2235 You can import a patch straight from a mail message. Even patches
2235 You can import a patch straight from a mail message. Even patches
2236 as attachments work (to use the body part, it must have type
2236 as attachments work (to use the body part, it must have type
2237 text/plain or text/x-patch). From and Subject headers of email
2237 text/plain or text/x-patch). From and Subject headers of email
2238 message are used as default committer and commit message. All
2238 message are used as default committer and commit message. All
2239 text/plain body parts before first diff are added to commit
2239 text/plain body parts before first diff are added to commit
2240 message.
2240 message.
2241
2241
2242 If the imported patch was generated by :hg:`export`, user and
2242 If the imported patch was generated by :hg:`export`, user and
2243 description from patch override values from message headers and
2243 description from patch override values from message headers and
2244 body. Values given on command line with -m/--message and -u/--user
2244 body. Values given on command line with -m/--message and -u/--user
2245 override these.
2245 override these.
2246
2246
2247 If --exact is specified, import will set the working directory to
2247 If --exact is specified, import will set the working directory to
2248 the parent of each patch before applying it, and will abort if the
2248 the parent of each patch before applying it, and will abort if the
2249 resulting changeset has a different ID than the one recorded in
2249 resulting changeset has a different ID than the one recorded in
2250 the patch. This may happen due to character set problems or other
2250 the patch. This may happen due to character set problems or other
2251 deficiencies in the text patch format.
2251 deficiencies in the text patch format.
2252
2252
2253 With -s/--similarity, hg will attempt to discover renames and
2253 With -s/--similarity, hg will attempt to discover renames and
2254 copies in the patch in the same way as 'addremove'.
2254 copies in the patch in the same way as 'addremove'.
2255
2255
2256 To read a patch from standard input, use "-" as the patch name. If
2256 To read a patch from standard input, use "-" as the patch name. If
2257 a URL is specified, the patch will be downloaded from it.
2257 a URL is specified, the patch will be downloaded from it.
2258 See :hg:`help dates` for a list of formats valid for -d/--date.
2258 See :hg:`help dates` for a list of formats valid for -d/--date.
2259
2259
2260 Returns 0 on success.
2260 Returns 0 on success.
2261 """
2261 """
2262 patches = (patch1,) + patches
2262 patches = (patch1,) + patches
2263
2263
2264 date = opts.get('date')
2264 date = opts.get('date')
2265 if date:
2265 if date:
2266 opts['date'] = util.parsedate(date)
2266 opts['date'] = util.parsedate(date)
2267
2267
2268 try:
2268 try:
2269 sim = float(opts.get('similarity') or 0)
2269 sim = float(opts.get('similarity') or 0)
2270 except ValueError:
2270 except ValueError:
2271 raise util.Abort(_('similarity must be a number'))
2271 raise util.Abort(_('similarity must be a number'))
2272 if sim < 0 or sim > 100:
2272 if sim < 0 or sim > 100:
2273 raise util.Abort(_('similarity must be between 0 and 100'))
2273 raise util.Abort(_('similarity must be between 0 and 100'))
2274
2274
2275 if opts.get('exact') or not opts.get('force'):
2275 if opts.get('exact') or not opts.get('force'):
2276 cmdutil.bail_if_changed(repo)
2276 cmdutil.bail_if_changed(repo)
2277
2277
2278 d = opts["base"]
2278 d = opts["base"]
2279 strip = opts["strip"]
2279 strip = opts["strip"]
2280 wlock = lock = None
2280 wlock = lock = None
2281 msgs = []
2281 msgs = []
2282
2282
2283 def tryone(ui, hunk):
2283 def tryone(ui, hunk):
2284 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2284 tmpname, message, user, date, branch, nodeid, p1, p2 = \
2285 patch.extract(ui, hunk)
2285 patch.extract(ui, hunk)
2286
2286
2287 if not tmpname:
2287 if not tmpname:
2288 return None
2288 return None
2289 commitid = _('to working directory')
2289 commitid = _('to working directory')
2290
2290
2291 try:
2291 try:
2292 cmdline_message = cmdutil.logmessage(opts)
2292 cmdline_message = cmdutil.logmessage(opts)
2293 if cmdline_message:
2293 if cmdline_message:
2294 # pickup the cmdline msg
2294 # pickup the cmdline msg
2295 message = cmdline_message
2295 message = cmdline_message
2296 elif message:
2296 elif message:
2297 # pickup the patch msg
2297 # pickup the patch msg
2298 message = message.strip()
2298 message = message.strip()
2299 else:
2299 else:
2300 # launch the editor
2300 # launch the editor
2301 message = None
2301 message = None
2302 ui.debug('message:\n%s\n' % message)
2302 ui.debug('message:\n%s\n' % message)
2303
2303
2304 wp = repo.parents()
2304 wp = repo.parents()
2305 if opts.get('exact'):
2305 if opts.get('exact'):
2306 if not nodeid or not p1:
2306 if not nodeid or not p1:
2307 raise util.Abort(_('not a Mercurial patch'))
2307 raise util.Abort(_('not a Mercurial patch'))
2308 p1 = repo.lookup(p1)
2308 p1 = repo.lookup(p1)
2309 p2 = repo.lookup(p2 or hex(nullid))
2309 p2 = repo.lookup(p2 or hex(nullid))
2310
2310
2311 if p1 != wp[0].node():
2311 if p1 != wp[0].node():
2312 hg.clean(repo, p1)
2312 hg.clean(repo, p1)
2313 repo.dirstate.setparents(p1, p2)
2313 repo.dirstate.setparents(p1, p2)
2314 elif p2:
2314 elif p2:
2315 try:
2315 try:
2316 p1 = repo.lookup(p1)
2316 p1 = repo.lookup(p1)
2317 p2 = repo.lookup(p2)
2317 p2 = repo.lookup(p2)
2318 if p1 == wp[0].node():
2318 if p1 == wp[0].node():
2319 repo.dirstate.setparents(p1, p2)
2319 repo.dirstate.setparents(p1, p2)
2320 except error.RepoError:
2320 except error.RepoError:
2321 pass
2321 pass
2322 if opts.get('exact') or opts.get('import_branch'):
2322 if opts.get('exact') or opts.get('import_branch'):
2323 repo.dirstate.setbranch(branch or 'default')
2323 repo.dirstate.setbranch(branch or 'default')
2324
2324
2325 files = {}
2325 files = {}
2326 try:
2326 try:
2327 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2327 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2328 files=files, eolmode=None)
2328 files=files, eolmode=None)
2329 finally:
2329 finally:
2330 files = cmdutil.updatedir(ui, repo, files,
2330 files = cmdutil.updatedir(ui, repo, files,
2331 similarity=sim / 100.0)
2331 similarity=sim / 100.0)
2332 if opts.get('no_commit'):
2332 if opts.get('no_commit'):
2333 if message:
2333 if message:
2334 msgs.append(message)
2334 msgs.append(message)
2335 else:
2335 else:
2336 if opts.get('exact'):
2336 if opts.get('exact'):
2337 m = None
2337 m = None
2338 else:
2338 else:
2339 m = cmdutil.matchfiles(repo, files or [])
2339 m = cmdutil.matchfiles(repo, files or [])
2340 n = repo.commit(message, opts.get('user') or user,
2340 n = repo.commit(message, opts.get('user') or user,
2341 opts.get('date') or date, match=m,
2341 opts.get('date') or date, match=m,
2342 editor=cmdutil.commiteditor)
2342 editor=cmdutil.commiteditor)
2343 if opts.get('exact'):
2343 if opts.get('exact'):
2344 if hex(n) != nodeid:
2344 if hex(n) != nodeid:
2345 repo.rollback()
2345 repo.rollback()
2346 raise util.Abort(_('patch is damaged'
2346 raise util.Abort(_('patch is damaged'
2347 ' or loses information'))
2347 ' or loses information'))
2348 # Force a dirstate write so that the next transaction
2348 # Force a dirstate write so that the next transaction
2349 # backups an up-do-date file.
2349 # backups an up-do-date file.
2350 repo.dirstate.write()
2350 repo.dirstate.write()
2351 if n:
2351 if n:
2352 commitid = short(n)
2352 commitid = short(n)
2353
2353
2354 return commitid
2354 return commitid
2355 finally:
2355 finally:
2356 os.unlink(tmpname)
2356 os.unlink(tmpname)
2357
2357
2358 try:
2358 try:
2359 wlock = repo.wlock()
2359 wlock = repo.wlock()
2360 lock = repo.lock()
2360 lock = repo.lock()
2361 lastcommit = None
2361 lastcommit = None
2362 for p in patches:
2362 for p in patches:
2363 pf = os.path.join(d, p)
2363 pf = os.path.join(d, p)
2364
2364
2365 if pf == '-':
2365 if pf == '-':
2366 ui.status(_("applying patch from stdin\n"))
2366 ui.status(_("applying patch from stdin\n"))
2367 pf = sys.stdin
2367 pf = sys.stdin
2368 else:
2368 else:
2369 ui.status(_("applying %s\n") % p)
2369 ui.status(_("applying %s\n") % p)
2370 pf = url.open(ui, pf)
2370 pf = url.open(ui, pf)
2371
2371
2372 haspatch = False
2372 haspatch = False
2373 for hunk in patch.split(pf):
2373 for hunk in patch.split(pf):
2374 commitid = tryone(ui, hunk)
2374 commitid = tryone(ui, hunk)
2375 if commitid:
2375 if commitid:
2376 haspatch = True
2376 haspatch = True
2377 if lastcommit:
2377 if lastcommit:
2378 ui.status(_('applied %s\n') % lastcommit)
2378 ui.status(_('applied %s\n') % lastcommit)
2379 lastcommit = commitid
2379 lastcommit = commitid
2380
2380
2381 if not haspatch:
2381 if not haspatch:
2382 raise util.Abort(_('no diffs found'))
2382 raise util.Abort(_('no diffs found'))
2383
2383
2384 if msgs:
2384 if msgs:
2385 repo.opener('last-message.txt', 'wb').write('\n* * *\n'.join(msgs))
2385 repo.opener('last-message.txt', 'wb').write('\n* * *\n'.join(msgs))
2386 finally:
2386 finally:
2387 release(lock, wlock)
2387 release(lock, wlock)
2388
2388
2389 def incoming(ui, repo, source="default", **opts):
2389 def incoming(ui, repo, source="default", **opts):
2390 """show new changesets found in source
2390 """show new changesets found in source
2391
2391
2392 Show new changesets found in the specified path/URL or the default
2392 Show new changesets found in the specified path/URL or the default
2393 pull location. These are the changesets that would have been pulled
2393 pull location. These are the changesets that would have been pulled
2394 if a pull at the time you issued this command.
2394 if a pull at the time you issued this command.
2395
2395
2396 For remote repository, using --bundle avoids downloading the
2396 For remote repository, using --bundle avoids downloading the
2397 changesets twice if the incoming is followed by a pull.
2397 changesets twice if the incoming is followed by a pull.
2398
2398
2399 See pull for valid source format details.
2399 See pull for valid source format details.
2400
2400
2401 Returns 0 if there are incoming changes, 1 otherwise.
2401 Returns 0 if there are incoming changes, 1 otherwise.
2402 """
2402 """
2403 if opts.get('bundle') and opts.get('subrepos'):
2403 if opts.get('bundle') and opts.get('subrepos'):
2404 raise util.Abort(_('cannot combine --bundle and --subrepos'))
2404 raise util.Abort(_('cannot combine --bundle and --subrepos'))
2405
2405
2406 ret = hg.incoming(ui, repo, source, opts)
2406 ret = hg.incoming(ui, repo, source, opts)
2407 return ret
2407 return ret
2408
2408
2409 def init(ui, dest=".", **opts):
2409 def init(ui, dest=".", **opts):
2410 """create a new repository in the given directory
2410 """create a new repository in the given directory
2411
2411
2412 Initialize a new repository in the given directory. If the given
2412 Initialize a new repository in the given directory. If the given
2413 directory does not exist, it will be created.
2413 directory does not exist, it will be created.
2414
2414
2415 If no directory is given, the current directory is used.
2415 If no directory is given, the current directory is used.
2416
2416
2417 It is possible to specify an ``ssh://`` URL as the destination.
2417 It is possible to specify an ``ssh://`` URL as the destination.
2418 See :hg:`help urls` for more information.
2418 See :hg:`help urls` for more information.
2419
2419
2420 Returns 0 on success.
2420 Returns 0 on success.
2421 """
2421 """
2422 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
2422 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
2423
2423
2424 def locate(ui, repo, *pats, **opts):
2424 def locate(ui, repo, *pats, **opts):
2425 """locate files matching specific patterns
2425 """locate files matching specific patterns
2426
2426
2427 Print files under Mercurial control in the working directory whose
2427 Print files under Mercurial control in the working directory whose
2428 names match the given patterns.
2428 names match the given patterns.
2429
2429
2430 By default, this command searches all directories in the working
2430 By default, this command searches all directories in the working
2431 directory. To search just the current directory and its
2431 directory. To search just the current directory and its
2432 subdirectories, use "--include .".
2432 subdirectories, use "--include .".
2433
2433
2434 If no patterns are given to match, this command prints the names
2434 If no patterns are given to match, this command prints the names
2435 of all files under Mercurial control in the working directory.
2435 of all files under Mercurial control in the working directory.
2436
2436
2437 If you want to feed the output of this command into the "xargs"
2437 If you want to feed the output of this command into the "xargs"
2438 command, use the -0 option to both this command and "xargs". This
2438 command, use the -0 option to both this command and "xargs". This
2439 will avoid the problem of "xargs" treating single filenames that
2439 will avoid the problem of "xargs" treating single filenames that
2440 contain whitespace as multiple filenames.
2440 contain whitespace as multiple filenames.
2441
2441
2442 Returns 0 if a match is found, 1 otherwise.
2442 Returns 0 if a match is found, 1 otherwise.
2443 """
2443 """
2444 end = opts.get('print0') and '\0' or '\n'
2444 end = opts.get('print0') and '\0' or '\n'
2445 rev = cmdutil.revsingle(repo, opts.get('rev'), None).node()
2445 rev = cmdutil.revsingle(repo, opts.get('rev'), None).node()
2446
2446
2447 ret = 1
2447 ret = 1
2448 m = cmdutil.match(repo, pats, opts, default='relglob')
2448 m = cmdutil.match(repo, pats, opts, default='relglob')
2449 m.bad = lambda x, y: False
2449 m.bad = lambda x, y: False
2450 for abs in repo[rev].walk(m):
2450 for abs in repo[rev].walk(m):
2451 if not rev and abs not in repo.dirstate:
2451 if not rev and abs not in repo.dirstate:
2452 continue
2452 continue
2453 if opts.get('fullpath'):
2453 if opts.get('fullpath'):
2454 ui.write(repo.wjoin(abs), end)
2454 ui.write(repo.wjoin(abs), end)
2455 else:
2455 else:
2456 ui.write(((pats and m.rel(abs)) or abs), end)
2456 ui.write(((pats and m.rel(abs)) or abs), end)
2457 ret = 0
2457 ret = 0
2458
2458
2459 return ret
2459 return ret
2460
2460
2461 def log(ui, repo, *pats, **opts):
2461 def log(ui, repo, *pats, **opts):
2462 """show revision history of entire repository or files
2462 """show revision history of entire repository or files
2463
2463
2464 Print the revision history of the specified files or the entire
2464 Print the revision history of the specified files or the entire
2465 project.
2465 project.
2466
2466
2467 File history is shown without following rename or copy history of
2467 File history is shown without following rename or copy history of
2468 files. Use -f/--follow with a filename to follow history across
2468 files. Use -f/--follow with a filename to follow history across
2469 renames and copies. --follow without a filename will only show
2469 renames and copies. --follow without a filename will only show
2470 ancestors or descendants of the starting revision. --follow-first
2470 ancestors or descendants of the starting revision. --follow-first
2471 only follows the first parent of merge revisions.
2471 only follows the first parent of merge revisions.
2472
2472
2473 If no revision range is specified, the default is ``tip:0`` unless
2473 If no revision range is specified, the default is ``tip:0`` unless
2474 --follow is set, in which case the working directory parent is
2474 --follow is set, in which case the working directory parent is
2475 used as the starting revision. You can specify a revision set for
2475 used as the starting revision. You can specify a revision set for
2476 log, see :hg:`help revsets` for more information.
2476 log, see :hg:`help revsets` for more information.
2477
2477
2478 See :hg:`help dates` for a list of formats valid for -d/--date.
2478 See :hg:`help dates` for a list of formats valid for -d/--date.
2479
2479
2480 By default this command prints revision number and changeset id,
2480 By default this command prints revision number and changeset id,
2481 tags, non-trivial parents, user, date and time, and a summary for
2481 tags, non-trivial parents, user, date and time, and a summary for
2482 each commit. When the -v/--verbose switch is used, the list of
2482 each commit. When the -v/--verbose switch is used, the list of
2483 changed files and full commit message are shown.
2483 changed files and full commit message are shown.
2484
2484
2485 .. note::
2485 .. note::
2486 log -p/--patch may generate unexpected diff output for merge
2486 log -p/--patch may generate unexpected diff output for merge
2487 changesets, as it will only compare the merge changeset against
2487 changesets, as it will only compare the merge changeset against
2488 its first parent. Also, only files different from BOTH parents
2488 its first parent. Also, only files different from BOTH parents
2489 will appear in files:.
2489 will appear in files:.
2490
2490
2491 Returns 0 on success.
2491 Returns 0 on success.
2492 """
2492 """
2493
2493
2494 matchfn = cmdutil.match(repo, pats, opts)
2494 matchfn = cmdutil.match(repo, pats, opts)
2495 limit = cmdutil.loglimit(opts)
2495 limit = cmdutil.loglimit(opts)
2496 count = 0
2496 count = 0
2497
2497
2498 endrev = None
2498 endrev = None
2499 if opts.get('copies') and opts.get('rev'):
2499 if opts.get('copies') and opts.get('rev'):
2500 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2500 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
2501
2501
2502 df = False
2502 df = False
2503 if opts["date"]:
2503 if opts["date"]:
2504 df = util.matchdate(opts["date"])
2504 df = util.matchdate(opts["date"])
2505
2505
2506 branches = opts.get('branch', []) + opts.get('only_branch', [])
2506 branches = opts.get('branch', []) + opts.get('only_branch', [])
2507 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2507 opts['branch'] = [repo.lookupbranch(b) for b in branches]
2508
2508
2509 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2509 displayer = cmdutil.show_changeset(ui, repo, opts, True)
2510 def prep(ctx, fns):
2510 def prep(ctx, fns):
2511 rev = ctx.rev()
2511 rev = ctx.rev()
2512 parents = [p for p in repo.changelog.parentrevs(rev)
2512 parents = [p for p in repo.changelog.parentrevs(rev)
2513 if p != nullrev]
2513 if p != nullrev]
2514 if opts.get('no_merges') and len(parents) == 2:
2514 if opts.get('no_merges') and len(parents) == 2:
2515 return
2515 return
2516 if opts.get('only_merges') and len(parents) != 2:
2516 if opts.get('only_merges') and len(parents) != 2:
2517 return
2517 return
2518 if opts.get('branch') and ctx.branch() not in opts['branch']:
2518 if opts.get('branch') and ctx.branch() not in opts['branch']:
2519 return
2519 return
2520 if df and not df(ctx.date()[0]):
2520 if df and not df(ctx.date()[0]):
2521 return
2521 return
2522 if opts['user'] and not [k for k in opts['user']
2522 if opts['user'] and not [k for k in opts['user']
2523 if k.lower() in ctx.user().lower()]:
2523 if k.lower() in ctx.user().lower()]:
2524 return
2524 return
2525 if opts.get('keyword'):
2525 if opts.get('keyword'):
2526 for k in [kw.lower() for kw in opts['keyword']]:
2526 for k in [kw.lower() for kw in opts['keyword']]:
2527 if (k in ctx.user().lower() or
2527 if (k in ctx.user().lower() or
2528 k in ctx.description().lower() or
2528 k in ctx.description().lower() or
2529 k in " ".join(ctx.files()).lower()):
2529 k in " ".join(ctx.files()).lower()):
2530 break
2530 break
2531 else:
2531 else:
2532 return
2532 return
2533
2533
2534 copies = None
2534 copies = None
2535 if opts.get('copies') and rev:
2535 if opts.get('copies') and rev:
2536 copies = []
2536 copies = []
2537 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2537 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2538 for fn in ctx.files():
2538 for fn in ctx.files():
2539 rename = getrenamed(fn, rev)
2539 rename = getrenamed(fn, rev)
2540 if rename:
2540 if rename:
2541 copies.append((fn, rename[0]))
2541 copies.append((fn, rename[0]))
2542
2542
2543 revmatchfn = None
2543 revmatchfn = None
2544 if opts.get('patch') or opts.get('stat'):
2544 if opts.get('patch') or opts.get('stat'):
2545 if opts.get('follow') or opts.get('follow_first'):
2545 if opts.get('follow') or opts.get('follow_first'):
2546 # note: this might be wrong when following through merges
2546 # note: this might be wrong when following through merges
2547 revmatchfn = cmdutil.match(repo, fns, default='path')
2547 revmatchfn = cmdutil.match(repo, fns, default='path')
2548 else:
2548 else:
2549 revmatchfn = matchfn
2549 revmatchfn = matchfn
2550
2550
2551 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2551 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2552
2552
2553 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2553 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2554 if count == limit:
2554 if count == limit:
2555 break
2555 break
2556 if displayer.flush(ctx.rev()):
2556 if displayer.flush(ctx.rev()):
2557 count += 1
2557 count += 1
2558 displayer.close()
2558 displayer.close()
2559
2559
2560 def manifest(ui, repo, node=None, rev=None):
2560 def manifest(ui, repo, node=None, rev=None):
2561 """output the current or given revision of the project manifest
2561 """output the current or given revision of the project manifest
2562
2562
2563 Print a list of version controlled files for the given revision.
2563 Print a list of version controlled files for the given revision.
2564 If no revision is given, the first parent of the working directory
2564 If no revision is given, the first parent of the working directory
2565 is used, or the null revision if no revision is checked out.
2565 is used, or the null revision if no revision is checked out.
2566
2566
2567 With -v, print file permissions, symlink and executable bits.
2567 With -v, print file permissions, symlink and executable bits.
2568 With --debug, print file revision hashes.
2568 With --debug, print file revision hashes.
2569
2569
2570 Returns 0 on success.
2570 Returns 0 on success.
2571 """
2571 """
2572
2572
2573 if rev and node:
2573 if rev and node:
2574 raise util.Abort(_("please specify just one revision"))
2574 raise util.Abort(_("please specify just one revision"))
2575
2575
2576 if not node:
2576 if not node:
2577 node = rev
2577 node = rev
2578
2578
2579 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2579 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2580 ctx = cmdutil.revsingle(repo, node)
2580 ctx = cmdutil.revsingle(repo, node)
2581 for f in ctx:
2581 for f in ctx:
2582 if ui.debugflag:
2582 if ui.debugflag:
2583 ui.write("%40s " % hex(ctx.manifest()[f]))
2583 ui.write("%40s " % hex(ctx.manifest()[f]))
2584 if ui.verbose:
2584 if ui.verbose:
2585 ui.write(decor[ctx.flags(f)])
2585 ui.write(decor[ctx.flags(f)])
2586 ui.write("%s\n" % f)
2586 ui.write("%s\n" % f)
2587
2587
2588 def merge(ui, repo, node=None, **opts):
2588 def merge(ui, repo, node=None, **opts):
2589 """merge working directory with another revision
2589 """merge working directory with another revision
2590
2590
2591 The current working directory is updated with all changes made in
2591 The current working directory is updated with all changes made in
2592 the requested revision since the last common predecessor revision.
2592 the requested revision since the last common predecessor revision.
2593
2593
2594 Files that changed between either parent are marked as changed for
2594 Files that changed between either parent are marked as changed for
2595 the next commit and a commit must be performed before any further
2595 the next commit and a commit must be performed before any further
2596 updates to the repository are allowed. The next commit will have
2596 updates to the repository are allowed. The next commit will have
2597 two parents.
2597 two parents.
2598
2598
2599 ``--tool`` can be used to specify the merge tool used for file
2599 ``--tool`` can be used to specify the merge tool used for file
2600 merges. It overrides the HGMERGE environment variable and your
2600 merges. It overrides the HGMERGE environment variable and your
2601 configuration files.
2601 configuration files.
2602
2602
2603 If no revision is specified, the working directory's parent is a
2603 If no revision is specified, the working directory's parent is a
2604 head revision, and the current branch contains exactly one other
2604 head revision, and the current branch contains exactly one other
2605 head, the other head is merged with by default. Otherwise, an
2605 head, the other head is merged with by default. Otherwise, an
2606 explicit revision with which to merge with must be provided.
2606 explicit revision with which to merge with must be provided.
2607
2607
2608 :hg:`resolve` must be used to resolve unresolved files.
2608 :hg:`resolve` must be used to resolve unresolved files.
2609
2609
2610 To undo an uncommitted merge, use :hg:`update --clean .` which
2610 To undo an uncommitted merge, use :hg:`update --clean .` which
2611 will check out a clean copy of the original merge parent, losing
2611 will check out a clean copy of the original merge parent, losing
2612 all changes.
2612 all changes.
2613
2613
2614 Returns 0 on success, 1 if there are unresolved files.
2614 Returns 0 on success, 1 if there are unresolved files.
2615 """
2615 """
2616
2616
2617 if opts.get('rev') and node:
2617 if opts.get('rev') and node:
2618 raise util.Abort(_("please specify just one revision"))
2618 raise util.Abort(_("please specify just one revision"))
2619 if not node:
2619 if not node:
2620 node = opts.get('rev')
2620 node = opts.get('rev')
2621
2621
2622 if not node:
2622 if not node:
2623 branch = repo[None].branch()
2623 branch = repo[None].branch()
2624 bheads = repo.branchheads(branch)
2624 bheads = repo.branchheads(branch)
2625 if len(bheads) > 2:
2625 if len(bheads) > 2:
2626 raise util.Abort(_(
2626 raise util.Abort(_(
2627 'branch \'%s\' has %d heads - '
2627 'branch \'%s\' has %d heads - '
2628 'please merge with an explicit rev\n'
2628 'please merge with an explicit rev\n'
2629 '(run \'hg heads .\' to see heads)')
2629 '(run \'hg heads .\' to see heads)')
2630 % (branch, len(bheads)))
2630 % (branch, len(bheads)))
2631
2631
2632 parent = repo.dirstate.parents()[0]
2632 parent = repo.dirstate.parents()[0]
2633 if len(bheads) == 1:
2633 if len(bheads) == 1:
2634 if len(repo.heads()) > 1:
2634 if len(repo.heads()) > 1:
2635 raise util.Abort(_(
2635 raise util.Abort(_(
2636 'branch \'%s\' has one head - '
2636 'branch \'%s\' has one head - '
2637 'please merge with an explicit rev\n'
2637 'please merge with an explicit rev\n'
2638 '(run \'hg heads\' to see all heads)')
2638 '(run \'hg heads\' to see all heads)')
2639 % branch)
2639 % branch)
2640 msg = _('there is nothing to merge')
2640 msg = _('there is nothing to merge')
2641 if parent != repo.lookup(repo[None].branch()):
2641 if parent != repo.lookup(repo[None].branch()):
2642 msg = _('%s - use "hg update" instead') % msg
2642 msg = _('%s - use "hg update" instead') % msg
2643 raise util.Abort(msg)
2643 raise util.Abort(msg)
2644
2644
2645 if parent not in bheads:
2645 if parent not in bheads:
2646 raise util.Abort(_('working dir not at a head rev - '
2646 raise util.Abort(_('working dir not at a head rev - '
2647 'use "hg update" or merge with an explicit rev'))
2647 'use "hg update" or merge with an explicit rev'))
2648 node = parent == bheads[0] and bheads[-1] or bheads[0]
2648 node = parent == bheads[0] and bheads[-1] or bheads[0]
2649 else:
2649 else:
2650 node = cmdutil.revsingle(repo, node).node()
2650 node = cmdutil.revsingle(repo, node).node()
2651
2651
2652 if opts.get('preview'):
2652 if opts.get('preview'):
2653 # find nodes that are ancestors of p2 but not of p1
2653 # find nodes that are ancestors of p2 but not of p1
2654 p1 = repo.lookup('.')
2654 p1 = repo.lookup('.')
2655 p2 = repo.lookup(node)
2655 p2 = repo.lookup(node)
2656 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2656 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
2657
2657
2658 displayer = cmdutil.show_changeset(ui, repo, opts)
2658 displayer = cmdutil.show_changeset(ui, repo, opts)
2659 for node in nodes:
2659 for node in nodes:
2660 displayer.show(repo[node])
2660 displayer.show(repo[node])
2661 displayer.close()
2661 displayer.close()
2662 return 0
2662 return 0
2663
2663
2664 try:
2664 try:
2665 # ui.forcemerge is an internal variable, do not document
2665 # ui.forcemerge is an internal variable, do not document
2666 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2666 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
2667 return hg.merge(repo, node, force=opts.get('force'))
2667 return hg.merge(repo, node, force=opts.get('force'))
2668 finally:
2668 finally:
2669 ui.setconfig('ui', 'forcemerge', '')
2669 ui.setconfig('ui', 'forcemerge', '')
2670
2670
2671 def outgoing(ui, repo, dest=None, **opts):
2671 def outgoing(ui, repo, dest=None, **opts):
2672 """show changesets not found in the destination
2672 """show changesets not found in the destination
2673
2673
2674 Show changesets not found in the specified destination repository
2674 Show changesets not found in the specified destination repository
2675 or the default push location. These are the changesets that would
2675 or the default push location. These are the changesets that would
2676 be pushed if a push was requested.
2676 be pushed if a push was requested.
2677
2677
2678 See pull for details of valid destination formats.
2678 See pull for details of valid destination formats.
2679
2679
2680 Returns 0 if there are outgoing changes, 1 otherwise.
2680 Returns 0 if there are outgoing changes, 1 otherwise.
2681 """
2681 """
2682 ret = hg.outgoing(ui, repo, dest, opts)
2682 ret = hg.outgoing(ui, repo, dest, opts)
2683 return ret
2683 return ret
2684
2684
2685 def parents(ui, repo, file_=None, **opts):
2685 def parents(ui, repo, file_=None, **opts):
2686 """show the parents of the working directory or revision
2686 """show the parents of the working directory or revision
2687
2687
2688 Print the working directory's parent revisions. If a revision is
2688 Print the working directory's parent revisions. If a revision is
2689 given via -r/--rev, the parent of that revision will be printed.
2689 given via -r/--rev, the parent of that revision will be printed.
2690 If a file argument is given, the revision in which the file was
2690 If a file argument is given, the revision in which the file was
2691 last changed (before the working directory revision or the
2691 last changed (before the working directory revision or the
2692 argument to --rev if given) is printed.
2692 argument to --rev if given) is printed.
2693
2693
2694 Returns 0 on success.
2694 Returns 0 on success.
2695 """
2695 """
2696
2696
2697 ctx = cmdutil.revsingle(repo, opts.get('rev'), None)
2697 ctx = cmdutil.revsingle(repo, opts.get('rev'), None)
2698
2698
2699 if file_:
2699 if file_:
2700 m = cmdutil.match(repo, (file_,), opts)
2700 m = cmdutil.match(repo, (file_,), opts)
2701 if m.anypats() or len(m.files()) != 1:
2701 if m.anypats() or len(m.files()) != 1:
2702 raise util.Abort(_('can only specify an explicit filename'))
2702 raise util.Abort(_('can only specify an explicit filename'))
2703 file_ = m.files()[0]
2703 file_ = m.files()[0]
2704 filenodes = []
2704 filenodes = []
2705 for cp in ctx.parents():
2705 for cp in ctx.parents():
2706 if not cp:
2706 if not cp:
2707 continue
2707 continue
2708 try:
2708 try:
2709 filenodes.append(cp.filenode(file_))
2709 filenodes.append(cp.filenode(file_))
2710 except error.LookupError:
2710 except error.LookupError:
2711 pass
2711 pass
2712 if not filenodes:
2712 if not filenodes:
2713 raise util.Abort(_("'%s' not found in manifest!") % file_)
2713 raise util.Abort(_("'%s' not found in manifest!") % file_)
2714 fl = repo.file(file_)
2714 fl = repo.file(file_)
2715 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2715 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2716 else:
2716 else:
2717 p = [cp.node() for cp in ctx.parents()]
2717 p = [cp.node() for cp in ctx.parents()]
2718
2718
2719 displayer = cmdutil.show_changeset(ui, repo, opts)
2719 displayer = cmdutil.show_changeset(ui, repo, opts)
2720 for n in p:
2720 for n in p:
2721 if n != nullid:
2721 if n != nullid:
2722 displayer.show(repo[n])
2722 displayer.show(repo[n])
2723 displayer.close()
2723 displayer.close()
2724
2724
2725 def paths(ui, repo, search=None):
2725 def paths(ui, repo, search=None):
2726 """show aliases for remote repositories
2726 """show aliases for remote repositories
2727
2727
2728 Show definition of symbolic path name NAME. If no name is given,
2728 Show definition of symbolic path name NAME. If no name is given,
2729 show definition of all available names.
2729 show definition of all available names.
2730
2730
2731 Path names are defined in the [paths] section of your
2731 Path names are defined in the [paths] section of your
2732 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
2732 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
2733 repository, ``.hg/hgrc`` is used, too.
2733 repository, ``.hg/hgrc`` is used, too.
2734
2734
2735 The path names ``default`` and ``default-push`` have a special
2735 The path names ``default`` and ``default-push`` have a special
2736 meaning. When performing a push or pull operation, they are used
2736 meaning. When performing a push or pull operation, they are used
2737 as fallbacks if no location is specified on the command-line.
2737 as fallbacks if no location is specified on the command-line.
2738 When ``default-push`` is set, it will be used for push and
2738 When ``default-push`` is set, it will be used for push and
2739 ``default`` will be used for pull; otherwise ``default`` is used
2739 ``default`` will be used for pull; otherwise ``default`` is used
2740 as the fallback for both. When cloning a repository, the clone
2740 as the fallback for both. When cloning a repository, the clone
2741 source is written as ``default`` in ``.hg/hgrc``. Note that
2741 source is written as ``default`` in ``.hg/hgrc``. Note that
2742 ``default`` and ``default-push`` apply to all inbound (e.g.
2742 ``default`` and ``default-push`` apply to all inbound (e.g.
2743 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2743 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
2744 :hg:`bundle`) operations.
2744 :hg:`bundle`) operations.
2745
2745
2746 See :hg:`help urls` for more information.
2746 See :hg:`help urls` for more information.
2747
2747
2748 Returns 0 on success.
2748 Returns 0 on success.
2749 """
2749 """
2750 if search:
2750 if search:
2751 for name, path in ui.configitems("paths"):
2751 for name, path in ui.configitems("paths"):
2752 if name == search:
2752 if name == search:
2753 ui.write("%s\n" % url.hidepassword(path))
2753 ui.write("%s\n" % url.hidepassword(path))
2754 return
2754 return
2755 ui.warn(_("not found!\n"))
2755 ui.warn(_("not found!\n"))
2756 return 1
2756 return 1
2757 else:
2757 else:
2758 for name, path in ui.configitems("paths"):
2758 for name, path in ui.configitems("paths"):
2759 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2759 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2760
2760
2761 def postincoming(ui, repo, modheads, optupdate, checkout):
2761 def postincoming(ui, repo, modheads, optupdate, checkout):
2762 if modheads == 0:
2762 if modheads == 0:
2763 return
2763 return
2764 if optupdate:
2764 if optupdate:
2765 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2765 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2766 return hg.update(repo, checkout)
2766 return hg.update(repo, checkout)
2767 else:
2767 else:
2768 ui.status(_("not updating, since new heads added\n"))
2768 ui.status(_("not updating, since new heads added\n"))
2769 if modheads > 1:
2769 if modheads > 1:
2770 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2770 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2771 else:
2771 else:
2772 ui.status(_("(run 'hg update' to get a working copy)\n"))
2772 ui.status(_("(run 'hg update' to get a working copy)\n"))
2773
2773
2774 def pull(ui, repo, source="default", **opts):
2774 def pull(ui, repo, source="default", **opts):
2775 """pull changes from the specified source
2775 """pull changes from the specified source
2776
2776
2777 Pull changes from a remote repository to a local one.
2777 Pull changes from a remote repository to a local one.
2778
2778
2779 This finds all changes from the repository at the specified path
2779 This finds all changes from the repository at the specified path
2780 or URL and adds them to a local repository (the current one unless
2780 or URL and adds them to a local repository (the current one unless
2781 -R is specified). By default, this does not update the copy of the
2781 -R is specified). By default, this does not update the copy of the
2782 project in the working directory.
2782 project in the working directory.
2783
2783
2784 Use :hg:`incoming` if you want to see what would have been added
2784 Use :hg:`incoming` if you want to see what would have been added
2785 by a pull at the time you issued this command. If you then decide
2785 by a pull at the time you issued this command. If you then decide
2786 to add those changes to the repository, you should use :hg:`pull
2786 to add those changes to the repository, you should use :hg:`pull
2787 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2787 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
2788
2788
2789 If SOURCE is omitted, the 'default' path will be used.
2789 If SOURCE is omitted, the 'default' path will be used.
2790 See :hg:`help urls` for more information.
2790 See :hg:`help urls` for more information.
2791
2791
2792 Returns 0 on success, 1 if an update had unresolved files.
2792 Returns 0 on success, 1 if an update had unresolved files.
2793 """
2793 """
2794 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2794 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
2795 other = hg.repository(hg.remoteui(repo, opts), source)
2795 other = hg.repository(hg.remoteui(repo, opts), source)
2796 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2796 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2797 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2797 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
2798 if revs:
2798 if revs:
2799 try:
2799 try:
2800 revs = [other.lookup(rev) for rev in revs]
2800 revs = [other.lookup(rev) for rev in revs]
2801 except error.CapabilityError:
2801 except error.CapabilityError:
2802 err = _("other repository doesn't support revision lookup, "
2802 err = _("other repository doesn't support revision lookup, "
2803 "so a rev cannot be specified.")
2803 "so a rev cannot be specified.")
2804 raise util.Abort(err)
2804 raise util.Abort(err)
2805
2805
2806 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2806 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2807 if checkout:
2807 if checkout:
2808 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2808 checkout = str(repo.changelog.rev(other.lookup(checkout)))
2809 repo._subtoppath = source
2809 repo._subtoppath = source
2810 try:
2810 try:
2811 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2811 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2812 finally:
2812 finally:
2813 del repo._subtoppath
2813 del repo._subtoppath
2814
2814
2815 def push(ui, repo, dest=None, **opts):
2815 def push(ui, repo, dest=None, **opts):
2816 """push changes to the specified destination
2816 """push changes to the specified destination
2817
2817
2818 Push changesets from the local repository to the specified
2818 Push changesets from the local repository to the specified
2819 destination.
2819 destination.
2820
2820
2821 This operation is symmetrical to pull: it is identical to a pull
2821 This operation is symmetrical to pull: it is identical to a pull
2822 in the destination repository from the current one.
2822 in the destination repository from the current one.
2823
2823
2824 By default, push will not allow creation of new heads at the
2824 By default, push will not allow creation of new heads at the
2825 destination, since multiple heads would make it unclear which head
2825 destination, since multiple heads would make it unclear which head
2826 to use. In this situation, it is recommended to pull and merge
2826 to use. In this situation, it is recommended to pull and merge
2827 before pushing.
2827 before pushing.
2828
2828
2829 Use --new-branch if you want to allow push to create a new named
2829 Use --new-branch if you want to allow push to create a new named
2830 branch that is not present at the destination. This allows you to
2830 branch that is not present at the destination. This allows you to
2831 only create a new branch without forcing other changes.
2831 only create a new branch without forcing other changes.
2832
2832
2833 Use -f/--force to override the default behavior and push all
2833 Use -f/--force to override the default behavior and push all
2834 changesets on all branches.
2834 changesets on all branches.
2835
2835
2836 If -r/--rev is used, the specified revision and all its ancestors
2836 If -r/--rev is used, the specified revision and all its ancestors
2837 will be pushed to the remote repository.
2837 will be pushed to the remote repository.
2838
2838
2839 Please see :hg:`help urls` for important details about ``ssh://``
2839 Please see :hg:`help urls` for important details about ``ssh://``
2840 URLs. If DESTINATION is omitted, a default path will be used.
2840 URLs. If DESTINATION is omitted, a default path will be used.
2841
2841
2842 Returns 0 if push was successful, 1 if nothing to push.
2842 Returns 0 if push was successful, 1 if nothing to push.
2843 """
2843 """
2844 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2844 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2845 dest, branches = hg.parseurl(dest, opts.get('branch'))
2845 dest, branches = hg.parseurl(dest, opts.get('branch'))
2846 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2846 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
2847 other = hg.repository(hg.remoteui(repo, opts), dest)
2847 other = hg.repository(hg.remoteui(repo, opts), dest)
2848 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2848 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2849 if revs:
2849 if revs:
2850 revs = [repo.lookup(rev) for rev in revs]
2850 revs = [repo.lookup(rev) for rev in revs]
2851
2851
2852 repo._subtoppath = dest
2852 repo._subtoppath = dest
2853 try:
2853 try:
2854 # push subrepos depth-first for coherent ordering
2854 # push subrepos depth-first for coherent ordering
2855 c = repo['']
2855 c = repo['']
2856 subs = c.substate # only repos that are committed
2856 subs = c.substate # only repos that are committed
2857 for s in sorted(subs):
2857 for s in sorted(subs):
2858 if not c.sub(s).push(opts.get('force')):
2858 if not c.sub(s).push(opts.get('force')):
2859 return False
2859 return False
2860 finally:
2860 finally:
2861 del repo._subtoppath
2861 del repo._subtoppath
2862 r = repo.push(other, opts.get('force'), revs=revs,
2862 r = repo.push(other, opts.get('force'), revs=revs,
2863 newbranch=opts.get('new_branch'))
2863 newbranch=opts.get('new_branch'))
2864 return r == 0
2864 return r == 0
2865
2865
2866 def recover(ui, repo):
2866 def recover(ui, repo):
2867 """roll back an interrupted transaction
2867 """roll back an interrupted transaction
2868
2868
2869 Recover from an interrupted commit or pull.
2869 Recover from an interrupted commit or pull.
2870
2870
2871 This command tries to fix the repository status after an
2871 This command tries to fix the repository status after an
2872 interrupted operation. It should only be necessary when Mercurial
2872 interrupted operation. It should only be necessary when Mercurial
2873 suggests it.
2873 suggests it.
2874
2874
2875 Returns 0 if successful, 1 if nothing to recover or verify fails.
2875 Returns 0 if successful, 1 if nothing to recover or verify fails.
2876 """
2876 """
2877 if repo.recover():
2877 if repo.recover():
2878 return hg.verify(repo)
2878 return hg.verify(repo)
2879 return 1
2879 return 1
2880
2880
2881 def remove(ui, repo, *pats, **opts):
2881 def remove(ui, repo, *pats, **opts):
2882 """remove the specified files on the next commit
2882 """remove the specified files on the next commit
2883
2883
2884 Schedule the indicated files for removal from the repository.
2884 Schedule the indicated files for removal from the repository.
2885
2885
2886 This only removes files from the current branch, not from the
2886 This only removes files from the current branch, not from the
2887 entire project history. -A/--after can be used to remove only
2887 entire project history. -A/--after can be used to remove only
2888 files that have already been deleted, -f/--force can be used to
2888 files that have already been deleted, -f/--force can be used to
2889 force deletion, and -Af can be used to remove files from the next
2889 force deletion, and -Af can be used to remove files from the next
2890 revision without deleting them from the working directory.
2890 revision without deleting them from the working directory.
2891
2891
2892 The following table details the behavior of remove for different
2892 The following table details the behavior of remove for different
2893 file states (columns) and option combinations (rows). The file
2893 file states (columns) and option combinations (rows). The file
2894 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2894 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
2895 reported by :hg:`status`). The actions are Warn, Remove (from
2895 reported by :hg:`status`). The actions are Warn, Remove (from
2896 branch) and Delete (from disk)::
2896 branch) and Delete (from disk)::
2897
2897
2898 A C M !
2898 A C M !
2899 none W RD W R
2899 none W RD W R
2900 -f R RD RD R
2900 -f R RD RD R
2901 -A W W W R
2901 -A W W W R
2902 -Af R R R R
2902 -Af R R R R
2903
2903
2904 This command schedules the files to be removed at the next commit.
2904 This command schedules the files to be removed at the next commit.
2905 To undo a remove before that, see :hg:`revert`.
2905 To undo a remove before that, see :hg:`revert`.
2906
2906
2907 Returns 0 on success, 1 if any warnings encountered.
2907 Returns 0 on success, 1 if any warnings encountered.
2908 """
2908 """
2909
2909
2910 ret = 0
2910 ret = 0
2911 after, force = opts.get('after'), opts.get('force')
2911 after, force = opts.get('after'), opts.get('force')
2912 if not pats and not after:
2912 if not pats and not after:
2913 raise util.Abort(_('no files specified'))
2913 raise util.Abort(_('no files specified'))
2914
2914
2915 m = cmdutil.match(repo, pats, opts)
2915 m = cmdutil.match(repo, pats, opts)
2916 s = repo.status(match=m, clean=True)
2916 s = repo.status(match=m, clean=True)
2917 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2917 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2918
2918
2919 for f in m.files():
2919 for f in m.files():
2920 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2920 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2921 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2921 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2922 ret = 1
2922 ret = 1
2923
2923
2924 if force:
2924 if force:
2925 remove, forget = modified + deleted + clean, added
2925 remove, forget = modified + deleted + clean, added
2926 elif after:
2926 elif after:
2927 remove, forget = deleted, []
2927 remove, forget = deleted, []
2928 for f in modified + added + clean:
2928 for f in modified + added + clean:
2929 ui.warn(_('not removing %s: file still exists (use -f'
2929 ui.warn(_('not removing %s: file still exists (use -f'
2930 ' to force removal)\n') % m.rel(f))
2930 ' to force removal)\n') % m.rel(f))
2931 ret = 1
2931 ret = 1
2932 else:
2932 else:
2933 remove, forget = deleted + clean, []
2933 remove, forget = deleted + clean, []
2934 for f in modified:
2934 for f in modified:
2935 ui.warn(_('not removing %s: file is modified (use -f'
2935 ui.warn(_('not removing %s: file is modified (use -f'
2936 ' to force removal)\n') % m.rel(f))
2936 ' to force removal)\n') % m.rel(f))
2937 ret = 1
2937 ret = 1
2938 for f in added:
2938 for f in added:
2939 ui.warn(_('not removing %s: file has been marked for add (use -f'
2939 ui.warn(_('not removing %s: file has been marked for add (use -f'
2940 ' to force removal)\n') % m.rel(f))
2940 ' to force removal)\n') % m.rel(f))
2941 ret = 1
2941 ret = 1
2942
2942
2943 for f in sorted(remove + forget):
2943 for f in sorted(remove + forget):
2944 if ui.verbose or not m.exact(f):
2944 if ui.verbose or not m.exact(f):
2945 ui.status(_('removing %s\n') % m.rel(f))
2945 ui.status(_('removing %s\n') % m.rel(f))
2946
2946
2947 repo[None].forget(forget)
2947 repo[None].forget(forget)
2948 repo[None].remove(remove, unlink=not after)
2948 repo[None].remove(remove, unlink=not after)
2949 return ret
2949 return ret
2950
2950
2951 def rename(ui, repo, *pats, **opts):
2951 def rename(ui, repo, *pats, **opts):
2952 """rename files; equivalent of copy + remove
2952 """rename files; equivalent of copy + remove
2953
2953
2954 Mark dest as copies of sources; mark sources for deletion. If dest
2954 Mark dest as copies of sources; mark sources for deletion. If dest
2955 is a directory, copies are put in that directory. If dest is a
2955 is a directory, copies are put in that directory. If dest is a
2956 file, there can only be one source.
2956 file, there can only be one source.
2957
2957
2958 By default, this command copies the contents of files as they
2958 By default, this command copies the contents of files as they
2959 exist in the working directory. If invoked with -A/--after, the
2959 exist in the working directory. If invoked with -A/--after, the
2960 operation is recorded, but no copying is performed.
2960 operation is recorded, but no copying is performed.
2961
2961
2962 This command takes effect at the next commit. To undo a rename
2962 This command takes effect at the next commit. To undo a rename
2963 before that, see :hg:`revert`.
2963 before that, see :hg:`revert`.
2964
2964
2965 Returns 0 on success, 1 if errors are encountered.
2965 Returns 0 on success, 1 if errors are encountered.
2966 """
2966 """
2967 wlock = repo.wlock(False)
2967 wlock = repo.wlock(False)
2968 try:
2968 try:
2969 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2969 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2970 finally:
2970 finally:
2971 wlock.release()
2971 wlock.release()
2972
2972
2973 def resolve(ui, repo, *pats, **opts):
2973 def resolve(ui, repo, *pats, **opts):
2974 """redo merges or set/view the merge status of files
2974 """redo merges or set/view the merge status of files
2975
2975
2976 Merges with unresolved conflicts are often the result of
2976 Merges with unresolved conflicts are often the result of
2977 non-interactive merging using the ``internal:merge`` configuration
2977 non-interactive merging using the ``internal:merge`` configuration
2978 setting, or a command-line merge tool like ``diff3``. The resolve
2978 setting, or a command-line merge tool like ``diff3``. The resolve
2979 command is used to manage the files involved in a merge, after
2979 command is used to manage the files involved in a merge, after
2980 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
2980 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
2981 working directory must have two parents).
2981 working directory must have two parents).
2982
2982
2983 The resolve command can be used in the following ways:
2983 The resolve command can be used in the following ways:
2984
2984
2985 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
2985 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
2986 files, discarding any previous merge attempts. Re-merging is not
2986 files, discarding any previous merge attempts. Re-merging is not
2987 performed for files already marked as resolved. Use ``--all/-a``
2987 performed for files already marked as resolved. Use ``--all/-a``
2988 to selects all unresolved files. ``--tool`` can be used to specify
2988 to selects all unresolved files. ``--tool`` can be used to specify
2989 the merge tool used for the given files. It overrides the HGMERGE
2989 the merge tool used for the given files. It overrides the HGMERGE
2990 environment variable and your configuration files.
2990 environment variable and your configuration files.
2991
2991
2992 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
2992 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
2993 (e.g. after having manually fixed-up the files). The default is
2993 (e.g. after having manually fixed-up the files). The default is
2994 to mark all unresolved files.
2994 to mark all unresolved files.
2995
2995
2996 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
2996 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
2997 default is to mark all resolved files.
2997 default is to mark all resolved files.
2998
2998
2999 - :hg:`resolve -l`: list files which had or still have conflicts.
2999 - :hg:`resolve -l`: list files which had or still have conflicts.
3000 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3000 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3001
3001
3002 Note that Mercurial will not let you commit files with unresolved
3002 Note that Mercurial will not let you commit files with unresolved
3003 merge conflicts. You must use :hg:`resolve -m ...` before you can
3003 merge conflicts. You must use :hg:`resolve -m ...` before you can
3004 commit after a conflicting merge.
3004 commit after a conflicting merge.
3005
3005
3006 Returns 0 on success, 1 if any files fail a resolve attempt.
3006 Returns 0 on success, 1 if any files fail a resolve attempt.
3007 """
3007 """
3008
3008
3009 all, mark, unmark, show, nostatus = \
3009 all, mark, unmark, show, nostatus = \
3010 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3010 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3011
3011
3012 if (show and (mark or unmark)) or (mark and unmark):
3012 if (show and (mark or unmark)) or (mark and unmark):
3013 raise util.Abort(_("too many options specified"))
3013 raise util.Abort(_("too many options specified"))
3014 if pats and all:
3014 if pats and all:
3015 raise util.Abort(_("can't specify --all and patterns"))
3015 raise util.Abort(_("can't specify --all and patterns"))
3016 if not (all or pats or show or mark or unmark):
3016 if not (all or pats or show or mark or unmark):
3017 raise util.Abort(_('no files or directories specified; '
3017 raise util.Abort(_('no files or directories specified; '
3018 'use --all to remerge all files'))
3018 'use --all to remerge all files'))
3019
3019
3020 ms = mergemod.mergestate(repo)
3020 ms = mergemod.mergestate(repo)
3021 m = cmdutil.match(repo, pats, opts)
3021 m = cmdutil.match(repo, pats, opts)
3022 ret = 0
3022 ret = 0
3023
3023
3024 for f in ms:
3024 for f in ms:
3025 if m(f):
3025 if m(f):
3026 if show:
3026 if show:
3027 if nostatus:
3027 if nostatus:
3028 ui.write("%s\n" % f)
3028 ui.write("%s\n" % f)
3029 else:
3029 else:
3030 ui.write("%s %s\n" % (ms[f].upper(), f),
3030 ui.write("%s %s\n" % (ms[f].upper(), f),
3031 label='resolve.' +
3031 label='resolve.' +
3032 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3032 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3033 elif mark:
3033 elif mark:
3034 ms.mark(f, "r")
3034 ms.mark(f, "r")
3035 elif unmark:
3035 elif unmark:
3036 ms.mark(f, "u")
3036 ms.mark(f, "u")
3037 else:
3037 else:
3038 wctx = repo[None]
3038 wctx = repo[None]
3039 mctx = wctx.parents()[-1]
3039 mctx = wctx.parents()[-1]
3040
3040
3041 # backup pre-resolve (merge uses .orig for its own purposes)
3041 # backup pre-resolve (merge uses .orig for its own purposes)
3042 a = repo.wjoin(f)
3042 a = repo.wjoin(f)
3043 util.copyfile(a, a + ".resolve")
3043 util.copyfile(a, a + ".resolve")
3044
3044
3045 try:
3045 try:
3046 # resolve file
3046 # resolve file
3047 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3047 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3048 if ms.resolve(f, wctx, mctx):
3048 if ms.resolve(f, wctx, mctx):
3049 ret = 1
3049 ret = 1
3050 finally:
3050 finally:
3051 ui.setconfig('ui', 'forcemerge', '')
3051 ui.setconfig('ui', 'forcemerge', '')
3052
3052
3053 # replace filemerge's .orig file with our resolve file
3053 # replace filemerge's .orig file with our resolve file
3054 util.rename(a + ".resolve", a + ".orig")
3054 util.rename(a + ".resolve", a + ".orig")
3055
3055
3056 ms.commit()
3056 ms.commit()
3057 return ret
3057 return ret
3058
3058
3059 def revert(ui, repo, *pats, **opts):
3059 def revert(ui, repo, *pats, **opts):
3060 """restore individual files or directories to an earlier state
3060 """restore individual files or directories to an earlier state
3061
3061
3062 .. note::
3062 .. note::
3063 This command is most likely not what you are looking for.
3063 This command is most likely not what you are looking for.
3064 Revert will partially overwrite content in the working
3064 Revert will partially overwrite content in the working
3065 directory without changing the working directory parents. Use
3065 directory without changing the working directory parents. Use
3066 :hg:`update -r rev` to check out earlier revisions, or
3066 :hg:`update -r rev` to check out earlier revisions, or
3067 :hg:`update --clean .` to undo a merge which has added another
3067 :hg:`update --clean .` to undo a merge which has added another
3068 parent.
3068 parent.
3069
3069
3070 With no revision specified, revert the named files or directories
3070 With no revision specified, revert the named files or directories
3071 to the contents they had in the parent of the working directory.
3071 to the contents they had in the parent of the working directory.
3072 This restores the contents of the affected files to an unmodified
3072 This restores the contents of the affected files to an unmodified
3073 state and unschedules adds, removes, copies, and renames. If the
3073 state and unschedules adds, removes, copies, and renames. If the
3074 working directory has two parents, you must explicitly specify a
3074 working directory has two parents, you must explicitly specify a
3075 revision.
3075 revision.
3076
3076
3077 Using the -r/--rev option, revert the given files or directories
3077 Using the -r/--rev option, revert the given files or directories
3078 to their contents as of a specific revision. This can be helpful
3078 to their contents as of a specific revision. This can be helpful
3079 to "roll back" some or all of an earlier change. See :hg:`help
3079 to "roll back" some or all of an earlier change. See :hg:`help
3080 dates` for a list of formats valid for -d/--date.
3080 dates` for a list of formats valid for -d/--date.
3081
3081
3082 Revert modifies the working directory. It does not commit any
3082 Revert modifies the working directory. It does not commit any
3083 changes, or change the parent of the working directory. If you
3083 changes, or change the parent of the working directory. If you
3084 revert to a revision other than the parent of the working
3084 revert to a revision other than the parent of the working
3085 directory, the reverted files will thus appear modified
3085 directory, the reverted files will thus appear modified
3086 afterwards.
3086 afterwards.
3087
3087
3088 If a file has been deleted, it is restored. If the executable mode
3088 If a file has been deleted, it is restored. If the executable mode
3089 of a file was changed, it is reset.
3089 of a file was changed, it is reset.
3090
3090
3091 If names are given, all files matching the names are reverted.
3091 If names are given, all files matching the names are reverted.
3092 If no arguments are given, no files are reverted.
3092 If no arguments are given, no files are reverted.
3093
3093
3094 Modified files are saved with a .orig suffix before reverting.
3094 Modified files are saved with a .orig suffix before reverting.
3095 To disable these backups, use --no-backup.
3095 To disable these backups, use --no-backup.
3096
3096
3097 Returns 0 on success.
3097 Returns 0 on success.
3098 """
3098 """
3099
3099
3100 if opts.get("date"):
3100 if opts.get("date"):
3101 if opts.get("rev"):
3101 if opts.get("rev"):
3102 raise util.Abort(_("you can't specify a revision and a date"))
3102 raise util.Abort(_("you can't specify a revision and a date"))
3103 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3103 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
3104
3104
3105 parent, p2 = repo.dirstate.parents()
3105 parent, p2 = repo.dirstate.parents()
3106 if not opts.get('rev') and p2 != nullid:
3106 if not opts.get('rev') and p2 != nullid:
3107 raise util.Abort(_('uncommitted merge - '
3107 raise util.Abort(_('uncommitted merge - '
3108 'use "hg update", see "hg help revert"'))
3108 'use "hg update", see "hg help revert"'))
3109
3109
3110 if not pats and not opts.get('all'):
3110 if not pats and not opts.get('all'):
3111 raise util.Abort(_('no files or directories specified; '
3111 raise util.Abort(_('no files or directories specified; '
3112 'use --all to revert the whole repo'))
3112 'use --all to revert the whole repo'))
3113
3113
3114 ctx = cmdutil.revsingle(repo, opts.get('rev'))
3114 ctx = cmdutil.revsingle(repo, opts.get('rev'))
3115 node = ctx.node()
3115 node = ctx.node()
3116 mf = ctx.manifest()
3116 mf = ctx.manifest()
3117 if node == parent:
3117 if node == parent:
3118 pmf = mf
3118 pmf = mf
3119 else:
3119 else:
3120 pmf = None
3120 pmf = None
3121
3121
3122 # need all matching names in dirstate and manifest of target rev,
3122 # need all matching names in dirstate and manifest of target rev,
3123 # so have to walk both. do not print errors if files exist in one
3123 # so have to walk both. do not print errors if files exist in one
3124 # but not other.
3124 # but not other.
3125
3125
3126 names = {}
3126 names = {}
3127
3127
3128 wlock = repo.wlock()
3128 wlock = repo.wlock()
3129 try:
3129 try:
3130 # walk dirstate.
3130 # walk dirstate.
3131
3131
3132 m = cmdutil.match(repo, pats, opts)
3132 m = cmdutil.match(repo, pats, opts)
3133 m.bad = lambda x, y: False
3133 m.bad = lambda x, y: False
3134 for abs in repo.walk(m):
3134 for abs in repo.walk(m):
3135 names[abs] = m.rel(abs), m.exact(abs)
3135 names[abs] = m.rel(abs), m.exact(abs)
3136
3136
3137 # walk target manifest.
3137 # walk target manifest.
3138
3138
3139 def badfn(path, msg):
3139 def badfn(path, msg):
3140 if path in names:
3140 if path in names:
3141 return
3141 return
3142 path_ = path + '/'
3142 path_ = path + '/'
3143 for f in names:
3143 for f in names:
3144 if f.startswith(path_):
3144 if f.startswith(path_):
3145 return
3145 return
3146 ui.warn("%s: %s\n" % (m.rel(path), msg))
3146 ui.warn("%s: %s\n" % (m.rel(path), msg))
3147
3147
3148 m = cmdutil.match(repo, pats, opts)
3148 m = cmdutil.match(repo, pats, opts)
3149 m.bad = badfn
3149 m.bad = badfn
3150 for abs in repo[node].walk(m):
3150 for abs in repo[node].walk(m):
3151 if abs not in names:
3151 if abs not in names:
3152 names[abs] = m.rel(abs), m.exact(abs)
3152 names[abs] = m.rel(abs), m.exact(abs)
3153
3153
3154 m = cmdutil.matchfiles(repo, names)
3154 m = cmdutil.matchfiles(repo, names)
3155 changes = repo.status(match=m)[:4]
3155 changes = repo.status(match=m)[:4]
3156 modified, added, removed, deleted = map(set, changes)
3156 modified, added, removed, deleted = map(set, changes)
3157
3157
3158 # if f is a rename, also revert the source
3158 # if f is a rename, also revert the source
3159 cwd = repo.getcwd()
3159 cwd = repo.getcwd()
3160 for f in added:
3160 for f in added:
3161 src = repo.dirstate.copied(f)
3161 src = repo.dirstate.copied(f)
3162 if src and src not in names and repo.dirstate[src] == 'r':
3162 if src and src not in names and repo.dirstate[src] == 'r':
3163 removed.add(src)
3163 removed.add(src)
3164 names[src] = (repo.pathto(src, cwd), True)
3164 names[src] = (repo.pathto(src, cwd), True)
3165
3165
3166 def removeforget(abs):
3166 def removeforget(abs):
3167 if repo.dirstate[abs] == 'a':
3167 if repo.dirstate[abs] == 'a':
3168 return _('forgetting %s\n')
3168 return _('forgetting %s\n')
3169 return _('removing %s\n')
3169 return _('removing %s\n')
3170
3170
3171 revert = ([], _('reverting %s\n'))
3171 revert = ([], _('reverting %s\n'))
3172 add = ([], _('adding %s\n'))
3172 add = ([], _('adding %s\n'))
3173 remove = ([], removeforget)
3173 remove = ([], removeforget)
3174 undelete = ([], _('undeleting %s\n'))
3174 undelete = ([], _('undeleting %s\n'))
3175
3175
3176 disptable = (
3176 disptable = (
3177 # dispatch table:
3177 # dispatch table:
3178 # file state
3178 # file state
3179 # action if in target manifest
3179 # action if in target manifest
3180 # action if not in target manifest
3180 # action if not in target manifest
3181 # make backup if in target manifest
3181 # make backup if in target manifest
3182 # make backup if not in target manifest
3182 # make backup if not in target manifest
3183 (modified, revert, remove, True, True),
3183 (modified, revert, remove, True, True),
3184 (added, revert, remove, True, False),
3184 (added, revert, remove, True, False),
3185 (removed, undelete, None, False, False),
3185 (removed, undelete, None, False, False),
3186 (deleted, revert, remove, False, False),
3186 (deleted, revert, remove, False, False),
3187 )
3187 )
3188
3188
3189 for abs, (rel, exact) in sorted(names.items()):
3189 for abs, (rel, exact) in sorted(names.items()):
3190 mfentry = mf.get(abs)
3190 mfentry = mf.get(abs)
3191 target = repo.wjoin(abs)
3191 target = repo.wjoin(abs)
3192 def handle(xlist, dobackup):
3192 def handle(xlist, dobackup):
3193 xlist[0].append(abs)
3193 xlist[0].append(abs)
3194 if (dobackup and not opts.get('no_backup') and
3194 if (dobackup and not opts.get('no_backup') and
3195 os.path.lexists(target)):
3195 os.path.lexists(target)):
3196 bakname = "%s.orig" % rel
3196 bakname = "%s.orig" % rel
3197 ui.note(_('saving current version of %s as %s\n') %
3197 ui.note(_('saving current version of %s as %s\n') %
3198 (rel, bakname))
3198 (rel, bakname))
3199 if not opts.get('dry_run'):
3199 if not opts.get('dry_run'):
3200 util.rename(target, bakname)
3200 util.rename(target, bakname)
3201 if ui.verbose or not exact:
3201 if ui.verbose or not exact:
3202 msg = xlist[1]
3202 msg = xlist[1]
3203 if not isinstance(msg, basestring):
3203 if not isinstance(msg, basestring):
3204 msg = msg(abs)
3204 msg = msg(abs)
3205 ui.status(msg % rel)
3205 ui.status(msg % rel)
3206 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3206 for table, hitlist, misslist, backuphit, backupmiss in disptable:
3207 if abs not in table:
3207 if abs not in table:
3208 continue
3208 continue
3209 # file has changed in dirstate
3209 # file has changed in dirstate
3210 if mfentry:
3210 if mfentry:
3211 handle(hitlist, backuphit)
3211 handle(hitlist, backuphit)
3212 elif misslist is not None:
3212 elif misslist is not None:
3213 handle(misslist, backupmiss)
3213 handle(misslist, backupmiss)
3214 break
3214 break
3215 else:
3215 else:
3216 if abs not in repo.dirstate:
3216 if abs not in repo.dirstate:
3217 if mfentry:
3217 if mfentry:
3218 handle(add, True)
3218 handle(add, True)
3219 elif exact:
3219 elif exact:
3220 ui.warn(_('file not managed: %s\n') % rel)
3220 ui.warn(_('file not managed: %s\n') % rel)
3221 continue
3221 continue
3222 # file has not changed in dirstate
3222 # file has not changed in dirstate
3223 if node == parent:
3223 if node == parent:
3224 if exact:
3224 if exact:
3225 ui.warn(_('no changes needed to %s\n') % rel)
3225 ui.warn(_('no changes needed to %s\n') % rel)
3226 continue
3226 continue
3227 if pmf is None:
3227 if pmf is None:
3228 # only need parent manifest in this unlikely case,
3228 # only need parent manifest in this unlikely case,
3229 # so do not read by default
3229 # so do not read by default
3230 pmf = repo[parent].manifest()
3230 pmf = repo[parent].manifest()
3231 if abs in pmf:
3231 if abs in pmf:
3232 if mfentry:
3232 if mfentry:
3233 # if version of file is same in parent and target
3233 # if version of file is same in parent and target
3234 # manifests, do nothing
3234 # manifests, do nothing
3235 if (pmf[abs] != mfentry or
3235 if (pmf[abs] != mfentry or
3236 pmf.flags(abs) != mf.flags(abs)):
3236 pmf.flags(abs) != mf.flags(abs)):
3237 handle(revert, False)
3237 handle(revert, False)
3238 else:
3238 else:
3239 handle(remove, False)
3239 handle(remove, False)
3240
3240
3241 if not opts.get('dry_run'):
3241 if not opts.get('dry_run'):
3242 def checkout(f):
3242 def checkout(f):
3243 fc = ctx[f]
3243 fc = ctx[f]
3244 repo.wwrite(f, fc.data(), fc.flags())
3244 repo.wwrite(f, fc.data(), fc.flags())
3245
3245
3246 audit_path = util.path_auditor(repo.root)
3246 audit_path = util.path_auditor(repo.root)
3247 for f in remove[0]:
3247 for f in remove[0]:
3248 if repo.dirstate[f] == 'a':
3248 if repo.dirstate[f] == 'a':
3249 repo.dirstate.forget(f)
3249 repo.dirstate.forget(f)
3250 continue
3250 continue
3251 audit_path(f)
3251 audit_path(f)
3252 try:
3252 try:
3253 util.unlink(repo.wjoin(f))
3253 util.unlinkpath(repo.wjoin(f))
3254 except OSError:
3254 except OSError:
3255 pass
3255 pass
3256 repo.dirstate.remove(f)
3256 repo.dirstate.remove(f)
3257
3257
3258 normal = None
3258 normal = None
3259 if node == parent:
3259 if node == parent:
3260 # We're reverting to our parent. If possible, we'd like status
3260 # We're reverting to our parent. If possible, we'd like status
3261 # to report the file as clean. We have to use normallookup for
3261 # to report the file as clean. We have to use normallookup for
3262 # merges to avoid losing information about merged/dirty files.
3262 # merges to avoid losing information about merged/dirty files.
3263 if p2 != nullid:
3263 if p2 != nullid:
3264 normal = repo.dirstate.normallookup
3264 normal = repo.dirstate.normallookup
3265 else:
3265 else:
3266 normal = repo.dirstate.normal
3266 normal = repo.dirstate.normal
3267 for f in revert[0]:
3267 for f in revert[0]:
3268 checkout(f)
3268 checkout(f)
3269 if normal:
3269 if normal:
3270 normal(f)
3270 normal(f)
3271
3271
3272 for f in add[0]:
3272 for f in add[0]:
3273 checkout(f)
3273 checkout(f)
3274 repo.dirstate.add(f)
3274 repo.dirstate.add(f)
3275
3275
3276 normal = repo.dirstate.normallookup
3276 normal = repo.dirstate.normallookup
3277 if node == parent and p2 == nullid:
3277 if node == parent and p2 == nullid:
3278 normal = repo.dirstate.normal
3278 normal = repo.dirstate.normal
3279 for f in undelete[0]:
3279 for f in undelete[0]:
3280 checkout(f)
3280 checkout(f)
3281 normal(f)
3281 normal(f)
3282
3282
3283 finally:
3283 finally:
3284 wlock.release()
3284 wlock.release()
3285
3285
3286 def rollback(ui, repo, **opts):
3286 def rollback(ui, repo, **opts):
3287 """roll back the last transaction (dangerous)
3287 """roll back the last transaction (dangerous)
3288
3288
3289 This command should be used with care. There is only one level of
3289 This command should be used with care. There is only one level of
3290 rollback, and there is no way to undo a rollback. It will also
3290 rollback, and there is no way to undo a rollback. It will also
3291 restore the dirstate at the time of the last transaction, losing
3291 restore the dirstate at the time of the last transaction, losing
3292 any dirstate changes since that time. This command does not alter
3292 any dirstate changes since that time. This command does not alter
3293 the working directory.
3293 the working directory.
3294
3294
3295 Transactions are used to encapsulate the effects of all commands
3295 Transactions are used to encapsulate the effects of all commands
3296 that create new changesets or propagate existing changesets into a
3296 that create new changesets or propagate existing changesets into a
3297 repository. For example, the following commands are transactional,
3297 repository. For example, the following commands are transactional,
3298 and their effects can be rolled back:
3298 and their effects can be rolled back:
3299
3299
3300 - commit
3300 - commit
3301 - import
3301 - import
3302 - pull
3302 - pull
3303 - push (with this repository as the destination)
3303 - push (with this repository as the destination)
3304 - unbundle
3304 - unbundle
3305
3305
3306 This command is not intended for use on public repositories. Once
3306 This command is not intended for use on public repositories. Once
3307 changes are visible for pull by other users, rolling a transaction
3307 changes are visible for pull by other users, rolling a transaction
3308 back locally is ineffective (someone else may already have pulled
3308 back locally is ineffective (someone else may already have pulled
3309 the changes). Furthermore, a race is possible with readers of the
3309 the changes). Furthermore, a race is possible with readers of the
3310 repository; for example an in-progress pull from the repository
3310 repository; for example an in-progress pull from the repository
3311 may fail if a rollback is performed.
3311 may fail if a rollback is performed.
3312
3312
3313 Returns 0 on success, 1 if no rollback data is available.
3313 Returns 0 on success, 1 if no rollback data is available.
3314 """
3314 """
3315 return repo.rollback(opts.get('dry_run'))
3315 return repo.rollback(opts.get('dry_run'))
3316
3316
3317 def root(ui, repo):
3317 def root(ui, repo):
3318 """print the root (top) of the current working directory
3318 """print the root (top) of the current working directory
3319
3319
3320 Print the root directory of the current repository.
3320 Print the root directory of the current repository.
3321
3321
3322 Returns 0 on success.
3322 Returns 0 on success.
3323 """
3323 """
3324 ui.write(repo.root + "\n")
3324 ui.write(repo.root + "\n")
3325
3325
3326 def serve(ui, repo, **opts):
3326 def serve(ui, repo, **opts):
3327 """start stand-alone webserver
3327 """start stand-alone webserver
3328
3328
3329 Start a local HTTP repository browser and pull server. You can use
3329 Start a local HTTP repository browser and pull server. You can use
3330 this for ad-hoc sharing and browsing of repositories. It is
3330 this for ad-hoc sharing and browsing of repositories. It is
3331 recommended to use a real web server to serve a repository for
3331 recommended to use a real web server to serve a repository for
3332 longer periods of time.
3332 longer periods of time.
3333
3333
3334 Please note that the server does not implement access control.
3334 Please note that the server does not implement access control.
3335 This means that, by default, anybody can read from the server and
3335 This means that, by default, anybody can read from the server and
3336 nobody can write to it by default. Set the ``web.allow_push``
3336 nobody can write to it by default. Set the ``web.allow_push``
3337 option to ``*`` to allow everybody to push to the server. You
3337 option to ``*`` to allow everybody to push to the server. You
3338 should use a real web server if you need to authenticate users.
3338 should use a real web server if you need to authenticate users.
3339
3339
3340 By default, the server logs accesses to stdout and errors to
3340 By default, the server logs accesses to stdout and errors to
3341 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3341 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
3342 files.
3342 files.
3343
3343
3344 To have the server choose a free port number to listen on, specify
3344 To have the server choose a free port number to listen on, specify
3345 a port number of 0; in this case, the server will print the port
3345 a port number of 0; in this case, the server will print the port
3346 number it uses.
3346 number it uses.
3347
3347
3348 Returns 0 on success.
3348 Returns 0 on success.
3349 """
3349 """
3350
3350
3351 if opts["stdio"]:
3351 if opts["stdio"]:
3352 if repo is None:
3352 if repo is None:
3353 raise error.RepoError(_("There is no Mercurial repository here"
3353 raise error.RepoError(_("There is no Mercurial repository here"
3354 " (.hg not found)"))
3354 " (.hg not found)"))
3355 s = sshserver.sshserver(ui, repo)
3355 s = sshserver.sshserver(ui, repo)
3356 s.serve_forever()
3356 s.serve_forever()
3357
3357
3358 # this way we can check if something was given in the command-line
3358 # this way we can check if something was given in the command-line
3359 if opts.get('port'):
3359 if opts.get('port'):
3360 opts['port'] = util.getport(opts.get('port'))
3360 opts['port'] = util.getport(opts.get('port'))
3361
3361
3362 baseui = repo and repo.baseui or ui
3362 baseui = repo and repo.baseui or ui
3363 optlist = ("name templates style address port prefix ipv6"
3363 optlist = ("name templates style address port prefix ipv6"
3364 " accesslog errorlog certificate encoding")
3364 " accesslog errorlog certificate encoding")
3365 for o in optlist.split():
3365 for o in optlist.split():
3366 val = opts.get(o, '')
3366 val = opts.get(o, '')
3367 if val in (None, ''): # should check against default options instead
3367 if val in (None, ''): # should check against default options instead
3368 continue
3368 continue
3369 baseui.setconfig("web", o, val)
3369 baseui.setconfig("web", o, val)
3370 if repo and repo.ui != baseui:
3370 if repo and repo.ui != baseui:
3371 repo.ui.setconfig("web", o, val)
3371 repo.ui.setconfig("web", o, val)
3372
3372
3373 o = opts.get('web_conf') or opts.get('webdir_conf')
3373 o = opts.get('web_conf') or opts.get('webdir_conf')
3374 if not o:
3374 if not o:
3375 if not repo:
3375 if not repo:
3376 raise error.RepoError(_("There is no Mercurial repository"
3376 raise error.RepoError(_("There is no Mercurial repository"
3377 " here (.hg not found)"))
3377 " here (.hg not found)"))
3378 o = repo.root
3378 o = repo.root
3379
3379
3380 app = hgweb.hgweb(o, baseui=ui)
3380 app = hgweb.hgweb(o, baseui=ui)
3381
3381
3382 class service(object):
3382 class service(object):
3383 def init(self):
3383 def init(self):
3384 util.set_signal_handler()
3384 util.set_signal_handler()
3385 self.httpd = hgweb.server.create_server(ui, app)
3385 self.httpd = hgweb.server.create_server(ui, app)
3386
3386
3387 if opts['port'] and not ui.verbose:
3387 if opts['port'] and not ui.verbose:
3388 return
3388 return
3389
3389
3390 if self.httpd.prefix:
3390 if self.httpd.prefix:
3391 prefix = self.httpd.prefix.strip('/') + '/'
3391 prefix = self.httpd.prefix.strip('/') + '/'
3392 else:
3392 else:
3393 prefix = ''
3393 prefix = ''
3394
3394
3395 port = ':%d' % self.httpd.port
3395 port = ':%d' % self.httpd.port
3396 if port == ':80':
3396 if port == ':80':
3397 port = ''
3397 port = ''
3398
3398
3399 bindaddr = self.httpd.addr
3399 bindaddr = self.httpd.addr
3400 if bindaddr == '0.0.0.0':
3400 if bindaddr == '0.0.0.0':
3401 bindaddr = '*'
3401 bindaddr = '*'
3402 elif ':' in bindaddr: # IPv6
3402 elif ':' in bindaddr: # IPv6
3403 bindaddr = '[%s]' % bindaddr
3403 bindaddr = '[%s]' % bindaddr
3404
3404
3405 fqaddr = self.httpd.fqaddr
3405 fqaddr = self.httpd.fqaddr
3406 if ':' in fqaddr:
3406 if ':' in fqaddr:
3407 fqaddr = '[%s]' % fqaddr
3407 fqaddr = '[%s]' % fqaddr
3408 if opts['port']:
3408 if opts['port']:
3409 write = ui.status
3409 write = ui.status
3410 else:
3410 else:
3411 write = ui.write
3411 write = ui.write
3412 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3412 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
3413 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3413 (fqaddr, port, prefix, bindaddr, self.httpd.port))
3414
3414
3415 def run(self):
3415 def run(self):
3416 self.httpd.serve_forever()
3416 self.httpd.serve_forever()
3417
3417
3418 service = service()
3418 service = service()
3419
3419
3420 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3420 cmdutil.service(opts, initfn=service.init, runfn=service.run)
3421
3421
3422 def status(ui, repo, *pats, **opts):
3422 def status(ui, repo, *pats, **opts):
3423 """show changed files in the working directory
3423 """show changed files in the working directory
3424
3424
3425 Show status of files in the repository. If names are given, only
3425 Show status of files in the repository. If names are given, only
3426 files that match are shown. Files that are clean or ignored or
3426 files that match are shown. Files that are clean or ignored or
3427 the source of a copy/move operation, are not listed unless
3427 the source of a copy/move operation, are not listed unless
3428 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3428 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
3429 Unless options described with "show only ..." are given, the
3429 Unless options described with "show only ..." are given, the
3430 options -mardu are used.
3430 options -mardu are used.
3431
3431
3432 Option -q/--quiet hides untracked (unknown and ignored) files
3432 Option -q/--quiet hides untracked (unknown and ignored) files
3433 unless explicitly requested with -u/--unknown or -i/--ignored.
3433 unless explicitly requested with -u/--unknown or -i/--ignored.
3434
3434
3435 .. note::
3435 .. note::
3436 status may appear to disagree with diff if permissions have
3436 status may appear to disagree with diff if permissions have
3437 changed or a merge has occurred. The standard diff format does
3437 changed or a merge has occurred. The standard diff format does
3438 not report permission changes and diff only reports changes
3438 not report permission changes and diff only reports changes
3439 relative to one merge parent.
3439 relative to one merge parent.
3440
3440
3441 If one revision is given, it is used as the base revision.
3441 If one revision is given, it is used as the base revision.
3442 If two revisions are given, the differences between them are
3442 If two revisions are given, the differences between them are
3443 shown. The --change option can also be used as a shortcut to list
3443 shown. The --change option can also be used as a shortcut to list
3444 the changed files of a revision from its first parent.
3444 the changed files of a revision from its first parent.
3445
3445
3446 The codes used to show the status of files are::
3446 The codes used to show the status of files are::
3447
3447
3448 M = modified
3448 M = modified
3449 A = added
3449 A = added
3450 R = removed
3450 R = removed
3451 C = clean
3451 C = clean
3452 ! = missing (deleted by non-hg command, but still tracked)
3452 ! = missing (deleted by non-hg command, but still tracked)
3453 ? = not tracked
3453 ? = not tracked
3454 I = ignored
3454 I = ignored
3455 = origin of the previous file listed as A (added)
3455 = origin of the previous file listed as A (added)
3456
3456
3457 Returns 0 on success.
3457 Returns 0 on success.
3458 """
3458 """
3459
3459
3460 revs = opts.get('rev')
3460 revs = opts.get('rev')
3461 change = opts.get('change')
3461 change = opts.get('change')
3462
3462
3463 if revs and change:
3463 if revs and change:
3464 msg = _('cannot specify --rev and --change at the same time')
3464 msg = _('cannot specify --rev and --change at the same time')
3465 raise util.Abort(msg)
3465 raise util.Abort(msg)
3466 elif change:
3466 elif change:
3467 node2 = repo.lookup(change)
3467 node2 = repo.lookup(change)
3468 node1 = repo[node2].parents()[0].node()
3468 node1 = repo[node2].parents()[0].node()
3469 else:
3469 else:
3470 node1, node2 = cmdutil.revpair(repo, revs)
3470 node1, node2 = cmdutil.revpair(repo, revs)
3471
3471
3472 cwd = (pats and repo.getcwd()) or ''
3472 cwd = (pats and repo.getcwd()) or ''
3473 end = opts.get('print0') and '\0' or '\n'
3473 end = opts.get('print0') and '\0' or '\n'
3474 copy = {}
3474 copy = {}
3475 states = 'modified added removed deleted unknown ignored clean'.split()
3475 states = 'modified added removed deleted unknown ignored clean'.split()
3476 show = [k for k in states if opts.get(k)]
3476 show = [k for k in states if opts.get(k)]
3477 if opts.get('all'):
3477 if opts.get('all'):
3478 show += ui.quiet and (states[:4] + ['clean']) or states
3478 show += ui.quiet and (states[:4] + ['clean']) or states
3479 if not show:
3479 if not show:
3480 show = ui.quiet and states[:4] or states[:5]
3480 show = ui.quiet and states[:4] or states[:5]
3481
3481
3482 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3482 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
3483 'ignored' in show, 'clean' in show, 'unknown' in show,
3483 'ignored' in show, 'clean' in show, 'unknown' in show,
3484 opts.get('subrepos'))
3484 opts.get('subrepos'))
3485 changestates = zip(states, 'MAR!?IC', stat)
3485 changestates = zip(states, 'MAR!?IC', stat)
3486
3486
3487 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3487 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
3488 ctxn = repo[nullid]
3488 ctxn = repo[nullid]
3489 ctx1 = repo[node1]
3489 ctx1 = repo[node1]
3490 ctx2 = repo[node2]
3490 ctx2 = repo[node2]
3491 added = stat[1]
3491 added = stat[1]
3492 if node2 is None:
3492 if node2 is None:
3493 added = stat[0] + stat[1] # merged?
3493 added = stat[0] + stat[1] # merged?
3494
3494
3495 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3495 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
3496 if k in added:
3496 if k in added:
3497 copy[k] = v
3497 copy[k] = v
3498 elif v in added:
3498 elif v in added:
3499 copy[v] = k
3499 copy[v] = k
3500
3500
3501 for state, char, files in changestates:
3501 for state, char, files in changestates:
3502 if state in show:
3502 if state in show:
3503 format = "%s %%s%s" % (char, end)
3503 format = "%s %%s%s" % (char, end)
3504 if opts.get('no_status'):
3504 if opts.get('no_status'):
3505 format = "%%s%s" % end
3505 format = "%%s%s" % end
3506
3506
3507 for f in files:
3507 for f in files:
3508 ui.write(format % repo.pathto(f, cwd),
3508 ui.write(format % repo.pathto(f, cwd),
3509 label='status.' + state)
3509 label='status.' + state)
3510 if f in copy:
3510 if f in copy:
3511 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3511 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
3512 label='status.copied')
3512 label='status.copied')
3513
3513
3514 def summary(ui, repo, **opts):
3514 def summary(ui, repo, **opts):
3515 """summarize working directory state
3515 """summarize working directory state
3516
3516
3517 This generates a brief summary of the working directory state,
3517 This generates a brief summary of the working directory state,
3518 including parents, branch, commit status, and available updates.
3518 including parents, branch, commit status, and available updates.
3519
3519
3520 With the --remote option, this will check the default paths for
3520 With the --remote option, this will check the default paths for
3521 incoming and outgoing changes. This can be time-consuming.
3521 incoming and outgoing changes. This can be time-consuming.
3522
3522
3523 Returns 0 on success.
3523 Returns 0 on success.
3524 """
3524 """
3525
3525
3526 ctx = repo[None]
3526 ctx = repo[None]
3527 parents = ctx.parents()
3527 parents = ctx.parents()
3528 pnode = parents[0].node()
3528 pnode = parents[0].node()
3529
3529
3530 for p in parents:
3530 for p in parents:
3531 # label with log.changeset (instead of log.parent) since this
3531 # label with log.changeset (instead of log.parent) since this
3532 # shows a working directory parent *changeset*:
3532 # shows a working directory parent *changeset*:
3533 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3533 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
3534 label='log.changeset')
3534 label='log.changeset')
3535 ui.write(' '.join(p.tags()), label='log.tag')
3535 ui.write(' '.join(p.tags()), label='log.tag')
3536 if p.rev() == -1:
3536 if p.rev() == -1:
3537 if not len(repo):
3537 if not len(repo):
3538 ui.write(_(' (empty repository)'))
3538 ui.write(_(' (empty repository)'))
3539 else:
3539 else:
3540 ui.write(_(' (no revision checked out)'))
3540 ui.write(_(' (no revision checked out)'))
3541 ui.write('\n')
3541 ui.write('\n')
3542 if p.description():
3542 if p.description():
3543 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3543 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
3544 label='log.summary')
3544 label='log.summary')
3545
3545
3546 branch = ctx.branch()
3546 branch = ctx.branch()
3547 bheads = repo.branchheads(branch)
3547 bheads = repo.branchheads(branch)
3548 m = _('branch: %s\n') % branch
3548 m = _('branch: %s\n') % branch
3549 if branch != 'default':
3549 if branch != 'default':
3550 ui.write(m, label='log.branch')
3550 ui.write(m, label='log.branch')
3551 else:
3551 else:
3552 ui.status(m, label='log.branch')
3552 ui.status(m, label='log.branch')
3553
3553
3554 st = list(repo.status(unknown=True))[:6]
3554 st = list(repo.status(unknown=True))[:6]
3555
3555
3556 c = repo.dirstate.copies()
3556 c = repo.dirstate.copies()
3557 copied, renamed = [], []
3557 copied, renamed = [], []
3558 for d, s in c.iteritems():
3558 for d, s in c.iteritems():
3559 if s in st[2]:
3559 if s in st[2]:
3560 st[2].remove(s)
3560 st[2].remove(s)
3561 renamed.append(d)
3561 renamed.append(d)
3562 else:
3562 else:
3563 copied.append(d)
3563 copied.append(d)
3564 if d in st[1]:
3564 if d in st[1]:
3565 st[1].remove(d)
3565 st[1].remove(d)
3566 st.insert(3, renamed)
3566 st.insert(3, renamed)
3567 st.insert(4, copied)
3567 st.insert(4, copied)
3568
3568
3569 ms = mergemod.mergestate(repo)
3569 ms = mergemod.mergestate(repo)
3570 st.append([f for f in ms if ms[f] == 'u'])
3570 st.append([f for f in ms if ms[f] == 'u'])
3571
3571
3572 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3572 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
3573 st.append(subs)
3573 st.append(subs)
3574
3574
3575 labels = [ui.label(_('%d modified'), 'status.modified'),
3575 labels = [ui.label(_('%d modified'), 'status.modified'),
3576 ui.label(_('%d added'), 'status.added'),
3576 ui.label(_('%d added'), 'status.added'),
3577 ui.label(_('%d removed'), 'status.removed'),
3577 ui.label(_('%d removed'), 'status.removed'),
3578 ui.label(_('%d renamed'), 'status.copied'),
3578 ui.label(_('%d renamed'), 'status.copied'),
3579 ui.label(_('%d copied'), 'status.copied'),
3579 ui.label(_('%d copied'), 'status.copied'),
3580 ui.label(_('%d deleted'), 'status.deleted'),
3580 ui.label(_('%d deleted'), 'status.deleted'),
3581 ui.label(_('%d unknown'), 'status.unknown'),
3581 ui.label(_('%d unknown'), 'status.unknown'),
3582 ui.label(_('%d ignored'), 'status.ignored'),
3582 ui.label(_('%d ignored'), 'status.ignored'),
3583 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3583 ui.label(_('%d unresolved'), 'resolve.unresolved'),
3584 ui.label(_('%d subrepos'), 'status.modified')]
3584 ui.label(_('%d subrepos'), 'status.modified')]
3585 t = []
3585 t = []
3586 for s, l in zip(st, labels):
3586 for s, l in zip(st, labels):
3587 if s:
3587 if s:
3588 t.append(l % len(s))
3588 t.append(l % len(s))
3589
3589
3590 t = ', '.join(t)
3590 t = ', '.join(t)
3591 cleanworkdir = False
3591 cleanworkdir = False
3592
3592
3593 if len(parents) > 1:
3593 if len(parents) > 1:
3594 t += _(' (merge)')
3594 t += _(' (merge)')
3595 elif branch != parents[0].branch():
3595 elif branch != parents[0].branch():
3596 t += _(' (new branch)')
3596 t += _(' (new branch)')
3597 elif (parents[0].extra().get('close') and
3597 elif (parents[0].extra().get('close') and
3598 pnode in repo.branchheads(branch, closed=True)):
3598 pnode in repo.branchheads(branch, closed=True)):
3599 t += _(' (head closed)')
3599 t += _(' (head closed)')
3600 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3600 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
3601 t += _(' (clean)')
3601 t += _(' (clean)')
3602 cleanworkdir = True
3602 cleanworkdir = True
3603 elif pnode not in bheads:
3603 elif pnode not in bheads:
3604 t += _(' (new branch head)')
3604 t += _(' (new branch head)')
3605
3605
3606 if cleanworkdir:
3606 if cleanworkdir:
3607 ui.status(_('commit: %s\n') % t.strip())
3607 ui.status(_('commit: %s\n') % t.strip())
3608 else:
3608 else:
3609 ui.write(_('commit: %s\n') % t.strip())
3609 ui.write(_('commit: %s\n') % t.strip())
3610
3610
3611 # all ancestors of branch heads - all ancestors of parent = new csets
3611 # all ancestors of branch heads - all ancestors of parent = new csets
3612 new = [0] * len(repo)
3612 new = [0] * len(repo)
3613 cl = repo.changelog
3613 cl = repo.changelog
3614 for a in [cl.rev(n) for n in bheads]:
3614 for a in [cl.rev(n) for n in bheads]:
3615 new[a] = 1
3615 new[a] = 1
3616 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3616 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
3617 new[a] = 1
3617 new[a] = 1
3618 for a in [p.rev() for p in parents]:
3618 for a in [p.rev() for p in parents]:
3619 if a >= 0:
3619 if a >= 0:
3620 new[a] = 0
3620 new[a] = 0
3621 for a in cl.ancestors(*[p.rev() for p in parents]):
3621 for a in cl.ancestors(*[p.rev() for p in parents]):
3622 new[a] = 0
3622 new[a] = 0
3623 new = sum(new)
3623 new = sum(new)
3624
3624
3625 if new == 0:
3625 if new == 0:
3626 ui.status(_('update: (current)\n'))
3626 ui.status(_('update: (current)\n'))
3627 elif pnode not in bheads:
3627 elif pnode not in bheads:
3628 ui.write(_('update: %d new changesets (update)\n') % new)
3628 ui.write(_('update: %d new changesets (update)\n') % new)
3629 else:
3629 else:
3630 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3630 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
3631 (new, len(bheads)))
3631 (new, len(bheads)))
3632
3632
3633 if opts.get('remote'):
3633 if opts.get('remote'):
3634 t = []
3634 t = []
3635 source, branches = hg.parseurl(ui.expandpath('default'))
3635 source, branches = hg.parseurl(ui.expandpath('default'))
3636 other = hg.repository(hg.remoteui(repo, {}), source)
3636 other = hg.repository(hg.remoteui(repo, {}), source)
3637 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3637 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3638 ui.debug('comparing with %s\n' % url.hidepassword(source))
3638 ui.debug('comparing with %s\n' % url.hidepassword(source))
3639 repo.ui.pushbuffer()
3639 repo.ui.pushbuffer()
3640 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3640 common, incoming, rheads = discovery.findcommonincoming(repo, other)
3641 repo.ui.popbuffer()
3641 repo.ui.popbuffer()
3642 if incoming:
3642 if incoming:
3643 t.append(_('1 or more incoming'))
3643 t.append(_('1 or more incoming'))
3644
3644
3645 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3645 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
3646 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3646 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
3647 other = hg.repository(hg.remoteui(repo, {}), dest)
3647 other = hg.repository(hg.remoteui(repo, {}), dest)
3648 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3648 ui.debug('comparing with %s\n' % url.hidepassword(dest))
3649 repo.ui.pushbuffer()
3649 repo.ui.pushbuffer()
3650 o = discovery.findoutgoing(repo, other)
3650 o = discovery.findoutgoing(repo, other)
3651 repo.ui.popbuffer()
3651 repo.ui.popbuffer()
3652 o = repo.changelog.nodesbetween(o, None)[0]
3652 o = repo.changelog.nodesbetween(o, None)[0]
3653 if o:
3653 if o:
3654 t.append(_('%d outgoing') % len(o))
3654 t.append(_('%d outgoing') % len(o))
3655
3655
3656 if t:
3656 if t:
3657 ui.write(_('remote: %s\n') % (', '.join(t)))
3657 ui.write(_('remote: %s\n') % (', '.join(t)))
3658 else:
3658 else:
3659 ui.status(_('remote: (synced)\n'))
3659 ui.status(_('remote: (synced)\n'))
3660
3660
3661 def tag(ui, repo, name1, *names, **opts):
3661 def tag(ui, repo, name1, *names, **opts):
3662 """add one or more tags for the current or given revision
3662 """add one or more tags for the current or given revision
3663
3663
3664 Name a particular revision using <name>.
3664 Name a particular revision using <name>.
3665
3665
3666 Tags are used to name particular revisions of the repository and are
3666 Tags are used to name particular revisions of the repository and are
3667 very useful to compare different revisions, to go back to significant
3667 very useful to compare different revisions, to go back to significant
3668 earlier versions or to mark branch points as releases, etc. Changing
3668 earlier versions or to mark branch points as releases, etc. Changing
3669 an existing tag is normally disallowed; use -f/--force to override.
3669 an existing tag is normally disallowed; use -f/--force to override.
3670
3670
3671 If no revision is given, the parent of the working directory is
3671 If no revision is given, the parent of the working directory is
3672 used, or tip if no revision is checked out.
3672 used, or tip if no revision is checked out.
3673
3673
3674 To facilitate version control, distribution, and merging of tags,
3674 To facilitate version control, distribution, and merging of tags,
3675 they are stored as a file named ".hgtags" which is managed similarly
3675 they are stored as a file named ".hgtags" which is managed similarly
3676 to other project files and can be hand-edited if necessary. This
3676 to other project files and can be hand-edited if necessary. This
3677 also means that tagging creates a new commit. The file
3677 also means that tagging creates a new commit. The file
3678 ".hg/localtags" is used for local tags (not shared among
3678 ".hg/localtags" is used for local tags (not shared among
3679 repositories).
3679 repositories).
3680
3680
3681 Tag commits are usually made at the head of a branch. If the parent
3681 Tag commits are usually made at the head of a branch. If the parent
3682 of the working directory is not a branch head, :hg:`tag` aborts; use
3682 of the working directory is not a branch head, :hg:`tag` aborts; use
3683 -f/--force to force the tag commit to be based on a non-head
3683 -f/--force to force the tag commit to be based on a non-head
3684 changeset.
3684 changeset.
3685
3685
3686 See :hg:`help dates` for a list of formats valid for -d/--date.
3686 See :hg:`help dates` for a list of formats valid for -d/--date.
3687
3687
3688 Since tag names have priority over branch names during revision
3688 Since tag names have priority over branch names during revision
3689 lookup, using an existing branch name as a tag name is discouraged.
3689 lookup, using an existing branch name as a tag name is discouraged.
3690
3690
3691 Returns 0 on success.
3691 Returns 0 on success.
3692 """
3692 """
3693
3693
3694 rev_ = "."
3694 rev_ = "."
3695 names = [t.strip() for t in (name1,) + names]
3695 names = [t.strip() for t in (name1,) + names]
3696 if len(names) != len(set(names)):
3696 if len(names) != len(set(names)):
3697 raise util.Abort(_('tag names must be unique'))
3697 raise util.Abort(_('tag names must be unique'))
3698 for n in names:
3698 for n in names:
3699 if n in ['tip', '.', 'null']:
3699 if n in ['tip', '.', 'null']:
3700 raise util.Abort(_('the name \'%s\' is reserved') % n)
3700 raise util.Abort(_('the name \'%s\' is reserved') % n)
3701 if not n:
3701 if not n:
3702 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3702 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
3703 if opts.get('rev') and opts.get('remove'):
3703 if opts.get('rev') and opts.get('remove'):
3704 raise util.Abort(_("--rev and --remove are incompatible"))
3704 raise util.Abort(_("--rev and --remove are incompatible"))
3705 if opts.get('rev'):
3705 if opts.get('rev'):
3706 rev_ = opts['rev']
3706 rev_ = opts['rev']
3707 message = opts.get('message')
3707 message = opts.get('message')
3708 if opts.get('remove'):
3708 if opts.get('remove'):
3709 expectedtype = opts.get('local') and 'local' or 'global'
3709 expectedtype = opts.get('local') and 'local' or 'global'
3710 for n in names:
3710 for n in names:
3711 if not repo.tagtype(n):
3711 if not repo.tagtype(n):
3712 raise util.Abort(_('tag \'%s\' does not exist') % n)
3712 raise util.Abort(_('tag \'%s\' does not exist') % n)
3713 if repo.tagtype(n) != expectedtype:
3713 if repo.tagtype(n) != expectedtype:
3714 if expectedtype == 'global':
3714 if expectedtype == 'global':
3715 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3715 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
3716 else:
3716 else:
3717 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3717 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
3718 rev_ = nullid
3718 rev_ = nullid
3719 if not message:
3719 if not message:
3720 # we don't translate commit messages
3720 # we don't translate commit messages
3721 message = 'Removed tag %s' % ', '.join(names)
3721 message = 'Removed tag %s' % ', '.join(names)
3722 elif not opts.get('force'):
3722 elif not opts.get('force'):
3723 for n in names:
3723 for n in names:
3724 if n in repo.tags():
3724 if n in repo.tags():
3725 raise util.Abort(_('tag \'%s\' already exists '
3725 raise util.Abort(_('tag \'%s\' already exists '
3726 '(use -f to force)') % n)
3726 '(use -f to force)') % n)
3727 if not opts.get('local'):
3727 if not opts.get('local'):
3728 p1, p2 = repo.dirstate.parents()
3728 p1, p2 = repo.dirstate.parents()
3729 if p2 != nullid:
3729 if p2 != nullid:
3730 raise util.Abort(_('uncommitted merge'))
3730 raise util.Abort(_('uncommitted merge'))
3731 bheads = repo.branchheads()
3731 bheads = repo.branchheads()
3732 if not opts.get('force') and bheads and p1 not in bheads:
3732 if not opts.get('force') and bheads and p1 not in bheads:
3733 raise util.Abort(_('not at a branch head (use -f to force)'))
3733 raise util.Abort(_('not at a branch head (use -f to force)'))
3734 r = cmdutil.revsingle(repo, rev_).node()
3734 r = cmdutil.revsingle(repo, rev_).node()
3735
3735
3736 if not message:
3736 if not message:
3737 # we don't translate commit messages
3737 # we don't translate commit messages
3738 message = ('Added tag %s for changeset %s' %
3738 message = ('Added tag %s for changeset %s' %
3739 (', '.join(names), short(r)))
3739 (', '.join(names), short(r)))
3740
3740
3741 date = opts.get('date')
3741 date = opts.get('date')
3742 if date:
3742 if date:
3743 date = util.parsedate(date)
3743 date = util.parsedate(date)
3744
3744
3745 if opts.get('edit'):
3745 if opts.get('edit'):
3746 message = ui.edit(message, ui.username())
3746 message = ui.edit(message, ui.username())
3747
3747
3748 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3748 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
3749
3749
3750 def tags(ui, repo):
3750 def tags(ui, repo):
3751 """list repository tags
3751 """list repository tags
3752
3752
3753 This lists both regular and local tags. When the -v/--verbose
3753 This lists both regular and local tags. When the -v/--verbose
3754 switch is used, a third column "local" is printed for local tags.
3754 switch is used, a third column "local" is printed for local tags.
3755
3755
3756 Returns 0 on success.
3756 Returns 0 on success.
3757 """
3757 """
3758
3758
3759 hexfunc = ui.debugflag and hex or short
3759 hexfunc = ui.debugflag and hex or short
3760 tagtype = ""
3760 tagtype = ""
3761
3761
3762 for t, n in reversed(repo.tagslist()):
3762 for t, n in reversed(repo.tagslist()):
3763 if ui.quiet:
3763 if ui.quiet:
3764 ui.write("%s\n" % t)
3764 ui.write("%s\n" % t)
3765 continue
3765 continue
3766
3766
3767 try:
3767 try:
3768 hn = hexfunc(n)
3768 hn = hexfunc(n)
3769 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3769 r = "%5d:%s" % (repo.changelog.rev(n), hn)
3770 except error.LookupError:
3770 except error.LookupError:
3771 r = " ?:%s" % hn
3771 r = " ?:%s" % hn
3772 else:
3772 else:
3773 spaces = " " * (30 - encoding.colwidth(t))
3773 spaces = " " * (30 - encoding.colwidth(t))
3774 if ui.verbose:
3774 if ui.verbose:
3775 if repo.tagtype(t) == 'local':
3775 if repo.tagtype(t) == 'local':
3776 tagtype = " local"
3776 tagtype = " local"
3777 else:
3777 else:
3778 tagtype = ""
3778 tagtype = ""
3779 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3779 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
3780
3780
3781 def tip(ui, repo, **opts):
3781 def tip(ui, repo, **opts):
3782 """show the tip revision
3782 """show the tip revision
3783
3783
3784 The tip revision (usually just called the tip) is the changeset
3784 The tip revision (usually just called the tip) is the changeset
3785 most recently added to the repository (and therefore the most
3785 most recently added to the repository (and therefore the most
3786 recently changed head).
3786 recently changed head).
3787
3787
3788 If you have just made a commit, that commit will be the tip. If
3788 If you have just made a commit, that commit will be the tip. If
3789 you have just pulled changes from another repository, the tip of
3789 you have just pulled changes from another repository, the tip of
3790 that repository becomes the current tip. The "tip" tag is special
3790 that repository becomes the current tip. The "tip" tag is special
3791 and cannot be renamed or assigned to a different changeset.
3791 and cannot be renamed or assigned to a different changeset.
3792
3792
3793 Returns 0 on success.
3793 Returns 0 on success.
3794 """
3794 """
3795 displayer = cmdutil.show_changeset(ui, repo, opts)
3795 displayer = cmdutil.show_changeset(ui, repo, opts)
3796 displayer.show(repo[len(repo) - 1])
3796 displayer.show(repo[len(repo) - 1])
3797 displayer.close()
3797 displayer.close()
3798
3798
3799 def unbundle(ui, repo, fname1, *fnames, **opts):
3799 def unbundle(ui, repo, fname1, *fnames, **opts):
3800 """apply one or more changegroup files
3800 """apply one or more changegroup files
3801
3801
3802 Apply one or more compressed changegroup files generated by the
3802 Apply one or more compressed changegroup files generated by the
3803 bundle command.
3803 bundle command.
3804
3804
3805 Returns 0 on success, 1 if an update has unresolved files.
3805 Returns 0 on success, 1 if an update has unresolved files.
3806 """
3806 """
3807 fnames = (fname1,) + fnames
3807 fnames = (fname1,) + fnames
3808
3808
3809 lock = repo.lock()
3809 lock = repo.lock()
3810 try:
3810 try:
3811 for fname in fnames:
3811 for fname in fnames:
3812 f = url.open(ui, fname)
3812 f = url.open(ui, fname)
3813 gen = changegroup.readbundle(f, fname)
3813 gen = changegroup.readbundle(f, fname)
3814 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
3814 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
3815 lock=lock)
3815 lock=lock)
3816 finally:
3816 finally:
3817 lock.release()
3817 lock.release()
3818
3818
3819 return postincoming(ui, repo, modheads, opts.get('update'), None)
3819 return postincoming(ui, repo, modheads, opts.get('update'), None)
3820
3820
3821 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3821 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
3822 """update working directory (or switch revisions)
3822 """update working directory (or switch revisions)
3823
3823
3824 Update the repository's working directory to the specified
3824 Update the repository's working directory to the specified
3825 changeset. If no changeset is specified, update to the tip of the
3825 changeset. If no changeset is specified, update to the tip of the
3826 current named branch.
3826 current named branch.
3827
3827
3828 If the changeset is not a descendant of the working directory's
3828 If the changeset is not a descendant of the working directory's
3829 parent, the update is aborted. With the -c/--check option, the
3829 parent, the update is aborted. With the -c/--check option, the
3830 working directory is checked for uncommitted changes; if none are
3830 working directory is checked for uncommitted changes; if none are
3831 found, the working directory is updated to the specified
3831 found, the working directory is updated to the specified
3832 changeset.
3832 changeset.
3833
3833
3834 The following rules apply when the working directory contains
3834 The following rules apply when the working directory contains
3835 uncommitted changes:
3835 uncommitted changes:
3836
3836
3837 1. If neither -c/--check nor -C/--clean is specified, and if
3837 1. If neither -c/--check nor -C/--clean is specified, and if
3838 the requested changeset is an ancestor or descendant of
3838 the requested changeset is an ancestor or descendant of
3839 the working directory's parent, the uncommitted changes
3839 the working directory's parent, the uncommitted changes
3840 are merged into the requested changeset and the merged
3840 are merged into the requested changeset and the merged
3841 result is left uncommitted. If the requested changeset is
3841 result is left uncommitted. If the requested changeset is
3842 not an ancestor or descendant (that is, it is on another
3842 not an ancestor or descendant (that is, it is on another
3843 branch), the update is aborted and the uncommitted changes
3843 branch), the update is aborted and the uncommitted changes
3844 are preserved.
3844 are preserved.
3845
3845
3846 2. With the -c/--check option, the update is aborted and the
3846 2. With the -c/--check option, the update is aborted and the
3847 uncommitted changes are preserved.
3847 uncommitted changes are preserved.
3848
3848
3849 3. With the -C/--clean option, uncommitted changes are discarded and
3849 3. With the -C/--clean option, uncommitted changes are discarded and
3850 the working directory is updated to the requested changeset.
3850 the working directory is updated to the requested changeset.
3851
3851
3852 Use null as the changeset to remove the working directory (like
3852 Use null as the changeset to remove the working directory (like
3853 :hg:`clone -U`).
3853 :hg:`clone -U`).
3854
3854
3855 If you want to update just one file to an older changeset, use
3855 If you want to update just one file to an older changeset, use
3856 :hg:`revert`.
3856 :hg:`revert`.
3857
3857
3858 See :hg:`help dates` for a list of formats valid for -d/--date.
3858 See :hg:`help dates` for a list of formats valid for -d/--date.
3859
3859
3860 Returns 0 on success, 1 if there are unresolved files.
3860 Returns 0 on success, 1 if there are unresolved files.
3861 """
3861 """
3862 if rev and node:
3862 if rev and node:
3863 raise util.Abort(_("please specify just one revision"))
3863 raise util.Abort(_("please specify just one revision"))
3864
3864
3865 if not rev:
3865 if not rev:
3866 rev = node
3866 rev = node
3867
3867
3868 rev = cmdutil.revsingle(repo, rev, rev).rev()
3868 rev = cmdutil.revsingle(repo, rev, rev).rev()
3869
3869
3870 if check and clean:
3870 if check and clean:
3871 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3871 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
3872
3872
3873 if check:
3873 if check:
3874 # we could use dirty() but we can ignore merge and branch trivia
3874 # we could use dirty() but we can ignore merge and branch trivia
3875 c = repo[None]
3875 c = repo[None]
3876 if c.modified() or c.added() or c.removed():
3876 if c.modified() or c.added() or c.removed():
3877 raise util.Abort(_("uncommitted local changes"))
3877 raise util.Abort(_("uncommitted local changes"))
3878
3878
3879 if date:
3879 if date:
3880 if rev:
3880 if rev:
3881 raise util.Abort(_("you can't specify a revision and a date"))
3881 raise util.Abort(_("you can't specify a revision and a date"))
3882 rev = cmdutil.finddate(ui, repo, date)
3882 rev = cmdutil.finddate(ui, repo, date)
3883
3883
3884 if clean or check:
3884 if clean or check:
3885 return hg.clean(repo, rev)
3885 return hg.clean(repo, rev)
3886 else:
3886 else:
3887 return hg.update(repo, rev)
3887 return hg.update(repo, rev)
3888
3888
3889 def verify(ui, repo):
3889 def verify(ui, repo):
3890 """verify the integrity of the repository
3890 """verify the integrity of the repository
3891
3891
3892 Verify the integrity of the current repository.
3892 Verify the integrity of the current repository.
3893
3893
3894 This will perform an extensive check of the repository's
3894 This will perform an extensive check of the repository's
3895 integrity, validating the hashes and checksums of each entry in
3895 integrity, validating the hashes and checksums of each entry in
3896 the changelog, manifest, and tracked files, as well as the
3896 the changelog, manifest, and tracked files, as well as the
3897 integrity of their crosslinks and indices.
3897 integrity of their crosslinks and indices.
3898
3898
3899 Returns 0 on success, 1 if errors are encountered.
3899 Returns 0 on success, 1 if errors are encountered.
3900 """
3900 """
3901 return hg.verify(repo)
3901 return hg.verify(repo)
3902
3902
3903 def version_(ui):
3903 def version_(ui):
3904 """output version and copyright information"""
3904 """output version and copyright information"""
3905 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3905 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3906 % util.version())
3906 % util.version())
3907 ui.status(_(
3907 ui.status(_(
3908 "(see http://mercurial.selenic.com for more information)\n"
3908 "(see http://mercurial.selenic.com for more information)\n"
3909 "\nCopyright (C) 2005-2010 Matt Mackall and others\n"
3909 "\nCopyright (C) 2005-2010 Matt Mackall and others\n"
3910 "This is free software; see the source for copying conditions. "
3910 "This is free software; see the source for copying conditions. "
3911 "There is NO\nwarranty; "
3911 "There is NO\nwarranty; "
3912 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3912 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3913 ))
3913 ))
3914
3914
3915 # Command options and aliases are listed here, alphabetically
3915 # Command options and aliases are listed here, alphabetically
3916
3916
3917 globalopts = [
3917 globalopts = [
3918 ('R', 'repository', '',
3918 ('R', 'repository', '',
3919 _('repository root directory or name of overlay bundle file'),
3919 _('repository root directory or name of overlay bundle file'),
3920 _('REPO')),
3920 _('REPO')),
3921 ('', 'cwd', '',
3921 ('', 'cwd', '',
3922 _('change working directory'), _('DIR')),
3922 _('change working directory'), _('DIR')),
3923 ('y', 'noninteractive', None,
3923 ('y', 'noninteractive', None,
3924 _('do not prompt, assume \'yes\' for any required answers')),
3924 _('do not prompt, assume \'yes\' for any required answers')),
3925 ('q', 'quiet', None, _('suppress output')),
3925 ('q', 'quiet', None, _('suppress output')),
3926 ('v', 'verbose', None, _('enable additional output')),
3926 ('v', 'verbose', None, _('enable additional output')),
3927 ('', 'config', [],
3927 ('', 'config', [],
3928 _('set/override config option (use \'section.name=value\')'),
3928 _('set/override config option (use \'section.name=value\')'),
3929 _('CONFIG')),
3929 _('CONFIG')),
3930 ('', 'debug', None, _('enable debugging output')),
3930 ('', 'debug', None, _('enable debugging output')),
3931 ('', 'debugger', None, _('start debugger')),
3931 ('', 'debugger', None, _('start debugger')),
3932 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
3932 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
3933 _('ENCODE')),
3933 _('ENCODE')),
3934 ('', 'encodingmode', encoding.encodingmode,
3934 ('', 'encodingmode', encoding.encodingmode,
3935 _('set the charset encoding mode'), _('MODE')),
3935 _('set the charset encoding mode'), _('MODE')),
3936 ('', 'traceback', None, _('always print a traceback on exception')),
3936 ('', 'traceback', None, _('always print a traceback on exception')),
3937 ('', 'time', None, _('time how long the command takes')),
3937 ('', 'time', None, _('time how long the command takes')),
3938 ('', 'profile', None, _('print command execution profile')),
3938 ('', 'profile', None, _('print command execution profile')),
3939 ('', 'version', None, _('output version information and exit')),
3939 ('', 'version', None, _('output version information and exit')),
3940 ('h', 'help', None, _('display help and exit')),
3940 ('h', 'help', None, _('display help and exit')),
3941 ]
3941 ]
3942
3942
3943 dryrunopts = [('n', 'dry-run', None,
3943 dryrunopts = [('n', 'dry-run', None,
3944 _('do not perform actions, just print output'))]
3944 _('do not perform actions, just print output'))]
3945
3945
3946 remoteopts = [
3946 remoteopts = [
3947 ('e', 'ssh', '',
3947 ('e', 'ssh', '',
3948 _('specify ssh command to use'), _('CMD')),
3948 _('specify ssh command to use'), _('CMD')),
3949 ('', 'remotecmd', '',
3949 ('', 'remotecmd', '',
3950 _('specify hg command to run on the remote side'), _('CMD')),
3950 _('specify hg command to run on the remote side'), _('CMD')),
3951 ]
3951 ]
3952
3952
3953 walkopts = [
3953 walkopts = [
3954 ('I', 'include', [],
3954 ('I', 'include', [],
3955 _('include names matching the given patterns'), _('PATTERN')),
3955 _('include names matching the given patterns'), _('PATTERN')),
3956 ('X', 'exclude', [],
3956 ('X', 'exclude', [],
3957 _('exclude names matching the given patterns'), _('PATTERN')),
3957 _('exclude names matching the given patterns'), _('PATTERN')),
3958 ]
3958 ]
3959
3959
3960 commitopts = [
3960 commitopts = [
3961 ('m', 'message', '',
3961 ('m', 'message', '',
3962 _('use text as commit message'), _('TEXT')),
3962 _('use text as commit message'), _('TEXT')),
3963 ('l', 'logfile', '',
3963 ('l', 'logfile', '',
3964 _('read commit message from file'), _('FILE')),
3964 _('read commit message from file'), _('FILE')),
3965 ]
3965 ]
3966
3966
3967 commitopts2 = [
3967 commitopts2 = [
3968 ('d', 'date', '',
3968 ('d', 'date', '',
3969 _('record datecode as commit date'), _('DATE')),
3969 _('record datecode as commit date'), _('DATE')),
3970 ('u', 'user', '',
3970 ('u', 'user', '',
3971 _('record the specified user as committer'), _('USER')),
3971 _('record the specified user as committer'), _('USER')),
3972 ]
3972 ]
3973
3973
3974 templateopts = [
3974 templateopts = [
3975 ('', 'style', '',
3975 ('', 'style', '',
3976 _('display using template map file'), _('STYLE')),
3976 _('display using template map file'), _('STYLE')),
3977 ('', 'template', '',
3977 ('', 'template', '',
3978 _('display with template'), _('TEMPLATE')),
3978 _('display with template'), _('TEMPLATE')),
3979 ]
3979 ]
3980
3980
3981 logopts = [
3981 logopts = [
3982 ('p', 'patch', None, _('show patch')),
3982 ('p', 'patch', None, _('show patch')),
3983 ('g', 'git', None, _('use git extended diff format')),
3983 ('g', 'git', None, _('use git extended diff format')),
3984 ('l', 'limit', '',
3984 ('l', 'limit', '',
3985 _('limit number of changes displayed'), _('NUM')),
3985 _('limit number of changes displayed'), _('NUM')),
3986 ('M', 'no-merges', None, _('do not show merges')),
3986 ('M', 'no-merges', None, _('do not show merges')),
3987 ('', 'stat', None, _('output diffstat-style summary of changes')),
3987 ('', 'stat', None, _('output diffstat-style summary of changes')),
3988 ] + templateopts
3988 ] + templateopts
3989
3989
3990 diffopts = [
3990 diffopts = [
3991 ('a', 'text', None, _('treat all files as text')),
3991 ('a', 'text', None, _('treat all files as text')),
3992 ('g', 'git', None, _('use git extended diff format')),
3992 ('g', 'git', None, _('use git extended diff format')),
3993 ('', 'nodates', None, _('omit dates from diff headers'))
3993 ('', 'nodates', None, _('omit dates from diff headers'))
3994 ]
3994 ]
3995
3995
3996 diffopts2 = [
3996 diffopts2 = [
3997 ('p', 'show-function', None, _('show which function each change is in')),
3997 ('p', 'show-function', None, _('show which function each change is in')),
3998 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3998 ('', 'reverse', None, _('produce a diff that undoes the changes')),
3999 ('w', 'ignore-all-space', None,
3999 ('w', 'ignore-all-space', None,
4000 _('ignore white space when comparing lines')),
4000 _('ignore white space when comparing lines')),
4001 ('b', 'ignore-space-change', None,
4001 ('b', 'ignore-space-change', None,
4002 _('ignore changes in the amount of white space')),
4002 _('ignore changes in the amount of white space')),
4003 ('B', 'ignore-blank-lines', None,
4003 ('B', 'ignore-blank-lines', None,
4004 _('ignore changes whose lines are all blank')),
4004 _('ignore changes whose lines are all blank')),
4005 ('U', 'unified', '',
4005 ('U', 'unified', '',
4006 _('number of lines of context to show'), _('NUM')),
4006 _('number of lines of context to show'), _('NUM')),
4007 ('', 'stat', None, _('output diffstat-style summary of changes')),
4007 ('', 'stat', None, _('output diffstat-style summary of changes')),
4008 ]
4008 ]
4009
4009
4010 similarityopts = [
4010 similarityopts = [
4011 ('s', 'similarity', '',
4011 ('s', 'similarity', '',
4012 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
4012 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
4013 ]
4013 ]
4014
4014
4015 subrepoopts = [
4015 subrepoopts = [
4016 ('S', 'subrepos', None,
4016 ('S', 'subrepos', None,
4017 _('recurse into subrepositories'))
4017 _('recurse into subrepositories'))
4018 ]
4018 ]
4019
4019
4020 table = {
4020 table = {
4021 "^add": (add, walkopts + subrepoopts + dryrunopts,
4021 "^add": (add, walkopts + subrepoopts + dryrunopts,
4022 _('[OPTION]... [FILE]...')),
4022 _('[OPTION]... [FILE]...')),
4023 "addremove":
4023 "addremove":
4024 (addremove, similarityopts + walkopts + dryrunopts,
4024 (addremove, similarityopts + walkopts + dryrunopts,
4025 _('[OPTION]... [FILE]...')),
4025 _('[OPTION]... [FILE]...')),
4026 "^annotate|blame":
4026 "^annotate|blame":
4027 (annotate,
4027 (annotate,
4028 [('r', 'rev', '',
4028 [('r', 'rev', '',
4029 _('annotate the specified revision'), _('REV')),
4029 _('annotate the specified revision'), _('REV')),
4030 ('', 'follow', None,
4030 ('', 'follow', None,
4031 _('follow copies/renames and list the filename (DEPRECATED)')),
4031 _('follow copies/renames and list the filename (DEPRECATED)')),
4032 ('', 'no-follow', None, _("don't follow copies and renames")),
4032 ('', 'no-follow', None, _("don't follow copies and renames")),
4033 ('a', 'text', None, _('treat all files as text')),
4033 ('a', 'text', None, _('treat all files as text')),
4034 ('u', 'user', None, _('list the author (long with -v)')),
4034 ('u', 'user', None, _('list the author (long with -v)')),
4035 ('f', 'file', None, _('list the filename')),
4035 ('f', 'file', None, _('list the filename')),
4036 ('d', 'date', None, _('list the date (short with -q)')),
4036 ('d', 'date', None, _('list the date (short with -q)')),
4037 ('n', 'number', None, _('list the revision number (default)')),
4037 ('n', 'number', None, _('list the revision number (default)')),
4038 ('c', 'changeset', None, _('list the changeset')),
4038 ('c', 'changeset', None, _('list the changeset')),
4039 ('l', 'line-number', None,
4039 ('l', 'line-number', None,
4040 _('show line number at the first appearance'))
4040 _('show line number at the first appearance'))
4041 ] + walkopts,
4041 ] + walkopts,
4042 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
4042 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
4043 "archive":
4043 "archive":
4044 (archive,
4044 (archive,
4045 [('', 'no-decode', None, _('do not pass files through decoders')),
4045 [('', 'no-decode', None, _('do not pass files through decoders')),
4046 ('p', 'prefix', '',
4046 ('p', 'prefix', '',
4047 _('directory prefix for files in archive'), _('PREFIX')),
4047 _('directory prefix for files in archive'), _('PREFIX')),
4048 ('r', 'rev', '',
4048 ('r', 'rev', '',
4049 _('revision to distribute'), _('REV')),
4049 _('revision to distribute'), _('REV')),
4050 ('t', 'type', '',
4050 ('t', 'type', '',
4051 _('type of distribution to create'), _('TYPE')),
4051 _('type of distribution to create'), _('TYPE')),
4052 ] + subrepoopts + walkopts,
4052 ] + subrepoopts + walkopts,
4053 _('[OPTION]... DEST')),
4053 _('[OPTION]... DEST')),
4054 "backout":
4054 "backout":
4055 (backout,
4055 (backout,
4056 [('', 'merge', None,
4056 [('', 'merge', None,
4057 _('merge with old dirstate parent after backout')),
4057 _('merge with old dirstate parent after backout')),
4058 ('', 'parent', '',
4058 ('', 'parent', '',
4059 _('parent to choose when backing out merge'), _('REV')),
4059 _('parent to choose when backing out merge'), _('REV')),
4060 ('t', 'tool', '',
4060 ('t', 'tool', '',
4061 _('specify merge tool')),
4061 _('specify merge tool')),
4062 ('r', 'rev', '',
4062 ('r', 'rev', '',
4063 _('revision to backout'), _('REV')),
4063 _('revision to backout'), _('REV')),
4064 ] + walkopts + commitopts + commitopts2,
4064 ] + walkopts + commitopts + commitopts2,
4065 _('[OPTION]... [-r] REV')),
4065 _('[OPTION]... [-r] REV')),
4066 "bisect":
4066 "bisect":
4067 (bisect,
4067 (bisect,
4068 [('r', 'reset', False, _('reset bisect state')),
4068 [('r', 'reset', False, _('reset bisect state')),
4069 ('g', 'good', False, _('mark changeset good')),
4069 ('g', 'good', False, _('mark changeset good')),
4070 ('b', 'bad', False, _('mark changeset bad')),
4070 ('b', 'bad', False, _('mark changeset bad')),
4071 ('s', 'skip', False, _('skip testing changeset')),
4071 ('s', 'skip', False, _('skip testing changeset')),
4072 ('c', 'command', '',
4072 ('c', 'command', '',
4073 _('use command to check changeset state'), _('CMD')),
4073 _('use command to check changeset state'), _('CMD')),
4074 ('U', 'noupdate', False, _('do not update to target'))],
4074 ('U', 'noupdate', False, _('do not update to target'))],
4075 _("[-gbsr] [-U] [-c CMD] [REV]")),
4075 _("[-gbsr] [-U] [-c CMD] [REV]")),
4076 "branch":
4076 "branch":
4077 (branch,
4077 (branch,
4078 [('f', 'force', None,
4078 [('f', 'force', None,
4079 _('set branch name even if it shadows an existing branch')),
4079 _('set branch name even if it shadows an existing branch')),
4080 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4080 ('C', 'clean', None, _('reset branch name to parent branch name'))],
4081 _('[-fC] [NAME]')),
4081 _('[-fC] [NAME]')),
4082 "branches":
4082 "branches":
4083 (branches,
4083 (branches,
4084 [('a', 'active', False,
4084 [('a', 'active', False,
4085 _('show only branches that have unmerged heads')),
4085 _('show only branches that have unmerged heads')),
4086 ('c', 'closed', False,
4086 ('c', 'closed', False,
4087 _('show normal and closed branches'))],
4087 _('show normal and closed branches'))],
4088 _('[-ac]')),
4088 _('[-ac]')),
4089 "bundle":
4089 "bundle":
4090 (bundle,
4090 (bundle,
4091 [('f', 'force', None,
4091 [('f', 'force', None,
4092 _('run even when the destination is unrelated')),
4092 _('run even when the destination is unrelated')),
4093 ('r', 'rev', [],
4093 ('r', 'rev', [],
4094 _('a changeset intended to be added to the destination'),
4094 _('a changeset intended to be added to the destination'),
4095 _('REV')),
4095 _('REV')),
4096 ('b', 'branch', [],
4096 ('b', 'branch', [],
4097 _('a specific branch you would like to bundle'),
4097 _('a specific branch you would like to bundle'),
4098 _('BRANCH')),
4098 _('BRANCH')),
4099 ('', 'base', [],
4099 ('', 'base', [],
4100 _('a base changeset assumed to be available at the destination'),
4100 _('a base changeset assumed to be available at the destination'),
4101 _('REV')),
4101 _('REV')),
4102 ('a', 'all', None, _('bundle all changesets in the repository')),
4102 ('a', 'all', None, _('bundle all changesets in the repository')),
4103 ('t', 'type', 'bzip2',
4103 ('t', 'type', 'bzip2',
4104 _('bundle compression type to use'), _('TYPE')),
4104 _('bundle compression type to use'), _('TYPE')),
4105 ] + remoteopts,
4105 ] + remoteopts,
4106 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4106 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
4107 "cat":
4107 "cat":
4108 (cat,
4108 (cat,
4109 [('o', 'output', '',
4109 [('o', 'output', '',
4110 _('print output to file with formatted name'), _('FORMAT')),
4110 _('print output to file with formatted name'), _('FORMAT')),
4111 ('r', 'rev', '',
4111 ('r', 'rev', '',
4112 _('print the given revision'), _('REV')),
4112 _('print the given revision'), _('REV')),
4113 ('', 'decode', None, _('apply any matching decode filter')),
4113 ('', 'decode', None, _('apply any matching decode filter')),
4114 ] + walkopts,
4114 ] + walkopts,
4115 _('[OPTION]... FILE...')),
4115 _('[OPTION]... FILE...')),
4116 "^clone":
4116 "^clone":
4117 (clone,
4117 (clone,
4118 [('U', 'noupdate', None,
4118 [('U', 'noupdate', None,
4119 _('the clone will include an empty working copy (only a repository)')),
4119 _('the clone will include an empty working copy (only a repository)')),
4120 ('u', 'updaterev', '',
4120 ('u', 'updaterev', '',
4121 _('revision, tag or branch to check out'), _('REV')),
4121 _('revision, tag or branch to check out'), _('REV')),
4122 ('r', 'rev', [],
4122 ('r', 'rev', [],
4123 _('include the specified changeset'), _('REV')),
4123 _('include the specified changeset'), _('REV')),
4124 ('b', 'branch', [],
4124 ('b', 'branch', [],
4125 _('clone only the specified branch'), _('BRANCH')),
4125 _('clone only the specified branch'), _('BRANCH')),
4126 ('', 'pull', None, _('use pull protocol to copy metadata')),
4126 ('', 'pull', None, _('use pull protocol to copy metadata')),
4127 ('', 'uncompressed', None,
4127 ('', 'uncompressed', None,
4128 _('use uncompressed transfer (fast over LAN)')),
4128 _('use uncompressed transfer (fast over LAN)')),
4129 ] + remoteopts,
4129 ] + remoteopts,
4130 _('[OPTION]... SOURCE [DEST]')),
4130 _('[OPTION]... SOURCE [DEST]')),
4131 "^commit|ci":
4131 "^commit|ci":
4132 (commit,
4132 (commit,
4133 [('A', 'addremove', None,
4133 [('A', 'addremove', None,
4134 _('mark new/missing files as added/removed before committing')),
4134 _('mark new/missing files as added/removed before committing')),
4135 ('', 'close-branch', None,
4135 ('', 'close-branch', None,
4136 _('mark a branch as closed, hiding it from the branch list')),
4136 _('mark a branch as closed, hiding it from the branch list')),
4137 ] + walkopts + commitopts + commitopts2,
4137 ] + walkopts + commitopts + commitopts2,
4138 _('[OPTION]... [FILE]...')),
4138 _('[OPTION]... [FILE]...')),
4139 "copy|cp":
4139 "copy|cp":
4140 (copy,
4140 (copy,
4141 [('A', 'after', None, _('record a copy that has already occurred')),
4141 [('A', 'after', None, _('record a copy that has already occurred')),
4142 ('f', 'force', None,
4142 ('f', 'force', None,
4143 _('forcibly copy over an existing managed file')),
4143 _('forcibly copy over an existing managed file')),
4144 ] + walkopts + dryrunopts,
4144 ] + walkopts + dryrunopts,
4145 _('[OPTION]... [SOURCE]... DEST')),
4145 _('[OPTION]... [SOURCE]... DEST')),
4146 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4146 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
4147 "debugbuilddag":
4147 "debugbuilddag":
4148 (debugbuilddag,
4148 (debugbuilddag,
4149 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4149 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
4150 ('a', 'appended-file', None, _('add single file all revs append to')),
4150 ('a', 'appended-file', None, _('add single file all revs append to')),
4151 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4151 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
4152 ('n', 'new-file', None, _('add new file at each rev')),
4152 ('n', 'new-file', None, _('add new file at each rev')),
4153 ],
4153 ],
4154 _('[OPTION]... TEXT')),
4154 _('[OPTION]... TEXT')),
4155 "debugcheckstate": (debugcheckstate, [], ''),
4155 "debugcheckstate": (debugcheckstate, [], ''),
4156 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4156 "debugcommands": (debugcommands, [], _('[COMMAND]')),
4157 "debugcomplete":
4157 "debugcomplete":
4158 (debugcomplete,
4158 (debugcomplete,
4159 [('o', 'options', None, _('show the command options'))],
4159 [('o', 'options', None, _('show the command options'))],
4160 _('[-o] CMD')),
4160 _('[-o] CMD')),
4161 "debugdag":
4161 "debugdag":
4162 (debugdag,
4162 (debugdag,
4163 [('t', 'tags', None, _('use tags as labels')),
4163 [('t', 'tags', None, _('use tags as labels')),
4164 ('b', 'branches', None, _('annotate with branch names')),
4164 ('b', 'branches', None, _('annotate with branch names')),
4165 ('', 'dots', None, _('use dots for runs')),
4165 ('', 'dots', None, _('use dots for runs')),
4166 ('s', 'spaces', None, _('separate elements by spaces')),
4166 ('s', 'spaces', None, _('separate elements by spaces')),
4167 ],
4167 ],
4168 _('[OPTION]... [FILE [REV]...]')),
4168 _('[OPTION]... [FILE [REV]...]')),
4169 "debugdate":
4169 "debugdate":
4170 (debugdate,
4170 (debugdate,
4171 [('e', 'extended', None, _('try extended date formats'))],
4171 [('e', 'extended', None, _('try extended date formats'))],
4172 _('[-e] DATE [RANGE]')),
4172 _('[-e] DATE [RANGE]')),
4173 "debugdata": (debugdata, [], _('FILE REV')),
4173 "debugdata": (debugdata, [], _('FILE REV')),
4174 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4174 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
4175 "debugindex": (debugindex,
4175 "debugindex": (debugindex,
4176 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
4176 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
4177 _('FILE')),
4177 _('FILE')),
4178 "debugindexdot": (debugindexdot, [], _('FILE')),
4178 "debugindexdot": (debugindexdot, [], _('FILE')),
4179 "debuginstall": (debuginstall, [], ''),
4179 "debuginstall": (debuginstall, [], ''),
4180 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4180 "debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
4181 "debugrebuildstate":
4181 "debugrebuildstate":
4182 (debugrebuildstate,
4182 (debugrebuildstate,
4183 [('r', 'rev', '',
4183 [('r', 'rev', '',
4184 _('revision to rebuild to'), _('REV'))],
4184 _('revision to rebuild to'), _('REV'))],
4185 _('[-r REV] [REV]')),
4185 _('[-r REV] [REV]')),
4186 "debugrename":
4186 "debugrename":
4187 (debugrename,
4187 (debugrename,
4188 [('r', 'rev', '',
4188 [('r', 'rev', '',
4189 _('revision to debug'), _('REV'))],
4189 _('revision to debug'), _('REV'))],
4190 _('[-r REV] FILE')),
4190 _('[-r REV] FILE')),
4191 "debugrevspec":
4191 "debugrevspec":
4192 (debugrevspec, [], ('REVSPEC')),
4192 (debugrevspec, [], ('REVSPEC')),
4193 "debugsetparents":
4193 "debugsetparents":
4194 (debugsetparents, [], _('REV1 [REV2]')),
4194 (debugsetparents, [], _('REV1 [REV2]')),
4195 "debugstate":
4195 "debugstate":
4196 (debugstate,
4196 (debugstate,
4197 [('', 'nodates', None, _('do not display the saved mtime'))],
4197 [('', 'nodates', None, _('do not display the saved mtime'))],
4198 _('[OPTION]...')),
4198 _('[OPTION]...')),
4199 "debugsub":
4199 "debugsub":
4200 (debugsub,
4200 (debugsub,
4201 [('r', 'rev', '',
4201 [('r', 'rev', '',
4202 _('revision to check'), _('REV'))],
4202 _('revision to check'), _('REV'))],
4203 _('[-r REV] [REV]')),
4203 _('[-r REV] [REV]')),
4204 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4204 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
4205 "^diff":
4205 "^diff":
4206 (diff,
4206 (diff,
4207 [('r', 'rev', [],
4207 [('r', 'rev', [],
4208 _('revision'), _('REV')),
4208 _('revision'), _('REV')),
4209 ('c', 'change', '',
4209 ('c', 'change', '',
4210 _('change made by revision'), _('REV'))
4210 _('change made by revision'), _('REV'))
4211 ] + diffopts + diffopts2 + walkopts + subrepoopts,
4211 ] + diffopts + diffopts2 + walkopts + subrepoopts,
4212 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4212 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...')),
4213 "^export":
4213 "^export":
4214 (export,
4214 (export,
4215 [('o', 'output', '',
4215 [('o', 'output', '',
4216 _('print output to file with formatted name'), _('FORMAT')),
4216 _('print output to file with formatted name'), _('FORMAT')),
4217 ('', 'switch-parent', None, _('diff against the second parent')),
4217 ('', 'switch-parent', None, _('diff against the second parent')),
4218 ('r', 'rev', [],
4218 ('r', 'rev', [],
4219 _('revisions to export'), _('REV')),
4219 _('revisions to export'), _('REV')),
4220 ] + diffopts,
4220 ] + diffopts,
4221 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4221 _('[OPTION]... [-o OUTFILESPEC] REV...')),
4222 "^forget":
4222 "^forget":
4223 (forget,
4223 (forget,
4224 [] + walkopts,
4224 [] + walkopts,
4225 _('[OPTION]... FILE...')),
4225 _('[OPTION]... FILE...')),
4226 "grep":
4226 "grep":
4227 (grep,
4227 (grep,
4228 [('0', 'print0', None, _('end fields with NUL')),
4228 [('0', 'print0', None, _('end fields with NUL')),
4229 ('', 'all', None, _('print all revisions that match')),
4229 ('', 'all', None, _('print all revisions that match')),
4230 ('f', 'follow', None,
4230 ('f', 'follow', None,
4231 _('follow changeset history,'
4231 _('follow changeset history,'
4232 ' or file history across copies and renames')),
4232 ' or file history across copies and renames')),
4233 ('i', 'ignore-case', None, _('ignore case when matching')),
4233 ('i', 'ignore-case', None, _('ignore case when matching')),
4234 ('l', 'files-with-matches', None,
4234 ('l', 'files-with-matches', None,
4235 _('print only filenames and revisions that match')),
4235 _('print only filenames and revisions that match')),
4236 ('n', 'line-number', None, _('print matching line numbers')),
4236 ('n', 'line-number', None, _('print matching line numbers')),
4237 ('r', 'rev', [],
4237 ('r', 'rev', [],
4238 _('only search files changed within revision range'), _('REV')),
4238 _('only search files changed within revision range'), _('REV')),
4239 ('u', 'user', None, _('list the author (long with -v)')),
4239 ('u', 'user', None, _('list the author (long with -v)')),
4240 ('d', 'date', None, _('list the date (short with -q)')),
4240 ('d', 'date', None, _('list the date (short with -q)')),
4241 ] + walkopts,
4241 ] + walkopts,
4242 _('[OPTION]... PATTERN [FILE]...')),
4242 _('[OPTION]... PATTERN [FILE]...')),
4243 "heads":
4243 "heads":
4244 (heads,
4244 (heads,
4245 [('r', 'rev', '',
4245 [('r', 'rev', '',
4246 _('show only heads which are descendants of STARTREV'),
4246 _('show only heads which are descendants of STARTREV'),
4247 _('STARTREV')),
4247 _('STARTREV')),
4248 ('t', 'topo', False, _('show topological heads only')),
4248 ('t', 'topo', False, _('show topological heads only')),
4249 ('a', 'active', False,
4249 ('a', 'active', False,
4250 _('show active branchheads only (DEPRECATED)')),
4250 _('show active branchheads only (DEPRECATED)')),
4251 ('c', 'closed', False,
4251 ('c', 'closed', False,
4252 _('show normal and closed branch heads')),
4252 _('show normal and closed branch heads')),
4253 ] + templateopts,
4253 ] + templateopts,
4254 _('[-ac] [-r STARTREV] [REV]...')),
4254 _('[-ac] [-r STARTREV] [REV]...')),
4255 "help": (help_, [], _('[TOPIC]')),
4255 "help": (help_, [], _('[TOPIC]')),
4256 "identify|id":
4256 "identify|id":
4257 (identify,
4257 (identify,
4258 [('r', 'rev', '',
4258 [('r', 'rev', '',
4259 _('identify the specified revision'), _('REV')),
4259 _('identify the specified revision'), _('REV')),
4260 ('n', 'num', None, _('show local revision number')),
4260 ('n', 'num', None, _('show local revision number')),
4261 ('i', 'id', None, _('show global revision id')),
4261 ('i', 'id', None, _('show global revision id')),
4262 ('b', 'branch', None, _('show branch')),
4262 ('b', 'branch', None, _('show branch')),
4263 ('t', 'tags', None, _('show tags'))],
4263 ('t', 'tags', None, _('show tags'))],
4264 _('[-nibt] [-r REV] [SOURCE]')),
4264 _('[-nibt] [-r REV] [SOURCE]')),
4265 "import|patch":
4265 "import|patch":
4266 (import_,
4266 (import_,
4267 [('p', 'strip', 1,
4267 [('p', 'strip', 1,
4268 _('directory strip option for patch. This has the same '
4268 _('directory strip option for patch. This has the same '
4269 'meaning as the corresponding patch option'),
4269 'meaning as the corresponding patch option'),
4270 _('NUM')),
4270 _('NUM')),
4271 ('b', 'base', '',
4271 ('b', 'base', '',
4272 _('base path'), _('PATH')),
4272 _('base path'), _('PATH')),
4273 ('f', 'force', None,
4273 ('f', 'force', None,
4274 _('skip check for outstanding uncommitted changes')),
4274 _('skip check for outstanding uncommitted changes')),
4275 ('', 'no-commit', None,
4275 ('', 'no-commit', None,
4276 _("don't commit, just update the working directory")),
4276 _("don't commit, just update the working directory")),
4277 ('', 'exact', None,
4277 ('', 'exact', None,
4278 _('apply patch to the nodes from which it was generated')),
4278 _('apply patch to the nodes from which it was generated')),
4279 ('', 'import-branch', None,
4279 ('', 'import-branch', None,
4280 _('use any branch information in patch (implied by --exact)'))] +
4280 _('use any branch information in patch (implied by --exact)'))] +
4281 commitopts + commitopts2 + similarityopts,
4281 commitopts + commitopts2 + similarityopts,
4282 _('[OPTION]... PATCH...')),
4282 _('[OPTION]... PATCH...')),
4283 "incoming|in":
4283 "incoming|in":
4284 (incoming,
4284 (incoming,
4285 [('f', 'force', None,
4285 [('f', 'force', None,
4286 _('run even if remote repository is unrelated')),
4286 _('run even if remote repository is unrelated')),
4287 ('n', 'newest-first', None, _('show newest record first')),
4287 ('n', 'newest-first', None, _('show newest record first')),
4288 ('', 'bundle', '',
4288 ('', 'bundle', '',
4289 _('file to store the bundles into'), _('FILE')),
4289 _('file to store the bundles into'), _('FILE')),
4290 ('r', 'rev', [],
4290 ('r', 'rev', [],
4291 _('a remote changeset intended to be added'), _('REV')),
4291 _('a remote changeset intended to be added'), _('REV')),
4292 ('b', 'branch', [],
4292 ('b', 'branch', [],
4293 _('a specific branch you would like to pull'), _('BRANCH')),
4293 _('a specific branch you would like to pull'), _('BRANCH')),
4294 ] + logopts + remoteopts + subrepoopts,
4294 ] + logopts + remoteopts + subrepoopts,
4295 _('[-p] [-n] [-M] [-f] [-r REV]...'
4295 _('[-p] [-n] [-M] [-f] [-r REV]...'
4296 ' [--bundle FILENAME] [SOURCE]')),
4296 ' [--bundle FILENAME] [SOURCE]')),
4297 "^init":
4297 "^init":
4298 (init,
4298 (init,
4299 remoteopts,
4299 remoteopts,
4300 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4300 _('[-e CMD] [--remotecmd CMD] [DEST]')),
4301 "locate":
4301 "locate":
4302 (locate,
4302 (locate,
4303 [('r', 'rev', '',
4303 [('r', 'rev', '',
4304 _('search the repository as it is in REV'), _('REV')),
4304 _('search the repository as it is in REV'), _('REV')),
4305 ('0', 'print0', None,
4305 ('0', 'print0', None,
4306 _('end filenames with NUL, for use with xargs')),
4306 _('end filenames with NUL, for use with xargs')),
4307 ('f', 'fullpath', None,
4307 ('f', 'fullpath', None,
4308 _('print complete paths from the filesystem root')),
4308 _('print complete paths from the filesystem root')),
4309 ] + walkopts,
4309 ] + walkopts,
4310 _('[OPTION]... [PATTERN]...')),
4310 _('[OPTION]... [PATTERN]...')),
4311 "^log|history":
4311 "^log|history":
4312 (log,
4312 (log,
4313 [('f', 'follow', None,
4313 [('f', 'follow', None,
4314 _('follow changeset history,'
4314 _('follow changeset history,'
4315 ' or file history across copies and renames')),
4315 ' or file history across copies and renames')),
4316 ('', 'follow-first', None,
4316 ('', 'follow-first', None,
4317 _('only follow the first parent of merge changesets')),
4317 _('only follow the first parent of merge changesets')),
4318 ('d', 'date', '',
4318 ('d', 'date', '',
4319 _('show revisions matching date spec'), _('DATE')),
4319 _('show revisions matching date spec'), _('DATE')),
4320 ('C', 'copies', None, _('show copied files')),
4320 ('C', 'copies', None, _('show copied files')),
4321 ('k', 'keyword', [],
4321 ('k', 'keyword', [],
4322 _('do case-insensitive search for a given text'), _('TEXT')),
4322 _('do case-insensitive search for a given text'), _('TEXT')),
4323 ('r', 'rev', [],
4323 ('r', 'rev', [],
4324 _('show the specified revision or range'), _('REV')),
4324 _('show the specified revision or range'), _('REV')),
4325 ('', 'removed', None, _('include revisions where files were removed')),
4325 ('', 'removed', None, _('include revisions where files were removed')),
4326 ('m', 'only-merges', None, _('show only merges')),
4326 ('m', 'only-merges', None, _('show only merges')),
4327 ('u', 'user', [],
4327 ('u', 'user', [],
4328 _('revisions committed by user'), _('USER')),
4328 _('revisions committed by user'), _('USER')),
4329 ('', 'only-branch', [],
4329 ('', 'only-branch', [],
4330 _('show only changesets within the given named branch (DEPRECATED)'),
4330 _('show only changesets within the given named branch (DEPRECATED)'),
4331 _('BRANCH')),
4331 _('BRANCH')),
4332 ('b', 'branch', [],
4332 ('b', 'branch', [],
4333 _('show changesets within the given named branch'), _('BRANCH')),
4333 _('show changesets within the given named branch'), _('BRANCH')),
4334 ('P', 'prune', [],
4334 ('P', 'prune', [],
4335 _('do not display revision or any of its ancestors'), _('REV')),
4335 _('do not display revision or any of its ancestors'), _('REV')),
4336 ] + logopts + walkopts,
4336 ] + logopts + walkopts,
4337 _('[OPTION]... [FILE]')),
4337 _('[OPTION]... [FILE]')),
4338 "manifest":
4338 "manifest":
4339 (manifest,
4339 (manifest,
4340 [('r', 'rev', '',
4340 [('r', 'rev', '',
4341 _('revision to display'), _('REV'))],
4341 _('revision to display'), _('REV'))],
4342 _('[-r REV]')),
4342 _('[-r REV]')),
4343 "^merge":
4343 "^merge":
4344 (merge,
4344 (merge,
4345 [('f', 'force', None, _('force a merge with outstanding changes')),
4345 [('f', 'force', None, _('force a merge with outstanding changes')),
4346 ('t', 'tool', '', _('specify merge tool')),
4346 ('t', 'tool', '', _('specify merge tool')),
4347 ('r', 'rev', '',
4347 ('r', 'rev', '',
4348 _('revision to merge'), _('REV')),
4348 _('revision to merge'), _('REV')),
4349 ('P', 'preview', None,
4349 ('P', 'preview', None,
4350 _('review revisions to merge (no merge is performed)'))],
4350 _('review revisions to merge (no merge is performed)'))],
4351 _('[-P] [-f] [[-r] REV]')),
4351 _('[-P] [-f] [[-r] REV]')),
4352 "outgoing|out":
4352 "outgoing|out":
4353 (outgoing,
4353 (outgoing,
4354 [('f', 'force', None,
4354 [('f', 'force', None,
4355 _('run even when the destination is unrelated')),
4355 _('run even when the destination is unrelated')),
4356 ('r', 'rev', [],
4356 ('r', 'rev', [],
4357 _('a changeset intended to be included in the destination'),
4357 _('a changeset intended to be included in the destination'),
4358 _('REV')),
4358 _('REV')),
4359 ('n', 'newest-first', None, _('show newest record first')),
4359 ('n', 'newest-first', None, _('show newest record first')),
4360 ('b', 'branch', [],
4360 ('b', 'branch', [],
4361 _('a specific branch you would like to push'), _('BRANCH')),
4361 _('a specific branch you would like to push'), _('BRANCH')),
4362 ] + logopts + remoteopts + subrepoopts,
4362 ] + logopts + remoteopts + subrepoopts,
4363 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4363 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
4364 "parents":
4364 "parents":
4365 (parents,
4365 (parents,
4366 [('r', 'rev', '',
4366 [('r', 'rev', '',
4367 _('show parents of the specified revision'), _('REV')),
4367 _('show parents of the specified revision'), _('REV')),
4368 ] + templateopts,
4368 ] + templateopts,
4369 _('[-r REV] [FILE]')),
4369 _('[-r REV] [FILE]')),
4370 "paths": (paths, [], _('[NAME]')),
4370 "paths": (paths, [], _('[NAME]')),
4371 "^pull":
4371 "^pull":
4372 (pull,
4372 (pull,
4373 [('u', 'update', None,
4373 [('u', 'update', None,
4374 _('update to new branch head if changesets were pulled')),
4374 _('update to new branch head if changesets were pulled')),
4375 ('f', 'force', None,
4375 ('f', 'force', None,
4376 _('run even when remote repository is unrelated')),
4376 _('run even when remote repository is unrelated')),
4377 ('r', 'rev', [],
4377 ('r', 'rev', [],
4378 _('a remote changeset intended to be added'), _('REV')),
4378 _('a remote changeset intended to be added'), _('REV')),
4379 ('b', 'branch', [],
4379 ('b', 'branch', [],
4380 _('a specific branch you would like to pull'), _('BRANCH')),
4380 _('a specific branch you would like to pull'), _('BRANCH')),
4381 ] + remoteopts,
4381 ] + remoteopts,
4382 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4382 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
4383 "^push":
4383 "^push":
4384 (push,
4384 (push,
4385 [('f', 'force', None, _('force push')),
4385 [('f', 'force', None, _('force push')),
4386 ('r', 'rev', [],
4386 ('r', 'rev', [],
4387 _('a changeset intended to be included in the destination'),
4387 _('a changeset intended to be included in the destination'),
4388 _('REV')),
4388 _('REV')),
4389 ('b', 'branch', [],
4389 ('b', 'branch', [],
4390 _('a specific branch you would like to push'), _('BRANCH')),
4390 _('a specific branch you would like to push'), _('BRANCH')),
4391 ('', 'new-branch', False, _('allow pushing a new branch')),
4391 ('', 'new-branch', False, _('allow pushing a new branch')),
4392 ] + remoteopts,
4392 ] + remoteopts,
4393 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4393 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
4394 "recover": (recover, []),
4394 "recover": (recover, []),
4395 "^remove|rm":
4395 "^remove|rm":
4396 (remove,
4396 (remove,
4397 [('A', 'after', None, _('record delete for missing files')),
4397 [('A', 'after', None, _('record delete for missing files')),
4398 ('f', 'force', None,
4398 ('f', 'force', None,
4399 _('remove (and delete) file even if added or modified')),
4399 _('remove (and delete) file even if added or modified')),
4400 ] + walkopts,
4400 ] + walkopts,
4401 _('[OPTION]... FILE...')),
4401 _('[OPTION]... FILE...')),
4402 "rename|move|mv":
4402 "rename|move|mv":
4403 (rename,
4403 (rename,
4404 [('A', 'after', None, _('record a rename that has already occurred')),
4404 [('A', 'after', None, _('record a rename that has already occurred')),
4405 ('f', 'force', None,
4405 ('f', 'force', None,
4406 _('forcibly copy over an existing managed file')),
4406 _('forcibly copy over an existing managed file')),
4407 ] + walkopts + dryrunopts,
4407 ] + walkopts + dryrunopts,
4408 _('[OPTION]... SOURCE... DEST')),
4408 _('[OPTION]... SOURCE... DEST')),
4409 "resolve":
4409 "resolve":
4410 (resolve,
4410 (resolve,
4411 [('a', 'all', None, _('select all unresolved files')),
4411 [('a', 'all', None, _('select all unresolved files')),
4412 ('l', 'list', None, _('list state of files needing merge')),
4412 ('l', 'list', None, _('list state of files needing merge')),
4413 ('m', 'mark', None, _('mark files as resolved')),
4413 ('m', 'mark', None, _('mark files as resolved')),
4414 ('u', 'unmark', None, _('mark files as unresolved')),
4414 ('u', 'unmark', None, _('mark files as unresolved')),
4415 ('t', 'tool', '', _('specify merge tool')),
4415 ('t', 'tool', '', _('specify merge tool')),
4416 ('n', 'no-status', None, _('hide status prefix'))]
4416 ('n', 'no-status', None, _('hide status prefix'))]
4417 + walkopts,
4417 + walkopts,
4418 _('[OPTION]... [FILE]...')),
4418 _('[OPTION]... [FILE]...')),
4419 "revert":
4419 "revert":
4420 (revert,
4420 (revert,
4421 [('a', 'all', None, _('revert all changes when no arguments given')),
4421 [('a', 'all', None, _('revert all changes when no arguments given')),
4422 ('d', 'date', '',
4422 ('d', 'date', '',
4423 _('tipmost revision matching date'), _('DATE')),
4423 _('tipmost revision matching date'), _('DATE')),
4424 ('r', 'rev', '',
4424 ('r', 'rev', '',
4425 _('revert to the specified revision'), _('REV')),
4425 _('revert to the specified revision'), _('REV')),
4426 ('', 'no-backup', None, _('do not save backup copies of files')),
4426 ('', 'no-backup', None, _('do not save backup copies of files')),
4427 ] + walkopts + dryrunopts,
4427 ] + walkopts + dryrunopts,
4428 _('[OPTION]... [-r REV] [NAME]...')),
4428 _('[OPTION]... [-r REV] [NAME]...')),
4429 "rollback": (rollback, dryrunopts),
4429 "rollback": (rollback, dryrunopts),
4430 "root": (root, []),
4430 "root": (root, []),
4431 "^serve":
4431 "^serve":
4432 (serve,
4432 (serve,
4433 [('A', 'accesslog', '',
4433 [('A', 'accesslog', '',
4434 _('name of access log file to write to'), _('FILE')),
4434 _('name of access log file to write to'), _('FILE')),
4435 ('d', 'daemon', None, _('run server in background')),
4435 ('d', 'daemon', None, _('run server in background')),
4436 ('', 'daemon-pipefds', '',
4436 ('', 'daemon-pipefds', '',
4437 _('used internally by daemon mode'), _('NUM')),
4437 _('used internally by daemon mode'), _('NUM')),
4438 ('E', 'errorlog', '',
4438 ('E', 'errorlog', '',
4439 _('name of error log file to write to'), _('FILE')),
4439 _('name of error log file to write to'), _('FILE')),
4440 # use string type, then we can check if something was passed
4440 # use string type, then we can check if something was passed
4441 ('p', 'port', '',
4441 ('p', 'port', '',
4442 _('port to listen on (default: 8000)'), _('PORT')),
4442 _('port to listen on (default: 8000)'), _('PORT')),
4443 ('a', 'address', '',
4443 ('a', 'address', '',
4444 _('address to listen on (default: all interfaces)'), _('ADDR')),
4444 _('address to listen on (default: all interfaces)'), _('ADDR')),
4445 ('', 'prefix', '',
4445 ('', 'prefix', '',
4446 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4446 _('prefix path to serve from (default: server root)'), _('PREFIX')),
4447 ('n', 'name', '',
4447 ('n', 'name', '',
4448 _('name to show in web pages (default: working directory)'),
4448 _('name to show in web pages (default: working directory)'),
4449 _('NAME')),
4449 _('NAME')),
4450 ('', 'web-conf', '',
4450 ('', 'web-conf', '',
4451 _('name of the hgweb config file (see "hg help hgweb")'),
4451 _('name of the hgweb config file (see "hg help hgweb")'),
4452 _('FILE')),
4452 _('FILE')),
4453 ('', 'webdir-conf', '',
4453 ('', 'webdir-conf', '',
4454 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4454 _('name of the hgweb config file (DEPRECATED)'), _('FILE')),
4455 ('', 'pid-file', '',
4455 ('', 'pid-file', '',
4456 _('name of file to write process ID to'), _('FILE')),
4456 _('name of file to write process ID to'), _('FILE')),
4457 ('', 'stdio', None, _('for remote clients')),
4457 ('', 'stdio', None, _('for remote clients')),
4458 ('t', 'templates', '',
4458 ('t', 'templates', '',
4459 _('web templates to use'), _('TEMPLATE')),
4459 _('web templates to use'), _('TEMPLATE')),
4460 ('', 'style', '',
4460 ('', 'style', '',
4461 _('template style to use'), _('STYLE')),
4461 _('template style to use'), _('STYLE')),
4462 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4462 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4463 ('', 'certificate', '',
4463 ('', 'certificate', '',
4464 _('SSL certificate file'), _('FILE'))],
4464 _('SSL certificate file'), _('FILE'))],
4465 _('[OPTION]...')),
4465 _('[OPTION]...')),
4466 "showconfig|debugconfig":
4466 "showconfig|debugconfig":
4467 (showconfig,
4467 (showconfig,
4468 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4468 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4469 _('[-u] [NAME]...')),
4469 _('[-u] [NAME]...')),
4470 "^summary|sum":
4470 "^summary|sum":
4471 (summary,
4471 (summary,
4472 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4472 [('', 'remote', None, _('check for push and pull'))], '[--remote]'),
4473 "^status|st":
4473 "^status|st":
4474 (status,
4474 (status,
4475 [('A', 'all', None, _('show status of all files')),
4475 [('A', 'all', None, _('show status of all files')),
4476 ('m', 'modified', None, _('show only modified files')),
4476 ('m', 'modified', None, _('show only modified files')),
4477 ('a', 'added', None, _('show only added files')),
4477 ('a', 'added', None, _('show only added files')),
4478 ('r', 'removed', None, _('show only removed files')),
4478 ('r', 'removed', None, _('show only removed files')),
4479 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4479 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4480 ('c', 'clean', None, _('show only files without changes')),
4480 ('c', 'clean', None, _('show only files without changes')),
4481 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4481 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4482 ('i', 'ignored', None, _('show only ignored files')),
4482 ('i', 'ignored', None, _('show only ignored files')),
4483 ('n', 'no-status', None, _('hide status prefix')),
4483 ('n', 'no-status', None, _('hide status prefix')),
4484 ('C', 'copies', None, _('show source of copied files')),
4484 ('C', 'copies', None, _('show source of copied files')),
4485 ('0', 'print0', None,
4485 ('0', 'print0', None,
4486 _('end filenames with NUL, for use with xargs')),
4486 _('end filenames with NUL, for use with xargs')),
4487 ('', 'rev', [],
4487 ('', 'rev', [],
4488 _('show difference from revision'), _('REV')),
4488 _('show difference from revision'), _('REV')),
4489 ('', 'change', '',
4489 ('', 'change', '',
4490 _('list the changed files of a revision'), _('REV')),
4490 _('list the changed files of a revision'), _('REV')),
4491 ] + walkopts + subrepoopts,
4491 ] + walkopts + subrepoopts,
4492 _('[OPTION]... [FILE]...')),
4492 _('[OPTION]... [FILE]...')),
4493 "tag":
4493 "tag":
4494 (tag,
4494 (tag,
4495 [('f', 'force', None, _('force tag')),
4495 [('f', 'force', None, _('force tag')),
4496 ('l', 'local', None, _('make the tag local')),
4496 ('l', 'local', None, _('make the tag local')),
4497 ('r', 'rev', '',
4497 ('r', 'rev', '',
4498 _('revision to tag'), _('REV')),
4498 _('revision to tag'), _('REV')),
4499 ('', 'remove', None, _('remove a tag')),
4499 ('', 'remove', None, _('remove a tag')),
4500 # -l/--local is already there, commitopts cannot be used
4500 # -l/--local is already there, commitopts cannot be used
4501 ('e', 'edit', None, _('edit commit message')),
4501 ('e', 'edit', None, _('edit commit message')),
4502 ('m', 'message', '',
4502 ('m', 'message', '',
4503 _('use <text> as commit message'), _('TEXT')),
4503 _('use <text> as commit message'), _('TEXT')),
4504 ] + commitopts2,
4504 ] + commitopts2,
4505 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4505 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
4506 "tags": (tags, [], ''),
4506 "tags": (tags, [], ''),
4507 "tip":
4507 "tip":
4508 (tip,
4508 (tip,
4509 [('p', 'patch', None, _('show patch')),
4509 [('p', 'patch', None, _('show patch')),
4510 ('g', 'git', None, _('use git extended diff format')),
4510 ('g', 'git', None, _('use git extended diff format')),
4511 ] + templateopts,
4511 ] + templateopts,
4512 _('[-p] [-g]')),
4512 _('[-p] [-g]')),
4513 "unbundle":
4513 "unbundle":
4514 (unbundle,
4514 (unbundle,
4515 [('u', 'update', None,
4515 [('u', 'update', None,
4516 _('update to new branch head if changesets were unbundled'))],
4516 _('update to new branch head if changesets were unbundled'))],
4517 _('[-u] FILE...')),
4517 _('[-u] FILE...')),
4518 "^update|up|checkout|co":
4518 "^update|up|checkout|co":
4519 (update,
4519 (update,
4520 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4520 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4521 ('c', 'check', None,
4521 ('c', 'check', None,
4522 _('update across branches if no uncommitted changes')),
4522 _('update across branches if no uncommitted changes')),
4523 ('d', 'date', '',
4523 ('d', 'date', '',
4524 _('tipmost revision matching date'), _('DATE')),
4524 _('tipmost revision matching date'), _('DATE')),
4525 ('r', 'rev', '',
4525 ('r', 'rev', '',
4526 _('revision'), _('REV'))],
4526 _('revision'), _('REV'))],
4527 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4527 _('[-c] [-C] [-d DATE] [[-r] REV]')),
4528 "verify": (verify, []),
4528 "verify": (verify, []),
4529 "version": (version_, []),
4529 "version": (version_, []),
4530 }
4530 }
4531
4531
4532 norepo = ("clone init version help debugcommands debugcomplete"
4532 norepo = ("clone init version help debugcommands debugcomplete"
4533 " debugdate debuginstall debugfsinfo debugpushkey")
4533 " debugdate debuginstall debugfsinfo debugpushkey")
4534 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4534 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
4535 " debugdata debugindex debugindexdot")
4535 " debugdata debugindex debugindexdot")
@@ -1,1103 +1,1103 b''
1 # context.py - changeset and file context objects for mercurial
1 # context.py - changeset and file context objects for mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import nullid, nullrev, short, hex
8 from node import nullid, nullrev, short, hex
9 from i18n import _
9 from i18n import _
10 import ancestor, bdiff, error, util, subrepo, patch, encoding
10 import ancestor, bdiff, error, util, subrepo, patch, encoding
11 import os, errno, stat
11 import os, errno, stat
12
12
13 propertycache = util.propertycache
13 propertycache = util.propertycache
14
14
15 class changectx(object):
15 class changectx(object):
16 """A changecontext object makes access to data related to a particular
16 """A changecontext object makes access to data related to a particular
17 changeset convenient."""
17 changeset convenient."""
18 def __init__(self, repo, changeid=''):
18 def __init__(self, repo, changeid=''):
19 """changeid is a revision number, node, or tag"""
19 """changeid is a revision number, node, or tag"""
20 if changeid == '':
20 if changeid == '':
21 changeid = '.'
21 changeid = '.'
22 self._repo = repo
22 self._repo = repo
23 if isinstance(changeid, (long, int)):
23 if isinstance(changeid, (long, int)):
24 self._rev = changeid
24 self._rev = changeid
25 self._node = self._repo.changelog.node(changeid)
25 self._node = self._repo.changelog.node(changeid)
26 else:
26 else:
27 self._node = self._repo.lookup(changeid)
27 self._node = self._repo.lookup(changeid)
28 self._rev = self._repo.changelog.rev(self._node)
28 self._rev = self._repo.changelog.rev(self._node)
29
29
30 def __str__(self):
30 def __str__(self):
31 return short(self.node())
31 return short(self.node())
32
32
33 def __int__(self):
33 def __int__(self):
34 return self.rev()
34 return self.rev()
35
35
36 def __repr__(self):
36 def __repr__(self):
37 return "<changectx %s>" % str(self)
37 return "<changectx %s>" % str(self)
38
38
39 def __hash__(self):
39 def __hash__(self):
40 try:
40 try:
41 return hash(self._rev)
41 return hash(self._rev)
42 except AttributeError:
42 except AttributeError:
43 return id(self)
43 return id(self)
44
44
45 def __eq__(self, other):
45 def __eq__(self, other):
46 try:
46 try:
47 return self._rev == other._rev
47 return self._rev == other._rev
48 except AttributeError:
48 except AttributeError:
49 return False
49 return False
50
50
51 def __ne__(self, other):
51 def __ne__(self, other):
52 return not (self == other)
52 return not (self == other)
53
53
54 def __nonzero__(self):
54 def __nonzero__(self):
55 return self._rev != nullrev
55 return self._rev != nullrev
56
56
57 @propertycache
57 @propertycache
58 def _changeset(self):
58 def _changeset(self):
59 return self._repo.changelog.read(self.node())
59 return self._repo.changelog.read(self.node())
60
60
61 @propertycache
61 @propertycache
62 def _manifest(self):
62 def _manifest(self):
63 return self._repo.manifest.read(self._changeset[0])
63 return self._repo.manifest.read(self._changeset[0])
64
64
65 @propertycache
65 @propertycache
66 def _manifestdelta(self):
66 def _manifestdelta(self):
67 return self._repo.manifest.readdelta(self._changeset[0])
67 return self._repo.manifest.readdelta(self._changeset[0])
68
68
69 @propertycache
69 @propertycache
70 def _parents(self):
70 def _parents(self):
71 p = self._repo.changelog.parentrevs(self._rev)
71 p = self._repo.changelog.parentrevs(self._rev)
72 if p[1] == nullrev:
72 if p[1] == nullrev:
73 p = p[:-1]
73 p = p[:-1]
74 return [changectx(self._repo, x) for x in p]
74 return [changectx(self._repo, x) for x in p]
75
75
76 @propertycache
76 @propertycache
77 def substate(self):
77 def substate(self):
78 return subrepo.state(self, self._repo.ui)
78 return subrepo.state(self, self._repo.ui)
79
79
80 def __contains__(self, key):
80 def __contains__(self, key):
81 return key in self._manifest
81 return key in self._manifest
82
82
83 def __getitem__(self, key):
83 def __getitem__(self, key):
84 return self.filectx(key)
84 return self.filectx(key)
85
85
86 def __iter__(self):
86 def __iter__(self):
87 for f in sorted(self._manifest):
87 for f in sorted(self._manifest):
88 yield f
88 yield f
89
89
90 def changeset(self):
90 def changeset(self):
91 return self._changeset
91 return self._changeset
92 def manifest(self):
92 def manifest(self):
93 return self._manifest
93 return self._manifest
94 def manifestnode(self):
94 def manifestnode(self):
95 return self._changeset[0]
95 return self._changeset[0]
96
96
97 def rev(self):
97 def rev(self):
98 return self._rev
98 return self._rev
99 def node(self):
99 def node(self):
100 return self._node
100 return self._node
101 def hex(self):
101 def hex(self):
102 return hex(self._node)
102 return hex(self._node)
103 def user(self):
103 def user(self):
104 return self._changeset[1]
104 return self._changeset[1]
105 def date(self):
105 def date(self):
106 return self._changeset[2]
106 return self._changeset[2]
107 def files(self):
107 def files(self):
108 return self._changeset[3]
108 return self._changeset[3]
109 def description(self):
109 def description(self):
110 return self._changeset[4]
110 return self._changeset[4]
111 def branch(self):
111 def branch(self):
112 return encoding.tolocal(self._changeset[5].get("branch"))
112 return encoding.tolocal(self._changeset[5].get("branch"))
113 def extra(self):
113 def extra(self):
114 return self._changeset[5]
114 return self._changeset[5]
115 def tags(self):
115 def tags(self):
116 return self._repo.nodetags(self._node)
116 return self._repo.nodetags(self._node)
117
117
118 def parents(self):
118 def parents(self):
119 """return contexts for each parent changeset"""
119 """return contexts for each parent changeset"""
120 return self._parents
120 return self._parents
121
121
122 def p1(self):
122 def p1(self):
123 return self._parents[0]
123 return self._parents[0]
124
124
125 def p2(self):
125 def p2(self):
126 if len(self._parents) == 2:
126 if len(self._parents) == 2:
127 return self._parents[1]
127 return self._parents[1]
128 return changectx(self._repo, -1)
128 return changectx(self._repo, -1)
129
129
130 def children(self):
130 def children(self):
131 """return contexts for each child changeset"""
131 """return contexts for each child changeset"""
132 c = self._repo.changelog.children(self._node)
132 c = self._repo.changelog.children(self._node)
133 return [changectx(self._repo, x) for x in c]
133 return [changectx(self._repo, x) for x in c]
134
134
135 def ancestors(self):
135 def ancestors(self):
136 for a in self._repo.changelog.ancestors(self._rev):
136 for a in self._repo.changelog.ancestors(self._rev):
137 yield changectx(self._repo, a)
137 yield changectx(self._repo, a)
138
138
139 def descendants(self):
139 def descendants(self):
140 for d in self._repo.changelog.descendants(self._rev):
140 for d in self._repo.changelog.descendants(self._rev):
141 yield changectx(self._repo, d)
141 yield changectx(self._repo, d)
142
142
143 def _fileinfo(self, path):
143 def _fileinfo(self, path):
144 if '_manifest' in self.__dict__:
144 if '_manifest' in self.__dict__:
145 try:
145 try:
146 return self._manifest[path], self._manifest.flags(path)
146 return self._manifest[path], self._manifest.flags(path)
147 except KeyError:
147 except KeyError:
148 raise error.LookupError(self._node, path,
148 raise error.LookupError(self._node, path,
149 _('not found in manifest'))
149 _('not found in manifest'))
150 if '_manifestdelta' in self.__dict__ or path in self.files():
150 if '_manifestdelta' in self.__dict__ or path in self.files():
151 if path in self._manifestdelta:
151 if path in self._manifestdelta:
152 return self._manifestdelta[path], self._manifestdelta.flags(path)
152 return self._manifestdelta[path], self._manifestdelta.flags(path)
153 node, flag = self._repo.manifest.find(self._changeset[0], path)
153 node, flag = self._repo.manifest.find(self._changeset[0], path)
154 if not node:
154 if not node:
155 raise error.LookupError(self._node, path,
155 raise error.LookupError(self._node, path,
156 _('not found in manifest'))
156 _('not found in manifest'))
157
157
158 return node, flag
158 return node, flag
159
159
160 def filenode(self, path):
160 def filenode(self, path):
161 return self._fileinfo(path)[0]
161 return self._fileinfo(path)[0]
162
162
163 def flags(self, path):
163 def flags(self, path):
164 try:
164 try:
165 return self._fileinfo(path)[1]
165 return self._fileinfo(path)[1]
166 except error.LookupError:
166 except error.LookupError:
167 return ''
167 return ''
168
168
169 def filectx(self, path, fileid=None, filelog=None):
169 def filectx(self, path, fileid=None, filelog=None):
170 """get a file context from this changeset"""
170 """get a file context from this changeset"""
171 if fileid is None:
171 if fileid is None:
172 fileid = self.filenode(path)
172 fileid = self.filenode(path)
173 return filectx(self._repo, path, fileid=fileid,
173 return filectx(self._repo, path, fileid=fileid,
174 changectx=self, filelog=filelog)
174 changectx=self, filelog=filelog)
175
175
176 def ancestor(self, c2):
176 def ancestor(self, c2):
177 """
177 """
178 return the ancestor context of self and c2
178 return the ancestor context of self and c2
179 """
179 """
180 # deal with workingctxs
180 # deal with workingctxs
181 n2 = c2._node
181 n2 = c2._node
182 if n2 is None:
182 if n2 is None:
183 n2 = c2._parents[0]._node
183 n2 = c2._parents[0]._node
184 n = self._repo.changelog.ancestor(self._node, n2)
184 n = self._repo.changelog.ancestor(self._node, n2)
185 return changectx(self._repo, n)
185 return changectx(self._repo, n)
186
186
187 def walk(self, match):
187 def walk(self, match):
188 fset = set(match.files())
188 fset = set(match.files())
189 # for dirstate.walk, files=['.'] means "walk the whole tree".
189 # for dirstate.walk, files=['.'] means "walk the whole tree".
190 # follow that here, too
190 # follow that here, too
191 fset.discard('.')
191 fset.discard('.')
192 for fn in self:
192 for fn in self:
193 for ffn in fset:
193 for ffn in fset:
194 # match if the file is the exact name or a directory
194 # match if the file is the exact name or a directory
195 if ffn == fn or fn.startswith("%s/" % ffn):
195 if ffn == fn or fn.startswith("%s/" % ffn):
196 fset.remove(ffn)
196 fset.remove(ffn)
197 break
197 break
198 if match(fn):
198 if match(fn):
199 yield fn
199 yield fn
200 for fn in sorted(fset):
200 for fn in sorted(fset):
201 if match.bad(fn, _('no such file in rev %s') % self) and match(fn):
201 if match.bad(fn, _('no such file in rev %s') % self) and match(fn):
202 yield fn
202 yield fn
203
203
204 def sub(self, path):
204 def sub(self, path):
205 return subrepo.subrepo(self, path)
205 return subrepo.subrepo(self, path)
206
206
207 def diff(self, ctx2=None, match=None, **opts):
207 def diff(self, ctx2=None, match=None, **opts):
208 """Returns a diff generator for the given contexts and matcher"""
208 """Returns a diff generator for the given contexts and matcher"""
209 if ctx2 is None:
209 if ctx2 is None:
210 ctx2 = self.p1()
210 ctx2 = self.p1()
211 if ctx2 is not None and not isinstance(ctx2, changectx):
211 if ctx2 is not None and not isinstance(ctx2, changectx):
212 ctx2 = self._repo[ctx2]
212 ctx2 = self._repo[ctx2]
213 diffopts = patch.diffopts(self._repo.ui, opts)
213 diffopts = patch.diffopts(self._repo.ui, opts)
214 return patch.diff(self._repo, ctx2.node(), self.node(),
214 return patch.diff(self._repo, ctx2.node(), self.node(),
215 match=match, opts=diffopts)
215 match=match, opts=diffopts)
216
216
217 class filectx(object):
217 class filectx(object):
218 """A filecontext object makes access to data related to a particular
218 """A filecontext object makes access to data related to a particular
219 filerevision convenient."""
219 filerevision convenient."""
220 def __init__(self, repo, path, changeid=None, fileid=None,
220 def __init__(self, repo, path, changeid=None, fileid=None,
221 filelog=None, changectx=None):
221 filelog=None, changectx=None):
222 """changeid can be a changeset revision, node, or tag.
222 """changeid can be a changeset revision, node, or tag.
223 fileid can be a file revision or node."""
223 fileid can be a file revision or node."""
224 self._repo = repo
224 self._repo = repo
225 self._path = path
225 self._path = path
226
226
227 assert (changeid is not None
227 assert (changeid is not None
228 or fileid is not None
228 or fileid is not None
229 or changectx is not None), \
229 or changectx is not None), \
230 ("bad args: changeid=%r, fileid=%r, changectx=%r"
230 ("bad args: changeid=%r, fileid=%r, changectx=%r"
231 % (changeid, fileid, changectx))
231 % (changeid, fileid, changectx))
232
232
233 if filelog:
233 if filelog:
234 self._filelog = filelog
234 self._filelog = filelog
235
235
236 if changeid is not None:
236 if changeid is not None:
237 self._changeid = changeid
237 self._changeid = changeid
238 if changectx is not None:
238 if changectx is not None:
239 self._changectx = changectx
239 self._changectx = changectx
240 if fileid is not None:
240 if fileid is not None:
241 self._fileid = fileid
241 self._fileid = fileid
242
242
243 @propertycache
243 @propertycache
244 def _changectx(self):
244 def _changectx(self):
245 return changectx(self._repo, self._changeid)
245 return changectx(self._repo, self._changeid)
246
246
247 @propertycache
247 @propertycache
248 def _filelog(self):
248 def _filelog(self):
249 return self._repo.file(self._path)
249 return self._repo.file(self._path)
250
250
251 @propertycache
251 @propertycache
252 def _changeid(self):
252 def _changeid(self):
253 if '_changectx' in self.__dict__:
253 if '_changectx' in self.__dict__:
254 return self._changectx.rev()
254 return self._changectx.rev()
255 else:
255 else:
256 return self._filelog.linkrev(self._filerev)
256 return self._filelog.linkrev(self._filerev)
257
257
258 @propertycache
258 @propertycache
259 def _filenode(self):
259 def _filenode(self):
260 if '_fileid' in self.__dict__:
260 if '_fileid' in self.__dict__:
261 return self._filelog.lookup(self._fileid)
261 return self._filelog.lookup(self._fileid)
262 else:
262 else:
263 return self._changectx.filenode(self._path)
263 return self._changectx.filenode(self._path)
264
264
265 @propertycache
265 @propertycache
266 def _filerev(self):
266 def _filerev(self):
267 return self._filelog.rev(self._filenode)
267 return self._filelog.rev(self._filenode)
268
268
269 @propertycache
269 @propertycache
270 def _repopath(self):
270 def _repopath(self):
271 return self._path
271 return self._path
272
272
273 def __nonzero__(self):
273 def __nonzero__(self):
274 try:
274 try:
275 self._filenode
275 self._filenode
276 return True
276 return True
277 except error.LookupError:
277 except error.LookupError:
278 # file is missing
278 # file is missing
279 return False
279 return False
280
280
281 def __str__(self):
281 def __str__(self):
282 return "%s@%s" % (self.path(), short(self.node()))
282 return "%s@%s" % (self.path(), short(self.node()))
283
283
284 def __repr__(self):
284 def __repr__(self):
285 return "<filectx %s>" % str(self)
285 return "<filectx %s>" % str(self)
286
286
287 def __hash__(self):
287 def __hash__(self):
288 try:
288 try:
289 return hash((self._path, self._filenode))
289 return hash((self._path, self._filenode))
290 except AttributeError:
290 except AttributeError:
291 return id(self)
291 return id(self)
292
292
293 def __eq__(self, other):
293 def __eq__(self, other):
294 try:
294 try:
295 return (self._path == other._path
295 return (self._path == other._path
296 and self._filenode == other._filenode)
296 and self._filenode == other._filenode)
297 except AttributeError:
297 except AttributeError:
298 return False
298 return False
299
299
300 def __ne__(self, other):
300 def __ne__(self, other):
301 return not (self == other)
301 return not (self == other)
302
302
303 def filectx(self, fileid):
303 def filectx(self, fileid):
304 '''opens an arbitrary revision of the file without
304 '''opens an arbitrary revision of the file without
305 opening a new filelog'''
305 opening a new filelog'''
306 return filectx(self._repo, self._path, fileid=fileid,
306 return filectx(self._repo, self._path, fileid=fileid,
307 filelog=self._filelog)
307 filelog=self._filelog)
308
308
309 def filerev(self):
309 def filerev(self):
310 return self._filerev
310 return self._filerev
311 def filenode(self):
311 def filenode(self):
312 return self._filenode
312 return self._filenode
313 def flags(self):
313 def flags(self):
314 return self._changectx.flags(self._path)
314 return self._changectx.flags(self._path)
315 def filelog(self):
315 def filelog(self):
316 return self._filelog
316 return self._filelog
317
317
318 def rev(self):
318 def rev(self):
319 if '_changectx' in self.__dict__:
319 if '_changectx' in self.__dict__:
320 return self._changectx.rev()
320 return self._changectx.rev()
321 if '_changeid' in self.__dict__:
321 if '_changeid' in self.__dict__:
322 return self._changectx.rev()
322 return self._changectx.rev()
323 return self._filelog.linkrev(self._filerev)
323 return self._filelog.linkrev(self._filerev)
324
324
325 def linkrev(self):
325 def linkrev(self):
326 return self._filelog.linkrev(self._filerev)
326 return self._filelog.linkrev(self._filerev)
327 def node(self):
327 def node(self):
328 return self._changectx.node()
328 return self._changectx.node()
329 def hex(self):
329 def hex(self):
330 return hex(self.node())
330 return hex(self.node())
331 def user(self):
331 def user(self):
332 return self._changectx.user()
332 return self._changectx.user()
333 def date(self):
333 def date(self):
334 return self._changectx.date()
334 return self._changectx.date()
335 def files(self):
335 def files(self):
336 return self._changectx.files()
336 return self._changectx.files()
337 def description(self):
337 def description(self):
338 return self._changectx.description()
338 return self._changectx.description()
339 def branch(self):
339 def branch(self):
340 return self._changectx.branch()
340 return self._changectx.branch()
341 def extra(self):
341 def extra(self):
342 return self._changectx.extra()
342 return self._changectx.extra()
343 def manifest(self):
343 def manifest(self):
344 return self._changectx.manifest()
344 return self._changectx.manifest()
345 def changectx(self):
345 def changectx(self):
346 return self._changectx
346 return self._changectx
347
347
348 def data(self):
348 def data(self):
349 return self._filelog.read(self._filenode)
349 return self._filelog.read(self._filenode)
350 def path(self):
350 def path(self):
351 return self._path
351 return self._path
352 def size(self):
352 def size(self):
353 return self._filelog.size(self._filerev)
353 return self._filelog.size(self._filerev)
354
354
355 def cmp(self, fctx):
355 def cmp(self, fctx):
356 """compare with other file context
356 """compare with other file context
357
357
358 returns True if different than fctx.
358 returns True if different than fctx.
359 """
359 """
360 if (fctx._filerev is None and self._repo._encodefilterpats
360 if (fctx._filerev is None and self._repo._encodefilterpats
361 or self.size() == fctx.size()):
361 or self.size() == fctx.size()):
362 return self._filelog.cmp(self._filenode, fctx.data())
362 return self._filelog.cmp(self._filenode, fctx.data())
363
363
364 return True
364 return True
365
365
366 def renamed(self):
366 def renamed(self):
367 """check if file was actually renamed in this changeset revision
367 """check if file was actually renamed in this changeset revision
368
368
369 If rename logged in file revision, we report copy for changeset only
369 If rename logged in file revision, we report copy for changeset only
370 if file revisions linkrev points back to the changeset in question
370 if file revisions linkrev points back to the changeset in question
371 or both changeset parents contain different file revisions.
371 or both changeset parents contain different file revisions.
372 """
372 """
373
373
374 renamed = self._filelog.renamed(self._filenode)
374 renamed = self._filelog.renamed(self._filenode)
375 if not renamed:
375 if not renamed:
376 return renamed
376 return renamed
377
377
378 if self.rev() == self.linkrev():
378 if self.rev() == self.linkrev():
379 return renamed
379 return renamed
380
380
381 name = self.path()
381 name = self.path()
382 fnode = self._filenode
382 fnode = self._filenode
383 for p in self._changectx.parents():
383 for p in self._changectx.parents():
384 try:
384 try:
385 if fnode == p.filenode(name):
385 if fnode == p.filenode(name):
386 return None
386 return None
387 except error.LookupError:
387 except error.LookupError:
388 pass
388 pass
389 return renamed
389 return renamed
390
390
391 def parents(self):
391 def parents(self):
392 p = self._path
392 p = self._path
393 fl = self._filelog
393 fl = self._filelog
394 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
394 pl = [(p, n, fl) for n in self._filelog.parents(self._filenode)]
395
395
396 r = self._filelog.renamed(self._filenode)
396 r = self._filelog.renamed(self._filenode)
397 if r:
397 if r:
398 pl[0] = (r[0], r[1], None)
398 pl[0] = (r[0], r[1], None)
399
399
400 return [filectx(self._repo, p, fileid=n, filelog=l)
400 return [filectx(self._repo, p, fileid=n, filelog=l)
401 for p, n, l in pl if n != nullid]
401 for p, n, l in pl if n != nullid]
402
402
403 def children(self):
403 def children(self):
404 # hard for renames
404 # hard for renames
405 c = self._filelog.children(self._filenode)
405 c = self._filelog.children(self._filenode)
406 return [filectx(self._repo, self._path, fileid=x,
406 return [filectx(self._repo, self._path, fileid=x,
407 filelog=self._filelog) for x in c]
407 filelog=self._filelog) for x in c]
408
408
409 def annotate(self, follow=False, linenumber=None):
409 def annotate(self, follow=False, linenumber=None):
410 '''returns a list of tuples of (ctx, line) for each line
410 '''returns a list of tuples of (ctx, line) for each line
411 in the file, where ctx is the filectx of the node where
411 in the file, where ctx is the filectx of the node where
412 that line was last changed.
412 that line was last changed.
413 This returns tuples of ((ctx, linenumber), line) for each line,
413 This returns tuples of ((ctx, linenumber), line) for each line,
414 if "linenumber" parameter is NOT "None".
414 if "linenumber" parameter is NOT "None".
415 In such tuples, linenumber means one at the first appearance
415 In such tuples, linenumber means one at the first appearance
416 in the managed file.
416 in the managed file.
417 To reduce annotation cost,
417 To reduce annotation cost,
418 this returns fixed value(False is used) as linenumber,
418 this returns fixed value(False is used) as linenumber,
419 if "linenumber" parameter is "False".'''
419 if "linenumber" parameter is "False".'''
420
420
421 def decorate_compat(text, rev):
421 def decorate_compat(text, rev):
422 return ([rev] * len(text.splitlines()), text)
422 return ([rev] * len(text.splitlines()), text)
423
423
424 def without_linenumber(text, rev):
424 def without_linenumber(text, rev):
425 return ([(rev, False)] * len(text.splitlines()), text)
425 return ([(rev, False)] * len(text.splitlines()), text)
426
426
427 def with_linenumber(text, rev):
427 def with_linenumber(text, rev):
428 size = len(text.splitlines())
428 size = len(text.splitlines())
429 return ([(rev, i) for i in xrange(1, size + 1)], text)
429 return ([(rev, i) for i in xrange(1, size + 1)], text)
430
430
431 decorate = (((linenumber is None) and decorate_compat) or
431 decorate = (((linenumber is None) and decorate_compat) or
432 (linenumber and with_linenumber) or
432 (linenumber and with_linenumber) or
433 without_linenumber)
433 without_linenumber)
434
434
435 def pair(parent, child):
435 def pair(parent, child):
436 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
436 for a1, a2, b1, b2 in bdiff.blocks(parent[1], child[1]):
437 child[0][b1:b2] = parent[0][a1:a2]
437 child[0][b1:b2] = parent[0][a1:a2]
438 return child
438 return child
439
439
440 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
440 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
441 def getctx(path, fileid):
441 def getctx(path, fileid):
442 log = path == self._path and self._filelog or getlog(path)
442 log = path == self._path and self._filelog or getlog(path)
443 return filectx(self._repo, path, fileid=fileid, filelog=log)
443 return filectx(self._repo, path, fileid=fileid, filelog=log)
444 getctx = util.lrucachefunc(getctx)
444 getctx = util.lrucachefunc(getctx)
445
445
446 def parents(f):
446 def parents(f):
447 # we want to reuse filectx objects as much as possible
447 # we want to reuse filectx objects as much as possible
448 p = f._path
448 p = f._path
449 if f._filerev is None: # working dir
449 if f._filerev is None: # working dir
450 pl = [(n.path(), n.filerev()) for n in f.parents()]
450 pl = [(n.path(), n.filerev()) for n in f.parents()]
451 else:
451 else:
452 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
452 pl = [(p, n) for n in f._filelog.parentrevs(f._filerev)]
453
453
454 if follow:
454 if follow:
455 r = f.renamed()
455 r = f.renamed()
456 if r:
456 if r:
457 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
457 pl[0] = (r[0], getlog(r[0]).rev(r[1]))
458
458
459 return [getctx(p, n) for p, n in pl if n != nullrev]
459 return [getctx(p, n) for p, n in pl if n != nullrev]
460
460
461 # use linkrev to find the first changeset where self appeared
461 # use linkrev to find the first changeset where self appeared
462 if self.rev() != self.linkrev():
462 if self.rev() != self.linkrev():
463 base = self.filectx(self.filerev())
463 base = self.filectx(self.filerev())
464 else:
464 else:
465 base = self
465 base = self
466
466
467 # find all ancestors
467 # find all ancestors
468 needed = {base: 1}
468 needed = {base: 1}
469 visit = [base]
469 visit = [base]
470 files = [base._path]
470 files = [base._path]
471 while visit:
471 while visit:
472 f = visit.pop(0)
472 f = visit.pop(0)
473 for p in parents(f):
473 for p in parents(f):
474 if p not in needed:
474 if p not in needed:
475 needed[p] = 1
475 needed[p] = 1
476 visit.append(p)
476 visit.append(p)
477 if p._path not in files:
477 if p._path not in files:
478 files.append(p._path)
478 files.append(p._path)
479 else:
479 else:
480 # count how many times we'll use this
480 # count how many times we'll use this
481 needed[p] += 1
481 needed[p] += 1
482
482
483 # sort by revision (per file) which is a topological order
483 # sort by revision (per file) which is a topological order
484 visit = []
484 visit = []
485 for f in files:
485 for f in files:
486 visit.extend(n for n in needed if n._path == f)
486 visit.extend(n for n in needed if n._path == f)
487
487
488 hist = {}
488 hist = {}
489 for f in sorted(visit, key=lambda x: x.rev()):
489 for f in sorted(visit, key=lambda x: x.rev()):
490 curr = decorate(f.data(), f)
490 curr = decorate(f.data(), f)
491 for p in parents(f):
491 for p in parents(f):
492 curr = pair(hist[p], curr)
492 curr = pair(hist[p], curr)
493 # trim the history of unneeded revs
493 # trim the history of unneeded revs
494 needed[p] -= 1
494 needed[p] -= 1
495 if not needed[p]:
495 if not needed[p]:
496 del hist[p]
496 del hist[p]
497 hist[f] = curr
497 hist[f] = curr
498
498
499 return zip(hist[f][0], hist[f][1].splitlines(True))
499 return zip(hist[f][0], hist[f][1].splitlines(True))
500
500
501 def ancestor(self, fc2, actx=None):
501 def ancestor(self, fc2, actx=None):
502 """
502 """
503 find the common ancestor file context, if any, of self, and fc2
503 find the common ancestor file context, if any, of self, and fc2
504
504
505 If actx is given, it must be the changectx of the common ancestor
505 If actx is given, it must be the changectx of the common ancestor
506 of self's and fc2's respective changesets.
506 of self's and fc2's respective changesets.
507 """
507 """
508
508
509 if actx is None:
509 if actx is None:
510 actx = self.changectx().ancestor(fc2.changectx())
510 actx = self.changectx().ancestor(fc2.changectx())
511
511
512 # the trivial case: changesets are unrelated, files must be too
512 # the trivial case: changesets are unrelated, files must be too
513 if not actx:
513 if not actx:
514 return None
514 return None
515
515
516 # the easy case: no (relevant) renames
516 # the easy case: no (relevant) renames
517 if fc2.path() == self.path() and self.path() in actx:
517 if fc2.path() == self.path() and self.path() in actx:
518 return actx[self.path()]
518 return actx[self.path()]
519 acache = {}
519 acache = {}
520
520
521 # prime the ancestor cache for the working directory
521 # prime the ancestor cache for the working directory
522 for c in (self, fc2):
522 for c in (self, fc2):
523 if c._filerev is None:
523 if c._filerev is None:
524 pl = [(n.path(), n.filenode()) for n in c.parents()]
524 pl = [(n.path(), n.filenode()) for n in c.parents()]
525 acache[(c._path, None)] = pl
525 acache[(c._path, None)] = pl
526
526
527 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
527 flcache = {self._repopath:self._filelog, fc2._repopath:fc2._filelog}
528 def parents(vertex):
528 def parents(vertex):
529 if vertex in acache:
529 if vertex in acache:
530 return acache[vertex]
530 return acache[vertex]
531 f, n = vertex
531 f, n = vertex
532 if f not in flcache:
532 if f not in flcache:
533 flcache[f] = self._repo.file(f)
533 flcache[f] = self._repo.file(f)
534 fl = flcache[f]
534 fl = flcache[f]
535 pl = [(f, p) for p in fl.parents(n) if p != nullid]
535 pl = [(f, p) for p in fl.parents(n) if p != nullid]
536 re = fl.renamed(n)
536 re = fl.renamed(n)
537 if re:
537 if re:
538 pl.append(re)
538 pl.append(re)
539 acache[vertex] = pl
539 acache[vertex] = pl
540 return pl
540 return pl
541
541
542 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
542 a, b = (self._path, self._filenode), (fc2._path, fc2._filenode)
543 v = ancestor.ancestor(a, b, parents)
543 v = ancestor.ancestor(a, b, parents)
544 if v:
544 if v:
545 f, n = v
545 f, n = v
546 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
546 return filectx(self._repo, f, fileid=n, filelog=flcache[f])
547
547
548 return None
548 return None
549
549
550 def ancestors(self):
550 def ancestors(self):
551 seen = set(str(self))
551 seen = set(str(self))
552 visit = [self]
552 visit = [self]
553 while visit:
553 while visit:
554 for parent in visit.pop(0).parents():
554 for parent in visit.pop(0).parents():
555 s = str(parent)
555 s = str(parent)
556 if s not in seen:
556 if s not in seen:
557 visit.append(parent)
557 visit.append(parent)
558 seen.add(s)
558 seen.add(s)
559 yield parent
559 yield parent
560
560
561 class workingctx(changectx):
561 class workingctx(changectx):
562 """A workingctx object makes access to data related to
562 """A workingctx object makes access to data related to
563 the current working directory convenient.
563 the current working directory convenient.
564 date - any valid date string or (unixtime, offset), or None.
564 date - any valid date string or (unixtime, offset), or None.
565 user - username string, or None.
565 user - username string, or None.
566 extra - a dictionary of extra values, or None.
566 extra - a dictionary of extra values, or None.
567 changes - a list of file lists as returned by localrepo.status()
567 changes - a list of file lists as returned by localrepo.status()
568 or None to use the repository status.
568 or None to use the repository status.
569 """
569 """
570 def __init__(self, repo, text="", user=None, date=None, extra=None,
570 def __init__(self, repo, text="", user=None, date=None, extra=None,
571 changes=None):
571 changes=None):
572 self._repo = repo
572 self._repo = repo
573 self._rev = None
573 self._rev = None
574 self._node = None
574 self._node = None
575 self._text = text
575 self._text = text
576 if date:
576 if date:
577 self._date = util.parsedate(date)
577 self._date = util.parsedate(date)
578 if user:
578 if user:
579 self._user = user
579 self._user = user
580 if changes:
580 if changes:
581 self._status = list(changes[:4])
581 self._status = list(changes[:4])
582 self._unknown = changes[4]
582 self._unknown = changes[4]
583 self._ignored = changes[5]
583 self._ignored = changes[5]
584 self._clean = changes[6]
584 self._clean = changes[6]
585 else:
585 else:
586 self._unknown = None
586 self._unknown = None
587 self._ignored = None
587 self._ignored = None
588 self._clean = None
588 self._clean = None
589
589
590 self._extra = {}
590 self._extra = {}
591 if extra:
591 if extra:
592 self._extra = extra.copy()
592 self._extra = extra.copy()
593 if 'branch' not in self._extra:
593 if 'branch' not in self._extra:
594 try:
594 try:
595 branch = encoding.fromlocal(self._repo.dirstate.branch())
595 branch = encoding.fromlocal(self._repo.dirstate.branch())
596 except UnicodeDecodeError:
596 except UnicodeDecodeError:
597 raise util.Abort(_('branch name not in UTF-8!'))
597 raise util.Abort(_('branch name not in UTF-8!'))
598 self._extra['branch'] = branch
598 self._extra['branch'] = branch
599 if self._extra['branch'] == '':
599 if self._extra['branch'] == '':
600 self._extra['branch'] = 'default'
600 self._extra['branch'] = 'default'
601
601
602 def __str__(self):
602 def __str__(self):
603 return str(self._parents[0]) + "+"
603 return str(self._parents[0]) + "+"
604
604
605 def __repr__(self):
605 def __repr__(self):
606 return "<workingctx %s>" % str(self)
606 return "<workingctx %s>" % str(self)
607
607
608 def __nonzero__(self):
608 def __nonzero__(self):
609 return True
609 return True
610
610
611 def __contains__(self, key):
611 def __contains__(self, key):
612 return self._repo.dirstate[key] not in "?r"
612 return self._repo.dirstate[key] not in "?r"
613
613
614 @propertycache
614 @propertycache
615 def _manifest(self):
615 def _manifest(self):
616 """generate a manifest corresponding to the working directory"""
616 """generate a manifest corresponding to the working directory"""
617
617
618 if self._unknown is None:
618 if self._unknown is None:
619 self.status(unknown=True)
619 self.status(unknown=True)
620
620
621 man = self._parents[0].manifest().copy()
621 man = self._parents[0].manifest().copy()
622 copied = self._repo.dirstate.copies()
622 copied = self._repo.dirstate.copies()
623 if len(self._parents) > 1:
623 if len(self._parents) > 1:
624 man2 = self.p2().manifest()
624 man2 = self.p2().manifest()
625 def getman(f):
625 def getman(f):
626 if f in man:
626 if f in man:
627 return man
627 return man
628 return man2
628 return man2
629 else:
629 else:
630 getman = lambda f: man
630 getman = lambda f: man
631 def cf(f):
631 def cf(f):
632 f = copied.get(f, f)
632 f = copied.get(f, f)
633 return getman(f).flags(f)
633 return getman(f).flags(f)
634 ff = self._repo.dirstate.flagfunc(cf)
634 ff = self._repo.dirstate.flagfunc(cf)
635 modified, added, removed, deleted = self._status
635 modified, added, removed, deleted = self._status
636 unknown = self._unknown
636 unknown = self._unknown
637 for i, l in (("a", added), ("m", modified), ("u", unknown)):
637 for i, l in (("a", added), ("m", modified), ("u", unknown)):
638 for f in l:
638 for f in l:
639 orig = copied.get(f, f)
639 orig = copied.get(f, f)
640 man[f] = getman(orig).get(orig, nullid) + i
640 man[f] = getman(orig).get(orig, nullid) + i
641 try:
641 try:
642 man.set(f, ff(f))
642 man.set(f, ff(f))
643 except OSError:
643 except OSError:
644 pass
644 pass
645
645
646 for f in deleted + removed:
646 for f in deleted + removed:
647 if f in man:
647 if f in man:
648 del man[f]
648 del man[f]
649
649
650 return man
650 return man
651
651
652 @propertycache
652 @propertycache
653 def _status(self):
653 def _status(self):
654 return self._repo.status()[:4]
654 return self._repo.status()[:4]
655
655
656 @propertycache
656 @propertycache
657 def _user(self):
657 def _user(self):
658 return self._repo.ui.username()
658 return self._repo.ui.username()
659
659
660 @propertycache
660 @propertycache
661 def _date(self):
661 def _date(self):
662 return util.makedate()
662 return util.makedate()
663
663
664 @propertycache
664 @propertycache
665 def _parents(self):
665 def _parents(self):
666 p = self._repo.dirstate.parents()
666 p = self._repo.dirstate.parents()
667 if p[1] == nullid:
667 if p[1] == nullid:
668 p = p[:-1]
668 p = p[:-1]
669 self._parents = [changectx(self._repo, x) for x in p]
669 self._parents = [changectx(self._repo, x) for x in p]
670 return self._parents
670 return self._parents
671
671
672 def status(self, ignored=False, clean=False, unknown=False):
672 def status(self, ignored=False, clean=False, unknown=False):
673 """Explicit status query
673 """Explicit status query
674 Unless this method is used to query the working copy status, the
674 Unless this method is used to query the working copy status, the
675 _status property will implicitly read the status using its default
675 _status property will implicitly read the status using its default
676 arguments."""
676 arguments."""
677 stat = self._repo.status(ignored=ignored, clean=clean, unknown=unknown)
677 stat = self._repo.status(ignored=ignored, clean=clean, unknown=unknown)
678 self._unknown = self._ignored = self._clean = None
678 self._unknown = self._ignored = self._clean = None
679 if unknown:
679 if unknown:
680 self._unknown = stat[4]
680 self._unknown = stat[4]
681 if ignored:
681 if ignored:
682 self._ignored = stat[5]
682 self._ignored = stat[5]
683 if clean:
683 if clean:
684 self._clean = stat[6]
684 self._clean = stat[6]
685 self._status = stat[:4]
685 self._status = stat[:4]
686 return stat
686 return stat
687
687
688 def manifest(self):
688 def manifest(self):
689 return self._manifest
689 return self._manifest
690 def user(self):
690 def user(self):
691 return self._user or self._repo.ui.username()
691 return self._user or self._repo.ui.username()
692 def date(self):
692 def date(self):
693 return self._date
693 return self._date
694 def description(self):
694 def description(self):
695 return self._text
695 return self._text
696 def files(self):
696 def files(self):
697 return sorted(self._status[0] + self._status[1] + self._status[2])
697 return sorted(self._status[0] + self._status[1] + self._status[2])
698
698
699 def modified(self):
699 def modified(self):
700 return self._status[0]
700 return self._status[0]
701 def added(self):
701 def added(self):
702 return self._status[1]
702 return self._status[1]
703 def removed(self):
703 def removed(self):
704 return self._status[2]
704 return self._status[2]
705 def deleted(self):
705 def deleted(self):
706 return self._status[3]
706 return self._status[3]
707 def unknown(self):
707 def unknown(self):
708 assert self._unknown is not None # must call status first
708 assert self._unknown is not None # must call status first
709 return self._unknown
709 return self._unknown
710 def ignored(self):
710 def ignored(self):
711 assert self._ignored is not None # must call status first
711 assert self._ignored is not None # must call status first
712 return self._ignored
712 return self._ignored
713 def clean(self):
713 def clean(self):
714 assert self._clean is not None # must call status first
714 assert self._clean is not None # must call status first
715 return self._clean
715 return self._clean
716 def branch(self):
716 def branch(self):
717 return encoding.tolocal(self._extra['branch'])
717 return encoding.tolocal(self._extra['branch'])
718 def extra(self):
718 def extra(self):
719 return self._extra
719 return self._extra
720
720
721 def tags(self):
721 def tags(self):
722 t = []
722 t = []
723 [t.extend(p.tags()) for p in self.parents()]
723 [t.extend(p.tags()) for p in self.parents()]
724 return t
724 return t
725
725
726 def children(self):
726 def children(self):
727 return []
727 return []
728
728
729 def flags(self, path):
729 def flags(self, path):
730 if '_manifest' in self.__dict__:
730 if '_manifest' in self.__dict__:
731 try:
731 try:
732 return self._manifest.flags(path)
732 return self._manifest.flags(path)
733 except KeyError:
733 except KeyError:
734 return ''
734 return ''
735
735
736 orig = self._repo.dirstate.copies().get(path, path)
736 orig = self._repo.dirstate.copies().get(path, path)
737
737
738 def findflag(ctx):
738 def findflag(ctx):
739 mnode = ctx.changeset()[0]
739 mnode = ctx.changeset()[0]
740 node, flag = self._repo.manifest.find(mnode, orig)
740 node, flag = self._repo.manifest.find(mnode, orig)
741 ff = self._repo.dirstate.flagfunc(lambda x: flag or '')
741 ff = self._repo.dirstate.flagfunc(lambda x: flag or '')
742 try:
742 try:
743 return ff(path)
743 return ff(path)
744 except OSError:
744 except OSError:
745 pass
745 pass
746
746
747 flag = findflag(self._parents[0])
747 flag = findflag(self._parents[0])
748 if flag is None and len(self.parents()) > 1:
748 if flag is None and len(self.parents()) > 1:
749 flag = findflag(self._parents[1])
749 flag = findflag(self._parents[1])
750 if flag is None or self._repo.dirstate[path] == 'r':
750 if flag is None or self._repo.dirstate[path] == 'r':
751 return ''
751 return ''
752 return flag
752 return flag
753
753
754 def filectx(self, path, filelog=None):
754 def filectx(self, path, filelog=None):
755 """get a file context from the working directory"""
755 """get a file context from the working directory"""
756 return workingfilectx(self._repo, path, workingctx=self,
756 return workingfilectx(self._repo, path, workingctx=self,
757 filelog=filelog)
757 filelog=filelog)
758
758
759 def ancestor(self, c2):
759 def ancestor(self, c2):
760 """return the ancestor context of self and c2"""
760 """return the ancestor context of self and c2"""
761 return self._parents[0].ancestor(c2) # punt on two parents for now
761 return self._parents[0].ancestor(c2) # punt on two parents for now
762
762
763 def walk(self, match):
763 def walk(self, match):
764 return sorted(self._repo.dirstate.walk(match, self.substate.keys(),
764 return sorted(self._repo.dirstate.walk(match, self.substate.keys(),
765 True, False))
765 True, False))
766
766
767 def dirty(self, missing=False):
767 def dirty(self, missing=False):
768 "check whether a working directory is modified"
768 "check whether a working directory is modified"
769 # check subrepos first
769 # check subrepos first
770 for s in self.substate:
770 for s in self.substate:
771 if self.sub(s).dirty():
771 if self.sub(s).dirty():
772 return True
772 return True
773 # check current working dir
773 # check current working dir
774 return (self.p2() or self.branch() != self.p1().branch() or
774 return (self.p2() or self.branch() != self.p1().branch() or
775 self.modified() or self.added() or self.removed() or
775 self.modified() or self.added() or self.removed() or
776 (missing and self.deleted()))
776 (missing and self.deleted()))
777
777
778 def add(self, list, prefix=""):
778 def add(self, list, prefix=""):
779 join = lambda f: os.path.join(prefix, f)
779 join = lambda f: os.path.join(prefix, f)
780 wlock = self._repo.wlock()
780 wlock = self._repo.wlock()
781 ui, ds = self._repo.ui, self._repo.dirstate
781 ui, ds = self._repo.ui, self._repo.dirstate
782 try:
782 try:
783 rejected = []
783 rejected = []
784 for f in list:
784 for f in list:
785 p = self._repo.wjoin(f)
785 p = self._repo.wjoin(f)
786 try:
786 try:
787 st = os.lstat(p)
787 st = os.lstat(p)
788 except:
788 except:
789 ui.warn(_("%s does not exist!\n") % join(f))
789 ui.warn(_("%s does not exist!\n") % join(f))
790 rejected.append(f)
790 rejected.append(f)
791 continue
791 continue
792 if st.st_size > 10000000:
792 if st.st_size > 10000000:
793 ui.warn(_("%s: up to %d MB of RAM may be required "
793 ui.warn(_("%s: up to %d MB of RAM may be required "
794 "to manage this file\n"
794 "to manage this file\n"
795 "(use 'hg revert %s' to cancel the "
795 "(use 'hg revert %s' to cancel the "
796 "pending addition)\n")
796 "pending addition)\n")
797 % (f, 3 * st.st_size // 1000000, join(f)))
797 % (f, 3 * st.st_size // 1000000, join(f)))
798 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
798 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
799 ui.warn(_("%s not added: only files and symlinks "
799 ui.warn(_("%s not added: only files and symlinks "
800 "supported currently\n") % join(f))
800 "supported currently\n") % join(f))
801 rejected.append(p)
801 rejected.append(p)
802 elif ds[f] in 'amn':
802 elif ds[f] in 'amn':
803 ui.warn(_("%s already tracked!\n") % join(f))
803 ui.warn(_("%s already tracked!\n") % join(f))
804 elif ds[f] == 'r':
804 elif ds[f] == 'r':
805 ds.normallookup(f)
805 ds.normallookup(f)
806 else:
806 else:
807 ds.add(f)
807 ds.add(f)
808 return rejected
808 return rejected
809 finally:
809 finally:
810 wlock.release()
810 wlock.release()
811
811
812 def forget(self, list):
812 def forget(self, list):
813 wlock = self._repo.wlock()
813 wlock = self._repo.wlock()
814 try:
814 try:
815 for f in list:
815 for f in list:
816 if self._repo.dirstate[f] != 'a':
816 if self._repo.dirstate[f] != 'a':
817 self._repo.ui.warn(_("%s not added!\n") % f)
817 self._repo.ui.warn(_("%s not added!\n") % f)
818 else:
818 else:
819 self._repo.dirstate.forget(f)
819 self._repo.dirstate.forget(f)
820 finally:
820 finally:
821 wlock.release()
821 wlock.release()
822
822
823 def ancestors(self):
823 def ancestors(self):
824 for a in self._repo.changelog.ancestors(
824 for a in self._repo.changelog.ancestors(
825 *[p.rev() for p in self._parents]):
825 *[p.rev() for p in self._parents]):
826 yield changectx(self._repo, a)
826 yield changectx(self._repo, a)
827
827
828 def remove(self, list, unlink=False):
828 def remove(self, list, unlink=False):
829 if unlink:
829 if unlink:
830 for f in list:
830 for f in list:
831 try:
831 try:
832 util.unlink(self._repo.wjoin(f))
832 util.unlinkpath(self._repo.wjoin(f))
833 except OSError, inst:
833 except OSError, inst:
834 if inst.errno != errno.ENOENT:
834 if inst.errno != errno.ENOENT:
835 raise
835 raise
836 wlock = self._repo.wlock()
836 wlock = self._repo.wlock()
837 try:
837 try:
838 for f in list:
838 for f in list:
839 if unlink and os.path.lexists(self._repo.wjoin(f)):
839 if unlink and os.path.lexists(self._repo.wjoin(f)):
840 self._repo.ui.warn(_("%s still exists!\n") % f)
840 self._repo.ui.warn(_("%s still exists!\n") % f)
841 elif self._repo.dirstate[f] == 'a':
841 elif self._repo.dirstate[f] == 'a':
842 self._repo.dirstate.forget(f)
842 self._repo.dirstate.forget(f)
843 elif f not in self._repo.dirstate:
843 elif f not in self._repo.dirstate:
844 self._repo.ui.warn(_("%s not tracked!\n") % f)
844 self._repo.ui.warn(_("%s not tracked!\n") % f)
845 else:
845 else:
846 self._repo.dirstate.remove(f)
846 self._repo.dirstate.remove(f)
847 finally:
847 finally:
848 wlock.release()
848 wlock.release()
849
849
850 def undelete(self, list):
850 def undelete(self, list):
851 pctxs = self.parents()
851 pctxs = self.parents()
852 wlock = self._repo.wlock()
852 wlock = self._repo.wlock()
853 try:
853 try:
854 for f in list:
854 for f in list:
855 if self._repo.dirstate[f] != 'r':
855 if self._repo.dirstate[f] != 'r':
856 self._repo.ui.warn(_("%s not removed!\n") % f)
856 self._repo.ui.warn(_("%s not removed!\n") % f)
857 else:
857 else:
858 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
858 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
859 t = fctx.data()
859 t = fctx.data()
860 self._repo.wwrite(f, t, fctx.flags())
860 self._repo.wwrite(f, t, fctx.flags())
861 self._repo.dirstate.normal(f)
861 self._repo.dirstate.normal(f)
862 finally:
862 finally:
863 wlock.release()
863 wlock.release()
864
864
865 def copy(self, source, dest):
865 def copy(self, source, dest):
866 p = self._repo.wjoin(dest)
866 p = self._repo.wjoin(dest)
867 if not os.path.lexists(p):
867 if not os.path.lexists(p):
868 self._repo.ui.warn(_("%s does not exist!\n") % dest)
868 self._repo.ui.warn(_("%s does not exist!\n") % dest)
869 elif not (os.path.isfile(p) or os.path.islink(p)):
869 elif not (os.path.isfile(p) or os.path.islink(p)):
870 self._repo.ui.warn(_("copy failed: %s is not a file or a "
870 self._repo.ui.warn(_("copy failed: %s is not a file or a "
871 "symbolic link\n") % dest)
871 "symbolic link\n") % dest)
872 else:
872 else:
873 wlock = self._repo.wlock()
873 wlock = self._repo.wlock()
874 try:
874 try:
875 if self._repo.dirstate[dest] in '?r':
875 if self._repo.dirstate[dest] in '?r':
876 self._repo.dirstate.add(dest)
876 self._repo.dirstate.add(dest)
877 self._repo.dirstate.copy(source, dest)
877 self._repo.dirstate.copy(source, dest)
878 finally:
878 finally:
879 wlock.release()
879 wlock.release()
880
880
881 class workingfilectx(filectx):
881 class workingfilectx(filectx):
882 """A workingfilectx object makes access to data related to a particular
882 """A workingfilectx object makes access to data related to a particular
883 file in the working directory convenient."""
883 file in the working directory convenient."""
884 def __init__(self, repo, path, filelog=None, workingctx=None):
884 def __init__(self, repo, path, filelog=None, workingctx=None):
885 """changeid can be a changeset revision, node, or tag.
885 """changeid can be a changeset revision, node, or tag.
886 fileid can be a file revision or node."""
886 fileid can be a file revision or node."""
887 self._repo = repo
887 self._repo = repo
888 self._path = path
888 self._path = path
889 self._changeid = None
889 self._changeid = None
890 self._filerev = self._filenode = None
890 self._filerev = self._filenode = None
891
891
892 if filelog:
892 if filelog:
893 self._filelog = filelog
893 self._filelog = filelog
894 if workingctx:
894 if workingctx:
895 self._changectx = workingctx
895 self._changectx = workingctx
896
896
897 @propertycache
897 @propertycache
898 def _changectx(self):
898 def _changectx(self):
899 return workingctx(self._repo)
899 return workingctx(self._repo)
900
900
901 def __nonzero__(self):
901 def __nonzero__(self):
902 return True
902 return True
903
903
904 def __str__(self):
904 def __str__(self):
905 return "%s@%s" % (self.path(), self._changectx)
905 return "%s@%s" % (self.path(), self._changectx)
906
906
907 def __repr__(self):
907 def __repr__(self):
908 return "<workingfilectx %s>" % str(self)
908 return "<workingfilectx %s>" % str(self)
909
909
910 def data(self):
910 def data(self):
911 return self._repo.wread(self._path)
911 return self._repo.wread(self._path)
912 def renamed(self):
912 def renamed(self):
913 rp = self._repo.dirstate.copied(self._path)
913 rp = self._repo.dirstate.copied(self._path)
914 if not rp:
914 if not rp:
915 return None
915 return None
916 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
916 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
917
917
918 def parents(self):
918 def parents(self):
919 '''return parent filectxs, following copies if necessary'''
919 '''return parent filectxs, following copies if necessary'''
920 def filenode(ctx, path):
920 def filenode(ctx, path):
921 return ctx._manifest.get(path, nullid)
921 return ctx._manifest.get(path, nullid)
922
922
923 path = self._path
923 path = self._path
924 fl = self._filelog
924 fl = self._filelog
925 pcl = self._changectx._parents
925 pcl = self._changectx._parents
926 renamed = self.renamed()
926 renamed = self.renamed()
927
927
928 if renamed:
928 if renamed:
929 pl = [renamed + (None,)]
929 pl = [renamed + (None,)]
930 else:
930 else:
931 pl = [(path, filenode(pcl[0], path), fl)]
931 pl = [(path, filenode(pcl[0], path), fl)]
932
932
933 for pc in pcl[1:]:
933 for pc in pcl[1:]:
934 pl.append((path, filenode(pc, path), fl))
934 pl.append((path, filenode(pc, path), fl))
935
935
936 return [filectx(self._repo, p, fileid=n, filelog=l)
936 return [filectx(self._repo, p, fileid=n, filelog=l)
937 for p, n, l in pl if n != nullid]
937 for p, n, l in pl if n != nullid]
938
938
939 def children(self):
939 def children(self):
940 return []
940 return []
941
941
942 def size(self):
942 def size(self):
943 return os.lstat(self._repo.wjoin(self._path)).st_size
943 return os.lstat(self._repo.wjoin(self._path)).st_size
944 def date(self):
944 def date(self):
945 t, tz = self._changectx.date()
945 t, tz = self._changectx.date()
946 try:
946 try:
947 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
947 return (int(os.lstat(self._repo.wjoin(self._path)).st_mtime), tz)
948 except OSError, err:
948 except OSError, err:
949 if err.errno != errno.ENOENT:
949 if err.errno != errno.ENOENT:
950 raise
950 raise
951 return (t, tz)
951 return (t, tz)
952
952
953 def cmp(self, fctx):
953 def cmp(self, fctx):
954 """compare with other file context
954 """compare with other file context
955
955
956 returns True if different than fctx.
956 returns True if different than fctx.
957 """
957 """
958 # fctx should be a filectx (not a wfctx)
958 # fctx should be a filectx (not a wfctx)
959 # invert comparison to reuse the same code path
959 # invert comparison to reuse the same code path
960 return fctx.cmp(self)
960 return fctx.cmp(self)
961
961
962 class memctx(object):
962 class memctx(object):
963 """Use memctx to perform in-memory commits via localrepo.commitctx().
963 """Use memctx to perform in-memory commits via localrepo.commitctx().
964
964
965 Revision information is supplied at initialization time while
965 Revision information is supplied at initialization time while
966 related files data and is made available through a callback
966 related files data and is made available through a callback
967 mechanism. 'repo' is the current localrepo, 'parents' is a
967 mechanism. 'repo' is the current localrepo, 'parents' is a
968 sequence of two parent revisions identifiers (pass None for every
968 sequence of two parent revisions identifiers (pass None for every
969 missing parent), 'text' is the commit message and 'files' lists
969 missing parent), 'text' is the commit message and 'files' lists
970 names of files touched by the revision (normalized and relative to
970 names of files touched by the revision (normalized and relative to
971 repository root).
971 repository root).
972
972
973 filectxfn(repo, memctx, path) is a callable receiving the
973 filectxfn(repo, memctx, path) is a callable receiving the
974 repository, the current memctx object and the normalized path of
974 repository, the current memctx object and the normalized path of
975 requested file, relative to repository root. It is fired by the
975 requested file, relative to repository root. It is fired by the
976 commit function for every file in 'files', but calls order is
976 commit function for every file in 'files', but calls order is
977 undefined. If the file is available in the revision being
977 undefined. If the file is available in the revision being
978 committed (updated or added), filectxfn returns a memfilectx
978 committed (updated or added), filectxfn returns a memfilectx
979 object. If the file was removed, filectxfn raises an
979 object. If the file was removed, filectxfn raises an
980 IOError. Moved files are represented by marking the source file
980 IOError. Moved files are represented by marking the source file
981 removed and the new file added with copy information (see
981 removed and the new file added with copy information (see
982 memfilectx).
982 memfilectx).
983
983
984 user receives the committer name and defaults to current
984 user receives the committer name and defaults to current
985 repository username, date is the commit date in any format
985 repository username, date is the commit date in any format
986 supported by util.parsedate() and defaults to current date, extra
986 supported by util.parsedate() and defaults to current date, extra
987 is a dictionary of metadata or is left empty.
987 is a dictionary of metadata or is left empty.
988 """
988 """
989 def __init__(self, repo, parents, text, files, filectxfn, user=None,
989 def __init__(self, repo, parents, text, files, filectxfn, user=None,
990 date=None, extra=None):
990 date=None, extra=None):
991 self._repo = repo
991 self._repo = repo
992 self._rev = None
992 self._rev = None
993 self._node = None
993 self._node = None
994 self._text = text
994 self._text = text
995 self._date = date and util.parsedate(date) or util.makedate()
995 self._date = date and util.parsedate(date) or util.makedate()
996 self._user = user
996 self._user = user
997 parents = [(p or nullid) for p in parents]
997 parents = [(p or nullid) for p in parents]
998 p1, p2 = parents
998 p1, p2 = parents
999 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
999 self._parents = [changectx(self._repo, p) for p in (p1, p2)]
1000 files = sorted(set(files))
1000 files = sorted(set(files))
1001 self._status = [files, [], [], [], []]
1001 self._status = [files, [], [], [], []]
1002 self._filectxfn = filectxfn
1002 self._filectxfn = filectxfn
1003
1003
1004 self._extra = extra and extra.copy() or {}
1004 self._extra = extra and extra.copy() or {}
1005 if 'branch' not in self._extra:
1005 if 'branch' not in self._extra:
1006 self._extra['branch'] = 'default'
1006 self._extra['branch'] = 'default'
1007 elif self._extra.get('branch') == '':
1007 elif self._extra.get('branch') == '':
1008 self._extra['branch'] = 'default'
1008 self._extra['branch'] = 'default'
1009
1009
1010 def __str__(self):
1010 def __str__(self):
1011 return str(self._parents[0]) + "+"
1011 return str(self._parents[0]) + "+"
1012
1012
1013 def __int__(self):
1013 def __int__(self):
1014 return self._rev
1014 return self._rev
1015
1015
1016 def __nonzero__(self):
1016 def __nonzero__(self):
1017 return True
1017 return True
1018
1018
1019 def __getitem__(self, key):
1019 def __getitem__(self, key):
1020 return self.filectx(key)
1020 return self.filectx(key)
1021
1021
1022 def p1(self):
1022 def p1(self):
1023 return self._parents[0]
1023 return self._parents[0]
1024 def p2(self):
1024 def p2(self):
1025 return self._parents[1]
1025 return self._parents[1]
1026
1026
1027 def user(self):
1027 def user(self):
1028 return self._user or self._repo.ui.username()
1028 return self._user or self._repo.ui.username()
1029 def date(self):
1029 def date(self):
1030 return self._date
1030 return self._date
1031 def description(self):
1031 def description(self):
1032 return self._text
1032 return self._text
1033 def files(self):
1033 def files(self):
1034 return self.modified()
1034 return self.modified()
1035 def modified(self):
1035 def modified(self):
1036 return self._status[0]
1036 return self._status[0]
1037 def added(self):
1037 def added(self):
1038 return self._status[1]
1038 return self._status[1]
1039 def removed(self):
1039 def removed(self):
1040 return self._status[2]
1040 return self._status[2]
1041 def deleted(self):
1041 def deleted(self):
1042 return self._status[3]
1042 return self._status[3]
1043 def unknown(self):
1043 def unknown(self):
1044 return self._status[4]
1044 return self._status[4]
1045 def ignored(self):
1045 def ignored(self):
1046 return self._status[5]
1046 return self._status[5]
1047 def clean(self):
1047 def clean(self):
1048 return self._status[6]
1048 return self._status[6]
1049 def branch(self):
1049 def branch(self):
1050 return encoding.tolocal(self._extra['branch'])
1050 return encoding.tolocal(self._extra['branch'])
1051 def extra(self):
1051 def extra(self):
1052 return self._extra
1052 return self._extra
1053 def flags(self, f):
1053 def flags(self, f):
1054 return self[f].flags()
1054 return self[f].flags()
1055
1055
1056 def parents(self):
1056 def parents(self):
1057 """return contexts for each parent changeset"""
1057 """return contexts for each parent changeset"""
1058 return self._parents
1058 return self._parents
1059
1059
1060 def filectx(self, path, filelog=None):
1060 def filectx(self, path, filelog=None):
1061 """get a file context from the working directory"""
1061 """get a file context from the working directory"""
1062 return self._filectxfn(self._repo, self, path)
1062 return self._filectxfn(self._repo, self, path)
1063
1063
1064 def commit(self):
1064 def commit(self):
1065 """commit context to the repo"""
1065 """commit context to the repo"""
1066 return self._repo.commitctx(self)
1066 return self._repo.commitctx(self)
1067
1067
1068 class memfilectx(object):
1068 class memfilectx(object):
1069 """memfilectx represents an in-memory file to commit.
1069 """memfilectx represents an in-memory file to commit.
1070
1070
1071 See memctx for more details.
1071 See memctx for more details.
1072 """
1072 """
1073 def __init__(self, path, data, islink=False, isexec=False, copied=None):
1073 def __init__(self, path, data, islink=False, isexec=False, copied=None):
1074 """
1074 """
1075 path is the normalized file path relative to repository root.
1075 path is the normalized file path relative to repository root.
1076 data is the file content as a string.
1076 data is the file content as a string.
1077 islink is True if the file is a symbolic link.
1077 islink is True if the file is a symbolic link.
1078 isexec is True if the file is executable.
1078 isexec is True if the file is executable.
1079 copied is the source file path if current file was copied in the
1079 copied is the source file path if current file was copied in the
1080 revision being committed, or None."""
1080 revision being committed, or None."""
1081 self._path = path
1081 self._path = path
1082 self._data = data
1082 self._data = data
1083 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
1083 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
1084 self._copied = None
1084 self._copied = None
1085 if copied:
1085 if copied:
1086 self._copied = (copied, nullid)
1086 self._copied = (copied, nullid)
1087
1087
1088 def __nonzero__(self):
1088 def __nonzero__(self):
1089 return True
1089 return True
1090 def __str__(self):
1090 def __str__(self):
1091 return "%s@%s" % (self.path(), self._changectx)
1091 return "%s@%s" % (self.path(), self._changectx)
1092 def path(self):
1092 def path(self):
1093 return self._path
1093 return self._path
1094 def data(self):
1094 def data(self):
1095 return self._data
1095 return self._data
1096 def flags(self):
1096 def flags(self):
1097 return self._flags
1097 return self._flags
1098 def isexec(self):
1098 def isexec(self):
1099 return 'x' in self._flags
1099 return 'x' in self._flags
1100 def islink(self):
1100 def islink(self):
1101 return 'l' in self._flags
1101 return 'l' in self._flags
1102 def renamed(self):
1102 def renamed(self):
1103 return self._copied
1103 return self._copied
@@ -1,549 +1,549 b''
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import nullid, nullrev, hex, bin
8 from node import nullid, nullrev, hex, bin
9 from i18n import _
9 from i18n import _
10 import util, filemerge, copies, subrepo
10 import util, filemerge, copies, subrepo
11 import errno, os, shutil
11 import errno, os, shutil
12
12
13 class mergestate(object):
13 class mergestate(object):
14 '''track 3-way merge state of individual files'''
14 '''track 3-way merge state of individual files'''
15 def __init__(self, repo):
15 def __init__(self, repo):
16 self._repo = repo
16 self._repo = repo
17 self._dirty = False
17 self._dirty = False
18 self._read()
18 self._read()
19 def reset(self, node=None):
19 def reset(self, node=None):
20 self._state = {}
20 self._state = {}
21 if node:
21 if node:
22 self._local = node
22 self._local = node
23 shutil.rmtree(self._repo.join("merge"), True)
23 shutil.rmtree(self._repo.join("merge"), True)
24 self._dirty = False
24 self._dirty = False
25 def _read(self):
25 def _read(self):
26 self._state = {}
26 self._state = {}
27 try:
27 try:
28 f = self._repo.opener("merge/state")
28 f = self._repo.opener("merge/state")
29 for i, l in enumerate(f):
29 for i, l in enumerate(f):
30 if i == 0:
30 if i == 0:
31 self._local = bin(l[:-1])
31 self._local = bin(l[:-1])
32 else:
32 else:
33 bits = l[:-1].split("\0")
33 bits = l[:-1].split("\0")
34 self._state[bits[0]] = bits[1:]
34 self._state[bits[0]] = bits[1:]
35 except IOError, err:
35 except IOError, err:
36 if err.errno != errno.ENOENT:
36 if err.errno != errno.ENOENT:
37 raise
37 raise
38 self._dirty = False
38 self._dirty = False
39 def commit(self):
39 def commit(self):
40 if self._dirty:
40 if self._dirty:
41 f = self._repo.opener("merge/state", "w")
41 f = self._repo.opener("merge/state", "w")
42 f.write(hex(self._local) + "\n")
42 f.write(hex(self._local) + "\n")
43 for d, v in self._state.iteritems():
43 for d, v in self._state.iteritems():
44 f.write("\0".join([d] + v) + "\n")
44 f.write("\0".join([d] + v) + "\n")
45 self._dirty = False
45 self._dirty = False
46 def add(self, fcl, fco, fca, fd, flags):
46 def add(self, fcl, fco, fca, fd, flags):
47 hash = util.sha1(fcl.path()).hexdigest()
47 hash = util.sha1(fcl.path()).hexdigest()
48 self._repo.opener("merge/" + hash, "w").write(fcl.data())
48 self._repo.opener("merge/" + hash, "w").write(fcl.data())
49 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
49 self._state[fd] = ['u', hash, fcl.path(), fca.path(),
50 hex(fca.filenode()), fco.path(), flags]
50 hex(fca.filenode()), fco.path(), flags]
51 self._dirty = True
51 self._dirty = True
52 def __contains__(self, dfile):
52 def __contains__(self, dfile):
53 return dfile in self._state
53 return dfile in self._state
54 def __getitem__(self, dfile):
54 def __getitem__(self, dfile):
55 return self._state[dfile][0]
55 return self._state[dfile][0]
56 def __iter__(self):
56 def __iter__(self):
57 l = self._state.keys()
57 l = self._state.keys()
58 l.sort()
58 l.sort()
59 for f in l:
59 for f in l:
60 yield f
60 yield f
61 def mark(self, dfile, state):
61 def mark(self, dfile, state):
62 self._state[dfile][0] = state
62 self._state[dfile][0] = state
63 self._dirty = True
63 self._dirty = True
64 def resolve(self, dfile, wctx, octx):
64 def resolve(self, dfile, wctx, octx):
65 if self[dfile] == 'r':
65 if self[dfile] == 'r':
66 return 0
66 return 0
67 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
67 state, hash, lfile, afile, anode, ofile, flags = self._state[dfile]
68 f = self._repo.opener("merge/" + hash)
68 f = self._repo.opener("merge/" + hash)
69 self._repo.wwrite(dfile, f.read(), flags)
69 self._repo.wwrite(dfile, f.read(), flags)
70 fcd = wctx[dfile]
70 fcd = wctx[dfile]
71 fco = octx[ofile]
71 fco = octx[ofile]
72 fca = self._repo.filectx(afile, fileid=anode)
72 fca = self._repo.filectx(afile, fileid=anode)
73 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
73 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca)
74 if not r:
74 if not r:
75 self.mark(dfile, 'r')
75 self.mark(dfile, 'r')
76 return r
76 return r
77
77
78 def _checkunknown(wctx, mctx):
78 def _checkunknown(wctx, mctx):
79 "check for collisions between unknown files and files in mctx"
79 "check for collisions between unknown files and files in mctx"
80 for f in wctx.unknown():
80 for f in wctx.unknown():
81 if f in mctx and mctx[f].cmp(wctx[f]):
81 if f in mctx and mctx[f].cmp(wctx[f]):
82 raise util.Abort(_("untracked file in working directory differs"
82 raise util.Abort(_("untracked file in working directory differs"
83 " from file in requested revision: '%s'") % f)
83 " from file in requested revision: '%s'") % f)
84
84
85 def _checkcollision(mctx):
85 def _checkcollision(mctx):
86 "check for case folding collisions in the destination context"
86 "check for case folding collisions in the destination context"
87 folded = {}
87 folded = {}
88 for fn in mctx:
88 for fn in mctx:
89 fold = fn.lower()
89 fold = fn.lower()
90 if fold in folded:
90 if fold in folded:
91 raise util.Abort(_("case-folding collision between %s and %s")
91 raise util.Abort(_("case-folding collision between %s and %s")
92 % (fn, folded[fold]))
92 % (fn, folded[fold]))
93 folded[fold] = fn
93 folded[fold] = fn
94
94
95 def _forgetremoved(wctx, mctx, branchmerge):
95 def _forgetremoved(wctx, mctx, branchmerge):
96 """
96 """
97 Forget removed files
97 Forget removed files
98
98
99 If we're jumping between revisions (as opposed to merging), and if
99 If we're jumping between revisions (as opposed to merging), and if
100 neither the working directory nor the target rev has the file,
100 neither the working directory nor the target rev has the file,
101 then we need to remove it from the dirstate, to prevent the
101 then we need to remove it from the dirstate, to prevent the
102 dirstate from listing the file when it is no longer in the
102 dirstate from listing the file when it is no longer in the
103 manifest.
103 manifest.
104
104
105 If we're merging, and the other revision has removed a file
105 If we're merging, and the other revision has removed a file
106 that is not present in the working directory, we need to mark it
106 that is not present in the working directory, we need to mark it
107 as removed.
107 as removed.
108 """
108 """
109
109
110 action = []
110 action = []
111 state = branchmerge and 'r' or 'f'
111 state = branchmerge and 'r' or 'f'
112 for f in wctx.deleted():
112 for f in wctx.deleted():
113 if f not in mctx:
113 if f not in mctx:
114 action.append((f, state))
114 action.append((f, state))
115
115
116 if not branchmerge:
116 if not branchmerge:
117 for f in wctx.removed():
117 for f in wctx.removed():
118 if f not in mctx:
118 if f not in mctx:
119 action.append((f, "f"))
119 action.append((f, "f"))
120
120
121 return action
121 return action
122
122
123 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
123 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
124 """
124 """
125 Merge p1 and p2 with ancestor pa and generate merge action list
125 Merge p1 and p2 with ancestor pa and generate merge action list
126
126
127 overwrite = whether we clobber working files
127 overwrite = whether we clobber working files
128 partial = function to filter file lists
128 partial = function to filter file lists
129 """
129 """
130
130
131 def fmerge(f, f2, fa):
131 def fmerge(f, f2, fa):
132 """merge flags"""
132 """merge flags"""
133 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
133 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
134 if m == n: # flags agree
134 if m == n: # flags agree
135 return m # unchanged
135 return m # unchanged
136 if m and n and not a: # flags set, don't agree, differ from parent
136 if m and n and not a: # flags set, don't agree, differ from parent
137 r = repo.ui.promptchoice(
137 r = repo.ui.promptchoice(
138 _(" conflicting flags for %s\n"
138 _(" conflicting flags for %s\n"
139 "(n)one, e(x)ec or sym(l)ink?") % f,
139 "(n)one, e(x)ec or sym(l)ink?") % f,
140 (_("&None"), _("E&xec"), _("Sym&link")), 0)
140 (_("&None"), _("E&xec"), _("Sym&link")), 0)
141 if r == 1:
141 if r == 1:
142 return "x" # Exec
142 return "x" # Exec
143 if r == 2:
143 if r == 2:
144 return "l" # Symlink
144 return "l" # Symlink
145 return ""
145 return ""
146 if m and m != a: # changed from a to m
146 if m and m != a: # changed from a to m
147 return m
147 return m
148 if n and n != a: # changed from a to n
148 if n and n != a: # changed from a to n
149 return n
149 return n
150 return '' # flag was cleared
150 return '' # flag was cleared
151
151
152 def act(msg, m, f, *args):
152 def act(msg, m, f, *args):
153 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
153 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
154 action.append((f, m) + args)
154 action.append((f, m) + args)
155
155
156 action, copy = [], {}
156 action, copy = [], {}
157
157
158 if overwrite:
158 if overwrite:
159 pa = p1
159 pa = p1
160 elif pa == p2: # backwards
160 elif pa == p2: # backwards
161 pa = p1.p1()
161 pa = p1.p1()
162 elif pa and repo.ui.configbool("merge", "followcopies", True):
162 elif pa and repo.ui.configbool("merge", "followcopies", True):
163 dirs = repo.ui.configbool("merge", "followdirs", True)
163 dirs = repo.ui.configbool("merge", "followdirs", True)
164 copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
164 copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
165 for of, fl in diverge.iteritems():
165 for of, fl in diverge.iteritems():
166 act("divergent renames", "dr", of, fl)
166 act("divergent renames", "dr", of, fl)
167
167
168 repo.ui.note(_("resolving manifests\n"))
168 repo.ui.note(_("resolving manifests\n"))
169 repo.ui.debug(" overwrite %s partial %s\n" % (overwrite, bool(partial)))
169 repo.ui.debug(" overwrite %s partial %s\n" % (overwrite, bool(partial)))
170 repo.ui.debug(" ancestor %s local %s remote %s\n" % (pa, p1, p2))
170 repo.ui.debug(" ancestor %s local %s remote %s\n" % (pa, p1, p2))
171
171
172 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
172 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
173 copied = set(copy.values())
173 copied = set(copy.values())
174
174
175 if '.hgsubstate' in m1:
175 if '.hgsubstate' in m1:
176 # check whether sub state is modified
176 # check whether sub state is modified
177 for s in p1.substate:
177 for s in p1.substate:
178 if p1.sub(s).dirty():
178 if p1.sub(s).dirty():
179 m1['.hgsubstate'] += "+"
179 m1['.hgsubstate'] += "+"
180 break
180 break
181
181
182 # Compare manifests
182 # Compare manifests
183 for f, n in m1.iteritems():
183 for f, n in m1.iteritems():
184 if partial and not partial(f):
184 if partial and not partial(f):
185 continue
185 continue
186 if f in m2:
186 if f in m2:
187 rflags = fmerge(f, f, f)
187 rflags = fmerge(f, f, f)
188 a = ma.get(f, nullid)
188 a = ma.get(f, nullid)
189 if n == m2[f] or m2[f] == a: # same or local newer
189 if n == m2[f] or m2[f] == a: # same or local newer
190 # is file locally modified or flags need changing?
190 # is file locally modified or flags need changing?
191 # dirstate flags may need to be made current
191 # dirstate flags may need to be made current
192 if m1.flags(f) != rflags or n[20:]:
192 if m1.flags(f) != rflags or n[20:]:
193 act("update permissions", "e", f, rflags)
193 act("update permissions", "e", f, rflags)
194 elif n == a: # remote newer
194 elif n == a: # remote newer
195 act("remote is newer", "g", f, rflags)
195 act("remote is newer", "g", f, rflags)
196 else: # both changed
196 else: # both changed
197 act("versions differ", "m", f, f, f, rflags, False)
197 act("versions differ", "m", f, f, f, rflags, False)
198 elif f in copied: # files we'll deal with on m2 side
198 elif f in copied: # files we'll deal with on m2 side
199 pass
199 pass
200 elif f in copy:
200 elif f in copy:
201 f2 = copy[f]
201 f2 = copy[f]
202 if f2 not in m2: # directory rename
202 if f2 not in m2: # directory rename
203 act("remote renamed directory to " + f2, "d",
203 act("remote renamed directory to " + f2, "d",
204 f, None, f2, m1.flags(f))
204 f, None, f2, m1.flags(f))
205 else: # case 2 A,B/B/B or case 4,21 A/B/B
205 else: # case 2 A,B/B/B or case 4,21 A/B/B
206 act("local copied/moved to " + f2, "m",
206 act("local copied/moved to " + f2, "m",
207 f, f2, f, fmerge(f, f2, f2), False)
207 f, f2, f, fmerge(f, f2, f2), False)
208 elif f in ma: # clean, a different, no remote
208 elif f in ma: # clean, a different, no remote
209 if n != ma[f]:
209 if n != ma[f]:
210 if repo.ui.promptchoice(
210 if repo.ui.promptchoice(
211 _(" local changed %s which remote deleted\n"
211 _(" local changed %s which remote deleted\n"
212 "use (c)hanged version or (d)elete?") % f,
212 "use (c)hanged version or (d)elete?") % f,
213 (_("&Changed"), _("&Delete")), 0):
213 (_("&Changed"), _("&Delete")), 0):
214 act("prompt delete", "r", f)
214 act("prompt delete", "r", f)
215 else:
215 else:
216 act("prompt keep", "a", f)
216 act("prompt keep", "a", f)
217 elif n[20:] == "a": # added, no remote
217 elif n[20:] == "a": # added, no remote
218 act("remote deleted", "f", f)
218 act("remote deleted", "f", f)
219 elif n[20:] != "u":
219 elif n[20:] != "u":
220 act("other deleted", "r", f)
220 act("other deleted", "r", f)
221
221
222 for f, n in m2.iteritems():
222 for f, n in m2.iteritems():
223 if partial and not partial(f):
223 if partial and not partial(f):
224 continue
224 continue
225 if f in m1 or f in copied: # files already visited
225 if f in m1 or f in copied: # files already visited
226 continue
226 continue
227 if f in copy:
227 if f in copy:
228 f2 = copy[f]
228 f2 = copy[f]
229 if f2 not in m1: # directory rename
229 if f2 not in m1: # directory rename
230 act("local renamed directory to " + f2, "d",
230 act("local renamed directory to " + f2, "d",
231 None, f, f2, m2.flags(f))
231 None, f, f2, m2.flags(f))
232 elif f2 in m2: # rename case 1, A/A,B/A
232 elif f2 in m2: # rename case 1, A/A,B/A
233 act("remote copied to " + f, "m",
233 act("remote copied to " + f, "m",
234 f2, f, f, fmerge(f2, f, f2), False)
234 f2, f, f, fmerge(f2, f, f2), False)
235 else: # case 3,20 A/B/A
235 else: # case 3,20 A/B/A
236 act("remote moved to " + f, "m",
236 act("remote moved to " + f, "m",
237 f2, f, f, fmerge(f2, f, f2), True)
237 f2, f, f, fmerge(f2, f, f2), True)
238 elif f not in ma:
238 elif f not in ma:
239 act("remote created", "g", f, m2.flags(f))
239 act("remote created", "g", f, m2.flags(f))
240 elif n != ma[f]:
240 elif n != ma[f]:
241 if repo.ui.promptchoice(
241 if repo.ui.promptchoice(
242 _("remote changed %s which local deleted\n"
242 _("remote changed %s which local deleted\n"
243 "use (c)hanged version or leave (d)eleted?") % f,
243 "use (c)hanged version or leave (d)eleted?") % f,
244 (_("&Changed"), _("&Deleted")), 0) == 0:
244 (_("&Changed"), _("&Deleted")), 0) == 0:
245 act("prompt recreating", "g", f, m2.flags(f))
245 act("prompt recreating", "g", f, m2.flags(f))
246
246
247 return action
247 return action
248
248
249 def actionkey(a):
249 def actionkey(a):
250 return a[1] == 'r' and -1 or 0, a
250 return a[1] == 'r' and -1 or 0, a
251
251
252 def applyupdates(repo, action, wctx, mctx, actx):
252 def applyupdates(repo, action, wctx, mctx, actx):
253 """apply the merge action list to the working directory
253 """apply the merge action list to the working directory
254
254
255 wctx is the working copy context
255 wctx is the working copy context
256 mctx is the context to be merged into the working copy
256 mctx is the context to be merged into the working copy
257 actx is the context of the common ancestor
257 actx is the context of the common ancestor
258
258
259 Return a tuple of counts (updated, merged, removed, unresolved) that
259 Return a tuple of counts (updated, merged, removed, unresolved) that
260 describes how many files were affected by the update.
260 describes how many files were affected by the update.
261 """
261 """
262
262
263 updated, merged, removed, unresolved = 0, 0, 0, 0
263 updated, merged, removed, unresolved = 0, 0, 0, 0
264 ms = mergestate(repo)
264 ms = mergestate(repo)
265 ms.reset(wctx.parents()[0].node())
265 ms.reset(wctx.parents()[0].node())
266 moves = []
266 moves = []
267 action.sort(key=actionkey)
267 action.sort(key=actionkey)
268 substate = wctx.substate # prime
268 substate = wctx.substate # prime
269
269
270 # prescan for merges
270 # prescan for merges
271 u = repo.ui
271 u = repo.ui
272 for a in action:
272 for a in action:
273 f, m = a[:2]
273 f, m = a[:2]
274 if m == 'm': # merge
274 if m == 'm': # merge
275 f2, fd, flags, move = a[2:]
275 f2, fd, flags, move = a[2:]
276 if f == '.hgsubstate': # merged internally
276 if f == '.hgsubstate': # merged internally
277 continue
277 continue
278 repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
278 repo.ui.debug("preserving %s for resolve of %s\n" % (f, fd))
279 fcl = wctx[f]
279 fcl = wctx[f]
280 fco = mctx[f2]
280 fco = mctx[f2]
281 if mctx == actx: # backwards, use working dir parent as ancestor
281 if mctx == actx: # backwards, use working dir parent as ancestor
282 if fcl.parents():
282 if fcl.parents():
283 fca = fcl.parents()[0]
283 fca = fcl.parents()[0]
284 else:
284 else:
285 fca = repo.filectx(f, fileid=nullrev)
285 fca = repo.filectx(f, fileid=nullrev)
286 else:
286 else:
287 fca = fcl.ancestor(fco, actx)
287 fca = fcl.ancestor(fco, actx)
288 if not fca:
288 if not fca:
289 fca = repo.filectx(f, fileid=nullrev)
289 fca = repo.filectx(f, fileid=nullrev)
290 ms.add(fcl, fco, fca, fd, flags)
290 ms.add(fcl, fco, fca, fd, flags)
291 if f != fd and move:
291 if f != fd and move:
292 moves.append(f)
292 moves.append(f)
293
293
294 # remove renamed files after safely stored
294 # remove renamed files after safely stored
295 for f in moves:
295 for f in moves:
296 if os.path.lexists(repo.wjoin(f)):
296 if os.path.lexists(repo.wjoin(f)):
297 repo.ui.debug("removing %s\n" % f)
297 repo.ui.debug("removing %s\n" % f)
298 os.unlink(repo.wjoin(f))
298 os.unlink(repo.wjoin(f))
299
299
300 audit_path = util.path_auditor(repo.root)
300 audit_path = util.path_auditor(repo.root)
301
301
302 numupdates = len(action)
302 numupdates = len(action)
303 for i, a in enumerate(action):
303 for i, a in enumerate(action):
304 f, m = a[:2]
304 f, m = a[:2]
305 u.progress(_('updating'), i + 1, item=f, total=numupdates,
305 u.progress(_('updating'), i + 1, item=f, total=numupdates,
306 unit=_('files'))
306 unit=_('files'))
307 if f and f[0] == "/":
307 if f and f[0] == "/":
308 continue
308 continue
309 if m == "r": # remove
309 if m == "r": # remove
310 repo.ui.note(_("removing %s\n") % f)
310 repo.ui.note(_("removing %s\n") % f)
311 audit_path(f)
311 audit_path(f)
312 if f == '.hgsubstate': # subrepo states need updating
312 if f == '.hgsubstate': # subrepo states need updating
313 subrepo.submerge(repo, wctx, mctx, wctx)
313 subrepo.submerge(repo, wctx, mctx, wctx)
314 try:
314 try:
315 util.unlink(repo.wjoin(f))
315 util.unlinkpath(repo.wjoin(f))
316 except OSError, inst:
316 except OSError, inst:
317 if inst.errno != errno.ENOENT:
317 if inst.errno != errno.ENOENT:
318 repo.ui.warn(_("update failed to remove %s: %s!\n") %
318 repo.ui.warn(_("update failed to remove %s: %s!\n") %
319 (f, inst.strerror))
319 (f, inst.strerror))
320 removed += 1
320 removed += 1
321 elif m == "m": # merge
321 elif m == "m": # merge
322 if f == '.hgsubstate': # subrepo states need updating
322 if f == '.hgsubstate': # subrepo states need updating
323 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx))
323 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx))
324 continue
324 continue
325 f2, fd, flags, move = a[2:]
325 f2, fd, flags, move = a[2:]
326 r = ms.resolve(fd, wctx, mctx)
326 r = ms.resolve(fd, wctx, mctx)
327 if r is not None and r > 0:
327 if r is not None and r > 0:
328 unresolved += 1
328 unresolved += 1
329 else:
329 else:
330 if r is None:
330 if r is None:
331 updated += 1
331 updated += 1
332 else:
332 else:
333 merged += 1
333 merged += 1
334 util.set_flags(repo.wjoin(fd), 'l' in flags, 'x' in flags)
334 util.set_flags(repo.wjoin(fd), 'l' in flags, 'x' in flags)
335 if f != fd and move and os.path.lexists(repo.wjoin(f)):
335 if f != fd and move and os.path.lexists(repo.wjoin(f)):
336 repo.ui.debug("removing %s\n" % f)
336 repo.ui.debug("removing %s\n" % f)
337 os.unlink(repo.wjoin(f))
337 os.unlink(repo.wjoin(f))
338 elif m == "g": # get
338 elif m == "g": # get
339 flags = a[2]
339 flags = a[2]
340 repo.ui.note(_("getting %s\n") % f)
340 repo.ui.note(_("getting %s\n") % f)
341 t = mctx.filectx(f).data()
341 t = mctx.filectx(f).data()
342 repo.wwrite(f, t, flags)
342 repo.wwrite(f, t, flags)
343 t = None
343 t = None
344 updated += 1
344 updated += 1
345 if f == '.hgsubstate': # subrepo states need updating
345 if f == '.hgsubstate': # subrepo states need updating
346 subrepo.submerge(repo, wctx, mctx, wctx)
346 subrepo.submerge(repo, wctx, mctx, wctx)
347 elif m == "d": # directory rename
347 elif m == "d": # directory rename
348 f2, fd, flags = a[2:]
348 f2, fd, flags = a[2:]
349 if f:
349 if f:
350 repo.ui.note(_("moving %s to %s\n") % (f, fd))
350 repo.ui.note(_("moving %s to %s\n") % (f, fd))
351 t = wctx.filectx(f).data()
351 t = wctx.filectx(f).data()
352 repo.wwrite(fd, t, flags)
352 repo.wwrite(fd, t, flags)
353 util.unlink(repo.wjoin(f))
353 util.unlinkpath(repo.wjoin(f))
354 if f2:
354 if f2:
355 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
355 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
356 t = mctx.filectx(f2).data()
356 t = mctx.filectx(f2).data()
357 repo.wwrite(fd, t, flags)
357 repo.wwrite(fd, t, flags)
358 updated += 1
358 updated += 1
359 elif m == "dr": # divergent renames
359 elif m == "dr": # divergent renames
360 fl = a[2]
360 fl = a[2]
361 repo.ui.warn(_("note: possible conflict - %s was renamed "
361 repo.ui.warn(_("note: possible conflict - %s was renamed "
362 "multiple times to:\n") % f)
362 "multiple times to:\n") % f)
363 for nf in fl:
363 for nf in fl:
364 repo.ui.warn(" %s\n" % nf)
364 repo.ui.warn(" %s\n" % nf)
365 elif m == "e": # exec
365 elif m == "e": # exec
366 flags = a[2]
366 flags = a[2]
367 util.set_flags(repo.wjoin(f), 'l' in flags, 'x' in flags)
367 util.set_flags(repo.wjoin(f), 'l' in flags, 'x' in flags)
368 ms.commit()
368 ms.commit()
369 u.progress(_('updating'), None, total=numupdates, unit=_('files'))
369 u.progress(_('updating'), None, total=numupdates, unit=_('files'))
370
370
371 return updated, merged, removed, unresolved
371 return updated, merged, removed, unresolved
372
372
373 def recordupdates(repo, action, branchmerge):
373 def recordupdates(repo, action, branchmerge):
374 "record merge actions to the dirstate"
374 "record merge actions to the dirstate"
375
375
376 for a in action:
376 for a in action:
377 f, m = a[:2]
377 f, m = a[:2]
378 if m == "r": # remove
378 if m == "r": # remove
379 if branchmerge:
379 if branchmerge:
380 repo.dirstate.remove(f)
380 repo.dirstate.remove(f)
381 else:
381 else:
382 repo.dirstate.forget(f)
382 repo.dirstate.forget(f)
383 elif m == "a": # re-add
383 elif m == "a": # re-add
384 if not branchmerge:
384 if not branchmerge:
385 repo.dirstate.add(f)
385 repo.dirstate.add(f)
386 elif m == "f": # forget
386 elif m == "f": # forget
387 repo.dirstate.forget(f)
387 repo.dirstate.forget(f)
388 elif m == "e": # exec change
388 elif m == "e": # exec change
389 repo.dirstate.normallookup(f)
389 repo.dirstate.normallookup(f)
390 elif m == "g": # get
390 elif m == "g": # get
391 if branchmerge:
391 if branchmerge:
392 repo.dirstate.otherparent(f)
392 repo.dirstate.otherparent(f)
393 else:
393 else:
394 repo.dirstate.normal(f)
394 repo.dirstate.normal(f)
395 elif m == "m": # merge
395 elif m == "m": # merge
396 f2, fd, flag, move = a[2:]
396 f2, fd, flag, move = a[2:]
397 if branchmerge:
397 if branchmerge:
398 # We've done a branch merge, mark this file as merged
398 # We've done a branch merge, mark this file as merged
399 # so that we properly record the merger later
399 # so that we properly record the merger later
400 repo.dirstate.merge(fd)
400 repo.dirstate.merge(fd)
401 if f != f2: # copy/rename
401 if f != f2: # copy/rename
402 if move:
402 if move:
403 repo.dirstate.remove(f)
403 repo.dirstate.remove(f)
404 if f != fd:
404 if f != fd:
405 repo.dirstate.copy(f, fd)
405 repo.dirstate.copy(f, fd)
406 else:
406 else:
407 repo.dirstate.copy(f2, fd)
407 repo.dirstate.copy(f2, fd)
408 else:
408 else:
409 # We've update-merged a locally modified file, so
409 # We've update-merged a locally modified file, so
410 # we set the dirstate to emulate a normal checkout
410 # we set the dirstate to emulate a normal checkout
411 # of that file some time in the past. Thus our
411 # of that file some time in the past. Thus our
412 # merge will appear as a normal local file
412 # merge will appear as a normal local file
413 # modification.
413 # modification.
414 if f2 == fd: # file not locally copied/moved
414 if f2 == fd: # file not locally copied/moved
415 repo.dirstate.normallookup(fd)
415 repo.dirstate.normallookup(fd)
416 if move:
416 if move:
417 repo.dirstate.forget(f)
417 repo.dirstate.forget(f)
418 elif m == "d": # directory rename
418 elif m == "d": # directory rename
419 f2, fd, flag = a[2:]
419 f2, fd, flag = a[2:]
420 if not f2 and f not in repo.dirstate:
420 if not f2 and f not in repo.dirstate:
421 # untracked file moved
421 # untracked file moved
422 continue
422 continue
423 if branchmerge:
423 if branchmerge:
424 repo.dirstate.add(fd)
424 repo.dirstate.add(fd)
425 if f:
425 if f:
426 repo.dirstate.remove(f)
426 repo.dirstate.remove(f)
427 repo.dirstate.copy(f, fd)
427 repo.dirstate.copy(f, fd)
428 if f2:
428 if f2:
429 repo.dirstate.copy(f2, fd)
429 repo.dirstate.copy(f2, fd)
430 else:
430 else:
431 repo.dirstate.normal(fd)
431 repo.dirstate.normal(fd)
432 if f:
432 if f:
433 repo.dirstate.forget(f)
433 repo.dirstate.forget(f)
434
434
435 def update(repo, node, branchmerge, force, partial):
435 def update(repo, node, branchmerge, force, partial):
436 """
436 """
437 Perform a merge between the working directory and the given node
437 Perform a merge between the working directory and the given node
438
438
439 node = the node to update to, or None if unspecified
439 node = the node to update to, or None if unspecified
440 branchmerge = whether to merge between branches
440 branchmerge = whether to merge between branches
441 force = whether to force branch merging or file overwriting
441 force = whether to force branch merging or file overwriting
442 partial = a function to filter file lists (dirstate not updated)
442 partial = a function to filter file lists (dirstate not updated)
443
443
444 The table below shows all the behaviors of the update command
444 The table below shows all the behaviors of the update command
445 given the -c and -C or no options, whether the working directory
445 given the -c and -C or no options, whether the working directory
446 is dirty, whether a revision is specified, and the relationship of
446 is dirty, whether a revision is specified, and the relationship of
447 the parent rev to the target rev (linear, on the same named
447 the parent rev to the target rev (linear, on the same named
448 branch, or on another named branch).
448 branch, or on another named branch).
449
449
450 This logic is tested by test-update-branches.t.
450 This logic is tested by test-update-branches.t.
451
451
452 -c -C dirty rev | linear same cross
452 -c -C dirty rev | linear same cross
453 n n n n | ok (1) x
453 n n n n | ok (1) x
454 n n n y | ok ok ok
454 n n n y | ok ok ok
455 n n y * | merge (2) (2)
455 n n y * | merge (2) (2)
456 n y * * | --- discard ---
456 n y * * | --- discard ---
457 y n y * | --- (3) ---
457 y n y * | --- (3) ---
458 y n n * | --- ok ---
458 y n n * | --- ok ---
459 y y * * | --- (4) ---
459 y y * * | --- (4) ---
460
460
461 x = can't happen
461 x = can't happen
462 * = don't-care
462 * = don't-care
463 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
463 1 = abort: crosses branches (use 'hg merge' or 'hg update -c')
464 2 = abort: crosses branches (use 'hg merge' to merge or
464 2 = abort: crosses branches (use 'hg merge' to merge or
465 use 'hg update -C' to discard changes)
465 use 'hg update -C' to discard changes)
466 3 = abort: uncommitted local changes
466 3 = abort: uncommitted local changes
467 4 = incompatible options (checked in commands.py)
467 4 = incompatible options (checked in commands.py)
468
468
469 Return the same tuple as applyupdates().
469 Return the same tuple as applyupdates().
470 """
470 """
471
471
472 onode = node
472 onode = node
473 wlock = repo.wlock()
473 wlock = repo.wlock()
474 try:
474 try:
475 wc = repo[None]
475 wc = repo[None]
476 if node is None:
476 if node is None:
477 # tip of current branch
477 # tip of current branch
478 try:
478 try:
479 node = repo.branchtags()[wc.branch()]
479 node = repo.branchtags()[wc.branch()]
480 except KeyError:
480 except KeyError:
481 if wc.branch() == "default": # no default branch!
481 if wc.branch() == "default": # no default branch!
482 node = repo.lookup("tip") # update to tip
482 node = repo.lookup("tip") # update to tip
483 else:
483 else:
484 raise util.Abort(_("branch %s not found") % wc.branch())
484 raise util.Abort(_("branch %s not found") % wc.branch())
485 overwrite = force and not branchmerge
485 overwrite = force and not branchmerge
486 pl = wc.parents()
486 pl = wc.parents()
487 p1, p2 = pl[0], repo[node]
487 p1, p2 = pl[0], repo[node]
488 pa = p1.ancestor(p2)
488 pa = p1.ancestor(p2)
489 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
489 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
490 fastforward = False
490 fastforward = False
491
491
492 ### check phase
492 ### check phase
493 if not overwrite and len(pl) > 1:
493 if not overwrite and len(pl) > 1:
494 raise util.Abort(_("outstanding uncommitted merges"))
494 raise util.Abort(_("outstanding uncommitted merges"))
495 if branchmerge:
495 if branchmerge:
496 if pa == p2:
496 if pa == p2:
497 raise util.Abort(_("merging with a working directory ancestor"
497 raise util.Abort(_("merging with a working directory ancestor"
498 " has no effect"))
498 " has no effect"))
499 elif pa == p1:
499 elif pa == p1:
500 if p1.branch() != p2.branch():
500 if p1.branch() != p2.branch():
501 fastforward = True
501 fastforward = True
502 else:
502 else:
503 raise util.Abort(_("nothing to merge (use 'hg update'"
503 raise util.Abort(_("nothing to merge (use 'hg update'"
504 " or check 'hg heads')"))
504 " or check 'hg heads')"))
505 if not force and (wc.files() or wc.deleted()):
505 if not force and (wc.files() or wc.deleted()):
506 raise util.Abort(_("outstanding uncommitted changes "
506 raise util.Abort(_("outstanding uncommitted changes "
507 "(use 'hg status' to list changes)"))
507 "(use 'hg status' to list changes)"))
508 elif not overwrite:
508 elif not overwrite:
509 if pa == p1 or pa == p2: # linear
509 if pa == p1 or pa == p2: # linear
510 pass # all good
510 pass # all good
511 elif wc.files() or wc.deleted():
511 elif wc.files() or wc.deleted():
512 raise util.Abort(_("crosses branches (merge branches or use"
512 raise util.Abort(_("crosses branches (merge branches or use"
513 " --clean to discard changes)"))
513 " --clean to discard changes)"))
514 elif onode is None:
514 elif onode is None:
515 raise util.Abort(_("crosses branches (merge branches or use"
515 raise util.Abort(_("crosses branches (merge branches or use"
516 " --check to force update)"))
516 " --check to force update)"))
517 else:
517 else:
518 # Allow jumping branches if clean and specific rev given
518 # Allow jumping branches if clean and specific rev given
519 overwrite = True
519 overwrite = True
520
520
521 ### calculate phase
521 ### calculate phase
522 action = []
522 action = []
523 wc.status(unknown=True) # prime cache
523 wc.status(unknown=True) # prime cache
524 if not force:
524 if not force:
525 _checkunknown(wc, p2)
525 _checkunknown(wc, p2)
526 if not util.checkcase(repo.path):
526 if not util.checkcase(repo.path):
527 _checkcollision(p2)
527 _checkcollision(p2)
528 action += _forgetremoved(wc, p2, branchmerge)
528 action += _forgetremoved(wc, p2, branchmerge)
529 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
529 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
530
530
531 ### apply phase
531 ### apply phase
532 if not branchmerge or fastforward: # just jump to the new rev
532 if not branchmerge or fastforward: # just jump to the new rev
533 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
533 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
534 if not partial:
534 if not partial:
535 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
535 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
536
536
537 stats = applyupdates(repo, action, wc, p2, pa)
537 stats = applyupdates(repo, action, wc, p2, pa)
538
538
539 if not partial:
539 if not partial:
540 repo.dirstate.setparents(fp1, fp2)
540 repo.dirstate.setparents(fp1, fp2)
541 recordupdates(repo, action, branchmerge and not fastforward)
541 recordupdates(repo, action, branchmerge and not fastforward)
542 if not branchmerge and not fastforward:
542 if not branchmerge and not fastforward:
543 repo.dirstate.setbranch(p2.branch())
543 repo.dirstate.setbranch(p2.branch())
544 finally:
544 finally:
545 wlock.release()
545 wlock.release()
546
546
547 if not partial:
547 if not partial:
548 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
548 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
549 return stats
549 return stats
@@ -1,1551 +1,1551 b''
1 # util.py - Mercurial utility functions and platform specfic implementations
1 # util.py - Mercurial utility functions and platform specfic implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """Mercurial utility functions and platform specfic implementations.
10 """Mercurial utility functions and platform specfic implementations.
11
11
12 This contains helper routines that are independent of the SCM core and
12 This contains helper routines that are independent of the SCM core and
13 hide platform-specific details from the core.
13 hide platform-specific details from the core.
14 """
14 """
15
15
16 from i18n import _
16 from i18n import _
17 import error, osutil, encoding
17 import error, osutil, encoding
18 import errno, re, shutil, sys, tempfile, traceback
18 import errno, re, shutil, sys, tempfile, traceback
19 import os, stat, time, calendar, textwrap, unicodedata, signal
19 import os, stat, time, calendar, textwrap, unicodedata, signal
20 import imp, socket
20 import imp, socket
21
21
22 # Python compatibility
22 # Python compatibility
23
23
24 def sha1(s):
24 def sha1(s):
25 return _fastsha1(s)
25 return _fastsha1(s)
26
26
27 def _fastsha1(s):
27 def _fastsha1(s):
28 # This function will import sha1 from hashlib or sha (whichever is
28 # This function will import sha1 from hashlib or sha (whichever is
29 # available) and overwrite itself with it on the first call.
29 # available) and overwrite itself with it on the first call.
30 # Subsequent calls will go directly to the imported function.
30 # Subsequent calls will go directly to the imported function.
31 if sys.version_info >= (2, 5):
31 if sys.version_info >= (2, 5):
32 from hashlib import sha1 as _sha1
32 from hashlib import sha1 as _sha1
33 else:
33 else:
34 from sha import sha as _sha1
34 from sha import sha as _sha1
35 global _fastsha1, sha1
35 global _fastsha1, sha1
36 _fastsha1 = sha1 = _sha1
36 _fastsha1 = sha1 = _sha1
37 return _sha1(s)
37 return _sha1(s)
38
38
39 import __builtin__
39 import __builtin__
40
40
41 if sys.version_info[0] < 3:
41 if sys.version_info[0] < 3:
42 def fakebuffer(sliceable, offset=0):
42 def fakebuffer(sliceable, offset=0):
43 return sliceable[offset:]
43 return sliceable[offset:]
44 else:
44 else:
45 def fakebuffer(sliceable, offset=0):
45 def fakebuffer(sliceable, offset=0):
46 return memoryview(sliceable)[offset:]
46 return memoryview(sliceable)[offset:]
47 try:
47 try:
48 buffer
48 buffer
49 except NameError:
49 except NameError:
50 __builtin__.buffer = fakebuffer
50 __builtin__.buffer = fakebuffer
51
51
52 import subprocess
52 import subprocess
53 closefds = os.name == 'posix'
53 closefds = os.name == 'posix'
54
54
55 def popen2(cmd, env=None, newlines=False):
55 def popen2(cmd, env=None, newlines=False):
56 # Setting bufsize to -1 lets the system decide the buffer size.
56 # Setting bufsize to -1 lets the system decide the buffer size.
57 # The default for bufsize is 0, meaning unbuffered. This leads to
57 # The default for bufsize is 0, meaning unbuffered. This leads to
58 # poor performance on Mac OS X: http://bugs.python.org/issue4194
58 # poor performance on Mac OS X: http://bugs.python.org/issue4194
59 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
59 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
60 close_fds=closefds,
60 close_fds=closefds,
61 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
61 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
62 universal_newlines=newlines,
62 universal_newlines=newlines,
63 env=env)
63 env=env)
64 return p.stdin, p.stdout
64 return p.stdin, p.stdout
65
65
66 def popen3(cmd, env=None, newlines=False):
66 def popen3(cmd, env=None, newlines=False):
67 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
67 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
68 close_fds=closefds,
68 close_fds=closefds,
69 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
69 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
70 stderr=subprocess.PIPE,
70 stderr=subprocess.PIPE,
71 universal_newlines=newlines,
71 universal_newlines=newlines,
72 env=env)
72 env=env)
73 return p.stdin, p.stdout, p.stderr
73 return p.stdin, p.stdout, p.stderr
74
74
75 def version():
75 def version():
76 """Return version information if available."""
76 """Return version information if available."""
77 try:
77 try:
78 import __version__
78 import __version__
79 return __version__.version
79 return __version__.version
80 except ImportError:
80 except ImportError:
81 return 'unknown'
81 return 'unknown'
82
82
83 # used by parsedate
83 # used by parsedate
84 defaultdateformats = (
84 defaultdateformats = (
85 '%Y-%m-%d %H:%M:%S',
85 '%Y-%m-%d %H:%M:%S',
86 '%Y-%m-%d %I:%M:%S%p',
86 '%Y-%m-%d %I:%M:%S%p',
87 '%Y-%m-%d %H:%M',
87 '%Y-%m-%d %H:%M',
88 '%Y-%m-%d %I:%M%p',
88 '%Y-%m-%d %I:%M%p',
89 '%Y-%m-%d',
89 '%Y-%m-%d',
90 '%m-%d',
90 '%m-%d',
91 '%m/%d',
91 '%m/%d',
92 '%m/%d/%y',
92 '%m/%d/%y',
93 '%m/%d/%Y',
93 '%m/%d/%Y',
94 '%a %b %d %H:%M:%S %Y',
94 '%a %b %d %H:%M:%S %Y',
95 '%a %b %d %I:%M:%S%p %Y',
95 '%a %b %d %I:%M:%S%p %Y',
96 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
96 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
97 '%b %d %H:%M:%S %Y',
97 '%b %d %H:%M:%S %Y',
98 '%b %d %I:%M:%S%p %Y',
98 '%b %d %I:%M:%S%p %Y',
99 '%b %d %H:%M:%S',
99 '%b %d %H:%M:%S',
100 '%b %d %I:%M:%S%p',
100 '%b %d %I:%M:%S%p',
101 '%b %d %H:%M',
101 '%b %d %H:%M',
102 '%b %d %I:%M%p',
102 '%b %d %I:%M%p',
103 '%b %d %Y',
103 '%b %d %Y',
104 '%b %d',
104 '%b %d',
105 '%H:%M:%S',
105 '%H:%M:%S',
106 '%I:%M:%S%p',
106 '%I:%M:%S%p',
107 '%H:%M',
107 '%H:%M',
108 '%I:%M%p',
108 '%I:%M%p',
109 )
109 )
110
110
111 extendeddateformats = defaultdateformats + (
111 extendeddateformats = defaultdateformats + (
112 "%Y",
112 "%Y",
113 "%Y-%m",
113 "%Y-%m",
114 "%b",
114 "%b",
115 "%b %Y",
115 "%b %Y",
116 )
116 )
117
117
118 def cachefunc(func):
118 def cachefunc(func):
119 '''cache the result of function calls'''
119 '''cache the result of function calls'''
120 # XXX doesn't handle keywords args
120 # XXX doesn't handle keywords args
121 cache = {}
121 cache = {}
122 if func.func_code.co_argcount == 1:
122 if func.func_code.co_argcount == 1:
123 # we gain a small amount of time because
123 # we gain a small amount of time because
124 # we don't need to pack/unpack the list
124 # we don't need to pack/unpack the list
125 def f(arg):
125 def f(arg):
126 if arg not in cache:
126 if arg not in cache:
127 cache[arg] = func(arg)
127 cache[arg] = func(arg)
128 return cache[arg]
128 return cache[arg]
129 else:
129 else:
130 def f(*args):
130 def f(*args):
131 if args not in cache:
131 if args not in cache:
132 cache[args] = func(*args)
132 cache[args] = func(*args)
133 return cache[args]
133 return cache[args]
134
134
135 return f
135 return f
136
136
137 def lrucachefunc(func):
137 def lrucachefunc(func):
138 '''cache most recent results of function calls'''
138 '''cache most recent results of function calls'''
139 cache = {}
139 cache = {}
140 order = []
140 order = []
141 if func.func_code.co_argcount == 1:
141 if func.func_code.co_argcount == 1:
142 def f(arg):
142 def f(arg):
143 if arg not in cache:
143 if arg not in cache:
144 if len(cache) > 20:
144 if len(cache) > 20:
145 del cache[order.pop(0)]
145 del cache[order.pop(0)]
146 cache[arg] = func(arg)
146 cache[arg] = func(arg)
147 else:
147 else:
148 order.remove(arg)
148 order.remove(arg)
149 order.append(arg)
149 order.append(arg)
150 return cache[arg]
150 return cache[arg]
151 else:
151 else:
152 def f(*args):
152 def f(*args):
153 if args not in cache:
153 if args not in cache:
154 if len(cache) > 20:
154 if len(cache) > 20:
155 del cache[order.pop(0)]
155 del cache[order.pop(0)]
156 cache[args] = func(*args)
156 cache[args] = func(*args)
157 else:
157 else:
158 order.remove(args)
158 order.remove(args)
159 order.append(args)
159 order.append(args)
160 return cache[args]
160 return cache[args]
161
161
162 return f
162 return f
163
163
164 class propertycache(object):
164 class propertycache(object):
165 def __init__(self, func):
165 def __init__(self, func):
166 self.func = func
166 self.func = func
167 self.name = func.__name__
167 self.name = func.__name__
168 def __get__(self, obj, type=None):
168 def __get__(self, obj, type=None):
169 result = self.func(obj)
169 result = self.func(obj)
170 setattr(obj, self.name, result)
170 setattr(obj, self.name, result)
171 return result
171 return result
172
172
173 def pipefilter(s, cmd):
173 def pipefilter(s, cmd):
174 '''filter string S through command CMD, returning its output'''
174 '''filter string S through command CMD, returning its output'''
175 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
175 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
176 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
176 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
177 pout, perr = p.communicate(s)
177 pout, perr = p.communicate(s)
178 return pout
178 return pout
179
179
180 def tempfilter(s, cmd):
180 def tempfilter(s, cmd):
181 '''filter string S through a pair of temporary files with CMD.
181 '''filter string S through a pair of temporary files with CMD.
182 CMD is used as a template to create the real command to be run,
182 CMD is used as a template to create the real command to be run,
183 with the strings INFILE and OUTFILE replaced by the real names of
183 with the strings INFILE and OUTFILE replaced by the real names of
184 the temporary files generated.'''
184 the temporary files generated.'''
185 inname, outname = None, None
185 inname, outname = None, None
186 try:
186 try:
187 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
187 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
188 fp = os.fdopen(infd, 'wb')
188 fp = os.fdopen(infd, 'wb')
189 fp.write(s)
189 fp.write(s)
190 fp.close()
190 fp.close()
191 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
191 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
192 os.close(outfd)
192 os.close(outfd)
193 cmd = cmd.replace('INFILE', inname)
193 cmd = cmd.replace('INFILE', inname)
194 cmd = cmd.replace('OUTFILE', outname)
194 cmd = cmd.replace('OUTFILE', outname)
195 code = os.system(cmd)
195 code = os.system(cmd)
196 if sys.platform == 'OpenVMS' and code & 1:
196 if sys.platform == 'OpenVMS' and code & 1:
197 code = 0
197 code = 0
198 if code:
198 if code:
199 raise Abort(_("command '%s' failed: %s") %
199 raise Abort(_("command '%s' failed: %s") %
200 (cmd, explain_exit(code)))
200 (cmd, explain_exit(code)))
201 return open(outname, 'rb').read()
201 return open(outname, 'rb').read()
202 finally:
202 finally:
203 try:
203 try:
204 if inname:
204 if inname:
205 os.unlink(inname)
205 os.unlink(inname)
206 except:
206 except:
207 pass
207 pass
208 try:
208 try:
209 if outname:
209 if outname:
210 os.unlink(outname)
210 os.unlink(outname)
211 except:
211 except:
212 pass
212 pass
213
213
214 filtertable = {
214 filtertable = {
215 'tempfile:': tempfilter,
215 'tempfile:': tempfilter,
216 'pipe:': pipefilter,
216 'pipe:': pipefilter,
217 }
217 }
218
218
219 def filter(s, cmd):
219 def filter(s, cmd):
220 "filter a string through a command that transforms its input to its output"
220 "filter a string through a command that transforms its input to its output"
221 for name, fn in filtertable.iteritems():
221 for name, fn in filtertable.iteritems():
222 if cmd.startswith(name):
222 if cmd.startswith(name):
223 return fn(s, cmd[len(name):].lstrip())
223 return fn(s, cmd[len(name):].lstrip())
224 return pipefilter(s, cmd)
224 return pipefilter(s, cmd)
225
225
226 def binary(s):
226 def binary(s):
227 """return true if a string is binary data"""
227 """return true if a string is binary data"""
228 return bool(s and '\0' in s)
228 return bool(s and '\0' in s)
229
229
230 def increasingchunks(source, min=1024, max=65536):
230 def increasingchunks(source, min=1024, max=65536):
231 '''return no less than min bytes per chunk while data remains,
231 '''return no less than min bytes per chunk while data remains,
232 doubling min after each chunk until it reaches max'''
232 doubling min after each chunk until it reaches max'''
233 def log2(x):
233 def log2(x):
234 if not x:
234 if not x:
235 return 0
235 return 0
236 i = 0
236 i = 0
237 while x:
237 while x:
238 x >>= 1
238 x >>= 1
239 i += 1
239 i += 1
240 return i - 1
240 return i - 1
241
241
242 buf = []
242 buf = []
243 blen = 0
243 blen = 0
244 for chunk in source:
244 for chunk in source:
245 buf.append(chunk)
245 buf.append(chunk)
246 blen += len(chunk)
246 blen += len(chunk)
247 if blen >= min:
247 if blen >= min:
248 if min < max:
248 if min < max:
249 min = min << 1
249 min = min << 1
250 nmin = 1 << log2(blen)
250 nmin = 1 << log2(blen)
251 if nmin > min:
251 if nmin > min:
252 min = nmin
252 min = nmin
253 if min > max:
253 if min > max:
254 min = max
254 min = max
255 yield ''.join(buf)
255 yield ''.join(buf)
256 blen = 0
256 blen = 0
257 buf = []
257 buf = []
258 if buf:
258 if buf:
259 yield ''.join(buf)
259 yield ''.join(buf)
260
260
261 Abort = error.Abort
261 Abort = error.Abort
262
262
263 def always(fn):
263 def always(fn):
264 return True
264 return True
265
265
266 def never(fn):
266 def never(fn):
267 return False
267 return False
268
268
269 def pathto(root, n1, n2):
269 def pathto(root, n1, n2):
270 '''return the relative path from one place to another.
270 '''return the relative path from one place to another.
271 root should use os.sep to separate directories
271 root should use os.sep to separate directories
272 n1 should use os.sep to separate directories
272 n1 should use os.sep to separate directories
273 n2 should use "/" to separate directories
273 n2 should use "/" to separate directories
274 returns an os.sep-separated path.
274 returns an os.sep-separated path.
275
275
276 If n1 is a relative path, it's assumed it's
276 If n1 is a relative path, it's assumed it's
277 relative to root.
277 relative to root.
278 n2 should always be relative to root.
278 n2 should always be relative to root.
279 '''
279 '''
280 if not n1:
280 if not n1:
281 return localpath(n2)
281 return localpath(n2)
282 if os.path.isabs(n1):
282 if os.path.isabs(n1):
283 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
283 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
284 return os.path.join(root, localpath(n2))
284 return os.path.join(root, localpath(n2))
285 n2 = '/'.join((pconvert(root), n2))
285 n2 = '/'.join((pconvert(root), n2))
286 a, b = splitpath(n1), n2.split('/')
286 a, b = splitpath(n1), n2.split('/')
287 a.reverse()
287 a.reverse()
288 b.reverse()
288 b.reverse()
289 while a and b and a[-1] == b[-1]:
289 while a and b and a[-1] == b[-1]:
290 a.pop()
290 a.pop()
291 b.pop()
291 b.pop()
292 b.reverse()
292 b.reverse()
293 return os.sep.join((['..'] * len(a)) + b) or '.'
293 return os.sep.join((['..'] * len(a)) + b) or '.'
294
294
295 def canonpath(root, cwd, myname, auditor=None):
295 def canonpath(root, cwd, myname, auditor=None):
296 """return the canonical path of myname, given cwd and root"""
296 """return the canonical path of myname, given cwd and root"""
297 if endswithsep(root):
297 if endswithsep(root):
298 rootsep = root
298 rootsep = root
299 else:
299 else:
300 rootsep = root + os.sep
300 rootsep = root + os.sep
301 name = myname
301 name = myname
302 if not os.path.isabs(name):
302 if not os.path.isabs(name):
303 name = os.path.join(root, cwd, name)
303 name = os.path.join(root, cwd, name)
304 name = os.path.normpath(name)
304 name = os.path.normpath(name)
305 if auditor is None:
305 if auditor is None:
306 auditor = path_auditor(root)
306 auditor = path_auditor(root)
307 if name != rootsep and name.startswith(rootsep):
307 if name != rootsep and name.startswith(rootsep):
308 name = name[len(rootsep):]
308 name = name[len(rootsep):]
309 auditor(name)
309 auditor(name)
310 return pconvert(name)
310 return pconvert(name)
311 elif name == root:
311 elif name == root:
312 return ''
312 return ''
313 else:
313 else:
314 # Determine whether `name' is in the hierarchy at or beneath `root',
314 # Determine whether `name' is in the hierarchy at or beneath `root',
315 # by iterating name=dirname(name) until that causes no change (can't
315 # by iterating name=dirname(name) until that causes no change (can't
316 # check name == '/', because that doesn't work on windows). For each
316 # check name == '/', because that doesn't work on windows). For each
317 # `name', compare dev/inode numbers. If they match, the list `rel'
317 # `name', compare dev/inode numbers. If they match, the list `rel'
318 # holds the reversed list of components making up the relative file
318 # holds the reversed list of components making up the relative file
319 # name we want.
319 # name we want.
320 root_st = os.stat(root)
320 root_st = os.stat(root)
321 rel = []
321 rel = []
322 while True:
322 while True:
323 try:
323 try:
324 name_st = os.stat(name)
324 name_st = os.stat(name)
325 except OSError:
325 except OSError:
326 break
326 break
327 if samestat(name_st, root_st):
327 if samestat(name_st, root_st):
328 if not rel:
328 if not rel:
329 # name was actually the same as root (maybe a symlink)
329 # name was actually the same as root (maybe a symlink)
330 return ''
330 return ''
331 rel.reverse()
331 rel.reverse()
332 name = os.path.join(*rel)
332 name = os.path.join(*rel)
333 auditor(name)
333 auditor(name)
334 return pconvert(name)
334 return pconvert(name)
335 dirname, basename = os.path.split(name)
335 dirname, basename = os.path.split(name)
336 rel.append(basename)
336 rel.append(basename)
337 if dirname == name:
337 if dirname == name:
338 break
338 break
339 name = dirname
339 name = dirname
340
340
341 raise Abort('%s not under root' % myname)
341 raise Abort('%s not under root' % myname)
342
342
343 _hgexecutable = None
343 _hgexecutable = None
344
344
345 def main_is_frozen():
345 def main_is_frozen():
346 """return True if we are a frozen executable.
346 """return True if we are a frozen executable.
347
347
348 The code supports py2exe (most common, Windows only) and tools/freeze
348 The code supports py2exe (most common, Windows only) and tools/freeze
349 (portable, not much used).
349 (portable, not much used).
350 """
350 """
351 return (hasattr(sys, "frozen") or # new py2exe
351 return (hasattr(sys, "frozen") or # new py2exe
352 hasattr(sys, "importers") or # old py2exe
352 hasattr(sys, "importers") or # old py2exe
353 imp.is_frozen("__main__")) # tools/freeze
353 imp.is_frozen("__main__")) # tools/freeze
354
354
355 def hgexecutable():
355 def hgexecutable():
356 """return location of the 'hg' executable.
356 """return location of the 'hg' executable.
357
357
358 Defaults to $HG or 'hg' in the search path.
358 Defaults to $HG or 'hg' in the search path.
359 """
359 """
360 if _hgexecutable is None:
360 if _hgexecutable is None:
361 hg = os.environ.get('HG')
361 hg = os.environ.get('HG')
362 if hg:
362 if hg:
363 set_hgexecutable(hg)
363 set_hgexecutable(hg)
364 elif main_is_frozen():
364 elif main_is_frozen():
365 set_hgexecutable(sys.executable)
365 set_hgexecutable(sys.executable)
366 else:
366 else:
367 exe = find_exe('hg') or os.path.basename(sys.argv[0])
367 exe = find_exe('hg') or os.path.basename(sys.argv[0])
368 set_hgexecutable(exe)
368 set_hgexecutable(exe)
369 return _hgexecutable
369 return _hgexecutable
370
370
371 def set_hgexecutable(path):
371 def set_hgexecutable(path):
372 """set location of the 'hg' executable"""
372 """set location of the 'hg' executable"""
373 global _hgexecutable
373 global _hgexecutable
374 _hgexecutable = path
374 _hgexecutable = path
375
375
376 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
376 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
377 '''enhanced shell command execution.
377 '''enhanced shell command execution.
378 run with environment maybe modified, maybe in different dir.
378 run with environment maybe modified, maybe in different dir.
379
379
380 if command fails and onerr is None, return status. if ui object,
380 if command fails and onerr is None, return status. if ui object,
381 print error message and return status, else raise onerr object as
381 print error message and return status, else raise onerr object as
382 exception.
382 exception.
383
383
384 if out is specified, it is assumed to be a file-like object that has a
384 if out is specified, it is assumed to be a file-like object that has a
385 write() method. stdout and stderr will be redirected to out.'''
385 write() method. stdout and stderr will be redirected to out.'''
386 def py2shell(val):
386 def py2shell(val):
387 'convert python object into string that is useful to shell'
387 'convert python object into string that is useful to shell'
388 if val is None or val is False:
388 if val is None or val is False:
389 return '0'
389 return '0'
390 if val is True:
390 if val is True:
391 return '1'
391 return '1'
392 return str(val)
392 return str(val)
393 origcmd = cmd
393 origcmd = cmd
394 cmd = quotecommand(cmd)
394 cmd = quotecommand(cmd)
395 env = dict(os.environ)
395 env = dict(os.environ)
396 env.update((k, py2shell(v)) for k, v in environ.iteritems())
396 env.update((k, py2shell(v)) for k, v in environ.iteritems())
397 env['HG'] = hgexecutable()
397 env['HG'] = hgexecutable()
398 if out is None:
398 if out is None:
399 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
399 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
400 env=env, cwd=cwd)
400 env=env, cwd=cwd)
401 else:
401 else:
402 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
402 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
403 env=env, cwd=cwd, stdout=subprocess.PIPE,
403 env=env, cwd=cwd, stdout=subprocess.PIPE,
404 stderr=subprocess.STDOUT)
404 stderr=subprocess.STDOUT)
405 for line in proc.stdout:
405 for line in proc.stdout:
406 out.write(line)
406 out.write(line)
407 proc.wait()
407 proc.wait()
408 rc = proc.returncode
408 rc = proc.returncode
409 if sys.platform == 'OpenVMS' and rc & 1:
409 if sys.platform == 'OpenVMS' and rc & 1:
410 rc = 0
410 rc = 0
411 if rc and onerr:
411 if rc and onerr:
412 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
412 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
413 explain_exit(rc)[0])
413 explain_exit(rc)[0])
414 if errprefix:
414 if errprefix:
415 errmsg = '%s: %s' % (errprefix, errmsg)
415 errmsg = '%s: %s' % (errprefix, errmsg)
416 try:
416 try:
417 onerr.warn(errmsg + '\n')
417 onerr.warn(errmsg + '\n')
418 except AttributeError:
418 except AttributeError:
419 raise onerr(errmsg)
419 raise onerr(errmsg)
420 return rc
420 return rc
421
421
422 def checksignature(func):
422 def checksignature(func):
423 '''wrap a function with code to check for calling errors'''
423 '''wrap a function with code to check for calling errors'''
424 def check(*args, **kwargs):
424 def check(*args, **kwargs):
425 try:
425 try:
426 return func(*args, **kwargs)
426 return func(*args, **kwargs)
427 except TypeError:
427 except TypeError:
428 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
428 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
429 raise error.SignatureError
429 raise error.SignatureError
430 raise
430 raise
431
431
432 return check
432 return check
433
433
434 def unlink(f):
434 def unlinkpath(f):
435 """unlink and remove the directory if it is empty"""
435 """unlink and remove the directory if it is empty"""
436 os.unlink(f)
436 os.unlink(f)
437 # try removing directories that might now be empty
437 # try removing directories that might now be empty
438 try:
438 try:
439 os.removedirs(os.path.dirname(f))
439 os.removedirs(os.path.dirname(f))
440 except OSError:
440 except OSError:
441 pass
441 pass
442
442
443 def copyfile(src, dest):
443 def copyfile(src, dest):
444 "copy a file, preserving mode and atime/mtime"
444 "copy a file, preserving mode and atime/mtime"
445 if os.path.islink(src):
445 if os.path.islink(src):
446 try:
446 try:
447 os.unlink(dest)
447 os.unlink(dest)
448 except:
448 except:
449 pass
449 pass
450 os.symlink(os.readlink(src), dest)
450 os.symlink(os.readlink(src), dest)
451 else:
451 else:
452 try:
452 try:
453 shutil.copyfile(src, dest)
453 shutil.copyfile(src, dest)
454 shutil.copymode(src, dest)
454 shutil.copymode(src, dest)
455 except shutil.Error, inst:
455 except shutil.Error, inst:
456 raise Abort(str(inst))
456 raise Abort(str(inst))
457
457
458 def copyfiles(src, dst, hardlink=None):
458 def copyfiles(src, dst, hardlink=None):
459 """Copy a directory tree using hardlinks if possible"""
459 """Copy a directory tree using hardlinks if possible"""
460
460
461 if hardlink is None:
461 if hardlink is None:
462 hardlink = (os.stat(src).st_dev ==
462 hardlink = (os.stat(src).st_dev ==
463 os.stat(os.path.dirname(dst)).st_dev)
463 os.stat(os.path.dirname(dst)).st_dev)
464
464
465 num = 0
465 num = 0
466 if os.path.isdir(src):
466 if os.path.isdir(src):
467 os.mkdir(dst)
467 os.mkdir(dst)
468 for name, kind in osutil.listdir(src):
468 for name, kind in osutil.listdir(src):
469 srcname = os.path.join(src, name)
469 srcname = os.path.join(src, name)
470 dstname = os.path.join(dst, name)
470 dstname = os.path.join(dst, name)
471 hardlink, n = copyfiles(srcname, dstname, hardlink)
471 hardlink, n = copyfiles(srcname, dstname, hardlink)
472 num += n
472 num += n
473 else:
473 else:
474 if hardlink:
474 if hardlink:
475 try:
475 try:
476 os_link(src, dst)
476 os_link(src, dst)
477 except (IOError, OSError):
477 except (IOError, OSError):
478 hardlink = False
478 hardlink = False
479 shutil.copy(src, dst)
479 shutil.copy(src, dst)
480 else:
480 else:
481 shutil.copy(src, dst)
481 shutil.copy(src, dst)
482 num += 1
482 num += 1
483
483
484 return hardlink, num
484 return hardlink, num
485
485
486 class path_auditor(object):
486 class path_auditor(object):
487 '''ensure that a filesystem path contains no banned components.
487 '''ensure that a filesystem path contains no banned components.
488 the following properties of a path are checked:
488 the following properties of a path are checked:
489
489
490 - ends with a directory separator
490 - ends with a directory separator
491 - under top-level .hg
491 - under top-level .hg
492 - starts at the root of a windows drive
492 - starts at the root of a windows drive
493 - contains ".."
493 - contains ".."
494 - traverses a symlink (e.g. a/symlink_here/b)
494 - traverses a symlink (e.g. a/symlink_here/b)
495 - inside a nested repository (a callback can be used to approve
495 - inside a nested repository (a callback can be used to approve
496 some nested repositories, e.g., subrepositories)
496 some nested repositories, e.g., subrepositories)
497 '''
497 '''
498
498
499 def __init__(self, root, callback=None):
499 def __init__(self, root, callback=None):
500 self.audited = set()
500 self.audited = set()
501 self.auditeddir = set()
501 self.auditeddir = set()
502 self.root = root
502 self.root = root
503 self.callback = callback
503 self.callback = callback
504
504
505 def __call__(self, path):
505 def __call__(self, path):
506 if path in self.audited:
506 if path in self.audited:
507 return
507 return
508 # AIX ignores "/" at end of path, others raise EISDIR.
508 # AIX ignores "/" at end of path, others raise EISDIR.
509 if endswithsep(path):
509 if endswithsep(path):
510 raise Abort(_("path ends in directory separator: %s") % path)
510 raise Abort(_("path ends in directory separator: %s") % path)
511 normpath = os.path.normcase(path)
511 normpath = os.path.normcase(path)
512 parts = splitpath(normpath)
512 parts = splitpath(normpath)
513 if (os.path.splitdrive(path)[0]
513 if (os.path.splitdrive(path)[0]
514 or parts[0].lower() in ('.hg', '.hg.', '')
514 or parts[0].lower() in ('.hg', '.hg.', '')
515 or os.pardir in parts):
515 or os.pardir in parts):
516 raise Abort(_("path contains illegal component: %s") % path)
516 raise Abort(_("path contains illegal component: %s") % path)
517 if '.hg' in path.lower():
517 if '.hg' in path.lower():
518 lparts = [p.lower() for p in parts]
518 lparts = [p.lower() for p in parts]
519 for p in '.hg', '.hg.':
519 for p in '.hg', '.hg.':
520 if p in lparts[1:]:
520 if p in lparts[1:]:
521 pos = lparts.index(p)
521 pos = lparts.index(p)
522 base = os.path.join(*parts[:pos])
522 base = os.path.join(*parts[:pos])
523 raise Abort(_('path %r is inside repo %r') % (path, base))
523 raise Abort(_('path %r is inside repo %r') % (path, base))
524 def check(prefix):
524 def check(prefix):
525 curpath = os.path.join(self.root, prefix)
525 curpath = os.path.join(self.root, prefix)
526 try:
526 try:
527 st = os.lstat(curpath)
527 st = os.lstat(curpath)
528 except OSError, err:
528 except OSError, err:
529 # EINVAL can be raised as invalid path syntax under win32.
529 # EINVAL can be raised as invalid path syntax under win32.
530 # They must be ignored for patterns can be checked too.
530 # They must be ignored for patterns can be checked too.
531 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
531 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
532 raise
532 raise
533 else:
533 else:
534 if stat.S_ISLNK(st.st_mode):
534 if stat.S_ISLNK(st.st_mode):
535 raise Abort(_('path %r traverses symbolic link %r') %
535 raise Abort(_('path %r traverses symbolic link %r') %
536 (path, prefix))
536 (path, prefix))
537 elif (stat.S_ISDIR(st.st_mode) and
537 elif (stat.S_ISDIR(st.st_mode) and
538 os.path.isdir(os.path.join(curpath, '.hg'))):
538 os.path.isdir(os.path.join(curpath, '.hg'))):
539 if not self.callback or not self.callback(curpath):
539 if not self.callback or not self.callback(curpath):
540 raise Abort(_('path %r is inside repo %r') %
540 raise Abort(_('path %r is inside repo %r') %
541 (path, prefix))
541 (path, prefix))
542 parts.pop()
542 parts.pop()
543 prefixes = []
543 prefixes = []
544 while parts:
544 while parts:
545 prefix = os.sep.join(parts)
545 prefix = os.sep.join(parts)
546 if prefix in self.auditeddir:
546 if prefix in self.auditeddir:
547 break
547 break
548 check(prefix)
548 check(prefix)
549 prefixes.append(prefix)
549 prefixes.append(prefix)
550 parts.pop()
550 parts.pop()
551
551
552 self.audited.add(path)
552 self.audited.add(path)
553 # only add prefixes to the cache after checking everything: we don't
553 # only add prefixes to the cache after checking everything: we don't
554 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
554 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
555 self.auditeddir.update(prefixes)
555 self.auditeddir.update(prefixes)
556
556
557 def nlinks(pathname):
557 def nlinks(pathname):
558 """Return number of hardlinks for the given file."""
558 """Return number of hardlinks for the given file."""
559 return os.lstat(pathname).st_nlink
559 return os.lstat(pathname).st_nlink
560
560
561 if hasattr(os, 'link'):
561 if hasattr(os, 'link'):
562 os_link = os.link
562 os_link = os.link
563 else:
563 else:
564 def os_link(src, dst):
564 def os_link(src, dst):
565 raise OSError(0, _("Hardlinks not supported"))
565 raise OSError(0, _("Hardlinks not supported"))
566
566
567 def lookup_reg(key, name=None, scope=None):
567 def lookup_reg(key, name=None, scope=None):
568 return None
568 return None
569
569
570 def hidewindow():
570 def hidewindow():
571 """Hide current shell window.
571 """Hide current shell window.
572
572
573 Used to hide the window opened when starting asynchronous
573 Used to hide the window opened when starting asynchronous
574 child process under Windows, unneeded on other systems.
574 child process under Windows, unneeded on other systems.
575 """
575 """
576 pass
576 pass
577
577
578 if os.name == 'nt':
578 if os.name == 'nt':
579 from windows import *
579 from windows import *
580 else:
580 else:
581 from posix import *
581 from posix import *
582
582
583 def makelock(info, pathname):
583 def makelock(info, pathname):
584 try:
584 try:
585 return os.symlink(info, pathname)
585 return os.symlink(info, pathname)
586 except OSError, why:
586 except OSError, why:
587 if why.errno == errno.EEXIST:
587 if why.errno == errno.EEXIST:
588 raise
588 raise
589 except AttributeError: # no symlink in os
589 except AttributeError: # no symlink in os
590 pass
590 pass
591
591
592 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
592 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
593 os.write(ld, info)
593 os.write(ld, info)
594 os.close(ld)
594 os.close(ld)
595
595
596 def readlock(pathname):
596 def readlock(pathname):
597 try:
597 try:
598 return os.readlink(pathname)
598 return os.readlink(pathname)
599 except OSError, why:
599 except OSError, why:
600 if why.errno not in (errno.EINVAL, errno.ENOSYS):
600 if why.errno not in (errno.EINVAL, errno.ENOSYS):
601 raise
601 raise
602 except AttributeError: # no symlink in os
602 except AttributeError: # no symlink in os
603 pass
603 pass
604 return posixfile(pathname).read()
604 return posixfile(pathname).read()
605
605
606 def fstat(fp):
606 def fstat(fp):
607 '''stat file object that may not have fileno method.'''
607 '''stat file object that may not have fileno method.'''
608 try:
608 try:
609 return os.fstat(fp.fileno())
609 return os.fstat(fp.fileno())
610 except AttributeError:
610 except AttributeError:
611 return os.stat(fp.name)
611 return os.stat(fp.name)
612
612
613 # File system features
613 # File system features
614
614
615 def checkcase(path):
615 def checkcase(path):
616 """
616 """
617 Check whether the given path is on a case-sensitive filesystem
617 Check whether the given path is on a case-sensitive filesystem
618
618
619 Requires a path (like /foo/.hg) ending with a foldable final
619 Requires a path (like /foo/.hg) ending with a foldable final
620 directory component.
620 directory component.
621 """
621 """
622 s1 = os.stat(path)
622 s1 = os.stat(path)
623 d, b = os.path.split(path)
623 d, b = os.path.split(path)
624 p2 = os.path.join(d, b.upper())
624 p2 = os.path.join(d, b.upper())
625 if path == p2:
625 if path == p2:
626 p2 = os.path.join(d, b.lower())
626 p2 = os.path.join(d, b.lower())
627 try:
627 try:
628 s2 = os.stat(p2)
628 s2 = os.stat(p2)
629 if s2 == s1:
629 if s2 == s1:
630 return False
630 return False
631 return True
631 return True
632 except:
632 except:
633 return True
633 return True
634
634
635 _fspathcache = {}
635 _fspathcache = {}
636 def fspath(name, root):
636 def fspath(name, root):
637 '''Get name in the case stored in the filesystem
637 '''Get name in the case stored in the filesystem
638
638
639 The name is either relative to root, or it is an absolute path starting
639 The name is either relative to root, or it is an absolute path starting
640 with root. Note that this function is unnecessary, and should not be
640 with root. Note that this function is unnecessary, and should not be
641 called, for case-sensitive filesystems (simply because it's expensive).
641 called, for case-sensitive filesystems (simply because it's expensive).
642 '''
642 '''
643 # If name is absolute, make it relative
643 # If name is absolute, make it relative
644 if name.lower().startswith(root.lower()):
644 if name.lower().startswith(root.lower()):
645 l = len(root)
645 l = len(root)
646 if name[l] == os.sep or name[l] == os.altsep:
646 if name[l] == os.sep or name[l] == os.altsep:
647 l = l + 1
647 l = l + 1
648 name = name[l:]
648 name = name[l:]
649
649
650 if not os.path.lexists(os.path.join(root, name)):
650 if not os.path.lexists(os.path.join(root, name)):
651 return None
651 return None
652
652
653 seps = os.sep
653 seps = os.sep
654 if os.altsep:
654 if os.altsep:
655 seps = seps + os.altsep
655 seps = seps + os.altsep
656 # Protect backslashes. This gets silly very quickly.
656 # Protect backslashes. This gets silly very quickly.
657 seps.replace('\\','\\\\')
657 seps.replace('\\','\\\\')
658 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
658 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
659 dir = os.path.normcase(os.path.normpath(root))
659 dir = os.path.normcase(os.path.normpath(root))
660 result = []
660 result = []
661 for part, sep in pattern.findall(name):
661 for part, sep in pattern.findall(name):
662 if sep:
662 if sep:
663 result.append(sep)
663 result.append(sep)
664 continue
664 continue
665
665
666 if dir not in _fspathcache:
666 if dir not in _fspathcache:
667 _fspathcache[dir] = os.listdir(dir)
667 _fspathcache[dir] = os.listdir(dir)
668 contents = _fspathcache[dir]
668 contents = _fspathcache[dir]
669
669
670 lpart = part.lower()
670 lpart = part.lower()
671 lenp = len(part)
671 lenp = len(part)
672 for n in contents:
672 for n in contents:
673 if lenp == len(n) and n.lower() == lpart:
673 if lenp == len(n) and n.lower() == lpart:
674 result.append(n)
674 result.append(n)
675 break
675 break
676 else:
676 else:
677 # Cannot happen, as the file exists!
677 # Cannot happen, as the file exists!
678 result.append(part)
678 result.append(part)
679 dir = os.path.join(dir, lpart)
679 dir = os.path.join(dir, lpart)
680
680
681 return ''.join(result)
681 return ''.join(result)
682
682
683 def checkexec(path):
683 def checkexec(path):
684 """
684 """
685 Check whether the given path is on a filesystem with UNIX-like exec flags
685 Check whether the given path is on a filesystem with UNIX-like exec flags
686
686
687 Requires a directory (like /foo/.hg)
687 Requires a directory (like /foo/.hg)
688 """
688 """
689
689
690 # VFAT on some Linux versions can flip mode but it doesn't persist
690 # VFAT on some Linux versions can flip mode but it doesn't persist
691 # a FS remount. Frequently we can detect it if files are created
691 # a FS remount. Frequently we can detect it if files are created
692 # with exec bit on.
692 # with exec bit on.
693
693
694 try:
694 try:
695 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
695 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
696 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
696 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
697 try:
697 try:
698 os.close(fh)
698 os.close(fh)
699 m = os.stat(fn).st_mode & 0777
699 m = os.stat(fn).st_mode & 0777
700 new_file_has_exec = m & EXECFLAGS
700 new_file_has_exec = m & EXECFLAGS
701 os.chmod(fn, m ^ EXECFLAGS)
701 os.chmod(fn, m ^ EXECFLAGS)
702 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
702 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
703 finally:
703 finally:
704 os.unlink(fn)
704 os.unlink(fn)
705 except (IOError, OSError):
705 except (IOError, OSError):
706 # we don't care, the user probably won't be able to commit anyway
706 # we don't care, the user probably won't be able to commit anyway
707 return False
707 return False
708 return not (new_file_has_exec or exec_flags_cannot_flip)
708 return not (new_file_has_exec or exec_flags_cannot_flip)
709
709
710 def checklink(path):
710 def checklink(path):
711 """check whether the given path is on a symlink-capable filesystem"""
711 """check whether the given path is on a symlink-capable filesystem"""
712 # mktemp is not racy because symlink creation will fail if the
712 # mktemp is not racy because symlink creation will fail if the
713 # file already exists
713 # file already exists
714 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
714 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
715 try:
715 try:
716 os.symlink(".", name)
716 os.symlink(".", name)
717 os.unlink(name)
717 os.unlink(name)
718 return True
718 return True
719 except (OSError, AttributeError):
719 except (OSError, AttributeError):
720 return False
720 return False
721
721
722 def checknlink(testfile):
722 def checknlink(testfile):
723 '''check whether hardlink count reporting works properly'''
723 '''check whether hardlink count reporting works properly'''
724
724
725 # testfile may be open, so we need a separate file for checking to
725 # testfile may be open, so we need a separate file for checking to
726 # work around issue2543 (or testfile may get lost on Samba shares)
726 # work around issue2543 (or testfile may get lost on Samba shares)
727 f1 = testfile + ".hgtmp1"
727 f1 = testfile + ".hgtmp1"
728 if os.path.lexists(f1):
728 if os.path.lexists(f1):
729 return False
729 return False
730 try:
730 try:
731 posixfile(f1, 'w').close()
731 posixfile(f1, 'w').close()
732 except IOError:
732 except IOError:
733 return False
733 return False
734
734
735 f2 = testfile + ".hgtmp2"
735 f2 = testfile + ".hgtmp2"
736 fd = None
736 fd = None
737 try:
737 try:
738 try:
738 try:
739 os_link(f1, f2)
739 os_link(f1, f2)
740 except OSError:
740 except OSError:
741 return False
741 return False
742
742
743 # nlinks() may behave differently for files on Windows shares if
743 # nlinks() may behave differently for files on Windows shares if
744 # the file is open.
744 # the file is open.
745 fd = open(f2)
745 fd = open(f2)
746 return nlinks(f2) > 1
746 return nlinks(f2) > 1
747 finally:
747 finally:
748 if fd is not None:
748 if fd is not None:
749 fd.close()
749 fd.close()
750 for f in (f1, f2):
750 for f in (f1, f2):
751 try:
751 try:
752 os.unlink(f)
752 os.unlink(f)
753 except OSError:
753 except OSError:
754 pass
754 pass
755
755
756 return False
756 return False
757
757
758 def endswithsep(path):
758 def endswithsep(path):
759 '''Check path ends with os.sep or os.altsep.'''
759 '''Check path ends with os.sep or os.altsep.'''
760 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
760 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
761
761
762 def splitpath(path):
762 def splitpath(path):
763 '''Split path by os.sep.
763 '''Split path by os.sep.
764 Note that this function does not use os.altsep because this is
764 Note that this function does not use os.altsep because this is
765 an alternative of simple "xxx.split(os.sep)".
765 an alternative of simple "xxx.split(os.sep)".
766 It is recommended to use os.path.normpath() before using this
766 It is recommended to use os.path.normpath() before using this
767 function if need.'''
767 function if need.'''
768 return path.split(os.sep)
768 return path.split(os.sep)
769
769
770 def gui():
770 def gui():
771 '''Are we running in a GUI?'''
771 '''Are we running in a GUI?'''
772 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
772 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
773
773
774 def mktempcopy(name, emptyok=False, createmode=None):
774 def mktempcopy(name, emptyok=False, createmode=None):
775 """Create a temporary file with the same contents from name
775 """Create a temporary file with the same contents from name
776
776
777 The permission bits are copied from the original file.
777 The permission bits are copied from the original file.
778
778
779 If the temporary file is going to be truncated immediately, you
779 If the temporary file is going to be truncated immediately, you
780 can use emptyok=True as an optimization.
780 can use emptyok=True as an optimization.
781
781
782 Returns the name of the temporary file.
782 Returns the name of the temporary file.
783 """
783 """
784 d, fn = os.path.split(name)
784 d, fn = os.path.split(name)
785 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
785 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
786 os.close(fd)
786 os.close(fd)
787 # Temporary files are created with mode 0600, which is usually not
787 # Temporary files are created with mode 0600, which is usually not
788 # what we want. If the original file already exists, just copy
788 # what we want. If the original file already exists, just copy
789 # its mode. Otherwise, manually obey umask.
789 # its mode. Otherwise, manually obey umask.
790 try:
790 try:
791 st_mode = os.lstat(name).st_mode & 0777
791 st_mode = os.lstat(name).st_mode & 0777
792 except OSError, inst:
792 except OSError, inst:
793 if inst.errno != errno.ENOENT:
793 if inst.errno != errno.ENOENT:
794 raise
794 raise
795 st_mode = createmode
795 st_mode = createmode
796 if st_mode is None:
796 if st_mode is None:
797 st_mode = ~umask
797 st_mode = ~umask
798 st_mode &= 0666
798 st_mode &= 0666
799 os.chmod(temp, st_mode)
799 os.chmod(temp, st_mode)
800 if emptyok:
800 if emptyok:
801 return temp
801 return temp
802 try:
802 try:
803 try:
803 try:
804 ifp = posixfile(name, "rb")
804 ifp = posixfile(name, "rb")
805 except IOError, inst:
805 except IOError, inst:
806 if inst.errno == errno.ENOENT:
806 if inst.errno == errno.ENOENT:
807 return temp
807 return temp
808 if not getattr(inst, 'filename', None):
808 if not getattr(inst, 'filename', None):
809 inst.filename = name
809 inst.filename = name
810 raise
810 raise
811 ofp = posixfile(temp, "wb")
811 ofp = posixfile(temp, "wb")
812 for chunk in filechunkiter(ifp):
812 for chunk in filechunkiter(ifp):
813 ofp.write(chunk)
813 ofp.write(chunk)
814 ifp.close()
814 ifp.close()
815 ofp.close()
815 ofp.close()
816 except:
816 except:
817 try: os.unlink(temp)
817 try: os.unlink(temp)
818 except: pass
818 except: pass
819 raise
819 raise
820 return temp
820 return temp
821
821
822 class atomictempfile(object):
822 class atomictempfile(object):
823 """file-like object that atomically updates a file
823 """file-like object that atomically updates a file
824
824
825 All writes will be redirected to a temporary copy of the original
825 All writes will be redirected to a temporary copy of the original
826 file. When rename is called, the copy is renamed to the original
826 file. When rename is called, the copy is renamed to the original
827 name, making the changes visible.
827 name, making the changes visible.
828 """
828 """
829 def __init__(self, name, mode='w+b', createmode=None):
829 def __init__(self, name, mode='w+b', createmode=None):
830 self.__name = name
830 self.__name = name
831 self._fp = None
831 self._fp = None
832 self.temp = mktempcopy(name, emptyok=('w' in mode),
832 self.temp = mktempcopy(name, emptyok=('w' in mode),
833 createmode=createmode)
833 createmode=createmode)
834 self._fp = posixfile(self.temp, mode)
834 self._fp = posixfile(self.temp, mode)
835
835
836 def __getattr__(self, name):
836 def __getattr__(self, name):
837 return getattr(self._fp, name)
837 return getattr(self._fp, name)
838
838
839 def rename(self):
839 def rename(self):
840 if not self._fp.closed:
840 if not self._fp.closed:
841 self._fp.close()
841 self._fp.close()
842 rename(self.temp, localpath(self.__name))
842 rename(self.temp, localpath(self.__name))
843
843
844 def close(self):
844 def close(self):
845 if not self._fp:
845 if not self._fp:
846 return
846 return
847 if not self._fp.closed:
847 if not self._fp.closed:
848 try:
848 try:
849 os.unlink(self.temp)
849 os.unlink(self.temp)
850 except: pass
850 except: pass
851 self._fp.close()
851 self._fp.close()
852
852
853 def __del__(self):
853 def __del__(self):
854 self.close()
854 self.close()
855
855
856 def makedirs(name, mode=None):
856 def makedirs(name, mode=None):
857 """recursive directory creation with parent mode inheritance"""
857 """recursive directory creation with parent mode inheritance"""
858 parent = os.path.abspath(os.path.dirname(name))
858 parent = os.path.abspath(os.path.dirname(name))
859 try:
859 try:
860 os.mkdir(name)
860 os.mkdir(name)
861 if mode is not None:
861 if mode is not None:
862 os.chmod(name, mode)
862 os.chmod(name, mode)
863 return
863 return
864 except OSError, err:
864 except OSError, err:
865 if err.errno == errno.EEXIST:
865 if err.errno == errno.EEXIST:
866 return
866 return
867 if not name or parent == name or err.errno != errno.ENOENT:
867 if not name or parent == name or err.errno != errno.ENOENT:
868 raise
868 raise
869 makedirs(parent, mode)
869 makedirs(parent, mode)
870 makedirs(name, mode)
870 makedirs(name, mode)
871
871
872 class opener(object):
872 class opener(object):
873 """Open files relative to a base directory
873 """Open files relative to a base directory
874
874
875 This class is used to hide the details of COW semantics and
875 This class is used to hide the details of COW semantics and
876 remote file access from higher level code.
876 remote file access from higher level code.
877 """
877 """
878 def __init__(self, base, audit=True):
878 def __init__(self, base, audit=True):
879 self.base = base
879 self.base = base
880 if audit:
880 if audit:
881 self.auditor = path_auditor(base)
881 self.auditor = path_auditor(base)
882 else:
882 else:
883 self.auditor = always
883 self.auditor = always
884 self.createmode = None
884 self.createmode = None
885 self._trustnlink = None
885 self._trustnlink = None
886
886
887 @propertycache
887 @propertycache
888 def _can_symlink(self):
888 def _can_symlink(self):
889 return checklink(self.base)
889 return checklink(self.base)
890
890
891 def _fixfilemode(self, name):
891 def _fixfilemode(self, name):
892 if self.createmode is None:
892 if self.createmode is None:
893 return
893 return
894 os.chmod(name, self.createmode & 0666)
894 os.chmod(name, self.createmode & 0666)
895
895
896 def __call__(self, path, mode="r", text=False, atomictemp=False):
896 def __call__(self, path, mode="r", text=False, atomictemp=False):
897 self.auditor(path)
897 self.auditor(path)
898 f = os.path.join(self.base, path)
898 f = os.path.join(self.base, path)
899
899
900 if not text and "b" not in mode:
900 if not text and "b" not in mode:
901 mode += "b" # for that other OS
901 mode += "b" # for that other OS
902
902
903 nlink = -1
903 nlink = -1
904 dirname, basename = os.path.split(f)
904 dirname, basename = os.path.split(f)
905 # If basename is empty, then the path is malformed because it points
905 # If basename is empty, then the path is malformed because it points
906 # to a directory. Let the posixfile() call below raise IOError.
906 # to a directory. Let the posixfile() call below raise IOError.
907 if basename and mode not in ('r', 'rb'):
907 if basename and mode not in ('r', 'rb'):
908 if atomictemp:
908 if atomictemp:
909 if not os.path.isdir(dirname):
909 if not os.path.isdir(dirname):
910 makedirs(dirname, self.createmode)
910 makedirs(dirname, self.createmode)
911 return atomictempfile(f, mode, self.createmode)
911 return atomictempfile(f, mode, self.createmode)
912 try:
912 try:
913 if 'w' in mode:
913 if 'w' in mode:
914 os.unlink(f)
914 os.unlink(f)
915 nlink = 0
915 nlink = 0
916 else:
916 else:
917 # nlinks() may behave differently for files on Windows
917 # nlinks() may behave differently for files on Windows
918 # shares if the file is open.
918 # shares if the file is open.
919 fd = open(f)
919 fd = open(f)
920 nlink = nlinks(f)
920 nlink = nlinks(f)
921 fd.close()
921 fd.close()
922 except (OSError, IOError):
922 except (OSError, IOError):
923 nlink = 0
923 nlink = 0
924 if not os.path.isdir(dirname):
924 if not os.path.isdir(dirname):
925 makedirs(dirname, self.createmode)
925 makedirs(dirname, self.createmode)
926 if nlink > 0:
926 if nlink > 0:
927 if self._trustnlink is None:
927 if self._trustnlink is None:
928 self._trustnlink = nlink > 1 or checknlink(f)
928 self._trustnlink = nlink > 1 or checknlink(f)
929 if nlink > 1 or not self._trustnlink:
929 if nlink > 1 or not self._trustnlink:
930 rename(mktempcopy(f), f)
930 rename(mktempcopy(f), f)
931 fp = posixfile(f, mode)
931 fp = posixfile(f, mode)
932 if nlink == 0:
932 if nlink == 0:
933 self._fixfilemode(f)
933 self._fixfilemode(f)
934 return fp
934 return fp
935
935
936 def symlink(self, src, dst):
936 def symlink(self, src, dst):
937 self.auditor(dst)
937 self.auditor(dst)
938 linkname = os.path.join(self.base, dst)
938 linkname = os.path.join(self.base, dst)
939 try:
939 try:
940 os.unlink(linkname)
940 os.unlink(linkname)
941 except OSError:
941 except OSError:
942 pass
942 pass
943
943
944 dirname = os.path.dirname(linkname)
944 dirname = os.path.dirname(linkname)
945 if not os.path.exists(dirname):
945 if not os.path.exists(dirname):
946 makedirs(dirname, self.createmode)
946 makedirs(dirname, self.createmode)
947
947
948 if self._can_symlink:
948 if self._can_symlink:
949 try:
949 try:
950 os.symlink(src, linkname)
950 os.symlink(src, linkname)
951 except OSError, err:
951 except OSError, err:
952 raise OSError(err.errno, _('could not symlink to %r: %s') %
952 raise OSError(err.errno, _('could not symlink to %r: %s') %
953 (src, err.strerror), linkname)
953 (src, err.strerror), linkname)
954 else:
954 else:
955 f = self(dst, "w")
955 f = self(dst, "w")
956 f.write(src)
956 f.write(src)
957 f.close()
957 f.close()
958 self._fixfilemode(dst)
958 self._fixfilemode(dst)
959
959
960 class chunkbuffer(object):
960 class chunkbuffer(object):
961 """Allow arbitrary sized chunks of data to be efficiently read from an
961 """Allow arbitrary sized chunks of data to be efficiently read from an
962 iterator over chunks of arbitrary size."""
962 iterator over chunks of arbitrary size."""
963
963
964 def __init__(self, in_iter):
964 def __init__(self, in_iter):
965 """in_iter is the iterator that's iterating over the input chunks.
965 """in_iter is the iterator that's iterating over the input chunks.
966 targetsize is how big a buffer to try to maintain."""
966 targetsize is how big a buffer to try to maintain."""
967 def splitbig(chunks):
967 def splitbig(chunks):
968 for chunk in chunks:
968 for chunk in chunks:
969 if len(chunk) > 2**20:
969 if len(chunk) > 2**20:
970 pos = 0
970 pos = 0
971 while pos < len(chunk):
971 while pos < len(chunk):
972 end = pos + 2 ** 18
972 end = pos + 2 ** 18
973 yield chunk[pos:end]
973 yield chunk[pos:end]
974 pos = end
974 pos = end
975 else:
975 else:
976 yield chunk
976 yield chunk
977 self.iter = splitbig(in_iter)
977 self.iter = splitbig(in_iter)
978 self._queue = []
978 self._queue = []
979
979
980 def read(self, l):
980 def read(self, l):
981 """Read L bytes of data from the iterator of chunks of data.
981 """Read L bytes of data from the iterator of chunks of data.
982 Returns less than L bytes if the iterator runs dry."""
982 Returns less than L bytes if the iterator runs dry."""
983 left = l
983 left = l
984 buf = ''
984 buf = ''
985 queue = self._queue
985 queue = self._queue
986 while left > 0:
986 while left > 0:
987 # refill the queue
987 # refill the queue
988 if not queue:
988 if not queue:
989 target = 2**18
989 target = 2**18
990 for chunk in self.iter:
990 for chunk in self.iter:
991 queue.append(chunk)
991 queue.append(chunk)
992 target -= len(chunk)
992 target -= len(chunk)
993 if target <= 0:
993 if target <= 0:
994 break
994 break
995 if not queue:
995 if not queue:
996 break
996 break
997
997
998 chunk = queue.pop(0)
998 chunk = queue.pop(0)
999 left -= len(chunk)
999 left -= len(chunk)
1000 if left < 0:
1000 if left < 0:
1001 queue.insert(0, chunk[left:])
1001 queue.insert(0, chunk[left:])
1002 buf += chunk[:left]
1002 buf += chunk[:left]
1003 else:
1003 else:
1004 buf += chunk
1004 buf += chunk
1005
1005
1006 return buf
1006 return buf
1007
1007
1008 def filechunkiter(f, size=65536, limit=None):
1008 def filechunkiter(f, size=65536, limit=None):
1009 """Create a generator that produces the data in the file size
1009 """Create a generator that produces the data in the file size
1010 (default 65536) bytes at a time, up to optional limit (default is
1010 (default 65536) bytes at a time, up to optional limit (default is
1011 to read all data). Chunks may be less than size bytes if the
1011 to read all data). Chunks may be less than size bytes if the
1012 chunk is the last chunk in the file, or the file is a socket or
1012 chunk is the last chunk in the file, or the file is a socket or
1013 some other type of file that sometimes reads less data than is
1013 some other type of file that sometimes reads less data than is
1014 requested."""
1014 requested."""
1015 assert size >= 0
1015 assert size >= 0
1016 assert limit is None or limit >= 0
1016 assert limit is None or limit >= 0
1017 while True:
1017 while True:
1018 if limit is None:
1018 if limit is None:
1019 nbytes = size
1019 nbytes = size
1020 else:
1020 else:
1021 nbytes = min(limit, size)
1021 nbytes = min(limit, size)
1022 s = nbytes and f.read(nbytes)
1022 s = nbytes and f.read(nbytes)
1023 if not s:
1023 if not s:
1024 break
1024 break
1025 if limit:
1025 if limit:
1026 limit -= len(s)
1026 limit -= len(s)
1027 yield s
1027 yield s
1028
1028
1029 def makedate():
1029 def makedate():
1030 lt = time.localtime()
1030 lt = time.localtime()
1031 if lt[8] == 1 and time.daylight:
1031 if lt[8] == 1 and time.daylight:
1032 tz = time.altzone
1032 tz = time.altzone
1033 else:
1033 else:
1034 tz = time.timezone
1034 tz = time.timezone
1035 t = time.mktime(lt)
1035 t = time.mktime(lt)
1036 if t < 0:
1036 if t < 0:
1037 hint = _("check your clock")
1037 hint = _("check your clock")
1038 raise Abort(_("negative timestamp: %d") % t, hint=hint)
1038 raise Abort(_("negative timestamp: %d") % t, hint=hint)
1039 return t, tz
1039 return t, tz
1040
1040
1041 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1041 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1042 """represent a (unixtime, offset) tuple as a localized time.
1042 """represent a (unixtime, offset) tuple as a localized time.
1043 unixtime is seconds since the epoch, and offset is the time zone's
1043 unixtime is seconds since the epoch, and offset is the time zone's
1044 number of seconds away from UTC. if timezone is false, do not
1044 number of seconds away from UTC. if timezone is false, do not
1045 append time zone to string."""
1045 append time zone to string."""
1046 t, tz = date or makedate()
1046 t, tz = date or makedate()
1047 if t < 0:
1047 if t < 0:
1048 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1048 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1049 tz = 0
1049 tz = 0
1050 if "%1" in format or "%2" in format:
1050 if "%1" in format or "%2" in format:
1051 sign = (tz > 0) and "-" or "+"
1051 sign = (tz > 0) and "-" or "+"
1052 minutes = abs(tz) // 60
1052 minutes = abs(tz) // 60
1053 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1053 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1054 format = format.replace("%2", "%02d" % (minutes % 60))
1054 format = format.replace("%2", "%02d" % (minutes % 60))
1055 s = time.strftime(format, time.gmtime(float(t) - tz))
1055 s = time.strftime(format, time.gmtime(float(t) - tz))
1056 return s
1056 return s
1057
1057
1058 def shortdate(date=None):
1058 def shortdate(date=None):
1059 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1059 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1060 return datestr(date, format='%Y-%m-%d')
1060 return datestr(date, format='%Y-%m-%d')
1061
1061
1062 def strdate(string, format, defaults=[]):
1062 def strdate(string, format, defaults=[]):
1063 """parse a localized time string and return a (unixtime, offset) tuple.
1063 """parse a localized time string and return a (unixtime, offset) tuple.
1064 if the string cannot be parsed, ValueError is raised."""
1064 if the string cannot be parsed, ValueError is raised."""
1065 def timezone(string):
1065 def timezone(string):
1066 tz = string.split()[-1]
1066 tz = string.split()[-1]
1067 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1067 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1068 sign = (tz[0] == "+") and 1 or -1
1068 sign = (tz[0] == "+") and 1 or -1
1069 hours = int(tz[1:3])
1069 hours = int(tz[1:3])
1070 minutes = int(tz[3:5])
1070 minutes = int(tz[3:5])
1071 return -sign * (hours * 60 + minutes) * 60
1071 return -sign * (hours * 60 + minutes) * 60
1072 if tz == "GMT" or tz == "UTC":
1072 if tz == "GMT" or tz == "UTC":
1073 return 0
1073 return 0
1074 return None
1074 return None
1075
1075
1076 # NOTE: unixtime = localunixtime + offset
1076 # NOTE: unixtime = localunixtime + offset
1077 offset, date = timezone(string), string
1077 offset, date = timezone(string), string
1078 if offset is not None:
1078 if offset is not None:
1079 date = " ".join(string.split()[:-1])
1079 date = " ".join(string.split()[:-1])
1080
1080
1081 # add missing elements from defaults
1081 # add missing elements from defaults
1082 usenow = False # default to using biased defaults
1082 usenow = False # default to using biased defaults
1083 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1083 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1084 found = [True for p in part if ("%"+p) in format]
1084 found = [True for p in part if ("%"+p) in format]
1085 if not found:
1085 if not found:
1086 date += "@" + defaults[part][usenow]
1086 date += "@" + defaults[part][usenow]
1087 format += "@%" + part[0]
1087 format += "@%" + part[0]
1088 else:
1088 else:
1089 # We've found a specific time element, less specific time
1089 # We've found a specific time element, less specific time
1090 # elements are relative to today
1090 # elements are relative to today
1091 usenow = True
1091 usenow = True
1092
1092
1093 timetuple = time.strptime(date, format)
1093 timetuple = time.strptime(date, format)
1094 localunixtime = int(calendar.timegm(timetuple))
1094 localunixtime = int(calendar.timegm(timetuple))
1095 if offset is None:
1095 if offset is None:
1096 # local timezone
1096 # local timezone
1097 unixtime = int(time.mktime(timetuple))
1097 unixtime = int(time.mktime(timetuple))
1098 offset = unixtime - localunixtime
1098 offset = unixtime - localunixtime
1099 else:
1099 else:
1100 unixtime = localunixtime + offset
1100 unixtime = localunixtime + offset
1101 return unixtime, offset
1101 return unixtime, offset
1102
1102
1103 def parsedate(date, formats=None, bias={}):
1103 def parsedate(date, formats=None, bias={}):
1104 """parse a localized date/time and return a (unixtime, offset) tuple.
1104 """parse a localized date/time and return a (unixtime, offset) tuple.
1105
1105
1106 The date may be a "unixtime offset" string or in one of the specified
1106 The date may be a "unixtime offset" string or in one of the specified
1107 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1107 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1108 """
1108 """
1109 if not date:
1109 if not date:
1110 return 0, 0
1110 return 0, 0
1111 if isinstance(date, tuple) and len(date) == 2:
1111 if isinstance(date, tuple) and len(date) == 2:
1112 return date
1112 return date
1113 if not formats:
1113 if not formats:
1114 formats = defaultdateformats
1114 formats = defaultdateformats
1115 date = date.strip()
1115 date = date.strip()
1116 try:
1116 try:
1117 when, offset = map(int, date.split(' '))
1117 when, offset = map(int, date.split(' '))
1118 except ValueError:
1118 except ValueError:
1119 # fill out defaults
1119 # fill out defaults
1120 now = makedate()
1120 now = makedate()
1121 defaults = {}
1121 defaults = {}
1122 nowmap = {}
1122 nowmap = {}
1123 for part in ("d", "mb", "yY", "HI", "M", "S"):
1123 for part in ("d", "mb", "yY", "HI", "M", "S"):
1124 # this piece is for rounding the specific end of unknowns
1124 # this piece is for rounding the specific end of unknowns
1125 b = bias.get(part)
1125 b = bias.get(part)
1126 if b is None:
1126 if b is None:
1127 if part[0] in "HMS":
1127 if part[0] in "HMS":
1128 b = "00"
1128 b = "00"
1129 else:
1129 else:
1130 b = "0"
1130 b = "0"
1131
1131
1132 # this piece is for matching the generic end to today's date
1132 # this piece is for matching the generic end to today's date
1133 n = datestr(now, "%" + part[0])
1133 n = datestr(now, "%" + part[0])
1134
1134
1135 defaults[part] = (b, n)
1135 defaults[part] = (b, n)
1136
1136
1137 for format in formats:
1137 for format in formats:
1138 try:
1138 try:
1139 when, offset = strdate(date, format, defaults)
1139 when, offset = strdate(date, format, defaults)
1140 except (ValueError, OverflowError):
1140 except (ValueError, OverflowError):
1141 pass
1141 pass
1142 else:
1142 else:
1143 break
1143 break
1144 else:
1144 else:
1145 raise Abort(_('invalid date: %r') % date)
1145 raise Abort(_('invalid date: %r') % date)
1146 # validate explicit (probably user-specified) date and
1146 # validate explicit (probably user-specified) date and
1147 # time zone offset. values must fit in signed 32 bits for
1147 # time zone offset. values must fit in signed 32 bits for
1148 # current 32-bit linux runtimes. timezones go from UTC-12
1148 # current 32-bit linux runtimes. timezones go from UTC-12
1149 # to UTC+14
1149 # to UTC+14
1150 if abs(when) > 0x7fffffff:
1150 if abs(when) > 0x7fffffff:
1151 raise Abort(_('date exceeds 32 bits: %d') % when)
1151 raise Abort(_('date exceeds 32 bits: %d') % when)
1152 if when < 0:
1152 if when < 0:
1153 raise Abort(_('negative date value: %d') % when)
1153 raise Abort(_('negative date value: %d') % when)
1154 if offset < -50400 or offset > 43200:
1154 if offset < -50400 or offset > 43200:
1155 raise Abort(_('impossible time zone offset: %d') % offset)
1155 raise Abort(_('impossible time zone offset: %d') % offset)
1156 return when, offset
1156 return when, offset
1157
1157
1158 def matchdate(date):
1158 def matchdate(date):
1159 """Return a function that matches a given date match specifier
1159 """Return a function that matches a given date match specifier
1160
1160
1161 Formats include:
1161 Formats include:
1162
1162
1163 '{date}' match a given date to the accuracy provided
1163 '{date}' match a given date to the accuracy provided
1164
1164
1165 '<{date}' on or before a given date
1165 '<{date}' on or before a given date
1166
1166
1167 '>{date}' on or after a given date
1167 '>{date}' on or after a given date
1168
1168
1169 >>> p1 = parsedate("10:29:59")
1169 >>> p1 = parsedate("10:29:59")
1170 >>> p2 = parsedate("10:30:00")
1170 >>> p2 = parsedate("10:30:00")
1171 >>> p3 = parsedate("10:30:59")
1171 >>> p3 = parsedate("10:30:59")
1172 >>> p4 = parsedate("10:31:00")
1172 >>> p4 = parsedate("10:31:00")
1173 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1173 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1174 >>> f = matchdate("10:30")
1174 >>> f = matchdate("10:30")
1175 >>> f(p1[0])
1175 >>> f(p1[0])
1176 False
1176 False
1177 >>> f(p2[0])
1177 >>> f(p2[0])
1178 True
1178 True
1179 >>> f(p3[0])
1179 >>> f(p3[0])
1180 True
1180 True
1181 >>> f(p4[0])
1181 >>> f(p4[0])
1182 False
1182 False
1183 >>> f(p5[0])
1183 >>> f(p5[0])
1184 False
1184 False
1185 """
1185 """
1186
1186
1187 def lower(date):
1187 def lower(date):
1188 d = dict(mb="1", d="1")
1188 d = dict(mb="1", d="1")
1189 return parsedate(date, extendeddateformats, d)[0]
1189 return parsedate(date, extendeddateformats, d)[0]
1190
1190
1191 def upper(date):
1191 def upper(date):
1192 d = dict(mb="12", HI="23", M="59", S="59")
1192 d = dict(mb="12", HI="23", M="59", S="59")
1193 for days in ("31", "30", "29"):
1193 for days in ("31", "30", "29"):
1194 try:
1194 try:
1195 d["d"] = days
1195 d["d"] = days
1196 return parsedate(date, extendeddateformats, d)[0]
1196 return parsedate(date, extendeddateformats, d)[0]
1197 except:
1197 except:
1198 pass
1198 pass
1199 d["d"] = "28"
1199 d["d"] = "28"
1200 return parsedate(date, extendeddateformats, d)[0]
1200 return parsedate(date, extendeddateformats, d)[0]
1201
1201
1202 date = date.strip()
1202 date = date.strip()
1203 if date[0] == "<":
1203 if date[0] == "<":
1204 when = upper(date[1:])
1204 when = upper(date[1:])
1205 return lambda x: x <= when
1205 return lambda x: x <= when
1206 elif date[0] == ">":
1206 elif date[0] == ">":
1207 when = lower(date[1:])
1207 when = lower(date[1:])
1208 return lambda x: x >= when
1208 return lambda x: x >= when
1209 elif date[0] == "-":
1209 elif date[0] == "-":
1210 try:
1210 try:
1211 days = int(date[1:])
1211 days = int(date[1:])
1212 except ValueError:
1212 except ValueError:
1213 raise Abort(_("invalid day spec: %s") % date[1:])
1213 raise Abort(_("invalid day spec: %s") % date[1:])
1214 when = makedate()[0] - days * 3600 * 24
1214 when = makedate()[0] - days * 3600 * 24
1215 return lambda x: x >= when
1215 return lambda x: x >= when
1216 elif " to " in date:
1216 elif " to " in date:
1217 a, b = date.split(" to ")
1217 a, b = date.split(" to ")
1218 start, stop = lower(a), upper(b)
1218 start, stop = lower(a), upper(b)
1219 return lambda x: x >= start and x <= stop
1219 return lambda x: x >= start and x <= stop
1220 else:
1220 else:
1221 start, stop = lower(date), upper(date)
1221 start, stop = lower(date), upper(date)
1222 return lambda x: x >= start and x <= stop
1222 return lambda x: x >= start and x <= stop
1223
1223
1224 def shortuser(user):
1224 def shortuser(user):
1225 """Return a short representation of a user name or email address."""
1225 """Return a short representation of a user name or email address."""
1226 f = user.find('@')
1226 f = user.find('@')
1227 if f >= 0:
1227 if f >= 0:
1228 user = user[:f]
1228 user = user[:f]
1229 f = user.find('<')
1229 f = user.find('<')
1230 if f >= 0:
1230 if f >= 0:
1231 user = user[f + 1:]
1231 user = user[f + 1:]
1232 f = user.find(' ')
1232 f = user.find(' ')
1233 if f >= 0:
1233 if f >= 0:
1234 user = user[:f]
1234 user = user[:f]
1235 f = user.find('.')
1235 f = user.find('.')
1236 if f >= 0:
1236 if f >= 0:
1237 user = user[:f]
1237 user = user[:f]
1238 return user
1238 return user
1239
1239
1240 def email(author):
1240 def email(author):
1241 '''get email of author.'''
1241 '''get email of author.'''
1242 r = author.find('>')
1242 r = author.find('>')
1243 if r == -1:
1243 if r == -1:
1244 r = None
1244 r = None
1245 return author[author.find('<') + 1:r]
1245 return author[author.find('<') + 1:r]
1246
1246
1247 def _ellipsis(text, maxlength):
1247 def _ellipsis(text, maxlength):
1248 if len(text) <= maxlength:
1248 if len(text) <= maxlength:
1249 return text, False
1249 return text, False
1250 else:
1250 else:
1251 return "%s..." % (text[:maxlength - 3]), True
1251 return "%s..." % (text[:maxlength - 3]), True
1252
1252
1253 def ellipsis(text, maxlength=400):
1253 def ellipsis(text, maxlength=400):
1254 """Trim string to at most maxlength (default: 400) characters."""
1254 """Trim string to at most maxlength (default: 400) characters."""
1255 try:
1255 try:
1256 # use unicode not to split at intermediate multi-byte sequence
1256 # use unicode not to split at intermediate multi-byte sequence
1257 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1257 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1258 maxlength)
1258 maxlength)
1259 if not truncated:
1259 if not truncated:
1260 return text
1260 return text
1261 return utext.encode(encoding.encoding)
1261 return utext.encode(encoding.encoding)
1262 except (UnicodeDecodeError, UnicodeEncodeError):
1262 except (UnicodeDecodeError, UnicodeEncodeError):
1263 return _ellipsis(text, maxlength)[0]
1263 return _ellipsis(text, maxlength)[0]
1264
1264
1265 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1265 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1266 '''yield every hg repository under path, recursively.'''
1266 '''yield every hg repository under path, recursively.'''
1267 def errhandler(err):
1267 def errhandler(err):
1268 if err.filename == path:
1268 if err.filename == path:
1269 raise err
1269 raise err
1270 if followsym and hasattr(os.path, 'samestat'):
1270 if followsym and hasattr(os.path, 'samestat'):
1271 def _add_dir_if_not_there(dirlst, dirname):
1271 def _add_dir_if_not_there(dirlst, dirname):
1272 match = False
1272 match = False
1273 samestat = os.path.samestat
1273 samestat = os.path.samestat
1274 dirstat = os.stat(dirname)
1274 dirstat = os.stat(dirname)
1275 for lstdirstat in dirlst:
1275 for lstdirstat in dirlst:
1276 if samestat(dirstat, lstdirstat):
1276 if samestat(dirstat, lstdirstat):
1277 match = True
1277 match = True
1278 break
1278 break
1279 if not match:
1279 if not match:
1280 dirlst.append(dirstat)
1280 dirlst.append(dirstat)
1281 return not match
1281 return not match
1282 else:
1282 else:
1283 followsym = False
1283 followsym = False
1284
1284
1285 if (seen_dirs is None) and followsym:
1285 if (seen_dirs is None) and followsym:
1286 seen_dirs = []
1286 seen_dirs = []
1287 _add_dir_if_not_there(seen_dirs, path)
1287 _add_dir_if_not_there(seen_dirs, path)
1288 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1288 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1289 dirs.sort()
1289 dirs.sort()
1290 if '.hg' in dirs:
1290 if '.hg' in dirs:
1291 yield root # found a repository
1291 yield root # found a repository
1292 qroot = os.path.join(root, '.hg', 'patches')
1292 qroot = os.path.join(root, '.hg', 'patches')
1293 if os.path.isdir(os.path.join(qroot, '.hg')):
1293 if os.path.isdir(os.path.join(qroot, '.hg')):
1294 yield qroot # we have a patch queue repo here
1294 yield qroot # we have a patch queue repo here
1295 if recurse:
1295 if recurse:
1296 # avoid recursing inside the .hg directory
1296 # avoid recursing inside the .hg directory
1297 dirs.remove('.hg')
1297 dirs.remove('.hg')
1298 else:
1298 else:
1299 dirs[:] = [] # don't descend further
1299 dirs[:] = [] # don't descend further
1300 elif followsym:
1300 elif followsym:
1301 newdirs = []
1301 newdirs = []
1302 for d in dirs:
1302 for d in dirs:
1303 fname = os.path.join(root, d)
1303 fname = os.path.join(root, d)
1304 if _add_dir_if_not_there(seen_dirs, fname):
1304 if _add_dir_if_not_there(seen_dirs, fname):
1305 if os.path.islink(fname):
1305 if os.path.islink(fname):
1306 for hgname in walkrepos(fname, True, seen_dirs):
1306 for hgname in walkrepos(fname, True, seen_dirs):
1307 yield hgname
1307 yield hgname
1308 else:
1308 else:
1309 newdirs.append(d)
1309 newdirs.append(d)
1310 dirs[:] = newdirs
1310 dirs[:] = newdirs
1311
1311
1312 _rcpath = None
1312 _rcpath = None
1313
1313
1314 def os_rcpath():
1314 def os_rcpath():
1315 '''return default os-specific hgrc search path'''
1315 '''return default os-specific hgrc search path'''
1316 path = system_rcpath()
1316 path = system_rcpath()
1317 path.extend(user_rcpath())
1317 path.extend(user_rcpath())
1318 path = [os.path.normpath(f) for f in path]
1318 path = [os.path.normpath(f) for f in path]
1319 return path
1319 return path
1320
1320
1321 def rcpath():
1321 def rcpath():
1322 '''return hgrc search path. if env var HGRCPATH is set, use it.
1322 '''return hgrc search path. if env var HGRCPATH is set, use it.
1323 for each item in path, if directory, use files ending in .rc,
1323 for each item in path, if directory, use files ending in .rc,
1324 else use item.
1324 else use item.
1325 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1325 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1326 if no HGRCPATH, use default os-specific path.'''
1326 if no HGRCPATH, use default os-specific path.'''
1327 global _rcpath
1327 global _rcpath
1328 if _rcpath is None:
1328 if _rcpath is None:
1329 if 'HGRCPATH' in os.environ:
1329 if 'HGRCPATH' in os.environ:
1330 _rcpath = []
1330 _rcpath = []
1331 for p in os.environ['HGRCPATH'].split(os.pathsep):
1331 for p in os.environ['HGRCPATH'].split(os.pathsep):
1332 if not p:
1332 if not p:
1333 continue
1333 continue
1334 p = expandpath(p)
1334 p = expandpath(p)
1335 if os.path.isdir(p):
1335 if os.path.isdir(p):
1336 for f, kind in osutil.listdir(p):
1336 for f, kind in osutil.listdir(p):
1337 if f.endswith('.rc'):
1337 if f.endswith('.rc'):
1338 _rcpath.append(os.path.join(p, f))
1338 _rcpath.append(os.path.join(p, f))
1339 else:
1339 else:
1340 _rcpath.append(p)
1340 _rcpath.append(p)
1341 else:
1341 else:
1342 _rcpath = os_rcpath()
1342 _rcpath = os_rcpath()
1343 return _rcpath
1343 return _rcpath
1344
1344
1345 def bytecount(nbytes):
1345 def bytecount(nbytes):
1346 '''return byte count formatted as readable string, with units'''
1346 '''return byte count formatted as readable string, with units'''
1347
1347
1348 units = (
1348 units = (
1349 (100, 1 << 30, _('%.0f GB')),
1349 (100, 1 << 30, _('%.0f GB')),
1350 (10, 1 << 30, _('%.1f GB')),
1350 (10, 1 << 30, _('%.1f GB')),
1351 (1, 1 << 30, _('%.2f GB')),
1351 (1, 1 << 30, _('%.2f GB')),
1352 (100, 1 << 20, _('%.0f MB')),
1352 (100, 1 << 20, _('%.0f MB')),
1353 (10, 1 << 20, _('%.1f MB')),
1353 (10, 1 << 20, _('%.1f MB')),
1354 (1, 1 << 20, _('%.2f MB')),
1354 (1, 1 << 20, _('%.2f MB')),
1355 (100, 1 << 10, _('%.0f KB')),
1355 (100, 1 << 10, _('%.0f KB')),
1356 (10, 1 << 10, _('%.1f KB')),
1356 (10, 1 << 10, _('%.1f KB')),
1357 (1, 1 << 10, _('%.2f KB')),
1357 (1, 1 << 10, _('%.2f KB')),
1358 (1, 1, _('%.0f bytes')),
1358 (1, 1, _('%.0f bytes')),
1359 )
1359 )
1360
1360
1361 for multiplier, divisor, format in units:
1361 for multiplier, divisor, format in units:
1362 if nbytes >= divisor * multiplier:
1362 if nbytes >= divisor * multiplier:
1363 return format % (nbytes / float(divisor))
1363 return format % (nbytes / float(divisor))
1364 return units[-1][2] % nbytes
1364 return units[-1][2] % nbytes
1365
1365
1366 def drop_scheme(scheme, path):
1366 def drop_scheme(scheme, path):
1367 sc = scheme + ':'
1367 sc = scheme + ':'
1368 if path.startswith(sc):
1368 if path.startswith(sc):
1369 path = path[len(sc):]
1369 path = path[len(sc):]
1370 if path.startswith('//'):
1370 if path.startswith('//'):
1371 if scheme == 'file':
1371 if scheme == 'file':
1372 i = path.find('/', 2)
1372 i = path.find('/', 2)
1373 if i == -1:
1373 if i == -1:
1374 return ''
1374 return ''
1375 # On Windows, absolute paths are rooted at the current drive
1375 # On Windows, absolute paths are rooted at the current drive
1376 # root. On POSIX they are rooted at the file system root.
1376 # root. On POSIX they are rooted at the file system root.
1377 if os.name == 'nt':
1377 if os.name == 'nt':
1378 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1378 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1379 path = os.path.join(droot, path[i + 1:])
1379 path = os.path.join(droot, path[i + 1:])
1380 else:
1380 else:
1381 path = path[i:]
1381 path = path[i:]
1382 else:
1382 else:
1383 path = path[2:]
1383 path = path[2:]
1384 return path
1384 return path
1385
1385
1386 def uirepr(s):
1386 def uirepr(s):
1387 # Avoid double backslash in Windows path repr()
1387 # Avoid double backslash in Windows path repr()
1388 return repr(s).replace('\\\\', '\\')
1388 return repr(s).replace('\\\\', '\\')
1389
1389
1390 #### naming convention of below implementation follows 'textwrap' module
1390 #### naming convention of below implementation follows 'textwrap' module
1391
1391
1392 class MBTextWrapper(textwrap.TextWrapper):
1392 class MBTextWrapper(textwrap.TextWrapper):
1393 """
1393 """
1394 Extend TextWrapper for double-width characters.
1394 Extend TextWrapper for double-width characters.
1395
1395
1396 Some Asian characters use two terminal columns instead of one.
1396 Some Asian characters use two terminal columns instead of one.
1397 A good example of this behavior can be seen with u'\u65e5\u672c',
1397 A good example of this behavior can be seen with u'\u65e5\u672c',
1398 the two Japanese characters for "Japan":
1398 the two Japanese characters for "Japan":
1399 len() returns 2, but when printed to a terminal, they eat 4 columns.
1399 len() returns 2, but when printed to a terminal, they eat 4 columns.
1400
1400
1401 (Note that this has nothing to do whatsoever with unicode
1401 (Note that this has nothing to do whatsoever with unicode
1402 representation, or encoding of the underlying string)
1402 representation, or encoding of the underlying string)
1403 """
1403 """
1404 def __init__(self, **kwargs):
1404 def __init__(self, **kwargs):
1405 textwrap.TextWrapper.__init__(self, **kwargs)
1405 textwrap.TextWrapper.__init__(self, **kwargs)
1406
1406
1407 def _cutdown(self, str, space_left):
1407 def _cutdown(self, str, space_left):
1408 l = 0
1408 l = 0
1409 ucstr = unicode(str, encoding.encoding)
1409 ucstr = unicode(str, encoding.encoding)
1410 colwidth = unicodedata.east_asian_width
1410 colwidth = unicodedata.east_asian_width
1411 for i in xrange(len(ucstr)):
1411 for i in xrange(len(ucstr)):
1412 l += colwidth(ucstr[i]) in 'WFA' and 2 or 1
1412 l += colwidth(ucstr[i]) in 'WFA' and 2 or 1
1413 if space_left < l:
1413 if space_left < l:
1414 return (ucstr[:i].encode(encoding.encoding),
1414 return (ucstr[:i].encode(encoding.encoding),
1415 ucstr[i:].encode(encoding.encoding))
1415 ucstr[i:].encode(encoding.encoding))
1416 return str, ''
1416 return str, ''
1417
1417
1418 # ----------------------------------------
1418 # ----------------------------------------
1419 # overriding of base class
1419 # overriding of base class
1420
1420
1421 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1421 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1422 space_left = max(width - cur_len, 1)
1422 space_left = max(width - cur_len, 1)
1423
1423
1424 if self.break_long_words:
1424 if self.break_long_words:
1425 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1425 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1426 cur_line.append(cut)
1426 cur_line.append(cut)
1427 reversed_chunks[-1] = res
1427 reversed_chunks[-1] = res
1428 elif not cur_line:
1428 elif not cur_line:
1429 cur_line.append(reversed_chunks.pop())
1429 cur_line.append(reversed_chunks.pop())
1430
1430
1431 #### naming convention of above implementation follows 'textwrap' module
1431 #### naming convention of above implementation follows 'textwrap' module
1432
1432
1433 def wrap(line, width, initindent='', hangindent=''):
1433 def wrap(line, width, initindent='', hangindent=''):
1434 maxindent = max(len(hangindent), len(initindent))
1434 maxindent = max(len(hangindent), len(initindent))
1435 if width <= maxindent:
1435 if width <= maxindent:
1436 # adjust for weird terminal size
1436 # adjust for weird terminal size
1437 width = max(78, maxindent + 1)
1437 width = max(78, maxindent + 1)
1438 wrapper = MBTextWrapper(width=width,
1438 wrapper = MBTextWrapper(width=width,
1439 initial_indent=initindent,
1439 initial_indent=initindent,
1440 subsequent_indent=hangindent)
1440 subsequent_indent=hangindent)
1441 return wrapper.fill(line)
1441 return wrapper.fill(line)
1442
1442
1443 def iterlines(iterator):
1443 def iterlines(iterator):
1444 for chunk in iterator:
1444 for chunk in iterator:
1445 for line in chunk.splitlines():
1445 for line in chunk.splitlines():
1446 yield line
1446 yield line
1447
1447
1448 def expandpath(path):
1448 def expandpath(path):
1449 return os.path.expanduser(os.path.expandvars(path))
1449 return os.path.expanduser(os.path.expandvars(path))
1450
1450
1451 def hgcmd():
1451 def hgcmd():
1452 """Return the command used to execute current hg
1452 """Return the command used to execute current hg
1453
1453
1454 This is different from hgexecutable() because on Windows we want
1454 This is different from hgexecutable() because on Windows we want
1455 to avoid things opening new shell windows like batch files, so we
1455 to avoid things opening new shell windows like batch files, so we
1456 get either the python call or current executable.
1456 get either the python call or current executable.
1457 """
1457 """
1458 if main_is_frozen():
1458 if main_is_frozen():
1459 return [sys.executable]
1459 return [sys.executable]
1460 return gethgcmd()
1460 return gethgcmd()
1461
1461
1462 def rundetached(args, condfn):
1462 def rundetached(args, condfn):
1463 """Execute the argument list in a detached process.
1463 """Execute the argument list in a detached process.
1464
1464
1465 condfn is a callable which is called repeatedly and should return
1465 condfn is a callable which is called repeatedly and should return
1466 True once the child process is known to have started successfully.
1466 True once the child process is known to have started successfully.
1467 At this point, the child process PID is returned. If the child
1467 At this point, the child process PID is returned. If the child
1468 process fails to start or finishes before condfn() evaluates to
1468 process fails to start or finishes before condfn() evaluates to
1469 True, return -1.
1469 True, return -1.
1470 """
1470 """
1471 # Windows case is easier because the child process is either
1471 # Windows case is easier because the child process is either
1472 # successfully starting and validating the condition or exiting
1472 # successfully starting and validating the condition or exiting
1473 # on failure. We just poll on its PID. On Unix, if the child
1473 # on failure. We just poll on its PID. On Unix, if the child
1474 # process fails to start, it will be left in a zombie state until
1474 # process fails to start, it will be left in a zombie state until
1475 # the parent wait on it, which we cannot do since we expect a long
1475 # the parent wait on it, which we cannot do since we expect a long
1476 # running process on success. Instead we listen for SIGCHLD telling
1476 # running process on success. Instead we listen for SIGCHLD telling
1477 # us our child process terminated.
1477 # us our child process terminated.
1478 terminated = set()
1478 terminated = set()
1479 def handler(signum, frame):
1479 def handler(signum, frame):
1480 terminated.add(os.wait())
1480 terminated.add(os.wait())
1481 prevhandler = None
1481 prevhandler = None
1482 if hasattr(signal, 'SIGCHLD'):
1482 if hasattr(signal, 'SIGCHLD'):
1483 prevhandler = signal.signal(signal.SIGCHLD, handler)
1483 prevhandler = signal.signal(signal.SIGCHLD, handler)
1484 try:
1484 try:
1485 pid = spawndetached(args)
1485 pid = spawndetached(args)
1486 while not condfn():
1486 while not condfn():
1487 if ((pid in terminated or not testpid(pid))
1487 if ((pid in terminated or not testpid(pid))
1488 and not condfn()):
1488 and not condfn()):
1489 return -1
1489 return -1
1490 time.sleep(0.1)
1490 time.sleep(0.1)
1491 return pid
1491 return pid
1492 finally:
1492 finally:
1493 if prevhandler is not None:
1493 if prevhandler is not None:
1494 signal.signal(signal.SIGCHLD, prevhandler)
1494 signal.signal(signal.SIGCHLD, prevhandler)
1495
1495
1496 try:
1496 try:
1497 any, all = any, all
1497 any, all = any, all
1498 except NameError:
1498 except NameError:
1499 def any(iterable):
1499 def any(iterable):
1500 for i in iterable:
1500 for i in iterable:
1501 if i:
1501 if i:
1502 return True
1502 return True
1503 return False
1503 return False
1504
1504
1505 def all(iterable):
1505 def all(iterable):
1506 for i in iterable:
1506 for i in iterable:
1507 if not i:
1507 if not i:
1508 return False
1508 return False
1509 return True
1509 return True
1510
1510
1511 def interpolate(prefix, mapping, s, fn=None):
1511 def interpolate(prefix, mapping, s, fn=None):
1512 """Return the result of interpolating items in the mapping into string s.
1512 """Return the result of interpolating items in the mapping into string s.
1513
1513
1514 prefix is a single character string, or a two character string with
1514 prefix is a single character string, or a two character string with
1515 a backslash as the first character if the prefix needs to be escaped in
1515 a backslash as the first character if the prefix needs to be escaped in
1516 a regular expression.
1516 a regular expression.
1517
1517
1518 fn is an optional function that will be applied to the replacement text
1518 fn is an optional function that will be applied to the replacement text
1519 just before replacement.
1519 just before replacement.
1520 """
1520 """
1521 fn = fn or (lambda s: s)
1521 fn = fn or (lambda s: s)
1522 r = re.compile(r'%s(%s)' % (prefix, '|'.join(mapping.keys())))
1522 r = re.compile(r'%s(%s)' % (prefix, '|'.join(mapping.keys())))
1523 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1523 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1524
1524
1525 def getport(port):
1525 def getport(port):
1526 """Return the port for a given network service.
1526 """Return the port for a given network service.
1527
1527
1528 If port is an integer, it's returned as is. If it's a string, it's
1528 If port is an integer, it's returned as is. If it's a string, it's
1529 looked up using socket.getservbyname(). If there's no matching
1529 looked up using socket.getservbyname(). If there's no matching
1530 service, util.Abort is raised.
1530 service, util.Abort is raised.
1531 """
1531 """
1532 try:
1532 try:
1533 return int(port)
1533 return int(port)
1534 except ValueError:
1534 except ValueError:
1535 pass
1535 pass
1536
1536
1537 try:
1537 try:
1538 return socket.getservbyname(port)
1538 return socket.getservbyname(port)
1539 except socket.error:
1539 except socket.error:
1540 raise Abort(_("no port number associated with service '%s'") % port)
1540 raise Abort(_("no port number associated with service '%s'") % port)
1541
1541
1542 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1542 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1543 '0': False, 'no': False, 'false': False, 'off': False,
1543 '0': False, 'no': False, 'false': False, 'off': False,
1544 'never': False}
1544 'never': False}
1545
1545
1546 def parsebool(s):
1546 def parsebool(s):
1547 """Parse s into a boolean.
1547 """Parse s into a boolean.
1548
1548
1549 If s is not a valid boolean, returns None.
1549 If s is not a valid boolean, returns None.
1550 """
1550 """
1551 return _booleans.get(s.lower(), None)
1551 return _booleans.get(s.lower(), None)
@@ -1,375 +1,375 b''
1 # windows.py - Windows utility function implementations for Mercurial
1 # windows.py - Windows utility function implementations for Mercurial
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
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 i18n import _
8 from i18n import _
9 import osutil, error
9 import osutil, error
10 import errno, msvcrt, os, re, sys, random, subprocess
10 import errno, msvcrt, os, re, sys, random, subprocess
11
11
12 nulldev = 'NUL:'
12 nulldev = 'NUL:'
13 umask = 002
13 umask = 002
14
14
15 # wrap osutil.posixfile to provide friendlier exceptions
15 # wrap osutil.posixfile to provide friendlier exceptions
16 def posixfile(name, mode='r', buffering=-1):
16 def posixfile(name, mode='r', buffering=-1):
17 try:
17 try:
18 return osutil.posixfile(name, mode, buffering)
18 return osutil.posixfile(name, mode, buffering)
19 except WindowsError, err:
19 except WindowsError, err:
20 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
20 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
21 posixfile.__doc__ = osutil.posixfile.__doc__
21 posixfile.__doc__ = osutil.posixfile.__doc__
22
22
23 class winstdout(object):
23 class winstdout(object):
24 '''stdout on windows misbehaves if sent through a pipe'''
24 '''stdout on windows misbehaves if sent through a pipe'''
25
25
26 def __init__(self, fp):
26 def __init__(self, fp):
27 self.fp = fp
27 self.fp = fp
28
28
29 def __getattr__(self, key):
29 def __getattr__(self, key):
30 return getattr(self.fp, key)
30 return getattr(self.fp, key)
31
31
32 def close(self):
32 def close(self):
33 try:
33 try:
34 self.fp.close()
34 self.fp.close()
35 except: pass
35 except: pass
36
36
37 def write(self, s):
37 def write(self, s):
38 try:
38 try:
39 # This is workaround for "Not enough space" error on
39 # This is workaround for "Not enough space" error on
40 # writing large size of data to console.
40 # writing large size of data to console.
41 limit = 16000
41 limit = 16000
42 l = len(s)
42 l = len(s)
43 start = 0
43 start = 0
44 self.softspace = 0
44 self.softspace = 0
45 while start < l:
45 while start < l:
46 end = start + limit
46 end = start + limit
47 self.fp.write(s[start:end])
47 self.fp.write(s[start:end])
48 start = end
48 start = end
49 except IOError, inst:
49 except IOError, inst:
50 if inst.errno != 0:
50 if inst.errno != 0:
51 raise
51 raise
52 self.close()
52 self.close()
53 raise IOError(errno.EPIPE, 'Broken pipe')
53 raise IOError(errno.EPIPE, 'Broken pipe')
54
54
55 def flush(self):
55 def flush(self):
56 try:
56 try:
57 return self.fp.flush()
57 return self.fp.flush()
58 except IOError, inst:
58 except IOError, inst:
59 if inst.errno != errno.EINVAL:
59 if inst.errno != errno.EINVAL:
60 raise
60 raise
61 self.close()
61 self.close()
62 raise IOError(errno.EPIPE, 'Broken pipe')
62 raise IOError(errno.EPIPE, 'Broken pipe')
63
63
64 sys.stdout = winstdout(sys.stdout)
64 sys.stdout = winstdout(sys.stdout)
65
65
66 def _is_win_9x():
66 def _is_win_9x():
67 '''return true if run on windows 95, 98 or me.'''
67 '''return true if run on windows 95, 98 or me.'''
68 try:
68 try:
69 return sys.getwindowsversion()[3] == 1
69 return sys.getwindowsversion()[3] == 1
70 except AttributeError:
70 except AttributeError:
71 return 'command' in os.environ.get('comspec', '')
71 return 'command' in os.environ.get('comspec', '')
72
72
73 def openhardlinks():
73 def openhardlinks():
74 return not _is_win_9x() and "win32api" in globals()
74 return not _is_win_9x() and "win32api" in globals()
75
75
76 def system_rcpath():
76 def system_rcpath():
77 try:
77 try:
78 return system_rcpath_win32()
78 return system_rcpath_win32()
79 except:
79 except:
80 return [r'c:\mercurial\mercurial.ini']
80 return [r'c:\mercurial\mercurial.ini']
81
81
82 def user_rcpath():
82 def user_rcpath():
83 '''return os-specific hgrc search path to the user dir'''
83 '''return os-specific hgrc search path to the user dir'''
84 try:
84 try:
85 path = user_rcpath_win32()
85 path = user_rcpath_win32()
86 except:
86 except:
87 home = os.path.expanduser('~')
87 home = os.path.expanduser('~')
88 path = [os.path.join(home, 'mercurial.ini'),
88 path = [os.path.join(home, 'mercurial.ini'),
89 os.path.join(home, '.hgrc')]
89 os.path.join(home, '.hgrc')]
90 userprofile = os.environ.get('USERPROFILE')
90 userprofile = os.environ.get('USERPROFILE')
91 if userprofile:
91 if userprofile:
92 path.append(os.path.join(userprofile, 'mercurial.ini'))
92 path.append(os.path.join(userprofile, 'mercurial.ini'))
93 path.append(os.path.join(userprofile, '.hgrc'))
93 path.append(os.path.join(userprofile, '.hgrc'))
94 return path
94 return path
95
95
96 def parse_patch_output(output_line):
96 def parse_patch_output(output_line):
97 """parses the output produced by patch and returns the filename"""
97 """parses the output produced by patch and returns the filename"""
98 pf = output_line[14:]
98 pf = output_line[14:]
99 if pf[0] == '`':
99 if pf[0] == '`':
100 pf = pf[1:-1] # Remove the quotes
100 pf = pf[1:-1] # Remove the quotes
101 return pf
101 return pf
102
102
103 def sshargs(sshcmd, host, user, port):
103 def sshargs(sshcmd, host, user, port):
104 '''Build argument list for ssh or Plink'''
104 '''Build argument list for ssh or Plink'''
105 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
105 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
106 args = user and ("%s@%s" % (user, host)) or host
106 args = user and ("%s@%s" % (user, host)) or host
107 return port and ("%s %s %s" % (args, pflag, port)) or args
107 return port and ("%s %s %s" % (args, pflag, port)) or args
108
108
109 def testpid(pid):
109 def testpid(pid):
110 '''return False if pid dead, True if running or not known'''
110 '''return False if pid dead, True if running or not known'''
111 return True
111 return True
112
112
113 def set_flags(f, l, x):
113 def set_flags(f, l, x):
114 pass
114 pass
115
115
116 def set_binary(fd):
116 def set_binary(fd):
117 # When run without console, pipes may expose invalid
117 # When run without console, pipes may expose invalid
118 # fileno(), usually set to -1.
118 # fileno(), usually set to -1.
119 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
119 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
120 msvcrt.setmode(fd.fileno(), os.O_BINARY)
120 msvcrt.setmode(fd.fileno(), os.O_BINARY)
121
121
122 def pconvert(path):
122 def pconvert(path):
123 return '/'.join(path.split(os.sep))
123 return '/'.join(path.split(os.sep))
124
124
125 def localpath(path):
125 def localpath(path):
126 return path.replace('/', '\\')
126 return path.replace('/', '\\')
127
127
128 def normpath(path):
128 def normpath(path):
129 return pconvert(os.path.normpath(path))
129 return pconvert(os.path.normpath(path))
130
130
131 def realpath(path):
131 def realpath(path):
132 '''
132 '''
133 Returns the true, canonical file system path equivalent to the given
133 Returns the true, canonical file system path equivalent to the given
134 path.
134 path.
135 '''
135 '''
136 # TODO: There may be a more clever way to do this that also handles other,
136 # TODO: There may be a more clever way to do this that also handles other,
137 # less common file systems.
137 # less common file systems.
138 return os.path.normpath(os.path.normcase(os.path.realpath(path)))
138 return os.path.normpath(os.path.normcase(os.path.realpath(path)))
139
139
140 def samestat(s1, s2):
140 def samestat(s1, s2):
141 return False
141 return False
142
142
143 # A sequence of backslashes is special iff it precedes a double quote:
143 # A sequence of backslashes is special iff it precedes a double quote:
144 # - if there's an even number of backslashes, the double quote is not
144 # - if there's an even number of backslashes, the double quote is not
145 # quoted (i.e. it ends the quoted region)
145 # quoted (i.e. it ends the quoted region)
146 # - if there's an odd number of backslashes, the double quote is quoted
146 # - if there's an odd number of backslashes, the double quote is quoted
147 # - in both cases, every pair of backslashes is unquoted into a single
147 # - in both cases, every pair of backslashes is unquoted into a single
148 # backslash
148 # backslash
149 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
149 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
150 # So, to quote a string, we must surround it in double quotes, double
150 # So, to quote a string, we must surround it in double quotes, double
151 # the number of backslashes that preceed double quotes and add another
151 # the number of backslashes that preceed double quotes and add another
152 # backslash before every double quote (being careful with the double
152 # backslash before every double quote (being careful with the double
153 # quote we've appended to the end)
153 # quote we've appended to the end)
154 _quotere = None
154 _quotere = None
155 def shellquote(s):
155 def shellquote(s):
156 global _quotere
156 global _quotere
157 if _quotere is None:
157 if _quotere is None:
158 _quotere = re.compile(r'(\\*)("|\\$)')
158 _quotere = re.compile(r'(\\*)("|\\$)')
159 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
159 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
160
160
161 def quotecommand(cmd):
161 def quotecommand(cmd):
162 """Build a command string suitable for os.popen* calls."""
162 """Build a command string suitable for os.popen* calls."""
163 if sys.version_info < (2, 7, 1):
163 if sys.version_info < (2, 7, 1):
164 # Python versions since 2.7.1 do this extra quoting themselves
164 # Python versions since 2.7.1 do this extra quoting themselves
165 return '"' + cmd + '"'
165 return '"' + cmd + '"'
166 return cmd
166 return cmd
167
167
168 def popen(command, mode='r'):
168 def popen(command, mode='r'):
169 # Work around "popen spawned process may not write to stdout
169 # Work around "popen spawned process may not write to stdout
170 # under windows"
170 # under windows"
171 # http://bugs.python.org/issue1366
171 # http://bugs.python.org/issue1366
172 command += " 2> %s" % nulldev
172 command += " 2> %s" % nulldev
173 return os.popen(quotecommand(command), mode)
173 return os.popen(quotecommand(command), mode)
174
174
175 def explain_exit(code):
175 def explain_exit(code):
176 return _("exited with status %d") % code, code
176 return _("exited with status %d") % code, code
177
177
178 # if you change this stub into a real check, please try to implement the
178 # if you change this stub into a real check, please try to implement the
179 # username and groupname functions above, too.
179 # username and groupname functions above, too.
180 def isowner(st):
180 def isowner(st):
181 return True
181 return True
182
182
183 def find_exe(command):
183 def find_exe(command):
184 '''Find executable for command searching like cmd.exe does.
184 '''Find executable for command searching like cmd.exe does.
185 If command is a basename then PATH is searched for command.
185 If command is a basename then PATH is searched for command.
186 PATH isn't searched if command is an absolute or relative path.
186 PATH isn't searched if command is an absolute or relative path.
187 An extension from PATHEXT is found and added if not present.
187 An extension from PATHEXT is found and added if not present.
188 If command isn't found None is returned.'''
188 If command isn't found None is returned.'''
189 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
189 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
190 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
190 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
191 if os.path.splitext(command)[1].lower() in pathexts:
191 if os.path.splitext(command)[1].lower() in pathexts:
192 pathexts = ['']
192 pathexts = ['']
193
193
194 def findexisting(pathcommand):
194 def findexisting(pathcommand):
195 'Will append extension (if needed) and return existing file'
195 'Will append extension (if needed) and return existing file'
196 for ext in pathexts:
196 for ext in pathexts:
197 executable = pathcommand + ext
197 executable = pathcommand + ext
198 if os.path.exists(executable):
198 if os.path.exists(executable):
199 return executable
199 return executable
200 return None
200 return None
201
201
202 if os.sep in command:
202 if os.sep in command:
203 return findexisting(command)
203 return findexisting(command)
204
204
205 for path in os.environ.get('PATH', '').split(os.pathsep):
205 for path in os.environ.get('PATH', '').split(os.pathsep):
206 executable = findexisting(os.path.join(path, command))
206 executable = findexisting(os.path.join(path, command))
207 if executable is not None:
207 if executable is not None:
208 return executable
208 return executable
209 return findexisting(os.path.expanduser(os.path.expandvars(command)))
209 return findexisting(os.path.expanduser(os.path.expandvars(command)))
210
210
211 def set_signal_handler():
211 def set_signal_handler():
212 try:
212 try:
213 set_signal_handler_win32()
213 set_signal_handler_win32()
214 except NameError:
214 except NameError:
215 pass
215 pass
216
216
217 def statfiles(files):
217 def statfiles(files):
218 '''Stat each file in files and yield stat or None if file does not exist.
218 '''Stat each file in files and yield stat or None if file does not exist.
219 Cluster and cache stat per directory to minimize number of OS stat calls.'''
219 Cluster and cache stat per directory to minimize number of OS stat calls.'''
220 ncase = os.path.normcase
220 ncase = os.path.normcase
221 dircache = {} # dirname -> filename -> status | None if file does not exist
221 dircache = {} # dirname -> filename -> status | None if file does not exist
222 for nf in files:
222 for nf in files:
223 nf = ncase(nf)
223 nf = ncase(nf)
224 dir, base = os.path.split(nf)
224 dir, base = os.path.split(nf)
225 if not dir:
225 if not dir:
226 dir = '.'
226 dir = '.'
227 cache = dircache.get(dir, None)
227 cache = dircache.get(dir, None)
228 if cache is None:
228 if cache is None:
229 try:
229 try:
230 dmap = dict([(ncase(n), s)
230 dmap = dict([(ncase(n), s)
231 for n, k, s in osutil.listdir(dir, True)])
231 for n, k, s in osutil.listdir(dir, True)])
232 except OSError, err:
232 except OSError, err:
233 # handle directory not found in Python version prior to 2.5
233 # handle directory not found in Python version prior to 2.5
234 # Python <= 2.4 returns native Windows code 3 in errno
234 # Python <= 2.4 returns native Windows code 3 in errno
235 # Python >= 2.5 returns ENOENT and adds winerror field
235 # Python >= 2.5 returns ENOENT and adds winerror field
236 # EINVAL is raised if dir is not a directory.
236 # EINVAL is raised if dir is not a directory.
237 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
237 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
238 errno.ENOTDIR):
238 errno.ENOTDIR):
239 raise
239 raise
240 dmap = {}
240 dmap = {}
241 cache = dircache.setdefault(dir, dmap)
241 cache = dircache.setdefault(dir, dmap)
242 yield cache.get(base, None)
242 yield cache.get(base, None)
243
243
244 def getuser():
244 def getuser():
245 '''return name of current user'''
245 '''return name of current user'''
246 raise error.Abort(_('user name not available - set USERNAME '
246 raise error.Abort(_('user name not available - set USERNAME '
247 'environment variable'))
247 'environment variable'))
248
248
249 def username(uid=None):
249 def username(uid=None):
250 """Return the name of the user with the given uid.
250 """Return the name of the user with the given uid.
251
251
252 If uid is None, return the name of the current user."""
252 If uid is None, return the name of the current user."""
253 return None
253 return None
254
254
255 def groupname(gid=None):
255 def groupname(gid=None):
256 """Return the name of the group with the given gid.
256 """Return the name of the group with the given gid.
257
257
258 If gid is None, return the name of the current group."""
258 If gid is None, return the name of the current group."""
259 return None
259 return None
260
260
261 def _removedirs(name):
261 def _removedirs(name):
262 """special version of os.removedirs that does not remove symlinked
262 """special version of os.removedirs that does not remove symlinked
263 directories or junction points if they actually contain files"""
263 directories or junction points if they actually contain files"""
264 if osutil.listdir(name):
264 if osutil.listdir(name):
265 return
265 return
266 os.rmdir(name)
266 os.rmdir(name)
267 head, tail = os.path.split(name)
267 head, tail = os.path.split(name)
268 if not tail:
268 if not tail:
269 head, tail = os.path.split(head)
269 head, tail = os.path.split(head)
270 while head and tail:
270 while head and tail:
271 try:
271 try:
272 if osutil.listdir(head):
272 if osutil.listdir(head):
273 return
273 return
274 os.rmdir(head)
274 os.rmdir(head)
275 except:
275 except:
276 break
276 break
277 head, tail = os.path.split(head)
277 head, tail = os.path.split(head)
278
278
279 def unlink(f):
279 def unlinkpath(f):
280 """unlink and remove the directory if it is empty"""
280 """unlink and remove the directory if it is empty"""
281 os.unlink(f)
281 os.unlink(f)
282 # try removing directories that might now be empty
282 # try removing directories that might now be empty
283 try:
283 try:
284 _removedirs(os.path.dirname(f))
284 _removedirs(os.path.dirname(f))
285 except OSError:
285 except OSError:
286 pass
286 pass
287
287
288 def rename(src, dst):
288 def rename(src, dst):
289 '''atomically rename file src to dst, replacing dst if it exists'''
289 '''atomically rename file src to dst, replacing dst if it exists'''
290 try:
290 try:
291 os.rename(src, dst)
291 os.rename(src, dst)
292 except OSError: # FIXME: check err (EEXIST ?)
292 except OSError: # FIXME: check err (EEXIST ?)
293
293
294 # On windows, rename to existing file is not allowed, so we
294 # On windows, rename to existing file is not allowed, so we
295 # must delete destination first. But if a file is open, unlink
295 # must delete destination first. But if a file is open, unlink
296 # schedules it for delete but does not delete it. Rename
296 # schedules it for delete but does not delete it. Rename
297 # happens immediately even for open files, so we rename
297 # happens immediately even for open files, so we rename
298 # destination to a temporary name, then delete that. Then
298 # destination to a temporary name, then delete that. Then
299 # rename is safe to do.
299 # rename is safe to do.
300 # The temporary name is chosen at random to avoid the situation
300 # The temporary name is chosen at random to avoid the situation
301 # where a file is left lying around from a previous aborted run.
301 # where a file is left lying around from a previous aborted run.
302
302
303 for tries in xrange(10):
303 for tries in xrange(10):
304 temp = '%s-%08x' % (dst, random.randint(0, 0xffffffff))
304 temp = '%s-%08x' % (dst, random.randint(0, 0xffffffff))
305 try:
305 try:
306 os.rename(dst, temp) # raises OSError EEXIST if temp exists
306 os.rename(dst, temp) # raises OSError EEXIST if temp exists
307 break
307 break
308 except OSError, e:
308 except OSError, e:
309 if e.errno != errno.EEXIST:
309 if e.errno != errno.EEXIST:
310 raise
310 raise
311 else:
311 else:
312 raise IOError, (errno.EEXIST, "No usable temporary filename found")
312 raise IOError, (errno.EEXIST, "No usable temporary filename found")
313
313
314 try:
314 try:
315 os.unlink(temp)
315 os.unlink(temp)
316 except:
316 except:
317 # Some rude AV-scanners on Windows may cause the unlink to
317 # Some rude AV-scanners on Windows may cause the unlink to
318 # fail. Not aborting here just leaks the temp file, whereas
318 # fail. Not aborting here just leaks the temp file, whereas
319 # aborting at this point may leave serious inconsistencies.
319 # aborting at this point may leave serious inconsistencies.
320 # Ideally, we would notify the user here.
320 # Ideally, we would notify the user here.
321 pass
321 pass
322 os.rename(src, dst)
322 os.rename(src, dst)
323
323
324 def spawndetached(args):
324 def spawndetached(args):
325 # No standard library function really spawns a fully detached
325 # No standard library function really spawns a fully detached
326 # process under win32 because they allocate pipes or other objects
326 # process under win32 because they allocate pipes or other objects
327 # to handle standard streams communications. Passing these objects
327 # to handle standard streams communications. Passing these objects
328 # to the child process requires handle inheritance to be enabled
328 # to the child process requires handle inheritance to be enabled
329 # which makes really detached processes impossible.
329 # which makes really detached processes impossible.
330 class STARTUPINFO:
330 class STARTUPINFO:
331 dwFlags = subprocess.STARTF_USESHOWWINDOW
331 dwFlags = subprocess.STARTF_USESHOWWINDOW
332 hStdInput = None
332 hStdInput = None
333 hStdOutput = None
333 hStdOutput = None
334 hStdError = None
334 hStdError = None
335 wShowWindow = subprocess.SW_HIDE
335 wShowWindow = subprocess.SW_HIDE
336
336
337 args = subprocess.list2cmdline(args)
337 args = subprocess.list2cmdline(args)
338 # Not running the command in shell mode makes python26 hang when
338 # Not running the command in shell mode makes python26 hang when
339 # writing to hgweb output socket.
339 # writing to hgweb output socket.
340 comspec = os.environ.get("COMSPEC", "cmd.exe")
340 comspec = os.environ.get("COMSPEC", "cmd.exe")
341 args = comspec + " /c " + args
341 args = comspec + " /c " + args
342 hp, ht, pid, tid = subprocess.CreateProcess(
342 hp, ht, pid, tid = subprocess.CreateProcess(
343 None, args,
343 None, args,
344 # no special security
344 # no special security
345 None, None,
345 None, None,
346 # Do not inherit handles
346 # Do not inherit handles
347 0,
347 0,
348 # DETACHED_PROCESS
348 # DETACHED_PROCESS
349 0x00000008,
349 0x00000008,
350 os.environ,
350 os.environ,
351 os.getcwd(),
351 os.getcwd(),
352 STARTUPINFO())
352 STARTUPINFO())
353 return pid
353 return pid
354
354
355 def gethgcmd():
355 def gethgcmd():
356 return [sys.executable] + sys.argv[:1]
356 return [sys.executable] + sys.argv[:1]
357
357
358 def termwidth():
358 def termwidth():
359 # cmd.exe does not handle CR like a unix console, the CR is
359 # cmd.exe does not handle CR like a unix console, the CR is
360 # counted in the line length. On 80 columns consoles, if 80
360 # counted in the line length. On 80 columns consoles, if 80
361 # characters are written, the following CR won't apply on the
361 # characters are written, the following CR won't apply on the
362 # current line but on the new one. Keep room for it.
362 # current line but on the new one. Keep room for it.
363 return 79
363 return 79
364
364
365 def groupmembers(name):
365 def groupmembers(name):
366 # Don't support groups on Windows for now
366 # Don't support groups on Windows for now
367 raise KeyError()
367 raise KeyError()
368
368
369 try:
369 try:
370 # override functions with win32 versions if possible
370 # override functions with win32 versions if possible
371 from win32 import *
371 from win32 import *
372 except ImportError:
372 except ImportError:
373 pass
373 pass
374
374
375 expandglobs = True
375 expandglobs = True
General Comments 0
You need to be logged in to leave comments. Login now