##// END OF EJS Templates
Merge with stable
Matt Mackall -
r4068:5b1f663e merge default
parent child Browse files
Show More
@@ -0,0 +1,42 b''
1 #!/bin/sh
2 # Test basic extension support
3
4 cat > foobar.py <<EOF
5 import os
6 from mercurial import commands
7
8 def uisetup(ui):
9 ui.write("uisetup called\\n")
10
11 def reposetup(ui, repo):
12 ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
13
14 def foo(ui, *args, **kwargs):
15 ui.write("Foo\\n")
16
17 def bar(ui, *args, **kwargs):
18 ui.write("Bar\\n")
19
20 cmdtable = {
21 "foo": (foo, [], "hg foo"),
22 "bar": (bar, [], "hg bar"),
23 }
24
25 commands.norepo += ' bar'
26 EOF
27 abspath=`pwd`/foobar.py
28
29 hg init a
30 cd a
31 echo foo > file
32 hg add file
33 hg commit -m 'add file'
34
35 echo '[extensions]' >> $HGRCPATH
36 echo "foobar = $abspath" >> $HGRCPATH
37 hg foo
38
39 cd ..
40 hg clone a b
41
42 hg bar
@@ -0,0 +1,9 b''
1 uisetup called
2 reposetup called for a
3 Foo
4 uisetup called
5 reposetup called for a
6 reposetup called for b
7 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
8 uisetup called
9 Bar
@@ -1,2188 +1,2189 b''
1 # queue.py - patch queues for mercurial
1 # queue.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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 '''patch management and development
8 '''patch management and development
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 prepare repository to work with patches qinit
19 prepare repository to work with patches qinit
20 create new patch qnew
20 create new patch qnew
21 import existing patch qimport
21 import existing patch qimport
22
22
23 print patch series qseries
23 print patch series qseries
24 print applied patches qapplied
24 print applied patches qapplied
25 print name of top applied patch qtop
25 print name of top applied patch qtop
26
26
27 add known patch to applied stack qpush
27 add known patch to applied stack qpush
28 remove patch from applied stack qpop
28 remove patch from applied stack qpop
29 refresh contents of top applied patch qrefresh
29 refresh contents of top applied patch qrefresh
30 '''
30 '''
31
31
32 from mercurial.i18n import _
32 from mercurial.i18n import _
33 from mercurial import commands, cmdutil, hg, patch, revlog, util, changegroup
33 from mercurial import commands, cmdutil, hg, patch, revlog, util, changegroup
34 import os, sys, re, errno
34 import os, sys, re, errno
35
35
36 commands.norepo += " qclone qversion"
36 commands.norepo += " qclone qversion"
37
37
38 # Patch names looks like unix-file names.
38 # Patch names looks like unix-file names.
39 # They must be joinable with queue directory and result in the patch path.
39 # They must be joinable with queue directory and result in the patch path.
40 normname = util.normpath
40 normname = util.normpath
41
41
42 class statusentry:
42 class statusentry:
43 def __init__(self, rev, name=None):
43 def __init__(self, rev, name=None):
44 if not name:
44 if not name:
45 fields = rev.split(':', 1)
45 fields = rev.split(':', 1)
46 if len(fields) == 2:
46 if len(fields) == 2:
47 self.rev, self.name = fields
47 self.rev, self.name = fields
48 else:
48 else:
49 self.rev, self.name = None, None
49 self.rev, self.name = None, None
50 else:
50 else:
51 self.rev, self.name = rev, name
51 self.rev, self.name = rev, name
52
52
53 def __str__(self):
53 def __str__(self):
54 return self.rev + ':' + self.name
54 return self.rev + ':' + self.name
55
55
56 class queue:
56 class queue:
57 def __init__(self, ui, path, patchdir=None):
57 def __init__(self, ui, path, patchdir=None):
58 self.basepath = path
58 self.basepath = path
59 self.path = patchdir or os.path.join(path, "patches")
59 self.path = patchdir or os.path.join(path, "patches")
60 self.opener = util.opener(self.path)
60 self.opener = util.opener(self.path)
61 self.ui = ui
61 self.ui = ui
62 self.applied = []
62 self.applied = []
63 self.full_series = []
63 self.full_series = []
64 self.applied_dirty = 0
64 self.applied_dirty = 0
65 self.series_dirty = 0
65 self.series_dirty = 0
66 self.series_path = "series"
66 self.series_path = "series"
67 self.status_path = "status"
67 self.status_path = "status"
68 self.guards_path = "guards"
68 self.guards_path = "guards"
69 self.active_guards = None
69 self.active_guards = None
70 self.guards_dirty = False
70 self.guards_dirty = False
71 self._diffopts = None
71 self._diffopts = None
72
72
73 if os.path.exists(self.join(self.series_path)):
73 if os.path.exists(self.join(self.series_path)):
74 self.full_series = self.opener(self.series_path).read().splitlines()
74 self.full_series = self.opener(self.series_path).read().splitlines()
75 self.parse_series()
75 self.parse_series()
76
76
77 if os.path.exists(self.join(self.status_path)):
77 if os.path.exists(self.join(self.status_path)):
78 lines = self.opener(self.status_path).read().splitlines()
78 lines = self.opener(self.status_path).read().splitlines()
79 self.applied = [statusentry(l) for l in lines]
79 self.applied = [statusentry(l) for l in lines]
80
80
81 def diffopts(self):
81 def diffopts(self):
82 if self._diffopts is None:
82 if self._diffopts is None:
83 self._diffopts = patch.diffopts(self.ui)
83 self._diffopts = patch.diffopts(self.ui)
84 return self._diffopts
84 return self._diffopts
85
85
86 def join(self, *p):
86 def join(self, *p):
87 return os.path.join(self.path, *p)
87 return os.path.join(self.path, *p)
88
88
89 def find_series(self, patch):
89 def find_series(self, patch):
90 pre = re.compile("(\s*)([^#]+)")
90 pre = re.compile("(\s*)([^#]+)")
91 index = 0
91 index = 0
92 for l in self.full_series:
92 for l in self.full_series:
93 m = pre.match(l)
93 m = pre.match(l)
94 if m:
94 if m:
95 s = m.group(2)
95 s = m.group(2)
96 s = s.rstrip()
96 s = s.rstrip()
97 if s == patch:
97 if s == patch:
98 return index
98 return index
99 index += 1
99 index += 1
100 return None
100 return None
101
101
102 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
102 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
103
103
104 def parse_series(self):
104 def parse_series(self):
105 self.series = []
105 self.series = []
106 self.series_guards = []
106 self.series_guards = []
107 for l in self.full_series:
107 for l in self.full_series:
108 h = l.find('#')
108 h = l.find('#')
109 if h == -1:
109 if h == -1:
110 patch = l
110 patch = l
111 comment = ''
111 comment = ''
112 elif h == 0:
112 elif h == 0:
113 continue
113 continue
114 else:
114 else:
115 patch = l[:h]
115 patch = l[:h]
116 comment = l[h:]
116 comment = l[h:]
117 patch = patch.strip()
117 patch = patch.strip()
118 if patch:
118 if patch:
119 if patch in self.series:
119 if patch in self.series:
120 raise util.Abort(_('%s appears more than once in %s') %
120 raise util.Abort(_('%s appears more than once in %s') %
121 (patch, self.join(self.series_path)))
121 (patch, self.join(self.series_path)))
122 self.series.append(patch)
122 self.series.append(patch)
123 self.series_guards.append(self.guard_re.findall(comment))
123 self.series_guards.append(self.guard_re.findall(comment))
124
124
125 def check_guard(self, guard):
125 def check_guard(self, guard):
126 bad_chars = '# \t\r\n\f'
126 bad_chars = '# \t\r\n\f'
127 first = guard[0]
127 first = guard[0]
128 for c in '-+':
128 for c in '-+':
129 if first == c:
129 if first == c:
130 return (_('guard %r starts with invalid character: %r') %
130 return (_('guard %r starts with invalid character: %r') %
131 (guard, c))
131 (guard, c))
132 for c in bad_chars:
132 for c in bad_chars:
133 if c in guard:
133 if c in guard:
134 return _('invalid character in guard %r: %r') % (guard, c)
134 return _('invalid character in guard %r: %r') % (guard, c)
135
135
136 def set_active(self, guards):
136 def set_active(self, guards):
137 for guard in guards:
137 for guard in guards:
138 bad = self.check_guard(guard)
138 bad = self.check_guard(guard)
139 if bad:
139 if bad:
140 raise util.Abort(bad)
140 raise util.Abort(bad)
141 guards = dict.fromkeys(guards).keys()
141 guards = dict.fromkeys(guards).keys()
142 guards.sort()
142 guards.sort()
143 self.ui.debug('active guards: %s\n' % ' '.join(guards))
143 self.ui.debug('active guards: %s\n' % ' '.join(guards))
144 self.active_guards = guards
144 self.active_guards = guards
145 self.guards_dirty = True
145 self.guards_dirty = True
146
146
147 def active(self):
147 def active(self):
148 if self.active_guards is None:
148 if self.active_guards is None:
149 self.active_guards = []
149 self.active_guards = []
150 try:
150 try:
151 guards = self.opener(self.guards_path).read().split()
151 guards = self.opener(self.guards_path).read().split()
152 except IOError, err:
152 except IOError, err:
153 if err.errno != errno.ENOENT: raise
153 if err.errno != errno.ENOENT: raise
154 guards = []
154 guards = []
155 for i, guard in enumerate(guards):
155 for i, guard in enumerate(guards):
156 bad = self.check_guard(guard)
156 bad = self.check_guard(guard)
157 if bad:
157 if bad:
158 self.ui.warn('%s:%d: %s\n' %
158 self.ui.warn('%s:%d: %s\n' %
159 (self.join(self.guards_path), i + 1, bad))
159 (self.join(self.guards_path), i + 1, bad))
160 else:
160 else:
161 self.active_guards.append(guard)
161 self.active_guards.append(guard)
162 return self.active_guards
162 return self.active_guards
163
163
164 def set_guards(self, idx, guards):
164 def set_guards(self, idx, guards):
165 for g in guards:
165 for g in guards:
166 if len(g) < 2:
166 if len(g) < 2:
167 raise util.Abort(_('guard %r too short') % g)
167 raise util.Abort(_('guard %r too short') % g)
168 if g[0] not in '-+':
168 if g[0] not in '-+':
169 raise util.Abort(_('guard %r starts with invalid char') % g)
169 raise util.Abort(_('guard %r starts with invalid char') % g)
170 bad = self.check_guard(g[1:])
170 bad = self.check_guard(g[1:])
171 if bad:
171 if bad:
172 raise util.Abort(bad)
172 raise util.Abort(bad)
173 drop = self.guard_re.sub('', self.full_series[idx])
173 drop = self.guard_re.sub('', self.full_series[idx])
174 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
174 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
175 self.parse_series()
175 self.parse_series()
176 self.series_dirty = True
176 self.series_dirty = True
177
177
178 def pushable(self, idx):
178 def pushable(self, idx):
179 if isinstance(idx, str):
179 if isinstance(idx, str):
180 idx = self.series.index(idx)
180 idx = self.series.index(idx)
181 patchguards = self.series_guards[idx]
181 patchguards = self.series_guards[idx]
182 if not patchguards:
182 if not patchguards:
183 return True, None
183 return True, None
184 default = False
184 default = False
185 guards = self.active()
185 guards = self.active()
186 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
186 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
187 if exactneg:
187 if exactneg:
188 return False, exactneg[0]
188 return False, exactneg[0]
189 pos = [g for g in patchguards if g[0] == '+']
189 pos = [g for g in patchguards if g[0] == '+']
190 exactpos = [g for g in pos if g[1:] in guards]
190 exactpos = [g for g in pos if g[1:] in guards]
191 if pos:
191 if pos:
192 if exactpos:
192 if exactpos:
193 return True, exactpos[0]
193 return True, exactpos[0]
194 return False, pos
194 return False, pos
195 return True, ''
195 return True, ''
196
196
197 def explain_pushable(self, idx, all_patches=False):
197 def explain_pushable(self, idx, all_patches=False):
198 write = all_patches and self.ui.write or self.ui.warn
198 write = all_patches and self.ui.write or self.ui.warn
199 if all_patches or self.ui.verbose:
199 if all_patches or self.ui.verbose:
200 if isinstance(idx, str):
200 if isinstance(idx, str):
201 idx = self.series.index(idx)
201 idx = self.series.index(idx)
202 pushable, why = self.pushable(idx)
202 pushable, why = self.pushable(idx)
203 if all_patches and pushable:
203 if all_patches and pushable:
204 if why is None:
204 if why is None:
205 write(_('allowing %s - no guards in effect\n') %
205 write(_('allowing %s - no guards in effect\n') %
206 self.series[idx])
206 self.series[idx])
207 else:
207 else:
208 if not why:
208 if not why:
209 write(_('allowing %s - no matching negative guards\n') %
209 write(_('allowing %s - no matching negative guards\n') %
210 self.series[idx])
210 self.series[idx])
211 else:
211 else:
212 write(_('allowing %s - guarded by %r\n') %
212 write(_('allowing %s - guarded by %r\n') %
213 (self.series[idx], why))
213 (self.series[idx], why))
214 if not pushable:
214 if not pushable:
215 if why:
215 if why:
216 write(_('skipping %s - guarded by %r\n') %
216 write(_('skipping %s - guarded by %r\n') %
217 (self.series[idx], why))
217 (self.series[idx], why))
218 else:
218 else:
219 write(_('skipping %s - no matching guards\n') %
219 write(_('skipping %s - no matching guards\n') %
220 self.series[idx])
220 self.series[idx])
221
221
222 def save_dirty(self):
222 def save_dirty(self):
223 def write_list(items, path):
223 def write_list(items, path):
224 fp = self.opener(path, 'w')
224 fp = self.opener(path, 'w')
225 for i in items:
225 for i in items:
226 print >> fp, i
226 print >> fp, i
227 fp.close()
227 fp.close()
228 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
228 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
229 if self.series_dirty: write_list(self.full_series, self.series_path)
229 if self.series_dirty: write_list(self.full_series, self.series_path)
230 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
230 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
231
231
232 def readheaders(self, patch):
232 def readheaders(self, patch):
233 def eatdiff(lines):
233 def eatdiff(lines):
234 while lines:
234 while lines:
235 l = lines[-1]
235 l = lines[-1]
236 if (l.startswith("diff -") or
236 if (l.startswith("diff -") or
237 l.startswith("Index:") or
237 l.startswith("Index:") or
238 l.startswith("===========")):
238 l.startswith("===========")):
239 del lines[-1]
239 del lines[-1]
240 else:
240 else:
241 break
241 break
242 def eatempty(lines):
242 def eatempty(lines):
243 while lines:
243 while lines:
244 l = lines[-1]
244 l = lines[-1]
245 if re.match('\s*$', l):
245 if re.match('\s*$', l):
246 del lines[-1]
246 del lines[-1]
247 else:
247 else:
248 break
248 break
249
249
250 pf = self.join(patch)
250 pf = self.join(patch)
251 message = []
251 message = []
252 comments = []
252 comments = []
253 user = None
253 user = None
254 date = None
254 date = None
255 format = None
255 format = None
256 subject = None
256 subject = None
257 diffstart = 0
257 diffstart = 0
258
258
259 for line in file(pf):
259 for line in file(pf):
260 line = line.rstrip()
260 line = line.rstrip()
261 if line.startswith('diff --git'):
261 if line.startswith('diff --git'):
262 diffstart = 2
262 diffstart = 2
263 break
263 break
264 if diffstart:
264 if diffstart:
265 if line.startswith('+++ '):
265 if line.startswith('+++ '):
266 diffstart = 2
266 diffstart = 2
267 break
267 break
268 if line.startswith("--- "):
268 if line.startswith("--- "):
269 diffstart = 1
269 diffstart = 1
270 continue
270 continue
271 elif format == "hgpatch":
271 elif format == "hgpatch":
272 # parse values when importing the result of an hg export
272 # parse values when importing the result of an hg export
273 if line.startswith("# User "):
273 if line.startswith("# User "):
274 user = line[7:]
274 user = line[7:]
275 elif line.startswith("# Date "):
275 elif line.startswith("# Date "):
276 date = line[7:]
276 date = line[7:]
277 elif not line.startswith("# ") and line:
277 elif not line.startswith("# ") and line:
278 message.append(line)
278 message.append(line)
279 format = None
279 format = None
280 elif line == '# HG changeset patch':
280 elif line == '# HG changeset patch':
281 format = "hgpatch"
281 format = "hgpatch"
282 elif (format != "tagdone" and (line.startswith("Subject: ") or
282 elif (format != "tagdone" and (line.startswith("Subject: ") or
283 line.startswith("subject: "))):
283 line.startswith("subject: "))):
284 subject = line[9:]
284 subject = line[9:]
285 format = "tag"
285 format = "tag"
286 elif (format != "tagdone" and (line.startswith("From: ") or
286 elif (format != "tagdone" and (line.startswith("From: ") or
287 line.startswith("from: "))):
287 line.startswith("from: "))):
288 user = line[6:]
288 user = line[6:]
289 format = "tag"
289 format = "tag"
290 elif format == "tag" and line == "":
290 elif format == "tag" and line == "":
291 # when looking for tags (subject: from: etc) they
291 # when looking for tags (subject: from: etc) they
292 # end once you find a blank line in the source
292 # end once you find a blank line in the source
293 format = "tagdone"
293 format = "tagdone"
294 elif message or line:
294 elif message or line:
295 message.append(line)
295 message.append(line)
296 comments.append(line)
296 comments.append(line)
297
297
298 eatdiff(message)
298 eatdiff(message)
299 eatdiff(comments)
299 eatdiff(comments)
300 eatempty(message)
300 eatempty(message)
301 eatempty(comments)
301 eatempty(comments)
302
302
303 # make sure message isn't empty
303 # make sure message isn't empty
304 if format and format.startswith("tag") and subject:
304 if format and format.startswith("tag") and subject:
305 message.insert(0, "")
305 message.insert(0, "")
306 message.insert(0, subject)
306 message.insert(0, subject)
307 return (message, comments, user, date, diffstart > 1)
307 return (message, comments, user, date, diffstart > 1)
308
308
309 def printdiff(self, repo, node1, node2=None, files=None,
309 def printdiff(self, repo, node1, node2=None, files=None,
310 fp=None, changes=None, opts={}):
310 fp=None, changes=None, opts={}):
311 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
311 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
312
312
313 patch.diff(repo, node1, node2, fns, match=matchfn,
313 patch.diff(repo, node1, node2, fns, match=matchfn,
314 fp=fp, changes=changes, opts=self.diffopts())
314 fp=fp, changes=changes, opts=self.diffopts())
315
315
316 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
316 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
317 # first try just applying the patch
317 # first try just applying the patch
318 (err, n) = self.apply(repo, [ patch ], update_status=False,
318 (err, n) = self.apply(repo, [ patch ], update_status=False,
319 strict=True, merge=rev, wlock=wlock)
319 strict=True, merge=rev, wlock=wlock)
320
320
321 if err == 0:
321 if err == 0:
322 return (err, n)
322 return (err, n)
323
323
324 if n is None:
324 if n is None:
325 raise util.Abort(_("apply failed for patch %s") % patch)
325 raise util.Abort(_("apply failed for patch %s") % patch)
326
326
327 self.ui.warn("patch didn't work out, merging %s\n" % patch)
327 self.ui.warn("patch didn't work out, merging %s\n" % patch)
328
328
329 # apply failed, strip away that rev and merge.
329 # apply failed, strip away that rev and merge.
330 hg.clean(repo, head, wlock=wlock)
330 hg.clean(repo, head, wlock=wlock)
331 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
331 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
332
332
333 ctx = repo.changectx(rev)
333 ctx = repo.changectx(rev)
334 ret = hg.merge(repo, rev, wlock=wlock)
334 ret = hg.merge(repo, rev, wlock=wlock)
335 if ret:
335 if ret:
336 raise util.Abort(_("update returned %d") % ret)
336 raise util.Abort(_("update returned %d") % ret)
337 n = repo.commit(None, ctx.description(), ctx.user(),
337 n = repo.commit(None, ctx.description(), ctx.user(),
338 force=1, wlock=wlock)
338 force=1, wlock=wlock)
339 if n == None:
339 if n == None:
340 raise util.Abort(_("repo commit failed"))
340 raise util.Abort(_("repo commit failed"))
341 try:
341 try:
342 message, comments, user, date, patchfound = mergeq.readheaders(patch)
342 message, comments, user, date, patchfound = mergeq.readheaders(patch)
343 except:
343 except:
344 raise util.Abort(_("unable to read %s") % patch)
344 raise util.Abort(_("unable to read %s") % patch)
345
345
346 patchf = self.opener(patch, "w")
346 patchf = self.opener(patch, "w")
347 if comments:
347 if comments:
348 comments = "\n".join(comments) + '\n\n'
348 comments = "\n".join(comments) + '\n\n'
349 patchf.write(comments)
349 patchf.write(comments)
350 self.printdiff(repo, head, n, fp=patchf)
350 self.printdiff(repo, head, n, fp=patchf)
351 patchf.close()
351 patchf.close()
352 return (0, n)
352 return (0, n)
353
353
354 def qparents(self, repo, rev=None):
354 def qparents(self, repo, rev=None):
355 if rev is None:
355 if rev is None:
356 (p1, p2) = repo.dirstate.parents()
356 (p1, p2) = repo.dirstate.parents()
357 if p2 == revlog.nullid:
357 if p2 == revlog.nullid:
358 return p1
358 return p1
359 if len(self.applied) == 0:
359 if len(self.applied) == 0:
360 return None
360 return None
361 return revlog.bin(self.applied[-1].rev)
361 return revlog.bin(self.applied[-1].rev)
362 pp = repo.changelog.parents(rev)
362 pp = repo.changelog.parents(rev)
363 if pp[1] != revlog.nullid:
363 if pp[1] != revlog.nullid:
364 arevs = [ x.rev for x in self.applied ]
364 arevs = [ x.rev for x in self.applied ]
365 p0 = revlog.hex(pp[0])
365 p0 = revlog.hex(pp[0])
366 p1 = revlog.hex(pp[1])
366 p1 = revlog.hex(pp[1])
367 if p0 in arevs:
367 if p0 in arevs:
368 return pp[0]
368 return pp[0]
369 if p1 in arevs:
369 if p1 in arevs:
370 return pp[1]
370 return pp[1]
371 return pp[0]
371 return pp[0]
372
372
373 def mergepatch(self, repo, mergeq, series, wlock):
373 def mergepatch(self, repo, mergeq, series, wlock):
374 if len(self.applied) == 0:
374 if len(self.applied) == 0:
375 # each of the patches merged in will have two parents. This
375 # each of the patches merged in will have two parents. This
376 # can confuse the qrefresh, qdiff, and strip code because it
376 # can confuse the qrefresh, qdiff, and strip code because it
377 # needs to know which parent is actually in the patch queue.
377 # needs to know which parent is actually in the patch queue.
378 # so, we insert a merge marker with only one parent. This way
378 # so, we insert a merge marker with only one parent. This way
379 # the first patch in the queue is never a merge patch
379 # the first patch in the queue is never a merge patch
380 #
380 #
381 pname = ".hg.patches.merge.marker"
381 pname = ".hg.patches.merge.marker"
382 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
382 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
383 wlock=wlock)
383 wlock=wlock)
384 self.applied.append(statusentry(revlog.hex(n), pname))
384 self.applied.append(statusentry(revlog.hex(n), pname))
385 self.applied_dirty = 1
385 self.applied_dirty = 1
386
386
387 head = self.qparents(repo)
387 head = self.qparents(repo)
388
388
389 for patch in series:
389 for patch in series:
390 patch = mergeq.lookup(patch, strict=True)
390 patch = mergeq.lookup(patch, strict=True)
391 if not patch:
391 if not patch:
392 self.ui.warn("patch %s does not exist\n" % patch)
392 self.ui.warn("patch %s does not exist\n" % patch)
393 return (1, None)
393 return (1, None)
394 pushable, reason = self.pushable(patch)
394 pushable, reason = self.pushable(patch)
395 if not pushable:
395 if not pushable:
396 self.explain_pushable(patch, all_patches=True)
396 self.explain_pushable(patch, all_patches=True)
397 continue
397 continue
398 info = mergeq.isapplied(patch)
398 info = mergeq.isapplied(patch)
399 if not info:
399 if not info:
400 self.ui.warn("patch %s is not applied\n" % patch)
400 self.ui.warn("patch %s is not applied\n" % patch)
401 return (1, None)
401 return (1, None)
402 rev = revlog.bin(info[1])
402 rev = revlog.bin(info[1])
403 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
403 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
404 if head:
404 if head:
405 self.applied.append(statusentry(revlog.hex(head), patch))
405 self.applied.append(statusentry(revlog.hex(head), patch))
406 self.applied_dirty = 1
406 self.applied_dirty = 1
407 if err:
407 if err:
408 return (err, head)
408 return (err, head)
409 return (0, head)
409 return (0, head)
410
410
411 def patch(self, repo, patchfile):
411 def patch(self, repo, patchfile):
412 '''Apply patchfile to the working directory.
412 '''Apply patchfile to the working directory.
413 patchfile: file name of patch'''
413 patchfile: file name of patch'''
414 files = {}
414 files = {}
415 try:
415 try:
416 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
416 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
417 files=files)
417 files=files)
418 except Exception, inst:
418 except Exception, inst:
419 self.ui.note(str(inst) + '\n')
419 self.ui.note(str(inst) + '\n')
420 if not self.ui.verbose:
420 if not self.ui.verbose:
421 self.ui.warn("patch failed, unable to continue (try -v)\n")
421 self.ui.warn("patch failed, unable to continue (try -v)\n")
422 return (False, files, False)
422 return (False, files, False)
423
423
424 return (True, files, fuzz)
424 return (True, files, fuzz)
425
425
426 def apply(self, repo, series, list=False, update_status=True,
426 def apply(self, repo, series, list=False, update_status=True,
427 strict=False, patchdir=None, merge=None, wlock=None):
427 strict=False, patchdir=None, merge=None, wlock=None):
428 # TODO unify with commands.py
428 # TODO unify with commands.py
429 if not patchdir:
429 if not patchdir:
430 patchdir = self.path
430 patchdir = self.path
431 err = 0
431 err = 0
432 if not wlock:
432 if not wlock:
433 wlock = repo.wlock()
433 wlock = repo.wlock()
434 lock = repo.lock()
434 lock = repo.lock()
435 tr = repo.transaction()
435 tr = repo.transaction()
436 n = None
436 n = None
437 for patchname in series:
437 for patchname in series:
438 pushable, reason = self.pushable(patchname)
438 pushable, reason = self.pushable(patchname)
439 if not pushable:
439 if not pushable:
440 self.explain_pushable(patchname, all_patches=True)
440 self.explain_pushable(patchname, all_patches=True)
441 continue
441 continue
442 self.ui.warn("applying %s\n" % patchname)
442 self.ui.warn("applying %s\n" % patchname)
443 pf = os.path.join(patchdir, patchname)
443 pf = os.path.join(patchdir, patchname)
444
444
445 try:
445 try:
446 message, comments, user, date, patchfound = self.readheaders(patchname)
446 message, comments, user, date, patchfound = self.readheaders(patchname)
447 except:
447 except:
448 self.ui.warn("Unable to read %s\n" % patchname)
448 self.ui.warn("Unable to read %s\n" % patchname)
449 err = 1
449 err = 1
450 break
450 break
451
451
452 if not message:
452 if not message:
453 message = "imported patch %s\n" % patchname
453 message = "imported patch %s\n" % patchname
454 else:
454 else:
455 if list:
455 if list:
456 message.append("\nimported patch %s" % patchname)
456 message.append("\nimported patch %s" % patchname)
457 message = '\n'.join(message)
457 message = '\n'.join(message)
458
458
459 (patcherr, files, fuzz) = self.patch(repo, pf)
459 (patcherr, files, fuzz) = self.patch(repo, pf)
460 patcherr = not patcherr
460 patcherr = not patcherr
461
461
462 if merge and files:
462 if merge and files:
463 # Mark as merged and update dirstate parent info
463 # Mark as merged and update dirstate parent info
464 repo.dirstate.update(repo.dirstate.filterfiles(files.keys()), 'm')
464 repo.dirstate.update(repo.dirstate.filterfiles(files.keys()), 'm')
465 p1, p2 = repo.dirstate.parents()
465 p1, p2 = repo.dirstate.parents()
466 repo.dirstate.setparents(p1, merge)
466 repo.dirstate.setparents(p1, merge)
467 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
467 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
468 n = repo.commit(files, message, user, date, force=1, lock=lock,
468 n = repo.commit(files, message, user, date, force=1, lock=lock,
469 wlock=wlock)
469 wlock=wlock)
470
470
471 if n == None:
471 if n == None:
472 raise util.Abort(_("repo commit failed"))
472 raise util.Abort(_("repo commit failed"))
473
473
474 if update_status:
474 if update_status:
475 self.applied.append(statusentry(revlog.hex(n), patchname))
475 self.applied.append(statusentry(revlog.hex(n), patchname))
476
476
477 if patcherr:
477 if patcherr:
478 if not patchfound:
478 if not patchfound:
479 self.ui.warn("patch %s is empty\n" % patchname)
479 self.ui.warn("patch %s is empty\n" % patchname)
480 err = 0
480 err = 0
481 else:
481 else:
482 self.ui.warn("patch failed, rejects left in working dir\n")
482 self.ui.warn("patch failed, rejects left in working dir\n")
483 err = 1
483 err = 1
484 break
484 break
485
485
486 if fuzz and strict:
486 if fuzz and strict:
487 self.ui.warn("fuzz found when applying patch, stopping\n")
487 self.ui.warn("fuzz found when applying patch, stopping\n")
488 err = 1
488 err = 1
489 break
489 break
490 tr.close()
490 tr.close()
491 return (err, n)
491 return (err, n)
492
492
493 def delete(self, repo, patches, opts):
493 def delete(self, repo, patches, opts):
494 realpatches = []
494 realpatches = []
495 for patch in patches:
495 for patch in patches:
496 patch = self.lookup(patch, strict=True)
496 patch = self.lookup(patch, strict=True)
497 info = self.isapplied(patch)
497 info = self.isapplied(patch)
498 if info:
498 if info:
499 raise util.Abort(_("cannot delete applied patch %s") % patch)
499 raise util.Abort(_("cannot delete applied patch %s") % patch)
500 if patch not in self.series:
500 if patch not in self.series:
501 raise util.Abort(_("patch %s not in series file") % patch)
501 raise util.Abort(_("patch %s not in series file") % patch)
502 realpatches.append(patch)
502 realpatches.append(patch)
503
503
504 appliedbase = 0
504 appliedbase = 0
505 if opts.get('rev'):
505 if opts.get('rev'):
506 if not self.applied:
506 if not self.applied:
507 raise util.Abort(_('no patches applied'))
507 raise util.Abort(_('no patches applied'))
508 revs = cmdutil.revrange(repo, opts['rev'])
508 revs = cmdutil.revrange(repo, opts['rev'])
509 if len(revs) > 1 and revs[0] > revs[1]:
509 if len(revs) > 1 and revs[0] > revs[1]:
510 revs.reverse()
510 revs.reverse()
511 for rev in revs:
511 for rev in revs:
512 if appliedbase >= len(self.applied):
512 if appliedbase >= len(self.applied):
513 raise util.Abort(_("revision %d is not managed") % rev)
513 raise util.Abort(_("revision %d is not managed") % rev)
514
514
515 base = revlog.bin(self.applied[appliedbase].rev)
515 base = revlog.bin(self.applied[appliedbase].rev)
516 node = repo.changelog.node(rev)
516 node = repo.changelog.node(rev)
517 if node != base:
517 if node != base:
518 raise util.Abort(_("cannot delete revision %d above "
518 raise util.Abort(_("cannot delete revision %d above "
519 "applied patches") % rev)
519 "applied patches") % rev)
520 realpatches.append(self.applied[appliedbase].name)
520 realpatches.append(self.applied[appliedbase].name)
521 appliedbase += 1
521 appliedbase += 1
522
522
523 if not opts.get('keep'):
523 if not opts.get('keep'):
524 r = self.qrepo()
524 r = self.qrepo()
525 if r:
525 if r:
526 r.remove(realpatches, True)
526 r.remove(realpatches, True)
527 else:
527 else:
528 for p in realpatches:
528 for p in realpatches:
529 os.unlink(self.join(p))
529 os.unlink(self.join(p))
530
530
531 if appliedbase:
531 if appliedbase:
532 del self.applied[:appliedbase]
532 del self.applied[:appliedbase]
533 self.applied_dirty = 1
533 self.applied_dirty = 1
534 indices = [self.find_series(p) for p in realpatches]
534 indices = [self.find_series(p) for p in realpatches]
535 indices.sort()
535 indices.sort()
536 for i in indices[-1::-1]:
536 for i in indices[-1::-1]:
537 del self.full_series[i]
537 del self.full_series[i]
538 self.parse_series()
538 self.parse_series()
539 self.series_dirty = 1
539 self.series_dirty = 1
540
540
541 def check_toppatch(self, repo):
541 def check_toppatch(self, repo):
542 if len(self.applied) > 0:
542 if len(self.applied) > 0:
543 top = revlog.bin(self.applied[-1].rev)
543 top = revlog.bin(self.applied[-1].rev)
544 pp = repo.dirstate.parents()
544 pp = repo.dirstate.parents()
545 if top not in pp:
545 if top not in pp:
546 raise util.Abort(_("queue top not at same revision as working directory"))
546 raise util.Abort(_("queue top not at same revision as working directory"))
547 return top
547 return top
548 return None
548 return None
549 def check_localchanges(self, repo, force=False, refresh=True):
549 def check_localchanges(self, repo, force=False, refresh=True):
550 m, a, r, d = repo.status()[:4]
550 m, a, r, d = repo.status()[:4]
551 if m or a or r or d:
551 if m or a or r or d:
552 if not force:
552 if not force:
553 if refresh:
553 if refresh:
554 raise util.Abort(_("local changes found, refresh first"))
554 raise util.Abort(_("local changes found, refresh first"))
555 else:
555 else:
556 raise util.Abort(_("local changes found"))
556 raise util.Abort(_("local changes found"))
557 return m, a, r, d
557 return m, a, r, d
558 def new(self, repo, patch, msg=None, force=None):
558 def new(self, repo, patch, msg=None, force=None):
559 if os.path.exists(self.join(patch)):
559 if os.path.exists(self.join(patch)):
560 raise util.Abort(_('patch "%s" already exists') % patch)
560 raise util.Abort(_('patch "%s" already exists') % patch)
561 m, a, r, d = self.check_localchanges(repo, force)
561 m, a, r, d = self.check_localchanges(repo, force)
562 commitfiles = m + a + r
562 commitfiles = m + a + r
563 self.check_toppatch(repo)
563 self.check_toppatch(repo)
564 wlock = repo.wlock()
564 wlock = repo.wlock()
565 insert = self.full_series_end()
565 insert = self.full_series_end()
566 if msg:
566 if msg:
567 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
567 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
568 wlock=wlock)
568 wlock=wlock)
569 else:
569 else:
570 n = repo.commit(commitfiles,
570 n = repo.commit(commitfiles,
571 "New patch: %s" % patch, force=True, wlock=wlock)
571 "New patch: %s" % patch, force=True, wlock=wlock)
572 if n == None:
572 if n == None:
573 raise util.Abort(_("repo commit failed"))
573 raise util.Abort(_("repo commit failed"))
574 self.full_series[insert:insert] = [patch]
574 self.full_series[insert:insert] = [patch]
575 self.applied.append(statusentry(revlog.hex(n), patch))
575 self.applied.append(statusentry(revlog.hex(n), patch))
576 self.parse_series()
576 self.parse_series()
577 self.series_dirty = 1
577 self.series_dirty = 1
578 self.applied_dirty = 1
578 self.applied_dirty = 1
579 p = self.opener(patch, "w")
579 p = self.opener(patch, "w")
580 if msg:
580 if msg:
581 msg = msg + "\n"
581 msg = msg + "\n"
582 p.write(msg)
582 p.write(msg)
583 p.close()
583 p.close()
584 wlock = None
584 wlock = None
585 r = self.qrepo()
585 r = self.qrepo()
586 if r: r.add([patch])
586 if r: r.add([patch])
587 if commitfiles:
587 if commitfiles:
588 self.refresh(repo, short=True)
588 self.refresh(repo, short=True)
589
589
590 def strip(self, repo, rev, update=True, backup="all", wlock=None):
590 def strip(self, repo, rev, update=True, backup="all", wlock=None):
591 def limitheads(chlog, stop):
591 def limitheads(chlog, stop):
592 """return the list of all nodes that have no children"""
592 """return the list of all nodes that have no children"""
593 p = {}
593 p = {}
594 h = []
594 h = []
595 stoprev = 0
595 stoprev = 0
596 if stop in chlog.nodemap:
596 if stop in chlog.nodemap:
597 stoprev = chlog.rev(stop)
597 stoprev = chlog.rev(stop)
598
598
599 for r in xrange(chlog.count() - 1, -1, -1):
599 for r in xrange(chlog.count() - 1, -1, -1):
600 n = chlog.node(r)
600 n = chlog.node(r)
601 if n not in p:
601 if n not in p:
602 h.append(n)
602 h.append(n)
603 if n == stop:
603 if n == stop:
604 break
604 break
605 if r < stoprev:
605 if r < stoprev:
606 break
606 break
607 for pn in chlog.parents(n):
607 for pn in chlog.parents(n):
608 p[pn] = 1
608 p[pn] = 1
609 return h
609 return h
610
610
611 def bundle(cg):
611 def bundle(cg):
612 backupdir = repo.join("strip-backup")
612 backupdir = repo.join("strip-backup")
613 if not os.path.isdir(backupdir):
613 if not os.path.isdir(backupdir):
614 os.mkdir(backupdir)
614 os.mkdir(backupdir)
615 name = os.path.join(backupdir, "%s" % revlog.short(rev))
615 name = os.path.join(backupdir, "%s" % revlog.short(rev))
616 name = savename(name)
616 name = savename(name)
617 self.ui.warn("saving bundle to %s\n" % name)
617 self.ui.warn("saving bundle to %s\n" % name)
618 return changegroup.writebundle(cg, name, "HG10BZ")
618 return changegroup.writebundle(cg, name, "HG10BZ")
619
619
620 def stripall(revnum):
620 def stripall(revnum):
621 mm = repo.changectx(rev).manifest()
621 mm = repo.changectx(rev).manifest()
622 seen = {}
622 seen = {}
623
623
624 for x in xrange(revnum, repo.changelog.count()):
624 for x in xrange(revnum, repo.changelog.count()):
625 for f in repo.changectx(x).files():
625 for f in repo.changectx(x).files():
626 if f in seen:
626 if f in seen:
627 continue
627 continue
628 seen[f] = 1
628 seen[f] = 1
629 if f in mm:
629 if f in mm:
630 filerev = mm[f]
630 filerev = mm[f]
631 else:
631 else:
632 filerev = 0
632 filerev = 0
633 seen[f] = filerev
633 seen[f] = filerev
634 # we go in two steps here so the strip loop happens in a
634 # we go in two steps here so the strip loop happens in a
635 # sensible order. When stripping many files, this helps keep
635 # sensible order. When stripping many files, this helps keep
636 # our disk access patterns under control.
636 # our disk access patterns under control.
637 seen_list = seen.keys()
637 seen_list = seen.keys()
638 seen_list.sort()
638 seen_list.sort()
639 for f in seen_list:
639 for f in seen_list:
640 ff = repo.file(f)
640 ff = repo.file(f)
641 filerev = seen[f]
641 filerev = seen[f]
642 if filerev != 0:
642 if filerev != 0:
643 if filerev in ff.nodemap:
643 if filerev in ff.nodemap:
644 filerev = ff.rev(filerev)
644 filerev = ff.rev(filerev)
645 else:
645 else:
646 filerev = 0
646 filerev = 0
647 ff.strip(filerev, revnum)
647 ff.strip(filerev, revnum)
648
648
649 if not wlock:
649 if not wlock:
650 wlock = repo.wlock()
650 wlock = repo.wlock()
651 lock = repo.lock()
651 lock = repo.lock()
652 chlog = repo.changelog
652 chlog = repo.changelog
653 # TODO delete the undo files, and handle undo of merge sets
653 # TODO delete the undo files, and handle undo of merge sets
654 pp = chlog.parents(rev)
654 pp = chlog.parents(rev)
655 revnum = chlog.rev(rev)
655 revnum = chlog.rev(rev)
656
656
657 if update:
657 if update:
658 self.check_localchanges(repo, refresh=False)
658 self.check_localchanges(repo, refresh=False)
659 urev = self.qparents(repo, rev)
659 urev = self.qparents(repo, rev)
660 hg.clean(repo, urev, wlock=wlock)
660 hg.clean(repo, urev, wlock=wlock)
661 repo.dirstate.write()
661 repo.dirstate.write()
662
662
663 # save is a list of all the branches we are truncating away
663 # save is a list of all the branches we are truncating away
664 # that we actually want to keep. changegroup will be used
664 # that we actually want to keep. changegroup will be used
665 # to preserve them and add them back after the truncate
665 # to preserve them and add them back after the truncate
666 saveheads = []
666 saveheads = []
667 savebases = {}
667 savebases = {}
668
668
669 heads = limitheads(chlog, rev)
669 heads = limitheads(chlog, rev)
670 seen = {}
670 seen = {}
671
671
672 # search through all the heads, finding those where the revision
672 # search through all the heads, finding those where the revision
673 # we want to strip away is an ancestor. Also look for merges
673 # we want to strip away is an ancestor. Also look for merges
674 # that might be turned into new heads by the strip.
674 # that might be turned into new heads by the strip.
675 while heads:
675 while heads:
676 h = heads.pop()
676 h = heads.pop()
677 n = h
677 n = h
678 while True:
678 while True:
679 seen[n] = 1
679 seen[n] = 1
680 pp = chlog.parents(n)
680 pp = chlog.parents(n)
681 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
681 if pp[1] != revlog.nullid:
682 if pp[1] not in seen:
682 for p in pp:
683 heads.append(pp[1])
683 if chlog.rev(p) > revnum and p not in seen:
684 heads.append(p)
684 if pp[0] == revlog.nullid:
685 if pp[0] == revlog.nullid:
685 break
686 break
686 if chlog.rev(pp[0]) < revnum:
687 if chlog.rev(pp[0]) < revnum:
687 break
688 break
688 n = pp[0]
689 n = pp[0]
689 if n == rev:
690 if n == rev:
690 break
691 break
691 r = chlog.reachable(h, rev)
692 r = chlog.reachable(h, rev)
692 if rev not in r:
693 if rev not in r:
693 saveheads.append(h)
694 saveheads.append(h)
694 for x in r:
695 for x in r:
695 if chlog.rev(x) > revnum:
696 if chlog.rev(x) > revnum:
696 savebases[x] = 1
697 savebases[x] = 1
697
698
698 # create a changegroup for all the branches we need to keep
699 # create a changegroup for all the branches we need to keep
699 if backup == "all":
700 if backup == "all":
700 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
701 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
701 bundle(backupch)
702 bundle(backupch)
702 if saveheads:
703 if saveheads:
703 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
704 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
704 chgrpfile = bundle(backupch)
705 chgrpfile = bundle(backupch)
705
706
706 stripall(revnum)
707 stripall(revnum)
707
708
708 change = chlog.read(rev)
709 change = chlog.read(rev)
709 chlog.strip(revnum, revnum)
710 chlog.strip(revnum, revnum)
710 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
711 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
711 if saveheads:
712 if saveheads:
712 self.ui.status("adding branch\n")
713 self.ui.status("adding branch\n")
713 commands.unbundle(self.ui, repo, "file:%s" % chgrpfile,
714 commands.unbundle(self.ui, repo, "file:%s" % chgrpfile,
714 update=False)
715 update=False)
715 if backup != "strip":
716 if backup != "strip":
716 os.unlink(chgrpfile)
717 os.unlink(chgrpfile)
717
718
718 def isapplied(self, patch):
719 def isapplied(self, patch):
719 """returns (index, rev, patch)"""
720 """returns (index, rev, patch)"""
720 for i in xrange(len(self.applied)):
721 for i in xrange(len(self.applied)):
721 a = self.applied[i]
722 a = self.applied[i]
722 if a.name == patch:
723 if a.name == patch:
723 return (i, a.rev, a.name)
724 return (i, a.rev, a.name)
724 return None
725 return None
725
726
726 # if the exact patch name does not exist, we try a few
727 # if the exact patch name does not exist, we try a few
727 # variations. If strict is passed, we try only #1
728 # variations. If strict is passed, we try only #1
728 #
729 #
729 # 1) a number to indicate an offset in the series file
730 # 1) a number to indicate an offset in the series file
730 # 2) a unique substring of the patch name was given
731 # 2) a unique substring of the patch name was given
731 # 3) patchname[-+]num to indicate an offset in the series file
732 # 3) patchname[-+]num to indicate an offset in the series file
732 def lookup(self, patch, strict=False):
733 def lookup(self, patch, strict=False):
733 patch = patch and str(patch)
734 patch = patch and str(patch)
734
735
735 def partial_name(s):
736 def partial_name(s):
736 if s in self.series:
737 if s in self.series:
737 return s
738 return s
738 matches = [x for x in self.series if s in x]
739 matches = [x for x in self.series if s in x]
739 if len(matches) > 1:
740 if len(matches) > 1:
740 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
741 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
741 for m in matches:
742 for m in matches:
742 self.ui.warn(' %s\n' % m)
743 self.ui.warn(' %s\n' % m)
743 return None
744 return None
744 if matches:
745 if matches:
745 return matches[0]
746 return matches[0]
746 if len(self.series) > 0 and len(self.applied) > 0:
747 if len(self.series) > 0 and len(self.applied) > 0:
747 if s == 'qtip':
748 if s == 'qtip':
748 return self.series[self.series_end(True)-1]
749 return self.series[self.series_end(True)-1]
749 if s == 'qbase':
750 if s == 'qbase':
750 return self.series[0]
751 return self.series[0]
751 return None
752 return None
752 if patch == None:
753 if patch == None:
753 return None
754 return None
754
755
755 # we don't want to return a partial match until we make
756 # we don't want to return a partial match until we make
756 # sure the file name passed in does not exist (checked below)
757 # sure the file name passed in does not exist (checked below)
757 res = partial_name(patch)
758 res = partial_name(patch)
758 if res and res == patch:
759 if res and res == patch:
759 return res
760 return res
760
761
761 if not os.path.isfile(self.join(patch)):
762 if not os.path.isfile(self.join(patch)):
762 try:
763 try:
763 sno = int(patch)
764 sno = int(patch)
764 except(ValueError, OverflowError):
765 except(ValueError, OverflowError):
765 pass
766 pass
766 else:
767 else:
767 if sno < len(self.series):
768 if sno < len(self.series):
768 return self.series[sno]
769 return self.series[sno]
769 if not strict:
770 if not strict:
770 # return any partial match made above
771 # return any partial match made above
771 if res:
772 if res:
772 return res
773 return res
773 minus = patch.rfind('-')
774 minus = patch.rfind('-')
774 if minus >= 0:
775 if minus >= 0:
775 res = partial_name(patch[:minus])
776 res = partial_name(patch[:minus])
776 if res:
777 if res:
777 i = self.series.index(res)
778 i = self.series.index(res)
778 try:
779 try:
779 off = int(patch[minus+1:] or 1)
780 off = int(patch[minus+1:] or 1)
780 except(ValueError, OverflowError):
781 except(ValueError, OverflowError):
781 pass
782 pass
782 else:
783 else:
783 if i - off >= 0:
784 if i - off >= 0:
784 return self.series[i - off]
785 return self.series[i - off]
785 plus = patch.rfind('+')
786 plus = patch.rfind('+')
786 if plus >= 0:
787 if plus >= 0:
787 res = partial_name(patch[:plus])
788 res = partial_name(patch[:plus])
788 if res:
789 if res:
789 i = self.series.index(res)
790 i = self.series.index(res)
790 try:
791 try:
791 off = int(patch[plus+1:] or 1)
792 off = int(patch[plus+1:] or 1)
792 except(ValueError, OverflowError):
793 except(ValueError, OverflowError):
793 pass
794 pass
794 else:
795 else:
795 if i + off < len(self.series):
796 if i + off < len(self.series):
796 return self.series[i + off]
797 return self.series[i + off]
797 raise util.Abort(_("patch %s not in series") % patch)
798 raise util.Abort(_("patch %s not in series") % patch)
798
799
799 def push(self, repo, patch=None, force=False, list=False,
800 def push(self, repo, patch=None, force=False, list=False,
800 mergeq=None, wlock=None):
801 mergeq=None, wlock=None):
801 if not wlock:
802 if not wlock:
802 wlock = repo.wlock()
803 wlock = repo.wlock()
803 patch = self.lookup(patch)
804 patch = self.lookup(patch)
804 if patch and self.isapplied(patch):
805 if patch and self.isapplied(patch):
805 raise util.Abort(_("patch %s is already applied") % patch)
806 raise util.Abort(_("patch %s is already applied") % patch)
806 if self.series_end() == len(self.series):
807 if self.series_end() == len(self.series):
807 raise util.Abort(_("patch series fully applied"))
808 raise util.Abort(_("patch series fully applied"))
808 if not force:
809 if not force:
809 self.check_localchanges(repo)
810 self.check_localchanges(repo)
810
811
811 self.applied_dirty = 1;
812 self.applied_dirty = 1;
812 start = self.series_end()
813 start = self.series_end()
813 if start > 0:
814 if start > 0:
814 self.check_toppatch(repo)
815 self.check_toppatch(repo)
815 if not patch:
816 if not patch:
816 patch = self.series[start]
817 patch = self.series[start]
817 end = start + 1
818 end = start + 1
818 else:
819 else:
819 end = self.series.index(patch, start) + 1
820 end = self.series.index(patch, start) + 1
820 s = self.series[start:end]
821 s = self.series[start:end]
821 if mergeq:
822 if mergeq:
822 ret = self.mergepatch(repo, mergeq, s, wlock)
823 ret = self.mergepatch(repo, mergeq, s, wlock)
823 else:
824 else:
824 ret = self.apply(repo, s, list, wlock=wlock)
825 ret = self.apply(repo, s, list, wlock=wlock)
825 top = self.applied[-1].name
826 top = self.applied[-1].name
826 if ret[0]:
827 if ret[0]:
827 self.ui.write("Errors during apply, please fix and refresh %s\n" %
828 self.ui.write("Errors during apply, please fix and refresh %s\n" %
828 top)
829 top)
829 else:
830 else:
830 self.ui.write("Now at: %s\n" % top)
831 self.ui.write("Now at: %s\n" % top)
831 return ret[0]
832 return ret[0]
832
833
833 def pop(self, repo, patch=None, force=False, update=True, all=False,
834 def pop(self, repo, patch=None, force=False, update=True, all=False,
834 wlock=None):
835 wlock=None):
835 def getfile(f, rev):
836 def getfile(f, rev):
836 t = repo.file(f).read(rev)
837 t = repo.file(f).read(rev)
837 repo.wfile(f, "w").write(t)
838 repo.wfile(f, "w").write(t)
838
839
839 if not wlock:
840 if not wlock:
840 wlock = repo.wlock()
841 wlock = repo.wlock()
841 if patch:
842 if patch:
842 # index, rev, patch
843 # index, rev, patch
843 info = self.isapplied(patch)
844 info = self.isapplied(patch)
844 if not info:
845 if not info:
845 patch = self.lookup(patch)
846 patch = self.lookup(patch)
846 info = self.isapplied(patch)
847 info = self.isapplied(patch)
847 if not info:
848 if not info:
848 raise util.Abort(_("patch %s is not applied") % patch)
849 raise util.Abort(_("patch %s is not applied") % patch)
849 if len(self.applied) == 0:
850 if len(self.applied) == 0:
850 raise util.Abort(_("no patches applied"))
851 raise util.Abort(_("no patches applied"))
851
852
852 if not update:
853 if not update:
853 parents = repo.dirstate.parents()
854 parents = repo.dirstate.parents()
854 rr = [ revlog.bin(x.rev) for x in self.applied ]
855 rr = [ revlog.bin(x.rev) for x in self.applied ]
855 for p in parents:
856 for p in parents:
856 if p in rr:
857 if p in rr:
857 self.ui.warn("qpop: forcing dirstate update\n")
858 self.ui.warn("qpop: forcing dirstate update\n")
858 update = True
859 update = True
859
860
860 if not force and update:
861 if not force and update:
861 self.check_localchanges(repo)
862 self.check_localchanges(repo)
862
863
863 self.applied_dirty = 1;
864 self.applied_dirty = 1;
864 end = len(self.applied)
865 end = len(self.applied)
865 if not patch:
866 if not patch:
866 if all:
867 if all:
867 popi = 0
868 popi = 0
868 else:
869 else:
869 popi = len(self.applied) - 1
870 popi = len(self.applied) - 1
870 else:
871 else:
871 popi = info[0] + 1
872 popi = info[0] + 1
872 if popi >= end:
873 if popi >= end:
873 self.ui.warn("qpop: %s is already at the top\n" % patch)
874 self.ui.warn("qpop: %s is already at the top\n" % patch)
874 return
875 return
875 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
876 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
876
877
877 start = info[0]
878 start = info[0]
878 rev = revlog.bin(info[1])
879 rev = revlog.bin(info[1])
879
880
880 # we know there are no local changes, so we can make a simplified
881 # we know there are no local changes, so we can make a simplified
881 # form of hg.update.
882 # form of hg.update.
882 if update:
883 if update:
883 top = self.check_toppatch(repo)
884 top = self.check_toppatch(repo)
884 qp = self.qparents(repo, rev)
885 qp = self.qparents(repo, rev)
885 changes = repo.changelog.read(qp)
886 changes = repo.changelog.read(qp)
886 mmap = repo.manifest.read(changes[0])
887 mmap = repo.manifest.read(changes[0])
887 m, a, r, d, u = repo.status(qp, top)[:5]
888 m, a, r, d, u = repo.status(qp, top)[:5]
888 if d:
889 if d:
889 raise util.Abort("deletions found between repo revs")
890 raise util.Abort("deletions found between repo revs")
890 for f in m:
891 for f in m:
891 getfile(f, mmap[f])
892 getfile(f, mmap[f])
892 for f in r:
893 for f in r:
893 getfile(f, mmap[f])
894 getfile(f, mmap[f])
894 util.set_exec(repo.wjoin(f), mmap.execf(f))
895 util.set_exec(repo.wjoin(f), mmap.execf(f))
895 repo.dirstate.update(m + r, 'n')
896 repo.dirstate.update(m + r, 'n')
896 for f in a:
897 for f in a:
897 try:
898 try:
898 os.unlink(repo.wjoin(f))
899 os.unlink(repo.wjoin(f))
899 except OSError, e:
900 except OSError, e:
900 if e.errno != errno.ENOENT:
901 if e.errno != errno.ENOENT:
901 raise
902 raise
902 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
903 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
903 except: pass
904 except: pass
904 if a:
905 if a:
905 repo.dirstate.forget(a)
906 repo.dirstate.forget(a)
906 repo.dirstate.setparents(qp, revlog.nullid)
907 repo.dirstate.setparents(qp, revlog.nullid)
907 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
908 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
908 del self.applied[start:end]
909 del self.applied[start:end]
909 if len(self.applied):
910 if len(self.applied):
910 self.ui.write("Now at: %s\n" % self.applied[-1].name)
911 self.ui.write("Now at: %s\n" % self.applied[-1].name)
911 else:
912 else:
912 self.ui.write("Patch queue now empty\n")
913 self.ui.write("Patch queue now empty\n")
913
914
914 def diff(self, repo, pats, opts):
915 def diff(self, repo, pats, opts):
915 top = self.check_toppatch(repo)
916 top = self.check_toppatch(repo)
916 if not top:
917 if not top:
917 self.ui.write("No patches applied\n")
918 self.ui.write("No patches applied\n")
918 return
919 return
919 qp = self.qparents(repo, top)
920 qp = self.qparents(repo, top)
920 if opts.get('git'):
921 if opts.get('git'):
921 self.diffopts().git = True
922 self.diffopts().git = True
922 self.printdiff(repo, qp, files=pats, opts=opts)
923 self.printdiff(repo, qp, files=pats, opts=opts)
923
924
924 def refresh(self, repo, pats=None, **opts):
925 def refresh(self, repo, pats=None, **opts):
925 if len(self.applied) == 0:
926 if len(self.applied) == 0:
926 self.ui.write("No patches applied\n")
927 self.ui.write("No patches applied\n")
927 return 1
928 return 1
928 wlock = repo.wlock()
929 wlock = repo.wlock()
929 self.check_toppatch(repo)
930 self.check_toppatch(repo)
930 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
931 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
931 top = revlog.bin(top)
932 top = revlog.bin(top)
932 cparents = repo.changelog.parents(top)
933 cparents = repo.changelog.parents(top)
933 patchparent = self.qparents(repo, top)
934 patchparent = self.qparents(repo, top)
934 message, comments, user, date, patchfound = self.readheaders(patchfn)
935 message, comments, user, date, patchfound = self.readheaders(patchfn)
935
936
936 patchf = self.opener(patchfn, "w")
937 patchf = self.opener(patchfn, "w")
937 msg = opts.get('msg', '').rstrip()
938 msg = opts.get('msg', '').rstrip()
938 if msg:
939 if msg:
939 if comments:
940 if comments:
940 # Remove existing message.
941 # Remove existing message.
941 ci = 0
942 ci = 0
942 for mi in xrange(len(message)):
943 for mi in xrange(len(message)):
943 while message[mi] != comments[ci]:
944 while message[mi] != comments[ci]:
944 ci += 1
945 ci += 1
945 del comments[ci]
946 del comments[ci]
946 comments.append(msg)
947 comments.append(msg)
947 if comments:
948 if comments:
948 comments = "\n".join(comments) + '\n\n'
949 comments = "\n".join(comments) + '\n\n'
949 patchf.write(comments)
950 patchf.write(comments)
950
951
951 if opts.get('git'):
952 if opts.get('git'):
952 self.diffopts().git = True
953 self.diffopts().git = True
953 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
954 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
954 tip = repo.changelog.tip()
955 tip = repo.changelog.tip()
955 if top == tip:
956 if top == tip:
956 # if the top of our patch queue is also the tip, there is an
957 # if the top of our patch queue is also the tip, there is an
957 # optimization here. We update the dirstate in place and strip
958 # optimization here. We update the dirstate in place and strip
958 # off the tip commit. Then just commit the current directory
959 # off the tip commit. Then just commit the current directory
959 # tree. We can also send repo.commit the list of files
960 # tree. We can also send repo.commit the list of files
960 # changed to speed up the diff
961 # changed to speed up the diff
961 #
962 #
962 # in short mode, we only diff the files included in the
963 # in short mode, we only diff the files included in the
963 # patch already
964 # patch already
964 #
965 #
965 # this should really read:
966 # this should really read:
966 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
967 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
967 # but we do it backwards to take advantage of manifest/chlog
968 # but we do it backwards to take advantage of manifest/chlog
968 # caching against the next repo.status call
969 # caching against the next repo.status call
969 #
970 #
970 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
971 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
971 changes = repo.changelog.read(tip)
972 changes = repo.changelog.read(tip)
972 man = repo.manifest.read(changes[0])
973 man = repo.manifest.read(changes[0])
973 aaa = aa[:]
974 aaa = aa[:]
974 if opts.get('short'):
975 if opts.get('short'):
975 filelist = mm + aa + dd
976 filelist = mm + aa + dd
976 else:
977 else:
977 filelist = None
978 filelist = None
978 m, a, r, d, u = repo.status(files=filelist)[:5]
979 m, a, r, d, u = repo.status(files=filelist)[:5]
979
980
980 # we might end up with files that were added between tip and
981 # we might end up with files that were added between tip and
981 # the dirstate parent, but then changed in the local dirstate.
982 # the dirstate parent, but then changed in the local dirstate.
982 # in this case, we want them to only show up in the added section
983 # in this case, we want them to only show up in the added section
983 for x in m:
984 for x in m:
984 if x not in aa:
985 if x not in aa:
985 mm.append(x)
986 mm.append(x)
986 # we might end up with files added by the local dirstate that
987 # we might end up with files added by the local dirstate that
987 # were deleted by the patch. In this case, they should only
988 # were deleted by the patch. In this case, they should only
988 # show up in the changed section.
989 # show up in the changed section.
989 for x in a:
990 for x in a:
990 if x in dd:
991 if x in dd:
991 del dd[dd.index(x)]
992 del dd[dd.index(x)]
992 mm.append(x)
993 mm.append(x)
993 else:
994 else:
994 aa.append(x)
995 aa.append(x)
995 # make sure any files deleted in the local dirstate
996 # make sure any files deleted in the local dirstate
996 # are not in the add or change column of the patch
997 # are not in the add or change column of the patch
997 forget = []
998 forget = []
998 for x in d + r:
999 for x in d + r:
999 if x in aa:
1000 if x in aa:
1000 del aa[aa.index(x)]
1001 del aa[aa.index(x)]
1001 forget.append(x)
1002 forget.append(x)
1002 continue
1003 continue
1003 elif x in mm:
1004 elif x in mm:
1004 del mm[mm.index(x)]
1005 del mm[mm.index(x)]
1005 dd.append(x)
1006 dd.append(x)
1006
1007
1007 m = util.unique(mm)
1008 m = util.unique(mm)
1008 r = util.unique(dd)
1009 r = util.unique(dd)
1009 a = util.unique(aa)
1010 a = util.unique(aa)
1010 filelist = filter(matchfn, util.unique(m + r + a))
1011 filelist = filter(matchfn, util.unique(m + r + a))
1011 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1012 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1012 fp=patchf, changes=(m, a, r, [], u),
1013 fp=patchf, changes=(m, a, r, [], u),
1013 opts=self.diffopts())
1014 opts=self.diffopts())
1014 patchf.close()
1015 patchf.close()
1015
1016
1016 repo.dirstate.setparents(*cparents)
1017 repo.dirstate.setparents(*cparents)
1017 copies = {}
1018 copies = {}
1018 for dst in a:
1019 for dst in a:
1019 src = repo.dirstate.copied(dst)
1020 src = repo.dirstate.copied(dst)
1020 if src is None:
1021 if src is None:
1021 continue
1022 continue
1022 copies.setdefault(src, []).append(dst)
1023 copies.setdefault(src, []).append(dst)
1023 repo.dirstate.update(a, 'a')
1024 repo.dirstate.update(a, 'a')
1024 # remember the copies between patchparent and tip
1025 # remember the copies between patchparent and tip
1025 # this may be slow, so don't do it if we're not tracking copies
1026 # this may be slow, so don't do it if we're not tracking copies
1026 if self.diffopts().git:
1027 if self.diffopts().git:
1027 for dst in aaa:
1028 for dst in aaa:
1028 f = repo.file(dst)
1029 f = repo.file(dst)
1029 src = f.renamed(man[dst])
1030 src = f.renamed(man[dst])
1030 if src:
1031 if src:
1031 copies[src[0]] = copies.get(dst, [])
1032 copies[src[0]] = copies.get(dst, [])
1032 if dst in a:
1033 if dst in a:
1033 copies[src[0]].append(dst)
1034 copies[src[0]].append(dst)
1034 # we can't copy a file created by the patch itself
1035 # we can't copy a file created by the patch itself
1035 if dst in copies:
1036 if dst in copies:
1036 del copies[dst]
1037 del copies[dst]
1037 for src, dsts in copies.iteritems():
1038 for src, dsts in copies.iteritems():
1038 for dst in dsts:
1039 for dst in dsts:
1039 repo.dirstate.copy(src, dst)
1040 repo.dirstate.copy(src, dst)
1040 repo.dirstate.update(r, 'r')
1041 repo.dirstate.update(r, 'r')
1041 # if the patch excludes a modified file, mark that file with mtime=0
1042 # if the patch excludes a modified file, mark that file with mtime=0
1042 # so status can see it.
1043 # so status can see it.
1043 mm = []
1044 mm = []
1044 for i in xrange(len(m)-1, -1, -1):
1045 for i in xrange(len(m)-1, -1, -1):
1045 if not matchfn(m[i]):
1046 if not matchfn(m[i]):
1046 mm.append(m[i])
1047 mm.append(m[i])
1047 del m[i]
1048 del m[i]
1048 repo.dirstate.update(m, 'n')
1049 repo.dirstate.update(m, 'n')
1049 repo.dirstate.update(mm, 'n', st_mtime=0)
1050 repo.dirstate.update(mm, 'n', st_mtime=0)
1050 repo.dirstate.forget(forget)
1051 repo.dirstate.forget(forget)
1051
1052
1052 if not msg:
1053 if not msg:
1053 if not message:
1054 if not message:
1054 message = "patch queue: %s\n" % patchfn
1055 message = "patch queue: %s\n" % patchfn
1055 else:
1056 else:
1056 message = "\n".join(message)
1057 message = "\n".join(message)
1057 else:
1058 else:
1058 message = msg
1059 message = msg
1059
1060
1060 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1061 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1061 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
1062 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
1062 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1063 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1063 self.applied_dirty = 1
1064 self.applied_dirty = 1
1064 else:
1065 else:
1065 self.printdiff(repo, patchparent, fp=patchf)
1066 self.printdiff(repo, patchparent, fp=patchf)
1066 patchf.close()
1067 patchf.close()
1067 added = repo.status()[1]
1068 added = repo.status()[1]
1068 for a in added:
1069 for a in added:
1069 f = repo.wjoin(a)
1070 f = repo.wjoin(a)
1070 try:
1071 try:
1071 os.unlink(f)
1072 os.unlink(f)
1072 except OSError, e:
1073 except OSError, e:
1073 if e.errno != errno.ENOENT:
1074 if e.errno != errno.ENOENT:
1074 raise
1075 raise
1075 try: os.removedirs(os.path.dirname(f))
1076 try: os.removedirs(os.path.dirname(f))
1076 except: pass
1077 except: pass
1077 # forget the file copies in the dirstate
1078 # forget the file copies in the dirstate
1078 # push should readd the files later on
1079 # push should readd the files later on
1079 repo.dirstate.forget(added)
1080 repo.dirstate.forget(added)
1080 self.pop(repo, force=True, wlock=wlock)
1081 self.pop(repo, force=True, wlock=wlock)
1081 self.push(repo, force=True, wlock=wlock)
1082 self.push(repo, force=True, wlock=wlock)
1082
1083
1083 def init(self, repo, create=False):
1084 def init(self, repo, create=False):
1084 if os.path.isdir(self.path):
1085 if os.path.isdir(self.path):
1085 raise util.Abort(_("patch queue directory already exists"))
1086 raise util.Abort(_("patch queue directory already exists"))
1086 os.mkdir(self.path)
1087 os.mkdir(self.path)
1087 if create:
1088 if create:
1088 return self.qrepo(create=True)
1089 return self.qrepo(create=True)
1089
1090
1090 def unapplied(self, repo, patch=None):
1091 def unapplied(self, repo, patch=None):
1091 if patch and patch not in self.series:
1092 if patch and patch not in self.series:
1092 raise util.Abort(_("patch %s is not in series file") % patch)
1093 raise util.Abort(_("patch %s is not in series file") % patch)
1093 if not patch:
1094 if not patch:
1094 start = self.series_end()
1095 start = self.series_end()
1095 else:
1096 else:
1096 start = self.series.index(patch) + 1
1097 start = self.series.index(patch) + 1
1097 unapplied = []
1098 unapplied = []
1098 for i in xrange(start, len(self.series)):
1099 for i in xrange(start, len(self.series)):
1099 pushable, reason = self.pushable(i)
1100 pushable, reason = self.pushable(i)
1100 if pushable:
1101 if pushable:
1101 unapplied.append((i, self.series[i]))
1102 unapplied.append((i, self.series[i]))
1102 self.explain_pushable(i)
1103 self.explain_pushable(i)
1103 return unapplied
1104 return unapplied
1104
1105
1105 def qseries(self, repo, missing=None, start=0, length=0, status=None,
1106 def qseries(self, repo, missing=None, start=0, length=0, status=None,
1106 summary=False):
1107 summary=False):
1107 def displayname(patchname):
1108 def displayname(patchname):
1108 if summary:
1109 if summary:
1109 msg = self.readheaders(patchname)[0]
1110 msg = self.readheaders(patchname)[0]
1110 msg = msg and ': ' + msg[0] or ': '
1111 msg = msg and ': ' + msg[0] or ': '
1111 else:
1112 else:
1112 msg = ''
1113 msg = ''
1113 return '%s%s' % (patchname, msg)
1114 return '%s%s' % (patchname, msg)
1114
1115
1115 def pname(i):
1116 def pname(i):
1116 if status == 'A':
1117 if status == 'A':
1117 return self.applied[i].name
1118 return self.applied[i].name
1118 else:
1119 else:
1119 return self.series[i]
1120 return self.series[i]
1120
1121
1121 applied = dict.fromkeys([p.name for p in self.applied])
1122 applied = dict.fromkeys([p.name for p in self.applied])
1122 if not length:
1123 if not length:
1123 length = len(self.series) - start
1124 length = len(self.series) - start
1124 if not missing:
1125 if not missing:
1125 for i in xrange(start, start+length):
1126 for i in xrange(start, start+length):
1126 pfx = ''
1127 pfx = ''
1127 patch = pname(i)
1128 patch = pname(i)
1128 if self.ui.verbose:
1129 if self.ui.verbose:
1129 if patch in applied:
1130 if patch in applied:
1130 stat = 'A'
1131 stat = 'A'
1131 elif self.pushable(i)[0]:
1132 elif self.pushable(i)[0]:
1132 stat = 'U'
1133 stat = 'U'
1133 else:
1134 else:
1134 stat = 'G'
1135 stat = 'G'
1135 pfx = '%d %s ' % (i, stat)
1136 pfx = '%d %s ' % (i, stat)
1136 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1137 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1137 else:
1138 else:
1138 msng_list = []
1139 msng_list = []
1139 for root, dirs, files in os.walk(self.path):
1140 for root, dirs, files in os.walk(self.path):
1140 d = root[len(self.path) + 1:]
1141 d = root[len(self.path) + 1:]
1141 for f in files:
1142 for f in files:
1142 fl = os.path.join(d, f)
1143 fl = os.path.join(d, f)
1143 if (fl not in self.series and
1144 if (fl not in self.series and
1144 fl not in (self.status_path, self.series_path)
1145 fl not in (self.status_path, self.series_path)
1145 and not fl.startswith('.')):
1146 and not fl.startswith('.')):
1146 msng_list.append(fl)
1147 msng_list.append(fl)
1147 msng_list.sort()
1148 msng_list.sort()
1148 for x in msng_list:
1149 for x in msng_list:
1149 pfx = self.ui.verbose and ('D ') or ''
1150 pfx = self.ui.verbose and ('D ') or ''
1150 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1151 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1151
1152
1152 def issaveline(self, l):
1153 def issaveline(self, l):
1153 if l.name == '.hg.patches.save.line':
1154 if l.name == '.hg.patches.save.line':
1154 return True
1155 return True
1155
1156
1156 def qrepo(self, create=False):
1157 def qrepo(self, create=False):
1157 if create or os.path.isdir(self.join(".hg")):
1158 if create or os.path.isdir(self.join(".hg")):
1158 return hg.repository(self.ui, path=self.path, create=create)
1159 return hg.repository(self.ui, path=self.path, create=create)
1159
1160
1160 def restore(self, repo, rev, delete=None, qupdate=None):
1161 def restore(self, repo, rev, delete=None, qupdate=None):
1161 c = repo.changelog.read(rev)
1162 c = repo.changelog.read(rev)
1162 desc = c[4].strip()
1163 desc = c[4].strip()
1163 lines = desc.splitlines()
1164 lines = desc.splitlines()
1164 i = 0
1165 i = 0
1165 datastart = None
1166 datastart = None
1166 series = []
1167 series = []
1167 applied = []
1168 applied = []
1168 qpp = None
1169 qpp = None
1169 for i in xrange(0, len(lines)):
1170 for i in xrange(0, len(lines)):
1170 if lines[i] == 'Patch Data:':
1171 if lines[i] == 'Patch Data:':
1171 datastart = i + 1
1172 datastart = i + 1
1172 elif lines[i].startswith('Dirstate:'):
1173 elif lines[i].startswith('Dirstate:'):
1173 l = lines[i].rstrip()
1174 l = lines[i].rstrip()
1174 l = l[10:].split(' ')
1175 l = l[10:].split(' ')
1175 qpp = [ hg.bin(x) for x in l ]
1176 qpp = [ hg.bin(x) for x in l ]
1176 elif datastart != None:
1177 elif datastart != None:
1177 l = lines[i].rstrip()
1178 l = lines[i].rstrip()
1178 se = statusentry(l)
1179 se = statusentry(l)
1179 file_ = se.name
1180 file_ = se.name
1180 if se.rev:
1181 if se.rev:
1181 applied.append(se)
1182 applied.append(se)
1182 else:
1183 else:
1183 series.append(file_)
1184 series.append(file_)
1184 if datastart == None:
1185 if datastart == None:
1185 self.ui.warn("No saved patch data found\n")
1186 self.ui.warn("No saved patch data found\n")
1186 return 1
1187 return 1
1187 self.ui.warn("restoring status: %s\n" % lines[0])
1188 self.ui.warn("restoring status: %s\n" % lines[0])
1188 self.full_series = series
1189 self.full_series = series
1189 self.applied = applied
1190 self.applied = applied
1190 self.parse_series()
1191 self.parse_series()
1191 self.series_dirty = 1
1192 self.series_dirty = 1
1192 self.applied_dirty = 1
1193 self.applied_dirty = 1
1193 heads = repo.changelog.heads()
1194 heads = repo.changelog.heads()
1194 if delete:
1195 if delete:
1195 if rev not in heads:
1196 if rev not in heads:
1196 self.ui.warn("save entry has children, leaving it alone\n")
1197 self.ui.warn("save entry has children, leaving it alone\n")
1197 else:
1198 else:
1198 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1199 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1199 pp = repo.dirstate.parents()
1200 pp = repo.dirstate.parents()
1200 if rev in pp:
1201 if rev in pp:
1201 update = True
1202 update = True
1202 else:
1203 else:
1203 update = False
1204 update = False
1204 self.strip(repo, rev, update=update, backup='strip')
1205 self.strip(repo, rev, update=update, backup='strip')
1205 if qpp:
1206 if qpp:
1206 self.ui.warn("saved queue repository parents: %s %s\n" %
1207 self.ui.warn("saved queue repository parents: %s %s\n" %
1207 (hg.short(qpp[0]), hg.short(qpp[1])))
1208 (hg.short(qpp[0]), hg.short(qpp[1])))
1208 if qupdate:
1209 if qupdate:
1209 print "queue directory updating"
1210 print "queue directory updating"
1210 r = self.qrepo()
1211 r = self.qrepo()
1211 if not r:
1212 if not r:
1212 self.ui.warn("Unable to load queue repository\n")
1213 self.ui.warn("Unable to load queue repository\n")
1213 return 1
1214 return 1
1214 hg.clean(r, qpp[0])
1215 hg.clean(r, qpp[0])
1215
1216
1216 def save(self, repo, msg=None):
1217 def save(self, repo, msg=None):
1217 if len(self.applied) == 0:
1218 if len(self.applied) == 0:
1218 self.ui.warn("save: no patches applied, exiting\n")
1219 self.ui.warn("save: no patches applied, exiting\n")
1219 return 1
1220 return 1
1220 if self.issaveline(self.applied[-1]):
1221 if self.issaveline(self.applied[-1]):
1221 self.ui.warn("status is already saved\n")
1222 self.ui.warn("status is already saved\n")
1222 return 1
1223 return 1
1223
1224
1224 ar = [ ':' + x for x in self.full_series ]
1225 ar = [ ':' + x for x in self.full_series ]
1225 if not msg:
1226 if not msg:
1226 msg = "hg patches saved state"
1227 msg = "hg patches saved state"
1227 else:
1228 else:
1228 msg = "hg patches: " + msg.rstrip('\r\n')
1229 msg = "hg patches: " + msg.rstrip('\r\n')
1229 r = self.qrepo()
1230 r = self.qrepo()
1230 if r:
1231 if r:
1231 pp = r.dirstate.parents()
1232 pp = r.dirstate.parents()
1232 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1233 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1233 msg += "\n\nPatch Data:\n"
1234 msg += "\n\nPatch Data:\n"
1234 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1235 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1235 "\n".join(ar) + '\n' or "")
1236 "\n".join(ar) + '\n' or "")
1236 n = repo.commit(None, text, user=None, force=1)
1237 n = repo.commit(None, text, user=None, force=1)
1237 if not n:
1238 if not n:
1238 self.ui.warn("repo commit failed\n")
1239 self.ui.warn("repo commit failed\n")
1239 return 1
1240 return 1
1240 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1241 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1241 self.applied_dirty = 1
1242 self.applied_dirty = 1
1242
1243
1243 def full_series_end(self):
1244 def full_series_end(self):
1244 if len(self.applied) > 0:
1245 if len(self.applied) > 0:
1245 p = self.applied[-1].name
1246 p = self.applied[-1].name
1246 end = self.find_series(p)
1247 end = self.find_series(p)
1247 if end == None:
1248 if end == None:
1248 return len(self.full_series)
1249 return len(self.full_series)
1249 return end + 1
1250 return end + 1
1250 return 0
1251 return 0
1251
1252
1252 def series_end(self, all_patches=False):
1253 def series_end(self, all_patches=False):
1253 end = 0
1254 end = 0
1254 def next(start):
1255 def next(start):
1255 if all_patches:
1256 if all_patches:
1256 return start
1257 return start
1257 i = start
1258 i = start
1258 while i < len(self.series):
1259 while i < len(self.series):
1259 p, reason = self.pushable(i)
1260 p, reason = self.pushable(i)
1260 if p:
1261 if p:
1261 break
1262 break
1262 self.explain_pushable(i)
1263 self.explain_pushable(i)
1263 i += 1
1264 i += 1
1264 return i
1265 return i
1265 if len(self.applied) > 0:
1266 if len(self.applied) > 0:
1266 p = self.applied[-1].name
1267 p = self.applied[-1].name
1267 try:
1268 try:
1268 end = self.series.index(p)
1269 end = self.series.index(p)
1269 except ValueError:
1270 except ValueError:
1270 return 0
1271 return 0
1271 return next(end + 1)
1272 return next(end + 1)
1272 return next(end)
1273 return next(end)
1273
1274
1274 def appliedname(self, index):
1275 def appliedname(self, index):
1275 pname = self.applied[index].name
1276 pname = self.applied[index].name
1276 if not self.ui.verbose:
1277 if not self.ui.verbose:
1277 p = pname
1278 p = pname
1278 else:
1279 else:
1279 p = str(self.series.index(pname)) + " " + pname
1280 p = str(self.series.index(pname)) + " " + pname
1280 return p
1281 return p
1281
1282
1282 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1283 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1283 force=None, git=False):
1284 force=None, git=False):
1284 def checkseries(patchname):
1285 def checkseries(patchname):
1285 if patchname in self.series:
1286 if patchname in self.series:
1286 raise util.Abort(_('patch %s is already in the series file')
1287 raise util.Abort(_('patch %s is already in the series file')
1287 % patchname)
1288 % patchname)
1288 def checkfile(patchname):
1289 def checkfile(patchname):
1289 if not force and os.path.exists(self.join(patchname)):
1290 if not force and os.path.exists(self.join(patchname)):
1290 raise util.Abort(_('patch "%s" already exists')
1291 raise util.Abort(_('patch "%s" already exists')
1291 % patchname)
1292 % patchname)
1292
1293
1293 if rev:
1294 if rev:
1294 if files:
1295 if files:
1295 raise util.Abort(_('option "-r" not valid when importing '
1296 raise util.Abort(_('option "-r" not valid when importing '
1296 'files'))
1297 'files'))
1297 rev = cmdutil.revrange(repo, rev)
1298 rev = cmdutil.revrange(repo, rev)
1298 rev.sort(lambda x, y: cmp(y, x))
1299 rev.sort(lambda x, y: cmp(y, x))
1299 if (len(files) > 1 or len(rev) > 1) and patchname:
1300 if (len(files) > 1 or len(rev) > 1) and patchname:
1300 raise util.Abort(_('option "-n" not valid when importing multiple '
1301 raise util.Abort(_('option "-n" not valid when importing multiple '
1301 'patches'))
1302 'patches'))
1302 i = 0
1303 i = 0
1303 added = []
1304 added = []
1304 if rev:
1305 if rev:
1305 # If mq patches are applied, we can only import revisions
1306 # If mq patches are applied, we can only import revisions
1306 # that form a linear path to qbase.
1307 # that form a linear path to qbase.
1307 # Otherwise, they should form a linear path to a head.
1308 # Otherwise, they should form a linear path to a head.
1308 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1309 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1309 if len(heads) > 1:
1310 if len(heads) > 1:
1310 raise util.Abort(_('revision %d is the root of more than one '
1311 raise util.Abort(_('revision %d is the root of more than one '
1311 'branch') % rev[-1])
1312 'branch') % rev[-1])
1312 if self.applied:
1313 if self.applied:
1313 base = revlog.hex(repo.changelog.node(rev[0]))
1314 base = revlog.hex(repo.changelog.node(rev[0]))
1314 if base in [n.rev for n in self.applied]:
1315 if base in [n.rev for n in self.applied]:
1315 raise util.Abort(_('revision %d is already managed')
1316 raise util.Abort(_('revision %d is already managed')
1316 % rev[0])
1317 % rev[0])
1317 if heads != [revlog.bin(self.applied[-1].rev)]:
1318 if heads != [revlog.bin(self.applied[-1].rev)]:
1318 raise util.Abort(_('revision %d is not the parent of '
1319 raise util.Abort(_('revision %d is not the parent of '
1319 'the queue') % rev[0])
1320 'the queue') % rev[0])
1320 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1321 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1321 lastparent = repo.changelog.parentrevs(base)[0]
1322 lastparent = repo.changelog.parentrevs(base)[0]
1322 else:
1323 else:
1323 if heads != [repo.changelog.node(rev[0])]:
1324 if heads != [repo.changelog.node(rev[0])]:
1324 raise util.Abort(_('revision %d has unmanaged children')
1325 raise util.Abort(_('revision %d has unmanaged children')
1325 % rev[0])
1326 % rev[0])
1326 lastparent = None
1327 lastparent = None
1327
1328
1328 if git:
1329 if git:
1329 self.diffopts().git = True
1330 self.diffopts().git = True
1330
1331
1331 for r in rev:
1332 for r in rev:
1332 p1, p2 = repo.changelog.parentrevs(r)
1333 p1, p2 = repo.changelog.parentrevs(r)
1333 n = repo.changelog.node(r)
1334 n = repo.changelog.node(r)
1334 if p2 != revlog.nullrev:
1335 if p2 != revlog.nullrev:
1335 raise util.Abort(_('cannot import merge revision %d') % r)
1336 raise util.Abort(_('cannot import merge revision %d') % r)
1336 if lastparent and lastparent != r:
1337 if lastparent and lastparent != r:
1337 raise util.Abort(_('revision %d is not the parent of %d')
1338 raise util.Abort(_('revision %d is not the parent of %d')
1338 % (r, lastparent))
1339 % (r, lastparent))
1339 lastparent = p1
1340 lastparent = p1
1340
1341
1341 if not patchname:
1342 if not patchname:
1342 patchname = normname('%d.diff' % r)
1343 patchname = normname('%d.diff' % r)
1343 checkseries(patchname)
1344 checkseries(patchname)
1344 checkfile(patchname)
1345 checkfile(patchname)
1345 self.full_series.insert(0, patchname)
1346 self.full_series.insert(0, patchname)
1346
1347
1347 patchf = self.opener(patchname, "w")
1348 patchf = self.opener(patchname, "w")
1348 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1349 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1349 patchf.close()
1350 patchf.close()
1350
1351
1351 se = statusentry(revlog.hex(n), patchname)
1352 se = statusentry(revlog.hex(n), patchname)
1352 self.applied.insert(0, se)
1353 self.applied.insert(0, se)
1353
1354
1354 added.append(patchname)
1355 added.append(patchname)
1355 patchname = None
1356 patchname = None
1356 self.parse_series()
1357 self.parse_series()
1357 self.applied_dirty = 1
1358 self.applied_dirty = 1
1358
1359
1359 for filename in files:
1360 for filename in files:
1360 if existing:
1361 if existing:
1361 if filename == '-':
1362 if filename == '-':
1362 raise util.Abort(_('-e is incompatible with import from -'))
1363 raise util.Abort(_('-e is incompatible with import from -'))
1363 if not patchname:
1364 if not patchname:
1364 patchname = normname(filename)
1365 patchname = normname(filename)
1365 if not os.path.isfile(self.join(patchname)):
1366 if not os.path.isfile(self.join(patchname)):
1366 raise util.Abort(_("patch %s does not exist") % patchname)
1367 raise util.Abort(_("patch %s does not exist") % patchname)
1367 else:
1368 else:
1368 try:
1369 try:
1369 if filename == '-':
1370 if filename == '-':
1370 if not patchname:
1371 if not patchname:
1371 raise util.Abort(_('need --name to import a patch from -'))
1372 raise util.Abort(_('need --name to import a patch from -'))
1372 text = sys.stdin.read()
1373 text = sys.stdin.read()
1373 else:
1374 else:
1374 text = file(filename).read()
1375 text = file(filename).read()
1375 except IOError:
1376 except IOError:
1376 raise util.Abort(_("unable to read %s") % patchname)
1377 raise util.Abort(_("unable to read %s") % patchname)
1377 if not patchname:
1378 if not patchname:
1378 patchname = normname(os.path.basename(filename))
1379 patchname = normname(os.path.basename(filename))
1379 checkfile(patchname)
1380 checkfile(patchname)
1380 patchf = self.opener(patchname, "w")
1381 patchf = self.opener(patchname, "w")
1381 patchf.write(text)
1382 patchf.write(text)
1382 checkseries(patchname)
1383 checkseries(patchname)
1383 index = self.full_series_end() + i
1384 index = self.full_series_end() + i
1384 self.full_series[index:index] = [patchname]
1385 self.full_series[index:index] = [patchname]
1385 self.parse_series()
1386 self.parse_series()
1386 self.ui.warn("adding %s to series file\n" % patchname)
1387 self.ui.warn("adding %s to series file\n" % patchname)
1387 i += 1
1388 i += 1
1388 added.append(patchname)
1389 added.append(patchname)
1389 patchname = None
1390 patchname = None
1390 self.series_dirty = 1
1391 self.series_dirty = 1
1391 qrepo = self.qrepo()
1392 qrepo = self.qrepo()
1392 if qrepo:
1393 if qrepo:
1393 qrepo.add(added)
1394 qrepo.add(added)
1394
1395
1395 def delete(ui, repo, *patches, **opts):
1396 def delete(ui, repo, *patches, **opts):
1396 """remove patches from queue
1397 """remove patches from queue
1397
1398
1398 With --rev, mq will stop managing the named revisions. The
1399 With --rev, mq will stop managing the named revisions. The
1399 patches must be applied and at the base of the stack. This option
1400 patches must be applied and at the base of the stack. This option
1400 is useful when the patches have been applied upstream.
1401 is useful when the patches have been applied upstream.
1401
1402
1402 Otherwise, the patches must not be applied.
1403 Otherwise, the patches must not be applied.
1403
1404
1404 With --keep, the patch files are preserved in the patch directory."""
1405 With --keep, the patch files are preserved in the patch directory."""
1405 q = repo.mq
1406 q = repo.mq
1406 q.delete(repo, patches, opts)
1407 q.delete(repo, patches, opts)
1407 q.save_dirty()
1408 q.save_dirty()
1408 return 0
1409 return 0
1409
1410
1410 def applied(ui, repo, patch=None, **opts):
1411 def applied(ui, repo, patch=None, **opts):
1411 """print the patches already applied"""
1412 """print the patches already applied"""
1412 q = repo.mq
1413 q = repo.mq
1413 if patch:
1414 if patch:
1414 if patch not in q.series:
1415 if patch not in q.series:
1415 raise util.Abort(_("patch %s is not in series file") % patch)
1416 raise util.Abort(_("patch %s is not in series file") % patch)
1416 end = q.series.index(patch) + 1
1417 end = q.series.index(patch) + 1
1417 else:
1418 else:
1418 end = len(q.applied)
1419 end = len(q.applied)
1419 if not end:
1420 if not end:
1420 return
1421 return
1421
1422
1422 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1423 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1423
1424
1424 def unapplied(ui, repo, patch=None, **opts):
1425 def unapplied(ui, repo, patch=None, **opts):
1425 """print the patches not yet applied"""
1426 """print the patches not yet applied"""
1426 q = repo.mq
1427 q = repo.mq
1427 if patch:
1428 if patch:
1428 if patch not in q.series:
1429 if patch not in q.series:
1429 raise util.Abort(_("patch %s is not in series file") % patch)
1430 raise util.Abort(_("patch %s is not in series file") % patch)
1430 start = q.series.index(patch) + 1
1431 start = q.series.index(patch) + 1
1431 else:
1432 else:
1432 start = q.series_end()
1433 start = q.series_end()
1433 q.qseries(repo, start=start, summary=opts.get('summary'))
1434 q.qseries(repo, start=start, summary=opts.get('summary'))
1434
1435
1435 def qimport(ui, repo, *filename, **opts):
1436 def qimport(ui, repo, *filename, **opts):
1436 """import a patch
1437 """import a patch
1437
1438
1438 The patch will have the same name as its source file unless you
1439 The patch will have the same name as its source file unless you
1439 give it a new one with --name.
1440 give it a new one with --name.
1440
1441
1441 You can register an existing patch inside the patch directory
1442 You can register an existing patch inside the patch directory
1442 with the --existing flag.
1443 with the --existing flag.
1443
1444
1444 With --force, an existing patch of the same name will be overwritten.
1445 With --force, an existing patch of the same name will be overwritten.
1445
1446
1446 An existing changeset may be placed under mq control with --rev
1447 An existing changeset may be placed under mq control with --rev
1447 (e.g. qimport --rev tip -n patch will place tip under mq control).
1448 (e.g. qimport --rev tip -n patch will place tip under mq control).
1448 With --git, patches imported with --rev will use the git diff
1449 With --git, patches imported with --rev will use the git diff
1449 format.
1450 format.
1450 """
1451 """
1451 q = repo.mq
1452 q = repo.mq
1452 q.qimport(repo, filename, patchname=opts['name'],
1453 q.qimport(repo, filename, patchname=opts['name'],
1453 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1454 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1454 git=opts['git'])
1455 git=opts['git'])
1455 q.save_dirty()
1456 q.save_dirty()
1456 return 0
1457 return 0
1457
1458
1458 def init(ui, repo, **opts):
1459 def init(ui, repo, **opts):
1459 """init a new queue repository
1460 """init a new queue repository
1460
1461
1461 The queue repository is unversioned by default. If -c is
1462 The queue repository is unversioned by default. If -c is
1462 specified, qinit will create a separate nested repository
1463 specified, qinit will create a separate nested repository
1463 for patches. Use qcommit to commit changes to this queue
1464 for patches. Use qcommit to commit changes to this queue
1464 repository."""
1465 repository."""
1465 q = repo.mq
1466 q = repo.mq
1466 r = q.init(repo, create=opts['create_repo'])
1467 r = q.init(repo, create=opts['create_repo'])
1467 q.save_dirty()
1468 q.save_dirty()
1468 if r:
1469 if r:
1469 fp = r.wopener('.hgignore', 'w')
1470 fp = r.wopener('.hgignore', 'w')
1470 print >> fp, 'syntax: glob'
1471 print >> fp, 'syntax: glob'
1471 print >> fp, 'status'
1472 print >> fp, 'status'
1472 print >> fp, 'guards'
1473 print >> fp, 'guards'
1473 fp.close()
1474 fp.close()
1474 r.wopener('series', 'w').close()
1475 r.wopener('series', 'w').close()
1475 r.add(['.hgignore', 'series'])
1476 r.add(['.hgignore', 'series'])
1476 return 0
1477 return 0
1477
1478
1478 def clone(ui, source, dest=None, **opts):
1479 def clone(ui, source, dest=None, **opts):
1479 '''clone main and patch repository at same time
1480 '''clone main and patch repository at same time
1480
1481
1481 If source is local, destination will have no patches applied. If
1482 If source is local, destination will have no patches applied. If
1482 source is remote, this command can not check if patches are
1483 source is remote, this command can not check if patches are
1483 applied in source, so cannot guarantee that patches are not
1484 applied in source, so cannot guarantee that patches are not
1484 applied in destination. If you clone remote repository, be sure
1485 applied in destination. If you clone remote repository, be sure
1485 before that it has no patches applied.
1486 before that it has no patches applied.
1486
1487
1487 Source patch repository is looked for in <src>/.hg/patches by
1488 Source patch repository is looked for in <src>/.hg/patches by
1488 default. Use -p <url> to change.
1489 default. Use -p <url> to change.
1489 '''
1490 '''
1490 commands.setremoteconfig(ui, opts)
1491 commands.setremoteconfig(ui, opts)
1491 if dest is None:
1492 if dest is None:
1492 dest = hg.defaultdest(source)
1493 dest = hg.defaultdest(source)
1493 sr = hg.repository(ui, ui.expandpath(source))
1494 sr = hg.repository(ui, ui.expandpath(source))
1494 qbase, destrev = None, None
1495 qbase, destrev = None, None
1495 if sr.local():
1496 if sr.local():
1496 reposetup(ui, sr)
1497 reposetup(ui, sr)
1497 if sr.mq.applied:
1498 if sr.mq.applied:
1498 qbase = revlog.bin(sr.mq.applied[0].rev)
1499 qbase = revlog.bin(sr.mq.applied[0].rev)
1499 if not hg.islocal(dest):
1500 if not hg.islocal(dest):
1500 destrev = sr.parents(qbase)[0]
1501 destrev = sr.parents(qbase)[0]
1501 ui.note(_('cloning main repo\n'))
1502 ui.note(_('cloning main repo\n'))
1502 sr, dr = hg.clone(ui, sr, dest,
1503 sr, dr = hg.clone(ui, sr, dest,
1503 pull=opts['pull'],
1504 pull=opts['pull'],
1504 rev=destrev,
1505 rev=destrev,
1505 update=False,
1506 update=False,
1506 stream=opts['uncompressed'])
1507 stream=opts['uncompressed'])
1507 ui.note(_('cloning patch repo\n'))
1508 ui.note(_('cloning patch repo\n'))
1508 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1509 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1509 dr.url() + '/.hg/patches',
1510 dr.url() + '/.hg/patches',
1510 pull=opts['pull'],
1511 pull=opts['pull'],
1511 update=not opts['noupdate'],
1512 update=not opts['noupdate'],
1512 stream=opts['uncompressed'])
1513 stream=opts['uncompressed'])
1513 if dr.local():
1514 if dr.local():
1514 if qbase:
1515 if qbase:
1515 ui.note(_('stripping applied patches from destination repo\n'))
1516 ui.note(_('stripping applied patches from destination repo\n'))
1516 reposetup(ui, dr)
1517 reposetup(ui, dr)
1517 dr.mq.strip(dr, qbase, update=False, backup=None)
1518 dr.mq.strip(dr, qbase, update=False, backup=None)
1518 if not opts['noupdate']:
1519 if not opts['noupdate']:
1519 ui.note(_('updating destination repo\n'))
1520 ui.note(_('updating destination repo\n'))
1520 hg.update(dr, dr.changelog.tip())
1521 hg.update(dr, dr.changelog.tip())
1521
1522
1522 def commit(ui, repo, *pats, **opts):
1523 def commit(ui, repo, *pats, **opts):
1523 """commit changes in the queue repository"""
1524 """commit changes in the queue repository"""
1524 q = repo.mq
1525 q = repo.mq
1525 r = q.qrepo()
1526 r = q.qrepo()
1526 if not r: raise util.Abort('no queue repository')
1527 if not r: raise util.Abort('no queue repository')
1527 commands.commit(r.ui, r, *pats, **opts)
1528 commands.commit(r.ui, r, *pats, **opts)
1528
1529
1529 def series(ui, repo, **opts):
1530 def series(ui, repo, **opts):
1530 """print the entire series file"""
1531 """print the entire series file"""
1531 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1532 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1532 return 0
1533 return 0
1533
1534
1534 def top(ui, repo, **opts):
1535 def top(ui, repo, **opts):
1535 """print the name of the current patch"""
1536 """print the name of the current patch"""
1536 q = repo.mq
1537 q = repo.mq
1537 t = len(q.applied)
1538 t = len(q.applied)
1538 if t:
1539 if t:
1539 return q.qseries(repo, start=t-1, length=1, status='A',
1540 return q.qseries(repo, start=t-1, length=1, status='A',
1540 summary=opts.get('summary'))
1541 summary=opts.get('summary'))
1541 else:
1542 else:
1542 ui.write("No patches applied\n")
1543 ui.write("No patches applied\n")
1543 return 1
1544 return 1
1544
1545
1545 def next(ui, repo, **opts):
1546 def next(ui, repo, **opts):
1546 """print the name of the next patch"""
1547 """print the name of the next patch"""
1547 q = repo.mq
1548 q = repo.mq
1548 end = q.series_end()
1549 end = q.series_end()
1549 if end == len(q.series):
1550 if end == len(q.series):
1550 ui.write("All patches applied\n")
1551 ui.write("All patches applied\n")
1551 return 1
1552 return 1
1552 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1553 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1553
1554
1554 def prev(ui, repo, **opts):
1555 def prev(ui, repo, **opts):
1555 """print the name of the previous patch"""
1556 """print the name of the previous patch"""
1556 q = repo.mq
1557 q = repo.mq
1557 l = len(q.applied)
1558 l = len(q.applied)
1558 if l == 1:
1559 if l == 1:
1559 ui.write("Only one patch applied\n")
1560 ui.write("Only one patch applied\n")
1560 return 1
1561 return 1
1561 if not l:
1562 if not l:
1562 ui.write("No patches applied\n")
1563 ui.write("No patches applied\n")
1563 return 1
1564 return 1
1564 return q.qseries(repo, start=l-2, length=1, status='A',
1565 return q.qseries(repo, start=l-2, length=1, status='A',
1565 summary=opts.get('summary'))
1566 summary=opts.get('summary'))
1566
1567
1567 def new(ui, repo, patch, **opts):
1568 def new(ui, repo, patch, **opts):
1568 """create a new patch
1569 """create a new patch
1569
1570
1570 qnew creates a new patch on top of the currently-applied patch
1571 qnew creates a new patch on top of the currently-applied patch
1571 (if any). It will refuse to run if there are any outstanding
1572 (if any). It will refuse to run if there are any outstanding
1572 changes unless -f is specified, in which case the patch will
1573 changes unless -f is specified, in which case the patch will
1573 be initialised with them.
1574 be initialised with them.
1574
1575
1575 -e, -m or -l set the patch header as well as the commit message.
1576 -e, -m or -l set the patch header as well as the commit message.
1576 If none is specified, the patch header is empty and the
1577 If none is specified, the patch header is empty and the
1577 commit message is 'New patch: PATCH'"""
1578 commit message is 'New patch: PATCH'"""
1578 q = repo.mq
1579 q = repo.mq
1579 message = commands.logmessage(opts)
1580 message = commands.logmessage(opts)
1580 if opts['edit']:
1581 if opts['edit']:
1581 message = ui.edit(message, ui.username())
1582 message = ui.edit(message, ui.username())
1582 q.new(repo, patch, msg=message, force=opts['force'])
1583 q.new(repo, patch, msg=message, force=opts['force'])
1583 q.save_dirty()
1584 q.save_dirty()
1584 return 0
1585 return 0
1585
1586
1586 def refresh(ui, repo, *pats, **opts):
1587 def refresh(ui, repo, *pats, **opts):
1587 """update the current patch
1588 """update the current patch
1588
1589
1589 If any file patterns are provided, the refreshed patch will contain only
1590 If any file patterns are provided, the refreshed patch will contain only
1590 the modifications that match those patterns; the remaining modifications
1591 the modifications that match those patterns; the remaining modifications
1591 will remain in the working directory.
1592 will remain in the working directory.
1592
1593
1593 hg add/remove/copy/rename work as usual, though you might want to use
1594 hg add/remove/copy/rename work as usual, though you might want to use
1594 git-style patches (--git or [diff] git=1) to track copies and renames.
1595 git-style patches (--git or [diff] git=1) to track copies and renames.
1595 """
1596 """
1596 q = repo.mq
1597 q = repo.mq
1597 message = commands.logmessage(opts)
1598 message = commands.logmessage(opts)
1598 if opts['edit']:
1599 if opts['edit']:
1599 if message:
1600 if message:
1600 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1601 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1601 patch = q.applied[-1].name
1602 patch = q.applied[-1].name
1602 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1603 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1603 message = ui.edit('\n'.join(message), user or ui.username())
1604 message = ui.edit('\n'.join(message), user or ui.username())
1604 ret = q.refresh(repo, pats, msg=message, **opts)
1605 ret = q.refresh(repo, pats, msg=message, **opts)
1605 q.save_dirty()
1606 q.save_dirty()
1606 return ret
1607 return ret
1607
1608
1608 def diff(ui, repo, *pats, **opts):
1609 def diff(ui, repo, *pats, **opts):
1609 """diff of the current patch"""
1610 """diff of the current patch"""
1610 repo.mq.diff(repo, pats, opts)
1611 repo.mq.diff(repo, pats, opts)
1611 return 0
1612 return 0
1612
1613
1613 def fold(ui, repo, *files, **opts):
1614 def fold(ui, repo, *files, **opts):
1614 """fold the named patches into the current patch
1615 """fold the named patches into the current patch
1615
1616
1616 Patches must not yet be applied. Each patch will be successively
1617 Patches must not yet be applied. Each patch will be successively
1617 applied to the current patch in the order given. If all the
1618 applied to the current patch in the order given. If all the
1618 patches apply successfully, the current patch will be refreshed
1619 patches apply successfully, the current patch will be refreshed
1619 with the new cumulative patch, and the folded patches will
1620 with the new cumulative patch, and the folded patches will
1620 be deleted. With -k/--keep, the folded patch files will not
1621 be deleted. With -k/--keep, the folded patch files will not
1621 be removed afterwards.
1622 be removed afterwards.
1622
1623
1623 The header for each folded patch will be concatenated with
1624 The header for each folded patch will be concatenated with
1624 the current patch header, separated by a line of '* * *'."""
1625 the current patch header, separated by a line of '* * *'."""
1625
1626
1626 q = repo.mq
1627 q = repo.mq
1627
1628
1628 if not files:
1629 if not files:
1629 raise util.Abort(_('qfold requires at least one patch name'))
1630 raise util.Abort(_('qfold requires at least one patch name'))
1630 if not q.check_toppatch(repo):
1631 if not q.check_toppatch(repo):
1631 raise util.Abort(_('No patches applied'))
1632 raise util.Abort(_('No patches applied'))
1632
1633
1633 message = commands.logmessage(opts)
1634 message = commands.logmessage(opts)
1634 if opts['edit']:
1635 if opts['edit']:
1635 if message:
1636 if message:
1636 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1637 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1637
1638
1638 parent = q.lookup('qtip')
1639 parent = q.lookup('qtip')
1639 patches = []
1640 patches = []
1640 messages = []
1641 messages = []
1641 for f in files:
1642 for f in files:
1642 p = q.lookup(f)
1643 p = q.lookup(f)
1643 if p in patches or p == parent:
1644 if p in patches or p == parent:
1644 ui.warn(_('Skipping already folded patch %s') % p)
1645 ui.warn(_('Skipping already folded patch %s') % p)
1645 if q.isapplied(p):
1646 if q.isapplied(p):
1646 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1647 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1647 patches.append(p)
1648 patches.append(p)
1648
1649
1649 for p in patches:
1650 for p in patches:
1650 if not message:
1651 if not message:
1651 messages.append(q.readheaders(p)[0])
1652 messages.append(q.readheaders(p)[0])
1652 pf = q.join(p)
1653 pf = q.join(p)
1653 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1654 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1654 if not patchsuccess:
1655 if not patchsuccess:
1655 raise util.Abort(_('Error folding patch %s') % p)
1656 raise util.Abort(_('Error folding patch %s') % p)
1656 patch.updatedir(ui, repo, files)
1657 patch.updatedir(ui, repo, files)
1657
1658
1658 if not message:
1659 if not message:
1659 message, comments, user = q.readheaders(parent)[0:3]
1660 message, comments, user = q.readheaders(parent)[0:3]
1660 for msg in messages:
1661 for msg in messages:
1661 message.append('* * *')
1662 message.append('* * *')
1662 message.extend(msg)
1663 message.extend(msg)
1663 message = '\n'.join(message)
1664 message = '\n'.join(message)
1664
1665
1665 if opts['edit']:
1666 if opts['edit']:
1666 message = ui.edit(message, user or ui.username())
1667 message = ui.edit(message, user or ui.username())
1667
1668
1668 q.refresh(repo, msg=message)
1669 q.refresh(repo, msg=message)
1669 q.delete(repo, patches, opts)
1670 q.delete(repo, patches, opts)
1670 q.save_dirty()
1671 q.save_dirty()
1671
1672
1672 def guard(ui, repo, *args, **opts):
1673 def guard(ui, repo, *args, **opts):
1673 '''set or print guards for a patch
1674 '''set or print guards for a patch
1674
1675
1675 Guards control whether a patch can be pushed. A patch with no
1676 Guards control whether a patch can be pushed. A patch with no
1676 guards is always pushed. A patch with a positive guard ("+foo") is
1677 guards is always pushed. A patch with a positive guard ("+foo") is
1677 pushed only if the qselect command has activated it. A patch with
1678 pushed only if the qselect command has activated it. A patch with
1678 a negative guard ("-foo") is never pushed if the qselect command
1679 a negative guard ("-foo") is never pushed if the qselect command
1679 has activated it.
1680 has activated it.
1680
1681
1681 With no arguments, print the currently active guards.
1682 With no arguments, print the currently active guards.
1682 With arguments, set guards for the named patch.
1683 With arguments, set guards for the named patch.
1683
1684
1684 To set a negative guard "-foo" on topmost patch ("--" is needed so
1685 To set a negative guard "-foo" on topmost patch ("--" is needed so
1685 hg will not interpret "-foo" as an option):
1686 hg will not interpret "-foo" as an option):
1686 hg qguard -- -foo
1687 hg qguard -- -foo
1687
1688
1688 To set guards on another patch:
1689 To set guards on another patch:
1689 hg qguard other.patch +2.6.17 -stable
1690 hg qguard other.patch +2.6.17 -stable
1690 '''
1691 '''
1691 def status(idx):
1692 def status(idx):
1692 guards = q.series_guards[idx] or ['unguarded']
1693 guards = q.series_guards[idx] or ['unguarded']
1693 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1694 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1694 q = repo.mq
1695 q = repo.mq
1695 patch = None
1696 patch = None
1696 args = list(args)
1697 args = list(args)
1697 if opts['list']:
1698 if opts['list']:
1698 if args or opts['none']:
1699 if args or opts['none']:
1699 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1700 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1700 for i in xrange(len(q.series)):
1701 for i in xrange(len(q.series)):
1701 status(i)
1702 status(i)
1702 return
1703 return
1703 if not args or args[0][0:1] in '-+':
1704 if not args or args[0][0:1] in '-+':
1704 if not q.applied:
1705 if not q.applied:
1705 raise util.Abort(_('no patches applied'))
1706 raise util.Abort(_('no patches applied'))
1706 patch = q.applied[-1].name
1707 patch = q.applied[-1].name
1707 if patch is None and args[0][0:1] not in '-+':
1708 if patch is None and args[0][0:1] not in '-+':
1708 patch = args.pop(0)
1709 patch = args.pop(0)
1709 if patch is None:
1710 if patch is None:
1710 raise util.Abort(_('no patch to work with'))
1711 raise util.Abort(_('no patch to work with'))
1711 if args or opts['none']:
1712 if args or opts['none']:
1712 q.set_guards(q.find_series(patch), args)
1713 q.set_guards(q.find_series(patch), args)
1713 q.save_dirty()
1714 q.save_dirty()
1714 else:
1715 else:
1715 status(q.series.index(q.lookup(patch)))
1716 status(q.series.index(q.lookup(patch)))
1716
1717
1717 def header(ui, repo, patch=None):
1718 def header(ui, repo, patch=None):
1718 """Print the header of the topmost or specified patch"""
1719 """Print the header of the topmost or specified patch"""
1719 q = repo.mq
1720 q = repo.mq
1720
1721
1721 if patch:
1722 if patch:
1722 patch = q.lookup(patch)
1723 patch = q.lookup(patch)
1723 else:
1724 else:
1724 if not q.applied:
1725 if not q.applied:
1725 ui.write('No patches applied\n')
1726 ui.write('No patches applied\n')
1726 return 1
1727 return 1
1727 patch = q.lookup('qtip')
1728 patch = q.lookup('qtip')
1728 message = repo.mq.readheaders(patch)[0]
1729 message = repo.mq.readheaders(patch)[0]
1729
1730
1730 ui.write('\n'.join(message) + '\n')
1731 ui.write('\n'.join(message) + '\n')
1731
1732
1732 def lastsavename(path):
1733 def lastsavename(path):
1733 (directory, base) = os.path.split(path)
1734 (directory, base) = os.path.split(path)
1734 names = os.listdir(directory)
1735 names = os.listdir(directory)
1735 namere = re.compile("%s.([0-9]+)" % base)
1736 namere = re.compile("%s.([0-9]+)" % base)
1736 maxindex = None
1737 maxindex = None
1737 maxname = None
1738 maxname = None
1738 for f in names:
1739 for f in names:
1739 m = namere.match(f)
1740 m = namere.match(f)
1740 if m:
1741 if m:
1741 index = int(m.group(1))
1742 index = int(m.group(1))
1742 if maxindex == None or index > maxindex:
1743 if maxindex == None or index > maxindex:
1743 maxindex = index
1744 maxindex = index
1744 maxname = f
1745 maxname = f
1745 if maxname:
1746 if maxname:
1746 return (os.path.join(directory, maxname), maxindex)
1747 return (os.path.join(directory, maxname), maxindex)
1747 return (None, None)
1748 return (None, None)
1748
1749
1749 def savename(path):
1750 def savename(path):
1750 (last, index) = lastsavename(path)
1751 (last, index) = lastsavename(path)
1751 if last is None:
1752 if last is None:
1752 index = 0
1753 index = 0
1753 newpath = path + ".%d" % (index + 1)
1754 newpath = path + ".%d" % (index + 1)
1754 return newpath
1755 return newpath
1755
1756
1756 def push(ui, repo, patch=None, **opts):
1757 def push(ui, repo, patch=None, **opts):
1757 """push the next patch onto the stack"""
1758 """push the next patch onto the stack"""
1758 q = repo.mq
1759 q = repo.mq
1759 mergeq = None
1760 mergeq = None
1760
1761
1761 if opts['all']:
1762 if opts['all']:
1762 if not q.series:
1763 if not q.series:
1763 raise util.Abort(_('no patches in series'))
1764 raise util.Abort(_('no patches in series'))
1764 patch = q.series[-1]
1765 patch = q.series[-1]
1765 if opts['merge']:
1766 if opts['merge']:
1766 if opts['name']:
1767 if opts['name']:
1767 newpath = opts['name']
1768 newpath = opts['name']
1768 else:
1769 else:
1769 newpath, i = lastsavename(q.path)
1770 newpath, i = lastsavename(q.path)
1770 if not newpath:
1771 if not newpath:
1771 ui.warn("no saved queues found, please use -n\n")
1772 ui.warn("no saved queues found, please use -n\n")
1772 return 1
1773 return 1
1773 mergeq = queue(ui, repo.join(""), newpath)
1774 mergeq = queue(ui, repo.join(""), newpath)
1774 ui.warn("merging with queue at: %s\n" % mergeq.path)
1775 ui.warn("merging with queue at: %s\n" % mergeq.path)
1775 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1776 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1776 mergeq=mergeq)
1777 mergeq=mergeq)
1777 q.save_dirty()
1778 q.save_dirty()
1778 return ret
1779 return ret
1779
1780
1780 def pop(ui, repo, patch=None, **opts):
1781 def pop(ui, repo, patch=None, **opts):
1781 """pop the current patch off the stack"""
1782 """pop the current patch off the stack"""
1782 localupdate = True
1783 localupdate = True
1783 if opts['name']:
1784 if opts['name']:
1784 q = queue(ui, repo.join(""), repo.join(opts['name']))
1785 q = queue(ui, repo.join(""), repo.join(opts['name']))
1785 ui.warn('using patch queue: %s\n' % q.path)
1786 ui.warn('using patch queue: %s\n' % q.path)
1786 localupdate = False
1787 localupdate = False
1787 else:
1788 else:
1788 q = repo.mq
1789 q = repo.mq
1789 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1790 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1790 q.save_dirty()
1791 q.save_dirty()
1791 return 0
1792 return 0
1792
1793
1793 def rename(ui, repo, patch, name=None, **opts):
1794 def rename(ui, repo, patch, name=None, **opts):
1794 """rename a patch
1795 """rename a patch
1795
1796
1796 With one argument, renames the current patch to PATCH1.
1797 With one argument, renames the current patch to PATCH1.
1797 With two arguments, renames PATCH1 to PATCH2."""
1798 With two arguments, renames PATCH1 to PATCH2."""
1798
1799
1799 q = repo.mq
1800 q = repo.mq
1800
1801
1801 if not name:
1802 if not name:
1802 name = patch
1803 name = patch
1803 patch = None
1804 patch = None
1804
1805
1805 if patch:
1806 if patch:
1806 patch = q.lookup(patch)
1807 patch = q.lookup(patch)
1807 else:
1808 else:
1808 if not q.applied:
1809 if not q.applied:
1809 ui.write(_('No patches applied\n'))
1810 ui.write(_('No patches applied\n'))
1810 return
1811 return
1811 patch = q.lookup('qtip')
1812 patch = q.lookup('qtip')
1812 absdest = q.join(name)
1813 absdest = q.join(name)
1813 if os.path.isdir(absdest):
1814 if os.path.isdir(absdest):
1814 name = normname(os.path.join(name, os.path.basename(patch)))
1815 name = normname(os.path.join(name, os.path.basename(patch)))
1815 absdest = q.join(name)
1816 absdest = q.join(name)
1816 if os.path.exists(absdest):
1817 if os.path.exists(absdest):
1817 raise util.Abort(_('%s already exists') % absdest)
1818 raise util.Abort(_('%s already exists') % absdest)
1818
1819
1819 if name in q.series:
1820 if name in q.series:
1820 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1821 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1821
1822
1822 if ui.verbose:
1823 if ui.verbose:
1823 ui.write('Renaming %s to %s\n' % (patch, name))
1824 ui.write('Renaming %s to %s\n' % (patch, name))
1824 i = q.find_series(patch)
1825 i = q.find_series(patch)
1825 guards = q.guard_re.findall(q.full_series[i])
1826 guards = q.guard_re.findall(q.full_series[i])
1826 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1827 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1827 q.parse_series()
1828 q.parse_series()
1828 q.series_dirty = 1
1829 q.series_dirty = 1
1829
1830
1830 info = q.isapplied(patch)
1831 info = q.isapplied(patch)
1831 if info:
1832 if info:
1832 q.applied[info[0]] = statusentry(info[1], name)
1833 q.applied[info[0]] = statusentry(info[1], name)
1833 q.applied_dirty = 1
1834 q.applied_dirty = 1
1834
1835
1835 util.rename(q.join(patch), absdest)
1836 util.rename(q.join(patch), absdest)
1836 r = q.qrepo()
1837 r = q.qrepo()
1837 if r:
1838 if r:
1838 wlock = r.wlock()
1839 wlock = r.wlock()
1839 if r.dirstate.state(name) == 'r':
1840 if r.dirstate.state(name) == 'r':
1840 r.undelete([name], wlock)
1841 r.undelete([name], wlock)
1841 r.copy(patch, name, wlock)
1842 r.copy(patch, name, wlock)
1842 r.remove([patch], False, wlock)
1843 r.remove([patch], False, wlock)
1843
1844
1844 q.save_dirty()
1845 q.save_dirty()
1845
1846
1846 def restore(ui, repo, rev, **opts):
1847 def restore(ui, repo, rev, **opts):
1847 """restore the queue state saved by a rev"""
1848 """restore the queue state saved by a rev"""
1848 rev = repo.lookup(rev)
1849 rev = repo.lookup(rev)
1849 q = repo.mq
1850 q = repo.mq
1850 q.restore(repo, rev, delete=opts['delete'],
1851 q.restore(repo, rev, delete=opts['delete'],
1851 qupdate=opts['update'])
1852 qupdate=opts['update'])
1852 q.save_dirty()
1853 q.save_dirty()
1853 return 0
1854 return 0
1854
1855
1855 def save(ui, repo, **opts):
1856 def save(ui, repo, **opts):
1856 """save current queue state"""
1857 """save current queue state"""
1857 q = repo.mq
1858 q = repo.mq
1858 message = commands.logmessage(opts)
1859 message = commands.logmessage(opts)
1859 ret = q.save(repo, msg=message)
1860 ret = q.save(repo, msg=message)
1860 if ret:
1861 if ret:
1861 return ret
1862 return ret
1862 q.save_dirty()
1863 q.save_dirty()
1863 if opts['copy']:
1864 if opts['copy']:
1864 path = q.path
1865 path = q.path
1865 if opts['name']:
1866 if opts['name']:
1866 newpath = os.path.join(q.basepath, opts['name'])
1867 newpath = os.path.join(q.basepath, opts['name'])
1867 if os.path.exists(newpath):
1868 if os.path.exists(newpath):
1868 if not os.path.isdir(newpath):
1869 if not os.path.isdir(newpath):
1869 raise util.Abort(_('destination %s exists and is not '
1870 raise util.Abort(_('destination %s exists and is not '
1870 'a directory') % newpath)
1871 'a directory') % newpath)
1871 if not opts['force']:
1872 if not opts['force']:
1872 raise util.Abort(_('destination %s exists, '
1873 raise util.Abort(_('destination %s exists, '
1873 'use -f to force') % newpath)
1874 'use -f to force') % newpath)
1874 else:
1875 else:
1875 newpath = savename(path)
1876 newpath = savename(path)
1876 ui.warn("copy %s to %s\n" % (path, newpath))
1877 ui.warn("copy %s to %s\n" % (path, newpath))
1877 util.copyfiles(path, newpath)
1878 util.copyfiles(path, newpath)
1878 if opts['empty']:
1879 if opts['empty']:
1879 try:
1880 try:
1880 os.unlink(q.join(q.status_path))
1881 os.unlink(q.join(q.status_path))
1881 except:
1882 except:
1882 pass
1883 pass
1883 return 0
1884 return 0
1884
1885
1885 def strip(ui, repo, rev, **opts):
1886 def strip(ui, repo, rev, **opts):
1886 """strip a revision and all later revs on the same branch"""
1887 """strip a revision and all later revs on the same branch"""
1887 rev = repo.lookup(rev)
1888 rev = repo.lookup(rev)
1888 backup = 'all'
1889 backup = 'all'
1889 if opts['backup']:
1890 if opts['backup']:
1890 backup = 'strip'
1891 backup = 'strip'
1891 elif opts['nobackup']:
1892 elif opts['nobackup']:
1892 backup = 'none'
1893 backup = 'none'
1893 update = repo.dirstate.parents()[0] != revlog.nullid
1894 update = repo.dirstate.parents()[0] != revlog.nullid
1894 repo.mq.strip(repo, rev, backup=backup, update=update)
1895 repo.mq.strip(repo, rev, backup=backup, update=update)
1895 return 0
1896 return 0
1896
1897
1897 def select(ui, repo, *args, **opts):
1898 def select(ui, repo, *args, **opts):
1898 '''set or print guarded patches to push
1899 '''set or print guarded patches to push
1899
1900
1900 Use the qguard command to set or print guards on patch, then use
1901 Use the qguard command to set or print guards on patch, then use
1901 qselect to tell mq which guards to use. A patch will be pushed if it
1902 qselect to tell mq which guards to use. A patch will be pushed if it
1902 has no guards or any positive guards match the currently selected guard,
1903 has no guards or any positive guards match the currently selected guard,
1903 but will not be pushed if any negative guards match the current guard.
1904 but will not be pushed if any negative guards match the current guard.
1904 For example:
1905 For example:
1905
1906
1906 qguard foo.patch -stable (negative guard)
1907 qguard foo.patch -stable (negative guard)
1907 qguard bar.patch +stable (positive guard)
1908 qguard bar.patch +stable (positive guard)
1908 qselect stable
1909 qselect stable
1909
1910
1910 This activates the "stable" guard. mq will skip foo.patch (because
1911 This activates the "stable" guard. mq will skip foo.patch (because
1911 it has a negative match) but push bar.patch (because it
1912 it has a negative match) but push bar.patch (because it
1912 has a positive match).
1913 has a positive match).
1913
1914
1914 With no arguments, prints the currently active guards.
1915 With no arguments, prints the currently active guards.
1915 With one argument, sets the active guard.
1916 With one argument, sets the active guard.
1916
1917
1917 Use -n/--none to deactivate guards (no other arguments needed).
1918 Use -n/--none to deactivate guards (no other arguments needed).
1918 When no guards are active, patches with positive guards are skipped
1919 When no guards are active, patches with positive guards are skipped
1919 and patches with negative guards are pushed.
1920 and patches with negative guards are pushed.
1920
1921
1921 qselect can change the guards on applied patches. It does not pop
1922 qselect can change the guards on applied patches. It does not pop
1922 guarded patches by default. Use --pop to pop back to the last applied
1923 guarded patches by default. Use --pop to pop back to the last applied
1923 patch that is not guarded. Use --reapply (which implies --pop) to push
1924 patch that is not guarded. Use --reapply (which implies --pop) to push
1924 back to the current patch afterwards, but skip guarded patches.
1925 back to the current patch afterwards, but skip guarded patches.
1925
1926
1926 Use -s/--series to print a list of all guards in the series file (no
1927 Use -s/--series to print a list of all guards in the series file (no
1927 other arguments needed). Use -v for more information.'''
1928 other arguments needed). Use -v for more information.'''
1928
1929
1929 q = repo.mq
1930 q = repo.mq
1930 guards = q.active()
1931 guards = q.active()
1931 if args or opts['none']:
1932 if args or opts['none']:
1932 old_unapplied = q.unapplied(repo)
1933 old_unapplied = q.unapplied(repo)
1933 old_guarded = [i for i in xrange(len(q.applied)) if
1934 old_guarded = [i for i in xrange(len(q.applied)) if
1934 not q.pushable(i)[0]]
1935 not q.pushable(i)[0]]
1935 q.set_active(args)
1936 q.set_active(args)
1936 q.save_dirty()
1937 q.save_dirty()
1937 if not args:
1938 if not args:
1938 ui.status(_('guards deactivated\n'))
1939 ui.status(_('guards deactivated\n'))
1939 if not opts['pop'] and not opts['reapply']:
1940 if not opts['pop'] and not opts['reapply']:
1940 unapplied = q.unapplied(repo)
1941 unapplied = q.unapplied(repo)
1941 guarded = [i for i in xrange(len(q.applied))
1942 guarded = [i for i in xrange(len(q.applied))
1942 if not q.pushable(i)[0]]
1943 if not q.pushable(i)[0]]
1943 if len(unapplied) != len(old_unapplied):
1944 if len(unapplied) != len(old_unapplied):
1944 ui.status(_('number of unguarded, unapplied patches has '
1945 ui.status(_('number of unguarded, unapplied patches has '
1945 'changed from %d to %d\n') %
1946 'changed from %d to %d\n') %
1946 (len(old_unapplied), len(unapplied)))
1947 (len(old_unapplied), len(unapplied)))
1947 if len(guarded) != len(old_guarded):
1948 if len(guarded) != len(old_guarded):
1948 ui.status(_('number of guarded, applied patches has changed '
1949 ui.status(_('number of guarded, applied patches has changed '
1949 'from %d to %d\n') %
1950 'from %d to %d\n') %
1950 (len(old_guarded), len(guarded)))
1951 (len(old_guarded), len(guarded)))
1951 elif opts['series']:
1952 elif opts['series']:
1952 guards = {}
1953 guards = {}
1953 noguards = 0
1954 noguards = 0
1954 for gs in q.series_guards:
1955 for gs in q.series_guards:
1955 if not gs:
1956 if not gs:
1956 noguards += 1
1957 noguards += 1
1957 for g in gs:
1958 for g in gs:
1958 guards.setdefault(g, 0)
1959 guards.setdefault(g, 0)
1959 guards[g] += 1
1960 guards[g] += 1
1960 if ui.verbose:
1961 if ui.verbose:
1961 guards['NONE'] = noguards
1962 guards['NONE'] = noguards
1962 guards = guards.items()
1963 guards = guards.items()
1963 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
1964 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
1964 if guards:
1965 if guards:
1965 ui.note(_('guards in series file:\n'))
1966 ui.note(_('guards in series file:\n'))
1966 for guard, count in guards:
1967 for guard, count in guards:
1967 ui.note('%2d ' % count)
1968 ui.note('%2d ' % count)
1968 ui.write(guard, '\n')
1969 ui.write(guard, '\n')
1969 else:
1970 else:
1970 ui.note(_('no guards in series file\n'))
1971 ui.note(_('no guards in series file\n'))
1971 else:
1972 else:
1972 if guards:
1973 if guards:
1973 ui.note(_('active guards:\n'))
1974 ui.note(_('active guards:\n'))
1974 for g in guards:
1975 for g in guards:
1975 ui.write(g, '\n')
1976 ui.write(g, '\n')
1976 else:
1977 else:
1977 ui.write(_('no active guards\n'))
1978 ui.write(_('no active guards\n'))
1978 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
1979 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
1979 popped = False
1980 popped = False
1980 if opts['pop'] or opts['reapply']:
1981 if opts['pop'] or opts['reapply']:
1981 for i in xrange(len(q.applied)):
1982 for i in xrange(len(q.applied)):
1982 pushable, reason = q.pushable(i)
1983 pushable, reason = q.pushable(i)
1983 if not pushable:
1984 if not pushable:
1984 ui.status(_('popping guarded patches\n'))
1985 ui.status(_('popping guarded patches\n'))
1985 popped = True
1986 popped = True
1986 if i == 0:
1987 if i == 0:
1987 q.pop(repo, all=True)
1988 q.pop(repo, all=True)
1988 else:
1989 else:
1989 q.pop(repo, i-1)
1990 q.pop(repo, i-1)
1990 break
1991 break
1991 if popped:
1992 if popped:
1992 try:
1993 try:
1993 if reapply:
1994 if reapply:
1994 ui.status(_('reapplying unguarded patches\n'))
1995 ui.status(_('reapplying unguarded patches\n'))
1995 q.push(repo, reapply)
1996 q.push(repo, reapply)
1996 finally:
1997 finally:
1997 q.save_dirty()
1998 q.save_dirty()
1998
1999
1999 def reposetup(ui, repo):
2000 def reposetup(ui, repo):
2000 class mqrepo(repo.__class__):
2001 class mqrepo(repo.__class__):
2001 def abort_if_wdir_patched(self, errmsg, force=False):
2002 def abort_if_wdir_patched(self, errmsg, force=False):
2002 if self.mq.applied and not force:
2003 if self.mq.applied and not force:
2003 parent = revlog.hex(self.dirstate.parents()[0])
2004 parent = revlog.hex(self.dirstate.parents()[0])
2004 if parent in [s.rev for s in self.mq.applied]:
2005 if parent in [s.rev for s in self.mq.applied]:
2005 raise util.Abort(errmsg)
2006 raise util.Abort(errmsg)
2006
2007
2007 def commit(self, *args, **opts):
2008 def commit(self, *args, **opts):
2008 if len(args) >= 6:
2009 if len(args) >= 6:
2009 force = args[5]
2010 force = args[5]
2010 else:
2011 else:
2011 force = opts.get('force')
2012 force = opts.get('force')
2012 self.abort_if_wdir_patched(
2013 self.abort_if_wdir_patched(
2013 _('cannot commit over an applied mq patch'),
2014 _('cannot commit over an applied mq patch'),
2014 force)
2015 force)
2015
2016
2016 return super(mqrepo, self).commit(*args, **opts)
2017 return super(mqrepo, self).commit(*args, **opts)
2017
2018
2018 def push(self, remote, force=False, revs=None):
2019 def push(self, remote, force=False, revs=None):
2019 if self.mq.applied and not force and not revs:
2020 if self.mq.applied and not force and not revs:
2020 raise util.Abort(_('source has mq patches applied'))
2021 raise util.Abort(_('source has mq patches applied'))
2021 return super(mqrepo, self).push(remote, force, revs)
2022 return super(mqrepo, self).push(remote, force, revs)
2022
2023
2023 def tags(self):
2024 def tags(self):
2024 if self.tagscache:
2025 if self.tagscache:
2025 return self.tagscache
2026 return self.tagscache
2026
2027
2027 tagscache = super(mqrepo, self).tags()
2028 tagscache = super(mqrepo, self).tags()
2028
2029
2029 q = self.mq
2030 q = self.mq
2030 if not q.applied:
2031 if not q.applied:
2031 return tagscache
2032 return tagscache
2032
2033
2033 mqtags = [(patch.rev, patch.name) for patch in q.applied]
2034 mqtags = [(patch.rev, patch.name) for patch in q.applied]
2034 mqtags.append((mqtags[-1][0], 'qtip'))
2035 mqtags.append((mqtags[-1][0], 'qtip'))
2035 mqtags.append((mqtags[0][0], 'qbase'))
2036 mqtags.append((mqtags[0][0], 'qbase'))
2036 for patch in mqtags:
2037 for patch in mqtags:
2037 if patch[1] in tagscache:
2038 if patch[1] in tagscache:
2038 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2039 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2039 else:
2040 else:
2040 tagscache[patch[1]] = revlog.bin(patch[0])
2041 tagscache[patch[1]] = revlog.bin(patch[0])
2041
2042
2042 return tagscache
2043 return tagscache
2043
2044
2044 def _branchtags(self):
2045 def _branchtags(self):
2045 q = self.mq
2046 q = self.mq
2046 if not q.applied:
2047 if not q.applied:
2047 return super(mqrepo, self)._branchtags()
2048 return super(mqrepo, self)._branchtags()
2048
2049
2049 self.branchcache = {} # avoid recursion in changectx
2050 self.branchcache = {} # avoid recursion in changectx
2050 cl = self.changelog
2051 cl = self.changelog
2051 partial, last, lrev = self._readbranchcache()
2052 partial, last, lrev = self._readbranchcache()
2052
2053
2053 qbase = cl.rev(revlog.bin(q.applied[0].rev))
2054 qbase = cl.rev(revlog.bin(q.applied[0].rev))
2054 start = lrev + 1
2055 start = lrev + 1
2055 if start < qbase:
2056 if start < qbase:
2056 # update the cache (excluding the patches) and save it
2057 # update the cache (excluding the patches) and save it
2057 self._updatebranchcache(partial, lrev+1, qbase)
2058 self._updatebranchcache(partial, lrev+1, qbase)
2058 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2059 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2059 start = qbase
2060 start = qbase
2060 # if start = qbase, the cache is as updated as it should be.
2061 # if start = qbase, the cache is as updated as it should be.
2061 # if start > qbase, the cache includes (part of) the patches.
2062 # if start > qbase, the cache includes (part of) the patches.
2062 # we might as well use it, but we won't save it.
2063 # we might as well use it, but we won't save it.
2063
2064
2064 # update the cache up to the tip
2065 # update the cache up to the tip
2065 self._updatebranchcache(partial, start, cl.count())
2066 self._updatebranchcache(partial, start, cl.count())
2066
2067
2067 return partial
2068 return partial
2068
2069
2069 if repo.local():
2070 if repo.local():
2070 repo.__class__ = mqrepo
2071 repo.__class__ = mqrepo
2071 repo.mq = queue(ui, repo.join(""))
2072 repo.mq = queue(ui, repo.join(""))
2072
2073
2073 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2074 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2074
2075
2075 cmdtable = {
2076 cmdtable = {
2076 "qapplied": (applied, [] + seriesopts, 'hg qapplied [-s] [PATCH]'),
2077 "qapplied": (applied, [] + seriesopts, 'hg qapplied [-s] [PATCH]'),
2077 "qclone": (clone,
2078 "qclone": (clone,
2078 [('', 'pull', None, _('use pull protocol to copy metadata')),
2079 [('', 'pull', None, _('use pull protocol to copy metadata')),
2079 ('U', 'noupdate', None, _('do not update the new working directories')),
2080 ('U', 'noupdate', None, _('do not update the new working directories')),
2080 ('', 'uncompressed', None,
2081 ('', 'uncompressed', None,
2081 _('use uncompressed transfer (fast over LAN)')),
2082 _('use uncompressed transfer (fast over LAN)')),
2082 ('e', 'ssh', '', _('specify ssh command to use')),
2083 ('e', 'ssh', '', _('specify ssh command to use')),
2083 ('p', 'patches', '', _('location of source patch repo')),
2084 ('p', 'patches', '', _('location of source patch repo')),
2084 ('', 'remotecmd', '',
2085 ('', 'remotecmd', '',
2085 _('specify hg command to run on the remote side'))],
2086 _('specify hg command to run on the remote side'))],
2086 'hg qclone [OPTION]... SOURCE [DEST]'),
2087 'hg qclone [OPTION]... SOURCE [DEST]'),
2087 "qcommit|qci":
2088 "qcommit|qci":
2088 (commit,
2089 (commit,
2089 commands.table["^commit|ci"][1],
2090 commands.table["^commit|ci"][1],
2090 'hg qcommit [OPTION]... [FILE]...'),
2091 'hg qcommit [OPTION]... [FILE]...'),
2091 "^qdiff": (diff,
2092 "^qdiff": (diff,
2092 [('g', 'git', None, _('use git extended diff format')),
2093 [('g', 'git', None, _('use git extended diff format')),
2093 ('I', 'include', [], _('include names matching the given patterns')),
2094 ('I', 'include', [], _('include names matching the given patterns')),
2094 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2095 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2095 'hg qdiff [-I] [-X] [FILE]...'),
2096 'hg qdiff [-I] [-X] [FILE]...'),
2096 "qdelete|qremove|qrm":
2097 "qdelete|qremove|qrm":
2097 (delete,
2098 (delete,
2098 [('k', 'keep', None, _('keep patch file')),
2099 [('k', 'keep', None, _('keep patch file')),
2099 ('r', 'rev', [], _('stop managing a revision'))],
2100 ('r', 'rev', [], _('stop managing a revision'))],
2100 'hg qdelete [-k] [-r REV]... PATCH...'),
2101 'hg qdelete [-k] [-r REV]... PATCH...'),
2101 'qfold':
2102 'qfold':
2102 (fold,
2103 (fold,
2103 [('e', 'edit', None, _('edit patch header')),
2104 [('e', 'edit', None, _('edit patch header')),
2104 ('k', 'keep', None, _('keep folded patch files'))
2105 ('k', 'keep', None, _('keep folded patch files'))
2105 ] + commands.commitopts,
2106 ] + commands.commitopts,
2106 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
2107 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
2107 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
2108 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
2108 ('n', 'none', None, _('drop all guards'))],
2109 ('n', 'none', None, _('drop all guards'))],
2109 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
2110 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
2110 'qheader': (header, [],
2111 'qheader': (header, [],
2111 _('hg qheader [PATCH]')),
2112 _('hg qheader [PATCH]')),
2112 "^qimport":
2113 "^qimport":
2113 (qimport,
2114 (qimport,
2114 [('e', 'existing', None, 'import file in patch dir'),
2115 [('e', 'existing', None, 'import file in patch dir'),
2115 ('n', 'name', '', 'patch file name'),
2116 ('n', 'name', '', 'patch file name'),
2116 ('f', 'force', None, 'overwrite existing files'),
2117 ('f', 'force', None, 'overwrite existing files'),
2117 ('r', 'rev', [], 'place existing revisions under mq control'),
2118 ('r', 'rev', [], 'place existing revisions under mq control'),
2118 ('g', 'git', None, _('use git extended diff format'))],
2119 ('g', 'git', None, _('use git extended diff format'))],
2119 'hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...'),
2120 'hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...'),
2120 "^qinit":
2121 "^qinit":
2121 (init,
2122 (init,
2122 [('c', 'create-repo', None, 'create queue repository')],
2123 [('c', 'create-repo', None, 'create queue repository')],
2123 'hg qinit [-c]'),
2124 'hg qinit [-c]'),
2124 "qnew":
2125 "qnew":
2125 (new,
2126 (new,
2126 [('e', 'edit', None, _('edit commit message')),
2127 [('e', 'edit', None, _('edit commit message')),
2127 ('f', 'force', None, _('import uncommitted changes into patch'))
2128 ('f', 'force', None, _('import uncommitted changes into patch'))
2128 ] + commands.commitopts,
2129 ] + commands.commitopts,
2129 'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
2130 'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
2130 "qnext": (next, [] + seriesopts, 'hg qnext [-s]'),
2131 "qnext": (next, [] + seriesopts, 'hg qnext [-s]'),
2131 "qprev": (prev, [] + seriesopts, 'hg qprev [-s]'),
2132 "qprev": (prev, [] + seriesopts, 'hg qprev [-s]'),
2132 "^qpop":
2133 "^qpop":
2133 (pop,
2134 (pop,
2134 [('a', 'all', None, 'pop all patches'),
2135 [('a', 'all', None, 'pop all patches'),
2135 ('n', 'name', '', 'queue name to pop'),
2136 ('n', 'name', '', 'queue name to pop'),
2136 ('f', 'force', None, 'forget any local changes')],
2137 ('f', 'force', None, 'forget any local changes')],
2137 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
2138 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
2138 "^qpush":
2139 "^qpush":
2139 (push,
2140 (push,
2140 [('f', 'force', None, 'apply if the patch has rejects'),
2141 [('f', 'force', None, 'apply if the patch has rejects'),
2141 ('l', 'list', None, 'list patch name in commit text'),
2142 ('l', 'list', None, 'list patch name in commit text'),
2142 ('a', 'all', None, 'apply all patches'),
2143 ('a', 'all', None, 'apply all patches'),
2143 ('m', 'merge', None, 'merge from another queue'),
2144 ('m', 'merge', None, 'merge from another queue'),
2144 ('n', 'name', '', 'merge queue name')],
2145 ('n', 'name', '', 'merge queue name')],
2145 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
2146 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
2146 "^qrefresh":
2147 "^qrefresh":
2147 (refresh,
2148 (refresh,
2148 [('e', 'edit', None, _('edit commit message')),
2149 [('e', 'edit', None, _('edit commit message')),
2149 ('g', 'git', None, _('use git extended diff format')),
2150 ('g', 'git', None, _('use git extended diff format')),
2150 ('s', 'short', None, 'refresh only files already in the patch'),
2151 ('s', 'short', None, 'refresh only files already in the patch'),
2151 ('I', 'include', [], _('include names matching the given patterns')),
2152 ('I', 'include', [], _('include names matching the given patterns')),
2152 ('X', 'exclude', [], _('exclude names matching the given patterns'))
2153 ('X', 'exclude', [], _('exclude names matching the given patterns'))
2153 ] + commands.commitopts,
2154 ] + commands.commitopts,
2154 'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] FILES...'),
2155 'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] FILES...'),
2155 'qrename|qmv':
2156 'qrename|qmv':
2156 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
2157 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
2157 "qrestore":
2158 "qrestore":
2158 (restore,
2159 (restore,
2159 [('d', 'delete', None, 'delete save entry'),
2160 [('d', 'delete', None, 'delete save entry'),
2160 ('u', 'update', None, 'update queue working dir')],
2161 ('u', 'update', None, 'update queue working dir')],
2161 'hg qrestore [-d] [-u] REV'),
2162 'hg qrestore [-d] [-u] REV'),
2162 "qsave":
2163 "qsave":
2163 (save,
2164 (save,
2164 [('c', 'copy', None, 'copy patch directory'),
2165 [('c', 'copy', None, 'copy patch directory'),
2165 ('n', 'name', '', 'copy directory name'),
2166 ('n', 'name', '', 'copy directory name'),
2166 ('e', 'empty', None, 'clear queue status file'),
2167 ('e', 'empty', None, 'clear queue status file'),
2167 ('f', 'force', None, 'force copy')] + commands.commitopts,
2168 ('f', 'force', None, 'force copy')] + commands.commitopts,
2168 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
2169 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
2169 "qselect": (select,
2170 "qselect": (select,
2170 [('n', 'none', None, _('disable all guards')),
2171 [('n', 'none', None, _('disable all guards')),
2171 ('s', 'series', None, _('list all guards in series file')),
2172 ('s', 'series', None, _('list all guards in series file')),
2172 ('', 'pop', None,
2173 ('', 'pop', None,
2173 _('pop to before first guarded applied patch')),
2174 _('pop to before first guarded applied patch')),
2174 ('', 'reapply', None, _('pop, then reapply patches'))],
2175 ('', 'reapply', None, _('pop, then reapply patches'))],
2175 'hg qselect [OPTION...] [GUARD...]'),
2176 'hg qselect [OPTION...] [GUARD...]'),
2176 "qseries":
2177 "qseries":
2177 (series,
2178 (series,
2178 [('m', 'missing', None, 'print patches not in series')] + seriesopts,
2179 [('m', 'missing', None, 'print patches not in series')] + seriesopts,
2179 'hg qseries [-ms]'),
2180 'hg qseries [-ms]'),
2180 "^strip":
2181 "^strip":
2181 (strip,
2182 (strip,
2182 [('f', 'force', None, 'force multi-head removal'),
2183 [('f', 'force', None, 'force multi-head removal'),
2183 ('b', 'backup', None, 'bundle unrelated changesets'),
2184 ('b', 'backup', None, 'bundle unrelated changesets'),
2184 ('n', 'nobackup', None, 'no backups')],
2185 ('n', 'nobackup', None, 'no backups')],
2185 'hg strip [-f] [-b] [-n] REV'),
2186 'hg strip [-f] [-b] [-n] REV'),
2186 "qtop": (top, [] + seriesopts, 'hg qtop [-s]'),
2187 "qtop": (top, [] + seriesopts, 'hg qtop [-s]'),
2187 "qunapplied": (unapplied, [] + seriesopts, 'hg qunapplied [-s] [PATCH]'),
2188 "qunapplied": (unapplied, [] + seriesopts, 'hg qunapplied [-s] [PATCH]'),
2188 }
2189 }
@@ -1,3307 +1,3305 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import demandimport; demandimport.enable()
8 import demandimport; demandimport.enable()
9 from node import *
9 from node import *
10 from i18n import _
10 from i18n import _
11 import bisect, os, re, sys, signal, imp, urllib, pdb, shlex, stat
11 import bisect, os, re, sys, signal, imp, urllib, pdb, shlex, stat
12 import fancyopts, ui, hg, util, lock, revlog, bundlerepo
12 import fancyopts, ui, hg, util, lock, revlog, bundlerepo
13 import difflib, patch, time, help, mdiff, tempfile
13 import difflib, patch, time, help, mdiff, tempfile
14 import traceback, errno, version, atexit, socket
14 import traceback, errno, version, atexit, socket
15 import archival, changegroup, cmdutil, hgweb.server, sshserver
15 import archival, changegroup, cmdutil, hgweb.server, sshserver
16
16
17 class UnknownCommand(Exception):
17 class UnknownCommand(Exception):
18 """Exception raised if command is not in the command table."""
18 """Exception raised if command is not in the command table."""
19 class AmbiguousCommand(Exception):
19 class AmbiguousCommand(Exception):
20 """Exception raised if command shortcut matches more than one command."""
20 """Exception raised if command shortcut matches more than one command."""
21
21
22 def bail_if_changed(repo):
22 def bail_if_changed(repo):
23 modified, added, removed, deleted = repo.status()[:4]
23 modified, added, removed, deleted = repo.status()[:4]
24 if modified or added or removed or deleted:
24 if modified or added or removed or deleted:
25 raise util.Abort(_("outstanding uncommitted changes"))
25 raise util.Abort(_("outstanding uncommitted changes"))
26
26
27 def logmessage(opts):
27 def logmessage(opts):
28 """ get the log message according to -m and -l option """
28 """ get the log message according to -m and -l option """
29 message = opts['message']
29 message = opts['message']
30 logfile = opts['logfile']
30 logfile = opts['logfile']
31
31
32 if message and logfile:
32 if message and logfile:
33 raise util.Abort(_('options --message and --logfile are mutually '
33 raise util.Abort(_('options --message and --logfile are mutually '
34 'exclusive'))
34 'exclusive'))
35 if not message and logfile:
35 if not message and logfile:
36 try:
36 try:
37 if logfile == '-':
37 if logfile == '-':
38 message = sys.stdin.read()
38 message = sys.stdin.read()
39 else:
39 else:
40 message = open(logfile).read()
40 message = open(logfile).read()
41 except IOError, inst:
41 except IOError, inst:
42 raise util.Abort(_("can't read commit message '%s': %s") %
42 raise util.Abort(_("can't read commit message '%s': %s") %
43 (logfile, inst.strerror))
43 (logfile, inst.strerror))
44 return message
44 return message
45
45
46 def setremoteconfig(ui, opts):
46 def setremoteconfig(ui, opts):
47 "copy remote options to ui tree"
47 "copy remote options to ui tree"
48 if opts.get('ssh'):
48 if opts.get('ssh'):
49 ui.setconfig("ui", "ssh", opts['ssh'])
49 ui.setconfig("ui", "ssh", opts['ssh'])
50 if opts.get('remotecmd'):
50 if opts.get('remotecmd'):
51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
52
52
53 # Commands start here, listed alphabetically
53 # Commands start here, listed alphabetically
54
54
55 def add(ui, repo, *pats, **opts):
55 def add(ui, repo, *pats, **opts):
56 """add the specified files on the next commit
56 """add the specified files on the next commit
57
57
58 Schedule files to be version controlled and added to the repository.
58 Schedule files to be version controlled and added to the repository.
59
59
60 The files will be added to the repository at the next commit. To
60 The files will be added to the repository at the next commit. To
61 undo an add before that, see hg revert.
61 undo an add before that, see hg revert.
62
62
63 If no names are given, add all files in the repository.
63 If no names are given, add all files in the repository.
64 """
64 """
65
65
66 names = []
66 names = []
67 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
67 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
68 if exact:
68 if exact:
69 if ui.verbose:
69 if ui.verbose:
70 ui.status(_('adding %s\n') % rel)
70 ui.status(_('adding %s\n') % rel)
71 names.append(abs)
71 names.append(abs)
72 elif repo.dirstate.state(abs) == '?':
72 elif repo.dirstate.state(abs) == '?':
73 ui.status(_('adding %s\n') % rel)
73 ui.status(_('adding %s\n') % rel)
74 names.append(abs)
74 names.append(abs)
75 if not opts.get('dry_run'):
75 if not opts.get('dry_run'):
76 repo.add(names)
76 repo.add(names)
77
77
78 def addremove(ui, repo, *pats, **opts):
78 def addremove(ui, repo, *pats, **opts):
79 """add all new files, delete all missing files
79 """add all new files, delete all missing files
80
80
81 Add all new files and remove all missing files from the repository.
81 Add all new files and remove all missing files from the repository.
82
82
83 New files are ignored if they match any of the patterns in .hgignore. As
83 New files are ignored if they match any of the patterns in .hgignore. As
84 with add, these changes take effect at the next commit.
84 with add, these changes take effect at the next commit.
85
85
86 Use the -s option to detect renamed files. With a parameter > 0,
86 Use the -s option to detect renamed files. With a parameter > 0,
87 this compares every removed file with every added file and records
87 this compares every removed file with every added file and records
88 those similar enough as renames. This option takes a percentage
88 those similar enough as renames. This option takes a percentage
89 between 0 (disabled) and 100 (files must be identical) as its
89 between 0 (disabled) and 100 (files must be identical) as its
90 parameter. Detecting renamed files this way can be expensive.
90 parameter. Detecting renamed files this way can be expensive.
91 """
91 """
92 sim = float(opts.get('similarity') or 0)
92 sim = float(opts.get('similarity') or 0)
93 if sim < 0 or sim > 100:
93 if sim < 0 or sim > 100:
94 raise util.Abort(_('similarity must be between 0 and 100'))
94 raise util.Abort(_('similarity must be between 0 and 100'))
95 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
95 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
96
96
97 def annotate(ui, repo, *pats, **opts):
97 def annotate(ui, repo, *pats, **opts):
98 """show changeset information per file line
98 """show changeset information per file line
99
99
100 List changes in files, showing the revision id responsible for each line
100 List changes in files, showing the revision id responsible for each line
101
101
102 This command is useful to discover who did a change or when a change took
102 This command is useful to discover who did a change or when a change took
103 place.
103 place.
104
104
105 Without the -a option, annotate will avoid processing files it
105 Without the -a option, annotate will avoid processing files it
106 detects as binary. With -a, annotate will generate an annotation
106 detects as binary. With -a, annotate will generate an annotation
107 anyway, probably with undesirable results.
107 anyway, probably with undesirable results.
108 """
108 """
109 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
109 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
110
110
111 if not pats:
111 if not pats:
112 raise util.Abort(_('at least one file name or pattern required'))
112 raise util.Abort(_('at least one file name or pattern required'))
113
113
114 opmap = [['user', lambda x: ui.shortuser(x.user())],
114 opmap = [['user', lambda x: ui.shortuser(x.user())],
115 ['number', lambda x: str(x.rev())],
115 ['number', lambda x: str(x.rev())],
116 ['changeset', lambda x: short(x.node())],
116 ['changeset', lambda x: short(x.node())],
117 ['date', getdate], ['follow', lambda x: x.path()]]
117 ['date', getdate], ['follow', lambda x: x.path()]]
118 if (not opts['user'] and not opts['changeset'] and not opts['date']
118 if (not opts['user'] and not opts['changeset'] and not opts['date']
119 and not opts['follow']):
119 and not opts['follow']):
120 opts['number'] = 1
120 opts['number'] = 1
121
121
122 ctx = repo.changectx(opts['rev'])
122 ctx = repo.changectx(opts['rev'])
123
123
124 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
124 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
125 node=ctx.node()):
125 node=ctx.node()):
126 fctx = ctx.filectx(abs)
126 fctx = ctx.filectx(abs)
127 if not opts['text'] and util.binary(fctx.data()):
127 if not opts['text'] and util.binary(fctx.data()):
128 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
128 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
129 continue
129 continue
130
130
131 lines = fctx.annotate(follow=opts.get('follow'))
131 lines = fctx.annotate(follow=opts.get('follow'))
132 pieces = []
132 pieces = []
133
133
134 for o, f in opmap:
134 for o, f in opmap:
135 if opts[o]:
135 if opts[o]:
136 l = [f(n) for n, dummy in lines]
136 l = [f(n) for n, dummy in lines]
137 if l:
137 if l:
138 m = max(map(len, l))
138 m = max(map(len, l))
139 pieces.append(["%*s" % (m, x) for x in l])
139 pieces.append(["%*s" % (m, x) for x in l])
140
140
141 if pieces:
141 if pieces:
142 for p, l in zip(zip(*pieces), lines):
142 for p, l in zip(zip(*pieces), lines):
143 ui.write("%s: %s" % (" ".join(p), l[1]))
143 ui.write("%s: %s" % (" ".join(p), l[1]))
144
144
145 def archive(ui, repo, dest, **opts):
145 def archive(ui, repo, dest, **opts):
146 '''create unversioned archive of a repository revision
146 '''create unversioned archive of a repository revision
147
147
148 By default, the revision used is the parent of the working
148 By default, the revision used is the parent of the working
149 directory; use "-r" to specify a different revision.
149 directory; use "-r" to specify a different revision.
150
150
151 To specify the type of archive to create, use "-t". Valid
151 To specify the type of archive to create, use "-t". Valid
152 types are:
152 types are:
153
153
154 "files" (default): a directory full of files
154 "files" (default): a directory full of files
155 "tar": tar archive, uncompressed
155 "tar": tar archive, uncompressed
156 "tbz2": tar archive, compressed using bzip2
156 "tbz2": tar archive, compressed using bzip2
157 "tgz": tar archive, compressed using gzip
157 "tgz": tar archive, compressed using gzip
158 "uzip": zip archive, uncompressed
158 "uzip": zip archive, uncompressed
159 "zip": zip archive, compressed using deflate
159 "zip": zip archive, compressed using deflate
160
160
161 The exact name of the destination archive or directory is given
161 The exact name of the destination archive or directory is given
162 using a format string; see "hg help export" for details.
162 using a format string; see "hg help export" for details.
163
163
164 Each member added to an archive file has a directory prefix
164 Each member added to an archive file has a directory prefix
165 prepended. Use "-p" to specify a format string for the prefix.
165 prepended. Use "-p" to specify a format string for the prefix.
166 The default is the basename of the archive, with suffixes removed.
166 The default is the basename of the archive, with suffixes removed.
167 '''
167 '''
168
168
169 node = repo.changectx(opts['rev']).node()
169 node = repo.changectx(opts['rev']).node()
170 dest = cmdutil.make_filename(repo, dest, node)
170 dest = cmdutil.make_filename(repo, dest, node)
171 if os.path.realpath(dest) == repo.root:
171 if os.path.realpath(dest) == repo.root:
172 raise util.Abort(_('repository root cannot be destination'))
172 raise util.Abort(_('repository root cannot be destination'))
173 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
173 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
174 kind = opts.get('type') or 'files'
174 kind = opts.get('type') or 'files'
175 prefix = opts['prefix']
175 prefix = opts['prefix']
176 if dest == '-':
176 if dest == '-':
177 if kind == 'files':
177 if kind == 'files':
178 raise util.Abort(_('cannot archive plain files to stdout'))
178 raise util.Abort(_('cannot archive plain files to stdout'))
179 dest = sys.stdout
179 dest = sys.stdout
180 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
180 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
181 prefix = cmdutil.make_filename(repo, prefix, node)
181 prefix = cmdutil.make_filename(repo, prefix, node)
182 archival.archive(repo, dest, node, kind, not opts['no_decode'],
182 archival.archive(repo, dest, node, kind, not opts['no_decode'],
183 matchfn, prefix)
183 matchfn, prefix)
184
184
185 def backout(ui, repo, rev, **opts):
185 def backout(ui, repo, rev, **opts):
186 '''reverse effect of earlier changeset
186 '''reverse effect of earlier changeset
187
187
188 Commit the backed out changes as a new changeset. The new
188 Commit the backed out changes as a new changeset. The new
189 changeset is a child of the backed out changeset.
189 changeset is a child of the backed out changeset.
190
190
191 If you back out a changeset other than the tip, a new head is
191 If you back out a changeset other than the tip, a new head is
192 created. This head is the parent of the working directory. If
192 created. This head is the parent of the working directory. If
193 you back out an old changeset, your working directory will appear
193 you back out an old changeset, your working directory will appear
194 old after the backout. You should merge the backout changeset
194 old after the backout. You should merge the backout changeset
195 with another head.
195 with another head.
196
196
197 The --merge option remembers the parent of the working directory
197 The --merge option remembers the parent of the working directory
198 before starting the backout, then merges the new head with that
198 before starting the backout, then merges the new head with that
199 changeset afterwards. This saves you from doing the merge by
199 changeset afterwards. This saves you from doing the merge by
200 hand. The result of this merge is not committed, as for a normal
200 hand. The result of this merge is not committed, as for a normal
201 merge.'''
201 merge.'''
202
202
203 bail_if_changed(repo)
203 bail_if_changed(repo)
204 op1, op2 = repo.dirstate.parents()
204 op1, op2 = repo.dirstate.parents()
205 if op2 != nullid:
205 if op2 != nullid:
206 raise util.Abort(_('outstanding uncommitted merge'))
206 raise util.Abort(_('outstanding uncommitted merge'))
207 node = repo.lookup(rev)
207 node = repo.lookup(rev)
208 p1, p2 = repo.changelog.parents(node)
208 p1, p2 = repo.changelog.parents(node)
209 if p1 == nullid:
209 if p1 == nullid:
210 raise util.Abort(_('cannot back out a change with no parents'))
210 raise util.Abort(_('cannot back out a change with no parents'))
211 if p2 != nullid:
211 if p2 != nullid:
212 if not opts['parent']:
212 if not opts['parent']:
213 raise util.Abort(_('cannot back out a merge changeset without '
213 raise util.Abort(_('cannot back out a merge changeset without '
214 '--parent'))
214 '--parent'))
215 p = repo.lookup(opts['parent'])
215 p = repo.lookup(opts['parent'])
216 if p not in (p1, p2):
216 if p not in (p1, p2):
217 raise util.Abort(_('%s is not a parent of %s') %
217 raise util.Abort(_('%s is not a parent of %s') %
218 (short(p), short(node)))
218 (short(p), short(node)))
219 parent = p
219 parent = p
220 else:
220 else:
221 if opts['parent']:
221 if opts['parent']:
222 raise util.Abort(_('cannot use --parent on non-merge changeset'))
222 raise util.Abort(_('cannot use --parent on non-merge changeset'))
223 parent = p1
223 parent = p1
224 hg.clean(repo, node, show_stats=False)
224 hg.clean(repo, node, show_stats=False)
225 revert_opts = opts.copy()
225 revert_opts = opts.copy()
226 revert_opts['date'] = None
226 revert_opts['date'] = None
227 revert_opts['all'] = True
227 revert_opts['all'] = True
228 revert_opts['rev'] = hex(parent)
228 revert_opts['rev'] = hex(parent)
229 revert(ui, repo, **revert_opts)
229 revert(ui, repo, **revert_opts)
230 commit_opts = opts.copy()
230 commit_opts = opts.copy()
231 commit_opts['addremove'] = False
231 commit_opts['addremove'] = False
232 if not commit_opts['message'] and not commit_opts['logfile']:
232 if not commit_opts['message'] and not commit_opts['logfile']:
233 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
233 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
234 commit_opts['force_editor'] = True
234 commit_opts['force_editor'] = True
235 commit(ui, repo, **commit_opts)
235 commit(ui, repo, **commit_opts)
236 def nice(node):
236 def nice(node):
237 return '%d:%s' % (repo.changelog.rev(node), short(node))
237 return '%d:%s' % (repo.changelog.rev(node), short(node))
238 ui.status(_('changeset %s backs out changeset %s\n') %
238 ui.status(_('changeset %s backs out changeset %s\n') %
239 (nice(repo.changelog.tip()), nice(node)))
239 (nice(repo.changelog.tip()), nice(node)))
240 if op1 != node:
240 if op1 != node:
241 if opts['merge']:
241 if opts['merge']:
242 ui.status(_('merging with changeset %s\n') % nice(op1))
242 ui.status(_('merging with changeset %s\n') % nice(op1))
243 hg.merge(repo, hex(op1))
243 hg.merge(repo, hex(op1))
244 else:
244 else:
245 ui.status(_('the backout changeset is a new head - '
245 ui.status(_('the backout changeset is a new head - '
246 'do not forget to merge\n'))
246 'do not forget to merge\n'))
247 ui.status(_('(use "backout --merge" '
247 ui.status(_('(use "backout --merge" '
248 'if you want to auto-merge)\n'))
248 'if you want to auto-merge)\n'))
249
249
250 def branch(ui, repo, label=None):
250 def branch(ui, repo, label=None):
251 """set or show the current branch name
251 """set or show the current branch name
252
252
253 With <name>, set the current branch name. Otherwise, show the
253 With <name>, set the current branch name. Otherwise, show the
254 current branch name.
254 current branch name.
255 """
255 """
256
256
257 if label is not None:
257 if label is not None:
258 repo.opener("branch", "w").write(util.fromlocal(label) + '\n')
258 repo.opener("branch", "w").write(util.fromlocal(label) + '\n')
259 else:
259 else:
260 b = util.tolocal(repo.workingctx().branch())
260 b = util.tolocal(repo.workingctx().branch())
261 if b:
261 if b:
262 ui.write("%s\n" % b)
262 ui.write("%s\n" % b)
263
263
264 def branches(ui, repo):
264 def branches(ui, repo):
265 """list repository named branches
265 """list repository named branches
266
266
267 List the repository's named branches.
267 List the repository's named branches.
268 """
268 """
269 b = repo.branchtags()
269 b = repo.branchtags()
270 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
270 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
271 l.sort()
271 l.sort()
272 for r, n, t in l:
272 for r, n, t in l:
273 hexfunc = ui.debugflag and hex or short
273 hexfunc = ui.debugflag and hex or short
274 if ui.quiet:
274 if ui.quiet:
275 ui.write("%s\n" % t)
275 ui.write("%s\n" % t)
276 else:
276 else:
277 t = util.localsub(t, 30)
277 t = util.localsub(t, 30)
278 t += " " * (30 - util.locallen(t))
278 t += " " * (30 - util.locallen(t))
279 ui.write("%s %s:%s\n" % (t, -r, hexfunc(n)))
279 ui.write("%s %s:%s\n" % (t, -r, hexfunc(n)))
280
280
281 def bundle(ui, repo, fname, dest=None, **opts):
281 def bundle(ui, repo, fname, dest=None, **opts):
282 """create a changegroup file
282 """create a changegroup file
283
283
284 Generate a compressed changegroup file collecting changesets not
284 Generate a compressed changegroup file collecting changesets not
285 found in the other repository.
285 found in the other repository.
286
286
287 If no destination repository is specified the destination is assumed
287 If no destination repository is specified the destination is assumed
288 to have all the nodes specified by one or more --base parameters.
288 to have all the nodes specified by one or more --base parameters.
289
289
290 The bundle file can then be transferred using conventional means and
290 The bundle file can then be transferred using conventional means and
291 applied to another repository with the unbundle or pull command.
291 applied to another repository with the unbundle or pull command.
292 This is useful when direct push and pull are not available or when
292 This is useful when direct push and pull are not available or when
293 exporting an entire repository is undesirable.
293 exporting an entire repository is undesirable.
294
294
295 Applying bundles preserves all changeset contents including
295 Applying bundles preserves all changeset contents including
296 permissions, copy/rename information, and revision history.
296 permissions, copy/rename information, and revision history.
297 """
297 """
298 revs = opts.get('rev') or None
298 revs = opts.get('rev') or None
299 if revs:
299 if revs:
300 revs = [repo.lookup(rev) for rev in revs]
300 revs = [repo.lookup(rev) for rev in revs]
301 base = opts.get('base')
301 base = opts.get('base')
302 if base:
302 if base:
303 if dest:
303 if dest:
304 raise util.Abort(_("--base is incompatible with specifiying "
304 raise util.Abort(_("--base is incompatible with specifiying "
305 "a destination"))
305 "a destination"))
306 base = [repo.lookup(rev) for rev in base]
306 base = [repo.lookup(rev) for rev in base]
307 # create the right base
307 # create the right base
308 # XXX: nodesbetween / changegroup* should be "fixed" instead
308 # XXX: nodesbetween / changegroup* should be "fixed" instead
309 o = []
309 o = []
310 has = {nullid: None}
310 has = {nullid: None}
311 for n in base:
311 for n in base:
312 has.update(repo.changelog.reachable(n))
312 has.update(repo.changelog.reachable(n))
313 if revs:
313 if revs:
314 visit = list(revs)
314 visit = list(revs)
315 else:
315 else:
316 visit = repo.changelog.heads()
316 visit = repo.changelog.heads()
317 seen = {}
317 seen = {}
318 while visit:
318 while visit:
319 n = visit.pop(0)
319 n = visit.pop(0)
320 parents = [p for p in repo.changelog.parents(n) if p not in has]
320 parents = [p for p in repo.changelog.parents(n) if p not in has]
321 if len(parents) == 0:
321 if len(parents) == 0:
322 o.insert(0, n)
322 o.insert(0, n)
323 else:
323 else:
324 for p in parents:
324 for p in parents:
325 if p not in seen:
325 if p not in seen:
326 seen[p] = 1
326 seen[p] = 1
327 visit.append(p)
327 visit.append(p)
328 else:
328 else:
329 setremoteconfig(ui, opts)
329 setremoteconfig(ui, opts)
330 dest = ui.expandpath(dest or 'default-push', dest or 'default')
330 dest = ui.expandpath(dest or 'default-push', dest or 'default')
331 other = hg.repository(ui, dest)
331 other = hg.repository(ui, dest)
332 o = repo.findoutgoing(other, force=opts['force'])
332 o = repo.findoutgoing(other, force=opts['force'])
333
333
334 if revs:
334 if revs:
335 cg = repo.changegroupsubset(o, revs, 'bundle')
335 cg = repo.changegroupsubset(o, revs, 'bundle')
336 else:
336 else:
337 cg = repo.changegroup(o, 'bundle')
337 cg = repo.changegroup(o, 'bundle')
338 changegroup.writebundle(cg, fname, "HG10BZ")
338 changegroup.writebundle(cg, fname, "HG10BZ")
339
339
340 def cat(ui, repo, file1, *pats, **opts):
340 def cat(ui, repo, file1, *pats, **opts):
341 """output the current or given revision of files
341 """output the current or given revision of files
342
342
343 Print the specified files as they were at the given revision.
343 Print the specified files as they were at the given revision.
344 If no revision is given, the parent of the working directory is used,
344 If no revision is given, the parent of the working directory is used,
345 or tip if no revision is checked out.
345 or tip if no revision is checked out.
346
346
347 Output may be to a file, in which case the name of the file is
347 Output may be to a file, in which case the name of the file is
348 given using a format string. The formatting rules are the same as
348 given using a format string. The formatting rules are the same as
349 for the export command, with the following additions:
349 for the export command, with the following additions:
350
350
351 %s basename of file being printed
351 %s basename of file being printed
352 %d dirname of file being printed, or '.' if in repo root
352 %d dirname of file being printed, or '.' if in repo root
353 %p root-relative path name of file being printed
353 %p root-relative path name of file being printed
354 """
354 """
355 ctx = repo.changectx(opts['rev'])
355 ctx = repo.changectx(opts['rev'])
356 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
356 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
357 ctx.node()):
357 ctx.node()):
358 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
358 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
359 fp.write(ctx.filectx(abs).data())
359 fp.write(ctx.filectx(abs).data())
360
360
361 def clone(ui, source, dest=None, **opts):
361 def clone(ui, source, dest=None, **opts):
362 """make a copy of an existing repository
362 """make a copy of an existing repository
363
363
364 Create a copy of an existing repository in a new directory.
364 Create a copy of an existing repository in a new directory.
365
365
366 If no destination directory name is specified, it defaults to the
366 If no destination directory name is specified, it defaults to the
367 basename of the source.
367 basename of the source.
368
368
369 The location of the source is added to the new repository's
369 The location of the source is added to the new repository's
370 .hg/hgrc file, as the default to be used for future pulls.
370 .hg/hgrc file, as the default to be used for future pulls.
371
371
372 For efficiency, hardlinks are used for cloning whenever the source
372 For efficiency, hardlinks are used for cloning whenever the source
373 and destination are on the same filesystem (note this applies only
373 and destination are on the same filesystem (note this applies only
374 to the repository data, not to the checked out files). Some
374 to the repository data, not to the checked out files). Some
375 filesystems, such as AFS, implement hardlinking incorrectly, but
375 filesystems, such as AFS, implement hardlinking incorrectly, but
376 do not report errors. In these cases, use the --pull option to
376 do not report errors. In these cases, use the --pull option to
377 avoid hardlinking.
377 avoid hardlinking.
378
378
379 You can safely clone repositories and checked out files using full
379 You can safely clone repositories and checked out files using full
380 hardlinks with
380 hardlinks with
381
381
382 $ cp -al REPO REPOCLONE
382 $ cp -al REPO REPOCLONE
383
383
384 which is the fastest way to clone. However, the operation is not
384 which is the fastest way to clone. However, the operation is not
385 atomic (making sure REPO is not modified during the operation is
385 atomic (making sure REPO is not modified during the operation is
386 up to you) and you have to make sure your editor breaks hardlinks
386 up to you) and you have to make sure your editor breaks hardlinks
387 (Emacs and most Linux Kernel tools do so).
387 (Emacs and most Linux Kernel tools do so).
388
388
389 If you use the -r option to clone up to a specific revision, no
389 If you use the -r option to clone up to a specific revision, no
390 subsequent revisions will be present in the cloned repository.
390 subsequent revisions will be present in the cloned repository.
391 This option implies --pull, even on local repositories.
391 This option implies --pull, even on local repositories.
392
392
393 See pull for valid source format details.
393 See pull for valid source format details.
394
394
395 It is possible to specify an ssh:// URL as the destination, but no
395 It is possible to specify an ssh:// URL as the destination, but no
396 .hg/hgrc and working directory will be created on the remote side.
396 .hg/hgrc and working directory will be created on the remote side.
397 Look at the help text for the pull command for important details
397 Look at the help text for the pull command for important details
398 about ssh:// URLs.
398 about ssh:// URLs.
399 """
399 """
400 setremoteconfig(ui, opts)
400 setremoteconfig(ui, opts)
401 hg.clone(ui, ui.expandpath(source), dest,
401 hg.clone(ui, ui.expandpath(source), dest,
402 pull=opts['pull'],
402 pull=opts['pull'],
403 stream=opts['uncompressed'],
403 stream=opts['uncompressed'],
404 rev=opts['rev'],
404 rev=opts['rev'],
405 update=not opts['noupdate'])
405 update=not opts['noupdate'])
406
406
407 def commit(ui, repo, *pats, **opts):
407 def commit(ui, repo, *pats, **opts):
408 """commit the specified files or all outstanding changes
408 """commit the specified files or all outstanding changes
409
409
410 Commit changes to the given files into the repository.
410 Commit changes to the given files into the repository.
411
411
412 If a list of files is omitted, all changes reported by "hg status"
412 If a list of files is omitted, all changes reported by "hg status"
413 will be committed.
413 will be committed.
414
414
415 If no commit message is specified, the editor configured in your hgrc
415 If no commit message is specified, the editor configured in your hgrc
416 or in the EDITOR environment variable is started to enter a message.
416 or in the EDITOR environment variable is started to enter a message.
417 """
417 """
418 message = logmessage(opts)
418 message = logmessage(opts)
419
419
420 if opts['addremove']:
420 if opts['addremove']:
421 cmdutil.addremove(repo, pats, opts)
421 cmdutil.addremove(repo, pats, opts)
422 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
422 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
423 if pats:
423 if pats:
424 status = repo.status(files=fns, match=match)
424 status = repo.status(files=fns, match=match)
425 modified, added, removed, deleted, unknown = status[:5]
425 modified, added, removed, deleted, unknown = status[:5]
426 files = modified + added + removed
426 files = modified + added + removed
427 slist = None
427 slist = None
428 for f in fns:
428 for f in fns:
429 if f not in files:
429 if f not in files:
430 rf = repo.wjoin(f)
430 rf = repo.wjoin(f)
431 if f in unknown:
431 if f in unknown:
432 raise util.Abort(_("file %s not tracked!") % rf)
432 raise util.Abort(_("file %s not tracked!") % rf)
433 try:
433 try:
434 mode = os.lstat(rf)[stat.ST_MODE]
434 mode = os.lstat(rf)[stat.ST_MODE]
435 except OSError:
435 except OSError:
436 raise util.Abort(_("file %s not found!") % rf)
436 raise util.Abort(_("file %s not found!") % rf)
437 if stat.S_ISDIR(mode):
437 if stat.S_ISDIR(mode):
438 name = f + '/'
438 name = f + '/'
439 if slist is None:
439 if slist is None:
440 slist = list(files)
440 slist = list(files)
441 slist.sort()
441 slist.sort()
442 i = bisect.bisect(slist, name)
442 i = bisect.bisect(slist, name)
443 if i >= len(slist) or not slist[i].startswith(name):
443 if i >= len(slist) or not slist[i].startswith(name):
444 raise util.Abort(_("no match under directory %s!")
444 raise util.Abort(_("no match under directory %s!")
445 % rf)
445 % rf)
446 elif not stat.S_ISREG(mode):
446 elif not stat.S_ISREG(mode):
447 raise util.Abort(_("can't commit %s: "
447 raise util.Abort(_("can't commit %s: "
448 "unsupported file type!") % rf)
448 "unsupported file type!") % rf)
449 else:
449 else:
450 files = []
450 files = []
451 try:
451 try:
452 repo.commit(files, message, opts['user'], opts['date'], match,
452 repo.commit(files, message, opts['user'], opts['date'], match,
453 force_editor=opts.get('force_editor'))
453 force_editor=opts.get('force_editor'))
454 except ValueError, inst:
454 except ValueError, inst:
455 raise util.Abort(str(inst))
455 raise util.Abort(str(inst))
456
456
457 def docopy(ui, repo, pats, opts, wlock):
457 def docopy(ui, repo, pats, opts, wlock):
458 # called with the repo lock held
458 # called with the repo lock held
459 #
459 #
460 # hgsep => pathname that uses "/" to separate directories
460 # hgsep => pathname that uses "/" to separate directories
461 # ossep => pathname that uses os.sep to separate directories
461 # ossep => pathname that uses os.sep to separate directories
462 cwd = repo.getcwd()
462 cwd = repo.getcwd()
463 errors = 0
463 errors = 0
464 copied = []
464 copied = []
465 targets = {}
465 targets = {}
466
466
467 # abs: hgsep
467 # abs: hgsep
468 # rel: ossep
468 # rel: ossep
469 # return: hgsep
469 # return: hgsep
470 def okaytocopy(abs, rel, exact):
470 def okaytocopy(abs, rel, exact):
471 reasons = {'?': _('is not managed'),
471 reasons = {'?': _('is not managed'),
472 'a': _('has been marked for add'),
472 'a': _('has been marked for add'),
473 'r': _('has been marked for remove')}
473 'r': _('has been marked for remove')}
474 state = repo.dirstate.state(abs)
474 state = repo.dirstate.state(abs)
475 reason = reasons.get(state)
475 reason = reasons.get(state)
476 if reason:
476 if reason:
477 if state == 'a':
477 if state == 'a':
478 origsrc = repo.dirstate.copied(abs)
478 origsrc = repo.dirstate.copied(abs)
479 if origsrc is not None:
479 if origsrc is not None:
480 return origsrc
480 return origsrc
481 if exact:
481 if exact:
482 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
482 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
483 else:
483 else:
484 return abs
484 return abs
485
485
486 # origsrc: hgsep
486 # origsrc: hgsep
487 # abssrc: hgsep
487 # abssrc: hgsep
488 # relsrc: ossep
488 # relsrc: ossep
489 # target: ossep
489 # target: ossep
490 def copy(origsrc, abssrc, relsrc, target, exact):
490 def copy(origsrc, abssrc, relsrc, target, exact):
491 abstarget = util.canonpath(repo.root, cwd, target)
491 abstarget = util.canonpath(repo.root, cwd, target)
492 reltarget = util.pathto(cwd, abstarget)
492 reltarget = util.pathto(cwd, abstarget)
493 prevsrc = targets.get(abstarget)
493 prevsrc = targets.get(abstarget)
494 if prevsrc is not None:
494 if prevsrc is not None:
495 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
495 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
496 (reltarget, util.localpath(abssrc),
496 (reltarget, util.localpath(abssrc),
497 util.localpath(prevsrc)))
497 util.localpath(prevsrc)))
498 return
498 return
499 if (not opts['after'] and os.path.exists(reltarget) or
499 if (not opts['after'] and os.path.exists(reltarget) or
500 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
500 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
501 if not opts['force']:
501 if not opts['force']:
502 ui.warn(_('%s: not overwriting - file exists\n') %
502 ui.warn(_('%s: not overwriting - file exists\n') %
503 reltarget)
503 reltarget)
504 return
504 return
505 if not opts['after'] and not opts.get('dry_run'):
505 if not opts['after'] and not opts.get('dry_run'):
506 os.unlink(reltarget)
506 os.unlink(reltarget)
507 if opts['after']:
507 if opts['after']:
508 if not os.path.exists(reltarget):
508 if not os.path.exists(reltarget):
509 return
509 return
510 else:
510 else:
511 targetdir = os.path.dirname(reltarget) or '.'
511 targetdir = os.path.dirname(reltarget) or '.'
512 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
512 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
513 os.makedirs(targetdir)
513 os.makedirs(targetdir)
514 try:
514 try:
515 restore = repo.dirstate.state(abstarget) == 'r'
515 restore = repo.dirstate.state(abstarget) == 'r'
516 if restore and not opts.get('dry_run'):
516 if restore and not opts.get('dry_run'):
517 repo.undelete([abstarget], wlock)
517 repo.undelete([abstarget], wlock)
518 try:
518 try:
519 if not opts.get('dry_run'):
519 if not opts.get('dry_run'):
520 util.copyfile(relsrc, reltarget)
520 util.copyfile(relsrc, reltarget)
521 restore = False
521 restore = False
522 finally:
522 finally:
523 if restore:
523 if restore:
524 repo.remove([abstarget], wlock)
524 repo.remove([abstarget], wlock)
525 except IOError, inst:
525 except IOError, inst:
526 if inst.errno == errno.ENOENT:
526 if inst.errno == errno.ENOENT:
527 ui.warn(_('%s: deleted in working copy\n') % relsrc)
527 ui.warn(_('%s: deleted in working copy\n') % relsrc)
528 else:
528 else:
529 ui.warn(_('%s: cannot copy - %s\n') %
529 ui.warn(_('%s: cannot copy - %s\n') %
530 (relsrc, inst.strerror))
530 (relsrc, inst.strerror))
531 errors += 1
531 errors += 1
532 return
532 return
533 if ui.verbose or not exact:
533 if ui.verbose or not exact:
534 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
534 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
535 targets[abstarget] = abssrc
535 targets[abstarget] = abssrc
536 if abstarget != origsrc and not opts.get('dry_run'):
536 if abstarget != origsrc and not opts.get('dry_run'):
537 repo.copy(origsrc, abstarget, wlock)
537 repo.copy(origsrc, abstarget, wlock)
538 copied.append((abssrc, relsrc, exact))
538 copied.append((abssrc, relsrc, exact))
539
539
540 # pat: ossep
540 # pat: ossep
541 # dest ossep
541 # dest ossep
542 # srcs: list of (hgsep, hgsep, ossep, bool)
542 # srcs: list of (hgsep, hgsep, ossep, bool)
543 # return: function that takes hgsep and returns ossep
543 # return: function that takes hgsep and returns ossep
544 def targetpathfn(pat, dest, srcs):
544 def targetpathfn(pat, dest, srcs):
545 if os.path.isdir(pat):
545 if os.path.isdir(pat):
546 abspfx = util.canonpath(repo.root, cwd, pat)
546 abspfx = util.canonpath(repo.root, cwd, pat)
547 abspfx = util.localpath(abspfx)
547 abspfx = util.localpath(abspfx)
548 if destdirexists:
548 if destdirexists:
549 striplen = len(os.path.split(abspfx)[0])
549 striplen = len(os.path.split(abspfx)[0])
550 else:
550 else:
551 striplen = len(abspfx)
551 striplen = len(abspfx)
552 if striplen:
552 if striplen:
553 striplen += len(os.sep)
553 striplen += len(os.sep)
554 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
554 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
555 elif destdirexists:
555 elif destdirexists:
556 res = lambda p: os.path.join(dest,
556 res = lambda p: os.path.join(dest,
557 os.path.basename(util.localpath(p)))
557 os.path.basename(util.localpath(p)))
558 else:
558 else:
559 res = lambda p: dest
559 res = lambda p: dest
560 return res
560 return res
561
561
562 # pat: ossep
562 # pat: ossep
563 # dest ossep
563 # dest ossep
564 # srcs: list of (hgsep, hgsep, ossep, bool)
564 # srcs: list of (hgsep, hgsep, ossep, bool)
565 # return: function that takes hgsep and returns ossep
565 # return: function that takes hgsep and returns ossep
566 def targetpathafterfn(pat, dest, srcs):
566 def targetpathafterfn(pat, dest, srcs):
567 if util.patkind(pat, None)[0]:
567 if util.patkind(pat, None)[0]:
568 # a mercurial pattern
568 # a mercurial pattern
569 res = lambda p: os.path.join(dest,
569 res = lambda p: os.path.join(dest,
570 os.path.basename(util.localpath(p)))
570 os.path.basename(util.localpath(p)))
571 else:
571 else:
572 abspfx = util.canonpath(repo.root, cwd, pat)
572 abspfx = util.canonpath(repo.root, cwd, pat)
573 if len(abspfx) < len(srcs[0][0]):
573 if len(abspfx) < len(srcs[0][0]):
574 # A directory. Either the target path contains the last
574 # A directory. Either the target path contains the last
575 # component of the source path or it does not.
575 # component of the source path or it does not.
576 def evalpath(striplen):
576 def evalpath(striplen):
577 score = 0
577 score = 0
578 for s in srcs:
578 for s in srcs:
579 t = os.path.join(dest, util.localpath(s[0])[striplen:])
579 t = os.path.join(dest, util.localpath(s[0])[striplen:])
580 if os.path.exists(t):
580 if os.path.exists(t):
581 score += 1
581 score += 1
582 return score
582 return score
583
583
584 abspfx = util.localpath(abspfx)
584 abspfx = util.localpath(abspfx)
585 striplen = len(abspfx)
585 striplen = len(abspfx)
586 if striplen:
586 if striplen:
587 striplen += len(os.sep)
587 striplen += len(os.sep)
588 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
588 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
589 score = evalpath(striplen)
589 score = evalpath(striplen)
590 striplen1 = len(os.path.split(abspfx)[0])
590 striplen1 = len(os.path.split(abspfx)[0])
591 if striplen1:
591 if striplen1:
592 striplen1 += len(os.sep)
592 striplen1 += len(os.sep)
593 if evalpath(striplen1) > score:
593 if evalpath(striplen1) > score:
594 striplen = striplen1
594 striplen = striplen1
595 res = lambda p: os.path.join(dest,
595 res = lambda p: os.path.join(dest,
596 util.localpath(p)[striplen:])
596 util.localpath(p)[striplen:])
597 else:
597 else:
598 # a file
598 # a file
599 if destdirexists:
599 if destdirexists:
600 res = lambda p: os.path.join(dest,
600 res = lambda p: os.path.join(dest,
601 os.path.basename(util.localpath(p)))
601 os.path.basename(util.localpath(p)))
602 else:
602 else:
603 res = lambda p: dest
603 res = lambda p: dest
604 return res
604 return res
605
605
606
606
607 pats = util.expand_glob(pats)
607 pats = util.expand_glob(pats)
608 if not pats:
608 if not pats:
609 raise util.Abort(_('no source or destination specified'))
609 raise util.Abort(_('no source or destination specified'))
610 if len(pats) == 1:
610 if len(pats) == 1:
611 raise util.Abort(_('no destination specified'))
611 raise util.Abort(_('no destination specified'))
612 dest = pats.pop()
612 dest = pats.pop()
613 destdirexists = os.path.isdir(dest)
613 destdirexists = os.path.isdir(dest)
614 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
614 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
615 raise util.Abort(_('with multiple sources, destination must be an '
615 raise util.Abort(_('with multiple sources, destination must be an '
616 'existing directory'))
616 'existing directory'))
617 if opts['after']:
617 if opts['after']:
618 tfn = targetpathafterfn
618 tfn = targetpathafterfn
619 else:
619 else:
620 tfn = targetpathfn
620 tfn = targetpathfn
621 copylist = []
621 copylist = []
622 for pat in pats:
622 for pat in pats:
623 srcs = []
623 srcs = []
624 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
624 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
625 globbed=True):
625 globbed=True):
626 origsrc = okaytocopy(abssrc, relsrc, exact)
626 origsrc = okaytocopy(abssrc, relsrc, exact)
627 if origsrc:
627 if origsrc:
628 srcs.append((origsrc, abssrc, relsrc, exact))
628 srcs.append((origsrc, abssrc, relsrc, exact))
629 if not srcs:
629 if not srcs:
630 continue
630 continue
631 copylist.append((tfn(pat, dest, srcs), srcs))
631 copylist.append((tfn(pat, dest, srcs), srcs))
632 if not copylist:
632 if not copylist:
633 raise util.Abort(_('no files to copy'))
633 raise util.Abort(_('no files to copy'))
634
634
635 for targetpath, srcs in copylist:
635 for targetpath, srcs in copylist:
636 for origsrc, abssrc, relsrc, exact in srcs:
636 for origsrc, abssrc, relsrc, exact in srcs:
637 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
637 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
638
638
639 if errors:
639 if errors:
640 ui.warn(_('(consider using --after)\n'))
640 ui.warn(_('(consider using --after)\n'))
641 return errors, copied
641 return errors, copied
642
642
643 def copy(ui, repo, *pats, **opts):
643 def copy(ui, repo, *pats, **opts):
644 """mark files as copied for the next commit
644 """mark files as copied for the next commit
645
645
646 Mark dest as having copies of source files. If dest is a
646 Mark dest as having copies of source files. If dest is a
647 directory, copies are put in that directory. If dest is a file,
647 directory, copies are put in that directory. If dest is a file,
648 there can only be one source.
648 there can only be one source.
649
649
650 By default, this command copies the contents of files as they
650 By default, this command copies the contents of files as they
651 stand in the working directory. If invoked with --after, the
651 stand in the working directory. If invoked with --after, the
652 operation is recorded, but no copying is performed.
652 operation is recorded, but no copying is performed.
653
653
654 This command takes effect in the next commit. To undo a copy
654 This command takes effect in the next commit. To undo a copy
655 before that, see hg revert.
655 before that, see hg revert.
656 """
656 """
657 wlock = repo.wlock(0)
657 wlock = repo.wlock(0)
658 errs, copied = docopy(ui, repo, pats, opts, wlock)
658 errs, copied = docopy(ui, repo, pats, opts, wlock)
659 return errs
659 return errs
660
660
661 def debugancestor(ui, index, rev1, rev2):
661 def debugancestor(ui, index, rev1, rev2):
662 """find the ancestor revision of two revisions in a given index"""
662 """find the ancestor revision of two revisions in a given index"""
663 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
663 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
664 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
664 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
665 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
665 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
666
666
667 def debugcomplete(ui, cmd='', **opts):
667 def debugcomplete(ui, cmd='', **opts):
668 """returns the completion list associated with the given command"""
668 """returns the completion list associated with the given command"""
669
669
670 if opts['options']:
670 if opts['options']:
671 options = []
671 options = []
672 otables = [globalopts]
672 otables = [globalopts]
673 if cmd:
673 if cmd:
674 aliases, entry = findcmd(ui, cmd)
674 aliases, entry = findcmd(ui, cmd)
675 otables.append(entry[1])
675 otables.append(entry[1])
676 for t in otables:
676 for t in otables:
677 for o in t:
677 for o in t:
678 if o[0]:
678 if o[0]:
679 options.append('-%s' % o[0])
679 options.append('-%s' % o[0])
680 options.append('--%s' % o[1])
680 options.append('--%s' % o[1])
681 ui.write("%s\n" % "\n".join(options))
681 ui.write("%s\n" % "\n".join(options))
682 return
682 return
683
683
684 clist = findpossible(ui, cmd).keys()
684 clist = findpossible(ui, cmd).keys()
685 clist.sort()
685 clist.sort()
686 ui.write("%s\n" % "\n".join(clist))
686 ui.write("%s\n" % "\n".join(clist))
687
687
688 def debugrebuildstate(ui, repo, rev=""):
688 def debugrebuildstate(ui, repo, rev=""):
689 """rebuild the dirstate as it would look like for the given revision"""
689 """rebuild the dirstate as it would look like for the given revision"""
690 if rev == "":
690 if rev == "":
691 rev = repo.changelog.tip()
691 rev = repo.changelog.tip()
692 ctx = repo.changectx(rev)
692 ctx = repo.changectx(rev)
693 files = ctx.manifest()
693 files = ctx.manifest()
694 wlock = repo.wlock()
694 wlock = repo.wlock()
695 repo.dirstate.rebuild(rev, files)
695 repo.dirstate.rebuild(rev, files)
696
696
697 def debugcheckstate(ui, repo):
697 def debugcheckstate(ui, repo):
698 """validate the correctness of the current dirstate"""
698 """validate the correctness of the current dirstate"""
699 parent1, parent2 = repo.dirstate.parents()
699 parent1, parent2 = repo.dirstate.parents()
700 repo.dirstate.read()
700 repo.dirstate.read()
701 dc = repo.dirstate.map
701 dc = repo.dirstate.map
702 keys = dc.keys()
702 keys = dc.keys()
703 keys.sort()
703 keys.sort()
704 m1 = repo.changectx(parent1).manifest()
704 m1 = repo.changectx(parent1).manifest()
705 m2 = repo.changectx(parent2).manifest()
705 m2 = repo.changectx(parent2).manifest()
706 errors = 0
706 errors = 0
707 for f in dc:
707 for f in dc:
708 state = repo.dirstate.state(f)
708 state = repo.dirstate.state(f)
709 if state in "nr" and f not in m1:
709 if state in "nr" and f not in m1:
710 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
710 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
711 errors += 1
711 errors += 1
712 if state in "a" and f in m1:
712 if state in "a" and f in m1:
713 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
713 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
714 errors += 1
714 errors += 1
715 if state in "m" and f not in m1 and f not in m2:
715 if state in "m" and f not in m1 and f not in m2:
716 ui.warn(_("%s in state %s, but not in either manifest\n") %
716 ui.warn(_("%s in state %s, but not in either manifest\n") %
717 (f, state))
717 (f, state))
718 errors += 1
718 errors += 1
719 for f in m1:
719 for f in m1:
720 state = repo.dirstate.state(f)
720 state = repo.dirstate.state(f)
721 if state not in "nrm":
721 if state not in "nrm":
722 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
722 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
723 errors += 1
723 errors += 1
724 if errors:
724 if errors:
725 error = _(".hg/dirstate inconsistent with current parent's manifest")
725 error = _(".hg/dirstate inconsistent with current parent's manifest")
726 raise util.Abort(error)
726 raise util.Abort(error)
727
727
728 def showconfig(ui, repo, *values, **opts):
728 def showconfig(ui, repo, *values, **opts):
729 """show combined config settings from all hgrc files
729 """show combined config settings from all hgrc files
730
730
731 With no args, print names and values of all config items.
731 With no args, print names and values of all config items.
732
732
733 With one arg of the form section.name, print just the value of
733 With one arg of the form section.name, print just the value of
734 that config item.
734 that config item.
735
735
736 With multiple args, print names and values of all config items
736 With multiple args, print names and values of all config items
737 with matching section names."""
737 with matching section names."""
738
738
739 untrusted = bool(opts.get('untrusted'))
739 untrusted = bool(opts.get('untrusted'))
740 if values:
740 if values:
741 if len([v for v in values if '.' in v]) > 1:
741 if len([v for v in values if '.' in v]) > 1:
742 raise util.Abort(_('only one config item permitted'))
742 raise util.Abort(_('only one config item permitted'))
743 for section, name, value in ui.walkconfig(untrusted=untrusted):
743 for section, name, value in ui.walkconfig(untrusted=untrusted):
744 sectname = section + '.' + name
744 sectname = section + '.' + name
745 if values:
745 if values:
746 for v in values:
746 for v in values:
747 if v == section:
747 if v == section:
748 ui.write('%s=%s\n' % (sectname, value))
748 ui.write('%s=%s\n' % (sectname, value))
749 elif v == sectname:
749 elif v == sectname:
750 ui.write(value, '\n')
750 ui.write(value, '\n')
751 else:
751 else:
752 ui.write('%s=%s\n' % (sectname, value))
752 ui.write('%s=%s\n' % (sectname, value))
753
753
754 def debugsetparents(ui, repo, rev1, rev2=None):
754 def debugsetparents(ui, repo, rev1, rev2=None):
755 """manually set the parents of the current working directory
755 """manually set the parents of the current working directory
756
756
757 This is useful for writing repository conversion tools, but should
757 This is useful for writing repository conversion tools, but should
758 be used with care.
758 be used with care.
759 """
759 """
760
760
761 if not rev2:
761 if not rev2:
762 rev2 = hex(nullid)
762 rev2 = hex(nullid)
763
763
764 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
764 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
765
765
766 def debugstate(ui, repo):
766 def debugstate(ui, repo):
767 """show the contents of the current dirstate"""
767 """show the contents of the current dirstate"""
768 repo.dirstate.read()
768 repo.dirstate.read()
769 dc = repo.dirstate.map
769 dc = repo.dirstate.map
770 keys = dc.keys()
770 keys = dc.keys()
771 keys.sort()
771 keys.sort()
772 for file_ in keys:
772 for file_ in keys:
773 if dc[file_][3] == -1:
773 if dc[file_][3] == -1:
774 # Pad or slice to locale representation
774 # Pad or slice to locale representation
775 locale_len = len(time.strftime("%x %X", time.localtime(0)))
775 locale_len = len(time.strftime("%x %X", time.localtime(0)))
776 timestr = 'unset'
776 timestr = 'unset'
777 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
777 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
778 else:
778 else:
779 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
779 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
780 ui.write("%c %3o %10d %s %s\n"
780 ui.write("%c %3o %10d %s %s\n"
781 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
781 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
782 timestr, file_))
782 timestr, file_))
783 for f in repo.dirstate.copies():
783 for f in repo.dirstate.copies():
784 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
784 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
785
785
786 def debugdata(ui, file_, rev):
786 def debugdata(ui, file_, rev):
787 """dump the contents of an data file revision"""
787 """dump the contents of an data file revision"""
788 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
788 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
789 file_[:-2] + ".i", file_, 0)
789 file_[:-2] + ".i", file_, 0)
790 try:
790 try:
791 ui.write(r.revision(r.lookup(rev)))
791 ui.write(r.revision(r.lookup(rev)))
792 except KeyError:
792 except KeyError:
793 raise util.Abort(_('invalid revision identifier %s') % rev)
793 raise util.Abort(_('invalid revision identifier %s') % rev)
794
794
795 def debugdate(ui, date, range=None, **opts):
795 def debugdate(ui, date, range=None, **opts):
796 """parse and display a date"""
796 """parse and display a date"""
797 if opts["extended"]:
797 if opts["extended"]:
798 d = util.parsedate(date, util.extendeddateformats)
798 d = util.parsedate(date, util.extendeddateformats)
799 else:
799 else:
800 d = util.parsedate(date)
800 d = util.parsedate(date)
801 ui.write("internal: %s %s\n" % d)
801 ui.write("internal: %s %s\n" % d)
802 ui.write("standard: %s\n" % util.datestr(d))
802 ui.write("standard: %s\n" % util.datestr(d))
803 if range:
803 if range:
804 m = util.matchdate(range)
804 m = util.matchdate(range)
805 ui.write("match: %s\n" % m(d[0]))
805 ui.write("match: %s\n" % m(d[0]))
806
806
807 def debugindex(ui, file_):
807 def debugindex(ui, file_):
808 """dump the contents of an index file"""
808 """dump the contents of an index file"""
809 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
809 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
810 ui.write(" rev offset length base linkrev" +
810 ui.write(" rev offset length base linkrev" +
811 " nodeid p1 p2\n")
811 " nodeid p1 p2\n")
812 for i in xrange(r.count()):
812 for i in xrange(r.count()):
813 node = r.node(i)
813 node = r.node(i)
814 pp = r.parents(node)
814 pp = r.parents(node)
815 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
815 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
816 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
816 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
817 short(node), short(pp[0]), short(pp[1])))
817 short(node), short(pp[0]), short(pp[1])))
818
818
819 def debugindexdot(ui, file_):
819 def debugindexdot(ui, file_):
820 """dump an index DAG as a .dot file"""
820 """dump an index DAG as a .dot file"""
821 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
821 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
822 ui.write("digraph G {\n")
822 ui.write("digraph G {\n")
823 for i in xrange(r.count()):
823 for i in xrange(r.count()):
824 node = r.node(i)
824 node = r.node(i)
825 pp = r.parents(node)
825 pp = r.parents(node)
826 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
826 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
827 if pp[1] != nullid:
827 if pp[1] != nullid:
828 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
828 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
829 ui.write("}\n")
829 ui.write("}\n")
830
830
831 def debuginstall(ui):
831 def debuginstall(ui):
832 '''test Mercurial installation'''
832 '''test Mercurial installation'''
833
833
834 def writetemp(contents):
834 def writetemp(contents):
835 (fd, name) = tempfile.mkstemp()
835 (fd, name) = tempfile.mkstemp()
836 f = os.fdopen(fd, "wb")
836 f = os.fdopen(fd, "wb")
837 f.write(contents)
837 f.write(contents)
838 f.close()
838 f.close()
839 return name
839 return name
840
840
841 problems = 0
841 problems = 0
842
842
843 # encoding
843 # encoding
844 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
844 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
845 try:
845 try:
846 util.fromlocal("test")
846 util.fromlocal("test")
847 except util.Abort, inst:
847 except util.Abort, inst:
848 ui.write(" %s\n" % inst)
848 ui.write(" %s\n" % inst)
849 ui.write(_(" (check that your locale is properly set)\n"))
849 ui.write(_(" (check that your locale is properly set)\n"))
850 problems += 1
850 problems += 1
851
851
852 # compiled modules
852 # compiled modules
853 ui.status(_("Checking extensions...\n"))
853 ui.status(_("Checking extensions...\n"))
854 try:
854 try:
855 import bdiff, mpatch, base85
855 import bdiff, mpatch, base85
856 except Exception, inst:
856 except Exception, inst:
857 ui.write(" %s\n" % inst)
857 ui.write(" %s\n" % inst)
858 ui.write(_(" One or more extensions could not be found"))
858 ui.write(_(" One or more extensions could not be found"))
859 ui.write(_(" (check that you compiled the extensions)\n"))
859 ui.write(_(" (check that you compiled the extensions)\n"))
860 problems += 1
860 problems += 1
861
861
862 # templates
862 # templates
863 ui.status(_("Checking templates...\n"))
863 ui.status(_("Checking templates...\n"))
864 try:
864 try:
865 import templater
865 import templater
866 t = templater.templater(templater.templatepath("map-cmdline.default"))
866 t = templater.templater(templater.templatepath("map-cmdline.default"))
867 except Exception, inst:
867 except Exception, inst:
868 ui.write(" %s\n" % inst)
868 ui.write(" %s\n" % inst)
869 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
869 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
870 problems += 1
870 problems += 1
871
871
872 # patch
872 # patch
873 ui.status(_("Checking patch...\n"))
873 ui.status(_("Checking patch...\n"))
874 path = os.environ.get('PATH', '')
874 path = os.environ.get('PATH', '')
875 patcher = util.find_in_path('gpatch', path,
875 patcher = util.find_in_path('gpatch', path,
876 util.find_in_path('patch', path, None))
876 util.find_in_path('patch', path, None))
877 if not patcher:
877 if not patcher:
878 ui.write(_(" Can't find patch or gpatch in PATH\n"))
878 ui.write(_(" Can't find patch or gpatch in PATH\n"))
879 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
879 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
880 problems += 1
880 problems += 1
881 else:
881 else:
882 # actually attempt a patch here
882 # actually attempt a patch here
883 a = "1\n2\n3\n4\n"
883 a = "1\n2\n3\n4\n"
884 b = "1\n2\n3\ninsert\n4\n"
884 b = "1\n2\n3\ninsert\n4\n"
885 d = mdiff.unidiff(a, None, b, None, "a")
885 d = mdiff.unidiff(a, None, b, None, "a")
886 fa = writetemp(a)
886 fa = writetemp(a)
887 fd = writetemp(d)
887 fd = writetemp(d)
888 fp = os.popen('%s %s %s' % (patcher, fa, fd))
888 fp = os.popen('%s %s %s' % (patcher, fa, fd))
889 files = []
889 files = []
890 output = ""
890 output = ""
891 for line in fp:
891 for line in fp:
892 output += line
892 output += line
893 if line.startswith('patching file '):
893 if line.startswith('patching file '):
894 pf = util.parse_patch_output(line.rstrip())
894 pf = util.parse_patch_output(line.rstrip())
895 files.append(pf)
895 files.append(pf)
896 if files != [fa]:
896 if files != [fa]:
897 ui.write(_(" unexpected patch output!"))
897 ui.write(_(" unexpected patch output!"))
898 ui.write(_(" (you may have an incompatible version of patch)\n"))
898 ui.write(_(" (you may have an incompatible version of patch)\n"))
899 ui.write(output)
899 ui.write(output)
900 problems += 1
900 problems += 1
901 a = file(fa).read()
901 a = file(fa).read()
902 if a != b:
902 if a != b:
903 ui.write(_(" patch test failed!"))
903 ui.write(_(" patch test failed!"))
904 ui.write(_(" (you may have an incompatible version of patch)\n"))
904 ui.write(_(" (you may have an incompatible version of patch)\n"))
905 problems += 1
905 problems += 1
906 os.unlink(fa)
906 os.unlink(fa)
907 os.unlink(fd)
907 os.unlink(fd)
908
908
909 # merge helper
909 # merge helper
910 ui.status(_("Checking merge helper...\n"))
910 ui.status(_("Checking merge helper...\n"))
911 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
911 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
912 or "hgmerge")
912 or "hgmerge")
913 cmdpath = util.find_in_path(cmd, path)
913 cmdpath = util.find_in_path(cmd, path)
914 if not cmdpath:
914 if not cmdpath:
915 cmdpath = util.find_in_path(cmd.split()[0], path)
915 cmdpath = util.find_in_path(cmd.split()[0], path)
916 if not cmdpath:
916 if not cmdpath:
917 if cmd == 'hgmerge':
917 if cmd == 'hgmerge':
918 ui.write(_(" No merge helper set and can't find default"
918 ui.write(_(" No merge helper set and can't find default"
919 " hgmerge script in PATH\n"))
919 " hgmerge script in PATH\n"))
920 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
920 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
921 else:
921 else:
922 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
922 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
923 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
923 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
924 problems += 1
924 problems += 1
925 else:
925 else:
926 # actually attempt a patch here
926 # actually attempt a patch here
927 fa = writetemp("1\n2\n3\n4\n")
927 fa = writetemp("1\n2\n3\n4\n")
928 fl = writetemp("1\n2\n3\ninsert\n4\n")
928 fl = writetemp("1\n2\n3\ninsert\n4\n")
929 fr = writetemp("begin\n1\n2\n3\n4\n")
929 fr = writetemp("begin\n1\n2\n3\n4\n")
930 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
930 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
931 if r:
931 if r:
932 ui.write(_(" got unexpected merge error %d!") % r)
932 ui.write(_(" got unexpected merge error %d!") % r)
933 problems += 1
933 problems += 1
934 m = file(fl).read()
934 m = file(fl).read()
935 if m != "begin\n1\n2\n3\ninsert\n4\n":
935 if m != "begin\n1\n2\n3\ninsert\n4\n":
936 ui.write(_(" got unexpected merge results!") % r)
936 ui.write(_(" got unexpected merge results!") % r)
937 ui.write(_(" (your merge helper may have the"
937 ui.write(_(" (your merge helper may have the"
938 " wrong argument order)\n"))
938 " wrong argument order)\n"))
939 ui.write(m)
939 ui.write(m)
940 os.unlink(fa)
940 os.unlink(fa)
941 os.unlink(fl)
941 os.unlink(fl)
942 os.unlink(fr)
942 os.unlink(fr)
943
943
944 # editor
944 # editor
945 ui.status(_("Checking commit editor...\n"))
945 ui.status(_("Checking commit editor...\n"))
946 editor = (os.environ.get("HGEDITOR") or
946 editor = (os.environ.get("HGEDITOR") or
947 ui.config("ui", "editor") or
947 ui.config("ui", "editor") or
948 os.environ.get("EDITOR", "vi"))
948 os.environ.get("EDITOR", "vi"))
949 cmdpath = util.find_in_path(editor, path)
949 cmdpath = util.find_in_path(editor, path)
950 if not cmdpath:
950 if not cmdpath:
951 cmdpath = util.find_in_path(editor.split()[0], path)
951 cmdpath = util.find_in_path(editor.split()[0], path)
952 if not cmdpath:
952 if not cmdpath:
953 if editor == 'vi':
953 if editor == 'vi':
954 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
954 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
955 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
955 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
956 else:
956 else:
957 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
957 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
958 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
958 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
959 problems += 1
959 problems += 1
960
960
961 # check username
961 # check username
962 ui.status(_("Checking username...\n"))
962 ui.status(_("Checking username...\n"))
963 user = os.environ.get("HGUSER")
963 user = os.environ.get("HGUSER")
964 if user is None:
964 if user is None:
965 user = ui.config("ui", "username")
965 user = ui.config("ui", "username")
966 if user is None:
966 if user is None:
967 user = os.environ.get("EMAIL")
967 user = os.environ.get("EMAIL")
968 if not user:
968 if not user:
969 ui.warn(" ")
969 ui.warn(" ")
970 ui.username()
970 ui.username()
971 ui.write(_(" (specify a username in your .hgrc file)\n"))
971 ui.write(_(" (specify a username in your .hgrc file)\n"))
972
972
973 if not problems:
973 if not problems:
974 ui.status(_("No problems detected\n"))
974 ui.status(_("No problems detected\n"))
975 else:
975 else:
976 ui.write(_("%s problems detected,"
976 ui.write(_("%s problems detected,"
977 " please check your install!\n") % problems)
977 " please check your install!\n") % problems)
978
978
979 return problems
979 return problems
980
980
981 def debugrename(ui, repo, file1, *pats, **opts):
981 def debugrename(ui, repo, file1, *pats, **opts):
982 """dump rename information"""
982 """dump rename information"""
983
983
984 ctx = repo.changectx(opts.get('rev', 'tip'))
984 ctx = repo.changectx(opts.get('rev', 'tip'))
985 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
985 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
986 ctx.node()):
986 ctx.node()):
987 m = ctx.filectx(abs).renamed()
987 m = ctx.filectx(abs).renamed()
988 if m:
988 if m:
989 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
989 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
990 else:
990 else:
991 ui.write(_("%s not renamed\n") % rel)
991 ui.write(_("%s not renamed\n") % rel)
992
992
993 def debugwalk(ui, repo, *pats, **opts):
993 def debugwalk(ui, repo, *pats, **opts):
994 """show how files match on given patterns"""
994 """show how files match on given patterns"""
995 items = list(cmdutil.walk(repo, pats, opts))
995 items = list(cmdutil.walk(repo, pats, opts))
996 if not items:
996 if not items:
997 return
997 return
998 fmt = '%%s %%-%ds %%-%ds %%s' % (
998 fmt = '%%s %%-%ds %%-%ds %%s' % (
999 max([len(abs) for (src, abs, rel, exact) in items]),
999 max([len(abs) for (src, abs, rel, exact) in items]),
1000 max([len(rel) for (src, abs, rel, exact) in items]))
1000 max([len(rel) for (src, abs, rel, exact) in items]))
1001 for src, abs, rel, exact in items:
1001 for src, abs, rel, exact in items:
1002 line = fmt % (src, abs, rel, exact and 'exact' or '')
1002 line = fmt % (src, abs, rel, exact and 'exact' or '')
1003 ui.write("%s\n" % line.rstrip())
1003 ui.write("%s\n" % line.rstrip())
1004
1004
1005 def diff(ui, repo, *pats, **opts):
1005 def diff(ui, repo, *pats, **opts):
1006 """diff repository (or selected files)
1006 """diff repository (or selected files)
1007
1007
1008 Show differences between revisions for the specified files.
1008 Show differences between revisions for the specified files.
1009
1009
1010 Differences between files are shown using the unified diff format.
1010 Differences between files are shown using the unified diff format.
1011
1011
1012 NOTE: diff may generate unexpected results for merges, as it will
1012 NOTE: diff may generate unexpected results for merges, as it will
1013 default to comparing against the working directory's first parent
1013 default to comparing against the working directory's first parent
1014 changeset if no revisions are specified.
1014 changeset if no revisions are specified.
1015
1015
1016 When two revision arguments are given, then changes are shown
1016 When two revision arguments are given, then changes are shown
1017 between those revisions. If only one revision is specified then
1017 between those revisions. If only one revision is specified then
1018 that revision is compared to the working directory, and, when no
1018 that revision is compared to the working directory, and, when no
1019 revisions are specified, the working directory files are compared
1019 revisions are specified, the working directory files are compared
1020 to its parent.
1020 to its parent.
1021
1021
1022 Without the -a option, diff will avoid generating diffs of files
1022 Without the -a option, diff will avoid generating diffs of files
1023 it detects as binary. With -a, diff will generate a diff anyway,
1023 it detects as binary. With -a, diff will generate a diff anyway,
1024 probably with undesirable results.
1024 probably with undesirable results.
1025 """
1025 """
1026 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1026 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1027
1027
1028 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1028 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1029
1029
1030 patch.diff(repo, node1, node2, fns, match=matchfn,
1030 patch.diff(repo, node1, node2, fns, match=matchfn,
1031 opts=patch.diffopts(ui, opts))
1031 opts=patch.diffopts(ui, opts))
1032
1032
1033 def export(ui, repo, *changesets, **opts):
1033 def export(ui, repo, *changesets, **opts):
1034 """dump the header and diffs for one or more changesets
1034 """dump the header and diffs for one or more changesets
1035
1035
1036 Print the changeset header and diffs for one or more revisions.
1036 Print the changeset header and diffs for one or more revisions.
1037
1037
1038 The information shown in the changeset header is: author,
1038 The information shown in the changeset header is: author,
1039 changeset hash, parent(s) and commit comment.
1039 changeset hash, parent(s) and commit comment.
1040
1040
1041 NOTE: export may generate unexpected diff output for merge changesets,
1041 NOTE: export may generate unexpected diff output for merge changesets,
1042 as it will compare the merge changeset against its first parent only.
1042 as it will compare the merge changeset against its first parent only.
1043
1043
1044 Output may be to a file, in which case the name of the file is
1044 Output may be to a file, in which case the name of the file is
1045 given using a format string. The formatting rules are as follows:
1045 given using a format string. The formatting rules are as follows:
1046
1046
1047 %% literal "%" character
1047 %% literal "%" character
1048 %H changeset hash (40 bytes of hexadecimal)
1048 %H changeset hash (40 bytes of hexadecimal)
1049 %N number of patches being generated
1049 %N number of patches being generated
1050 %R changeset revision number
1050 %R changeset revision number
1051 %b basename of the exporting repository
1051 %b basename of the exporting repository
1052 %h short-form changeset hash (12 bytes of hexadecimal)
1052 %h short-form changeset hash (12 bytes of hexadecimal)
1053 %n zero-padded sequence number, starting at 1
1053 %n zero-padded sequence number, starting at 1
1054 %r zero-padded changeset revision number
1054 %r zero-padded changeset revision number
1055
1055
1056 Without the -a option, export will avoid generating diffs of files
1056 Without the -a option, export will avoid generating diffs of files
1057 it detects as binary. With -a, export will generate a diff anyway,
1057 it detects as binary. With -a, export will generate a diff anyway,
1058 probably with undesirable results.
1058 probably with undesirable results.
1059
1059
1060 With the --switch-parent option, the diff will be against the second
1060 With the --switch-parent option, the diff will be against the second
1061 parent. It can be useful to review a merge.
1061 parent. It can be useful to review a merge.
1062 """
1062 """
1063 if not changesets:
1063 if not changesets:
1064 raise util.Abort(_("export requires at least one changeset"))
1064 raise util.Abort(_("export requires at least one changeset"))
1065 revs = cmdutil.revrange(repo, changesets)
1065 revs = cmdutil.revrange(repo, changesets)
1066 if len(revs) > 1:
1066 if len(revs) > 1:
1067 ui.note(_('exporting patches:\n'))
1067 ui.note(_('exporting patches:\n'))
1068 else:
1068 else:
1069 ui.note(_('exporting patch:\n'))
1069 ui.note(_('exporting patch:\n'))
1070 patch.export(repo, revs, template=opts['output'],
1070 patch.export(repo, revs, template=opts['output'],
1071 switch_parent=opts['switch_parent'],
1071 switch_parent=opts['switch_parent'],
1072 opts=patch.diffopts(ui, opts))
1072 opts=patch.diffopts(ui, opts))
1073
1073
1074 def grep(ui, repo, pattern, *pats, **opts):
1074 def grep(ui, repo, pattern, *pats, **opts):
1075 """search for a pattern in specified files and revisions
1075 """search for a pattern in specified files and revisions
1076
1076
1077 Search revisions of files for a regular expression.
1077 Search revisions of files for a regular expression.
1078
1078
1079 This command behaves differently than Unix grep. It only accepts
1079 This command behaves differently than Unix grep. It only accepts
1080 Python/Perl regexps. It searches repository history, not the
1080 Python/Perl regexps. It searches repository history, not the
1081 working directory. It always prints the revision number in which
1081 working directory. It always prints the revision number in which
1082 a match appears.
1082 a match appears.
1083
1083
1084 By default, grep only prints output for the first revision of a
1084 By default, grep only prints output for the first revision of a
1085 file in which it finds a match. To get it to print every revision
1085 file in which it finds a match. To get it to print every revision
1086 that contains a change in match status ("-" for a match that
1086 that contains a change in match status ("-" for a match that
1087 becomes a non-match, or "+" for a non-match that becomes a match),
1087 becomes a non-match, or "+" for a non-match that becomes a match),
1088 use the --all flag.
1088 use the --all flag.
1089 """
1089 """
1090 reflags = 0
1090 reflags = 0
1091 if opts['ignore_case']:
1091 if opts['ignore_case']:
1092 reflags |= re.I
1092 reflags |= re.I
1093 regexp = re.compile(pattern, reflags)
1093 regexp = re.compile(pattern, reflags)
1094 sep, eol = ':', '\n'
1094 sep, eol = ':', '\n'
1095 if opts['print0']:
1095 if opts['print0']:
1096 sep = eol = '\0'
1096 sep = eol = '\0'
1097
1097
1098 fcache = {}
1098 fcache = {}
1099 def getfile(fn):
1099 def getfile(fn):
1100 if fn not in fcache:
1100 if fn not in fcache:
1101 fcache[fn] = repo.file(fn)
1101 fcache[fn] = repo.file(fn)
1102 return fcache[fn]
1102 return fcache[fn]
1103
1103
1104 def matchlines(body):
1104 def matchlines(body):
1105 begin = 0
1105 begin = 0
1106 linenum = 0
1106 linenum = 0
1107 while True:
1107 while True:
1108 match = regexp.search(body, begin)
1108 match = regexp.search(body, begin)
1109 if not match:
1109 if not match:
1110 break
1110 break
1111 mstart, mend = match.span()
1111 mstart, mend = match.span()
1112 linenum += body.count('\n', begin, mstart) + 1
1112 linenum += body.count('\n', begin, mstart) + 1
1113 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1113 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1114 lend = body.find('\n', mend)
1114 lend = body.find('\n', mend)
1115 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1115 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1116 begin = lend + 1
1116 begin = lend + 1
1117
1117
1118 class linestate(object):
1118 class linestate(object):
1119 def __init__(self, line, linenum, colstart, colend):
1119 def __init__(self, line, linenum, colstart, colend):
1120 self.line = line
1120 self.line = line
1121 self.linenum = linenum
1121 self.linenum = linenum
1122 self.colstart = colstart
1122 self.colstart = colstart
1123 self.colend = colend
1123 self.colend = colend
1124
1124
1125 def __eq__(self, other):
1125 def __eq__(self, other):
1126 return self.line == other.line
1126 return self.line == other.line
1127
1127
1128 matches = {}
1128 matches = {}
1129 copies = {}
1129 copies = {}
1130 def grepbody(fn, rev, body):
1130 def grepbody(fn, rev, body):
1131 matches[rev].setdefault(fn, [])
1131 matches[rev].setdefault(fn, [])
1132 m = matches[rev][fn]
1132 m = matches[rev][fn]
1133 for lnum, cstart, cend, line in matchlines(body):
1133 for lnum, cstart, cend, line in matchlines(body):
1134 s = linestate(line, lnum, cstart, cend)
1134 s = linestate(line, lnum, cstart, cend)
1135 m.append(s)
1135 m.append(s)
1136
1136
1137 def difflinestates(a, b):
1137 def difflinestates(a, b):
1138 sm = difflib.SequenceMatcher(None, a, b)
1138 sm = difflib.SequenceMatcher(None, a, b)
1139 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1139 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1140 if tag == 'insert':
1140 if tag == 'insert':
1141 for i in xrange(blo, bhi):
1141 for i in xrange(blo, bhi):
1142 yield ('+', b[i])
1142 yield ('+', b[i])
1143 elif tag == 'delete':
1143 elif tag == 'delete':
1144 for i in xrange(alo, ahi):
1144 for i in xrange(alo, ahi):
1145 yield ('-', a[i])
1145 yield ('-', a[i])
1146 elif tag == 'replace':
1146 elif tag == 'replace':
1147 for i in xrange(alo, ahi):
1147 for i in xrange(alo, ahi):
1148 yield ('-', a[i])
1148 yield ('-', a[i])
1149 for i in xrange(blo, bhi):
1149 for i in xrange(blo, bhi):
1150 yield ('+', b[i])
1150 yield ('+', b[i])
1151
1151
1152 prev = {}
1152 prev = {}
1153 def display(fn, rev, states, prevstates):
1153 def display(fn, rev, states, prevstates):
1154 found = False
1154 found = False
1155 filerevmatches = {}
1155 filerevmatches = {}
1156 r = prev.get(fn, -1)
1156 r = prev.get(fn, -1)
1157 if opts['all']:
1157 if opts['all']:
1158 iter = difflinestates(states, prevstates)
1158 iter = difflinestates(states, prevstates)
1159 else:
1159 else:
1160 iter = [('', l) for l in prevstates]
1160 iter = [('', l) for l in prevstates]
1161 for change, l in iter:
1161 for change, l in iter:
1162 cols = [fn, str(r)]
1162 cols = [fn, str(r)]
1163 if opts['line_number']:
1163 if opts['line_number']:
1164 cols.append(str(l.linenum))
1164 cols.append(str(l.linenum))
1165 if opts['all']:
1165 if opts['all']:
1166 cols.append(change)
1166 cols.append(change)
1167 if opts['user']:
1167 if opts['user']:
1168 cols.append(ui.shortuser(get(r)[1]))
1168 cols.append(ui.shortuser(get(r)[1]))
1169 if opts['files_with_matches']:
1169 if opts['files_with_matches']:
1170 c = (fn, r)
1170 c = (fn, r)
1171 if c in filerevmatches:
1171 if c in filerevmatches:
1172 continue
1172 continue
1173 filerevmatches[c] = 1
1173 filerevmatches[c] = 1
1174 else:
1174 else:
1175 cols.append(l.line)
1175 cols.append(l.line)
1176 ui.write(sep.join(cols), eol)
1176 ui.write(sep.join(cols), eol)
1177 found = True
1177 found = True
1178 return found
1178 return found
1179
1179
1180 fstate = {}
1180 fstate = {}
1181 skip = {}
1181 skip = {}
1182 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1182 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1183 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1183 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1184 found = False
1184 found = False
1185 follow = opts.get('follow')
1185 follow = opts.get('follow')
1186 for st, rev, fns in changeiter:
1186 for st, rev, fns in changeiter:
1187 if st == 'window':
1187 if st == 'window':
1188 matches.clear()
1188 matches.clear()
1189 elif st == 'add':
1189 elif st == 'add':
1190 mf = repo.changectx(rev).manifest()
1190 mf = repo.changectx(rev).manifest()
1191 matches[rev] = {}
1191 matches[rev] = {}
1192 for fn in fns:
1192 for fn in fns:
1193 if fn in skip:
1193 if fn in skip:
1194 continue
1194 continue
1195 fstate.setdefault(fn, {})
1195 fstate.setdefault(fn, {})
1196 try:
1196 try:
1197 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1197 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1198 if follow:
1198 if follow:
1199 copied = getfile(fn).renamed(mf[fn])
1199 copied = getfile(fn).renamed(mf[fn])
1200 if copied:
1200 if copied:
1201 copies.setdefault(rev, {})[fn] = copied[0]
1201 copies.setdefault(rev, {})[fn] = copied[0]
1202 except KeyError:
1202 except KeyError:
1203 pass
1203 pass
1204 elif st == 'iter':
1204 elif st == 'iter':
1205 states = matches[rev].items()
1205 states = matches[rev].items()
1206 states.sort()
1206 states.sort()
1207 for fn, m in states:
1207 for fn, m in states:
1208 copy = copies.get(rev, {}).get(fn)
1208 copy = copies.get(rev, {}).get(fn)
1209 if fn in skip:
1209 if fn in skip:
1210 if copy:
1210 if copy:
1211 skip[copy] = True
1211 skip[copy] = True
1212 continue
1212 continue
1213 if fn in prev or fstate[fn]:
1213 if fn in prev or fstate[fn]:
1214 r = display(fn, rev, m, fstate[fn])
1214 r = display(fn, rev, m, fstate[fn])
1215 found = found or r
1215 found = found or r
1216 if r and not opts['all']:
1216 if r and not opts['all']:
1217 skip[fn] = True
1217 skip[fn] = True
1218 if copy:
1218 if copy:
1219 skip[copy] = True
1219 skip[copy] = True
1220 fstate[fn] = m
1220 fstate[fn] = m
1221 if copy:
1221 if copy:
1222 fstate[copy] = m
1222 fstate[copy] = m
1223 prev[fn] = rev
1223 prev[fn] = rev
1224
1224
1225 fstate = fstate.items()
1225 fstate = fstate.items()
1226 fstate.sort()
1226 fstate.sort()
1227 for fn, state in fstate:
1227 for fn, state in fstate:
1228 if fn in skip:
1228 if fn in skip:
1229 continue
1229 continue
1230 if fn not in copies.get(prev[fn], {}):
1230 if fn not in copies.get(prev[fn], {}):
1231 found = display(fn, rev, {}, state) or found
1231 found = display(fn, rev, {}, state) or found
1232 return (not found and 1) or 0
1232 return (not found and 1) or 0
1233
1233
1234 def heads(ui, repo, **opts):
1234 def heads(ui, repo, **opts):
1235 """show current repository heads
1235 """show current repository heads
1236
1236
1237 Show all repository head changesets.
1237 Show all repository head changesets.
1238
1238
1239 Repository "heads" are changesets that don't have children
1239 Repository "heads" are changesets that don't have children
1240 changesets. They are where development generally takes place and
1240 changesets. They are where development generally takes place and
1241 are the usual targets for update and merge operations.
1241 are the usual targets for update and merge operations.
1242 """
1242 """
1243 if opts['rev']:
1243 if opts['rev']:
1244 heads = repo.heads(repo.lookup(opts['rev']))
1244 heads = repo.heads(repo.lookup(opts['rev']))
1245 else:
1245 else:
1246 heads = repo.heads()
1246 heads = repo.heads()
1247 displayer = cmdutil.show_changeset(ui, repo, opts)
1247 displayer = cmdutil.show_changeset(ui, repo, opts)
1248 for n in heads:
1248 for n in heads:
1249 displayer.show(changenode=n)
1249 displayer.show(changenode=n)
1250
1250
1251 def help_(ui, name=None, with_version=False):
1251 def help_(ui, name=None, with_version=False):
1252 """show help for a command, extension, or list of commands
1252 """show help for a command, extension, or list of commands
1253
1253
1254 With no arguments, print a list of commands and short help.
1254 With no arguments, print a list of commands and short help.
1255
1255
1256 Given a command name, print help for that command.
1256 Given a command name, print help for that command.
1257
1257
1258 Given an extension name, print help for that extension, and the
1258 Given an extension name, print help for that extension, and the
1259 commands it provides."""
1259 commands it provides."""
1260 option_lists = []
1260 option_lists = []
1261
1261
1262 def helpcmd(name):
1262 def helpcmd(name):
1263 if with_version:
1263 if with_version:
1264 version_(ui)
1264 version_(ui)
1265 ui.write('\n')
1265 ui.write('\n')
1266 aliases, i = findcmd(ui, name)
1266 aliases, i = findcmd(ui, name)
1267 # synopsis
1267 # synopsis
1268 ui.write("%s\n\n" % i[2])
1268 ui.write("%s\n\n" % i[2])
1269
1269
1270 # description
1270 # description
1271 doc = i[0].__doc__
1271 doc = i[0].__doc__
1272 if not doc:
1272 if not doc:
1273 doc = _("(No help text available)")
1273 doc = _("(No help text available)")
1274 if ui.quiet:
1274 if ui.quiet:
1275 doc = doc.splitlines(0)[0]
1275 doc = doc.splitlines(0)[0]
1276 ui.write("%s\n" % doc.rstrip())
1276 ui.write("%s\n" % doc.rstrip())
1277
1277
1278 if not ui.quiet:
1278 if not ui.quiet:
1279 # aliases
1279 # aliases
1280 if len(aliases) > 1:
1280 if len(aliases) > 1:
1281 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1281 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1282
1282
1283 # options
1283 # options
1284 if i[1]:
1284 if i[1]:
1285 option_lists.append(("options", i[1]))
1285 option_lists.append(("options", i[1]))
1286
1286
1287 def helplist(select=None):
1287 def helplist(select=None):
1288 h = {}
1288 h = {}
1289 cmds = {}
1289 cmds = {}
1290 for c, e in table.items():
1290 for c, e in table.items():
1291 f = c.split("|", 1)[0]
1291 f = c.split("|", 1)[0]
1292 if select and not select(f):
1292 if select and not select(f):
1293 continue
1293 continue
1294 if name == "shortlist" and not f.startswith("^"):
1294 if name == "shortlist" and not f.startswith("^"):
1295 continue
1295 continue
1296 f = f.lstrip("^")
1296 f = f.lstrip("^")
1297 if not ui.debugflag and f.startswith("debug"):
1297 if not ui.debugflag and f.startswith("debug"):
1298 continue
1298 continue
1299 doc = e[0].__doc__
1299 doc = e[0].__doc__
1300 if not doc:
1300 if not doc:
1301 doc = _("(No help text available)")
1301 doc = _("(No help text available)")
1302 h[f] = doc.splitlines(0)[0].rstrip()
1302 h[f] = doc.splitlines(0)[0].rstrip()
1303 cmds[f] = c.lstrip("^")
1303 cmds[f] = c.lstrip("^")
1304
1304
1305 fns = h.keys()
1305 fns = h.keys()
1306 fns.sort()
1306 fns.sort()
1307 m = max(map(len, fns))
1307 m = max(map(len, fns))
1308 for f in fns:
1308 for f in fns:
1309 if ui.verbose:
1309 if ui.verbose:
1310 commands = cmds[f].replace("|",", ")
1310 commands = cmds[f].replace("|",", ")
1311 ui.write(" %s:\n %s\n"%(commands, h[f]))
1311 ui.write(" %s:\n %s\n"%(commands, h[f]))
1312 else:
1312 else:
1313 ui.write(' %-*s %s\n' % (m, f, h[f]))
1313 ui.write(' %-*s %s\n' % (m, f, h[f]))
1314
1314
1315 def helptopic(name):
1315 def helptopic(name):
1316 v = None
1316 v = None
1317 for i in help.helptable:
1317 for i in help.helptable:
1318 l = i.split('|')
1318 l = i.split('|')
1319 if name in l:
1319 if name in l:
1320 v = i
1320 v = i
1321 header = l[-1]
1321 header = l[-1]
1322 if not v:
1322 if not v:
1323 raise UnknownCommand(name)
1323 raise UnknownCommand(name)
1324
1324
1325 # description
1325 # description
1326 doc = help.helptable[v]
1326 doc = help.helptable[v]
1327 if not doc:
1327 if not doc:
1328 doc = _("(No help text available)")
1328 doc = _("(No help text available)")
1329 if callable(doc):
1329 if callable(doc):
1330 doc = doc()
1330 doc = doc()
1331
1331
1332 ui.write("%s\n" % header)
1332 ui.write("%s\n" % header)
1333 ui.write("%s\n" % doc.rstrip())
1333 ui.write("%s\n" % doc.rstrip())
1334
1334
1335 def helpext(name):
1335 def helpext(name):
1336 try:
1336 try:
1337 mod = findext(name)
1337 mod = findext(name)
1338 except KeyError:
1338 except KeyError:
1339 raise UnknownCommand(name)
1339 raise UnknownCommand(name)
1340
1340
1341 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1341 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1342 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1342 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1343 for d in doc[1:]:
1343 for d in doc[1:]:
1344 ui.write(d, '\n')
1344 ui.write(d, '\n')
1345
1345
1346 ui.status('\n')
1346 ui.status('\n')
1347
1347
1348 try:
1348 try:
1349 ct = mod.cmdtable
1349 ct = mod.cmdtable
1350 except AttributeError:
1350 except AttributeError:
1351 ui.status(_('no commands defined\n'))
1351 ui.status(_('no commands defined\n'))
1352 return
1352 return
1353
1353
1354 if ui.verbose:
1354 if ui.verbose:
1355 ui.status(_('list of commands:\n\n'))
1355 ui.status(_('list of commands:\n\n'))
1356 else:
1356 else:
1357 ui.status(_('list of commands (use "hg help -v %s" '
1357 ui.status(_('list of commands (use "hg help -v %s" '
1358 'to show aliases and global options):\n\n') % name)
1358 'to show aliases and global options):\n\n') % name)
1359
1359
1360 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1360 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1361 helplist(modcmds.has_key)
1361 helplist(modcmds.has_key)
1362
1362
1363 if name and name != 'shortlist':
1363 if name and name != 'shortlist':
1364 i = None
1364 i = None
1365 for f in (helpcmd, helptopic, helpext):
1365 for f in (helpcmd, helptopic, helpext):
1366 try:
1366 try:
1367 f(name)
1367 f(name)
1368 i = None
1368 i = None
1369 break
1369 break
1370 except UnknownCommand, inst:
1370 except UnknownCommand, inst:
1371 i = inst
1371 i = inst
1372 if i:
1372 if i:
1373 raise i
1373 raise i
1374
1374
1375 else:
1375 else:
1376 # program name
1376 # program name
1377 if ui.verbose or with_version:
1377 if ui.verbose or with_version:
1378 version_(ui)
1378 version_(ui)
1379 else:
1379 else:
1380 ui.status(_("Mercurial Distributed SCM\n"))
1380 ui.status(_("Mercurial Distributed SCM\n"))
1381 ui.status('\n')
1381 ui.status('\n')
1382
1382
1383 # list of commands
1383 # list of commands
1384 if name == "shortlist":
1384 if name == "shortlist":
1385 ui.status(_('basic commands (use "hg help" '
1385 ui.status(_('basic commands (use "hg help" '
1386 'for the full list or option "-v" for details):\n\n'))
1386 'for the full list or option "-v" for details):\n\n'))
1387 elif ui.verbose:
1387 elif ui.verbose:
1388 ui.status(_('list of commands:\n\n'))
1388 ui.status(_('list of commands:\n\n'))
1389 else:
1389 else:
1390 ui.status(_('list of commands (use "hg help -v" '
1390 ui.status(_('list of commands (use "hg help -v" '
1391 'to show aliases and global options):\n\n'))
1391 'to show aliases and global options):\n\n'))
1392
1392
1393 helplist()
1393 helplist()
1394
1394
1395 # global options
1395 # global options
1396 if ui.verbose:
1396 if ui.verbose:
1397 option_lists.append(("global options", globalopts))
1397 option_lists.append(("global options", globalopts))
1398
1398
1399 # list all option lists
1399 # list all option lists
1400 opt_output = []
1400 opt_output = []
1401 for title, options in option_lists:
1401 for title, options in option_lists:
1402 opt_output.append(("\n%s:\n" % title, None))
1402 opt_output.append(("\n%s:\n" % title, None))
1403 for shortopt, longopt, default, desc in options:
1403 for shortopt, longopt, default, desc in options:
1404 if "DEPRECATED" in desc and not ui.verbose: continue
1404 if "DEPRECATED" in desc and not ui.verbose: continue
1405 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1405 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1406 longopt and " --%s" % longopt),
1406 longopt and " --%s" % longopt),
1407 "%s%s" % (desc,
1407 "%s%s" % (desc,
1408 default
1408 default
1409 and _(" (default: %s)") % default
1409 and _(" (default: %s)") % default
1410 or "")))
1410 or "")))
1411
1411
1412 if opt_output:
1412 if opt_output:
1413 opts_len = max([len(line[0]) for line in opt_output if line[1]])
1413 opts_len = max([len(line[0]) for line in opt_output if line[1]])
1414 for first, second in opt_output:
1414 for first, second in opt_output:
1415 if second:
1415 if second:
1416 ui.write(" %-*s %s\n" % (opts_len, first, second))
1416 ui.write(" %-*s %s\n" % (opts_len, first, second))
1417 else:
1417 else:
1418 ui.write("%s\n" % first)
1418 ui.write("%s\n" % first)
1419
1419
1420 def identify(ui, repo):
1420 def identify(ui, repo):
1421 """print information about the working copy
1421 """print information about the working copy
1422
1422
1423 Print a short summary of the current state of the repo.
1423 Print a short summary of the current state of the repo.
1424
1424
1425 This summary identifies the repository state using one or two parent
1425 This summary identifies the repository state using one or two parent
1426 hash identifiers, followed by a "+" if there are uncommitted changes
1426 hash identifiers, followed by a "+" if there are uncommitted changes
1427 in the working directory, followed by a list of tags for this revision.
1427 in the working directory, followed by a list of tags for this revision.
1428 """
1428 """
1429 parents = [p for p in repo.dirstate.parents() if p != nullid]
1429 parents = [p for p in repo.dirstate.parents() if p != nullid]
1430 if not parents:
1430 if not parents:
1431 ui.write(_("unknown\n"))
1431 ui.write(_("unknown\n"))
1432 return
1432 return
1433
1433
1434 hexfunc = ui.debugflag and hex or short
1434 hexfunc = ui.debugflag and hex or short
1435 modified, added, removed, deleted = repo.status()[:4]
1435 modified, added, removed, deleted = repo.status()[:4]
1436 output = ["%s%s" %
1436 output = ["%s%s" %
1437 ('+'.join([hexfunc(parent) for parent in parents]),
1437 ('+'.join([hexfunc(parent) for parent in parents]),
1438 (modified or added or removed or deleted) and "+" or "")]
1438 (modified or added or removed or deleted) and "+" or "")]
1439
1439
1440 if not ui.quiet:
1440 if not ui.quiet:
1441
1441
1442 branch = util.tolocal(repo.workingctx().branch())
1442 branch = util.tolocal(repo.workingctx().branch())
1443 if branch:
1443 if branch:
1444 output.append("(%s)" % branch)
1444 output.append("(%s)" % branch)
1445
1445
1446 # multiple tags for a single parent separated by '/'
1446 # multiple tags for a single parent separated by '/'
1447 parenttags = ['/'.join(tags)
1447 parenttags = ['/'.join(tags)
1448 for tags in map(repo.nodetags, parents) if tags]
1448 for tags in map(repo.nodetags, parents) if tags]
1449 # tags for multiple parents separated by ' + '
1449 # tags for multiple parents separated by ' + '
1450 if parenttags:
1450 if parenttags:
1451 output.append(' + '.join(parenttags))
1451 output.append(' + '.join(parenttags))
1452
1452
1453 ui.write("%s\n" % ' '.join(output))
1453 ui.write("%s\n" % ' '.join(output))
1454
1454
1455 def import_(ui, repo, patch1, *patches, **opts):
1455 def import_(ui, repo, patch1, *patches, **opts):
1456 """import an ordered set of patches
1456 """import an ordered set of patches
1457
1457
1458 Import a list of patches and commit them individually.
1458 Import a list of patches and commit them individually.
1459
1459
1460 If there are outstanding changes in the working directory, import
1460 If there are outstanding changes in the working directory, import
1461 will abort unless given the -f flag.
1461 will abort unless given the -f flag.
1462
1462
1463 You can import a patch straight from a mail message. Even patches
1463 You can import a patch straight from a mail message. Even patches
1464 as attachments work (body part must be type text/plain or
1464 as attachments work (body part must be type text/plain or
1465 text/x-patch to be used). From and Subject headers of email
1465 text/x-patch to be used). From and Subject headers of email
1466 message are used as default committer and commit message. All
1466 message are used as default committer and commit message. All
1467 text/plain body parts before first diff are added to commit
1467 text/plain body parts before first diff are added to commit
1468 message.
1468 message.
1469
1469
1470 If imported patch was generated by hg export, user and description
1470 If imported patch was generated by hg export, user and description
1471 from patch override values from message headers and body. Values
1471 from patch override values from message headers and body. Values
1472 given on command line with -m and -u override these.
1472 given on command line with -m and -u override these.
1473
1473
1474 To read a patch from standard input, use patch name "-".
1474 To read a patch from standard input, use patch name "-".
1475 """
1475 """
1476 patches = (patch1,) + patches
1476 patches = (patch1,) + patches
1477
1477
1478 if not opts['force']:
1478 if not opts['force']:
1479 bail_if_changed(repo)
1479 bail_if_changed(repo)
1480
1480
1481 d = opts["base"]
1481 d = opts["base"]
1482 strip = opts["strip"]
1482 strip = opts["strip"]
1483
1483
1484 wlock = repo.wlock()
1484 wlock = repo.wlock()
1485 lock = repo.lock()
1485 lock = repo.lock()
1486
1486
1487 for p in patches:
1487 for p in patches:
1488 pf = os.path.join(d, p)
1488 pf = os.path.join(d, p)
1489
1489
1490 if pf == '-':
1490 if pf == '-':
1491 ui.status(_("applying patch from stdin\n"))
1491 ui.status(_("applying patch from stdin\n"))
1492 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1492 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1493 else:
1493 else:
1494 ui.status(_("applying %s\n") % p)
1494 ui.status(_("applying %s\n") % p)
1495 tmpname, message, user, date = patch.extract(ui, file(pf))
1495 tmpname, message, user, date = patch.extract(ui, file(pf))
1496
1496
1497 if tmpname is None:
1497 if tmpname is None:
1498 raise util.Abort(_('no diffs found'))
1498 raise util.Abort(_('no diffs found'))
1499
1499
1500 try:
1500 try:
1501 cmdline_message = logmessage(opts)
1501 cmdline_message = logmessage(opts)
1502 if cmdline_message:
1502 if cmdline_message:
1503 # pickup the cmdline msg
1503 # pickup the cmdline msg
1504 message = cmdline_message
1504 message = cmdline_message
1505 elif message:
1505 elif message:
1506 # pickup the patch msg
1506 # pickup the patch msg
1507 message = message.strip()
1507 message = message.strip()
1508 else:
1508 else:
1509 # launch the editor
1509 # launch the editor
1510 message = None
1510 message = None
1511 ui.debug(_('message:\n%s\n') % message)
1511 ui.debug(_('message:\n%s\n') % message)
1512
1512
1513 files = {}
1513 files = {}
1514 try:
1514 try:
1515 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1515 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1516 files=files)
1516 files=files)
1517 finally:
1517 finally:
1518 files = patch.updatedir(ui, repo, files, wlock=wlock)
1518 files = patch.updatedir(ui, repo, files, wlock=wlock)
1519 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1519 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1520 finally:
1520 finally:
1521 os.unlink(tmpname)
1521 os.unlink(tmpname)
1522
1522
1523 def incoming(ui, repo, source="default", **opts):
1523 def incoming(ui, repo, source="default", **opts):
1524 """show new changesets found in source
1524 """show new changesets found in source
1525
1525
1526 Show new changesets found in the specified path/URL or the default
1526 Show new changesets found in the specified path/URL or the default
1527 pull location. These are the changesets that would be pulled if a pull
1527 pull location. These are the changesets that would be pulled if a pull
1528 was requested.
1528 was requested.
1529
1529
1530 For remote repository, using --bundle avoids downloading the changesets
1530 For remote repository, using --bundle avoids downloading the changesets
1531 twice if the incoming is followed by a pull.
1531 twice if the incoming is followed by a pull.
1532
1532
1533 See pull for valid source format details.
1533 See pull for valid source format details.
1534 """
1534 """
1535 source = ui.expandpath(source)
1535 source = ui.expandpath(source)
1536 setremoteconfig(ui, opts)
1536 setremoteconfig(ui, opts)
1537
1537
1538 other = hg.repository(ui, source)
1538 other = hg.repository(ui, source)
1539 incoming = repo.findincoming(other, force=opts["force"])
1539 incoming = repo.findincoming(other, force=opts["force"])
1540 if not incoming:
1540 if not incoming:
1541 try:
1541 try:
1542 os.unlink(opts["bundle"])
1542 os.unlink(opts["bundle"])
1543 except:
1543 except:
1544 pass
1544 pass
1545 ui.status(_("no changes found\n"))
1545 ui.status(_("no changes found\n"))
1546 return 1
1546 return 1
1547
1547
1548 cleanup = None
1548 cleanup = None
1549 try:
1549 try:
1550 fname = opts["bundle"]
1550 fname = opts["bundle"]
1551 if fname or not other.local():
1551 if fname or not other.local():
1552 # create a bundle (uncompressed if other repo is not local)
1552 # create a bundle (uncompressed if other repo is not local)
1553 cg = other.changegroup(incoming, "incoming")
1553 cg = other.changegroup(incoming, "incoming")
1554 bundletype = other.local() and "HG10BZ" or "HG10UN"
1554 bundletype = other.local() and "HG10BZ" or "HG10UN"
1555 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1555 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1556 # keep written bundle?
1556 # keep written bundle?
1557 if opts["bundle"]:
1557 if opts["bundle"]:
1558 cleanup = None
1558 cleanup = None
1559 if not other.local():
1559 if not other.local():
1560 # use the created uncompressed bundlerepo
1560 # use the created uncompressed bundlerepo
1561 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1561 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1562
1562
1563 revs = None
1563 revs = None
1564 if opts['rev']:
1564 if opts['rev']:
1565 revs = [other.lookup(rev) for rev in opts['rev']]
1565 revs = [other.lookup(rev) for rev in opts['rev']]
1566 o = other.changelog.nodesbetween(incoming, revs)[0]
1566 o = other.changelog.nodesbetween(incoming, revs)[0]
1567 if opts['newest_first']:
1567 if opts['newest_first']:
1568 o.reverse()
1568 o.reverse()
1569 displayer = cmdutil.show_changeset(ui, other, opts)
1569 displayer = cmdutil.show_changeset(ui, other, opts)
1570 for n in o:
1570 for n in o:
1571 parents = [p for p in other.changelog.parents(n) if p != nullid]
1571 parents = [p for p in other.changelog.parents(n) if p != nullid]
1572 if opts['no_merges'] and len(parents) == 2:
1572 if opts['no_merges'] and len(parents) == 2:
1573 continue
1573 continue
1574 displayer.show(changenode=n)
1574 displayer.show(changenode=n)
1575 finally:
1575 finally:
1576 if hasattr(other, 'close'):
1576 if hasattr(other, 'close'):
1577 other.close()
1577 other.close()
1578 if cleanup:
1578 if cleanup:
1579 os.unlink(cleanup)
1579 os.unlink(cleanup)
1580
1580
1581 def init(ui, dest=".", **opts):
1581 def init(ui, dest=".", **opts):
1582 """create a new repository in the given directory
1582 """create a new repository in the given directory
1583
1583
1584 Initialize a new repository in the given directory. If the given
1584 Initialize a new repository in the given directory. If the given
1585 directory does not exist, it is created.
1585 directory does not exist, it is created.
1586
1586
1587 If no directory is given, the current directory is used.
1587 If no directory is given, the current directory is used.
1588
1588
1589 It is possible to specify an ssh:// URL as the destination.
1589 It is possible to specify an ssh:// URL as the destination.
1590 Look at the help text for the pull command for important details
1590 Look at the help text for the pull command for important details
1591 about ssh:// URLs.
1591 about ssh:// URLs.
1592 """
1592 """
1593 setremoteconfig(ui, opts)
1593 setremoteconfig(ui, opts)
1594 hg.repository(ui, dest, create=1)
1594 hg.repository(ui, dest, create=1)
1595
1595
1596 def locate(ui, repo, *pats, **opts):
1596 def locate(ui, repo, *pats, **opts):
1597 """locate files matching specific patterns
1597 """locate files matching specific patterns
1598
1598
1599 Print all files under Mercurial control whose names match the
1599 Print all files under Mercurial control whose names match the
1600 given patterns.
1600 given patterns.
1601
1601
1602 This command searches the current directory and its
1602 This command searches the current directory and its
1603 subdirectories. To search an entire repository, move to the root
1603 subdirectories. To search an entire repository, move to the root
1604 of the repository.
1604 of the repository.
1605
1605
1606 If no patterns are given to match, this command prints all file
1606 If no patterns are given to match, this command prints all file
1607 names.
1607 names.
1608
1608
1609 If you want to feed the output of this command into the "xargs"
1609 If you want to feed the output of this command into the "xargs"
1610 command, use the "-0" option to both this command and "xargs".
1610 command, use the "-0" option to both this command and "xargs".
1611 This will avoid the problem of "xargs" treating single filenames
1611 This will avoid the problem of "xargs" treating single filenames
1612 that contain white space as multiple filenames.
1612 that contain white space as multiple filenames.
1613 """
1613 """
1614 end = opts['print0'] and '\0' or '\n'
1614 end = opts['print0'] and '\0' or '\n'
1615 rev = opts['rev']
1615 rev = opts['rev']
1616 if rev:
1616 if rev:
1617 node = repo.lookup(rev)
1617 node = repo.lookup(rev)
1618 else:
1618 else:
1619 node = None
1619 node = None
1620
1620
1621 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1621 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1622 head='(?:.*/|)'):
1622 head='(?:.*/|)'):
1623 if not node and repo.dirstate.state(abs) == '?':
1623 if not node and repo.dirstate.state(abs) == '?':
1624 continue
1624 continue
1625 if opts['fullpath']:
1625 if opts['fullpath']:
1626 ui.write(os.path.join(repo.root, abs), end)
1626 ui.write(os.path.join(repo.root, abs), end)
1627 else:
1627 else:
1628 ui.write(((pats and rel) or abs), end)
1628 ui.write(((pats and rel) or abs), end)
1629
1629
1630 def log(ui, repo, *pats, **opts):
1630 def log(ui, repo, *pats, **opts):
1631 """show revision history of entire repository or files
1631 """show revision history of entire repository or files
1632
1632
1633 Print the revision history of the specified files or the entire
1633 Print the revision history of the specified files or the entire
1634 project.
1634 project.
1635
1635
1636 File history is shown without following rename or copy history of
1636 File history is shown without following rename or copy history of
1637 files. Use -f/--follow with a file name to follow history across
1637 files. Use -f/--follow with a file name to follow history across
1638 renames and copies. --follow without a file name will only show
1638 renames and copies. --follow without a file name will only show
1639 ancestors or descendants of the starting revision. --follow-first
1639 ancestors or descendants of the starting revision. --follow-first
1640 only follows the first parent of merge revisions.
1640 only follows the first parent of merge revisions.
1641
1641
1642 If no revision range is specified, the default is tip:0 unless
1642 If no revision range is specified, the default is tip:0 unless
1643 --follow is set, in which case the working directory parent is
1643 --follow is set, in which case the working directory parent is
1644 used as the starting revision.
1644 used as the starting revision.
1645
1645
1646 By default this command outputs: changeset id and hash, tags,
1646 By default this command outputs: changeset id and hash, tags,
1647 non-trivial parents, user, date and time, and a summary for each
1647 non-trivial parents, user, date and time, and a summary for each
1648 commit. When the -v/--verbose switch is used, the list of changed
1648 commit. When the -v/--verbose switch is used, the list of changed
1649 files and full commit message is shown.
1649 files and full commit message is shown.
1650
1650
1651 NOTE: log -p may generate unexpected diff output for merge
1651 NOTE: log -p may generate unexpected diff output for merge
1652 changesets, as it will compare the merge changeset against its
1652 changesets, as it will compare the merge changeset against its
1653 first parent only. Also, the files: list will only reflect files
1653 first parent only. Also, the files: list will only reflect files
1654 that are different from BOTH parents.
1654 that are different from BOTH parents.
1655
1655
1656 """
1656 """
1657
1657
1658 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1658 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1659 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1659 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1660
1660
1661 if opts['limit']:
1661 if opts['limit']:
1662 try:
1662 try:
1663 limit = int(opts['limit'])
1663 limit = int(opts['limit'])
1664 except ValueError:
1664 except ValueError:
1665 raise util.Abort(_('limit must be a positive integer'))
1665 raise util.Abort(_('limit must be a positive integer'))
1666 if limit <= 0: raise util.Abort(_('limit must be positive'))
1666 if limit <= 0: raise util.Abort(_('limit must be positive'))
1667 else:
1667 else:
1668 limit = sys.maxint
1668 limit = sys.maxint
1669 count = 0
1669 count = 0
1670
1670
1671 if opts['copies'] and opts['rev']:
1671 if opts['copies'] and opts['rev']:
1672 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1672 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1673 else:
1673 else:
1674 endrev = repo.changelog.count()
1674 endrev = repo.changelog.count()
1675 rcache = {}
1675 rcache = {}
1676 ncache = {}
1676 ncache = {}
1677 dcache = []
1677 dcache = []
1678 def getrenamed(fn, rev, man):
1678 def getrenamed(fn, rev, man):
1679 '''looks up all renames for a file (up to endrev) the first
1679 '''looks up all renames for a file (up to endrev) the first
1680 time the file is given. It indexes on the changerev and only
1680 time the file is given. It indexes on the changerev and only
1681 parses the manifest if linkrev != changerev.
1681 parses the manifest if linkrev != changerev.
1682 Returns rename info for fn at changerev rev.'''
1682 Returns rename info for fn at changerev rev.'''
1683 if fn not in rcache:
1683 if fn not in rcache:
1684 rcache[fn] = {}
1684 rcache[fn] = {}
1685 ncache[fn] = {}
1685 ncache[fn] = {}
1686 fl = repo.file(fn)
1686 fl = repo.file(fn)
1687 for i in xrange(fl.count()):
1687 for i in xrange(fl.count()):
1688 node = fl.node(i)
1688 node = fl.node(i)
1689 lr = fl.linkrev(node)
1689 lr = fl.linkrev(node)
1690 renamed = fl.renamed(node)
1690 renamed = fl.renamed(node)
1691 rcache[fn][lr] = renamed
1691 rcache[fn][lr] = renamed
1692 if renamed:
1692 if renamed:
1693 ncache[fn][node] = renamed
1693 ncache[fn][node] = renamed
1694 if lr >= endrev:
1694 if lr >= endrev:
1695 break
1695 break
1696 if rev in rcache[fn]:
1696 if rev in rcache[fn]:
1697 return rcache[fn][rev]
1697 return rcache[fn][rev]
1698 mr = repo.manifest.rev(man)
1698 mr = repo.manifest.rev(man)
1699 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1699 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1700 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1700 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1701 if not dcache or dcache[0] != man:
1701 if not dcache or dcache[0] != man:
1702 dcache[:] = [man, repo.manifest.readdelta(man)]
1702 dcache[:] = [man, repo.manifest.readdelta(man)]
1703 if fn in dcache[1]:
1703 if fn in dcache[1]:
1704 return ncache[fn].get(dcache[1][fn])
1704 return ncache[fn].get(dcache[1][fn])
1705 return None
1705 return None
1706
1706
1707 df = False
1707 df = False
1708 if opts["date"]:
1708 if opts["date"]:
1709 df = util.matchdate(opts["date"])
1709 df = util.matchdate(opts["date"])
1710
1710
1711 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1711 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1712 for st, rev, fns in changeiter:
1712 for st, rev, fns in changeiter:
1713 if st == 'add':
1713 if st == 'add':
1714 changenode = repo.changelog.node(rev)
1714 changenode = repo.changelog.node(rev)
1715 parents = [p for p in repo.changelog.parentrevs(rev)
1715 parents = [p for p in repo.changelog.parentrevs(rev)
1716 if p != nullrev]
1716 if p != nullrev]
1717 if opts['no_merges'] and len(parents) == 2:
1717 if opts['no_merges'] and len(parents) == 2:
1718 continue
1718 continue
1719 if opts['only_merges'] and len(parents) != 2:
1719 if opts['only_merges'] and len(parents) != 2:
1720 continue
1720 continue
1721
1721
1722 if df:
1722 if df:
1723 changes = get(rev)
1723 changes = get(rev)
1724 if not df(changes[2][0]):
1724 if not df(changes[2][0]):
1725 continue
1725 continue
1726
1726
1727 if opts['keyword']:
1727 if opts['keyword']:
1728 changes = get(rev)
1728 changes = get(rev)
1729 miss = 0
1729 miss = 0
1730 for k in [kw.lower() for kw in opts['keyword']]:
1730 for k in [kw.lower() for kw in opts['keyword']]:
1731 if not (k in changes[1].lower() or
1731 if not (k in changes[1].lower() or
1732 k in changes[4].lower() or
1732 k in changes[4].lower() or
1733 k in " ".join(changes[3][:20]).lower()):
1733 k in " ".join(changes[3][:20]).lower()):
1734 miss = 1
1734 miss = 1
1735 break
1735 break
1736 if miss:
1736 if miss:
1737 continue
1737 continue
1738
1738
1739 copies = []
1739 copies = []
1740 if opts.get('copies') and rev:
1740 if opts.get('copies') and rev:
1741 mf = get(rev)[0]
1741 mf = get(rev)[0]
1742 for fn in get(rev)[3]:
1742 for fn in get(rev)[3]:
1743 rename = getrenamed(fn, rev, mf)
1743 rename = getrenamed(fn, rev, mf)
1744 if rename:
1744 if rename:
1745 copies.append((fn, rename[0]))
1745 copies.append((fn, rename[0]))
1746 displayer.show(rev, changenode, copies=copies)
1746 displayer.show(rev, changenode, copies=copies)
1747 elif st == 'iter':
1747 elif st == 'iter':
1748 if count == limit: break
1748 if count == limit: break
1749 if displayer.flush(rev):
1749 if displayer.flush(rev):
1750 count += 1
1750 count += 1
1751
1751
1752 def manifest(ui, repo, rev=None):
1752 def manifest(ui, repo, rev=None):
1753 """output the current or given revision of the project manifest
1753 """output the current or given revision of the project manifest
1754
1754
1755 Print a list of version controlled files for the given revision.
1755 Print a list of version controlled files for the given revision.
1756 If no revision is given, the parent of the working directory is used,
1756 If no revision is given, the parent of the working directory is used,
1757 or tip if no revision is checked out.
1757 or tip if no revision is checked out.
1758
1758
1759 The manifest is the list of files being version controlled. If no revision
1759 The manifest is the list of files being version controlled. If no revision
1760 is given then the first parent of the working directory is used.
1760 is given then the first parent of the working directory is used.
1761
1761
1762 With -v flag, print file permissions. With --debug flag, print
1762 With -v flag, print file permissions. With --debug flag, print
1763 file revision hashes.
1763 file revision hashes.
1764 """
1764 """
1765
1765
1766 m = repo.changectx(rev).manifest()
1766 m = repo.changectx(rev).manifest()
1767 files = m.keys()
1767 files = m.keys()
1768 files.sort()
1768 files.sort()
1769
1769
1770 for f in files:
1770 for f in files:
1771 if ui.debugflag:
1771 if ui.debugflag:
1772 ui.write("%40s " % hex(m[f]))
1772 ui.write("%40s " % hex(m[f]))
1773 if ui.verbose:
1773 if ui.verbose:
1774 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1774 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1775 ui.write("%s\n" % f)
1775 ui.write("%s\n" % f)
1776
1776
1777 def merge(ui, repo, node=None, force=None):
1777 def merge(ui, repo, node=None, force=None):
1778 """merge working directory with another revision
1778 """merge working directory with another revision
1779
1779
1780 Merge the contents of the current working directory and the
1780 Merge the contents of the current working directory and the
1781 requested revision. Files that changed between either parent are
1781 requested revision. Files that changed between either parent are
1782 marked as changed for the next commit and a commit must be
1782 marked as changed for the next commit and a commit must be
1783 performed before any further updates are allowed.
1783 performed before any further updates are allowed.
1784
1784
1785 If no revision is specified, the working directory's parent is a
1785 If no revision is specified, the working directory's parent is a
1786 head revision, and the repository contains exactly one other head,
1786 head revision, and the repository contains exactly one other head,
1787 the other head is merged with by default. Otherwise, an explicit
1787 the other head is merged with by default. Otherwise, an explicit
1788 revision to merge with must be provided.
1788 revision to merge with must be provided.
1789 """
1789 """
1790
1790
1791 if not node:
1791 if not node:
1792 heads = repo.heads()
1792 heads = repo.heads()
1793 if len(heads) > 2:
1793 if len(heads) > 2:
1794 raise util.Abort(_('repo has %d heads - '
1794 raise util.Abort(_('repo has %d heads - '
1795 'please merge with an explicit rev') %
1795 'please merge with an explicit rev') %
1796 len(heads))
1796 len(heads))
1797 if len(heads) == 1:
1797 if len(heads) == 1:
1798 raise util.Abort(_('there is nothing to merge - '
1798 raise util.Abort(_('there is nothing to merge - '
1799 'use "hg update" instead'))
1799 'use "hg update" instead'))
1800 parent = repo.dirstate.parents()[0]
1800 parent = repo.dirstate.parents()[0]
1801 if parent not in heads:
1801 if parent not in heads:
1802 raise util.Abort(_('working dir not at a head rev - '
1802 raise util.Abort(_('working dir not at a head rev - '
1803 'use "hg update" or merge with an explicit rev'))
1803 'use "hg update" or merge with an explicit rev'))
1804 node = parent == heads[0] and heads[-1] or heads[0]
1804 node = parent == heads[0] and heads[-1] or heads[0]
1805 return hg.merge(repo, node, force=force)
1805 return hg.merge(repo, node, force=force)
1806
1806
1807 def outgoing(ui, repo, dest=None, **opts):
1807 def outgoing(ui, repo, dest=None, **opts):
1808 """show changesets not found in destination
1808 """show changesets not found in destination
1809
1809
1810 Show changesets not found in the specified destination repository or
1810 Show changesets not found in the specified destination repository or
1811 the default push location. These are the changesets that would be pushed
1811 the default push location. These are the changesets that would be pushed
1812 if a push was requested.
1812 if a push was requested.
1813
1813
1814 See pull for valid destination format details.
1814 See pull for valid destination format details.
1815 """
1815 """
1816 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1816 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1817 setremoteconfig(ui, opts)
1817 setremoteconfig(ui, opts)
1818 revs = None
1818 revs = None
1819 if opts['rev']:
1819 if opts['rev']:
1820 revs = [repo.lookup(rev) for rev in opts['rev']]
1820 revs = [repo.lookup(rev) for rev in opts['rev']]
1821
1821
1822 other = hg.repository(ui, dest)
1822 other = hg.repository(ui, dest)
1823 o = repo.findoutgoing(other, force=opts['force'])
1823 o = repo.findoutgoing(other, force=opts['force'])
1824 if not o:
1824 if not o:
1825 ui.status(_("no changes found\n"))
1825 ui.status(_("no changes found\n"))
1826 return 1
1826 return 1
1827 o = repo.changelog.nodesbetween(o, revs)[0]
1827 o = repo.changelog.nodesbetween(o, revs)[0]
1828 if opts['newest_first']:
1828 if opts['newest_first']:
1829 o.reverse()
1829 o.reverse()
1830 displayer = cmdutil.show_changeset(ui, repo, opts)
1830 displayer = cmdutil.show_changeset(ui, repo, opts)
1831 for n in o:
1831 for n in o:
1832 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1832 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1833 if opts['no_merges'] and len(parents) == 2:
1833 if opts['no_merges'] and len(parents) == 2:
1834 continue
1834 continue
1835 displayer.show(changenode=n)
1835 displayer.show(changenode=n)
1836
1836
1837 def parents(ui, repo, file_=None, **opts):
1837 def parents(ui, repo, file_=None, **opts):
1838 """show the parents of the working dir or revision
1838 """show the parents of the working dir or revision
1839
1839
1840 Print the working directory's parent revisions.
1840 Print the working directory's parent revisions.
1841 """
1841 """
1842 rev = opts.get('rev')
1842 rev = opts.get('rev')
1843 if rev:
1843 if rev:
1844 if file_:
1844 if file_:
1845 ctx = repo.filectx(file_, changeid=rev)
1845 ctx = repo.filectx(file_, changeid=rev)
1846 else:
1846 else:
1847 ctx = repo.changectx(rev)
1847 ctx = repo.changectx(rev)
1848 p = [cp.node() for cp in ctx.parents()]
1848 p = [cp.node() for cp in ctx.parents()]
1849 else:
1849 else:
1850 p = repo.dirstate.parents()
1850 p = repo.dirstate.parents()
1851
1851
1852 displayer = cmdutil.show_changeset(ui, repo, opts)
1852 displayer = cmdutil.show_changeset(ui, repo, opts)
1853 for n in p:
1853 for n in p:
1854 if n != nullid:
1854 if n != nullid:
1855 displayer.show(changenode=n)
1855 displayer.show(changenode=n)
1856
1856
1857 def paths(ui, repo, search=None):
1857 def paths(ui, repo, search=None):
1858 """show definition of symbolic path names
1858 """show definition of symbolic path names
1859
1859
1860 Show definition of symbolic path name NAME. If no name is given, show
1860 Show definition of symbolic path name NAME. If no name is given, show
1861 definition of available names.
1861 definition of available names.
1862
1862
1863 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1863 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1864 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1864 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1865 """
1865 """
1866 if search:
1866 if search:
1867 for name, path in ui.configitems("paths"):
1867 for name, path in ui.configitems("paths"):
1868 if name == search:
1868 if name == search:
1869 ui.write("%s\n" % path)
1869 ui.write("%s\n" % path)
1870 return
1870 return
1871 ui.warn(_("not found!\n"))
1871 ui.warn(_("not found!\n"))
1872 return 1
1872 return 1
1873 else:
1873 else:
1874 for name, path in ui.configitems("paths"):
1874 for name, path in ui.configitems("paths"):
1875 ui.write("%s = %s\n" % (name, path))
1875 ui.write("%s = %s\n" % (name, path))
1876
1876
1877 def postincoming(ui, repo, modheads, optupdate):
1877 def postincoming(ui, repo, modheads, optupdate):
1878 if modheads == 0:
1878 if modheads == 0:
1879 return
1879 return
1880 if optupdate:
1880 if optupdate:
1881 if modheads == 1:
1881 if modheads == 1:
1882 return hg.update(repo, repo.changelog.tip()) # update
1882 return hg.update(repo, repo.changelog.tip()) # update
1883 else:
1883 else:
1884 ui.status(_("not updating, since new heads added\n"))
1884 ui.status(_("not updating, since new heads added\n"))
1885 if modheads > 1:
1885 if modheads > 1:
1886 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1886 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1887 else:
1887 else:
1888 ui.status(_("(run 'hg update' to get a working copy)\n"))
1888 ui.status(_("(run 'hg update' to get a working copy)\n"))
1889
1889
1890 def pull(ui, repo, source="default", **opts):
1890 def pull(ui, repo, source="default", **opts):
1891 """pull changes from the specified source
1891 """pull changes from the specified source
1892
1892
1893 Pull changes from a remote repository to a local one.
1893 Pull changes from a remote repository to a local one.
1894
1894
1895 This finds all changes from the repository at the specified path
1895 This finds all changes from the repository at the specified path
1896 or URL and adds them to the local repository. By default, this
1896 or URL and adds them to the local repository. By default, this
1897 does not update the copy of the project in the working directory.
1897 does not update the copy of the project in the working directory.
1898
1898
1899 Valid URLs are of the form:
1899 Valid URLs are of the form:
1900
1900
1901 local/filesystem/path (or file://local/filesystem/path)
1901 local/filesystem/path (or file://local/filesystem/path)
1902 http://[user@]host[:port]/[path]
1902 http://[user@]host[:port]/[path]
1903 https://[user@]host[:port]/[path]
1903 https://[user@]host[:port]/[path]
1904 ssh://[user@]host[:port]/[path]
1904 ssh://[user@]host[:port]/[path]
1905 static-http://host[:port]/[path]
1905 static-http://host[:port]/[path]
1906
1906
1907 Paths in the local filesystem can either point to Mercurial
1907 Paths in the local filesystem can either point to Mercurial
1908 repositories or to bundle files (as created by 'hg bundle' or
1908 repositories or to bundle files (as created by 'hg bundle' or
1909 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1909 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1910 allows access to a Mercurial repository where you simply use a web
1910 allows access to a Mercurial repository where you simply use a web
1911 server to publish the .hg directory as static content.
1911 server to publish the .hg directory as static content.
1912
1912
1913 Some notes about using SSH with Mercurial:
1913 Some notes about using SSH with Mercurial:
1914 - SSH requires an accessible shell account on the destination machine
1914 - SSH requires an accessible shell account on the destination machine
1915 and a copy of hg in the remote path or specified with as remotecmd.
1915 and a copy of hg in the remote path or specified with as remotecmd.
1916 - path is relative to the remote user's home directory by default.
1916 - path is relative to the remote user's home directory by default.
1917 Use an extra slash at the start of a path to specify an absolute path:
1917 Use an extra slash at the start of a path to specify an absolute path:
1918 ssh://example.com//tmp/repository
1918 ssh://example.com//tmp/repository
1919 - Mercurial doesn't use its own compression via SSH; the right thing
1919 - Mercurial doesn't use its own compression via SSH; the right thing
1920 to do is to configure it in your ~/.ssh/config, e.g.:
1920 to do is to configure it in your ~/.ssh/config, e.g.:
1921 Host *.mylocalnetwork.example.com
1921 Host *.mylocalnetwork.example.com
1922 Compression no
1922 Compression no
1923 Host *
1923 Host *
1924 Compression yes
1924 Compression yes
1925 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1925 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1926 with the --ssh command line option.
1926 with the --ssh command line option.
1927 """
1927 """
1928 source = ui.expandpath(source)
1928 source = ui.expandpath(source)
1929 setremoteconfig(ui, opts)
1929 setremoteconfig(ui, opts)
1930
1930
1931 other = hg.repository(ui, source)
1931 other = hg.repository(ui, source)
1932 ui.status(_('pulling from %s\n') % (source))
1932 ui.status(_('pulling from %s\n') % (source))
1933 revs = None
1933 revs = None
1934 if opts['rev']:
1934 if opts['rev']:
1935 if 'lookup' in other.capabilities:
1935 if 'lookup' in other.capabilities:
1936 revs = [other.lookup(rev) for rev in opts['rev']]
1936 revs = [other.lookup(rev) for rev in opts['rev']]
1937 else:
1937 else:
1938 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1938 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1939 raise util.Abort(error)
1939 raise util.Abort(error)
1940 modheads = repo.pull(other, heads=revs, force=opts['force'])
1940 modheads = repo.pull(other, heads=revs, force=opts['force'])
1941 return postincoming(ui, repo, modheads, opts['update'])
1941 return postincoming(ui, repo, modheads, opts['update'])
1942
1942
1943 def push(ui, repo, dest=None, **opts):
1943 def push(ui, repo, dest=None, **opts):
1944 """push changes to the specified destination
1944 """push changes to the specified destination
1945
1945
1946 Push changes from the local repository to the given destination.
1946 Push changes from the local repository to the given destination.
1947
1947
1948 This is the symmetrical operation for pull. It helps to move
1948 This is the symmetrical operation for pull. It helps to move
1949 changes from the current repository to a different one. If the
1949 changes from the current repository to a different one. If the
1950 destination is local this is identical to a pull in that directory
1950 destination is local this is identical to a pull in that directory
1951 from the current one.
1951 from the current one.
1952
1952
1953 By default, push will refuse to run if it detects the result would
1953 By default, push will refuse to run if it detects the result would
1954 increase the number of remote heads. This generally indicates the
1954 increase the number of remote heads. This generally indicates the
1955 the client has forgotten to sync and merge before pushing.
1955 the client has forgotten to sync and merge before pushing.
1956
1956
1957 Valid URLs are of the form:
1957 Valid URLs are of the form:
1958
1958
1959 local/filesystem/path (or file://local/filesystem/path)
1959 local/filesystem/path (or file://local/filesystem/path)
1960 ssh://[user@]host[:port]/[path]
1960 ssh://[user@]host[:port]/[path]
1961 http://[user@]host[:port]/[path]
1961 http://[user@]host[:port]/[path]
1962 https://[user@]host[:port]/[path]
1962 https://[user@]host[:port]/[path]
1963
1963
1964 Look at the help text for the pull command for important details
1964 Look at the help text for the pull command for important details
1965 about ssh:// URLs.
1965 about ssh:// URLs.
1966
1966
1967 Pushing to http:// and https:// URLs is only possible, if this
1967 Pushing to http:// and https:// URLs is only possible, if this
1968 feature is explicitly enabled on the remote Mercurial server.
1968 feature is explicitly enabled on the remote Mercurial server.
1969 """
1969 """
1970 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1970 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1971 setremoteconfig(ui, opts)
1971 setremoteconfig(ui, opts)
1972
1972
1973 other = hg.repository(ui, dest)
1973 other = hg.repository(ui, dest)
1974 ui.status('pushing to %s\n' % (dest))
1974 ui.status('pushing to %s\n' % (dest))
1975 revs = None
1975 revs = None
1976 if opts['rev']:
1976 if opts['rev']:
1977 revs = [repo.lookup(rev) for rev in opts['rev']]
1977 revs = [repo.lookup(rev) for rev in opts['rev']]
1978 r = repo.push(other, opts['force'], revs=revs)
1978 r = repo.push(other, opts['force'], revs=revs)
1979 return r == 0
1979 return r == 0
1980
1980
1981 def rawcommit(ui, repo, *pats, **opts):
1981 def rawcommit(ui, repo, *pats, **opts):
1982 """raw commit interface (DEPRECATED)
1982 """raw commit interface (DEPRECATED)
1983
1983
1984 (DEPRECATED)
1984 (DEPRECATED)
1985 Lowlevel commit, for use in helper scripts.
1985 Lowlevel commit, for use in helper scripts.
1986
1986
1987 This command is not intended to be used by normal users, as it is
1987 This command is not intended to be used by normal users, as it is
1988 primarily useful for importing from other SCMs.
1988 primarily useful for importing from other SCMs.
1989
1989
1990 This command is now deprecated and will be removed in a future
1990 This command is now deprecated and will be removed in a future
1991 release, please use debugsetparents and commit instead.
1991 release, please use debugsetparents and commit instead.
1992 """
1992 """
1993
1993
1994 ui.warn(_("(the rawcommit command is deprecated)\n"))
1994 ui.warn(_("(the rawcommit command is deprecated)\n"))
1995
1995
1996 message = logmessage(opts)
1996 message = logmessage(opts)
1997
1997
1998 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
1998 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
1999 if opts['files']:
1999 if opts['files']:
2000 files += open(opts['files']).read().splitlines()
2000 files += open(opts['files']).read().splitlines()
2001
2001
2002 parents = [repo.lookup(p) for p in opts['parent']]
2002 parents = [repo.lookup(p) for p in opts['parent']]
2003
2003
2004 try:
2004 try:
2005 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2005 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2006 except ValueError, inst:
2006 except ValueError, inst:
2007 raise util.Abort(str(inst))
2007 raise util.Abort(str(inst))
2008
2008
2009 def recover(ui, repo):
2009 def recover(ui, repo):
2010 """roll back an interrupted transaction
2010 """roll back an interrupted transaction
2011
2011
2012 Recover from an interrupted commit or pull.
2012 Recover from an interrupted commit or pull.
2013
2013
2014 This command tries to fix the repository status after an interrupted
2014 This command tries to fix the repository status after an interrupted
2015 operation. It should only be necessary when Mercurial suggests it.
2015 operation. It should only be necessary when Mercurial suggests it.
2016 """
2016 """
2017 if repo.recover():
2017 if repo.recover():
2018 return hg.verify(repo)
2018 return hg.verify(repo)
2019 return 1
2019 return 1
2020
2020
2021 def remove(ui, repo, *pats, **opts):
2021 def remove(ui, repo, *pats, **opts):
2022 """remove the specified files on the next commit
2022 """remove the specified files on the next commit
2023
2023
2024 Schedule the indicated files for removal from the repository.
2024 Schedule the indicated files for removal from the repository.
2025
2025
2026 This only removes files from the current branch, not from the
2026 This only removes files from the current branch, not from the
2027 entire project history. If the files still exist in the working
2027 entire project history. If the files still exist in the working
2028 directory, they will be deleted from it. If invoked with --after,
2028 directory, they will be deleted from it. If invoked with --after,
2029 files that have been manually deleted are marked as removed.
2029 files that have been manually deleted are marked as removed.
2030
2030
2031 This command schedules the files to be removed at the next commit.
2031 This command schedules the files to be removed at the next commit.
2032 To undo a remove before that, see hg revert.
2032 To undo a remove before that, see hg revert.
2033
2033
2034 Modified files and added files are not removed by default. To
2034 Modified files and added files are not removed by default. To
2035 remove them, use the -f/--force option.
2035 remove them, use the -f/--force option.
2036 """
2036 """
2037 names = []
2037 names = []
2038 if not opts['after'] and not pats:
2038 if not opts['after'] and not pats:
2039 raise util.Abort(_('no files specified'))
2039 raise util.Abort(_('no files specified'))
2040 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2040 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2041 exact = dict.fromkeys(files)
2041 exact = dict.fromkeys(files)
2042 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2042 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2043 modified, added, removed, deleted, unknown = mardu
2043 modified, added, removed, deleted, unknown = mardu
2044 remove, forget = [], []
2044 remove, forget = [], []
2045 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2045 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2046 reason = None
2046 reason = None
2047 if abs not in deleted and opts['after']:
2047 if abs not in deleted and opts['after']:
2048 reason = _('is still present')
2048 reason = _('is still present')
2049 elif abs in modified and not opts['force']:
2049 elif abs in modified and not opts['force']:
2050 reason = _('is modified (use -f to force removal)')
2050 reason = _('is modified (use -f to force removal)')
2051 elif abs in added:
2051 elif abs in added:
2052 if opts['force']:
2052 if opts['force']:
2053 forget.append(abs)
2053 forget.append(abs)
2054 continue
2054 continue
2055 reason = _('has been marked for add (use -f to force removal)')
2055 reason = _('has been marked for add (use -f to force removal)')
2056 elif abs in unknown:
2056 elif abs in unknown:
2057 reason = _('is not managed')
2057 reason = _('is not managed')
2058 elif abs in removed:
2058 elif abs in removed:
2059 continue
2059 continue
2060 if reason:
2060 if reason:
2061 if exact:
2061 if exact:
2062 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2062 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2063 else:
2063 else:
2064 if ui.verbose or not exact:
2064 if ui.verbose or not exact:
2065 ui.status(_('removing %s\n') % rel)
2065 ui.status(_('removing %s\n') % rel)
2066 remove.append(abs)
2066 remove.append(abs)
2067 repo.forget(forget)
2067 repo.forget(forget)
2068 repo.remove(remove, unlink=not opts['after'])
2068 repo.remove(remove, unlink=not opts['after'])
2069
2069
2070 def rename(ui, repo, *pats, **opts):
2070 def rename(ui, repo, *pats, **opts):
2071 """rename files; equivalent of copy + remove
2071 """rename files; equivalent of copy + remove
2072
2072
2073 Mark dest as copies of sources; mark sources for deletion. If
2073 Mark dest as copies of sources; mark sources for deletion. If
2074 dest is a directory, copies are put in that directory. If dest is
2074 dest is a directory, copies are put in that directory. If dest is
2075 a file, there can only be one source.
2075 a file, there can only be one source.
2076
2076
2077 By default, this command copies the contents of files as they
2077 By default, this command copies the contents of files as they
2078 stand in the working directory. If invoked with --after, the
2078 stand in the working directory. If invoked with --after, the
2079 operation is recorded, but no copying is performed.
2079 operation is recorded, but no copying is performed.
2080
2080
2081 This command takes effect in the next commit. To undo a rename
2081 This command takes effect in the next commit. To undo a rename
2082 before that, see hg revert.
2082 before that, see hg revert.
2083 """
2083 """
2084 wlock = repo.wlock(0)
2084 wlock = repo.wlock(0)
2085 errs, copied = docopy(ui, repo, pats, opts, wlock)
2085 errs, copied = docopy(ui, repo, pats, opts, wlock)
2086 names = []
2086 names = []
2087 for abs, rel, exact in copied:
2087 for abs, rel, exact in copied:
2088 if ui.verbose or not exact:
2088 if ui.verbose or not exact:
2089 ui.status(_('removing %s\n') % rel)
2089 ui.status(_('removing %s\n') % rel)
2090 names.append(abs)
2090 names.append(abs)
2091 if not opts.get('dry_run'):
2091 if not opts.get('dry_run'):
2092 repo.remove(names, True, wlock)
2092 repo.remove(names, True, wlock)
2093 return errs
2093 return errs
2094
2094
2095 def revert(ui, repo, *pats, **opts):
2095 def revert(ui, repo, *pats, **opts):
2096 """revert files or dirs to their states as of some revision
2096 """revert files or dirs to their states as of some revision
2097
2097
2098 With no revision specified, revert the named files or directories
2098 With no revision specified, revert the named files or directories
2099 to the contents they had in the parent of the working directory.
2099 to the contents they had in the parent of the working directory.
2100 This restores the contents of the affected files to an unmodified
2100 This restores the contents of the affected files to an unmodified
2101 state and unschedules adds, removes, copies, and renames. If the
2101 state and unschedules adds, removes, copies, and renames. If the
2102 working directory has two parents, you must explicitly specify the
2102 working directory has two parents, you must explicitly specify the
2103 revision to revert to.
2103 revision to revert to.
2104
2104
2105 Modified files are saved with a .orig suffix before reverting.
2105 Modified files are saved with a .orig suffix before reverting.
2106 To disable these backups, use --no-backup.
2106 To disable these backups, use --no-backup.
2107
2107
2108 Using the -r option, revert the given files or directories to their
2108 Using the -r option, revert the given files or directories to their
2109 contents as of a specific revision. This can be helpful to "roll
2109 contents as of a specific revision. This can be helpful to "roll
2110 back" some or all of a change that should not have been committed.
2110 back" some or all of a change that should not have been committed.
2111
2111
2112 Revert modifies the working directory. It does not commit any
2112 Revert modifies the working directory. It does not commit any
2113 changes, or change the parent of the working directory. If you
2113 changes, or change the parent of the working directory. If you
2114 revert to a revision other than the parent of the working
2114 revert to a revision other than the parent of the working
2115 directory, the reverted files will thus appear modified
2115 directory, the reverted files will thus appear modified
2116 afterwards.
2116 afterwards.
2117
2117
2118 If a file has been deleted, it is recreated. If the executable
2118 If a file has been deleted, it is recreated. If the executable
2119 mode of a file was changed, it is reset.
2119 mode of a file was changed, it is reset.
2120
2120
2121 If names are given, all files matching the names are reverted.
2121 If names are given, all files matching the names are reverted.
2122
2122
2123 If no arguments are given, no files are reverted.
2123 If no arguments are given, no files are reverted.
2124 """
2124 """
2125
2125
2126 if opts["date"]:
2126 if opts["date"]:
2127 if opts["rev"]:
2127 if opts["rev"]:
2128 raise util.Abort(_("you can't specify a revision and a date"))
2128 raise util.Abort(_("you can't specify a revision and a date"))
2129 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2129 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2130
2130
2131 if not pats and not opts['all']:
2131 if not pats and not opts['all']:
2132 raise util.Abort(_('no files or directories specified; '
2132 raise util.Abort(_('no files or directories specified; '
2133 'use --all to revert the whole repo'))
2133 'use --all to revert the whole repo'))
2134
2134
2135 parent, p2 = repo.dirstate.parents()
2135 parent, p2 = repo.dirstate.parents()
2136 if not opts['rev'] and p2 != nullid:
2136 if not opts['rev'] and p2 != nullid:
2137 raise util.Abort(_('uncommitted merge - please provide a '
2137 raise util.Abort(_('uncommitted merge - please provide a '
2138 'specific revision'))
2138 'specific revision'))
2139 ctx = repo.changectx(opts['rev'])
2139 ctx = repo.changectx(opts['rev'])
2140 node = ctx.node()
2140 node = ctx.node()
2141 mf = ctx.manifest()
2141 mf = ctx.manifest()
2142 if node == parent:
2142 if node == parent:
2143 pmf = mf
2143 pmf = mf
2144 else:
2144 else:
2145 pmf = None
2145 pmf = None
2146
2146
2147 wlock = repo.wlock()
2147 wlock = repo.wlock()
2148
2148
2149 # need all matching names in dirstate and manifest of target rev,
2149 # need all matching names in dirstate and manifest of target rev,
2150 # so have to walk both. do not print errors if files exist in one
2150 # so have to walk both. do not print errors if files exist in one
2151 # but not other.
2151 # but not other.
2152
2152
2153 names = {}
2153 names = {}
2154 target_only = {}
2154 target_only = {}
2155
2155
2156 # walk dirstate.
2156 # walk dirstate.
2157
2157
2158 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2158 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2159 badmatch=mf.has_key):
2159 badmatch=mf.has_key):
2160 names[abs] = (rel, exact)
2160 names[abs] = (rel, exact)
2161 if src == 'b':
2161 if src == 'b':
2162 target_only[abs] = True
2162 target_only[abs] = True
2163
2163
2164 # walk target manifest.
2164 # walk target manifest.
2165
2165
2166 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2166 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2167 badmatch=names.has_key):
2167 badmatch=names.has_key):
2168 if abs in names: continue
2168 if abs in names: continue
2169 names[abs] = (rel, exact)
2169 names[abs] = (rel, exact)
2170 target_only[abs] = True
2170 target_only[abs] = True
2171
2171
2172 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2172 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2173 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2173 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2174
2174
2175 revert = ([], _('reverting %s\n'))
2175 revert = ([], _('reverting %s\n'))
2176 add = ([], _('adding %s\n'))
2176 add = ([], _('adding %s\n'))
2177 remove = ([], _('removing %s\n'))
2177 remove = ([], _('removing %s\n'))
2178 forget = ([], _('forgetting %s\n'))
2178 forget = ([], _('forgetting %s\n'))
2179 undelete = ([], _('undeleting %s\n'))
2179 undelete = ([], _('undeleting %s\n'))
2180 update = {}
2180 update = {}
2181
2181
2182 disptable = (
2182 disptable = (
2183 # dispatch table:
2183 # dispatch table:
2184 # file state
2184 # file state
2185 # action if in target manifest
2185 # action if in target manifest
2186 # action if not in target manifest
2186 # action if not in target manifest
2187 # make backup if in target manifest
2187 # make backup if in target manifest
2188 # make backup if not in target manifest
2188 # make backup if not in target manifest
2189 (modified, revert, remove, True, True),
2189 (modified, revert, remove, True, True),
2190 (added, revert, forget, True, False),
2190 (added, revert, forget, True, False),
2191 (removed, undelete, None, False, False),
2191 (removed, undelete, None, False, False),
2192 (deleted, revert, remove, False, False),
2192 (deleted, revert, remove, False, False),
2193 (unknown, add, None, True, False),
2193 (unknown, add, None, True, False),
2194 (target_only, add, None, False, False),
2194 (target_only, add, None, False, False),
2195 )
2195 )
2196
2196
2197 entries = names.items()
2197 entries = names.items()
2198 entries.sort()
2198 entries.sort()
2199
2199
2200 for abs, (rel, exact) in entries:
2200 for abs, (rel, exact) in entries:
2201 mfentry = mf.get(abs)
2201 mfentry = mf.get(abs)
2202 def handle(xlist, dobackup):
2202 def handle(xlist, dobackup):
2203 xlist[0].append(abs)
2203 xlist[0].append(abs)
2204 update[abs] = 1
2204 update[abs] = 1
2205 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2205 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2206 bakname = "%s.orig" % rel
2206 bakname = "%s.orig" % rel
2207 ui.note(_('saving current version of %s as %s\n') %
2207 ui.note(_('saving current version of %s as %s\n') %
2208 (rel, bakname))
2208 (rel, bakname))
2209 if not opts.get('dry_run'):
2209 if not opts.get('dry_run'):
2210 util.copyfile(rel, bakname)
2210 util.copyfile(rel, bakname)
2211 if ui.verbose or not exact:
2211 if ui.verbose or not exact:
2212 ui.status(xlist[1] % rel)
2212 ui.status(xlist[1] % rel)
2213 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2213 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2214 if abs not in table: continue
2214 if abs not in table: continue
2215 # file has changed in dirstate
2215 # file has changed in dirstate
2216 if mfentry:
2216 if mfentry:
2217 handle(hitlist, backuphit)
2217 handle(hitlist, backuphit)
2218 elif misslist is not None:
2218 elif misslist is not None:
2219 handle(misslist, backupmiss)
2219 handle(misslist, backupmiss)
2220 else:
2220 else:
2221 if exact: ui.warn(_('file not managed: %s\n') % rel)
2221 if exact: ui.warn(_('file not managed: %s\n') % rel)
2222 break
2222 break
2223 else:
2223 else:
2224 # file has not changed in dirstate
2224 # file has not changed in dirstate
2225 if node == parent:
2225 if node == parent:
2226 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2226 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2227 continue
2227 continue
2228 if pmf is None:
2228 if pmf is None:
2229 # only need parent manifest in this unlikely case,
2229 # only need parent manifest in this unlikely case,
2230 # so do not read by default
2230 # so do not read by default
2231 pmf = repo.changectx(parent).manifest()
2231 pmf = repo.changectx(parent).manifest()
2232 if abs in pmf:
2232 if abs in pmf:
2233 if mfentry:
2233 if mfentry:
2234 # if version of file is same in parent and target
2234 # if version of file is same in parent and target
2235 # manifests, do nothing
2235 # manifests, do nothing
2236 if pmf[abs] != mfentry:
2236 if pmf[abs] != mfentry:
2237 handle(revert, False)
2237 handle(revert, False)
2238 else:
2238 else:
2239 handle(remove, False)
2239 handle(remove, False)
2240
2240
2241 if not opts.get('dry_run'):
2241 if not opts.get('dry_run'):
2242 repo.dirstate.forget(forget[0])
2242 repo.dirstate.forget(forget[0])
2243 r = hg.revert(repo, node, update.has_key, wlock)
2243 r = hg.revert(repo, node, update.has_key, wlock)
2244 repo.dirstate.update(add[0], 'a')
2244 repo.dirstate.update(add[0], 'a')
2245 repo.dirstate.update(undelete[0], 'n')
2245 repo.dirstate.update(undelete[0], 'n')
2246 repo.dirstate.update(remove[0], 'r')
2246 repo.dirstate.update(remove[0], 'r')
2247 return r
2247 return r
2248
2248
2249 def rollback(ui, repo):
2249 def rollback(ui, repo):
2250 """roll back the last transaction in this repository
2250 """roll back the last transaction in this repository
2251
2251
2252 Roll back the last transaction in this repository, restoring the
2252 Roll back the last transaction in this repository, restoring the
2253 project to its state prior to the transaction.
2253 project to its state prior to the transaction.
2254
2254
2255 Transactions are used to encapsulate the effects of all commands
2255 Transactions are used to encapsulate the effects of all commands
2256 that create new changesets or propagate existing changesets into a
2256 that create new changesets or propagate existing changesets into a
2257 repository. For example, the following commands are transactional,
2257 repository. For example, the following commands are transactional,
2258 and their effects can be rolled back:
2258 and their effects can be rolled back:
2259
2259
2260 commit
2260 commit
2261 import
2261 import
2262 pull
2262 pull
2263 push (with this repository as destination)
2263 push (with this repository as destination)
2264 unbundle
2264 unbundle
2265
2265
2266 This command should be used with care. There is only one level of
2266 This command should be used with care. There is only one level of
2267 rollback, and there is no way to undo a rollback.
2267 rollback, and there is no way to undo a rollback.
2268
2268
2269 This command is not intended for use on public repositories. Once
2269 This command is not intended for use on public repositories. Once
2270 changes are visible for pull by other users, rolling a transaction
2270 changes are visible for pull by other users, rolling a transaction
2271 back locally is ineffective (someone else may already have pulled
2271 back locally is ineffective (someone else may already have pulled
2272 the changes). Furthermore, a race is possible with readers of the
2272 the changes). Furthermore, a race is possible with readers of the
2273 repository; for example an in-progress pull from the repository
2273 repository; for example an in-progress pull from the repository
2274 may fail if a rollback is performed.
2274 may fail if a rollback is performed.
2275 """
2275 """
2276 repo.rollback()
2276 repo.rollback()
2277
2277
2278 def root(ui, repo):
2278 def root(ui, repo):
2279 """print the root (top) of the current working dir
2279 """print the root (top) of the current working dir
2280
2280
2281 Print the root directory of the current repository.
2281 Print the root directory of the current repository.
2282 """
2282 """
2283 ui.write(repo.root + "\n")
2283 ui.write(repo.root + "\n")
2284
2284
2285 def serve(ui, repo, **opts):
2285 def serve(ui, repo, **opts):
2286 """export the repository via HTTP
2286 """export the repository via HTTP
2287
2287
2288 Start a local HTTP repository browser and pull server.
2288 Start a local HTTP repository browser and pull server.
2289
2289
2290 By default, the server logs accesses to stdout and errors to
2290 By default, the server logs accesses to stdout and errors to
2291 stderr. Use the "-A" and "-E" options to log to files.
2291 stderr. Use the "-A" and "-E" options to log to files.
2292 """
2292 """
2293
2293
2294 if opts["stdio"]:
2294 if opts["stdio"]:
2295 if repo is None:
2295 if repo is None:
2296 raise hg.RepoError(_("There is no Mercurial repository here"
2296 raise hg.RepoError(_("There is no Mercurial repository here"
2297 " (.hg not found)"))
2297 " (.hg not found)"))
2298 s = sshserver.sshserver(ui, repo)
2298 s = sshserver.sshserver(ui, repo)
2299 s.serve_forever()
2299 s.serve_forever()
2300
2300
2301 optlist = ("name templates style address port ipv6"
2301 optlist = ("name templates style address port ipv6"
2302 " accesslog errorlog webdir_conf")
2302 " accesslog errorlog webdir_conf")
2303 for o in optlist.split():
2303 for o in optlist.split():
2304 if opts[o]:
2304 if opts[o]:
2305 ui.setconfig("web", o, str(opts[o]))
2305 ui.setconfig("web", o, str(opts[o]))
2306
2306
2307 if repo is None and not ui.config("web", "webdir_conf"):
2307 if repo is None and not ui.config("web", "webdir_conf"):
2308 raise hg.RepoError(_("There is no Mercurial repository here"
2308 raise hg.RepoError(_("There is no Mercurial repository here"
2309 " (.hg not found)"))
2309 " (.hg not found)"))
2310
2310
2311 if opts['daemon'] and not opts['daemon_pipefds']:
2311 if opts['daemon'] and not opts['daemon_pipefds']:
2312 rfd, wfd = os.pipe()
2312 rfd, wfd = os.pipe()
2313 args = sys.argv[:]
2313 args = sys.argv[:]
2314 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2314 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2315 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2315 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2316 args[0], args)
2316 args[0], args)
2317 os.close(wfd)
2317 os.close(wfd)
2318 os.read(rfd, 1)
2318 os.read(rfd, 1)
2319 os._exit(0)
2319 os._exit(0)
2320
2320
2321 httpd = hgweb.server.create_server(ui, repo)
2321 httpd = hgweb.server.create_server(ui, repo)
2322
2322
2323 if ui.verbose:
2323 if ui.verbose:
2324 if httpd.port != 80:
2324 if httpd.port != 80:
2325 ui.status(_('listening at http://%s:%d/\n') %
2325 ui.status(_('listening at http://%s:%d/\n') %
2326 (httpd.addr, httpd.port))
2326 (httpd.addr, httpd.port))
2327 else:
2327 else:
2328 ui.status(_('listening at http://%s/\n') % httpd.addr)
2328 ui.status(_('listening at http://%s/\n') % httpd.addr)
2329
2329
2330 if opts['pid_file']:
2330 if opts['pid_file']:
2331 fp = open(opts['pid_file'], 'w')
2331 fp = open(opts['pid_file'], 'w')
2332 fp.write(str(os.getpid()) + '\n')
2332 fp.write(str(os.getpid()) + '\n')
2333 fp.close()
2333 fp.close()
2334
2334
2335 if opts['daemon_pipefds']:
2335 if opts['daemon_pipefds']:
2336 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2336 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2337 os.close(rfd)
2337 os.close(rfd)
2338 os.write(wfd, 'y')
2338 os.write(wfd, 'y')
2339 os.close(wfd)
2339 os.close(wfd)
2340 sys.stdout.flush()
2340 sys.stdout.flush()
2341 sys.stderr.flush()
2341 sys.stderr.flush()
2342 fd = os.open(util.nulldev, os.O_RDWR)
2342 fd = os.open(util.nulldev, os.O_RDWR)
2343 if fd != 0: os.dup2(fd, 0)
2343 if fd != 0: os.dup2(fd, 0)
2344 if fd != 1: os.dup2(fd, 1)
2344 if fd != 1: os.dup2(fd, 1)
2345 if fd != 2: os.dup2(fd, 2)
2345 if fd != 2: os.dup2(fd, 2)
2346 if fd not in (0, 1, 2): os.close(fd)
2346 if fd not in (0, 1, 2): os.close(fd)
2347
2347
2348 httpd.serve_forever()
2348 httpd.serve_forever()
2349
2349
2350 def status(ui, repo, *pats, **opts):
2350 def status(ui, repo, *pats, **opts):
2351 """show changed files in the working directory
2351 """show changed files in the working directory
2352
2352
2353 Show status of files in the repository. If names are given, only
2353 Show status of files in the repository. If names are given, only
2354 files that match are shown. Files that are clean or ignored, are
2354 files that match are shown. Files that are clean or ignored, are
2355 not listed unless -c (clean), -i (ignored) or -A is given.
2355 not listed unless -c (clean), -i (ignored) or -A is given.
2356
2356
2357 NOTE: status may appear to disagree with diff if permissions have
2357 NOTE: status may appear to disagree with diff if permissions have
2358 changed or a merge has occurred. The standard diff format does not
2358 changed or a merge has occurred. The standard diff format does not
2359 report permission changes and diff only reports changes relative
2359 report permission changes and diff only reports changes relative
2360 to one merge parent.
2360 to one merge parent.
2361
2361
2362 If one revision is given, it is used as the base revision.
2362 If one revision is given, it is used as the base revision.
2363 If two revisions are given, the difference between them is shown.
2363 If two revisions are given, the difference between them is shown.
2364
2364
2365 The codes used to show the status of files are:
2365 The codes used to show the status of files are:
2366 M = modified
2366 M = modified
2367 A = added
2367 A = added
2368 R = removed
2368 R = removed
2369 C = clean
2369 C = clean
2370 ! = deleted, but still tracked
2370 ! = deleted, but still tracked
2371 ? = not tracked
2371 ? = not tracked
2372 I = ignored (not shown by default)
2372 I = ignored (not shown by default)
2373 = the previous added file was copied from here
2373 = the previous added file was copied from here
2374 """
2374 """
2375
2375
2376 all = opts['all']
2376 all = opts['all']
2377 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2377 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2378
2378
2379 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2379 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2380 cwd = (pats and repo.getcwd()) or ''
2380 cwd = (pats and repo.getcwd()) or ''
2381 modified, added, removed, deleted, unknown, ignored, clean = [
2381 modified, added, removed, deleted, unknown, ignored, clean = [
2382 n for n in repo.status(node1=node1, node2=node2, files=files,
2382 n for n in repo.status(node1=node1, node2=node2, files=files,
2383 match=matchfn,
2383 match=matchfn,
2384 list_ignored=all or opts['ignored'],
2384 list_ignored=all or opts['ignored'],
2385 list_clean=all or opts['clean'])]
2385 list_clean=all or opts['clean'])]
2386
2386
2387 changetypes = (('modified', 'M', modified),
2387 changetypes = (('modified', 'M', modified),
2388 ('added', 'A', added),
2388 ('added', 'A', added),
2389 ('removed', 'R', removed),
2389 ('removed', 'R', removed),
2390 ('deleted', '!', deleted),
2390 ('deleted', '!', deleted),
2391 ('unknown', '?', unknown),
2391 ('unknown', '?', unknown),
2392 ('ignored', 'I', ignored))
2392 ('ignored', 'I', ignored))
2393
2393
2394 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2394 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2395
2395
2396 end = opts['print0'] and '\0' or '\n'
2396 end = opts['print0'] and '\0' or '\n'
2397
2397
2398 for opt, char, changes in ([ct for ct in explicit_changetypes
2398 for opt, char, changes in ([ct for ct in explicit_changetypes
2399 if all or opts[ct[0]]]
2399 if all or opts[ct[0]]]
2400 or changetypes):
2400 or changetypes):
2401 if opts['no_status']:
2401 if opts['no_status']:
2402 format = "%%s%s" % end
2402 format = "%%s%s" % end
2403 else:
2403 else:
2404 format = "%s %%s%s" % (char, end)
2404 format = "%s %%s%s" % (char, end)
2405
2405
2406 for f in changes:
2406 for f in changes:
2407 ui.write(format % util.pathto(cwd, f))
2407 ui.write(format % util.pathto(cwd, f))
2408 if ((all or opts.get('copies')) and not opts.get('no_status')):
2408 if ((all or opts.get('copies')) and not opts.get('no_status')):
2409 copied = repo.dirstate.copied(f)
2409 copied = repo.dirstate.copied(f)
2410 if copied:
2410 if copied:
2411 ui.write(' %s%s' % (util.pathto(cwd, copied), end))
2411 ui.write(' %s%s' % (util.pathto(cwd, copied), end))
2412
2412
2413 def tag(ui, repo, name, rev_=None, **opts):
2413 def tag(ui, repo, name, rev_=None, **opts):
2414 """add a tag for the current or given revision
2414 """add a tag for the current or given revision
2415
2415
2416 Name a particular revision using <name>.
2416 Name a particular revision using <name>.
2417
2417
2418 Tags are used to name particular revisions of the repository and are
2418 Tags are used to name particular revisions of the repository and are
2419 very useful to compare different revision, to go back to significant
2419 very useful to compare different revision, to go back to significant
2420 earlier versions or to mark branch points as releases, etc.
2420 earlier versions or to mark branch points as releases, etc.
2421
2421
2422 If no revision is given, the parent of the working directory is used,
2422 If no revision is given, the parent of the working directory is used,
2423 or tip if no revision is checked out.
2423 or tip if no revision is checked out.
2424
2424
2425 To facilitate version control, distribution, and merging of tags,
2425 To facilitate version control, distribution, and merging of tags,
2426 they are stored as a file named ".hgtags" which is managed
2426 they are stored as a file named ".hgtags" which is managed
2427 similarly to other project files and can be hand-edited if
2427 similarly to other project files and can be hand-edited if
2428 necessary. The file '.hg/localtags' is used for local tags (not
2428 necessary. The file '.hg/localtags' is used for local tags (not
2429 shared among repositories).
2429 shared among repositories).
2430 """
2430 """
2431 if name in ['tip', '.', 'null']:
2431 if name in ['tip', '.', 'null']:
2432 raise util.Abort(_("the name '%s' is reserved") % name)
2432 raise util.Abort(_("the name '%s' is reserved") % name)
2433 if rev_ is not None:
2433 if rev_ is not None:
2434 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2434 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2435 "please use 'hg tag [-r REV] NAME' instead\n"))
2435 "please use 'hg tag [-r REV] NAME' instead\n"))
2436 if opts['rev']:
2436 if opts['rev']:
2437 raise util.Abort(_("use only one form to specify the revision"))
2437 raise util.Abort(_("use only one form to specify the revision"))
2438 if opts['rev']:
2438 if opts['rev']:
2439 rev_ = opts['rev']
2439 rev_ = opts['rev']
2440 if not rev_ and repo.dirstate.parents()[1] != nullid:
2440 if not rev_ and repo.dirstate.parents()[1] != nullid:
2441 raise util.Abort(_('uncommitted merge - please provide a '
2441 raise util.Abort(_('uncommitted merge - please provide a '
2442 'specific revision'))
2442 'specific revision'))
2443 r = repo.changectx(rev_).node()
2443 r = repo.changectx(rev_).node()
2444
2444
2445 message = opts['message']
2445 message = opts['message']
2446 if not message:
2446 if not message:
2447 message = _('Added tag %s for changeset %s') % (name, short(r))
2447 message = _('Added tag %s for changeset %s') % (name, short(r))
2448
2448
2449 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2449 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2450
2450
2451 def tags(ui, repo):
2451 def tags(ui, repo):
2452 """list repository tags
2452 """list repository tags
2453
2453
2454 List the repository tags.
2454 List the repository tags.
2455
2455
2456 This lists both regular and local tags.
2456 This lists both regular and local tags.
2457 """
2457 """
2458
2458
2459 l = repo.tagslist()
2459 l = repo.tagslist()
2460 l.reverse()
2460 l.reverse()
2461 hexfunc = ui.debugflag and hex or short
2461 hexfunc = ui.debugflag and hex or short
2462 for t, n in l:
2462 for t, n in l:
2463 try:
2463 try:
2464 hn = hexfunc(n)
2464 hn = hexfunc(n)
2465 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2465 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2466 except revlog.LookupError:
2466 except revlog.LookupError:
2467 r = " ?:%s" % hn
2467 r = " ?:%s" % hn
2468 if ui.quiet:
2468 if ui.quiet:
2469 ui.write("%s\n" % t)
2469 ui.write("%s\n" % t)
2470 else:
2470 else:
2471 t = util.localsub(t, 30)
2471 t = util.localsub(t, 30)
2472 t += " " * (30 - util.locallen(t))
2472 t += " " * (30 - util.locallen(t))
2473 ui.write("%s %s\n" % (t, r))
2473 ui.write("%s %s\n" % (t, r))
2474
2474
2475 def tip(ui, repo, **opts):
2475 def tip(ui, repo, **opts):
2476 """show the tip revision
2476 """show the tip revision
2477
2477
2478 Show the tip revision.
2478 Show the tip revision.
2479 """
2479 """
2480 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2480 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2481
2481
2482 def unbundle(ui, repo, fname, **opts):
2482 def unbundle(ui, repo, fname, **opts):
2483 """apply a changegroup file
2483 """apply a changegroup file
2484
2484
2485 Apply a compressed changegroup file generated by the bundle
2485 Apply a compressed changegroup file generated by the bundle
2486 command.
2486 command.
2487 """
2487 """
2488 if os.path.exists(fname):
2488 if os.path.exists(fname):
2489 f = open(fname)
2489 f = open(fname, "rb")
2490 else:
2490 else:
2491 f = urllib.urlopen(fname)
2491 f = urllib.urlopen(fname)
2492 gen = changegroup.readbundle(f, fname)
2492 gen = changegroup.readbundle(f, fname)
2493 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2493 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2494 return postincoming(ui, repo, modheads, opts['update'])
2494 return postincoming(ui, repo, modheads, opts['update'])
2495
2495
2496 def update(ui, repo, node=None, clean=False, date=None):
2496 def update(ui, repo, node=None, clean=False, date=None):
2497 """update working directory
2497 """update working directory
2498
2498
2499 Update the working directory to the specified revision.
2499 Update the working directory to the specified revision.
2500
2500
2501 If there are no outstanding changes in the working directory and
2501 If there are no outstanding changes in the working directory and
2502 there is a linear relationship between the current version and the
2502 there is a linear relationship between the current version and the
2503 requested version, the result is the requested version.
2503 requested version, the result is the requested version.
2504
2504
2505 To merge the working directory with another revision, use the
2505 To merge the working directory with another revision, use the
2506 merge command.
2506 merge command.
2507
2507
2508 By default, update will refuse to run if doing so would require
2508 By default, update will refuse to run if doing so would require
2509 discarding local changes.
2509 discarding local changes.
2510 """
2510 """
2511 if date:
2511 if date:
2512 if node:
2512 if node:
2513 raise util.Abort(_("you can't specify a revision and a date"))
2513 raise util.Abort(_("you can't specify a revision and a date"))
2514 node = cmdutil.finddate(ui, repo, date)
2514 node = cmdutil.finddate(ui, repo, date)
2515
2515
2516 if clean:
2516 if clean:
2517 return hg.clean(repo, node)
2517 return hg.clean(repo, node)
2518 else:
2518 else:
2519 return hg.update(repo, node)
2519 return hg.update(repo, node)
2520
2520
2521 def verify(ui, repo):
2521 def verify(ui, repo):
2522 """verify the integrity of the repository
2522 """verify the integrity of the repository
2523
2523
2524 Verify the integrity of the current repository.
2524 Verify the integrity of the current repository.
2525
2525
2526 This will perform an extensive check of the repository's
2526 This will perform an extensive check of the repository's
2527 integrity, validating the hashes and checksums of each entry in
2527 integrity, validating the hashes and checksums of each entry in
2528 the changelog, manifest, and tracked files, as well as the
2528 the changelog, manifest, and tracked files, as well as the
2529 integrity of their crosslinks and indices.
2529 integrity of their crosslinks and indices.
2530 """
2530 """
2531 return hg.verify(repo)
2531 return hg.verify(repo)
2532
2532
2533 def version_(ui):
2533 def version_(ui):
2534 """output version and copyright information"""
2534 """output version and copyright information"""
2535 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2535 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2536 % version.get_version())
2536 % version.get_version())
2537 ui.status(_(
2537 ui.status(_(
2538 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2538 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2539 "This is free software; see the source for copying conditions. "
2539 "This is free software; see the source for copying conditions. "
2540 "There is NO\nwarranty; "
2540 "There is NO\nwarranty; "
2541 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2541 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2542 ))
2542 ))
2543
2543
2544 # Command options and aliases are listed here, alphabetically
2544 # Command options and aliases are listed here, alphabetically
2545
2545
2546 globalopts = [
2546 globalopts = [
2547 ('R', 'repository', '',
2547 ('R', 'repository', '',
2548 _('repository root directory or symbolic path name')),
2548 _('repository root directory or symbolic path name')),
2549 ('', 'cwd', '', _('change working directory')),
2549 ('', 'cwd', '', _('change working directory')),
2550 ('y', 'noninteractive', None,
2550 ('y', 'noninteractive', None,
2551 _('do not prompt, assume \'yes\' for any required answers')),
2551 _('do not prompt, assume \'yes\' for any required answers')),
2552 ('q', 'quiet', None, _('suppress output')),
2552 ('q', 'quiet', None, _('suppress output')),
2553 ('v', 'verbose', None, _('enable additional output')),
2553 ('v', 'verbose', None, _('enable additional output')),
2554 ('', 'config', [], _('set/override config option')),
2554 ('', 'config', [], _('set/override config option')),
2555 ('', 'debug', None, _('enable debugging output')),
2555 ('', 'debug', None, _('enable debugging output')),
2556 ('', 'debugger', None, _('start debugger')),
2556 ('', 'debugger', None, _('start debugger')),
2557 ('', 'encoding', util._encoding, _('set the charset encoding')),
2557 ('', 'encoding', util._encoding, _('set the charset encoding')),
2558 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2558 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2559 ('', 'lsprof', None, _('print improved command execution profile')),
2559 ('', 'lsprof', None, _('print improved command execution profile')),
2560 ('', 'traceback', None, _('print traceback on exception')),
2560 ('', 'traceback', None, _('print traceback on exception')),
2561 ('', 'time', None, _('time how long the command takes')),
2561 ('', 'time', None, _('time how long the command takes')),
2562 ('', 'profile', None, _('print command execution profile')),
2562 ('', 'profile', None, _('print command execution profile')),
2563 ('', 'version', None, _('output version information and exit')),
2563 ('', 'version', None, _('output version information and exit')),
2564 ('h', 'help', None, _('display help and exit')),
2564 ('h', 'help', None, _('display help and exit')),
2565 ]
2565 ]
2566
2566
2567 dryrunopts = [('n', 'dry-run', None,
2567 dryrunopts = [('n', 'dry-run', None,
2568 _('do not perform actions, just print output'))]
2568 _('do not perform actions, just print output'))]
2569
2569
2570 remoteopts = [
2570 remoteopts = [
2571 ('e', 'ssh', '', _('specify ssh command to use')),
2571 ('e', 'ssh', '', _('specify ssh command to use')),
2572 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2572 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2573 ]
2573 ]
2574
2574
2575 walkopts = [
2575 walkopts = [
2576 ('I', 'include', [], _('include names matching the given patterns')),
2576 ('I', 'include', [], _('include names matching the given patterns')),
2577 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2577 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2578 ]
2578 ]
2579
2579
2580 commitopts = [
2580 commitopts = [
2581 ('m', 'message', '', _('use <text> as commit message')),
2581 ('m', 'message', '', _('use <text> as commit message')),
2582 ('l', 'logfile', '', _('read commit message from <file>')),
2582 ('l', 'logfile', '', _('read commit message from <file>')),
2583 ]
2583 ]
2584
2584
2585 table = {
2585 table = {
2586 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2586 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2587 "addremove":
2587 "addremove":
2588 (addremove,
2588 (addremove,
2589 [('s', 'similarity', '',
2589 [('s', 'similarity', '',
2590 _('guess renamed files by similarity (0<=s<=100)')),
2590 _('guess renamed files by similarity (0<=s<=100)')),
2591 ] + walkopts + dryrunopts,
2591 ] + walkopts + dryrunopts,
2592 _('hg addremove [OPTION]... [FILE]...')),
2592 _('hg addremove [OPTION]... [FILE]...')),
2593 "^annotate":
2593 "^annotate":
2594 (annotate,
2594 (annotate,
2595 [('r', 'rev', '', _('annotate the specified revision')),
2595 [('r', 'rev', '', _('annotate the specified revision')),
2596 ('f', 'follow', None, _('follow file copies and renames')),
2596 ('f', 'follow', None, _('follow file copies and renames')),
2597 ('a', 'text', None, _('treat all files as text')),
2597 ('a', 'text', None, _('treat all files as text')),
2598 ('u', 'user', None, _('list the author')),
2598 ('u', 'user', None, _('list the author')),
2599 ('d', 'date', None, _('list the date')),
2599 ('d', 'date', None, _('list the date')),
2600 ('n', 'number', None, _('list the revision number (default)')),
2600 ('n', 'number', None, _('list the revision number (default)')),
2601 ('c', 'changeset', None, _('list the changeset')),
2601 ('c', 'changeset', None, _('list the changeset')),
2602 ] + walkopts,
2602 ] + walkopts,
2603 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2603 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2604 "archive":
2604 "archive":
2605 (archive,
2605 (archive,
2606 [('', 'no-decode', None, _('do not pass files through decoders')),
2606 [('', 'no-decode', None, _('do not pass files through decoders')),
2607 ('p', 'prefix', '', _('directory prefix for files in archive')),
2607 ('p', 'prefix', '', _('directory prefix for files in archive')),
2608 ('r', 'rev', '', _('revision to distribute')),
2608 ('r', 'rev', '', _('revision to distribute')),
2609 ('t', 'type', '', _('type of distribution to create')),
2609 ('t', 'type', '', _('type of distribution to create')),
2610 ] + walkopts,
2610 ] + walkopts,
2611 _('hg archive [OPTION]... DEST')),
2611 _('hg archive [OPTION]... DEST')),
2612 "backout":
2612 "backout":
2613 (backout,
2613 (backout,
2614 [('', 'merge', None,
2614 [('', 'merge', None,
2615 _('merge with old dirstate parent after backout')),
2615 _('merge with old dirstate parent after backout')),
2616 ('d', 'date', '', _('record datecode as commit date')),
2616 ('d', 'date', '', _('record datecode as commit date')),
2617 ('', 'parent', '', _('parent to choose when backing out merge')),
2617 ('', 'parent', '', _('parent to choose when backing out merge')),
2618 ('u', 'user', '', _('record user as committer')),
2618 ('u', 'user', '', _('record user as committer')),
2619 ] + walkopts + commitopts,
2619 ] + walkopts + commitopts,
2620 _('hg backout [OPTION]... REV')),
2620 _('hg backout [OPTION]... REV')),
2621 "branch": (branch, [], _('hg branch [NAME]')),
2621 "branch": (branch, [], _('hg branch [NAME]')),
2622 "branches": (branches, [], _('hg branches')),
2622 "branches": (branches, [], _('hg branches')),
2623 "bundle":
2623 "bundle":
2624 (bundle,
2624 (bundle,
2625 [('f', 'force', None,
2625 [('f', 'force', None,
2626 _('run even when remote repository is unrelated')),
2626 _('run even when remote repository is unrelated')),
2627 ('r', 'rev', [],
2627 ('r', 'rev', [],
2628 _('a changeset you would like to bundle')),
2628 _('a changeset you would like to bundle')),
2629 ('', 'base', [],
2629 ('', 'base', [],
2630 _('a base changeset to specify instead of a destination')),
2630 _('a base changeset to specify instead of a destination')),
2631 ] + remoteopts,
2631 ] + remoteopts,
2632 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2632 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2633 "cat":
2633 "cat":
2634 (cat,
2634 (cat,
2635 [('o', 'output', '', _('print output to file with formatted name')),
2635 [('o', 'output', '', _('print output to file with formatted name')),
2636 ('r', 'rev', '', _('print the given revision')),
2636 ('r', 'rev', '', _('print the given revision')),
2637 ] + walkopts,
2637 ] + walkopts,
2638 _('hg cat [OPTION]... FILE...')),
2638 _('hg cat [OPTION]... FILE...')),
2639 "^clone":
2639 "^clone":
2640 (clone,
2640 (clone,
2641 [('U', 'noupdate', None, _('do not update the new working directory')),
2641 [('U', 'noupdate', None, _('do not update the new working directory')),
2642 ('r', 'rev', [],
2642 ('r', 'rev', [],
2643 _('a changeset you would like to have after cloning')),
2643 _('a changeset you would like to have after cloning')),
2644 ('', 'pull', None, _('use pull protocol to copy metadata')),
2644 ('', 'pull', None, _('use pull protocol to copy metadata')),
2645 ('', 'uncompressed', None,
2645 ('', 'uncompressed', None,
2646 _('use uncompressed transfer (fast over LAN)')),
2646 _('use uncompressed transfer (fast over LAN)')),
2647 ] + remoteopts,
2647 ] + remoteopts,
2648 _('hg clone [OPTION]... SOURCE [DEST]')),
2648 _('hg clone [OPTION]... SOURCE [DEST]')),
2649 "^commit|ci":
2649 "^commit|ci":
2650 (commit,
2650 (commit,
2651 [('A', 'addremove', None,
2651 [('A', 'addremove', None,
2652 _('mark new/missing files as added/removed before committing')),
2652 _('mark new/missing files as added/removed before committing')),
2653 ('d', 'date', '', _('record datecode as commit date')),
2653 ('d', 'date', '', _('record datecode as commit date')),
2654 ('u', 'user', '', _('record user as commiter')),
2654 ('u', 'user', '', _('record user as commiter')),
2655 ] + walkopts + commitopts,
2655 ] + walkopts + commitopts,
2656 _('hg commit [OPTION]... [FILE]...')),
2656 _('hg commit [OPTION]... [FILE]...')),
2657 "copy|cp":
2657 "copy|cp":
2658 (copy,
2658 (copy,
2659 [('A', 'after', None, _('record a copy that has already occurred')),
2659 [('A', 'after', None, _('record a copy that has already occurred')),
2660 ('f', 'force', None,
2660 ('f', 'force', None,
2661 _('forcibly copy over an existing managed file')),
2661 _('forcibly copy over an existing managed file')),
2662 ] + walkopts + dryrunopts,
2662 ] + walkopts + dryrunopts,
2663 _('hg copy [OPTION]... [SOURCE]... DEST')),
2663 _('hg copy [OPTION]... [SOURCE]... DEST')),
2664 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2664 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2665 "debugcomplete":
2665 "debugcomplete":
2666 (debugcomplete,
2666 (debugcomplete,
2667 [('o', 'options', None, _('show the command options'))],
2667 [('o', 'options', None, _('show the command options'))],
2668 _('debugcomplete [-o] CMD')),
2668 _('debugcomplete [-o] CMD')),
2669 "debuginstall": (debuginstall, [], _('debuginstall')),
2669 "debuginstall": (debuginstall, [], _('debuginstall')),
2670 "debugrebuildstate":
2670 "debugrebuildstate":
2671 (debugrebuildstate,
2671 (debugrebuildstate,
2672 [('r', 'rev', '', _('revision to rebuild to'))],
2672 [('r', 'rev', '', _('revision to rebuild to'))],
2673 _('debugrebuildstate [-r REV] [REV]')),
2673 _('debugrebuildstate [-r REV] [REV]')),
2674 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2674 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2675 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2675 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2676 "debugstate": (debugstate, [], _('debugstate')),
2676 "debugstate": (debugstate, [], _('debugstate')),
2677 "debugdate":
2677 "debugdate":
2678 (debugdate,
2678 (debugdate,
2679 [('e', 'extended', None, _('try extended date formats'))],
2679 [('e', 'extended', None, _('try extended date formats'))],
2680 _('debugdate [-e] DATE [RANGE]')),
2680 _('debugdate [-e] DATE [RANGE]')),
2681 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2681 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2682 "debugindex": (debugindex, [], _('debugindex FILE')),
2682 "debugindex": (debugindex, [], _('debugindex FILE')),
2683 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2683 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2684 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2684 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2685 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2685 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2686 "^diff":
2686 "^diff":
2687 (diff,
2687 (diff,
2688 [('r', 'rev', [], _('revision')),
2688 [('r', 'rev', [], _('revision')),
2689 ('a', 'text', None, _('treat all files as text')),
2689 ('a', 'text', None, _('treat all files as text')),
2690 ('p', 'show-function', None,
2690 ('p', 'show-function', None,
2691 _('show which function each change is in')),
2691 _('show which function each change is in')),
2692 ('g', 'git', None, _('use git extended diff format')),
2692 ('g', 'git', None, _('use git extended diff format')),
2693 ('', 'nodates', None, _("don't include dates in diff headers")),
2693 ('', 'nodates', None, _("don't include dates in diff headers")),
2694 ('w', 'ignore-all-space', None,
2694 ('w', 'ignore-all-space', None,
2695 _('ignore white space when comparing lines')),
2695 _('ignore white space when comparing lines')),
2696 ('b', 'ignore-space-change', None,
2696 ('b', 'ignore-space-change', None,
2697 _('ignore changes in the amount of white space')),
2697 _('ignore changes in the amount of white space')),
2698 ('B', 'ignore-blank-lines', None,
2698 ('B', 'ignore-blank-lines', None,
2699 _('ignore changes whose lines are all blank')),
2699 _('ignore changes whose lines are all blank')),
2700 ] + walkopts,
2700 ] + walkopts,
2701 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2701 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2702 "^export":
2702 "^export":
2703 (export,
2703 (export,
2704 [('o', 'output', '', _('print output to file with formatted name')),
2704 [('o', 'output', '', _('print output to file with formatted name')),
2705 ('a', 'text', None, _('treat all files as text')),
2705 ('a', 'text', None, _('treat all files as text')),
2706 ('g', 'git', None, _('use git extended diff format')),
2706 ('g', 'git', None, _('use git extended diff format')),
2707 ('', 'nodates', None, _("don't include dates in diff headers")),
2707 ('', 'nodates', None, _("don't include dates in diff headers")),
2708 ('', 'switch-parent', None, _('diff against the second parent'))],
2708 ('', 'switch-parent', None, _('diff against the second parent'))],
2709 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2709 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2710 "grep":
2710 "grep":
2711 (grep,
2711 (grep,
2712 [('0', 'print0', None, _('end fields with NUL')),
2712 [('0', 'print0', None, _('end fields with NUL')),
2713 ('', 'all', None, _('print all revisions that match')),
2713 ('', 'all', None, _('print all revisions that match')),
2714 ('f', 'follow', None,
2714 ('f', 'follow', None,
2715 _('follow changeset history, or file history across copies and renames')),
2715 _('follow changeset history, or file history across copies and renames')),
2716 ('i', 'ignore-case', None, _('ignore case when matching')),
2716 ('i', 'ignore-case', None, _('ignore case when matching')),
2717 ('l', 'files-with-matches', None,
2717 ('l', 'files-with-matches', None,
2718 _('print only filenames and revs that match')),
2718 _('print only filenames and revs that match')),
2719 ('n', 'line-number', None, _('print matching line numbers')),
2719 ('n', 'line-number', None, _('print matching line numbers')),
2720 ('r', 'rev', [], _('search in given revision range')),
2720 ('r', 'rev', [], _('search in given revision range')),
2721 ('u', 'user', None, _('print user who committed change')),
2721 ('u', 'user', None, _('print user who committed change')),
2722 ] + walkopts,
2722 ] + walkopts,
2723 _('hg grep [OPTION]... PATTERN [FILE]...')),
2723 _('hg grep [OPTION]... PATTERN [FILE]...')),
2724 "heads":
2724 "heads":
2725 (heads,
2725 (heads,
2726 [('', 'style', '', _('display using template map file')),
2726 [('', 'style', '', _('display using template map file')),
2727 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2727 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2728 ('', 'template', '', _('display with template'))],
2728 ('', 'template', '', _('display with template'))],
2729 _('hg heads [-r REV]')),
2729 _('hg heads [-r REV]')),
2730 "help": (help_, [], _('hg help [COMMAND]')),
2730 "help": (help_, [], _('hg help [COMMAND]')),
2731 "identify|id": (identify, [], _('hg identify')),
2731 "identify|id": (identify, [], _('hg identify')),
2732 "import|patch":
2732 "import|patch":
2733 (import_,
2733 (import_,
2734 [('p', 'strip', 1,
2734 [('p', 'strip', 1,
2735 _('directory strip option for patch. This has the same\n'
2735 _('directory strip option for patch. This has the same\n'
2736 'meaning as the corresponding patch option')),
2736 'meaning as the corresponding patch option')),
2737 ('b', 'base', '', _('base path')),
2737 ('b', 'base', '', _('base path')),
2738 ('f', 'force', None,
2738 ('f', 'force', None,
2739 _('skip check for outstanding uncommitted changes'))] + commitopts,
2739 _('skip check for outstanding uncommitted changes'))] + commitopts,
2740 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2740 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2741 "incoming|in": (incoming,
2741 "incoming|in": (incoming,
2742 [('M', 'no-merges', None, _('do not show merges')),
2742 [('M', 'no-merges', None, _('do not show merges')),
2743 ('f', 'force', None,
2743 ('f', 'force', None,
2744 _('run even when remote repository is unrelated')),
2744 _('run even when remote repository is unrelated')),
2745 ('', 'style', '', _('display using template map file')),
2745 ('', 'style', '', _('display using template map file')),
2746 ('n', 'newest-first', None, _('show newest record first')),
2746 ('n', 'newest-first', None, _('show newest record first')),
2747 ('', 'bundle', '', _('file to store the bundles into')),
2747 ('', 'bundle', '', _('file to store the bundles into')),
2748 ('p', 'patch', None, _('show patch')),
2748 ('p', 'patch', None, _('show patch')),
2749 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2749 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2750 ('', 'template', '', _('display with template')),
2750 ('', 'template', '', _('display with template')),
2751 ] + remoteopts,
2751 ] + remoteopts,
2752 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2752 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2753 ' [--bundle FILENAME] [SOURCE]')),
2753 ' [--bundle FILENAME] [SOURCE]')),
2754 "^init":
2754 "^init":
2755 (init,
2755 (init,
2756 remoteopts,
2756 remoteopts,
2757 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2757 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2758 "locate":
2758 "locate":
2759 (locate,
2759 (locate,
2760 [('r', 'rev', '', _('search the repository as it stood at rev')),
2760 [('r', 'rev', '', _('search the repository as it stood at rev')),
2761 ('0', 'print0', None,
2761 ('0', 'print0', None,
2762 _('end filenames with NUL, for use with xargs')),
2762 _('end filenames with NUL, for use with xargs')),
2763 ('f', 'fullpath', None,
2763 ('f', 'fullpath', None,
2764 _('print complete paths from the filesystem root')),
2764 _('print complete paths from the filesystem root')),
2765 ] + walkopts,
2765 ] + walkopts,
2766 _('hg locate [OPTION]... [PATTERN]...')),
2766 _('hg locate [OPTION]... [PATTERN]...')),
2767 "^log|history":
2767 "^log|history":
2768 (log,
2768 (log,
2769 [('f', 'follow', None,
2769 [('f', 'follow', None,
2770 _('follow changeset history, or file history across copies and renames')),
2770 _('follow changeset history, or file history across copies and renames')),
2771 ('', 'follow-first', None,
2771 ('', 'follow-first', None,
2772 _('only follow the first parent of merge changesets')),
2772 _('only follow the first parent of merge changesets')),
2773 ('d', 'date', '', _('show revs matching date spec')),
2773 ('d', 'date', '', _('show revs matching date spec')),
2774 ('C', 'copies', None, _('show copied files')),
2774 ('C', 'copies', None, _('show copied files')),
2775 ('k', 'keyword', [], _('search for a keyword')),
2775 ('k', 'keyword', [], _('search for a keyword')),
2776 ('l', 'limit', '', _('limit number of changes displayed')),
2776 ('l', 'limit', '', _('limit number of changes displayed')),
2777 ('r', 'rev', [], _('show the specified revision or range')),
2777 ('r', 'rev', [], _('show the specified revision or range')),
2778 ('', 'removed', None, _('include revs where files were removed')),
2778 ('', 'removed', None, _('include revs where files were removed')),
2779 ('M', 'no-merges', None, _('do not show merges')),
2779 ('M', 'no-merges', None, _('do not show merges')),
2780 ('', 'style', '', _('display using template map file')),
2780 ('', 'style', '', _('display using template map file')),
2781 ('m', 'only-merges', None, _('show only merges')),
2781 ('m', 'only-merges', None, _('show only merges')),
2782 ('p', 'patch', None, _('show patch')),
2782 ('p', 'patch', None, _('show patch')),
2783 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2783 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2784 ('', 'template', '', _('display with template')),
2784 ('', 'template', '', _('display with template')),
2785 ] + walkopts,
2785 ] + walkopts,
2786 _('hg log [OPTION]... [FILE]')),
2786 _('hg log [OPTION]... [FILE]')),
2787 "manifest": (manifest, [], _('hg manifest [REV]')),
2787 "manifest": (manifest, [], _('hg manifest [REV]')),
2788 "^merge":
2788 "^merge":
2789 (merge,
2789 (merge,
2790 [('f', 'force', None, _('force a merge with outstanding changes'))],
2790 [('f', 'force', None, _('force a merge with outstanding changes'))],
2791 _('hg merge [-f] [REV]')),
2791 _('hg merge [-f] [REV]')),
2792 "outgoing|out": (outgoing,
2792 "outgoing|out": (outgoing,
2793 [('M', 'no-merges', None, _('do not show merges')),
2793 [('M', 'no-merges', None, _('do not show merges')),
2794 ('f', 'force', None,
2794 ('f', 'force', None,
2795 _('run even when remote repository is unrelated')),
2795 _('run even when remote repository is unrelated')),
2796 ('p', 'patch', None, _('show patch')),
2796 ('p', 'patch', None, _('show patch')),
2797 ('', 'style', '', _('display using template map file')),
2797 ('', 'style', '', _('display using template map file')),
2798 ('r', 'rev', [], _('a specific revision you would like to push')),
2798 ('r', 'rev', [], _('a specific revision you would like to push')),
2799 ('n', 'newest-first', None, _('show newest record first')),
2799 ('n', 'newest-first', None, _('show newest record first')),
2800 ('', 'template', '', _('display with template')),
2800 ('', 'template', '', _('display with template')),
2801 ] + remoteopts,
2801 ] + remoteopts,
2802 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2802 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2803 "^parents":
2803 "^parents":
2804 (parents,
2804 (parents,
2805 [('r', 'rev', '', _('show parents from the specified rev')),
2805 [('r', 'rev', '', _('show parents from the specified rev')),
2806 ('', 'style', '', _('display using template map file')),
2806 ('', 'style', '', _('display using template map file')),
2807 ('', 'template', '', _('display with template'))],
2807 ('', 'template', '', _('display with template'))],
2808 _('hg parents [-r REV] [FILE]')),
2808 _('hg parents [-r REV] [FILE]')),
2809 "paths": (paths, [], _('hg paths [NAME]')),
2809 "paths": (paths, [], _('hg paths [NAME]')),
2810 "^pull":
2810 "^pull":
2811 (pull,
2811 (pull,
2812 [('u', 'update', None,
2812 [('u', 'update', None,
2813 _('update to new tip if changesets were pulled')),
2813 _('update to new tip if changesets were pulled')),
2814 ('f', 'force', None,
2814 ('f', 'force', None,
2815 _('run even when remote repository is unrelated')),
2815 _('run even when remote repository is unrelated')),
2816 ('r', 'rev', [],
2816 ('r', 'rev', [],
2817 _('a specific revision up to which you would like to pull')),
2817 _('a specific revision up to which you would like to pull')),
2818 ] + remoteopts,
2818 ] + remoteopts,
2819 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2819 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2820 "^push":
2820 "^push":
2821 (push,
2821 (push,
2822 [('f', 'force', None, _('force push')),
2822 [('f', 'force', None, _('force push')),
2823 ('r', 'rev', [], _('a specific revision you would like to push')),
2823 ('r', 'rev', [], _('a specific revision you would like to push')),
2824 ] + remoteopts,
2824 ] + remoteopts,
2825 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
2825 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
2826 "debugrawcommit|rawcommit":
2826 "debugrawcommit|rawcommit":
2827 (rawcommit,
2827 (rawcommit,
2828 [('p', 'parent', [], _('parent')),
2828 [('p', 'parent', [], _('parent')),
2829 ('d', 'date', '', _('date code')),
2829 ('d', 'date', '', _('date code')),
2830 ('u', 'user', '', _('user')),
2830 ('u', 'user', '', _('user')),
2831 ('F', 'files', '', _('file list'))
2831 ('F', 'files', '', _('file list'))
2832 ] + commitopts,
2832 ] + commitopts,
2833 _('hg debugrawcommit [OPTION]... [FILE]...')),
2833 _('hg debugrawcommit [OPTION]... [FILE]...')),
2834 "recover": (recover, [], _('hg recover')),
2834 "recover": (recover, [], _('hg recover')),
2835 "^remove|rm":
2835 "^remove|rm":
2836 (remove,
2836 (remove,
2837 [('A', 'after', None, _('record remove that has already occurred')),
2837 [('A', 'after', None, _('record remove that has already occurred')),
2838 ('f', 'force', None, _('remove file even if modified')),
2838 ('f', 'force', None, _('remove file even if modified')),
2839 ] + walkopts,
2839 ] + walkopts,
2840 _('hg remove [OPTION]... FILE...')),
2840 _('hg remove [OPTION]... FILE...')),
2841 "rename|mv":
2841 "rename|mv":
2842 (rename,
2842 (rename,
2843 [('A', 'after', None, _('record a rename that has already occurred')),
2843 [('A', 'after', None, _('record a rename that has already occurred')),
2844 ('f', 'force', None,
2844 ('f', 'force', None,
2845 _('forcibly copy over an existing managed file')),
2845 _('forcibly copy over an existing managed file')),
2846 ] + walkopts + dryrunopts,
2846 ] + walkopts + dryrunopts,
2847 _('hg rename [OPTION]... SOURCE... DEST')),
2847 _('hg rename [OPTION]... SOURCE... DEST')),
2848 "^revert":
2848 "^revert":
2849 (revert,
2849 (revert,
2850 [('a', 'all', None, _('revert all changes when no arguments given')),
2850 [('a', 'all', None, _('revert all changes when no arguments given')),
2851 ('d', 'date', '', _('tipmost revision matching date')),
2851 ('d', 'date', '', _('tipmost revision matching date')),
2852 ('r', 'rev', '', _('revision to revert to')),
2852 ('r', 'rev', '', _('revision to revert to')),
2853 ('', 'no-backup', None, _('do not save backup copies of files')),
2853 ('', 'no-backup', None, _('do not save backup copies of files')),
2854 ] + walkopts + dryrunopts,
2854 ] + walkopts + dryrunopts,
2855 _('hg revert [OPTION]... [-r REV] [NAME]...')),
2855 _('hg revert [OPTION]... [-r REV] [NAME]...')),
2856 "rollback": (rollback, [], _('hg rollback')),
2856 "rollback": (rollback, [], _('hg rollback')),
2857 "root": (root, [], _('hg root')),
2857 "root": (root, [], _('hg root')),
2858 "showconfig|debugconfig":
2858 "showconfig|debugconfig":
2859 (showconfig,
2859 (showconfig,
2860 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2860 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2861 _('showconfig [-u] [NAME]...')),
2861 _('showconfig [-u] [NAME]...')),
2862 "^serve":
2862 "^serve":
2863 (serve,
2863 (serve,
2864 [('A', 'accesslog', '', _('name of access log file to write to')),
2864 [('A', 'accesslog', '', _('name of access log file to write to')),
2865 ('d', 'daemon', None, _('run server in background')),
2865 ('d', 'daemon', None, _('run server in background')),
2866 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2866 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2867 ('E', 'errorlog', '', _('name of error log file to write to')),
2867 ('E', 'errorlog', '', _('name of error log file to write to')),
2868 ('p', 'port', 0, _('port to use (default: 8000)')),
2868 ('p', 'port', 0, _('port to use (default: 8000)')),
2869 ('a', 'address', '', _('address to use')),
2869 ('a', 'address', '', _('address to use')),
2870 ('n', 'name', '',
2870 ('n', 'name', '',
2871 _('name to show in web pages (default: working dir)')),
2871 _('name to show in web pages (default: working dir)')),
2872 ('', 'webdir-conf', '', _('name of the webdir config file'
2872 ('', 'webdir-conf', '', _('name of the webdir config file'
2873 ' (serve more than one repo)')),
2873 ' (serve more than one repo)')),
2874 ('', 'pid-file', '', _('name of file to write process ID to')),
2874 ('', 'pid-file', '', _('name of file to write process ID to')),
2875 ('', 'stdio', None, _('for remote clients')),
2875 ('', 'stdio', None, _('for remote clients')),
2876 ('t', 'templates', '', _('web templates to use')),
2876 ('t', 'templates', '', _('web templates to use')),
2877 ('', 'style', '', _('template style to use')),
2877 ('', 'style', '', _('template style to use')),
2878 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2878 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2879 _('hg serve [OPTION]...')),
2879 _('hg serve [OPTION]...')),
2880 "^status|st":
2880 "^status|st":
2881 (status,
2881 (status,
2882 [('A', 'all', None, _('show status of all files')),
2882 [('A', 'all', None, _('show status of all files')),
2883 ('m', 'modified', None, _('show only modified files')),
2883 ('m', 'modified', None, _('show only modified files')),
2884 ('a', 'added', None, _('show only added files')),
2884 ('a', 'added', None, _('show only added files')),
2885 ('r', 'removed', None, _('show only removed files')),
2885 ('r', 'removed', None, _('show only removed files')),
2886 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2886 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2887 ('c', 'clean', None, _('show only files without changes')),
2887 ('c', 'clean', None, _('show only files without changes')),
2888 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2888 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2889 ('i', 'ignored', None, _('show ignored files')),
2889 ('i', 'ignored', None, _('show ignored files')),
2890 ('n', 'no-status', None, _('hide status prefix')),
2890 ('n', 'no-status', None, _('hide status prefix')),
2891 ('C', 'copies', None, _('show source of copied files')),
2891 ('C', 'copies', None, _('show source of copied files')),
2892 ('0', 'print0', None,
2892 ('0', 'print0', None,
2893 _('end filenames with NUL, for use with xargs')),
2893 _('end filenames with NUL, for use with xargs')),
2894 ('', 'rev', [], _('show difference from revision')),
2894 ('', 'rev', [], _('show difference from revision')),
2895 ] + walkopts,
2895 ] + walkopts,
2896 _('hg status [OPTION]... [FILE]...')),
2896 _('hg status [OPTION]... [FILE]...')),
2897 "tag":
2897 "tag":
2898 (tag,
2898 (tag,
2899 [('l', 'local', None, _('make the tag local')),
2899 [('l', 'local', None, _('make the tag local')),
2900 ('m', 'message', '', _('message for tag commit log entry')),
2900 ('m', 'message', '', _('message for tag commit log entry')),
2901 ('d', 'date', '', _('record datecode as commit date')),
2901 ('d', 'date', '', _('record datecode as commit date')),
2902 ('u', 'user', '', _('record user as commiter')),
2902 ('u', 'user', '', _('record user as commiter')),
2903 ('r', 'rev', '', _('revision to tag'))],
2903 ('r', 'rev', '', _('revision to tag'))],
2904 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2904 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2905 "tags": (tags, [], _('hg tags')),
2905 "tags": (tags, [], _('hg tags')),
2906 "tip":
2906 "tip":
2907 (tip,
2907 (tip,
2908 [('', 'style', '', _('display using template map file')),
2908 [('', 'style', '', _('display using template map file')),
2909 ('p', 'patch', None, _('show patch')),
2909 ('p', 'patch', None, _('show patch')),
2910 ('', 'template', '', _('display with template'))],
2910 ('', 'template', '', _('display with template'))],
2911 _('hg tip [-p]')),
2911 _('hg tip [-p]')),
2912 "unbundle":
2912 "unbundle":
2913 (unbundle,
2913 (unbundle,
2914 [('u', 'update', None,
2914 [('u', 'update', None,
2915 _('update to new tip if changesets were unbundled'))],
2915 _('update to new tip if changesets were unbundled'))],
2916 _('hg unbundle [-u] FILE')),
2916 _('hg unbundle [-u] FILE')),
2917 "^update|up|checkout|co":
2917 "^update|up|checkout|co":
2918 (update,
2918 (update,
2919 [('C', 'clean', None, _('overwrite locally modified files')),
2919 [('C', 'clean', None, _('overwrite locally modified files')),
2920 ('d', 'date', '', _('tipmost revision matching date'))],
2920 ('d', 'date', '', _('tipmost revision matching date'))],
2921 _('hg update [-C] [-d DATE] [REV]')),
2921 _('hg update [-C] [-d DATE] [REV]')),
2922 "verify": (verify, [], _('hg verify')),
2922 "verify": (verify, [], _('hg verify')),
2923 "version": (version_, [], _('hg version')),
2923 "version": (version_, [], _('hg version')),
2924 }
2924 }
2925
2925
2926 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2926 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2927 " debugindex debugindexdot debugdate debuginstall")
2927 " debugindex debugindexdot debugdate debuginstall")
2928 optionalrepo = ("paths serve showconfig")
2928 optionalrepo = ("paths serve showconfig")
2929
2929
2930 def findpossible(ui, cmd):
2930 def findpossible(ui, cmd):
2931 """
2931 """
2932 Return cmd -> (aliases, command table entry)
2932 Return cmd -> (aliases, command table entry)
2933 for each matching command.
2933 for each matching command.
2934 Return debug commands (or their aliases) only if no normal command matches.
2934 Return debug commands (or their aliases) only if no normal command matches.
2935 """
2935 """
2936 choice = {}
2936 choice = {}
2937 debugchoice = {}
2937 debugchoice = {}
2938 for e in table.keys():
2938 for e in table.keys():
2939 aliases = e.lstrip("^").split("|")
2939 aliases = e.lstrip("^").split("|")
2940 found = None
2940 found = None
2941 if cmd in aliases:
2941 if cmd in aliases:
2942 found = cmd
2942 found = cmd
2943 elif not ui.config("ui", "strict"):
2943 elif not ui.config("ui", "strict"):
2944 for a in aliases:
2944 for a in aliases:
2945 if a.startswith(cmd):
2945 if a.startswith(cmd):
2946 found = a
2946 found = a
2947 break
2947 break
2948 if found is not None:
2948 if found is not None:
2949 if aliases[0].startswith("debug") or found.startswith("debug"):
2949 if aliases[0].startswith("debug") or found.startswith("debug"):
2950 debugchoice[found] = (aliases, table[e])
2950 debugchoice[found] = (aliases, table[e])
2951 else:
2951 else:
2952 choice[found] = (aliases, table[e])
2952 choice[found] = (aliases, table[e])
2953
2953
2954 if not choice and debugchoice:
2954 if not choice and debugchoice:
2955 choice = debugchoice
2955 choice = debugchoice
2956
2956
2957 return choice
2957 return choice
2958
2958
2959 def findcmd(ui, cmd):
2959 def findcmd(ui, cmd):
2960 """Return (aliases, command table entry) for command string."""
2960 """Return (aliases, command table entry) for command string."""
2961 choice = findpossible(ui, cmd)
2961 choice = findpossible(ui, cmd)
2962
2962
2963 if choice.has_key(cmd):
2963 if choice.has_key(cmd):
2964 return choice[cmd]
2964 return choice[cmd]
2965
2965
2966 if len(choice) > 1:
2966 if len(choice) > 1:
2967 clist = choice.keys()
2967 clist = choice.keys()
2968 clist.sort()
2968 clist.sort()
2969 raise AmbiguousCommand(cmd, clist)
2969 raise AmbiguousCommand(cmd, clist)
2970
2970
2971 if choice:
2971 if choice:
2972 return choice.values()[0]
2972 return choice.values()[0]
2973
2973
2974 raise UnknownCommand(cmd)
2974 raise UnknownCommand(cmd)
2975
2975
2976 def catchterm(*args):
2976 def catchterm(*args):
2977 raise util.SignalInterrupt
2977 raise util.SignalInterrupt
2978
2978
2979 def run():
2979 def run():
2980 sys.exit(dispatch(sys.argv[1:]))
2980 sys.exit(dispatch(sys.argv[1:]))
2981
2981
2982 class ParseError(Exception):
2982 class ParseError(Exception):
2983 """Exception raised on errors in parsing the command line."""
2983 """Exception raised on errors in parsing the command line."""
2984
2984
2985 def parse(ui, args):
2985 def parse(ui, args):
2986 options = {}
2986 options = {}
2987 cmdoptions = {}
2987 cmdoptions = {}
2988
2988
2989 try:
2989 try:
2990 args = fancyopts.fancyopts(args, globalopts, options)
2990 args = fancyopts.fancyopts(args, globalopts, options)
2991 except fancyopts.getopt.GetoptError, inst:
2991 except fancyopts.getopt.GetoptError, inst:
2992 raise ParseError(None, inst)
2992 raise ParseError(None, inst)
2993
2993
2994 if args:
2994 if args:
2995 cmd, args = args[0], args[1:]
2995 cmd, args = args[0], args[1:]
2996 aliases, i = findcmd(ui, cmd)
2996 aliases, i = findcmd(ui, cmd)
2997 cmd = aliases[0]
2997 cmd = aliases[0]
2998 defaults = ui.config("defaults", cmd)
2998 defaults = ui.config("defaults", cmd)
2999 if defaults:
2999 if defaults:
3000 args = shlex.split(defaults) + args
3000 args = shlex.split(defaults) + args
3001 c = list(i[1])
3001 c = list(i[1])
3002 else:
3002 else:
3003 cmd = None
3003 cmd = None
3004 c = []
3004 c = []
3005
3005
3006 # combine global options into local
3006 # combine global options into local
3007 for o in globalopts:
3007 for o in globalopts:
3008 c.append((o[0], o[1], options[o[1]], o[3]))
3008 c.append((o[0], o[1], options[o[1]], o[3]))
3009
3009
3010 try:
3010 try:
3011 args = fancyopts.fancyopts(args, c, cmdoptions)
3011 args = fancyopts.fancyopts(args, c, cmdoptions)
3012 except fancyopts.getopt.GetoptError, inst:
3012 except fancyopts.getopt.GetoptError, inst:
3013 raise ParseError(cmd, inst)
3013 raise ParseError(cmd, inst)
3014
3014
3015 # separate global options back out
3015 # separate global options back out
3016 for o in globalopts:
3016 for o in globalopts:
3017 n = o[1]
3017 n = o[1]
3018 options[n] = cmdoptions[n]
3018 options[n] = cmdoptions[n]
3019 del cmdoptions[n]
3019 del cmdoptions[n]
3020
3020
3021 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3021 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3022
3022
3023 external = {}
3023 external = {}
3024
3024
3025 def findext(name):
3025 def findext(name):
3026 '''return module with given extension name'''
3026 '''return module with given extension name'''
3027 try:
3027 try:
3028 return sys.modules[external[name]]
3028 return sys.modules[external[name]]
3029 except KeyError:
3029 except KeyError:
3030 for k, v in external.iteritems():
3030 for k, v in external.iteritems():
3031 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3031 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3032 return sys.modules[v]
3032 return sys.modules[v]
3033 raise KeyError(name)
3033 raise KeyError(name)
3034
3034
3035 def load_extensions(ui):
3035 def load_extensions(ui):
3036 added = []
3036 added = []
3037 for ext_name, load_from_name in ui.extensions():
3037 for ext_name, load_from_name in ui.extensions():
3038 if ext_name in external:
3038 if ext_name in external:
3039 continue
3039 continue
3040 try:
3040 try:
3041 if load_from_name:
3041 if load_from_name:
3042 # the module will be loaded in sys.modules
3042 # the module will be loaded in sys.modules
3043 # choose an unique name so that it doesn't
3043 # choose an unique name so that it doesn't
3044 # conflicts with other modules
3044 # conflicts with other modules
3045 module_name = "hgext_%s" % ext_name.replace('.', '_')
3045 module_name = "hgext_%s" % ext_name.replace('.', '_')
3046 mod = imp.load_source(module_name, load_from_name)
3046 mod = imp.load_source(module_name, load_from_name)
3047 else:
3047 else:
3048 def importh(name):
3048 def importh(name):
3049 mod = __import__(name)
3049 mod = __import__(name)
3050 components = name.split('.')
3050 components = name.split('.')
3051 for comp in components[1:]:
3051 for comp in components[1:]:
3052 mod = getattr(mod, comp)
3052 mod = getattr(mod, comp)
3053 return mod
3053 return mod
3054 try:
3054 try:
3055 mod = importh("hgext.%s" % ext_name)
3055 mod = importh("hgext.%s" % ext_name)
3056 except ImportError:
3056 except ImportError:
3057 mod = importh(ext_name)
3057 mod = importh(ext_name)
3058 external[ext_name] = mod.__name__
3058 external[ext_name] = mod.__name__
3059 added.append((mod, ext_name))
3059 added.append((mod, ext_name))
3060 except (util.SignalInterrupt, KeyboardInterrupt):
3060 except (util.SignalInterrupt, KeyboardInterrupt):
3061 raise
3061 raise
3062 except Exception, inst:
3062 except Exception, inst:
3063 ui.warn(_("*** failed to import extension %s: %s\n") %
3063 ui.warn(_("*** failed to import extension %s: %s\n") %
3064 (ext_name, inst))
3064 (ext_name, inst))
3065 if ui.print_exc():
3065 if ui.print_exc():
3066 return 1
3066 return 1
3067
3067
3068 for mod, name in added:
3068 for mod, name in added:
3069 uisetup = getattr(mod, 'uisetup', None)
3069 uisetup = getattr(mod, 'uisetup', None)
3070 if uisetup:
3070 if uisetup:
3071 uisetup(ui)
3071 uisetup(ui)
3072 reposetup = getattr(mod, 'reposetup', None)
3073 if reposetup:
3074 hg.repo_setup_hooks.append(reposetup)
3072 cmdtable = getattr(mod, 'cmdtable', {})
3075 cmdtable = getattr(mod, 'cmdtable', {})
3073 overrides = [cmd for cmd in cmdtable if cmd in table]
3076 overrides = [cmd for cmd in cmdtable if cmd in table]
3074 if overrides:
3077 if overrides:
3075 ui.warn(_("extension '%s' overrides commands: %s\n")
3078 ui.warn(_("extension '%s' overrides commands: %s\n")
3076 % (name, " ".join(overrides)))
3079 % (name, " ".join(overrides)))
3077 table.update(cmdtable)
3080 table.update(cmdtable)
3078
3081
3079 def parseconfig(config):
3082 def parseconfig(config):
3080 """parse the --config options from the command line"""
3083 """parse the --config options from the command line"""
3081 parsed = []
3084 parsed = []
3082 for cfg in config:
3085 for cfg in config:
3083 try:
3086 try:
3084 name, value = cfg.split('=', 1)
3087 name, value = cfg.split('=', 1)
3085 section, name = name.split('.', 1)
3088 section, name = name.split('.', 1)
3086 if not section or not name:
3089 if not section or not name:
3087 raise IndexError
3090 raise IndexError
3088 parsed.append((section, name, value))
3091 parsed.append((section, name, value))
3089 except (IndexError, ValueError):
3092 except (IndexError, ValueError):
3090 raise util.Abort(_('malformed --config option: %s') % cfg)
3093 raise util.Abort(_('malformed --config option: %s') % cfg)
3091 return parsed
3094 return parsed
3092
3095
3093 def dispatch(args):
3096 def dispatch(args):
3094 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3097 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3095 num = getattr(signal, name, None)
3098 num = getattr(signal, name, None)
3096 if num: signal.signal(num, catchterm)
3099 if num: signal.signal(num, catchterm)
3097
3100
3098 try:
3101 try:
3099 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3102 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3100 except util.Abort, inst:
3103 except util.Abort, inst:
3101 sys.stderr.write(_("abort: %s\n") % inst)
3104 sys.stderr.write(_("abort: %s\n") % inst)
3102 return -1
3105 return -1
3103
3106
3104 load_extensions(u)
3107 load_extensions(u)
3105 u.addreadhook(load_extensions)
3108 u.addreadhook(load_extensions)
3106
3109
3107 try:
3110 try:
3108 cmd, func, args, options, cmdoptions = parse(u, args)
3111 cmd, func, args, options, cmdoptions = parse(u, args)
3109 if options["encoding"]:
3112 if options["encoding"]:
3110 util._encoding = options["encoding"]
3113 util._encoding = options["encoding"]
3111 if options["encodingmode"]:
3114 if options["encodingmode"]:
3112 util._encodingmode = options["encodingmode"]
3115 util._encodingmode = options["encodingmode"]
3113 if options["time"]:
3116 if options["time"]:
3114 def get_times():
3117 def get_times():
3115 t = os.times()
3118 t = os.times()
3116 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3119 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3117 t = (t[0], t[1], t[2], t[3], time.clock())
3120 t = (t[0], t[1], t[2], t[3], time.clock())
3118 return t
3121 return t
3119 s = get_times()
3122 s = get_times()
3120 def print_time():
3123 def print_time():
3121 t = get_times()
3124 t = get_times()
3122 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3125 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3123 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3126 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3124 atexit.register(print_time)
3127 atexit.register(print_time)
3125
3128
3126 # enter the debugger before command execution
3129 # enter the debugger before command execution
3127 if options['debugger']:
3130 if options['debugger']:
3128 pdb.set_trace()
3131 pdb.set_trace()
3129
3132
3130 try:
3133 try:
3131 if options['cwd']:
3134 if options['cwd']:
3132 os.chdir(options['cwd'])
3135 os.chdir(options['cwd'])
3133
3136
3134 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3137 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3135 not options["noninteractive"], options["traceback"],
3138 not options["noninteractive"], options["traceback"],
3136 parseconfig(options["config"]))
3139 parseconfig(options["config"]))
3137
3140
3138 path = u.expandpath(options["repository"]) or ""
3141 path = u.expandpath(options["repository"]) or ""
3139 repo = path and hg.repository(u, path=path) or None
3142 repo = path and hg.repository(u, path=path) or None
3140 if repo and not repo.local():
3143 if repo and not repo.local():
3141 raise util.Abort(_("repository '%s' is not local") % path)
3144 raise util.Abort(_("repository '%s' is not local") % path)
3142
3145
3143 if options['help']:
3146 if options['help']:
3144 return help_(u, cmd, options['version'])
3147 return help_(u, cmd, options['version'])
3145 elif options['version']:
3148 elif options['version']:
3146 return version_(u)
3149 return version_(u)
3147 elif not cmd:
3150 elif not cmd:
3148 return help_(u, 'shortlist')
3151 return help_(u, 'shortlist')
3149
3152
3150 if cmd not in norepo.split():
3153 if cmd not in norepo.split():
3151 try:
3154 try:
3152 if not repo:
3155 if not repo:
3153 repo = hg.repository(u, path=path)
3156 repo = hg.repository(u, path=path)
3154 u = repo.ui
3157 u = repo.ui
3155 for name in external.itervalues():
3156 mod = sys.modules[name]
3157 if hasattr(mod, 'reposetup'):
3158 mod.reposetup(u, repo)
3159 hg.repo_setup_hooks.append(mod.reposetup)
3160 except hg.RepoError:
3158 except hg.RepoError:
3161 if cmd not in optionalrepo.split():
3159 if cmd not in optionalrepo.split():
3162 raise
3160 raise
3163 d = lambda: func(u, repo, *args, **cmdoptions)
3161 d = lambda: func(u, repo, *args, **cmdoptions)
3164 else:
3162 else:
3165 d = lambda: func(u, *args, **cmdoptions)
3163 d = lambda: func(u, *args, **cmdoptions)
3166
3164
3167 try:
3165 try:
3168 if options['profile']:
3166 if options['profile']:
3169 import hotshot, hotshot.stats
3167 import hotshot, hotshot.stats
3170 prof = hotshot.Profile("hg.prof")
3168 prof = hotshot.Profile("hg.prof")
3171 try:
3169 try:
3172 try:
3170 try:
3173 return prof.runcall(d)
3171 return prof.runcall(d)
3174 except:
3172 except:
3175 try:
3173 try:
3176 u.warn(_('exception raised - generating '
3174 u.warn(_('exception raised - generating '
3177 'profile anyway\n'))
3175 'profile anyway\n'))
3178 except:
3176 except:
3179 pass
3177 pass
3180 raise
3178 raise
3181 finally:
3179 finally:
3182 prof.close()
3180 prof.close()
3183 stats = hotshot.stats.load("hg.prof")
3181 stats = hotshot.stats.load("hg.prof")
3184 stats.strip_dirs()
3182 stats.strip_dirs()
3185 stats.sort_stats('time', 'calls')
3183 stats.sort_stats('time', 'calls')
3186 stats.print_stats(40)
3184 stats.print_stats(40)
3187 elif options['lsprof']:
3185 elif options['lsprof']:
3188 try:
3186 try:
3189 from mercurial import lsprof
3187 from mercurial import lsprof
3190 except ImportError:
3188 except ImportError:
3191 raise util.Abort(_(
3189 raise util.Abort(_(
3192 'lsprof not available - install from '
3190 'lsprof not available - install from '
3193 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3191 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3194 p = lsprof.Profiler()
3192 p = lsprof.Profiler()
3195 p.enable(subcalls=True)
3193 p.enable(subcalls=True)
3196 try:
3194 try:
3197 return d()
3195 return d()
3198 finally:
3196 finally:
3199 p.disable()
3197 p.disable()
3200 stats = lsprof.Stats(p.getstats())
3198 stats = lsprof.Stats(p.getstats())
3201 stats.sort()
3199 stats.sort()
3202 stats.pprint(top=10, file=sys.stderr, climit=5)
3200 stats.pprint(top=10, file=sys.stderr, climit=5)
3203 else:
3201 else:
3204 return d()
3202 return d()
3205 finally:
3203 finally:
3206 u.flush()
3204 u.flush()
3207 except:
3205 except:
3208 # enter the debugger when we hit an exception
3206 # enter the debugger when we hit an exception
3209 if options['debugger']:
3207 if options['debugger']:
3210 pdb.post_mortem(sys.exc_info()[2])
3208 pdb.post_mortem(sys.exc_info()[2])
3211 u.print_exc()
3209 u.print_exc()
3212 raise
3210 raise
3213 except ParseError, inst:
3211 except ParseError, inst:
3214 if inst.args[0]:
3212 if inst.args[0]:
3215 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3213 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3216 help_(u, inst.args[0])
3214 help_(u, inst.args[0])
3217 else:
3215 else:
3218 u.warn(_("hg: %s\n") % inst.args[1])
3216 u.warn(_("hg: %s\n") % inst.args[1])
3219 help_(u, 'shortlist')
3217 help_(u, 'shortlist')
3220 except AmbiguousCommand, inst:
3218 except AmbiguousCommand, inst:
3221 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3219 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3222 (inst.args[0], " ".join(inst.args[1])))
3220 (inst.args[0], " ".join(inst.args[1])))
3223 except UnknownCommand, inst:
3221 except UnknownCommand, inst:
3224 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3222 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3225 help_(u, 'shortlist')
3223 help_(u, 'shortlist')
3226 except hg.RepoError, inst:
3224 except hg.RepoError, inst:
3227 u.warn(_("abort: %s!\n") % inst)
3225 u.warn(_("abort: %s!\n") % inst)
3228 except lock.LockHeld, inst:
3226 except lock.LockHeld, inst:
3229 if inst.errno == errno.ETIMEDOUT:
3227 if inst.errno == errno.ETIMEDOUT:
3230 reason = _('timed out waiting for lock held by %s') % inst.locker
3228 reason = _('timed out waiting for lock held by %s') % inst.locker
3231 else:
3229 else:
3232 reason = _('lock held by %s') % inst.locker
3230 reason = _('lock held by %s') % inst.locker
3233 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3231 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3234 except lock.LockUnavailable, inst:
3232 except lock.LockUnavailable, inst:
3235 u.warn(_("abort: could not lock %s: %s\n") %
3233 u.warn(_("abort: could not lock %s: %s\n") %
3236 (inst.desc or inst.filename, inst.strerror))
3234 (inst.desc or inst.filename, inst.strerror))
3237 except revlog.RevlogError, inst:
3235 except revlog.RevlogError, inst:
3238 u.warn(_("abort: %s!\n") % inst)
3236 u.warn(_("abort: %s!\n") % inst)
3239 except util.SignalInterrupt:
3237 except util.SignalInterrupt:
3240 u.warn(_("killed!\n"))
3238 u.warn(_("killed!\n"))
3241 except KeyboardInterrupt:
3239 except KeyboardInterrupt:
3242 try:
3240 try:
3243 u.warn(_("interrupted!\n"))
3241 u.warn(_("interrupted!\n"))
3244 except IOError, inst:
3242 except IOError, inst:
3245 if inst.errno == errno.EPIPE:
3243 if inst.errno == errno.EPIPE:
3246 if u.debugflag:
3244 if u.debugflag:
3247 u.warn(_("\nbroken pipe\n"))
3245 u.warn(_("\nbroken pipe\n"))
3248 else:
3246 else:
3249 raise
3247 raise
3250 except socket.error, inst:
3248 except socket.error, inst:
3251 u.warn(_("abort: %s\n") % inst[1])
3249 u.warn(_("abort: %s\n") % inst[1])
3252 except IOError, inst:
3250 except IOError, inst:
3253 if hasattr(inst, "code"):
3251 if hasattr(inst, "code"):
3254 u.warn(_("abort: %s\n") % inst)
3252 u.warn(_("abort: %s\n") % inst)
3255 elif hasattr(inst, "reason"):
3253 elif hasattr(inst, "reason"):
3256 try: # usually it is in the form (errno, strerror)
3254 try: # usually it is in the form (errno, strerror)
3257 reason = inst.reason.args[1]
3255 reason = inst.reason.args[1]
3258 except: # it might be anything, for example a string
3256 except: # it might be anything, for example a string
3259 reason = inst.reason
3257 reason = inst.reason
3260 u.warn(_("abort: error: %s\n") % reason)
3258 u.warn(_("abort: error: %s\n") % reason)
3261 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3259 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3262 if u.debugflag:
3260 if u.debugflag:
3263 u.warn(_("broken pipe\n"))
3261 u.warn(_("broken pipe\n"))
3264 elif getattr(inst, "strerror", None):
3262 elif getattr(inst, "strerror", None):
3265 if getattr(inst, "filename", None):
3263 if getattr(inst, "filename", None):
3266 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3264 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3267 else:
3265 else:
3268 u.warn(_("abort: %s\n") % inst.strerror)
3266 u.warn(_("abort: %s\n") % inst.strerror)
3269 else:
3267 else:
3270 raise
3268 raise
3271 except OSError, inst:
3269 except OSError, inst:
3272 if getattr(inst, "filename", None):
3270 if getattr(inst, "filename", None):
3273 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3271 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3274 else:
3272 else:
3275 u.warn(_("abort: %s\n") % inst.strerror)
3273 u.warn(_("abort: %s\n") % inst.strerror)
3276 except util.UnexpectedOutput, inst:
3274 except util.UnexpectedOutput, inst:
3277 u.warn(_("abort: %s") % inst[0])
3275 u.warn(_("abort: %s") % inst[0])
3278 if not isinstance(inst[1], basestring):
3276 if not isinstance(inst[1], basestring):
3279 u.warn(" %r\n" % (inst[1],))
3277 u.warn(" %r\n" % (inst[1],))
3280 elif not inst[1]:
3278 elif not inst[1]:
3281 u.warn(_(" empty string\n"))
3279 u.warn(_(" empty string\n"))
3282 else:
3280 else:
3283 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3281 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3284 except util.Abort, inst:
3282 except util.Abort, inst:
3285 u.warn(_("abort: %s\n") % inst)
3283 u.warn(_("abort: %s\n") % inst)
3286 except TypeError, inst:
3284 except TypeError, inst:
3287 # was this an argument error?
3285 # was this an argument error?
3288 tb = traceback.extract_tb(sys.exc_info()[2])
3286 tb = traceback.extract_tb(sys.exc_info()[2])
3289 if len(tb) > 2: # no
3287 if len(tb) > 2: # no
3290 raise
3288 raise
3291 u.debug(inst, "\n")
3289 u.debug(inst, "\n")
3292 u.warn(_("%s: invalid arguments\n") % cmd)
3290 u.warn(_("%s: invalid arguments\n") % cmd)
3293 help_(u, cmd)
3291 help_(u, cmd)
3294 except SystemExit, inst:
3292 except SystemExit, inst:
3295 # Commands shouldn't sys.exit directly, but give a return code.
3293 # Commands shouldn't sys.exit directly, but give a return code.
3296 # Just in case catch this and and pass exit code to caller.
3294 # Just in case catch this and and pass exit code to caller.
3297 return inst.code
3295 return inst.code
3298 except:
3296 except:
3299 u.warn(_("** unknown exception encountered, details follow\n"))
3297 u.warn(_("** unknown exception encountered, details follow\n"))
3300 u.warn(_("** report bug details to "
3298 u.warn(_("** report bug details to "
3301 "http://www.selenic.com/mercurial/bts\n"))
3299 "http://www.selenic.com/mercurial/bts\n"))
3302 u.warn(_("** or mercurial@selenic.com\n"))
3300 u.warn(_("** or mercurial@selenic.com\n"))
3303 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3301 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3304 % version.get_version())
3302 % version.get_version())
3305 raise
3303 raise
3306
3304
3307 return -1
3305 return -1
@@ -1,1404 +1,1404 b''
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7
7
8 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
10
10
11 This contains helper routines that are independent of the SCM core and hide
11 This contains helper routines that are independent of the SCM core and hide
12 platform-specific details from the core.
12 platform-specific details from the core.
13 """
13 """
14
14
15 from i18n import _
15 from i18n import _
16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile
16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile
17 import os, threading, time, calendar, ConfigParser, locale, glob
17 import os, threading, time, calendar, ConfigParser, locale, glob
18
18
19 try:
19 try:
20 _encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding() \
20 _encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding() \
21 or "ascii"
21 or "ascii"
22 except locale.Error:
22 except locale.Error:
23 _encoding = 'ascii'
23 _encoding = 'ascii'
24 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
24 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
25 _fallbackencoding = 'ISO-8859-1'
25 _fallbackencoding = 'ISO-8859-1'
26
26
27 def tolocal(s):
27 def tolocal(s):
28 """
28 """
29 Convert a string from internal UTF-8 to local encoding
29 Convert a string from internal UTF-8 to local encoding
30
30
31 All internal strings should be UTF-8 but some repos before the
31 All internal strings should be UTF-8 but some repos before the
32 implementation of locale support may contain latin1 or possibly
32 implementation of locale support may contain latin1 or possibly
33 other character sets. We attempt to decode everything strictly
33 other character sets. We attempt to decode everything strictly
34 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
34 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
35 replace unknown characters.
35 replace unknown characters.
36 """
36 """
37 for e in ('UTF-8', _fallbackencoding):
37 for e in ('UTF-8', _fallbackencoding):
38 try:
38 try:
39 u = s.decode(e) # attempt strict decoding
39 u = s.decode(e) # attempt strict decoding
40 return u.encode(_encoding, "replace")
40 return u.encode(_encoding, "replace")
41 except LookupError, k:
41 except LookupError, k:
42 raise Abort(_("%s, please check your locale settings") % k)
42 raise Abort(_("%s, please check your locale settings") % k)
43 except UnicodeDecodeError:
43 except UnicodeDecodeError:
44 pass
44 pass
45 u = s.decode("utf-8", "replace") # last ditch
45 u = s.decode("utf-8", "replace") # last ditch
46 return u.encode(_encoding, "replace")
46 return u.encode(_encoding, "replace")
47
47
48 def fromlocal(s):
48 def fromlocal(s):
49 """
49 """
50 Convert a string from the local character encoding to UTF-8
50 Convert a string from the local character encoding to UTF-8
51
51
52 We attempt to decode strings using the encoding mode set by
52 We attempt to decode strings using the encoding mode set by
53 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
53 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
54 characters will cause an error message. Other modes include
54 characters will cause an error message. Other modes include
55 'replace', which replaces unknown characters with a special
55 'replace', which replaces unknown characters with a special
56 Unicode character, and 'ignore', which drops the character.
56 Unicode character, and 'ignore', which drops the character.
57 """
57 """
58 try:
58 try:
59 return s.decode(_encoding, _encodingmode).encode("utf-8")
59 return s.decode(_encoding, _encodingmode).encode("utf-8")
60 except UnicodeDecodeError, inst:
60 except UnicodeDecodeError, inst:
61 sub = s[max(0, inst.start-10):inst.start+10]
61 sub = s[max(0, inst.start-10):inst.start+10]
62 raise Abort("decoding near '%s': %s!" % (sub, inst))
62 raise Abort("decoding near '%s': %s!" % (sub, inst))
63 except LookupError, k:
63 except LookupError, k:
64 raise Abort(_("%s, please check your locale settings") % k)
64 raise Abort(_("%s, please check your locale settings") % k)
65
65
66 def locallen(s):
66 def locallen(s):
67 """Find the length in characters of a local string"""
67 """Find the length in characters of a local string"""
68 return len(s.decode(_encoding, "replace"))
68 return len(s.decode(_encoding, "replace"))
69
69
70 def localsub(s, a, b=None):
70 def localsub(s, a, b=None):
71 try:
71 try:
72 u = s.decode(_encoding, _encodingmode)
72 u = s.decode(_encoding, _encodingmode)
73 if b is not None:
73 if b is not None:
74 u = u[a:b]
74 u = u[a:b]
75 else:
75 else:
76 u = u[:a]
76 u = u[:a]
77 return u.encode(_encoding, _encodingmode)
77 return u.encode(_encoding, _encodingmode)
78 except UnicodeDecodeError, inst:
78 except UnicodeDecodeError, inst:
79 sub = s[max(0, inst.start-10), inst.start+10]
79 sub = s[max(0, inst.start-10), inst.start+10]
80 raise Abort(_("decoding near '%s': %s!\n") % (sub, inst))
80 raise Abort(_("decoding near '%s': %s!\n") % (sub, inst))
81
81
82 # used by parsedate
82 # used by parsedate
83 defaultdateformats = (
83 defaultdateformats = (
84 '%Y-%m-%d %H:%M:%S',
84 '%Y-%m-%d %H:%M:%S',
85 '%Y-%m-%d %I:%M:%S%p',
85 '%Y-%m-%d %I:%M:%S%p',
86 '%Y-%m-%d %H:%M',
86 '%Y-%m-%d %H:%M',
87 '%Y-%m-%d %I:%M%p',
87 '%Y-%m-%d %I:%M%p',
88 '%Y-%m-%d',
88 '%Y-%m-%d',
89 '%m-%d',
89 '%m-%d',
90 '%m/%d',
90 '%m/%d',
91 '%m/%d/%y',
91 '%m/%d/%y',
92 '%m/%d/%Y',
92 '%m/%d/%Y',
93 '%a %b %d %H:%M:%S %Y',
93 '%a %b %d %H:%M:%S %Y',
94 '%a %b %d %I:%M:%S%p %Y',
94 '%a %b %d %I:%M:%S%p %Y',
95 '%b %d %H:%M:%S %Y',
95 '%b %d %H:%M:%S %Y',
96 '%b %d %I:%M:%S%p %Y',
96 '%b %d %I:%M:%S%p %Y',
97 '%b %d %H:%M:%S',
97 '%b %d %H:%M:%S',
98 '%b %d %I:%M:%S%p',
98 '%b %d %I:%M:%S%p',
99 '%b %d %H:%M',
99 '%b %d %H:%M',
100 '%b %d %I:%M%p',
100 '%b %d %I:%M%p',
101 '%b %d %Y',
101 '%b %d %Y',
102 '%b %d',
102 '%b %d',
103 '%H:%M:%S',
103 '%H:%M:%S',
104 '%I:%M:%SP',
104 '%I:%M:%SP',
105 '%H:%M',
105 '%H:%M',
106 '%I:%M%p',
106 '%I:%M%p',
107 )
107 )
108
108
109 extendeddateformats = defaultdateformats + (
109 extendeddateformats = defaultdateformats + (
110 "%Y",
110 "%Y",
111 "%Y-%m",
111 "%Y-%m",
112 "%b",
112 "%b",
113 "%b %Y",
113 "%b %Y",
114 )
114 )
115
115
116 class SignalInterrupt(Exception):
116 class SignalInterrupt(Exception):
117 """Exception raised on SIGTERM and SIGHUP."""
117 """Exception raised on SIGTERM and SIGHUP."""
118
118
119 # like SafeConfigParser but with case-sensitive keys
119 # like SafeConfigParser but with case-sensitive keys
120 class configparser(ConfigParser.SafeConfigParser):
120 class configparser(ConfigParser.SafeConfigParser):
121 def optionxform(self, optionstr):
121 def optionxform(self, optionstr):
122 return optionstr
122 return optionstr
123
123
124 def cachefunc(func):
124 def cachefunc(func):
125 '''cache the result of function calls'''
125 '''cache the result of function calls'''
126 # XXX doesn't handle keywords args
126 # XXX doesn't handle keywords args
127 cache = {}
127 cache = {}
128 if func.func_code.co_argcount == 1:
128 if func.func_code.co_argcount == 1:
129 # we gain a small amount of time because
129 # we gain a small amount of time because
130 # we don't need to pack/unpack the list
130 # we don't need to pack/unpack the list
131 def f(arg):
131 def f(arg):
132 if arg not in cache:
132 if arg not in cache:
133 cache[arg] = func(arg)
133 cache[arg] = func(arg)
134 return cache[arg]
134 return cache[arg]
135 else:
135 else:
136 def f(*args):
136 def f(*args):
137 if args not in cache:
137 if args not in cache:
138 cache[args] = func(*args)
138 cache[args] = func(*args)
139 return cache[args]
139 return cache[args]
140
140
141 return f
141 return f
142
142
143 def pipefilter(s, cmd):
143 def pipefilter(s, cmd):
144 '''filter string S through command CMD, returning its output'''
144 '''filter string S through command CMD, returning its output'''
145 (pout, pin) = popen2.popen2(cmd, -1, 'b')
145 (pout, pin) = popen2.popen2(cmd, -1, 'b')
146 def writer():
146 def writer():
147 try:
147 try:
148 pin.write(s)
148 pin.write(s)
149 pin.close()
149 pin.close()
150 except IOError, inst:
150 except IOError, inst:
151 if inst.errno != errno.EPIPE:
151 if inst.errno != errno.EPIPE:
152 raise
152 raise
153
153
154 # we should use select instead on UNIX, but this will work on most
154 # we should use select instead on UNIX, but this will work on most
155 # systems, including Windows
155 # systems, including Windows
156 w = threading.Thread(target=writer)
156 w = threading.Thread(target=writer)
157 w.start()
157 w.start()
158 f = pout.read()
158 f = pout.read()
159 pout.close()
159 pout.close()
160 w.join()
160 w.join()
161 return f
161 return f
162
162
163 def tempfilter(s, cmd):
163 def tempfilter(s, cmd):
164 '''filter string S through a pair of temporary files with CMD.
164 '''filter string S through a pair of temporary files with CMD.
165 CMD is used as a template to create the real command to be run,
165 CMD is used as a template to create the real command to be run,
166 with the strings INFILE and OUTFILE replaced by the real names of
166 with the strings INFILE and OUTFILE replaced by the real names of
167 the temporary files generated.'''
167 the temporary files generated.'''
168 inname, outname = None, None
168 inname, outname = None, None
169 try:
169 try:
170 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
170 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
171 fp = os.fdopen(infd, 'wb')
171 fp = os.fdopen(infd, 'wb')
172 fp.write(s)
172 fp.write(s)
173 fp.close()
173 fp.close()
174 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
174 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
175 os.close(outfd)
175 os.close(outfd)
176 cmd = cmd.replace('INFILE', inname)
176 cmd = cmd.replace('INFILE', inname)
177 cmd = cmd.replace('OUTFILE', outname)
177 cmd = cmd.replace('OUTFILE', outname)
178 code = os.system(cmd)
178 code = os.system(cmd)
179 if code: raise Abort(_("command '%s' failed: %s") %
179 if code: raise Abort(_("command '%s' failed: %s") %
180 (cmd, explain_exit(code)))
180 (cmd, explain_exit(code)))
181 return open(outname, 'rb').read()
181 return open(outname, 'rb').read()
182 finally:
182 finally:
183 try:
183 try:
184 if inname: os.unlink(inname)
184 if inname: os.unlink(inname)
185 except: pass
185 except: pass
186 try:
186 try:
187 if outname: os.unlink(outname)
187 if outname: os.unlink(outname)
188 except: pass
188 except: pass
189
189
190 filtertable = {
190 filtertable = {
191 'tempfile:': tempfilter,
191 'tempfile:': tempfilter,
192 'pipe:': pipefilter,
192 'pipe:': pipefilter,
193 }
193 }
194
194
195 def filter(s, cmd):
195 def filter(s, cmd):
196 "filter a string through a command that transforms its input to its output"
196 "filter a string through a command that transforms its input to its output"
197 for name, fn in filtertable.iteritems():
197 for name, fn in filtertable.iteritems():
198 if cmd.startswith(name):
198 if cmd.startswith(name):
199 return fn(s, cmd[len(name):].lstrip())
199 return fn(s, cmd[len(name):].lstrip())
200 return pipefilter(s, cmd)
200 return pipefilter(s, cmd)
201
201
202 def find_in_path(name, path, default=None):
202 def find_in_path(name, path, default=None):
203 '''find name in search path. path can be string (will be split
203 '''find name in search path. path can be string (will be split
204 with os.pathsep), or iterable thing that returns strings. if name
204 with os.pathsep), or iterable thing that returns strings. if name
205 found, return path to name. else return default.'''
205 found, return path to name. else return default.'''
206 if isinstance(path, str):
206 if isinstance(path, str):
207 path = path.split(os.pathsep)
207 path = path.split(os.pathsep)
208 for p in path:
208 for p in path:
209 p_name = os.path.join(p, name)
209 p_name = os.path.join(p, name)
210 if os.path.exists(p_name):
210 if os.path.exists(p_name):
211 return p_name
211 return p_name
212 return default
212 return default
213
213
214 def binary(s):
214 def binary(s):
215 """return true if a string is binary data using diff's heuristic"""
215 """return true if a string is binary data using diff's heuristic"""
216 if s and '\0' in s[:4096]:
216 if s and '\0' in s[:4096]:
217 return True
217 return True
218 return False
218 return False
219
219
220 def unique(g):
220 def unique(g):
221 """return the uniq elements of iterable g"""
221 """return the uniq elements of iterable g"""
222 seen = {}
222 seen = {}
223 l = []
223 l = []
224 for f in g:
224 for f in g:
225 if f not in seen:
225 if f not in seen:
226 seen[f] = 1
226 seen[f] = 1
227 l.append(f)
227 l.append(f)
228 return l
228 return l
229
229
230 class Abort(Exception):
230 class Abort(Exception):
231 """Raised if a command needs to print an error and exit."""
231 """Raised if a command needs to print an error and exit."""
232
232
233 class UnexpectedOutput(Abort):
233 class UnexpectedOutput(Abort):
234 """Raised to print an error with part of output and exit."""
234 """Raised to print an error with part of output and exit."""
235
235
236 def always(fn): return True
236 def always(fn): return True
237 def never(fn): return False
237 def never(fn): return False
238
238
239 def expand_glob(pats):
239 def expand_glob(pats):
240 '''On Windows, expand the implicit globs in a list of patterns'''
240 '''On Windows, expand the implicit globs in a list of patterns'''
241 if os.name != 'nt':
241 if os.name != 'nt':
242 return list(pats)
242 return list(pats)
243 ret = []
243 ret = []
244 for p in pats:
244 for p in pats:
245 kind, name = patkind(p, None)
245 kind, name = patkind(p, None)
246 if kind is None:
246 if kind is None:
247 globbed = glob.glob(name)
247 globbed = glob.glob(name)
248 if globbed:
248 if globbed:
249 ret.extend(globbed)
249 ret.extend(globbed)
250 continue
250 continue
251 # if we couldn't expand the glob, just keep it around
251 # if we couldn't expand the glob, just keep it around
252 ret.append(p)
252 ret.append(p)
253 return ret
253 return ret
254
254
255 def patkind(name, dflt_pat='glob'):
255 def patkind(name, dflt_pat='glob'):
256 """Split a string into an optional pattern kind prefix and the
256 """Split a string into an optional pattern kind prefix and the
257 actual pattern."""
257 actual pattern."""
258 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
258 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
259 if name.startswith(prefix + ':'): return name.split(':', 1)
259 if name.startswith(prefix + ':'): return name.split(':', 1)
260 return dflt_pat, name
260 return dflt_pat, name
261
261
262 def globre(pat, head='^', tail='$'):
262 def globre(pat, head='^', tail='$'):
263 "convert a glob pattern into a regexp"
263 "convert a glob pattern into a regexp"
264 i, n = 0, len(pat)
264 i, n = 0, len(pat)
265 res = ''
265 res = ''
266 group = False
266 group = False
267 def peek(): return i < n and pat[i]
267 def peek(): return i < n and pat[i]
268 while i < n:
268 while i < n:
269 c = pat[i]
269 c = pat[i]
270 i = i+1
270 i = i+1
271 if c == '*':
271 if c == '*':
272 if peek() == '*':
272 if peek() == '*':
273 i += 1
273 i += 1
274 res += '.*'
274 res += '.*'
275 else:
275 else:
276 res += '[^/]*'
276 res += '[^/]*'
277 elif c == '?':
277 elif c == '?':
278 res += '.'
278 res += '.'
279 elif c == '[':
279 elif c == '[':
280 j = i
280 j = i
281 if j < n and pat[j] in '!]':
281 if j < n and pat[j] in '!]':
282 j += 1
282 j += 1
283 while j < n and pat[j] != ']':
283 while j < n and pat[j] != ']':
284 j += 1
284 j += 1
285 if j >= n:
285 if j >= n:
286 res += '\\['
286 res += '\\['
287 else:
287 else:
288 stuff = pat[i:j].replace('\\','\\\\')
288 stuff = pat[i:j].replace('\\','\\\\')
289 i = j + 1
289 i = j + 1
290 if stuff[0] == '!':
290 if stuff[0] == '!':
291 stuff = '^' + stuff[1:]
291 stuff = '^' + stuff[1:]
292 elif stuff[0] == '^':
292 elif stuff[0] == '^':
293 stuff = '\\' + stuff
293 stuff = '\\' + stuff
294 res = '%s[%s]' % (res, stuff)
294 res = '%s[%s]' % (res, stuff)
295 elif c == '{':
295 elif c == '{':
296 group = True
296 group = True
297 res += '(?:'
297 res += '(?:'
298 elif c == '}' and group:
298 elif c == '}' and group:
299 res += ')'
299 res += ')'
300 group = False
300 group = False
301 elif c == ',' and group:
301 elif c == ',' and group:
302 res += '|'
302 res += '|'
303 elif c == '\\':
303 elif c == '\\':
304 p = peek()
304 p = peek()
305 if p:
305 if p:
306 i += 1
306 i += 1
307 res += re.escape(p)
307 res += re.escape(p)
308 else:
308 else:
309 res += re.escape(c)
309 res += re.escape(c)
310 else:
310 else:
311 res += re.escape(c)
311 res += re.escape(c)
312 return head + res + tail
312 return head + res + tail
313
313
314 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
314 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
315
315
316 def pathto(n1, n2):
316 def pathto(n1, n2):
317 '''return the relative path from one place to another.
317 '''return the relative path from one place to another.
318 n1 should use os.sep to separate directories
318 n1 should use os.sep to separate directories
319 n2 should use "/" to separate directories
319 n2 should use "/" to separate directories
320 returns an os.sep-separated path.
320 returns an os.sep-separated path.
321 '''
321 '''
322 if not n1: return localpath(n2)
322 if not n1: return localpath(n2)
323 a, b = n1.split(os.sep), n2.split('/')
323 a, b = n1.split(os.sep), n2.split('/')
324 a.reverse()
324 a.reverse()
325 b.reverse()
325 b.reverse()
326 while a and b and a[-1] == b[-1]:
326 while a and b and a[-1] == b[-1]:
327 a.pop()
327 a.pop()
328 b.pop()
328 b.pop()
329 b.reverse()
329 b.reverse()
330 return os.sep.join((['..'] * len(a)) + b)
330 return os.sep.join((['..'] * len(a)) + b)
331
331
332 def canonpath(root, cwd, myname):
332 def canonpath(root, cwd, myname):
333 """return the canonical path of myname, given cwd and root"""
333 """return the canonical path of myname, given cwd and root"""
334 if root == os.sep:
334 if root == os.sep:
335 rootsep = os.sep
335 rootsep = os.sep
336 elif root.endswith(os.sep):
336 elif root.endswith(os.sep):
337 rootsep = root
337 rootsep = root
338 else:
338 else:
339 rootsep = root + os.sep
339 rootsep = root + os.sep
340 name = myname
340 name = myname
341 if not os.path.isabs(name):
341 if not os.path.isabs(name):
342 name = os.path.join(root, cwd, name)
342 name = os.path.join(root, cwd, name)
343 name = os.path.normpath(name)
343 name = os.path.normpath(name)
344 if name != rootsep and name.startswith(rootsep):
344 if name != rootsep and name.startswith(rootsep):
345 name = name[len(rootsep):]
345 name = name[len(rootsep):]
346 audit_path(name)
346 audit_path(name)
347 return pconvert(name)
347 return pconvert(name)
348 elif name == root:
348 elif name == root:
349 return ''
349 return ''
350 else:
350 else:
351 # Determine whether `name' is in the hierarchy at or beneath `root',
351 # Determine whether `name' is in the hierarchy at or beneath `root',
352 # by iterating name=dirname(name) until that causes no change (can't
352 # by iterating name=dirname(name) until that causes no change (can't
353 # check name == '/', because that doesn't work on windows). For each
353 # check name == '/', because that doesn't work on windows). For each
354 # `name', compare dev/inode numbers. If they match, the list `rel'
354 # `name', compare dev/inode numbers. If they match, the list `rel'
355 # holds the reversed list of components making up the relative file
355 # holds the reversed list of components making up the relative file
356 # name we want.
356 # name we want.
357 root_st = os.stat(root)
357 root_st = os.stat(root)
358 rel = []
358 rel = []
359 while True:
359 while True:
360 try:
360 try:
361 name_st = os.stat(name)
361 name_st = os.stat(name)
362 except OSError:
362 except OSError:
363 break
363 break
364 if samestat(name_st, root_st):
364 if samestat(name_st, root_st):
365 rel.reverse()
365 rel.reverse()
366 name = os.path.join(*rel)
366 name = os.path.join(*rel)
367 audit_path(name)
367 audit_path(name)
368 return pconvert(name)
368 return pconvert(name)
369 dirname, basename = os.path.split(name)
369 dirname, basename = os.path.split(name)
370 rel.append(basename)
370 rel.append(basename)
371 if dirname == name:
371 if dirname == name:
372 break
372 break
373 name = dirname
373 name = dirname
374
374
375 raise Abort('%s not under root' % myname)
375 raise Abort('%s not under root' % myname)
376
376
377 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
377 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
378 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
378 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
379
379
380 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='',
380 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='',
381 src=None, globbed=False):
381 src=None, globbed=False):
382 if not globbed:
382 if not globbed:
383 names = expand_glob(names)
383 names = expand_glob(names)
384 return _matcher(canonroot, cwd, names, inc, exc, head, 'relpath', src)
384 return _matcher(canonroot, cwd, names, inc, exc, head, 'relpath', src)
385
385
386 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
386 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
387 """build a function to match a set of file patterns
387 """build a function to match a set of file patterns
388
388
389 arguments:
389 arguments:
390 canonroot - the canonical root of the tree you're matching against
390 canonroot - the canonical root of the tree you're matching against
391 cwd - the current working directory, if relevant
391 cwd - the current working directory, if relevant
392 names - patterns to find
392 names - patterns to find
393 inc - patterns to include
393 inc - patterns to include
394 exc - patterns to exclude
394 exc - patterns to exclude
395 head - a regex to prepend to patterns to control whether a match is rooted
395 head - a regex to prepend to patterns to control whether a match is rooted
396
396
397 a pattern is one of:
397 a pattern is one of:
398 'glob:<rooted glob>'
398 'glob:<rooted glob>'
399 're:<rooted regexp>'
399 're:<rooted regexp>'
400 'path:<rooted path>'
400 'path:<rooted path>'
401 'relglob:<relative glob>'
401 'relglob:<relative glob>'
402 'relpath:<relative path>'
402 'relpath:<relative path>'
403 'relre:<relative regexp>'
403 'relre:<relative regexp>'
404 '<rooted path or regexp>'
404 '<rooted path or regexp>'
405
405
406 returns:
406 returns:
407 a 3-tuple containing
407 a 3-tuple containing
408 - list of explicit non-pattern names passed in
408 - list of explicit non-pattern names passed in
409 - a bool match(filename) function
409 - a bool match(filename) function
410 - a bool indicating if any patterns were passed in
410 - a bool indicating if any patterns were passed in
411
411
412 todo:
412 todo:
413 make head regex a rooted bool
413 make head regex a rooted bool
414 """
414 """
415
415
416 def contains_glob(name):
416 def contains_glob(name):
417 for c in name:
417 for c in name:
418 if c in _globchars: return True
418 if c in _globchars: return True
419 return False
419 return False
420
420
421 def regex(kind, name, tail):
421 def regex(kind, name, tail):
422 '''convert a pattern into a regular expression'''
422 '''convert a pattern into a regular expression'''
423 if kind == 're':
423 if kind == 're':
424 return name
424 return name
425 elif kind == 'path':
425 elif kind == 'path':
426 return '^' + re.escape(name) + '(?:/|$)'
426 return '^' + re.escape(name) + '(?:/|$)'
427 elif kind == 'relglob':
427 elif kind == 'relglob':
428 return head + globre(name, '(?:|.*/)', tail)
428 return head + globre(name, '(?:|.*/)', tail)
429 elif kind == 'relpath':
429 elif kind == 'relpath':
430 return head + re.escape(name) + tail
430 return head + re.escape(name) + tail
431 elif kind == 'relre':
431 elif kind == 'relre':
432 if name.startswith('^'):
432 if name.startswith('^'):
433 return name
433 return name
434 return '.*' + name
434 return '.*' + name
435 return head + globre(name, '', tail)
435 return head + globre(name, '', tail)
436
436
437 def matchfn(pats, tail):
437 def matchfn(pats, tail):
438 """build a matching function from a set of patterns"""
438 """build a matching function from a set of patterns"""
439 if not pats:
439 if not pats:
440 return
440 return
441 matches = []
441 matches = []
442 for k, p in pats:
442 for k, p in pats:
443 try:
443 try:
444 pat = '(?:%s)' % regex(k, p, tail)
444 pat = '(?:%s)' % regex(k, p, tail)
445 matches.append(re.compile(pat).match)
445 matches.append(re.compile(pat).match)
446 except re.error:
446 except re.error:
447 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
447 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
448 else: raise Abort("invalid pattern (%s): %s" % (k, p))
448 else: raise Abort("invalid pattern (%s): %s" % (k, p))
449
449
450 def buildfn(text):
450 def buildfn(text):
451 for m in matches:
451 for m in matches:
452 r = m(text)
452 r = m(text)
453 if r:
453 if r:
454 return r
454 return r
455
455
456 return buildfn
456 return buildfn
457
457
458 def globprefix(pat):
458 def globprefix(pat):
459 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
459 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
460 root = []
460 root = []
461 for p in pat.split(os.sep):
461 for p in pat.split(os.sep):
462 if contains_glob(p): break
462 if contains_glob(p): break
463 root.append(p)
463 root.append(p)
464 return '/'.join(root)
464 return '/'.join(root)
465
465
466 pats = []
466 pats = []
467 files = []
467 files = []
468 roots = []
468 roots = []
469 for kind, name in [patkind(p, dflt_pat) for p in names]:
469 for kind, name in [patkind(p, dflt_pat) for p in names]:
470 if kind in ('glob', 'relpath'):
470 if kind in ('glob', 'relpath'):
471 name = canonpath(canonroot, cwd, name)
471 name = canonpath(canonroot, cwd, name)
472 if name == '':
472 if name == '':
473 kind, name = 'glob', '**'
473 kind, name = 'glob', '**'
474 if kind in ('glob', 'path', 're'):
474 if kind in ('glob', 'path', 're'):
475 pats.append((kind, name))
475 pats.append((kind, name))
476 if kind == 'glob':
476 if kind == 'glob':
477 root = globprefix(name)
477 root = globprefix(name)
478 if root: roots.append(root)
478 if root: roots.append(root)
479 elif kind == 'relpath':
479 elif kind == 'relpath':
480 files.append((kind, name))
480 files.append((kind, name))
481 roots.append(name)
481 roots.append(name)
482
482
483 patmatch = matchfn(pats, '$') or always
483 patmatch = matchfn(pats, '$') or always
484 filematch = matchfn(files, '(?:/|$)') or always
484 filematch = matchfn(files, '(?:/|$)') or always
485 incmatch = always
485 incmatch = always
486 if inc:
486 if inc:
487 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
487 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
488 incmatch = matchfn(inckinds, '(?:/|$)')
488 incmatch = matchfn(inckinds, '(?:/|$)')
489 excmatch = lambda fn: False
489 excmatch = lambda fn: False
490 if exc:
490 if exc:
491 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
491 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
492 excmatch = matchfn(exckinds, '(?:/|$)')
492 excmatch = matchfn(exckinds, '(?:/|$)')
493
493
494 return (roots,
494 return (roots,
495 lambda fn: (incmatch(fn) and not excmatch(fn) and
495 lambda fn: (incmatch(fn) and not excmatch(fn) and
496 (fn.endswith('/') or
496 (fn.endswith('/') or
497 (not pats and not files) or
497 (not pats and not files) or
498 (pats and patmatch(fn)) or
498 (pats and patmatch(fn)) or
499 (files and filematch(fn)))),
499 (files and filematch(fn)))),
500 (inc or exc or (pats and pats != [('glob', '**')])) and True)
500 (inc or exc or (pats and pats != [('glob', '**')])) and True)
501
501
502 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
502 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
503 '''enhanced shell command execution.
503 '''enhanced shell command execution.
504 run with environment maybe modified, maybe in different dir.
504 run with environment maybe modified, maybe in different dir.
505
505
506 if command fails and onerr is None, return status. if ui object,
506 if command fails and onerr is None, return status. if ui object,
507 print error message and return status, else raise onerr object as
507 print error message and return status, else raise onerr object as
508 exception.'''
508 exception.'''
509 def py2shell(val):
509 def py2shell(val):
510 'convert python object into string that is useful to shell'
510 'convert python object into string that is useful to shell'
511 if val in (None, False):
511 if val in (None, False):
512 return '0'
512 return '0'
513 if val == True:
513 if val == True:
514 return '1'
514 return '1'
515 return str(val)
515 return str(val)
516 oldenv = {}
516 oldenv = {}
517 for k in environ:
517 for k in environ:
518 oldenv[k] = os.environ.get(k)
518 oldenv[k] = os.environ.get(k)
519 if cwd is not None:
519 if cwd is not None:
520 oldcwd = os.getcwd()
520 oldcwd = os.getcwd()
521 origcmd = cmd
521 origcmd = cmd
522 if os.name == 'nt':
522 if os.name == 'nt':
523 cmd = '"%s"' % cmd
523 cmd = '"%s"' % cmd
524 try:
524 try:
525 for k, v in environ.iteritems():
525 for k, v in environ.iteritems():
526 os.environ[k] = py2shell(v)
526 os.environ[k] = py2shell(v)
527 if cwd is not None and oldcwd != cwd:
527 if cwd is not None and oldcwd != cwd:
528 os.chdir(cwd)
528 os.chdir(cwd)
529 rc = os.system(cmd)
529 rc = os.system(cmd)
530 if rc and onerr:
530 if rc and onerr:
531 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
531 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
532 explain_exit(rc)[0])
532 explain_exit(rc)[0])
533 if errprefix:
533 if errprefix:
534 errmsg = '%s: %s' % (errprefix, errmsg)
534 errmsg = '%s: %s' % (errprefix, errmsg)
535 try:
535 try:
536 onerr.warn(errmsg + '\n')
536 onerr.warn(errmsg + '\n')
537 except AttributeError:
537 except AttributeError:
538 raise onerr(errmsg)
538 raise onerr(errmsg)
539 return rc
539 return rc
540 finally:
540 finally:
541 for k, v in oldenv.iteritems():
541 for k, v in oldenv.iteritems():
542 if v is None:
542 if v is None:
543 del os.environ[k]
543 del os.environ[k]
544 else:
544 else:
545 os.environ[k] = v
545 os.environ[k] = v
546 if cwd is not None and oldcwd != cwd:
546 if cwd is not None and oldcwd != cwd:
547 os.chdir(oldcwd)
547 os.chdir(oldcwd)
548
548
549 def rename(src, dst):
549 def rename(src, dst):
550 """forcibly rename a file"""
550 """forcibly rename a file"""
551 try:
551 try:
552 os.rename(src, dst)
552 os.rename(src, dst)
553 except OSError, err:
553 except OSError, err:
554 # on windows, rename to existing file is not allowed, so we
554 # on windows, rename to existing file is not allowed, so we
555 # must delete destination first. but if file is open, unlink
555 # must delete destination first. but if file is open, unlink
556 # schedules it for delete but does not delete it. rename
556 # schedules it for delete but does not delete it. rename
557 # happens immediately even for open files, so we create
557 # happens immediately even for open files, so we create
558 # temporary file, delete it, rename destination to that name,
558 # temporary file, delete it, rename destination to that name,
559 # then delete that. then rename is safe to do.
559 # then delete that. then rename is safe to do.
560 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
560 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
561 os.close(fd)
561 os.close(fd)
562 os.unlink(temp)
562 os.unlink(temp)
563 os.rename(dst, temp)
563 os.rename(dst, temp)
564 os.unlink(temp)
564 os.unlink(temp)
565 os.rename(src, dst)
565 os.rename(src, dst)
566
566
567 def unlink(f):
567 def unlink(f):
568 """unlink and remove the directory if it is empty"""
568 """unlink and remove the directory if it is empty"""
569 os.unlink(f)
569 os.unlink(f)
570 # try removing directories that might now be empty
570 # try removing directories that might now be empty
571 try:
571 try:
572 os.removedirs(os.path.dirname(f))
572 os.removedirs(os.path.dirname(f))
573 except OSError:
573 except OSError:
574 pass
574 pass
575
575
576 def copyfile(src, dest):
576 def copyfile(src, dest):
577 "copy a file, preserving mode"
577 "copy a file, preserving mode"
578 try:
578 try:
579 shutil.copyfile(src, dest)
579 shutil.copyfile(src, dest)
580 shutil.copymode(src, dest)
580 shutil.copymode(src, dest)
581 except shutil.Error, inst:
581 except shutil.Error, inst:
582 raise util.Abort(str(inst))
582 raise Abort(str(inst))
583
583
584 def copyfiles(src, dst, hardlink=None):
584 def copyfiles(src, dst, hardlink=None):
585 """Copy a directory tree using hardlinks if possible"""
585 """Copy a directory tree using hardlinks if possible"""
586
586
587 if hardlink is None:
587 if hardlink is None:
588 hardlink = (os.stat(src).st_dev ==
588 hardlink = (os.stat(src).st_dev ==
589 os.stat(os.path.dirname(dst)).st_dev)
589 os.stat(os.path.dirname(dst)).st_dev)
590
590
591 if os.path.isdir(src):
591 if os.path.isdir(src):
592 os.mkdir(dst)
592 os.mkdir(dst)
593 for name in os.listdir(src):
593 for name in os.listdir(src):
594 srcname = os.path.join(src, name)
594 srcname = os.path.join(src, name)
595 dstname = os.path.join(dst, name)
595 dstname = os.path.join(dst, name)
596 copyfiles(srcname, dstname, hardlink)
596 copyfiles(srcname, dstname, hardlink)
597 else:
597 else:
598 if hardlink:
598 if hardlink:
599 try:
599 try:
600 os_link(src, dst)
600 os_link(src, dst)
601 except (IOError, OSError):
601 except (IOError, OSError):
602 hardlink = False
602 hardlink = False
603 shutil.copy(src, dst)
603 shutil.copy(src, dst)
604 else:
604 else:
605 shutil.copy(src, dst)
605 shutil.copy(src, dst)
606
606
607 def audit_path(path):
607 def audit_path(path):
608 """Abort if path contains dangerous components"""
608 """Abort if path contains dangerous components"""
609 parts = os.path.normcase(path).split(os.sep)
609 parts = os.path.normcase(path).split(os.sep)
610 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
610 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
611 or os.pardir in parts):
611 or os.pardir in parts):
612 raise Abort(_("path contains illegal component: %s\n") % path)
612 raise Abort(_("path contains illegal component: %s\n") % path)
613
613
614 def _makelock_file(info, pathname):
614 def _makelock_file(info, pathname):
615 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
615 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
616 os.write(ld, info)
616 os.write(ld, info)
617 os.close(ld)
617 os.close(ld)
618
618
619 def _readlock_file(pathname):
619 def _readlock_file(pathname):
620 return posixfile(pathname).read()
620 return posixfile(pathname).read()
621
621
622 def nlinks(pathname):
622 def nlinks(pathname):
623 """Return number of hardlinks for the given file."""
623 """Return number of hardlinks for the given file."""
624 return os.lstat(pathname).st_nlink
624 return os.lstat(pathname).st_nlink
625
625
626 if hasattr(os, 'link'):
626 if hasattr(os, 'link'):
627 os_link = os.link
627 os_link = os.link
628 else:
628 else:
629 def os_link(src, dst):
629 def os_link(src, dst):
630 raise OSError(0, _("Hardlinks not supported"))
630 raise OSError(0, _("Hardlinks not supported"))
631
631
632 def fstat(fp):
632 def fstat(fp):
633 '''stat file object that may not have fileno method.'''
633 '''stat file object that may not have fileno method.'''
634 try:
634 try:
635 return os.fstat(fp.fileno())
635 return os.fstat(fp.fileno())
636 except AttributeError:
636 except AttributeError:
637 return os.stat(fp.name)
637 return os.stat(fp.name)
638
638
639 posixfile = file
639 posixfile = file
640
640
641 def is_win_9x():
641 def is_win_9x():
642 '''return true if run on windows 95, 98 or me.'''
642 '''return true if run on windows 95, 98 or me.'''
643 try:
643 try:
644 return sys.getwindowsversion()[3] == 1
644 return sys.getwindowsversion()[3] == 1
645 except AttributeError:
645 except AttributeError:
646 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
646 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
647
647
648 getuser_fallback = None
648 getuser_fallback = None
649
649
650 def getuser():
650 def getuser():
651 '''return name of current user'''
651 '''return name of current user'''
652 try:
652 try:
653 return getpass.getuser()
653 return getpass.getuser()
654 except ImportError:
654 except ImportError:
655 # import of pwd will fail on windows - try fallback
655 # import of pwd will fail on windows - try fallback
656 if getuser_fallback:
656 if getuser_fallback:
657 return getuser_fallback()
657 return getuser_fallback()
658 # raised if win32api not available
658 # raised if win32api not available
659 raise Abort(_('user name not available - set USERNAME '
659 raise Abort(_('user name not available - set USERNAME '
660 'environment variable'))
660 'environment variable'))
661
661
662 def username(uid=None):
662 def username(uid=None):
663 """Return the name of the user with the given uid.
663 """Return the name of the user with the given uid.
664
664
665 If uid is None, return the name of the current user."""
665 If uid is None, return the name of the current user."""
666 try:
666 try:
667 import pwd
667 import pwd
668 if uid is None:
668 if uid is None:
669 uid = os.getuid()
669 uid = os.getuid()
670 try:
670 try:
671 return pwd.getpwuid(uid)[0]
671 return pwd.getpwuid(uid)[0]
672 except KeyError:
672 except KeyError:
673 return str(uid)
673 return str(uid)
674 except ImportError:
674 except ImportError:
675 return None
675 return None
676
676
677 def groupname(gid=None):
677 def groupname(gid=None):
678 """Return the name of the group with the given gid.
678 """Return the name of the group with the given gid.
679
679
680 If gid is None, return the name of the current group."""
680 If gid is None, return the name of the current group."""
681 try:
681 try:
682 import grp
682 import grp
683 if gid is None:
683 if gid is None:
684 gid = os.getgid()
684 gid = os.getgid()
685 try:
685 try:
686 return grp.getgrgid(gid)[0]
686 return grp.getgrgid(gid)[0]
687 except KeyError:
687 except KeyError:
688 return str(gid)
688 return str(gid)
689 except ImportError:
689 except ImportError:
690 return None
690 return None
691
691
692 # File system features
692 # File system features
693
693
694 def checkfolding(path):
694 def checkfolding(path):
695 """
695 """
696 Check whether the given path is on a case-sensitive filesystem
696 Check whether the given path is on a case-sensitive filesystem
697
697
698 Requires a path (like /foo/.hg) ending with a foldable final
698 Requires a path (like /foo/.hg) ending with a foldable final
699 directory component.
699 directory component.
700 """
700 """
701 s1 = os.stat(path)
701 s1 = os.stat(path)
702 d, b = os.path.split(path)
702 d, b = os.path.split(path)
703 p2 = os.path.join(d, b.upper())
703 p2 = os.path.join(d, b.upper())
704 if path == p2:
704 if path == p2:
705 p2 = os.path.join(d, b.lower())
705 p2 = os.path.join(d, b.lower())
706 try:
706 try:
707 s2 = os.stat(p2)
707 s2 = os.stat(p2)
708 if s2 == s1:
708 if s2 == s1:
709 return False
709 return False
710 return True
710 return True
711 except:
711 except:
712 return True
712 return True
713
713
714 def checkexec(path):
714 def checkexec(path):
715 """
715 """
716 Check whether the given path is on a filesystem with UNIX-like exec flags
716 Check whether the given path is on a filesystem with UNIX-like exec flags
717
717
718 Requires a directory (like /foo/.hg)
718 Requires a directory (like /foo/.hg)
719 """
719 """
720 fh, fn = tempfile.mkstemp("", "", path)
720 fh, fn = tempfile.mkstemp("", "", path)
721 os.close(fh)
721 os.close(fh)
722 m = os.stat(fn).st_mode
722 m = os.stat(fn).st_mode
723 os.chmod(fn, m ^ 0111)
723 os.chmod(fn, m ^ 0111)
724 r = (os.stat(fn).st_mode != m)
724 r = (os.stat(fn).st_mode != m)
725 os.unlink(fn)
725 os.unlink(fn)
726 return r
726 return r
727
727
728 def execfunc(path, fallback):
728 def execfunc(path, fallback):
729 '''return an is_exec() function with default to fallback'''
729 '''return an is_exec() function with default to fallback'''
730 if checkexec(path):
730 if checkexec(path):
731 return lambda x: is_exec(os.path.join(path, x))
731 return lambda x: is_exec(os.path.join(path, x))
732 return fallback
732 return fallback
733
733
734 def checklink(path):
734 def checklink(path):
735 """check whether the given path is on a symlink-capable filesystem"""
735 """check whether the given path is on a symlink-capable filesystem"""
736 # mktemp is not racy because symlink creation will fail if the
736 # mktemp is not racy because symlink creation will fail if the
737 # file already exists
737 # file already exists
738 name = tempfile.mktemp(dir=path)
738 name = tempfile.mktemp(dir=path)
739 try:
739 try:
740 os.symlink(".", name)
740 os.symlink(".", name)
741 os.unlink(name)
741 os.unlink(name)
742 return True
742 return True
743 except (OSError, AttributeError):
743 except (OSError, AttributeError):
744 return False
744 return False
745
745
746 def linkfunc(path, fallback):
746 def linkfunc(path, fallback):
747 '''return an is_link() function with default to fallback'''
747 '''return an is_link() function with default to fallback'''
748 if checklink(path):
748 if checklink(path):
749 return lambda x: is_link(os.path.join(path, x))
749 return lambda x: is_link(os.path.join(path, x))
750 return fallback
750 return fallback
751
751
752 # Platform specific variants
752 # Platform specific variants
753 if os.name == 'nt':
753 if os.name == 'nt':
754 import msvcrt
754 import msvcrt
755 nulldev = 'NUL:'
755 nulldev = 'NUL:'
756
756
757 class winstdout:
757 class winstdout:
758 '''stdout on windows misbehaves if sent through a pipe'''
758 '''stdout on windows misbehaves if sent through a pipe'''
759
759
760 def __init__(self, fp):
760 def __init__(self, fp):
761 self.fp = fp
761 self.fp = fp
762
762
763 def __getattr__(self, key):
763 def __getattr__(self, key):
764 return getattr(self.fp, key)
764 return getattr(self.fp, key)
765
765
766 def close(self):
766 def close(self):
767 try:
767 try:
768 self.fp.close()
768 self.fp.close()
769 except: pass
769 except: pass
770
770
771 def write(self, s):
771 def write(self, s):
772 try:
772 try:
773 return self.fp.write(s)
773 return self.fp.write(s)
774 except IOError, inst:
774 except IOError, inst:
775 if inst.errno != 0: raise
775 if inst.errno != 0: raise
776 self.close()
776 self.close()
777 raise IOError(errno.EPIPE, 'Broken pipe')
777 raise IOError(errno.EPIPE, 'Broken pipe')
778
778
779 sys.stdout = winstdout(sys.stdout)
779 sys.stdout = winstdout(sys.stdout)
780
780
781 def system_rcpath():
781 def system_rcpath():
782 try:
782 try:
783 return system_rcpath_win32()
783 return system_rcpath_win32()
784 except:
784 except:
785 return [r'c:\mercurial\mercurial.ini']
785 return [r'c:\mercurial\mercurial.ini']
786
786
787 def os_rcpath():
787 def os_rcpath():
788 '''return default os-specific hgrc search path'''
788 '''return default os-specific hgrc search path'''
789 path = system_rcpath()
789 path = system_rcpath()
790 path.append(user_rcpath())
790 path.append(user_rcpath())
791 userprofile = os.environ.get('USERPROFILE')
791 userprofile = os.environ.get('USERPROFILE')
792 if userprofile:
792 if userprofile:
793 path.append(os.path.join(userprofile, 'mercurial.ini'))
793 path.append(os.path.join(userprofile, 'mercurial.ini'))
794 return path
794 return path
795
795
796 def user_rcpath():
796 def user_rcpath():
797 '''return os-specific hgrc search path to the user dir'''
797 '''return os-specific hgrc search path to the user dir'''
798 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
798 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
799
799
800 def parse_patch_output(output_line):
800 def parse_patch_output(output_line):
801 """parses the output produced by patch and returns the file name"""
801 """parses the output produced by patch and returns the file name"""
802 pf = output_line[14:]
802 pf = output_line[14:]
803 if pf[0] == '`':
803 if pf[0] == '`':
804 pf = pf[1:-1] # Remove the quotes
804 pf = pf[1:-1] # Remove the quotes
805 return pf
805 return pf
806
806
807 def testpid(pid):
807 def testpid(pid):
808 '''return False if pid dead, True if running or not known'''
808 '''return False if pid dead, True if running or not known'''
809 return True
809 return True
810
810
811 def set_exec(f, mode):
811 def set_exec(f, mode):
812 pass
812 pass
813
813
814 def set_link(f, mode):
814 def set_link(f, mode):
815 pass
815 pass
816
816
817 def set_binary(fd):
817 def set_binary(fd):
818 msvcrt.setmode(fd.fileno(), os.O_BINARY)
818 msvcrt.setmode(fd.fileno(), os.O_BINARY)
819
819
820 def pconvert(path):
820 def pconvert(path):
821 return path.replace("\\", "/")
821 return path.replace("\\", "/")
822
822
823 def localpath(path):
823 def localpath(path):
824 return path.replace('/', '\\')
824 return path.replace('/', '\\')
825
825
826 def normpath(path):
826 def normpath(path):
827 return pconvert(os.path.normpath(path))
827 return pconvert(os.path.normpath(path))
828
828
829 makelock = _makelock_file
829 makelock = _makelock_file
830 readlock = _readlock_file
830 readlock = _readlock_file
831
831
832 def samestat(s1, s2):
832 def samestat(s1, s2):
833 return False
833 return False
834
834
835 def shellquote(s):
835 def shellquote(s):
836 return '"%s"' % s.replace('"', '\\"')
836 return '"%s"' % s.replace('"', '\\"')
837
837
838 def explain_exit(code):
838 def explain_exit(code):
839 return _("exited with status %d") % code, code
839 return _("exited with status %d") % code, code
840
840
841 # if you change this stub into a real check, please try to implement the
841 # if you change this stub into a real check, please try to implement the
842 # username and groupname functions above, too.
842 # username and groupname functions above, too.
843 def isowner(fp, st=None):
843 def isowner(fp, st=None):
844 return True
844 return True
845
845
846 try:
846 try:
847 # override functions with win32 versions if possible
847 # override functions with win32 versions if possible
848 from util_win32 import *
848 from util_win32 import *
849 if not is_win_9x():
849 if not is_win_9x():
850 posixfile = posixfile_nt
850 posixfile = posixfile_nt
851 except ImportError:
851 except ImportError:
852 pass
852 pass
853
853
854 else:
854 else:
855 nulldev = '/dev/null'
855 nulldev = '/dev/null'
856 _umask = os.umask(0)
856 _umask = os.umask(0)
857 os.umask(_umask)
857 os.umask(_umask)
858
858
859 def rcfiles(path):
859 def rcfiles(path):
860 rcs = [os.path.join(path, 'hgrc')]
860 rcs = [os.path.join(path, 'hgrc')]
861 rcdir = os.path.join(path, 'hgrc.d')
861 rcdir = os.path.join(path, 'hgrc.d')
862 try:
862 try:
863 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
863 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
864 if f.endswith(".rc")])
864 if f.endswith(".rc")])
865 except OSError:
865 except OSError:
866 pass
866 pass
867 return rcs
867 return rcs
868
868
869 def os_rcpath():
869 def os_rcpath():
870 '''return default os-specific hgrc search path'''
870 '''return default os-specific hgrc search path'''
871 path = []
871 path = []
872 # old mod_python does not set sys.argv
872 # old mod_python does not set sys.argv
873 if len(getattr(sys, 'argv', [])) > 0:
873 if len(getattr(sys, 'argv', [])) > 0:
874 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
874 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
875 '/../etc/mercurial'))
875 '/../etc/mercurial'))
876 path.extend(rcfiles('/etc/mercurial'))
876 path.extend(rcfiles('/etc/mercurial'))
877 path.append(os.path.expanduser('~/.hgrc'))
877 path.append(os.path.expanduser('~/.hgrc'))
878 path = [os.path.normpath(f) for f in path]
878 path = [os.path.normpath(f) for f in path]
879 return path
879 return path
880
880
881 def parse_patch_output(output_line):
881 def parse_patch_output(output_line):
882 """parses the output produced by patch and returns the file name"""
882 """parses the output produced by patch and returns the file name"""
883 pf = output_line[14:]
883 pf = output_line[14:]
884 if pf.startswith("'") and pf.endswith("'") and " " in pf:
884 if pf.startswith("'") and pf.endswith("'") and " " in pf:
885 pf = pf[1:-1] # Remove the quotes
885 pf = pf[1:-1] # Remove the quotes
886 return pf
886 return pf
887
887
888 def is_exec(f):
888 def is_exec(f):
889 """check whether a file is executable"""
889 """check whether a file is executable"""
890 return (os.lstat(f).st_mode & 0100 != 0)
890 return (os.lstat(f).st_mode & 0100 != 0)
891
891
892 def set_exec(f, mode):
892 def set_exec(f, mode):
893 s = os.lstat(f).st_mode
893 s = os.lstat(f).st_mode
894 if (s & 0100 != 0) == mode:
894 if (s & 0100 != 0) == mode:
895 return
895 return
896 if mode:
896 if mode:
897 # Turn on +x for every +r bit when making a file executable
897 # Turn on +x for every +r bit when making a file executable
898 # and obey umask.
898 # and obey umask.
899 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
899 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
900 else:
900 else:
901 os.chmod(f, s & 0666)
901 os.chmod(f, s & 0666)
902
902
903 def is_link(f):
903 def is_link(f):
904 """check whether a file is a symlink"""
904 """check whether a file is a symlink"""
905 return (os.lstat(f).st_mode & 0120000 == 0120000)
905 return (os.lstat(f).st_mode & 0120000 == 0120000)
906
906
907 def set_link(f, mode):
907 def set_link(f, mode):
908 """make a file a symbolic link/regular file
908 """make a file a symbolic link/regular file
909
909
910 if a file is changed to a link, its contents become the link data
910 if a file is changed to a link, its contents become the link data
911 if a link is changed to a file, its link data become its contents
911 if a link is changed to a file, its link data become its contents
912 """
912 """
913
913
914 m = is_link(f)
914 m = is_link(f)
915 if m == bool(mode):
915 if m == bool(mode):
916 return
916 return
917
917
918 if mode: # switch file to link
918 if mode: # switch file to link
919 data = file(f).read()
919 data = file(f).read()
920 os.unlink(f)
920 os.unlink(f)
921 os.symlink(data, f)
921 os.symlink(data, f)
922 else:
922 else:
923 data = os.readlink(f)
923 data = os.readlink(f)
924 os.unlink(f)
924 os.unlink(f)
925 file(f, "w").write(data)
925 file(f, "w").write(data)
926
926
927 def set_binary(fd):
927 def set_binary(fd):
928 pass
928 pass
929
929
930 def pconvert(path):
930 def pconvert(path):
931 return path
931 return path
932
932
933 def localpath(path):
933 def localpath(path):
934 return path
934 return path
935
935
936 normpath = os.path.normpath
936 normpath = os.path.normpath
937 samestat = os.path.samestat
937 samestat = os.path.samestat
938
938
939 def makelock(info, pathname):
939 def makelock(info, pathname):
940 try:
940 try:
941 os.symlink(info, pathname)
941 os.symlink(info, pathname)
942 except OSError, why:
942 except OSError, why:
943 if why.errno == errno.EEXIST:
943 if why.errno == errno.EEXIST:
944 raise
944 raise
945 else:
945 else:
946 _makelock_file(info, pathname)
946 _makelock_file(info, pathname)
947
947
948 def readlock(pathname):
948 def readlock(pathname):
949 try:
949 try:
950 return os.readlink(pathname)
950 return os.readlink(pathname)
951 except OSError, why:
951 except OSError, why:
952 if why.errno == errno.EINVAL:
952 if why.errno == errno.EINVAL:
953 return _readlock_file(pathname)
953 return _readlock_file(pathname)
954 else:
954 else:
955 raise
955 raise
956
956
957 def shellquote(s):
957 def shellquote(s):
958 return "'%s'" % s.replace("'", "'\\''")
958 return "'%s'" % s.replace("'", "'\\''")
959
959
960 def testpid(pid):
960 def testpid(pid):
961 '''return False if pid dead, True if running or not sure'''
961 '''return False if pid dead, True if running or not sure'''
962 try:
962 try:
963 os.kill(pid, 0)
963 os.kill(pid, 0)
964 return True
964 return True
965 except OSError, inst:
965 except OSError, inst:
966 return inst.errno != errno.ESRCH
966 return inst.errno != errno.ESRCH
967
967
968 def explain_exit(code):
968 def explain_exit(code):
969 """return a 2-tuple (desc, code) describing a process's status"""
969 """return a 2-tuple (desc, code) describing a process's status"""
970 if os.WIFEXITED(code):
970 if os.WIFEXITED(code):
971 val = os.WEXITSTATUS(code)
971 val = os.WEXITSTATUS(code)
972 return _("exited with status %d") % val, val
972 return _("exited with status %d") % val, val
973 elif os.WIFSIGNALED(code):
973 elif os.WIFSIGNALED(code):
974 val = os.WTERMSIG(code)
974 val = os.WTERMSIG(code)
975 return _("killed by signal %d") % val, val
975 return _("killed by signal %d") % val, val
976 elif os.WIFSTOPPED(code):
976 elif os.WIFSTOPPED(code):
977 val = os.WSTOPSIG(code)
977 val = os.WSTOPSIG(code)
978 return _("stopped by signal %d") % val, val
978 return _("stopped by signal %d") % val, val
979 raise ValueError(_("invalid exit code"))
979 raise ValueError(_("invalid exit code"))
980
980
981 def isowner(fp, st=None):
981 def isowner(fp, st=None):
982 """Return True if the file object f belongs to the current user.
982 """Return True if the file object f belongs to the current user.
983
983
984 The return value of a util.fstat(f) may be passed as the st argument.
984 The return value of a util.fstat(f) may be passed as the st argument.
985 """
985 """
986 if st is None:
986 if st is None:
987 st = fstat(fp)
987 st = fstat(fp)
988 return st.st_uid == os.getuid()
988 return st.st_uid == os.getuid()
989
989
990 def _buildencodefun():
990 def _buildencodefun():
991 e = '_'
991 e = '_'
992 win_reserved = [ord(x) for x in '\\:*?"<>|']
992 win_reserved = [ord(x) for x in '\\:*?"<>|']
993 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
993 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
994 for x in (range(32) + range(126, 256) + win_reserved):
994 for x in (range(32) + range(126, 256) + win_reserved):
995 cmap[chr(x)] = "~%02x" % x
995 cmap[chr(x)] = "~%02x" % x
996 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
996 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
997 cmap[chr(x)] = e + chr(x).lower()
997 cmap[chr(x)] = e + chr(x).lower()
998 dmap = {}
998 dmap = {}
999 for k, v in cmap.iteritems():
999 for k, v in cmap.iteritems():
1000 dmap[v] = k
1000 dmap[v] = k
1001 def decode(s):
1001 def decode(s):
1002 i = 0
1002 i = 0
1003 while i < len(s):
1003 while i < len(s):
1004 for l in xrange(1, 4):
1004 for l in xrange(1, 4):
1005 try:
1005 try:
1006 yield dmap[s[i:i+l]]
1006 yield dmap[s[i:i+l]]
1007 i += l
1007 i += l
1008 break
1008 break
1009 except KeyError:
1009 except KeyError:
1010 pass
1010 pass
1011 else:
1011 else:
1012 raise KeyError
1012 raise KeyError
1013 return (lambda s: "".join([cmap[c] for c in s]),
1013 return (lambda s: "".join([cmap[c] for c in s]),
1014 lambda s: "".join(list(decode(s))))
1014 lambda s: "".join(list(decode(s))))
1015
1015
1016 encodefilename, decodefilename = _buildencodefun()
1016 encodefilename, decodefilename = _buildencodefun()
1017
1017
1018 def encodedopener(openerfn, fn):
1018 def encodedopener(openerfn, fn):
1019 def o(path, *args, **kw):
1019 def o(path, *args, **kw):
1020 return openerfn(fn(path), *args, **kw)
1020 return openerfn(fn(path), *args, **kw)
1021 return o
1021 return o
1022
1022
1023 def opener(base, audit=True):
1023 def opener(base, audit=True):
1024 """
1024 """
1025 return a function that opens files relative to base
1025 return a function that opens files relative to base
1026
1026
1027 this function is used to hide the details of COW semantics and
1027 this function is used to hide the details of COW semantics and
1028 remote file access from higher level code.
1028 remote file access from higher level code.
1029 """
1029 """
1030 p = base
1030 p = base
1031 audit_p = audit
1031 audit_p = audit
1032
1032
1033 def mktempcopy(name):
1033 def mktempcopy(name):
1034 d, fn = os.path.split(name)
1034 d, fn = os.path.split(name)
1035 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1035 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1036 os.close(fd)
1036 os.close(fd)
1037 ofp = posixfile(temp, "wb")
1037 ofp = posixfile(temp, "wb")
1038 try:
1038 try:
1039 try:
1039 try:
1040 ifp = posixfile(name, "rb")
1040 ifp = posixfile(name, "rb")
1041 except IOError, inst:
1041 except IOError, inst:
1042 if not getattr(inst, 'filename', None):
1042 if not getattr(inst, 'filename', None):
1043 inst.filename = name
1043 inst.filename = name
1044 raise
1044 raise
1045 for chunk in filechunkiter(ifp):
1045 for chunk in filechunkiter(ifp):
1046 ofp.write(chunk)
1046 ofp.write(chunk)
1047 ifp.close()
1047 ifp.close()
1048 ofp.close()
1048 ofp.close()
1049 except:
1049 except:
1050 try: os.unlink(temp)
1050 try: os.unlink(temp)
1051 except: pass
1051 except: pass
1052 raise
1052 raise
1053 st = os.lstat(name)
1053 st = os.lstat(name)
1054 os.chmod(temp, st.st_mode)
1054 os.chmod(temp, st.st_mode)
1055 return temp
1055 return temp
1056
1056
1057 class atomictempfile(posixfile):
1057 class atomictempfile(posixfile):
1058 """the file will only be copied when rename is called"""
1058 """the file will only be copied when rename is called"""
1059 def __init__(self, name, mode):
1059 def __init__(self, name, mode):
1060 self.__name = name
1060 self.__name = name
1061 self.temp = mktempcopy(name)
1061 self.temp = mktempcopy(name)
1062 posixfile.__init__(self, self.temp, mode)
1062 posixfile.__init__(self, self.temp, mode)
1063 def rename(self):
1063 def rename(self):
1064 if not self.closed:
1064 if not self.closed:
1065 posixfile.close(self)
1065 posixfile.close(self)
1066 rename(self.temp, localpath(self.__name))
1066 rename(self.temp, localpath(self.__name))
1067 def __del__(self):
1067 def __del__(self):
1068 if not self.closed:
1068 if not self.closed:
1069 try:
1069 try:
1070 os.unlink(self.temp)
1070 os.unlink(self.temp)
1071 except: pass
1071 except: pass
1072 posixfile.close(self)
1072 posixfile.close(self)
1073
1073
1074 class atomicfile(atomictempfile):
1074 class atomicfile(atomictempfile):
1075 """the file will only be copied on close"""
1075 """the file will only be copied on close"""
1076 def __init__(self, name, mode):
1076 def __init__(self, name, mode):
1077 atomictempfile.__init__(self, name, mode)
1077 atomictempfile.__init__(self, name, mode)
1078 def close(self):
1078 def close(self):
1079 self.rename()
1079 self.rename()
1080 def __del__(self):
1080 def __del__(self):
1081 self.rename()
1081 self.rename()
1082
1082
1083 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
1083 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
1084 if audit_p:
1084 if audit_p:
1085 audit_path(path)
1085 audit_path(path)
1086 f = os.path.join(p, path)
1086 f = os.path.join(p, path)
1087
1087
1088 if not text:
1088 if not text:
1089 mode += "b" # for that other OS
1089 mode += "b" # for that other OS
1090
1090
1091 if mode[0] != "r":
1091 if mode[0] != "r":
1092 try:
1092 try:
1093 nlink = nlinks(f)
1093 nlink = nlinks(f)
1094 except OSError:
1094 except OSError:
1095 d = os.path.dirname(f)
1095 d = os.path.dirname(f)
1096 if not os.path.isdir(d):
1096 if not os.path.isdir(d):
1097 os.makedirs(d)
1097 os.makedirs(d)
1098 else:
1098 else:
1099 if atomic:
1099 if atomic:
1100 return atomicfile(f, mode)
1100 return atomicfile(f, mode)
1101 elif atomictemp:
1101 elif atomictemp:
1102 return atomictempfile(f, mode)
1102 return atomictempfile(f, mode)
1103 if nlink > 1:
1103 if nlink > 1:
1104 rename(mktempcopy(f), f)
1104 rename(mktempcopy(f), f)
1105 return posixfile(f, mode)
1105 return posixfile(f, mode)
1106
1106
1107 return o
1107 return o
1108
1108
1109 class chunkbuffer(object):
1109 class chunkbuffer(object):
1110 """Allow arbitrary sized chunks of data to be efficiently read from an
1110 """Allow arbitrary sized chunks of data to be efficiently read from an
1111 iterator over chunks of arbitrary size."""
1111 iterator over chunks of arbitrary size."""
1112
1112
1113 def __init__(self, in_iter, targetsize = 2**16):
1113 def __init__(self, in_iter, targetsize = 2**16):
1114 """in_iter is the iterator that's iterating over the input chunks.
1114 """in_iter is the iterator that's iterating over the input chunks.
1115 targetsize is how big a buffer to try to maintain."""
1115 targetsize is how big a buffer to try to maintain."""
1116 self.in_iter = iter(in_iter)
1116 self.in_iter = iter(in_iter)
1117 self.buf = ''
1117 self.buf = ''
1118 self.targetsize = int(targetsize)
1118 self.targetsize = int(targetsize)
1119 if self.targetsize <= 0:
1119 if self.targetsize <= 0:
1120 raise ValueError(_("targetsize must be greater than 0, was %d") %
1120 raise ValueError(_("targetsize must be greater than 0, was %d") %
1121 targetsize)
1121 targetsize)
1122 self.iterempty = False
1122 self.iterempty = False
1123
1123
1124 def fillbuf(self):
1124 def fillbuf(self):
1125 """Ignore target size; read every chunk from iterator until empty."""
1125 """Ignore target size; read every chunk from iterator until empty."""
1126 if not self.iterempty:
1126 if not self.iterempty:
1127 collector = cStringIO.StringIO()
1127 collector = cStringIO.StringIO()
1128 collector.write(self.buf)
1128 collector.write(self.buf)
1129 for ch in self.in_iter:
1129 for ch in self.in_iter:
1130 collector.write(ch)
1130 collector.write(ch)
1131 self.buf = collector.getvalue()
1131 self.buf = collector.getvalue()
1132 self.iterempty = True
1132 self.iterempty = True
1133
1133
1134 def read(self, l):
1134 def read(self, l):
1135 """Read L bytes of data from the iterator of chunks of data.
1135 """Read L bytes of data from the iterator of chunks of data.
1136 Returns less than L bytes if the iterator runs dry."""
1136 Returns less than L bytes if the iterator runs dry."""
1137 if l > len(self.buf) and not self.iterempty:
1137 if l > len(self.buf) and not self.iterempty:
1138 # Clamp to a multiple of self.targetsize
1138 # Clamp to a multiple of self.targetsize
1139 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1139 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1140 collector = cStringIO.StringIO()
1140 collector = cStringIO.StringIO()
1141 collector.write(self.buf)
1141 collector.write(self.buf)
1142 collected = len(self.buf)
1142 collected = len(self.buf)
1143 for chunk in self.in_iter:
1143 for chunk in self.in_iter:
1144 collector.write(chunk)
1144 collector.write(chunk)
1145 collected += len(chunk)
1145 collected += len(chunk)
1146 if collected >= targetsize:
1146 if collected >= targetsize:
1147 break
1147 break
1148 if collected < targetsize:
1148 if collected < targetsize:
1149 self.iterempty = True
1149 self.iterempty = True
1150 self.buf = collector.getvalue()
1150 self.buf = collector.getvalue()
1151 s, self.buf = self.buf[:l], buffer(self.buf, l)
1151 s, self.buf = self.buf[:l], buffer(self.buf, l)
1152 return s
1152 return s
1153
1153
1154 def filechunkiter(f, size=65536, limit=None):
1154 def filechunkiter(f, size=65536, limit=None):
1155 """Create a generator that produces the data in the file size
1155 """Create a generator that produces the data in the file size
1156 (default 65536) bytes at a time, up to optional limit (default is
1156 (default 65536) bytes at a time, up to optional limit (default is
1157 to read all data). Chunks may be less than size bytes if the
1157 to read all data). Chunks may be less than size bytes if the
1158 chunk is the last chunk in the file, or the file is a socket or
1158 chunk is the last chunk in the file, or the file is a socket or
1159 some other type of file that sometimes reads less data than is
1159 some other type of file that sometimes reads less data than is
1160 requested."""
1160 requested."""
1161 assert size >= 0
1161 assert size >= 0
1162 assert limit is None or limit >= 0
1162 assert limit is None or limit >= 0
1163 while True:
1163 while True:
1164 if limit is None: nbytes = size
1164 if limit is None: nbytes = size
1165 else: nbytes = min(limit, size)
1165 else: nbytes = min(limit, size)
1166 s = nbytes and f.read(nbytes)
1166 s = nbytes and f.read(nbytes)
1167 if not s: break
1167 if not s: break
1168 if limit: limit -= len(s)
1168 if limit: limit -= len(s)
1169 yield s
1169 yield s
1170
1170
1171 def makedate():
1171 def makedate():
1172 lt = time.localtime()
1172 lt = time.localtime()
1173 if lt[8] == 1 and time.daylight:
1173 if lt[8] == 1 and time.daylight:
1174 tz = time.altzone
1174 tz = time.altzone
1175 else:
1175 else:
1176 tz = time.timezone
1176 tz = time.timezone
1177 return time.mktime(lt), tz
1177 return time.mktime(lt), tz
1178
1178
1179 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1179 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1180 """represent a (unixtime, offset) tuple as a localized time.
1180 """represent a (unixtime, offset) tuple as a localized time.
1181 unixtime is seconds since the epoch, and offset is the time zone's
1181 unixtime is seconds since the epoch, and offset is the time zone's
1182 number of seconds away from UTC. if timezone is false, do not
1182 number of seconds away from UTC. if timezone is false, do not
1183 append time zone to string."""
1183 append time zone to string."""
1184 t, tz = date or makedate()
1184 t, tz = date or makedate()
1185 s = time.strftime(format, time.gmtime(float(t) - tz))
1185 s = time.strftime(format, time.gmtime(float(t) - tz))
1186 if timezone:
1186 if timezone:
1187 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1187 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1188 return s
1188 return s
1189
1189
1190 def strdate(string, format, defaults):
1190 def strdate(string, format, defaults):
1191 """parse a localized time string and return a (unixtime, offset) tuple.
1191 """parse a localized time string and return a (unixtime, offset) tuple.
1192 if the string cannot be parsed, ValueError is raised."""
1192 if the string cannot be parsed, ValueError is raised."""
1193 def timezone(string):
1193 def timezone(string):
1194 tz = string.split()[-1]
1194 tz = string.split()[-1]
1195 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1195 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1196 tz = int(tz)
1196 tz = int(tz)
1197 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1197 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1198 return offset
1198 return offset
1199 if tz == "GMT" or tz == "UTC":
1199 if tz == "GMT" or tz == "UTC":
1200 return 0
1200 return 0
1201 return None
1201 return None
1202
1202
1203 # NOTE: unixtime = localunixtime + offset
1203 # NOTE: unixtime = localunixtime + offset
1204 offset, date = timezone(string), string
1204 offset, date = timezone(string), string
1205 if offset != None:
1205 if offset != None:
1206 date = " ".join(string.split()[:-1])
1206 date = " ".join(string.split()[:-1])
1207
1207
1208 # add missing elements from defaults
1208 # add missing elements from defaults
1209 for part in defaults:
1209 for part in defaults:
1210 found = [True for p in part if ("%"+p) in format]
1210 found = [True for p in part if ("%"+p) in format]
1211 if not found:
1211 if not found:
1212 date += "@" + defaults[part]
1212 date += "@" + defaults[part]
1213 format += "@%" + part[0]
1213 format += "@%" + part[0]
1214
1214
1215 timetuple = time.strptime(date, format)
1215 timetuple = time.strptime(date, format)
1216 localunixtime = int(calendar.timegm(timetuple))
1216 localunixtime = int(calendar.timegm(timetuple))
1217 if offset is None:
1217 if offset is None:
1218 # local timezone
1218 # local timezone
1219 unixtime = int(time.mktime(timetuple))
1219 unixtime = int(time.mktime(timetuple))
1220 offset = unixtime - localunixtime
1220 offset = unixtime - localunixtime
1221 else:
1221 else:
1222 unixtime = localunixtime + offset
1222 unixtime = localunixtime + offset
1223 return unixtime, offset
1223 return unixtime, offset
1224
1224
1225 def parsedate(string, formats=None, defaults=None):
1225 def parsedate(string, formats=None, defaults=None):
1226 """parse a localized time string and return a (unixtime, offset) tuple.
1226 """parse a localized time string and return a (unixtime, offset) tuple.
1227 The date may be a "unixtime offset" string or in one of the specified
1227 The date may be a "unixtime offset" string or in one of the specified
1228 formats."""
1228 formats."""
1229 if not string:
1229 if not string:
1230 return 0, 0
1230 return 0, 0
1231 if not formats:
1231 if not formats:
1232 formats = defaultdateformats
1232 formats = defaultdateformats
1233 string = string.strip()
1233 string = string.strip()
1234 try:
1234 try:
1235 when, offset = map(int, string.split(' '))
1235 when, offset = map(int, string.split(' '))
1236 except ValueError:
1236 except ValueError:
1237 # fill out defaults
1237 # fill out defaults
1238 if not defaults:
1238 if not defaults:
1239 defaults = {}
1239 defaults = {}
1240 now = makedate()
1240 now = makedate()
1241 for part in "d mb yY HI M S".split():
1241 for part in "d mb yY HI M S".split():
1242 if part not in defaults:
1242 if part not in defaults:
1243 if part[0] in "HMS":
1243 if part[0] in "HMS":
1244 defaults[part] = "00"
1244 defaults[part] = "00"
1245 elif part[0] in "dm":
1245 elif part[0] in "dm":
1246 defaults[part] = "1"
1246 defaults[part] = "1"
1247 else:
1247 else:
1248 defaults[part] = datestr(now, "%" + part[0], False)
1248 defaults[part] = datestr(now, "%" + part[0], False)
1249
1249
1250 for format in formats:
1250 for format in formats:
1251 try:
1251 try:
1252 when, offset = strdate(string, format, defaults)
1252 when, offset = strdate(string, format, defaults)
1253 except ValueError:
1253 except ValueError:
1254 pass
1254 pass
1255 else:
1255 else:
1256 break
1256 break
1257 else:
1257 else:
1258 raise Abort(_('invalid date: %r ') % string)
1258 raise Abort(_('invalid date: %r ') % string)
1259 # validate explicit (probably user-specified) date and
1259 # validate explicit (probably user-specified) date and
1260 # time zone offset. values must fit in signed 32 bits for
1260 # time zone offset. values must fit in signed 32 bits for
1261 # current 32-bit linux runtimes. timezones go from UTC-12
1261 # current 32-bit linux runtimes. timezones go from UTC-12
1262 # to UTC+14
1262 # to UTC+14
1263 if abs(when) > 0x7fffffff:
1263 if abs(when) > 0x7fffffff:
1264 raise Abort(_('date exceeds 32 bits: %d') % when)
1264 raise Abort(_('date exceeds 32 bits: %d') % when)
1265 if offset < -50400 or offset > 43200:
1265 if offset < -50400 or offset > 43200:
1266 raise Abort(_('impossible time zone offset: %d') % offset)
1266 raise Abort(_('impossible time zone offset: %d') % offset)
1267 return when, offset
1267 return when, offset
1268
1268
1269 def matchdate(date):
1269 def matchdate(date):
1270 """Return a function that matches a given date match specifier
1270 """Return a function that matches a given date match specifier
1271
1271
1272 Formats include:
1272 Formats include:
1273
1273
1274 '{date}' match a given date to the accuracy provided
1274 '{date}' match a given date to the accuracy provided
1275
1275
1276 '<{date}' on or before a given date
1276 '<{date}' on or before a given date
1277
1277
1278 '>{date}' on or after a given date
1278 '>{date}' on or after a given date
1279
1279
1280 """
1280 """
1281
1281
1282 def lower(date):
1282 def lower(date):
1283 return parsedate(date, extendeddateformats)[0]
1283 return parsedate(date, extendeddateformats)[0]
1284
1284
1285 def upper(date):
1285 def upper(date):
1286 d = dict(mb="12", HI="23", M="59", S="59")
1286 d = dict(mb="12", HI="23", M="59", S="59")
1287 for days in "31 30 29".split():
1287 for days in "31 30 29".split():
1288 try:
1288 try:
1289 d["d"] = days
1289 d["d"] = days
1290 return parsedate(date, extendeddateformats, d)[0]
1290 return parsedate(date, extendeddateformats, d)[0]
1291 except:
1291 except:
1292 pass
1292 pass
1293 d["d"] = "28"
1293 d["d"] = "28"
1294 return parsedate(date, extendeddateformats, d)[0]
1294 return parsedate(date, extendeddateformats, d)[0]
1295
1295
1296 if date[0] == "<":
1296 if date[0] == "<":
1297 when = upper(date[1:])
1297 when = upper(date[1:])
1298 return lambda x: x <= when
1298 return lambda x: x <= when
1299 elif date[0] == ">":
1299 elif date[0] == ">":
1300 when = lower(date[1:])
1300 when = lower(date[1:])
1301 return lambda x: x >= when
1301 return lambda x: x >= when
1302 elif date[0] == "-":
1302 elif date[0] == "-":
1303 try:
1303 try:
1304 days = int(date[1:])
1304 days = int(date[1:])
1305 except ValueError:
1305 except ValueError:
1306 raise Abort(_("invalid day spec: %s") % date[1:])
1306 raise Abort(_("invalid day spec: %s") % date[1:])
1307 when = makedate()[0] - days * 3600 * 24
1307 when = makedate()[0] - days * 3600 * 24
1308 return lambda x: x >= when
1308 return lambda x: x >= when
1309 elif " to " in date:
1309 elif " to " in date:
1310 a, b = date.split(" to ")
1310 a, b = date.split(" to ")
1311 start, stop = lower(a), upper(b)
1311 start, stop = lower(a), upper(b)
1312 return lambda x: x >= start and x <= stop
1312 return lambda x: x >= start and x <= stop
1313 else:
1313 else:
1314 start, stop = lower(date), upper(date)
1314 start, stop = lower(date), upper(date)
1315 return lambda x: x >= start and x <= stop
1315 return lambda x: x >= start and x <= stop
1316
1316
1317 def shortuser(user):
1317 def shortuser(user):
1318 """Return a short representation of a user name or email address."""
1318 """Return a short representation of a user name or email address."""
1319 f = user.find('@')
1319 f = user.find('@')
1320 if f >= 0:
1320 if f >= 0:
1321 user = user[:f]
1321 user = user[:f]
1322 f = user.find('<')
1322 f = user.find('<')
1323 if f >= 0:
1323 if f >= 0:
1324 user = user[f+1:]
1324 user = user[f+1:]
1325 f = user.find(' ')
1325 f = user.find(' ')
1326 if f >= 0:
1326 if f >= 0:
1327 user = user[:f]
1327 user = user[:f]
1328 f = user.find('.')
1328 f = user.find('.')
1329 if f >= 0:
1329 if f >= 0:
1330 user = user[:f]
1330 user = user[:f]
1331 return user
1331 return user
1332
1332
1333 def ellipsis(text, maxlength=400):
1333 def ellipsis(text, maxlength=400):
1334 """Trim string to at most maxlength (default: 400) characters."""
1334 """Trim string to at most maxlength (default: 400) characters."""
1335 if len(text) <= maxlength:
1335 if len(text) <= maxlength:
1336 return text
1336 return text
1337 else:
1337 else:
1338 return "%s..." % (text[:maxlength-3])
1338 return "%s..." % (text[:maxlength-3])
1339
1339
1340 def walkrepos(path):
1340 def walkrepos(path):
1341 '''yield every hg repository under path, recursively.'''
1341 '''yield every hg repository under path, recursively.'''
1342 def errhandler(err):
1342 def errhandler(err):
1343 if err.filename == path:
1343 if err.filename == path:
1344 raise err
1344 raise err
1345
1345
1346 for root, dirs, files in os.walk(path, onerror=errhandler):
1346 for root, dirs, files in os.walk(path, onerror=errhandler):
1347 for d in dirs:
1347 for d in dirs:
1348 if d == '.hg':
1348 if d == '.hg':
1349 yield root
1349 yield root
1350 dirs[:] = []
1350 dirs[:] = []
1351 break
1351 break
1352
1352
1353 _rcpath = None
1353 _rcpath = None
1354
1354
1355 def rcpath():
1355 def rcpath():
1356 '''return hgrc search path. if env var HGRCPATH is set, use it.
1356 '''return hgrc search path. if env var HGRCPATH is set, use it.
1357 for each item in path, if directory, use files ending in .rc,
1357 for each item in path, if directory, use files ending in .rc,
1358 else use item.
1358 else use item.
1359 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1359 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1360 if no HGRCPATH, use default os-specific path.'''
1360 if no HGRCPATH, use default os-specific path.'''
1361 global _rcpath
1361 global _rcpath
1362 if _rcpath is None:
1362 if _rcpath is None:
1363 if 'HGRCPATH' in os.environ:
1363 if 'HGRCPATH' in os.environ:
1364 _rcpath = []
1364 _rcpath = []
1365 for p in os.environ['HGRCPATH'].split(os.pathsep):
1365 for p in os.environ['HGRCPATH'].split(os.pathsep):
1366 if not p: continue
1366 if not p: continue
1367 if os.path.isdir(p):
1367 if os.path.isdir(p):
1368 for f in os.listdir(p):
1368 for f in os.listdir(p):
1369 if f.endswith('.rc'):
1369 if f.endswith('.rc'):
1370 _rcpath.append(os.path.join(p, f))
1370 _rcpath.append(os.path.join(p, f))
1371 else:
1371 else:
1372 _rcpath.append(p)
1372 _rcpath.append(p)
1373 else:
1373 else:
1374 _rcpath = os_rcpath()
1374 _rcpath = os_rcpath()
1375 return _rcpath
1375 return _rcpath
1376
1376
1377 def bytecount(nbytes):
1377 def bytecount(nbytes):
1378 '''return byte count formatted as readable string, with units'''
1378 '''return byte count formatted as readable string, with units'''
1379
1379
1380 units = (
1380 units = (
1381 (100, 1<<30, _('%.0f GB')),
1381 (100, 1<<30, _('%.0f GB')),
1382 (10, 1<<30, _('%.1f GB')),
1382 (10, 1<<30, _('%.1f GB')),
1383 (1, 1<<30, _('%.2f GB')),
1383 (1, 1<<30, _('%.2f GB')),
1384 (100, 1<<20, _('%.0f MB')),
1384 (100, 1<<20, _('%.0f MB')),
1385 (10, 1<<20, _('%.1f MB')),
1385 (10, 1<<20, _('%.1f MB')),
1386 (1, 1<<20, _('%.2f MB')),
1386 (1, 1<<20, _('%.2f MB')),
1387 (100, 1<<10, _('%.0f KB')),
1387 (100, 1<<10, _('%.0f KB')),
1388 (10, 1<<10, _('%.1f KB')),
1388 (10, 1<<10, _('%.1f KB')),
1389 (1, 1<<10, _('%.2f KB')),
1389 (1, 1<<10, _('%.2f KB')),
1390 (1, 1, _('%.0f bytes')),
1390 (1, 1, _('%.0f bytes')),
1391 )
1391 )
1392
1392
1393 for multiplier, divisor, format in units:
1393 for multiplier, divisor, format in units:
1394 if nbytes >= divisor * multiplier:
1394 if nbytes >= divisor * multiplier:
1395 return format % (nbytes / float(divisor))
1395 return format % (nbytes / float(divisor))
1396 return units[-1][2] % nbytes
1396 return units[-1][2] % nbytes
1397
1397
1398 def drop_scheme(scheme, path):
1398 def drop_scheme(scheme, path):
1399 sc = scheme + ':'
1399 sc = scheme + ':'
1400 if path.startswith(sc):
1400 if path.startswith(sc):
1401 path = path[len(sc):]
1401 path = path[len(sc):]
1402 if path.startswith('//'):
1402 if path.startswith('//'):
1403 path = path[2:]
1403 path = path[2:]
1404 return path
1404 return path
@@ -1,74 +1,79 b''
1 # Copyright (C) 2005, 2006 by Intevation GmbH
1 # Copyright (C) 2005, 2006 by Intevation GmbH
2 # Author(s):
2 # Author(s):
3 # Thomas Arendsen Hein <thomas@intevation.de>
3 # Thomas Arendsen Hein <thomas@intevation.de>
4 #
4 #
5 # This program is free software under the GNU GPL (>=v2)
5 # This program is free software under the GNU GPL (>=v2)
6 # Read the file COPYING coming with the software for details.
6 # Read the file COPYING coming with the software for details.
7
7
8 """
8 """
9 Mercurial version
9 Mercurial version
10 """
10 """
11
11
12 import os
12 import os
13 import os.path
13 import os.path
14 import re
14 import re
15 import time
15 import time
16 import util
16 import util
17
17
18 unknown_version = 'unknown'
18 unknown_version = 'unknown'
19 remembered_version = False
19 remembered_version = False
20
20
21 def get_version():
21 def get_version(doreload=False):
22 """Return version information if available."""
22 """Return version information if available."""
23 try:
23 try:
24 from mercurial.__version__ import version
24 import mercurial.__version__
25 if doreload:
26 reload(mercurial.__version__)
27 version = mercurial.__version__.version
25 except ImportError:
28 except ImportError:
26 version = unknown_version
29 version = unknown_version
27 return version
30 return version
28
31
29 def write_version(version):
32 def write_version(version):
30 """Overwrite version file."""
33 """Overwrite version file."""
31 if version == get_version():
34 if version == get_version():
32 return
35 return
33 directory = os.path.dirname(__file__)
36 directory = os.path.dirname(__file__)
34 for suffix in ['py', 'pyc', 'pyo']:
37 for suffix in ['py', 'pyc', 'pyo']:
35 try:
38 try:
36 os.unlink(os.path.join(directory, '__version__.%s' % suffix))
39 os.unlink(os.path.join(directory, '__version__.%s' % suffix))
37 except OSError:
40 except OSError:
38 pass
41 pass
39 f = open(os.path.join(directory, '__version__.py'), 'w')
42 f = open(os.path.join(directory, '__version__.py'), 'w')
40 f.write("# This file is auto-generated.\n")
43 f.write("# This file is auto-generated.\n")
41 f.write("version = %r\n" % version)
44 f.write("version = %r\n" % version)
42 f.close()
45 f.close()
46 # reload the file we've just written
47 get_version(True)
43
48
44 def remember_version(version=None):
49 def remember_version(version=None):
45 """Store version information."""
50 """Store version information."""
46 global remembered_version
51 global remembered_version
47 if not version and os.path.isdir(".hg"):
52 if not version and os.path.isdir(".hg"):
48 f = os.popen("hg identify 2> %s" % util.nulldev) # use real hg installation
53 f = os.popen("hg identify 2> %s" % util.nulldev) # use real hg installation
49 ident = f.read()[:-1]
54 ident = f.read()[:-1]
50 if not f.close() and ident:
55 if not f.close() and ident:
51 ids = ident.split(' ', 1)
56 ids = ident.split(' ', 1)
52 version = ids.pop(0)
57 version = ids.pop(0)
53 if version[-1] == '+':
58 if version[-1] == '+':
54 version = version[:-1]
59 version = version[:-1]
55 modified = True
60 modified = True
56 else:
61 else:
57 modified = False
62 modified = False
58 if version.isalnum() and ids:
63 if version.isalnum() and ids:
59 for tag in ids[0].split('/'):
64 for tag in ids[0].split('/'):
60 # is a tag is suitable as a version number?
65 # is a tag is suitable as a version number?
61 if re.match(r'^(\d+\.)+[\w.-]+$', tag):
66 if re.match(r'^(\d+\.)+[\w.-]+$', tag):
62 version = tag
67 version = tag
63 break
68 break
64 if modified:
69 if modified:
65 version += time.strftime('+%Y%m%d')
70 version += time.strftime('+%Y%m%d')
66 if version:
71 if version:
67 remembered_version = True
72 remembered_version = True
68 write_version(version)
73 write_version(version)
69
74
70 def forget_version():
75 def forget_version():
71 """Remove version information."""
76 """Remove version information."""
72 if remembered_version:
77 if remembered_version:
73 write_version(unknown_version)
78 write_version(unknown_version)
74
79
@@ -1,248 +1,267 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 echo "[extensions]" >> $HGRCPATH
3 echo "[extensions]" >> $HGRCPATH
4 echo "mq=" >> $HGRCPATH
4 echo "mq=" >> $HGRCPATH
5
5
6 echo % help
6 echo % help
7 hg help mq
7 hg help mq
8
8
9 hg init a
9 hg init a
10 cd a
10 cd a
11 echo a > a
11 echo a > a
12 hg ci -Ama
12 hg ci -Ama
13
13
14 hg clone . ../k
14 hg clone . ../k
15
15
16 mkdir b
16 mkdir b
17 echo z > b/z
17 echo z > b/z
18 hg ci -Ama
18 hg ci -Ama
19
19
20 echo % qinit
20 echo % qinit
21
21
22 hg qinit
22 hg qinit
23
23
24 cd ..
24 cd ..
25 hg init b
25 hg init b
26
26
27 echo % -R qinit
27 echo % -R qinit
28
28
29 hg -R b qinit
29 hg -R b qinit
30
30
31 hg init c
31 hg init c
32
32
33 echo % qinit -c
33 echo % qinit -c
34
34
35 hg --cwd c qinit -c
35 hg --cwd c qinit -c
36 hg -R c/.hg/patches st
36 hg -R c/.hg/patches st
37
37
38 echo % qnew implies add
38 echo % qnew implies add
39
39
40 hg -R c qnew test.patch
40 hg -R c qnew test.patch
41 hg -R c/.hg/patches st
41 hg -R c/.hg/patches st
42
42
43 cd a
43 cd a
44
44
45 echo % qnew -m
45 echo % qnew -m
46
46
47 hg qnew -m 'foo bar' test.patch
47 hg qnew -m 'foo bar' test.patch
48 cat .hg/patches/test.patch
48 cat .hg/patches/test.patch
49
49
50 echo % qrefresh
50 echo % qrefresh
51
51
52 echo a >> a
52 echo a >> a
53 hg qrefresh
53 hg qrefresh
54 sed -e "s/^\(diff -r \)\([a-f0-9]* \)/\1 x/" \
54 sed -e "s/^\(diff -r \)\([a-f0-9]* \)/\1 x/" \
55 -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
55 -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
56 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/test.patch
56 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/test.patch
57
57
58 echo % qpop
58 echo % qpop
59
59
60 hg qpop
60 hg qpop
61
61
62 echo % qpush
62 echo % qpush
63
63
64 hg qpush
64 hg qpush
65
65
66 cd ..
66 cd ..
67
67
68 echo % pop/push outside repo
68 echo % pop/push outside repo
69
69
70 hg -R a qpop
70 hg -R a qpop
71 hg -R a qpush
71 hg -R a qpush
72
72
73 cd a
73 cd a
74 hg qnew test2.patch
74 hg qnew test2.patch
75
75
76 echo % qrefresh in subdir
76 echo % qrefresh in subdir
77
77
78 cd b
78 cd b
79 echo a > a
79 echo a > a
80 hg add a
80 hg add a
81 hg qrefresh
81 hg qrefresh
82
82
83 echo % pop/push -a in subdir
83 echo % pop/push -a in subdir
84
84
85 hg qpop -a
85 hg qpop -a
86 hg --traceback qpush -a
86 hg --traceback qpush -a
87
87
88 echo % qseries
88 echo % qseries
89 hg qseries
89 hg qseries
90 hg qpop
90 hg qpop
91 hg qseries -vs
91 hg qseries -vs
92 hg qpush
92 hg qpush
93
93
94 echo % qapplied
94 echo % qapplied
95 hg qapplied
95 hg qapplied
96
96
97 echo % qtop
97 echo % qtop
98 hg qtop
98 hg qtop
99
99
100 echo % qprev
100 echo % qprev
101 hg qprev
101 hg qprev
102
102
103 echo % qnext
103 echo % qnext
104 hg qnext
104 hg qnext
105
105
106 echo % pop, qnext, qprev, qapplied
106 echo % pop, qnext, qprev, qapplied
107 hg qpop
107 hg qpop
108 hg qnext
108 hg qnext
109 hg qprev
109 hg qprev
110 hg qapplied
110 hg qapplied
111
111
112 echo % commit should fail
112 echo % commit should fail
113 hg commit
113 hg commit
114
114
115 echo % push should fail
115 echo % push should fail
116 hg push ../../k
116 hg push ../../k
117
117
118 echo % qunapplied
118 echo % qunapplied
119 hg qunapplied
119 hg qunapplied
120
120
121 echo % qpush/qpop with index
121 echo % qpush/qpop with index
122 hg qnew test1b.patch
122 hg qnew test1b.patch
123 echo 1b > 1b
123 echo 1b > 1b
124 hg add 1b
124 hg add 1b
125 hg qrefresh
125 hg qrefresh
126 hg qpush 2
126 hg qpush 2
127 hg qpop 0
127 hg qpop 0
128 hg qpush test.patch+1
128 hg qpush test.patch+1
129 hg qpush test.patch+2
129 hg qpush test.patch+2
130 hg qpop test2.patch-1
130 hg qpop test2.patch-1
131 hg qpop test2.patch-2
131 hg qpop test2.patch-2
132 hg qpush test1b.patch+1
132 hg qpush test1b.patch+1
133
133
134 echo % push should succeed
134 echo % push should succeed
135 hg qpop -a
135 hg qpop -a
136 hg push ../../k
136 hg push ../../k
137
137
138 echo % strip
138 echo % strip
139 cd ../../b
139 cd ../../b
140 echo x>x
140 echo x>x
141 hg ci -Ama
141 hg ci -Ama
142 hg strip tip 2>&1 | sed 's/\(saving bundle to \).*/\1/'
142 hg strip tip 2>&1 | sed 's/\(saving bundle to \).*/\1/'
143 hg unbundle .hg/strip-backup/*
143 hg unbundle .hg/strip-backup/*
144
144
145 echo '% cd b; hg qrefresh'
145 echo '% cd b; hg qrefresh'
146 hg init refresh
146 hg init refresh
147 cd refresh
147 cd refresh
148 echo a > a
148 echo a > a
149 hg ci -Ama -d'0 0'
149 hg ci -Ama -d'0 0'
150 hg qnew -mfoo foo
150 hg qnew -mfoo foo
151 echo a >> a
151 echo a >> a
152 hg qrefresh
152 hg qrefresh
153 mkdir b
153 mkdir b
154 cd b
154 cd b
155 echo f > f
155 echo f > f
156 hg add f
156 hg add f
157 hg qrefresh
157 hg qrefresh
158 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
158 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
159 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" ../.hg/patches/foo
159 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" ../.hg/patches/foo
160 echo % hg qrefresh .
160 echo % hg qrefresh .
161 hg qrefresh .
161 hg qrefresh .
162 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
162 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
163 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" ../.hg/patches/foo
163 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" ../.hg/patches/foo
164 hg status
164 hg status
165
165
166 echo % qpush failure
166 echo % qpush failure
167 cd ..
167 cd ..
168 hg qrefresh
168 hg qrefresh
169 hg qnew -mbar bar
169 hg qnew -mbar bar
170 echo foo > foo
170 echo foo > foo
171 echo bar > bar
171 echo bar > bar
172 hg add foo bar
172 hg add foo bar
173 hg qrefresh
173 hg qrefresh
174 hg qpop -a
174 hg qpop -a
175 echo bar > foo
175 echo bar > foo
176 hg qpush -a
176 hg qpush -a
177 hg st
177 hg st
178
178
179 cat >>$HGRCPATH <<EOF
179 cat >>$HGRCPATH <<EOF
180 [diff]
180 [diff]
181 git = True
181 git = True
182 EOF
182 EOF
183 cd ..
183 cd ..
184 hg init git
184 hg init git
185 cd git
185 cd git
186 hg qinit
186 hg qinit
187
187
188 hg qnew -m'new file' new
188 hg qnew -m'new file' new
189 echo foo > new
189 echo foo > new
190 chmod +x new
190 chmod +x new
191 hg add new
191 hg add new
192 hg qrefresh
192 hg qrefresh
193 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
193 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
194 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/new
194 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/new
195
195
196 hg qnew -m'copy file' copy
196 hg qnew -m'copy file' copy
197 hg cp new copy
197 hg cp new copy
198 hg qrefresh
198 hg qrefresh
199 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
199 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
200 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/copy
200 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/copy
201
201
202 hg qpop
202 hg qpop
203 hg qpush
203 hg qpush
204 hg qdiff
204 hg qdiff
205 cat >>$HGRCPATH <<EOF
205 cat >>$HGRCPATH <<EOF
206 [diff]
206 [diff]
207 git = False
207 git = False
208 EOF
208 EOF
209 hg qdiff --git
209 hg qdiff --git
210
210
211 cd ..
211 cd ..
212 hg init slow
212 hg init slow
213 cd slow
213 cd slow
214 hg qinit
214 hg qinit
215 echo foo > foo
215 echo foo > foo
216 hg add foo
216 hg add foo
217 hg ci -m 'add foo'
217 hg ci -m 'add foo'
218 hg qnew bar
218 hg qnew bar
219 echo bar > bar
219 echo bar > bar
220 hg add bar
220 hg add bar
221 hg mv foo baz
221 hg mv foo baz
222 hg qrefresh --git
222 hg qrefresh --git
223 hg up -C 0
223 hg up -C 0
224 echo >> foo
224 echo >> foo
225 hg ci -m 'change foo'
225 hg ci -m 'change foo'
226 hg up -C 1
226 hg up -C 1
227 hg qrefresh --git 2>&1 | grep -v 'saving bundle'
227 hg qrefresh --git 2>&1 | grep -v 'saving bundle'
228 cat .hg/patches/bar
228 cat .hg/patches/bar
229 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r .
229 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r .
230 hg qrefresh --git
230 hg qrefresh --git
231 cat .hg/patches/bar
231 cat .hg/patches/bar
232 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r .
232 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r .
233
233
234 echo
234 echo
235 hg up -C 1
235 hg up -C 1
236 echo >> foo
236 echo >> foo
237 hg ci -m 'change foo again'
237 hg ci -m 'change foo again'
238 hg up -C 2
238 hg up -C 2
239 hg mv bar quux
239 hg mv bar quux
240 hg mv baz bleh
240 hg mv baz bleh
241 hg qrefresh --git 2>&1 | grep -v 'saving bundle'
241 hg qrefresh --git 2>&1 | grep -v 'saving bundle'
242 cat .hg/patches/bar
242 cat .hg/patches/bar
243 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r .
243 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r .
244 hg mv quux fred
244 hg mv quux fred
245 hg mv bleh barney
245 hg mv bleh barney
246 hg qrefresh --git
246 hg qrefresh --git
247 cat .hg/patches/bar
247 cat .hg/patches/bar
248 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r .
248 hg log -vC --template '{rev} {file_copies%filecopy}\n' -r .
249
250 echo '% strip again'
251 cd ..
252 hg init strip
253 cd strip
254 touch foo
255 hg add foo
256 hg ci -m 'add foo' -d '0 0'
257 echo >> foo
258 hg ci -m 'change foo 1' -d '0 0'
259 hg up -C 0
260 echo 1 >> foo
261 hg ci -m 'change foo 2' -d '0 0'
262 HGMERGE=true hg merge
263 hg ci -m merge -d '0 0'
264 hg log
265 hg strip 1 2>&1 | sed 's/\(saving bundle to \).*/\1/'
266 hg log
267
@@ -1,266 +1,315 b''
1 % help
1 % help
2 mq extension - patch management and development
2 mq extension - patch management and development
3
3
4 This extension lets you work with a stack of patches in a Mercurial
4 This extension lets you work with a stack of patches in a Mercurial
5 repository. It manages two stacks of patches - all known patches, and
5 repository. It manages two stacks of patches - all known patches, and
6 applied patches (subset of known patches).
6 applied patches (subset of known patches).
7
7
8 Known patches are represented as patch files in the .hg/patches
8 Known patches are represented as patch files in the .hg/patches
9 directory. Applied patches are both patch files and changesets.
9 directory. Applied patches are both patch files and changesets.
10
10
11 Common tasks (use "hg help command" for more details):
11 Common tasks (use "hg help command" for more details):
12
12
13 prepare repository to work with patches qinit
13 prepare repository to work with patches qinit
14 create new patch qnew
14 create new patch qnew
15 import existing patch qimport
15 import existing patch qimport
16
16
17 print patch series qseries
17 print patch series qseries
18 print applied patches qapplied
18 print applied patches qapplied
19 print name of top applied patch qtop
19 print name of top applied patch qtop
20
20
21 add known patch to applied stack qpush
21 add known patch to applied stack qpush
22 remove patch from applied stack qpop
22 remove patch from applied stack qpop
23 refresh contents of top applied patch qrefresh
23 refresh contents of top applied patch qrefresh
24
24
25 list of commands (use "hg help -v mq" to show aliases and global options):
25 list of commands (use "hg help -v mq" to show aliases and global options):
26
26
27 qapplied print the patches already applied
27 qapplied print the patches already applied
28 qclone clone main and patch repository at same time
28 qclone clone main and patch repository at same time
29 qcommit commit changes in the queue repository
29 qcommit commit changes in the queue repository
30 qdelete remove patches from queue
30 qdelete remove patches from queue
31 qdiff diff of the current patch
31 qdiff diff of the current patch
32 qfold fold the named patches into the current patch
32 qfold fold the named patches into the current patch
33 qguard set or print guards for a patch
33 qguard set or print guards for a patch
34 qheader Print the header of the topmost or specified patch
34 qheader Print the header of the topmost or specified patch
35 qimport import a patch
35 qimport import a patch
36 qinit init a new queue repository
36 qinit init a new queue repository
37 qnew create a new patch
37 qnew create a new patch
38 qnext print the name of the next patch
38 qnext print the name of the next patch
39 qpop pop the current patch off the stack
39 qpop pop the current patch off the stack
40 qprev print the name of the previous patch
40 qprev print the name of the previous patch
41 qpush push the next patch onto the stack
41 qpush push the next patch onto the stack
42 qrefresh update the current patch
42 qrefresh update the current patch
43 qrename rename a patch
43 qrename rename a patch
44 qrestore restore the queue state saved by a rev
44 qrestore restore the queue state saved by a rev
45 qsave save current queue state
45 qsave save current queue state
46 qselect set or print guarded patches to push
46 qselect set or print guarded patches to push
47 qseries print the entire series file
47 qseries print the entire series file
48 qtop print the name of the current patch
48 qtop print the name of the current patch
49 qunapplied print the patches not yet applied
49 qunapplied print the patches not yet applied
50 strip strip a revision and all later revs on the same branch
50 strip strip a revision and all later revs on the same branch
51 adding a
51 adding a
52 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
52 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
53 adding b/z
53 adding b/z
54 % qinit
54 % qinit
55 % -R qinit
55 % -R qinit
56 % qinit -c
56 % qinit -c
57 A .hgignore
57 A .hgignore
58 A series
58 A series
59 % qnew implies add
59 % qnew implies add
60 A .hgignore
60 A .hgignore
61 A series
61 A series
62 A test.patch
62 A test.patch
63 % qnew -m
63 % qnew -m
64 foo bar
64 foo bar
65 % qrefresh
65 % qrefresh
66 foo bar
66 foo bar
67
67
68 diff -r xa
68 diff -r xa
69 --- a/a
69 --- a/a
70 +++ b/a
70 +++ b/a
71 @@ -1,1 +1,2 @@ a
71 @@ -1,1 +1,2 @@ a
72 a
72 a
73 +a
73 +a
74 % qpop
74 % qpop
75 Patch queue now empty
75 Patch queue now empty
76 % qpush
76 % qpush
77 applying test.patch
77 applying test.patch
78 Now at: test.patch
78 Now at: test.patch
79 % pop/push outside repo
79 % pop/push outside repo
80 Patch queue now empty
80 Patch queue now empty
81 applying test.patch
81 applying test.patch
82 Now at: test.patch
82 Now at: test.patch
83 % qrefresh in subdir
83 % qrefresh in subdir
84 % pop/push -a in subdir
84 % pop/push -a in subdir
85 Patch queue now empty
85 Patch queue now empty
86 applying test.patch
86 applying test.patch
87 applying test2.patch
87 applying test2.patch
88 Now at: test2.patch
88 Now at: test2.patch
89 % qseries
89 % qseries
90 test.patch
90 test.patch
91 test2.patch
91 test2.patch
92 Now at: test.patch
92 Now at: test.patch
93 0 A test.patch: foo bar
93 0 A test.patch: foo bar
94 1 U test2.patch:
94 1 U test2.patch:
95 applying test2.patch
95 applying test2.patch
96 Now at: test2.patch
96 Now at: test2.patch
97 % qapplied
97 % qapplied
98 test.patch
98 test.patch
99 test2.patch
99 test2.patch
100 % qtop
100 % qtop
101 test2.patch
101 test2.patch
102 % qprev
102 % qprev
103 test.patch
103 test.patch
104 % qnext
104 % qnext
105 All patches applied
105 All patches applied
106 % pop, qnext, qprev, qapplied
106 % pop, qnext, qprev, qapplied
107 Now at: test.patch
107 Now at: test.patch
108 test2.patch
108 test2.patch
109 Only one patch applied
109 Only one patch applied
110 test.patch
110 test.patch
111 % commit should fail
111 % commit should fail
112 abort: cannot commit over an applied mq patch
112 abort: cannot commit over an applied mq patch
113 % push should fail
113 % push should fail
114 pushing to ../../k
114 pushing to ../../k
115 abort: source has mq patches applied
115 abort: source has mq patches applied
116 % qunapplied
116 % qunapplied
117 test2.patch
117 test2.patch
118 % qpush/qpop with index
118 % qpush/qpop with index
119 applying test2.patch
119 applying test2.patch
120 Now at: test2.patch
120 Now at: test2.patch
121 Now at: test.patch
121 Now at: test.patch
122 applying test1b.patch
122 applying test1b.patch
123 Now at: test1b.patch
123 Now at: test1b.patch
124 applying test2.patch
124 applying test2.patch
125 Now at: test2.patch
125 Now at: test2.patch
126 Now at: test1b.patch
126 Now at: test1b.patch
127 Now at: test.patch
127 Now at: test.patch
128 applying test1b.patch
128 applying test1b.patch
129 applying test2.patch
129 applying test2.patch
130 Now at: test2.patch
130 Now at: test2.patch
131 % push should succeed
131 % push should succeed
132 Patch queue now empty
132 Patch queue now empty
133 pushing to ../../k
133 pushing to ../../k
134 searching for changes
134 searching for changes
135 adding changesets
135 adding changesets
136 adding manifests
136 adding manifests
137 adding file changes
137 adding file changes
138 added 1 changesets with 1 changes to 1 files
138 added 1 changesets with 1 changes to 1 files
139 % strip
139 % strip
140 adding x
140 adding x
141 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
141 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
142 saving bundle to
142 saving bundle to
143 adding changesets
143 adding changesets
144 adding manifests
144 adding manifests
145 adding file changes
145 adding file changes
146 added 1 changesets with 1 changes to 1 files
146 added 1 changesets with 1 changes to 1 files
147 (run 'hg update' to get a working copy)
147 (run 'hg update' to get a working copy)
148 % cd b; hg qrefresh
148 % cd b; hg qrefresh
149 adding a
149 adding a
150 foo
150 foo
151
151
152 diff -r cb9a9f314b8b a
152 diff -r cb9a9f314b8b a
153 --- a/a
153 --- a/a
154 +++ b/a
154 +++ b/a
155 @@ -1,1 +1,2 @@ a
155 @@ -1,1 +1,2 @@ a
156 a
156 a
157 +a
157 +a
158 diff -r cb9a9f314b8b b/f
158 diff -r cb9a9f314b8b b/f
159 --- /dev/null
159 --- /dev/null
160 +++ b/b/f
160 +++ b/b/f
161 @@ -0,0 +1,1 @@
161 @@ -0,0 +1,1 @@
162 +f
162 +f
163 % hg qrefresh .
163 % hg qrefresh .
164 foo
164 foo
165
165
166 diff -r cb9a9f314b8b b/f
166 diff -r cb9a9f314b8b b/f
167 --- /dev/null
167 --- /dev/null
168 +++ b/b/f
168 +++ b/b/f
169 @@ -0,0 +1,1 @@
169 @@ -0,0 +1,1 @@
170 +f
170 +f
171 M a
171 M a
172 % qpush failure
172 % qpush failure
173 Patch queue now empty
173 Patch queue now empty
174 applying foo
174 applying foo
175 applying bar
175 applying bar
176 1 out of 1 hunk ignored -- saving rejects to file foo.rej
176 1 out of 1 hunk ignored -- saving rejects to file foo.rej
177 patch failed, unable to continue (try -v)
177 patch failed, unable to continue (try -v)
178 patch failed, rejects left in working dir
178 patch failed, rejects left in working dir
179 Errors during apply, please fix and refresh bar
179 Errors during apply, please fix and refresh bar
180 ? foo
180 ? foo
181 ? foo.rej
181 ? foo.rej
182 new file
182 new file
183
183
184 diff --git a/new b/new
184 diff --git a/new b/new
185 new file mode 100755
185 new file mode 100755
186 --- /dev/null
186 --- /dev/null
187 +++ b/new
187 +++ b/new
188 @@ -0,0 +1,1 @@
188 @@ -0,0 +1,1 @@
189 +foo
189 +foo
190 copy file
190 copy file
191
191
192 diff --git a/new b/copy
192 diff --git a/new b/copy
193 copy from new
193 copy from new
194 copy to copy
194 copy to copy
195 Now at: new
195 Now at: new
196 applying copy
196 applying copy
197 Now at: copy
197 Now at: copy
198 diff --git a/new b/copy
198 diff --git a/new b/copy
199 copy from new
199 copy from new
200 copy to copy
200 copy to copy
201 diff --git a/new b/copy
201 diff --git a/new b/copy
202 copy from new
202 copy from new
203 copy to copy
203 copy to copy
204 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
204 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
205 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
205 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
206 adding branch
206 adding branch
207 adding changesets
207 adding changesets
208 adding manifests
208 adding manifests
209 adding file changes
209 adding file changes
210 added 1 changesets with 1 changes to 1 files
210 added 1 changesets with 1 changes to 1 files
211 (run 'hg update' to get a working copy)
211 (run 'hg update' to get a working copy)
212 Patch queue now empty
212 Patch queue now empty
213 applying bar
213 applying bar
214 Now at: bar
214 Now at: bar
215 diff --git a/bar b/bar
215 diff --git a/bar b/bar
216 new file mode 100644
216 new file mode 100644
217 --- /dev/null
217 --- /dev/null
218 +++ b/bar
218 +++ b/bar
219 @@ -0,0 +1,1 @@
219 @@ -0,0 +1,1 @@
220 +bar
220 +bar
221 diff --git a/foo b/baz
221 diff --git a/foo b/baz
222 rename from foo
222 rename from foo
223 rename to baz
223 rename to baz
224 2 baz (foo)
224 2 baz (foo)
225 diff --git a/bar b/bar
225 diff --git a/bar b/bar
226 new file mode 100644
226 new file mode 100644
227 --- /dev/null
227 --- /dev/null
228 +++ b/bar
228 +++ b/bar
229 @@ -0,0 +1,1 @@
229 @@ -0,0 +1,1 @@
230 +bar
230 +bar
231 diff --git a/foo b/baz
231 diff --git a/foo b/baz
232 rename from foo
232 rename from foo
233 rename to baz
233 rename to baz
234 2 baz (foo)
234 2 baz (foo)
235
235
236 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
236 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
237 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
237 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
238 adding branch
238 adding branch
239 adding changesets
239 adding changesets
240 adding manifests
240 adding manifests
241 adding file changes
241 adding file changes
242 added 1 changesets with 1 changes to 1 files
242 added 1 changesets with 1 changes to 1 files
243 (run 'hg update' to get a working copy)
243 (run 'hg update' to get a working copy)
244 Patch queue now empty
244 Patch queue now empty
245 applying bar
245 applying bar
246 Now at: bar
246 Now at: bar
247 diff --git a/foo b/bleh
247 diff --git a/foo b/bleh
248 rename from foo
248 rename from foo
249 rename to bleh
249 rename to bleh
250 diff --git a/quux b/quux
250 diff --git a/quux b/quux
251 new file mode 100644
251 new file mode 100644
252 --- /dev/null
252 --- /dev/null
253 +++ b/quux
253 +++ b/quux
254 @@ -0,0 +1,1 @@
254 @@ -0,0 +1,1 @@
255 +bar
255 +bar
256 3 bleh (foo)
256 3 bleh (foo)
257 diff --git a/foo b/barney
257 diff --git a/foo b/barney
258 rename from foo
258 rename from foo
259 rename to barney
259 rename to barney
260 diff --git a/fred b/fred
260 diff --git a/fred b/fred
261 new file mode 100644
261 new file mode 100644
262 --- /dev/null
262 --- /dev/null
263 +++ b/fred
263 +++ b/fred
264 @@ -0,0 +1,1 @@
264 @@ -0,0 +1,1 @@
265 +bar
265 +bar
266 3 barney (foo)
266 3 barney (foo)
267 % strip again
268 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
269 merging foo
270 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
271 (branch merge, don't forget to commit)
272 changeset: 3:99615015637b
273 tag: tip
274 parent: 2:20cbbe65cff7
275 parent: 1:d2871fc282d4
276 user: test
277 date: Thu Jan 01 00:00:00 1970 +0000
278 summary: merge
279
280 changeset: 2:20cbbe65cff7
281 parent: 0:53245c60e682
282 user: test
283 date: Thu Jan 01 00:00:00 1970 +0000
284 summary: change foo 2
285
286 changeset: 1:d2871fc282d4
287 user: test
288 date: Thu Jan 01 00:00:00 1970 +0000
289 summary: change foo 1
290
291 changeset: 0:53245c60e682
292 user: test
293 date: Thu Jan 01 00:00:00 1970 +0000
294 summary: add foo
295
296 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
297 saving bundle to
298 saving bundle to
299 adding branch
300 adding changesets
301 adding manifests
302 adding file changes
303 added 1 changesets with 1 changes to 1 files
304 (run 'hg update' to get a working copy)
305 changeset: 1:20cbbe65cff7
306 tag: tip
307 user: test
308 date: Thu Jan 01 00:00:00 1970 +0000
309 summary: change foo 2
310
311 changeset: 0:53245c60e682
312 user: test
313 date: Thu Jan 01 00:00:00 1970 +0000
314 summary: add foo
315
General Comments 0
You need to be logged in to leave comments. Login now