##// END OF EJS Templates
Kill ui.setconfig_remoteopts...
Matt Mackall -
r2731:ad4155e7 default
parent child Browse files
Show More
@@ -1,1500 +1,1500 b''
1 # queue.py - patch queues for mercurial
1 # queue.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005 Chris Mason <mason@suse.com>
3 # Copyright 2005 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.demandload import *
32 from mercurial.demandload import *
33 demandload(globals(), "os sys re struct traceback errno bz2")
33 demandload(globals(), "os sys re struct traceback errno bz2")
34 from mercurial.i18n import gettext as _
34 from mercurial.i18n import gettext as _
35 from mercurial import ui, hg, revlog, commands, util
35 from mercurial import ui, hg, revlog, commands, util
36
36
37 versionstr = "0.45"
37 versionstr = "0.45"
38
38
39 commands.norepo += " qclone qversion"
39 commands.norepo += " qclone qversion"
40
40
41 class queue:
41 class queue:
42 def __init__(self, ui, path, patchdir=None):
42 def __init__(self, ui, path, patchdir=None):
43 self.basepath = path
43 self.basepath = path
44 if patchdir:
44 if patchdir:
45 self.path = patchdir
45 self.path = patchdir
46 else:
46 else:
47 self.path = os.path.join(path, "patches")
47 self.path = os.path.join(path, "patches")
48 self.opener = util.opener(self.path)
48 self.opener = util.opener(self.path)
49 self.ui = ui
49 self.ui = ui
50 self.applied = []
50 self.applied = []
51 self.full_series = []
51 self.full_series = []
52 self.applied_dirty = 0
52 self.applied_dirty = 0
53 self.series_dirty = 0
53 self.series_dirty = 0
54 self.series_path = "series"
54 self.series_path = "series"
55 self.status_path = "status"
55 self.status_path = "status"
56
56
57 if os.path.exists(os.path.join(self.path, self.series_path)):
57 if os.path.exists(os.path.join(self.path, self.series_path)):
58 self.full_series = self.opener(self.series_path).read().splitlines()
58 self.full_series = self.opener(self.series_path).read().splitlines()
59 self.read_series(self.full_series)
59 self.read_series(self.full_series)
60
60
61 if os.path.exists(os.path.join(self.path, self.status_path)):
61 if os.path.exists(os.path.join(self.path, self.status_path)):
62 self.applied = self.opener(self.status_path).read().splitlines()
62 self.applied = self.opener(self.status_path).read().splitlines()
63
63
64 def find_series(self, patch):
64 def find_series(self, patch):
65 pre = re.compile("(\s*)([^#]+)")
65 pre = re.compile("(\s*)([^#]+)")
66 index = 0
66 index = 0
67 for l in self.full_series:
67 for l in self.full_series:
68 m = pre.match(l)
68 m = pre.match(l)
69 if m:
69 if m:
70 s = m.group(2)
70 s = m.group(2)
71 s = s.rstrip()
71 s = s.rstrip()
72 if s == patch:
72 if s == patch:
73 return index
73 return index
74 index += 1
74 index += 1
75 return None
75 return None
76
76
77 def read_series(self, list):
77 def read_series(self, list):
78 def matcher(list):
78 def matcher(list):
79 pre = re.compile("(\s*)([^#]+)")
79 pre = re.compile("(\s*)([^#]+)")
80 for l in list:
80 for l in list:
81 m = pre.match(l)
81 m = pre.match(l)
82 if m:
82 if m:
83 s = m.group(2)
83 s = m.group(2)
84 s = s.rstrip()
84 s = s.rstrip()
85 if len(s) > 0:
85 if len(s) > 0:
86 yield s
86 yield s
87 self.series = []
87 self.series = []
88 self.series = [ x for x in matcher(list) ]
88 self.series = [ x for x in matcher(list) ]
89
89
90 def save_dirty(self):
90 def save_dirty(self):
91 if self.applied_dirty:
91 if self.applied_dirty:
92 if len(self.applied) > 0:
92 if len(self.applied) > 0:
93 nl = "\n"
93 nl = "\n"
94 else:
94 else:
95 nl = ""
95 nl = ""
96 f = self.opener(self.status_path, "w")
96 f = self.opener(self.status_path, "w")
97 f.write("\n".join(self.applied) + nl)
97 f.write("\n".join(self.applied) + nl)
98 if self.series_dirty:
98 if self.series_dirty:
99 if len(self.full_series) > 0:
99 if len(self.full_series) > 0:
100 nl = "\n"
100 nl = "\n"
101 else:
101 else:
102 nl = ""
102 nl = ""
103 f = self.opener(self.series_path, "w")
103 f = self.opener(self.series_path, "w")
104 f.write("\n".join(self.full_series) + nl)
104 f.write("\n".join(self.full_series) + nl)
105
105
106 def readheaders(self, patch):
106 def readheaders(self, patch):
107 def eatdiff(lines):
107 def eatdiff(lines):
108 while lines:
108 while lines:
109 l = lines[-1]
109 l = lines[-1]
110 if (l.startswith("diff -") or
110 if (l.startswith("diff -") or
111 l.startswith("Index:") or
111 l.startswith("Index:") or
112 l.startswith("===========")):
112 l.startswith("===========")):
113 del lines[-1]
113 del lines[-1]
114 else:
114 else:
115 break
115 break
116 def eatempty(lines):
116 def eatempty(lines):
117 while lines:
117 while lines:
118 l = lines[-1]
118 l = lines[-1]
119 if re.match('\s*$', l):
119 if re.match('\s*$', l):
120 del lines[-1]
120 del lines[-1]
121 else:
121 else:
122 break
122 break
123
123
124 pf = os.path.join(self.path, patch)
124 pf = os.path.join(self.path, patch)
125 message = []
125 message = []
126 comments = []
126 comments = []
127 user = None
127 user = None
128 date = None
128 date = None
129 format = None
129 format = None
130 subject = None
130 subject = None
131 diffstart = 0
131 diffstart = 0
132
132
133 for line in file(pf):
133 for line in file(pf):
134 line = line.rstrip()
134 line = line.rstrip()
135 if diffstart:
135 if diffstart:
136 if line.startswith('+++ '):
136 if line.startswith('+++ '):
137 diffstart = 2
137 diffstart = 2
138 break
138 break
139 if line.startswith("--- "):
139 if line.startswith("--- "):
140 diffstart = 1
140 diffstart = 1
141 continue
141 continue
142 elif format == "hgpatch":
142 elif format == "hgpatch":
143 # parse values when importing the result of an hg export
143 # parse values when importing the result of an hg export
144 if line.startswith("# User "):
144 if line.startswith("# User "):
145 user = line[7:]
145 user = line[7:]
146 elif line.startswith("# Date "):
146 elif line.startswith("# Date "):
147 date = line[7:]
147 date = line[7:]
148 elif not line.startswith("# ") and line:
148 elif not line.startswith("# ") and line:
149 message.append(line)
149 message.append(line)
150 format = None
150 format = None
151 elif line == '# HG changeset patch':
151 elif line == '# HG changeset patch':
152 format = "hgpatch"
152 format = "hgpatch"
153 elif (format != "tagdone" and (line.startswith("Subject: ") or
153 elif (format != "tagdone" and (line.startswith("Subject: ") or
154 line.startswith("subject: "))):
154 line.startswith("subject: "))):
155 subject = line[9:]
155 subject = line[9:]
156 format = "tag"
156 format = "tag"
157 elif (format != "tagdone" and (line.startswith("From: ") or
157 elif (format != "tagdone" and (line.startswith("From: ") or
158 line.startswith("from: "))):
158 line.startswith("from: "))):
159 user = line[6:]
159 user = line[6:]
160 format = "tag"
160 format = "tag"
161 elif format == "tag" and line == "":
161 elif format == "tag" and line == "":
162 # when looking for tags (subject: from: etc) they
162 # when looking for tags (subject: from: etc) they
163 # end once you find a blank line in the source
163 # end once you find a blank line in the source
164 format = "tagdone"
164 format = "tagdone"
165 elif message or line:
165 elif message or line:
166 message.append(line)
166 message.append(line)
167 comments.append(line)
167 comments.append(line)
168
168
169 eatdiff(message)
169 eatdiff(message)
170 eatdiff(comments)
170 eatdiff(comments)
171 eatempty(message)
171 eatempty(message)
172 eatempty(comments)
172 eatempty(comments)
173
173
174 # make sure message isn't empty
174 # make sure message isn't empty
175 if format and format.startswith("tag") and subject:
175 if format and format.startswith("tag") and subject:
176 message.insert(0, "")
176 message.insert(0, "")
177 message.insert(0, subject)
177 message.insert(0, subject)
178 return (message, comments, user, date, diffstart > 1)
178 return (message, comments, user, date, diffstart > 1)
179
179
180 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
180 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
181 # first try just applying the patch
181 # first try just applying the patch
182 (err, n) = self.apply(repo, [ patch ], update_status=False,
182 (err, n) = self.apply(repo, [ patch ], update_status=False,
183 strict=True, merge=rev, wlock=wlock)
183 strict=True, merge=rev, wlock=wlock)
184
184
185 if err == 0:
185 if err == 0:
186 return (err, n)
186 return (err, n)
187
187
188 if n is None:
188 if n is None:
189 raise util.Abort(_("apply failed for patch %s") % patch)
189 raise util.Abort(_("apply failed for patch %s") % patch)
190
190
191 self.ui.warn("patch didn't work out, merging %s\n" % patch)
191 self.ui.warn("patch didn't work out, merging %s\n" % patch)
192
192
193 # apply failed, strip away that rev and merge.
193 # apply failed, strip away that rev and merge.
194 repo.update(head, allow=False, force=True, wlock=wlock)
194 repo.update(head, allow=False, force=True, wlock=wlock)
195 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
195 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
196
196
197 c = repo.changelog.read(rev)
197 c = repo.changelog.read(rev)
198 ret = repo.update(rev, allow=True, wlock=wlock)
198 ret = repo.update(rev, allow=True, wlock=wlock)
199 if ret:
199 if ret:
200 raise util.Abort(_("update returned %d") % ret)
200 raise util.Abort(_("update returned %d") % ret)
201 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
201 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
202 if n == None:
202 if n == None:
203 raise util.Abort(_("repo commit failed"))
203 raise util.Abort(_("repo commit failed"))
204 try:
204 try:
205 message, comments, user, date, patchfound = mergeq.readheaders(patch)
205 message, comments, user, date, patchfound = mergeq.readheaders(patch)
206 except:
206 except:
207 raise util.Abort(_("unable to read %s") % patch)
207 raise util.Abort(_("unable to read %s") % patch)
208
208
209 patchf = self.opener(patch, "w")
209 patchf = self.opener(patch, "w")
210 if comments:
210 if comments:
211 comments = "\n".join(comments) + '\n\n'
211 comments = "\n".join(comments) + '\n\n'
212 patchf.write(comments)
212 patchf.write(comments)
213 commands.dodiff(patchf, self.ui, repo, head, n)
213 commands.dodiff(patchf, self.ui, repo, head, n)
214 patchf.close()
214 patchf.close()
215 return (0, n)
215 return (0, n)
216
216
217 def qparents(self, repo, rev=None):
217 def qparents(self, repo, rev=None):
218 if rev is None:
218 if rev is None:
219 (p1, p2) = repo.dirstate.parents()
219 (p1, p2) = repo.dirstate.parents()
220 if p2 == revlog.nullid:
220 if p2 == revlog.nullid:
221 return p1
221 return p1
222 if len(self.applied) == 0:
222 if len(self.applied) == 0:
223 return None
223 return None
224 (top, patch) = self.applied[-1].split(':')
224 (top, patch) = self.applied[-1].split(':')
225 top = revlog.bin(top)
225 top = revlog.bin(top)
226 return top
226 return top
227 pp = repo.changelog.parents(rev)
227 pp = repo.changelog.parents(rev)
228 if pp[1] != revlog.nullid:
228 if pp[1] != revlog.nullid:
229 arevs = [ x.split(':')[0] for x in self.applied ]
229 arevs = [ x.split(':')[0] for x in self.applied ]
230 p0 = revlog.hex(pp[0])
230 p0 = revlog.hex(pp[0])
231 p1 = revlog.hex(pp[1])
231 p1 = revlog.hex(pp[1])
232 if p0 in arevs:
232 if p0 in arevs:
233 return pp[0]
233 return pp[0]
234 if p1 in arevs:
234 if p1 in arevs:
235 return pp[1]
235 return pp[1]
236 return pp[0]
236 return pp[0]
237
237
238 def mergepatch(self, repo, mergeq, series, wlock):
238 def mergepatch(self, repo, mergeq, series, wlock):
239 if len(self.applied) == 0:
239 if len(self.applied) == 0:
240 # each of the patches merged in will have two parents. This
240 # each of the patches merged in will have two parents. This
241 # can confuse the qrefresh, qdiff, and strip code because it
241 # can confuse the qrefresh, qdiff, and strip code because it
242 # needs to know which parent is actually in the patch queue.
242 # needs to know which parent is actually in the patch queue.
243 # so, we insert a merge marker with only one parent. This way
243 # so, we insert a merge marker with only one parent. This way
244 # the first patch in the queue is never a merge patch
244 # the first patch in the queue is never a merge patch
245 #
245 #
246 pname = ".hg.patches.merge.marker"
246 pname = ".hg.patches.merge.marker"
247 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
247 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
248 wlock=wlock)
248 wlock=wlock)
249 self.applied.append(revlog.hex(n) + ":" + pname)
249 self.applied.append(revlog.hex(n) + ":" + pname)
250 self.applied_dirty = 1
250 self.applied_dirty = 1
251
251
252 head = self.qparents(repo)
252 head = self.qparents(repo)
253
253
254 for patch in series:
254 for patch in series:
255 patch = mergeq.lookup(patch, strict=True)
255 patch = mergeq.lookup(patch, strict=True)
256 if not patch:
256 if not patch:
257 self.ui.warn("patch %s does not exist\n" % patch)
257 self.ui.warn("patch %s does not exist\n" % patch)
258 return (1, None)
258 return (1, None)
259
259
260 info = mergeq.isapplied(patch)
260 info = mergeq.isapplied(patch)
261 if not info:
261 if not info:
262 self.ui.warn("patch %s is not applied\n" % patch)
262 self.ui.warn("patch %s is not applied\n" % patch)
263 return (1, None)
263 return (1, None)
264 rev = revlog.bin(info[1])
264 rev = revlog.bin(info[1])
265 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
265 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
266 if head:
266 if head:
267 self.applied.append(revlog.hex(head) + ":" + patch)
267 self.applied.append(revlog.hex(head) + ":" + patch)
268 self.applied_dirty = 1
268 self.applied_dirty = 1
269 if err:
269 if err:
270 return (err, head)
270 return (err, head)
271 return (0, head)
271 return (0, head)
272
272
273 def apply(self, repo, series, list=False, update_status=True,
273 def apply(self, repo, series, list=False, update_status=True,
274 strict=False, patchdir=None, merge=None, wlock=None):
274 strict=False, patchdir=None, merge=None, wlock=None):
275 # TODO unify with commands.py
275 # TODO unify with commands.py
276 if not patchdir:
276 if not patchdir:
277 patchdir = self.path
277 patchdir = self.path
278 err = 0
278 err = 0
279 if not wlock:
279 if not wlock:
280 wlock = repo.wlock()
280 wlock = repo.wlock()
281 lock = repo.lock()
281 lock = repo.lock()
282 tr = repo.transaction()
282 tr = repo.transaction()
283 n = None
283 n = None
284 for patch in series:
284 for patch in series:
285 self.ui.warn("applying %s\n" % patch)
285 self.ui.warn("applying %s\n" % patch)
286 pf = os.path.join(patchdir, patch)
286 pf = os.path.join(patchdir, patch)
287
287
288 try:
288 try:
289 message, comments, user, date, patchfound = self.readheaders(patch)
289 message, comments, user, date, patchfound = self.readheaders(patch)
290 except:
290 except:
291 self.ui.warn("Unable to read %s\n" % pf)
291 self.ui.warn("Unable to read %s\n" % pf)
292 err = 1
292 err = 1
293 break
293 break
294
294
295 if not message:
295 if not message:
296 message = "imported patch %s\n" % patch
296 message = "imported patch %s\n" % patch
297 else:
297 else:
298 if list:
298 if list:
299 message.append("\nimported patch %s" % patch)
299 message.append("\nimported patch %s" % patch)
300 message = '\n'.join(message)
300 message = '\n'.join(message)
301
301
302 try:
302 try:
303 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
303 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
304 f = os.popen("%s -d '%s' -p1 --no-backup-if-mismatch < '%s'" %
304 f = os.popen("%s -d '%s' -p1 --no-backup-if-mismatch < '%s'" %
305 (pp, repo.root, pf))
305 (pp, repo.root, pf))
306 except:
306 except:
307 self.ui.warn("patch failed, unable to continue (try -v)\n")
307 self.ui.warn("patch failed, unable to continue (try -v)\n")
308 err = 1
308 err = 1
309 break
309 break
310 files = []
310 files = []
311 fuzz = False
311 fuzz = False
312 for l in f:
312 for l in f:
313 l = l.rstrip('\r\n');
313 l = l.rstrip('\r\n');
314 if self.ui.verbose:
314 if self.ui.verbose:
315 self.ui.warn(l + "\n")
315 self.ui.warn(l + "\n")
316 if l[:14] == 'patching file ':
316 if l[:14] == 'patching file ':
317 pf = os.path.normpath(l[14:])
317 pf = os.path.normpath(l[14:])
318 # when patch finds a space in the file name, it puts
318 # when patch finds a space in the file name, it puts
319 # single quotes around the filename. strip them off
319 # single quotes around the filename. strip them off
320 if pf[0] == "'" and pf[-1] == "'":
320 if pf[0] == "'" and pf[-1] == "'":
321 pf = pf[1:-1]
321 pf = pf[1:-1]
322 if pf not in files:
322 if pf not in files:
323 files.append(pf)
323 files.append(pf)
324 printed_file = False
324 printed_file = False
325 file_str = l
325 file_str = l
326 elif l.find('with fuzz') >= 0:
326 elif l.find('with fuzz') >= 0:
327 if not printed_file:
327 if not printed_file:
328 self.ui.warn(file_str + '\n')
328 self.ui.warn(file_str + '\n')
329 printed_file = True
329 printed_file = True
330 self.ui.warn(l + '\n')
330 self.ui.warn(l + '\n')
331 fuzz = True
331 fuzz = True
332 elif l.find('saving rejects to file') >= 0:
332 elif l.find('saving rejects to file') >= 0:
333 self.ui.warn(l + '\n')
333 self.ui.warn(l + '\n')
334 elif l.find('FAILED') >= 0:
334 elif l.find('FAILED') >= 0:
335 if not printed_file:
335 if not printed_file:
336 self.ui.warn(file_str + '\n')
336 self.ui.warn(file_str + '\n')
337 printed_file = True
337 printed_file = True
338 self.ui.warn(l + '\n')
338 self.ui.warn(l + '\n')
339 patcherr = f.close()
339 patcherr = f.close()
340
340
341 if merge and len(files) > 0:
341 if merge and len(files) > 0:
342 # Mark as merged and update dirstate parent info
342 # Mark as merged and update dirstate parent info
343 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
343 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
344 p1, p2 = repo.dirstate.parents()
344 p1, p2 = repo.dirstate.parents()
345 repo.dirstate.setparents(p1, merge)
345 repo.dirstate.setparents(p1, merge)
346 if len(files) > 0:
346 if len(files) > 0:
347 cwd = repo.getcwd()
347 cwd = repo.getcwd()
348 cfiles = files
348 cfiles = files
349 if cwd:
349 if cwd:
350 cfiles = [util.pathto(cwd, f) for f in files]
350 cfiles = [util.pathto(cwd, f) for f in files]
351 commands.addremove_lock(self.ui, repo, cfiles,
351 commands.addremove_lock(self.ui, repo, cfiles,
352 opts={}, wlock=wlock)
352 opts={}, wlock=wlock)
353 n = repo.commit(files, message, user, date, force=1, lock=lock,
353 n = repo.commit(files, message, user, date, force=1, lock=lock,
354 wlock=wlock)
354 wlock=wlock)
355
355
356 if n == None:
356 if n == None:
357 raise util.Abort(_("repo commit failed"))
357 raise util.Abort(_("repo commit failed"))
358
358
359 if update_status:
359 if update_status:
360 self.applied.append(revlog.hex(n) + ":" + patch)
360 self.applied.append(revlog.hex(n) + ":" + patch)
361
361
362 if patcherr:
362 if patcherr:
363 if not patchfound:
363 if not patchfound:
364 self.ui.warn("patch %s is empty\n" % patch)
364 self.ui.warn("patch %s is empty\n" % patch)
365 err = 0
365 err = 0
366 else:
366 else:
367 self.ui.warn("patch failed, rejects left in working dir\n")
367 self.ui.warn("patch failed, rejects left in working dir\n")
368 err = 1
368 err = 1
369 break
369 break
370
370
371 if fuzz and strict:
371 if fuzz and strict:
372 self.ui.warn("fuzz found when applying patch, stopping\n")
372 self.ui.warn("fuzz found when applying patch, stopping\n")
373 err = 1
373 err = 1
374 break
374 break
375 tr.close()
375 tr.close()
376 return (err, n)
376 return (err, n)
377
377
378 def delete(self, repo, patch):
378 def delete(self, repo, patch):
379 patch = self.lookup(patch, strict=True)
379 patch = self.lookup(patch, strict=True)
380 info = self.isapplied(patch)
380 info = self.isapplied(patch)
381 if info:
381 if info:
382 raise util.Abort(_("cannot delete applied patch %s") % patch)
382 raise util.Abort(_("cannot delete applied patch %s") % patch)
383 if patch not in self.series:
383 if patch not in self.series:
384 raise util.Abort(_("patch %s not in series file") % patch)
384 raise util.Abort(_("patch %s not in series file") % patch)
385 i = self.find_series(patch)
385 i = self.find_series(patch)
386 del self.full_series[i]
386 del self.full_series[i]
387 self.read_series(self.full_series)
387 self.read_series(self.full_series)
388 self.series_dirty = 1
388 self.series_dirty = 1
389
389
390 def check_toppatch(self, repo):
390 def check_toppatch(self, repo):
391 if len(self.applied) > 0:
391 if len(self.applied) > 0:
392 (top, patch) = self.applied[-1].split(':')
392 (top, patch) = self.applied[-1].split(':')
393 top = revlog.bin(top)
393 top = revlog.bin(top)
394 pp = repo.dirstate.parents()
394 pp = repo.dirstate.parents()
395 if top not in pp:
395 if top not in pp:
396 raise util.Abort(_("queue top not at same revision as working directory"))
396 raise util.Abort(_("queue top not at same revision as working directory"))
397 return top
397 return top
398 return None
398 return None
399 def check_localchanges(self, repo):
399 def check_localchanges(self, repo):
400 (c, a, r, d, u) = repo.changes(None, None)
400 (c, a, r, d, u) = repo.changes(None, None)
401 if c or a or d or r:
401 if c or a or d or r:
402 raise util.Abort(_("local changes found, refresh first"))
402 raise util.Abort(_("local changes found, refresh first"))
403 def new(self, repo, patch, msg=None, force=None):
403 def new(self, repo, patch, msg=None, force=None):
404 if os.path.exists(os.path.join(self.path, patch)):
404 if os.path.exists(os.path.join(self.path, patch)):
405 raise util.Abort(_('patch "%s" already exists') % patch)
405 raise util.Abort(_('patch "%s" already exists') % patch)
406 commitfiles = []
406 commitfiles = []
407 (c, a, r, d, u) = repo.changes(None, None)
407 (c, a, r, d, u) = repo.changes(None, None)
408 if c or a or d or r:
408 if c or a or d or r:
409 if not force:
409 if not force:
410 raise util.Abort(_("local changes found, refresh first"))
410 raise util.Abort(_("local changes found, refresh first"))
411 commitfiles = c + a + r
411 commitfiles = c + a + r
412 self.check_toppatch(repo)
412 self.check_toppatch(repo)
413 wlock = repo.wlock()
413 wlock = repo.wlock()
414 insert = self.full_series_end()
414 insert = self.full_series_end()
415 if msg:
415 if msg:
416 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
416 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
417 wlock=wlock)
417 wlock=wlock)
418 else:
418 else:
419 n = repo.commit(commitfiles,
419 n = repo.commit(commitfiles,
420 "New patch: %s" % patch, force=True, wlock=wlock)
420 "New patch: %s" % patch, force=True, wlock=wlock)
421 if n == None:
421 if n == None:
422 raise util.Abort(_("repo commit failed"))
422 raise util.Abort(_("repo commit failed"))
423 self.full_series[insert:insert] = [patch]
423 self.full_series[insert:insert] = [patch]
424 self.applied.append(revlog.hex(n) + ":" + patch)
424 self.applied.append(revlog.hex(n) + ":" + patch)
425 self.read_series(self.full_series)
425 self.read_series(self.full_series)
426 self.series_dirty = 1
426 self.series_dirty = 1
427 self.applied_dirty = 1
427 self.applied_dirty = 1
428 p = self.opener(patch, "w")
428 p = self.opener(patch, "w")
429 if msg:
429 if msg:
430 msg = msg + "\n"
430 msg = msg + "\n"
431 p.write(msg)
431 p.write(msg)
432 p.close()
432 p.close()
433 wlock = None
433 wlock = None
434 r = self.qrepo()
434 r = self.qrepo()
435 if r: r.add([patch])
435 if r: r.add([patch])
436 if commitfiles:
436 if commitfiles:
437 self.refresh(repo, msg=None, short=True)
437 self.refresh(repo, msg=None, short=True)
438
438
439 def strip(self, repo, rev, update=True, backup="all", wlock=None):
439 def strip(self, repo, rev, update=True, backup="all", wlock=None):
440 def limitheads(chlog, stop):
440 def limitheads(chlog, stop):
441 """return the list of all nodes that have no children"""
441 """return the list of all nodes that have no children"""
442 p = {}
442 p = {}
443 h = []
443 h = []
444 stoprev = 0
444 stoprev = 0
445 if stop in chlog.nodemap:
445 if stop in chlog.nodemap:
446 stoprev = chlog.rev(stop)
446 stoprev = chlog.rev(stop)
447
447
448 for r in range(chlog.count() - 1, -1, -1):
448 for r in range(chlog.count() - 1, -1, -1):
449 n = chlog.node(r)
449 n = chlog.node(r)
450 if n not in p:
450 if n not in p:
451 h.append(n)
451 h.append(n)
452 if n == stop:
452 if n == stop:
453 break
453 break
454 if r < stoprev:
454 if r < stoprev:
455 break
455 break
456 for pn in chlog.parents(n):
456 for pn in chlog.parents(n):
457 p[pn] = 1
457 p[pn] = 1
458 return h
458 return h
459
459
460 def bundle(cg):
460 def bundle(cg):
461 backupdir = repo.join("strip-backup")
461 backupdir = repo.join("strip-backup")
462 if not os.path.isdir(backupdir):
462 if not os.path.isdir(backupdir):
463 os.mkdir(backupdir)
463 os.mkdir(backupdir)
464 name = os.path.join(backupdir, "%s" % revlog.short(rev))
464 name = os.path.join(backupdir, "%s" % revlog.short(rev))
465 name = savename(name)
465 name = savename(name)
466 self.ui.warn("saving bundle to %s\n" % name)
466 self.ui.warn("saving bundle to %s\n" % name)
467 # TODO, exclusive open
467 # TODO, exclusive open
468 f = open(name, "wb")
468 f = open(name, "wb")
469 try:
469 try:
470 f.write("HG10")
470 f.write("HG10")
471 z = bz2.BZ2Compressor(9)
471 z = bz2.BZ2Compressor(9)
472 while 1:
472 while 1:
473 chunk = cg.read(4096)
473 chunk = cg.read(4096)
474 if not chunk:
474 if not chunk:
475 break
475 break
476 f.write(z.compress(chunk))
476 f.write(z.compress(chunk))
477 f.write(z.flush())
477 f.write(z.flush())
478 except:
478 except:
479 os.unlink(name)
479 os.unlink(name)
480 raise
480 raise
481 f.close()
481 f.close()
482 return name
482 return name
483
483
484 def stripall(rev, revnum):
484 def stripall(rev, revnum):
485 cl = repo.changelog
485 cl = repo.changelog
486 c = cl.read(rev)
486 c = cl.read(rev)
487 mm = repo.manifest.read(c[0])
487 mm = repo.manifest.read(c[0])
488 seen = {}
488 seen = {}
489
489
490 for x in xrange(revnum, cl.count()):
490 for x in xrange(revnum, cl.count()):
491 c = cl.read(cl.node(x))
491 c = cl.read(cl.node(x))
492 for f in c[3]:
492 for f in c[3]:
493 if f in seen:
493 if f in seen:
494 continue
494 continue
495 seen[f] = 1
495 seen[f] = 1
496 if f in mm:
496 if f in mm:
497 filerev = mm[f]
497 filerev = mm[f]
498 else:
498 else:
499 filerev = 0
499 filerev = 0
500 seen[f] = filerev
500 seen[f] = filerev
501 # we go in two steps here so the strip loop happens in a
501 # we go in two steps here so the strip loop happens in a
502 # sensible order. When stripping many files, this helps keep
502 # sensible order. When stripping many files, this helps keep
503 # our disk access patterns under control.
503 # our disk access patterns under control.
504 list = seen.keys()
504 list = seen.keys()
505 list.sort()
505 list.sort()
506 for f in list:
506 for f in list:
507 ff = repo.file(f)
507 ff = repo.file(f)
508 filerev = seen[f]
508 filerev = seen[f]
509 if filerev != 0:
509 if filerev != 0:
510 if filerev in ff.nodemap:
510 if filerev in ff.nodemap:
511 filerev = ff.rev(filerev)
511 filerev = ff.rev(filerev)
512 else:
512 else:
513 filerev = 0
513 filerev = 0
514 ff.strip(filerev, revnum)
514 ff.strip(filerev, revnum)
515
515
516 if not wlock:
516 if not wlock:
517 wlock = repo.wlock()
517 wlock = repo.wlock()
518 lock = repo.lock()
518 lock = repo.lock()
519 chlog = repo.changelog
519 chlog = repo.changelog
520 # TODO delete the undo files, and handle undo of merge sets
520 # TODO delete the undo files, and handle undo of merge sets
521 pp = chlog.parents(rev)
521 pp = chlog.parents(rev)
522 revnum = chlog.rev(rev)
522 revnum = chlog.rev(rev)
523
523
524 if update:
524 if update:
525 (c, a, r, d, u) = repo.changes(None, None)
525 (c, a, r, d, u) = repo.changes(None, None)
526 if c or a or d or r:
526 if c or a or d or r:
527 raise util.Abort(_("local changes found"))
527 raise util.Abort(_("local changes found"))
528 urev = self.qparents(repo, rev)
528 urev = self.qparents(repo, rev)
529 repo.update(urev, allow=False, force=True, wlock=wlock)
529 repo.update(urev, allow=False, force=True, wlock=wlock)
530 repo.dirstate.write()
530 repo.dirstate.write()
531
531
532 # save is a list of all the branches we are truncating away
532 # save is a list of all the branches we are truncating away
533 # that we actually want to keep. changegroup will be used
533 # that we actually want to keep. changegroup will be used
534 # to preserve them and add them back after the truncate
534 # to preserve them and add them back after the truncate
535 saveheads = []
535 saveheads = []
536 savebases = {}
536 savebases = {}
537
537
538 tip = chlog.tip()
538 tip = chlog.tip()
539 heads = limitheads(chlog, rev)
539 heads = limitheads(chlog, rev)
540 seen = {}
540 seen = {}
541
541
542 # search through all the heads, finding those where the revision
542 # search through all the heads, finding those where the revision
543 # we want to strip away is an ancestor. Also look for merges
543 # we want to strip away is an ancestor. Also look for merges
544 # that might be turned into new heads by the strip.
544 # that might be turned into new heads by the strip.
545 while heads:
545 while heads:
546 h = heads.pop()
546 h = heads.pop()
547 n = h
547 n = h
548 while True:
548 while True:
549 seen[n] = 1
549 seen[n] = 1
550 pp = chlog.parents(n)
550 pp = chlog.parents(n)
551 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
551 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
552 if pp[1] not in seen:
552 if pp[1] not in seen:
553 heads.append(pp[1])
553 heads.append(pp[1])
554 if pp[0] == revlog.nullid:
554 if pp[0] == revlog.nullid:
555 break
555 break
556 if chlog.rev(pp[0]) < revnum:
556 if chlog.rev(pp[0]) < revnum:
557 break
557 break
558 n = pp[0]
558 n = pp[0]
559 if n == rev:
559 if n == rev:
560 break
560 break
561 r = chlog.reachable(h, rev)
561 r = chlog.reachable(h, rev)
562 if rev not in r:
562 if rev not in r:
563 saveheads.append(h)
563 saveheads.append(h)
564 for x in r:
564 for x in r:
565 if chlog.rev(x) > revnum:
565 if chlog.rev(x) > revnum:
566 savebases[x] = 1
566 savebases[x] = 1
567
567
568 # create a changegroup for all the branches we need to keep
568 # create a changegroup for all the branches we need to keep
569 if backup is "all":
569 if backup is "all":
570 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
570 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
571 bundle(backupch)
571 bundle(backupch)
572 if saveheads:
572 if saveheads:
573 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
573 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
574 chgrpfile = bundle(backupch)
574 chgrpfile = bundle(backupch)
575
575
576 stripall(rev, revnum)
576 stripall(rev, revnum)
577
577
578 change = chlog.read(rev)
578 change = chlog.read(rev)
579 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
579 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
580 chlog.strip(revnum, revnum)
580 chlog.strip(revnum, revnum)
581 if saveheads:
581 if saveheads:
582 self.ui.status("adding branch\n")
582 self.ui.status("adding branch\n")
583 commands.unbundle(self.ui, repo, chgrpfile, update=False)
583 commands.unbundle(self.ui, repo, chgrpfile, update=False)
584 if backup is not "strip":
584 if backup is not "strip":
585 os.unlink(chgrpfile)
585 os.unlink(chgrpfile)
586
586
587 def isapplied(self, patch):
587 def isapplied(self, patch):
588 """returns (index, rev, patch)"""
588 """returns (index, rev, patch)"""
589 for i in xrange(len(self.applied)):
589 for i in xrange(len(self.applied)):
590 p = self.applied[i]
590 p = self.applied[i]
591 a = p.split(':')
591 a = p.split(':')
592 if a[1] == patch:
592 if a[1] == patch:
593 return (i, a[0], a[1])
593 return (i, a[0], a[1])
594 return None
594 return None
595
595
596 # if the exact patch name does not exist, we try a few
596 # if the exact patch name does not exist, we try a few
597 # variations. If strict is passed, we try only #1
597 # variations. If strict is passed, we try only #1
598 #
598 #
599 # 1) a number to indicate an offset in the series file
599 # 1) a number to indicate an offset in the series file
600 # 2) a unique substring of the patch name was given
600 # 2) a unique substring of the patch name was given
601 # 3) patchname[-+]num to indicate an offset in the series file
601 # 3) patchname[-+]num to indicate an offset in the series file
602 def lookup(self, patch, strict=False):
602 def lookup(self, patch, strict=False):
603 def partial_name(s):
603 def partial_name(s):
604 count = 0
604 count = 0
605 if s in self.series:
605 if s in self.series:
606 return s
606 return s
607 for x in self.series:
607 for x in self.series:
608 if s in x:
608 if s in x:
609 count += 1
609 count += 1
610 last = x
610 last = x
611 if count > 1:
611 if count > 1:
612 return None
612 return None
613 if count:
613 if count:
614 return last
614 return last
615 if len(self.series) > 0 and len(self.applied) > 0:
615 if len(self.series) > 0 and len(self.applied) > 0:
616 if s == 'qtip':
616 if s == 'qtip':
617 return self.series[self.series_end()-1]
617 return self.series[self.series_end()-1]
618 if s == 'qbase':
618 if s == 'qbase':
619 return self.series[0]
619 return self.series[0]
620 return None
620 return None
621 if patch == None:
621 if patch == None:
622 return None
622 return None
623
623
624 # we don't want to return a partial match until we make
624 # we don't want to return a partial match until we make
625 # sure the file name passed in does not exist (checked below)
625 # sure the file name passed in does not exist (checked below)
626 res = partial_name(patch)
626 res = partial_name(patch)
627 if res and res == patch:
627 if res and res == patch:
628 return res
628 return res
629
629
630 if not os.path.isfile(os.path.join(self.path, patch)):
630 if not os.path.isfile(os.path.join(self.path, patch)):
631 try:
631 try:
632 sno = int(patch)
632 sno = int(patch)
633 except(ValueError, OverflowError):
633 except(ValueError, OverflowError):
634 pass
634 pass
635 else:
635 else:
636 if sno < len(self.series):
636 if sno < len(self.series):
637 patch = self.series[sno]
637 patch = self.series[sno]
638 return patch
638 return patch
639 if not strict:
639 if not strict:
640 # return any partial match made above
640 # return any partial match made above
641 if res:
641 if res:
642 return res
642 return res
643 minus = patch.rsplit('-', 1)
643 minus = patch.rsplit('-', 1)
644 if len(minus) > 1:
644 if len(minus) > 1:
645 res = partial_name(minus[0])
645 res = partial_name(minus[0])
646 if res:
646 if res:
647 i = self.series.index(res)
647 i = self.series.index(res)
648 try:
648 try:
649 off = int(minus[1] or 1)
649 off = int(minus[1] or 1)
650 except(ValueError, OverflowError):
650 except(ValueError, OverflowError):
651 pass
651 pass
652 else:
652 else:
653 if i - off >= 0:
653 if i - off >= 0:
654 return self.series[i - off]
654 return self.series[i - off]
655 plus = patch.rsplit('+', 1)
655 plus = patch.rsplit('+', 1)
656 if len(plus) > 1:
656 if len(plus) > 1:
657 res = partial_name(plus[0])
657 res = partial_name(plus[0])
658 if res:
658 if res:
659 i = self.series.index(res)
659 i = self.series.index(res)
660 try:
660 try:
661 off = int(plus[1] or 1)
661 off = int(plus[1] or 1)
662 except(ValueError, OverflowError):
662 except(ValueError, OverflowError):
663 pass
663 pass
664 else:
664 else:
665 if i + off < len(self.series):
665 if i + off < len(self.series):
666 return self.series[i + off]
666 return self.series[i + off]
667 raise util.Abort(_("patch %s not in series") % patch)
667 raise util.Abort(_("patch %s not in series") % patch)
668
668
669 def push(self, repo, patch=None, force=False, list=False,
669 def push(self, repo, patch=None, force=False, list=False,
670 mergeq=None, wlock=None):
670 mergeq=None, wlock=None):
671 if not wlock:
671 if not wlock:
672 wlock = repo.wlock()
672 wlock = repo.wlock()
673 patch = self.lookup(patch)
673 patch = self.lookup(patch)
674 if patch and self.isapplied(patch):
674 if patch and self.isapplied(patch):
675 self.ui.warn(_("patch %s is already applied\n") % patch)
675 self.ui.warn(_("patch %s is already applied\n") % patch)
676 sys.exit(1)
676 sys.exit(1)
677 if self.series_end() == len(self.series):
677 if self.series_end() == len(self.series):
678 self.ui.warn(_("patch series fully applied\n"))
678 self.ui.warn(_("patch series fully applied\n"))
679 sys.exit(1)
679 sys.exit(1)
680 if not force:
680 if not force:
681 self.check_localchanges(repo)
681 self.check_localchanges(repo)
682
682
683 self.applied_dirty = 1;
683 self.applied_dirty = 1;
684 start = self.series_end()
684 start = self.series_end()
685 if start > 0:
685 if start > 0:
686 self.check_toppatch(repo)
686 self.check_toppatch(repo)
687 if not patch:
687 if not patch:
688 patch = self.series[start]
688 patch = self.series[start]
689 end = start + 1
689 end = start + 1
690 else:
690 else:
691 end = self.series.index(patch, start) + 1
691 end = self.series.index(patch, start) + 1
692 s = self.series[start:end]
692 s = self.series[start:end]
693 if mergeq:
693 if mergeq:
694 ret = self.mergepatch(repo, mergeq, s, wlock)
694 ret = self.mergepatch(repo, mergeq, s, wlock)
695 else:
695 else:
696 ret = self.apply(repo, s, list, wlock=wlock)
696 ret = self.apply(repo, s, list, wlock=wlock)
697 top = self.applied[-1].split(':')[1]
697 top = self.applied[-1].split(':')[1]
698 if ret[0]:
698 if ret[0]:
699 self.ui.write("Errors during apply, please fix and refresh %s\n" %
699 self.ui.write("Errors during apply, please fix and refresh %s\n" %
700 top)
700 top)
701 else:
701 else:
702 self.ui.write("Now at: %s\n" % top)
702 self.ui.write("Now at: %s\n" % top)
703 return ret[0]
703 return ret[0]
704
704
705 def pop(self, repo, patch=None, force=False, update=True, all=False,
705 def pop(self, repo, patch=None, force=False, update=True, all=False,
706 wlock=None):
706 wlock=None):
707 def getfile(f, rev):
707 def getfile(f, rev):
708 t = repo.file(f).read(rev)
708 t = repo.file(f).read(rev)
709 try:
709 try:
710 repo.wfile(f, "w").write(t)
710 repo.wfile(f, "w").write(t)
711 except IOError:
711 except IOError:
712 try:
712 try:
713 os.makedirs(os.path.dirname(repo.wjoin(f)))
713 os.makedirs(os.path.dirname(repo.wjoin(f)))
714 except OSError, err:
714 except OSError, err:
715 if err.errno != errno.EEXIST: raise
715 if err.errno != errno.EEXIST: raise
716 repo.wfile(f, "w").write(t)
716 repo.wfile(f, "w").write(t)
717
717
718 if not wlock:
718 if not wlock:
719 wlock = repo.wlock()
719 wlock = repo.wlock()
720 if patch:
720 if patch:
721 # index, rev, patch
721 # index, rev, patch
722 info = self.isapplied(patch)
722 info = self.isapplied(patch)
723 if not info:
723 if not info:
724 patch = self.lookup(patch)
724 patch = self.lookup(patch)
725 info = self.isapplied(patch)
725 info = self.isapplied(patch)
726 if not info:
726 if not info:
727 raise util.Abort(_("patch %s is not applied") % patch)
727 raise util.Abort(_("patch %s is not applied") % patch)
728 if len(self.applied) == 0:
728 if len(self.applied) == 0:
729 self.ui.warn(_("no patches applied\n"))
729 self.ui.warn(_("no patches applied\n"))
730 sys.exit(1)
730 sys.exit(1)
731
731
732 if not update:
732 if not update:
733 parents = repo.dirstate.parents()
733 parents = repo.dirstate.parents()
734 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
734 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
735 for p in parents:
735 for p in parents:
736 if p in rr:
736 if p in rr:
737 self.ui.warn("qpop: forcing dirstate update\n")
737 self.ui.warn("qpop: forcing dirstate update\n")
738 update = True
738 update = True
739
739
740 if not force and update:
740 if not force and update:
741 self.check_localchanges(repo)
741 self.check_localchanges(repo)
742
742
743 self.applied_dirty = 1;
743 self.applied_dirty = 1;
744 end = len(self.applied)
744 end = len(self.applied)
745 if not patch:
745 if not patch:
746 if all:
746 if all:
747 popi = 0
747 popi = 0
748 else:
748 else:
749 popi = len(self.applied) - 1
749 popi = len(self.applied) - 1
750 else:
750 else:
751 popi = info[0] + 1
751 popi = info[0] + 1
752 if popi >= end:
752 if popi >= end:
753 self.ui.warn("qpop: %s is already at the top\n" % patch)
753 self.ui.warn("qpop: %s is already at the top\n" % patch)
754 return
754 return
755 info = [ popi ] + self.applied[popi].split(':')
755 info = [ popi ] + self.applied[popi].split(':')
756
756
757 start = info[0]
757 start = info[0]
758 rev = revlog.bin(info[1])
758 rev = revlog.bin(info[1])
759
759
760 # we know there are no local changes, so we can make a simplified
760 # we know there are no local changes, so we can make a simplified
761 # form of hg.update.
761 # form of hg.update.
762 if update:
762 if update:
763 top = self.check_toppatch(repo)
763 top = self.check_toppatch(repo)
764 qp = self.qparents(repo, rev)
764 qp = self.qparents(repo, rev)
765 changes = repo.changelog.read(qp)
765 changes = repo.changelog.read(qp)
766 mf1 = repo.manifest.readflags(changes[0])
766 mf1 = repo.manifest.readflags(changes[0])
767 mmap = repo.manifest.read(changes[0])
767 mmap = repo.manifest.read(changes[0])
768 (c, a, r, d, u) = repo.changes(qp, top)
768 (c, a, r, d, u) = repo.changes(qp, top)
769 if d:
769 if d:
770 raise util.Abort("deletions found between repo revs")
770 raise util.Abort("deletions found between repo revs")
771 for f in c:
771 for f in c:
772 getfile(f, mmap[f])
772 getfile(f, mmap[f])
773 for f in r:
773 for f in r:
774 getfile(f, mmap[f])
774 getfile(f, mmap[f])
775 util.set_exec(repo.wjoin(f), mf1[f])
775 util.set_exec(repo.wjoin(f), mf1[f])
776 repo.dirstate.update(c + r, 'n')
776 repo.dirstate.update(c + r, 'n')
777 for f in a:
777 for f in a:
778 try: os.unlink(repo.wjoin(f))
778 try: os.unlink(repo.wjoin(f))
779 except: raise
779 except: raise
780 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
780 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
781 except: pass
781 except: pass
782 if a:
782 if a:
783 repo.dirstate.forget(a)
783 repo.dirstate.forget(a)
784 repo.dirstate.setparents(qp, revlog.nullid)
784 repo.dirstate.setparents(qp, revlog.nullid)
785 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
785 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
786 del self.applied[start:end]
786 del self.applied[start:end]
787 if len(self.applied):
787 if len(self.applied):
788 self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
788 self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
789 else:
789 else:
790 self.ui.write("Patch queue now empty\n")
790 self.ui.write("Patch queue now empty\n")
791
791
792 def diff(self, repo, files):
792 def diff(self, repo, files):
793 top = self.check_toppatch(repo)
793 top = self.check_toppatch(repo)
794 if not top:
794 if not top:
795 self.ui.write("No patches applied\n")
795 self.ui.write("No patches applied\n")
796 return
796 return
797 qp = self.qparents(repo, top)
797 qp = self.qparents(repo, top)
798 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
798 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
799
799
800 def refresh(self, repo, msg=None, short=False):
800 def refresh(self, repo, msg=None, short=False):
801 if len(self.applied) == 0:
801 if len(self.applied) == 0:
802 self.ui.write("No patches applied\n")
802 self.ui.write("No patches applied\n")
803 return
803 return
804 wlock = repo.wlock()
804 wlock = repo.wlock()
805 self.check_toppatch(repo)
805 self.check_toppatch(repo)
806 qp = self.qparents(repo)
806 qp = self.qparents(repo)
807 (top, patch) = self.applied[-1].split(':')
807 (top, patch) = self.applied[-1].split(':')
808 top = revlog.bin(top)
808 top = revlog.bin(top)
809 cparents = repo.changelog.parents(top)
809 cparents = repo.changelog.parents(top)
810 patchparent = self.qparents(repo, top)
810 patchparent = self.qparents(repo, top)
811 message, comments, user, date, patchfound = self.readheaders(patch)
811 message, comments, user, date, patchfound = self.readheaders(patch)
812
812
813 patchf = self.opener(patch, "w")
813 patchf = self.opener(patch, "w")
814 if comments:
814 if comments:
815 comments = "\n".join(comments) + '\n\n'
815 comments = "\n".join(comments) + '\n\n'
816 patchf.write(comments)
816 patchf.write(comments)
817
817
818 tip = repo.changelog.tip()
818 tip = repo.changelog.tip()
819 if top == tip:
819 if top == tip:
820 # if the top of our patch queue is also the tip, there is an
820 # if the top of our patch queue is also the tip, there is an
821 # optimization here. We update the dirstate in place and strip
821 # optimization here. We update the dirstate in place and strip
822 # off the tip commit. Then just commit the current directory
822 # off the tip commit. Then just commit the current directory
823 # tree. We can also send repo.commit the list of files
823 # tree. We can also send repo.commit the list of files
824 # changed to speed up the diff
824 # changed to speed up the diff
825 #
825 #
826 # in short mode, we only diff the files included in the
826 # in short mode, we only diff the files included in the
827 # patch already
827 # patch already
828 #
828 #
829 # this should really read:
829 # this should really read:
830 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
830 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
831 # but we do it backwards to take advantage of manifest/chlog
831 # but we do it backwards to take advantage of manifest/chlog
832 # caching against the next repo.changes call
832 # caching against the next repo.changes call
833 #
833 #
834 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
834 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
835 if short:
835 if short:
836 filelist = cc + aa + dd
836 filelist = cc + aa + dd
837 else:
837 else:
838 filelist = None
838 filelist = None
839 (c, a, r, d, u) = repo.changes(None, None, filelist)
839 (c, a, r, d, u) = repo.changes(None, None, filelist)
840
840
841 # we might end up with files that were added between tip and
841 # we might end up with files that were added between tip and
842 # the dirstate parent, but then changed in the local dirstate.
842 # the dirstate parent, but then changed in the local dirstate.
843 # in this case, we want them to only show up in the added section
843 # in this case, we want them to only show up in the added section
844 for x in c:
844 for x in c:
845 if x not in aa:
845 if x not in aa:
846 cc.append(x)
846 cc.append(x)
847 # we might end up with files added by the local dirstate that
847 # we might end up with files added by the local dirstate that
848 # were deleted by the patch. In this case, they should only
848 # were deleted by the patch. In this case, they should only
849 # show up in the changed section.
849 # show up in the changed section.
850 for x in a:
850 for x in a:
851 if x in dd:
851 if x in dd:
852 del dd[dd.index(x)]
852 del dd[dd.index(x)]
853 cc.append(x)
853 cc.append(x)
854 else:
854 else:
855 aa.append(x)
855 aa.append(x)
856 # make sure any files deleted in the local dirstate
856 # make sure any files deleted in the local dirstate
857 # are not in the add or change column of the patch
857 # are not in the add or change column of the patch
858 forget = []
858 forget = []
859 for x in d + r:
859 for x in d + r:
860 if x in aa:
860 if x in aa:
861 del aa[aa.index(x)]
861 del aa[aa.index(x)]
862 forget.append(x)
862 forget.append(x)
863 continue
863 continue
864 elif x in cc:
864 elif x in cc:
865 del cc[cc.index(x)]
865 del cc[cc.index(x)]
866 dd.append(x)
866 dd.append(x)
867
867
868 c = list(util.unique(cc))
868 c = list(util.unique(cc))
869 r = list(util.unique(dd))
869 r = list(util.unique(dd))
870 a = list(util.unique(aa))
870 a = list(util.unique(aa))
871 filelist = list(util.unique(c + r + a ))
871 filelist = list(util.unique(c + r + a ))
872 commands.dodiff(patchf, self.ui, repo, patchparent, None,
872 commands.dodiff(patchf, self.ui, repo, patchparent, None,
873 filelist, changes=(c, a, r, [], u))
873 filelist, changes=(c, a, r, [], u))
874 patchf.close()
874 patchf.close()
875
875
876 changes = repo.changelog.read(tip)
876 changes = repo.changelog.read(tip)
877 repo.dirstate.setparents(*cparents)
877 repo.dirstate.setparents(*cparents)
878 repo.dirstate.update(a, 'a')
878 repo.dirstate.update(a, 'a')
879 repo.dirstate.update(r, 'r')
879 repo.dirstate.update(r, 'r')
880 repo.dirstate.update(c, 'n')
880 repo.dirstate.update(c, 'n')
881 repo.dirstate.forget(forget)
881 repo.dirstate.forget(forget)
882
882
883 if not msg:
883 if not msg:
884 if not message:
884 if not message:
885 message = "patch queue: %s\n" % patch
885 message = "patch queue: %s\n" % patch
886 else:
886 else:
887 message = "\n".join(message)
887 message = "\n".join(message)
888 else:
888 else:
889 message = msg
889 message = msg
890
890
891 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
891 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
892 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
892 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
893 self.applied[-1] = revlog.hex(n) + ':' + patch
893 self.applied[-1] = revlog.hex(n) + ':' + patch
894 self.applied_dirty = 1
894 self.applied_dirty = 1
895 else:
895 else:
896 commands.dodiff(patchf, self.ui, repo, patchparent, None)
896 commands.dodiff(patchf, self.ui, repo, patchparent, None)
897 patchf.close()
897 patchf.close()
898 self.pop(repo, force=True, wlock=wlock)
898 self.pop(repo, force=True, wlock=wlock)
899 self.push(repo, force=True, wlock=wlock)
899 self.push(repo, force=True, wlock=wlock)
900
900
901 def init(self, repo, create=False):
901 def init(self, repo, create=False):
902 if os.path.isdir(self.path):
902 if os.path.isdir(self.path):
903 raise util.Abort(_("patch queue directory already exists"))
903 raise util.Abort(_("patch queue directory already exists"))
904 os.mkdir(self.path)
904 os.mkdir(self.path)
905 if create:
905 if create:
906 return self.qrepo(create=True)
906 return self.qrepo(create=True)
907
907
908 def unapplied(self, repo, patch=None):
908 def unapplied(self, repo, patch=None):
909 if patch and patch not in self.series:
909 if patch and patch not in self.series:
910 raise util.Abort(_("patch %s is not in series file") % patch)
910 raise util.Abort(_("patch %s is not in series file") % patch)
911 if not patch:
911 if not patch:
912 start = self.series_end()
912 start = self.series_end()
913 else:
913 else:
914 start = self.series.index(patch) + 1
914 start = self.series.index(patch) + 1
915 for p in self.series[start:]:
915 for p in self.series[start:]:
916 if self.ui.verbose:
916 if self.ui.verbose:
917 self.ui.write("%d " % self.series.index(p))
917 self.ui.write("%d " % self.series.index(p))
918 self.ui.write("%s\n" % p)
918 self.ui.write("%s\n" % p)
919
919
920 def qseries(self, repo, missing=None):
920 def qseries(self, repo, missing=None):
921 start = self.series_end()
921 start = self.series_end()
922 if not missing:
922 if not missing:
923 for p in self.series[:start]:
923 for p in self.series[:start]:
924 if self.ui.verbose:
924 if self.ui.verbose:
925 self.ui.write("%d A " % self.series.index(p))
925 self.ui.write("%d A " % self.series.index(p))
926 self.ui.write("%s\n" % p)
926 self.ui.write("%s\n" % p)
927 for p in self.series[start:]:
927 for p in self.series[start:]:
928 if self.ui.verbose:
928 if self.ui.verbose:
929 self.ui.write("%d U " % self.series.index(p))
929 self.ui.write("%d U " % self.series.index(p))
930 self.ui.write("%s\n" % p)
930 self.ui.write("%s\n" % p)
931 else:
931 else:
932 list = []
932 list = []
933 for root, dirs, files in os.walk(self.path):
933 for root, dirs, files in os.walk(self.path):
934 d = root[len(self.path) + 1:]
934 d = root[len(self.path) + 1:]
935 for f in files:
935 for f in files:
936 fl = os.path.join(d, f)
936 fl = os.path.join(d, f)
937 if (fl not in self.series and
937 if (fl not in self.series and
938 fl not in (self.status_path, self.series_path)
938 fl not in (self.status_path, self.series_path)
939 and not fl.startswith('.')):
939 and not fl.startswith('.')):
940 list.append(fl)
940 list.append(fl)
941 list.sort()
941 list.sort()
942 if list:
942 if list:
943 for x in list:
943 for x in list:
944 if self.ui.verbose:
944 if self.ui.verbose:
945 self.ui.write("D ")
945 self.ui.write("D ")
946 self.ui.write("%s\n" % x)
946 self.ui.write("%s\n" % x)
947
947
948 def issaveline(self, l):
948 def issaveline(self, l):
949 name = l.split(':')[1]
949 name = l.split(':')[1]
950 if name == '.hg.patches.save.line':
950 if name == '.hg.patches.save.line':
951 return True
951 return True
952
952
953 def qrepo(self, create=False):
953 def qrepo(self, create=False):
954 if create or os.path.isdir(os.path.join(self.path, ".hg")):
954 if create or os.path.isdir(os.path.join(self.path, ".hg")):
955 return hg.repository(self.ui, path=self.path, create=create)
955 return hg.repository(self.ui, path=self.path, create=create)
956
956
957 def restore(self, repo, rev, delete=None, qupdate=None):
957 def restore(self, repo, rev, delete=None, qupdate=None):
958 c = repo.changelog.read(rev)
958 c = repo.changelog.read(rev)
959 desc = c[4].strip()
959 desc = c[4].strip()
960 lines = desc.splitlines()
960 lines = desc.splitlines()
961 i = 0
961 i = 0
962 datastart = None
962 datastart = None
963 series = []
963 series = []
964 applied = []
964 applied = []
965 qpp = None
965 qpp = None
966 for i in xrange(0, len(lines)):
966 for i in xrange(0, len(lines)):
967 if lines[i] == 'Patch Data:':
967 if lines[i] == 'Patch Data:':
968 datastart = i + 1
968 datastart = i + 1
969 elif lines[i].startswith('Dirstate:'):
969 elif lines[i].startswith('Dirstate:'):
970 l = lines[i].rstrip()
970 l = lines[i].rstrip()
971 l = l[10:].split(' ')
971 l = l[10:].split(' ')
972 qpp = [ hg.bin(x) for x in l ]
972 qpp = [ hg.bin(x) for x in l ]
973 elif datastart != None:
973 elif datastart != None:
974 l = lines[i].rstrip()
974 l = lines[i].rstrip()
975 index = l.index(':')
975 index = l.index(':')
976 id = l[:index]
976 id = l[:index]
977 file = l[index + 1:]
977 file = l[index + 1:]
978 if id:
978 if id:
979 applied.append(l)
979 applied.append(l)
980 series.append(file)
980 series.append(file)
981 if datastart == None:
981 if datastart == None:
982 self.ui.warn("No saved patch data found\n")
982 self.ui.warn("No saved patch data found\n")
983 return 1
983 return 1
984 self.ui.warn("restoring status: %s\n" % lines[0])
984 self.ui.warn("restoring status: %s\n" % lines[0])
985 self.full_series = series
985 self.full_series = series
986 self.applied = applied
986 self.applied = applied
987 self.read_series(self.full_series)
987 self.read_series(self.full_series)
988 self.series_dirty = 1
988 self.series_dirty = 1
989 self.applied_dirty = 1
989 self.applied_dirty = 1
990 heads = repo.changelog.heads()
990 heads = repo.changelog.heads()
991 if delete:
991 if delete:
992 if rev not in heads:
992 if rev not in heads:
993 self.ui.warn("save entry has children, leaving it alone\n")
993 self.ui.warn("save entry has children, leaving it alone\n")
994 else:
994 else:
995 self.ui.warn("removing save entry %s\n" % hg.short(rev))
995 self.ui.warn("removing save entry %s\n" % hg.short(rev))
996 pp = repo.dirstate.parents()
996 pp = repo.dirstate.parents()
997 if rev in pp:
997 if rev in pp:
998 update = True
998 update = True
999 else:
999 else:
1000 update = False
1000 update = False
1001 self.strip(repo, rev, update=update, backup='strip')
1001 self.strip(repo, rev, update=update, backup='strip')
1002 if qpp:
1002 if qpp:
1003 self.ui.warn("saved queue repository parents: %s %s\n" %
1003 self.ui.warn("saved queue repository parents: %s %s\n" %
1004 (hg.short(qpp[0]), hg.short(qpp[1])))
1004 (hg.short(qpp[0]), hg.short(qpp[1])))
1005 if qupdate:
1005 if qupdate:
1006 print "queue directory updating"
1006 print "queue directory updating"
1007 r = self.qrepo()
1007 r = self.qrepo()
1008 if not r:
1008 if not r:
1009 self.ui.warn("Unable to load queue repository\n")
1009 self.ui.warn("Unable to load queue repository\n")
1010 return 1
1010 return 1
1011 r.update(qpp[0], allow=False, force=True)
1011 r.update(qpp[0], allow=False, force=True)
1012
1012
1013 def save(self, repo, msg=None):
1013 def save(self, repo, msg=None):
1014 if len(self.applied) == 0:
1014 if len(self.applied) == 0:
1015 self.ui.warn("save: no patches applied, exiting\n")
1015 self.ui.warn("save: no patches applied, exiting\n")
1016 return 1
1016 return 1
1017 if self.issaveline(self.applied[-1]):
1017 if self.issaveline(self.applied[-1]):
1018 self.ui.warn("status is already saved\n")
1018 self.ui.warn("status is already saved\n")
1019 return 1
1019 return 1
1020
1020
1021 ar = [ ':' + x for x in self.full_series ]
1021 ar = [ ':' + x for x in self.full_series ]
1022 if not msg:
1022 if not msg:
1023 msg = "hg patches saved state"
1023 msg = "hg patches saved state"
1024 else:
1024 else:
1025 msg = "hg patches: " + msg.rstrip('\r\n')
1025 msg = "hg patches: " + msg.rstrip('\r\n')
1026 r = self.qrepo()
1026 r = self.qrepo()
1027 if r:
1027 if r:
1028 pp = r.dirstate.parents()
1028 pp = r.dirstate.parents()
1029 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1029 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1030 msg += "\n\nPatch Data:\n"
1030 msg += "\n\nPatch Data:\n"
1031 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
1031 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
1032 + '\n' or "")
1032 + '\n' or "")
1033 n = repo.commit(None, text, user=None, force=1)
1033 n = repo.commit(None, text, user=None, force=1)
1034 if not n:
1034 if not n:
1035 self.ui.warn("repo commit failed\n")
1035 self.ui.warn("repo commit failed\n")
1036 return 1
1036 return 1
1037 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
1037 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
1038 self.applied_dirty = 1
1038 self.applied_dirty = 1
1039
1039
1040 def full_series_end(self):
1040 def full_series_end(self):
1041 if len(self.applied) > 0:
1041 if len(self.applied) > 0:
1042 (top, p) = self.applied[-1].split(':')
1042 (top, p) = self.applied[-1].split(':')
1043 end = self.find_series(p)
1043 end = self.find_series(p)
1044 if end == None:
1044 if end == None:
1045 return len(self.full_series)
1045 return len(self.full_series)
1046 return end + 1
1046 return end + 1
1047 return 0
1047 return 0
1048
1048
1049 def series_end(self):
1049 def series_end(self):
1050 end = 0
1050 end = 0
1051 if len(self.applied) > 0:
1051 if len(self.applied) > 0:
1052 (top, p) = self.applied[-1].split(':')
1052 (top, p) = self.applied[-1].split(':')
1053 try:
1053 try:
1054 end = self.series.index(p)
1054 end = self.series.index(p)
1055 except ValueError:
1055 except ValueError:
1056 return 0
1056 return 0
1057 return end + 1
1057 return end + 1
1058 return end
1058 return end
1059
1059
1060 def qapplied(self, repo, patch=None):
1060 def qapplied(self, repo, patch=None):
1061 if patch and patch not in self.series:
1061 if patch and patch not in self.series:
1062 raise util.Abort(_("patch %s is not in series file") % patch)
1062 raise util.Abort(_("patch %s is not in series file") % patch)
1063 if not patch:
1063 if not patch:
1064 end = len(self.applied)
1064 end = len(self.applied)
1065 else:
1065 else:
1066 end = self.series.index(patch) + 1
1066 end = self.series.index(patch) + 1
1067 for x in xrange(end):
1067 for x in xrange(end):
1068 p = self.appliedname(x)
1068 p = self.appliedname(x)
1069 self.ui.write("%s\n" % p)
1069 self.ui.write("%s\n" % p)
1070
1070
1071 def appliedname(self, index):
1071 def appliedname(self, index):
1072 p = self.applied[index]
1072 p = self.applied[index]
1073 pname = p.split(':')[1]
1073 pname = p.split(':')[1]
1074 if not self.ui.verbose:
1074 if not self.ui.verbose:
1075 p = pname
1075 p = pname
1076 else:
1076 else:
1077 p = str(self.series.index(pname)) + " " + p
1077 p = str(self.series.index(pname)) + " " + p
1078 return p
1078 return p
1079
1079
1080 def top(self, repo):
1080 def top(self, repo):
1081 if len(self.applied):
1081 if len(self.applied):
1082 p = self.appliedname(-1)
1082 p = self.appliedname(-1)
1083 self.ui.write(p + '\n')
1083 self.ui.write(p + '\n')
1084 else:
1084 else:
1085 self.ui.write("No patches applied\n")
1085 self.ui.write("No patches applied\n")
1086
1086
1087 def next(self, repo):
1087 def next(self, repo):
1088 end = self.series_end()
1088 end = self.series_end()
1089 if end == len(self.series):
1089 if end == len(self.series):
1090 self.ui.write("All patches applied\n")
1090 self.ui.write("All patches applied\n")
1091 else:
1091 else:
1092 p = self.series[end]
1092 p = self.series[end]
1093 if self.ui.verbose:
1093 if self.ui.verbose:
1094 self.ui.write("%d " % self.series.index(p))
1094 self.ui.write("%d " % self.series.index(p))
1095 self.ui.write(p + '\n')
1095 self.ui.write(p + '\n')
1096
1096
1097 def prev(self, repo):
1097 def prev(self, repo):
1098 if len(self.applied) > 1:
1098 if len(self.applied) > 1:
1099 p = self.appliedname(-2)
1099 p = self.appliedname(-2)
1100 self.ui.write(p + '\n')
1100 self.ui.write(p + '\n')
1101 elif len(self.applied) == 1:
1101 elif len(self.applied) == 1:
1102 self.ui.write("Only one patch applied\n")
1102 self.ui.write("Only one patch applied\n")
1103 else:
1103 else:
1104 self.ui.write("No patches applied\n")
1104 self.ui.write("No patches applied\n")
1105
1105
1106 def qimport(self, repo, files, patch=None, existing=None, force=None):
1106 def qimport(self, repo, files, patch=None, existing=None, force=None):
1107 if len(files) > 1 and patch:
1107 if len(files) > 1 and patch:
1108 raise util.Abort(_('option "-n" not valid when importing multiple '
1108 raise util.Abort(_('option "-n" not valid when importing multiple '
1109 'files'))
1109 'files'))
1110 i = 0
1110 i = 0
1111 added = []
1111 added = []
1112 for filename in files:
1112 for filename in files:
1113 if existing:
1113 if existing:
1114 if not patch:
1114 if not patch:
1115 patch = filename
1115 patch = filename
1116 if not os.path.isfile(os.path.join(self.path, patch)):
1116 if not os.path.isfile(os.path.join(self.path, patch)):
1117 raise util.Abort(_("patch %s does not exist") % patch)
1117 raise util.Abort(_("patch %s does not exist") % patch)
1118 else:
1118 else:
1119 try:
1119 try:
1120 text = file(filename).read()
1120 text = file(filename).read()
1121 except IOError:
1121 except IOError:
1122 raise util.Abort(_("unable to read %s") % patch)
1122 raise util.Abort(_("unable to read %s") % patch)
1123 if not patch:
1123 if not patch:
1124 patch = os.path.split(filename)[1]
1124 patch = os.path.split(filename)[1]
1125 if not force and os.path.exists(os.path.join(self.path, patch)):
1125 if not force and os.path.exists(os.path.join(self.path, patch)):
1126 raise util.Abort(_('patch "%s" already exists') % patch)
1126 raise util.Abort(_('patch "%s" already exists') % patch)
1127 patchf = self.opener(patch, "w")
1127 patchf = self.opener(patch, "w")
1128 patchf.write(text)
1128 patchf.write(text)
1129 if patch in self.series:
1129 if patch in self.series:
1130 raise util.Abort(_('patch %s is already in the series file')
1130 raise util.Abort(_('patch %s is already in the series file')
1131 % patch)
1131 % patch)
1132 index = self.full_series_end() + i
1132 index = self.full_series_end() + i
1133 self.full_series[index:index] = [patch]
1133 self.full_series[index:index] = [patch]
1134 self.read_series(self.full_series)
1134 self.read_series(self.full_series)
1135 self.ui.warn("adding %s to series file\n" % patch)
1135 self.ui.warn("adding %s to series file\n" % patch)
1136 i += 1
1136 i += 1
1137 added.append(patch)
1137 added.append(patch)
1138 patch = None
1138 patch = None
1139 self.series_dirty = 1
1139 self.series_dirty = 1
1140 qrepo = self.qrepo()
1140 qrepo = self.qrepo()
1141 if qrepo:
1141 if qrepo:
1142 qrepo.add(added)
1142 qrepo.add(added)
1143
1143
1144 def delete(ui, repo, patch, **opts):
1144 def delete(ui, repo, patch, **opts):
1145 """remove a patch from the series file"""
1145 """remove a patch from the series file"""
1146 q = repo.mq
1146 q = repo.mq
1147 q.delete(repo, patch)
1147 q.delete(repo, patch)
1148 q.save_dirty()
1148 q.save_dirty()
1149 return 0
1149 return 0
1150
1150
1151 def applied(ui, repo, patch=None, **opts):
1151 def applied(ui, repo, patch=None, **opts):
1152 """print the patches already applied"""
1152 """print the patches already applied"""
1153 repo.mq.qapplied(repo, patch)
1153 repo.mq.qapplied(repo, patch)
1154 return 0
1154 return 0
1155
1155
1156 def unapplied(ui, repo, patch=None, **opts):
1156 def unapplied(ui, repo, patch=None, **opts):
1157 """print the patches not yet applied"""
1157 """print the patches not yet applied"""
1158 repo.mq.unapplied(repo, patch)
1158 repo.mq.unapplied(repo, patch)
1159 return 0
1159 return 0
1160
1160
1161 def qimport(ui, repo, *filename, **opts):
1161 def qimport(ui, repo, *filename, **opts):
1162 """import a patch"""
1162 """import a patch"""
1163 q = repo.mq
1163 q = repo.mq
1164 q.qimport(repo, filename, patch=opts['name'],
1164 q.qimport(repo, filename, patch=opts['name'],
1165 existing=opts['existing'], force=opts['force'])
1165 existing=opts['existing'], force=opts['force'])
1166 q.save_dirty()
1166 q.save_dirty()
1167 return 0
1167 return 0
1168
1168
1169 def init(ui, repo, **opts):
1169 def init(ui, repo, **opts):
1170 """init a new queue repository"""
1170 """init a new queue repository"""
1171 q = repo.mq
1171 q = repo.mq
1172 r = q.init(repo, create=opts['create_repo'])
1172 r = q.init(repo, create=opts['create_repo'])
1173 q.save_dirty()
1173 q.save_dirty()
1174 if r:
1174 if r:
1175 fp = r.wopener('.hgignore', 'w')
1175 fp = r.wopener('.hgignore', 'w')
1176 print >> fp, 'syntax: glob'
1176 print >> fp, 'syntax: glob'
1177 print >> fp, 'status'
1177 print >> fp, 'status'
1178 fp.close()
1178 fp.close()
1179 r.wopener('series', 'w').close()
1179 r.wopener('series', 'w').close()
1180 r.add(['.hgignore', 'series'])
1180 r.add(['.hgignore', 'series'])
1181 return 0
1181 return 0
1182
1182
1183 def clone(ui, source, dest=None, **opts):
1183 def clone(ui, source, dest=None, **opts):
1184 '''clone main and patch repository at same time
1184 '''clone main and patch repository at same time
1185
1185
1186 If source is local, destination will have no patches applied. If
1186 If source is local, destination will have no patches applied. If
1187 source is remote, this command can not check if patches are
1187 source is remote, this command can not check if patches are
1188 applied in source, so cannot guarantee that patches are not
1188 applied in source, so cannot guarantee that patches are not
1189 applied in destination. If you clone remote repository, be sure
1189 applied in destination. If you clone remote repository, be sure
1190 before that it has no patches applied.
1190 before that it has no patches applied.
1191
1191
1192 Source patch repository is looked for in <src>/.hg/patches by
1192 Source patch repository is looked for in <src>/.hg/patches by
1193 default. Use -p <url> to change.
1193 default. Use -p <url> to change.
1194 '''
1194 '''
1195 ui.setconfig_remoteopts(**opts)
1195 commands.setremoteconfig(**opts)
1196 if dest is None:
1196 if dest is None:
1197 dest = hg.defaultdest(source)
1197 dest = hg.defaultdest(source)
1198 sr = hg.repository(ui, ui.expandpath(source))
1198 sr = hg.repository(ui, ui.expandpath(source))
1199 qbase, destrev = None, None
1199 qbase, destrev = None, None
1200 if sr.local():
1200 if sr.local():
1201 reposetup(ui, sr)
1201 reposetup(ui, sr)
1202 if sr.mq.applied:
1202 if sr.mq.applied:
1203 qbase = revlog.bin(sr.mq.applied[0].split(':')[0])
1203 qbase = revlog.bin(sr.mq.applied[0].split(':')[0])
1204 if not hg.islocal(dest):
1204 if not hg.islocal(dest):
1205 destrev = sr.parents(qbase)[0]
1205 destrev = sr.parents(qbase)[0]
1206 ui.note(_('cloning main repo\n'))
1206 ui.note(_('cloning main repo\n'))
1207 sr, dr = hg.clone(ui, sr, dest,
1207 sr, dr = hg.clone(ui, sr, dest,
1208 pull=opts['pull'],
1208 pull=opts['pull'],
1209 rev=destrev,
1209 rev=destrev,
1210 update=False,
1210 update=False,
1211 stream=opts['uncompressed'])
1211 stream=opts['uncompressed'])
1212 ui.note(_('cloning patch repo\n'))
1212 ui.note(_('cloning patch repo\n'))
1213 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1213 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1214 dr.url() + '/.hg/patches',
1214 dr.url() + '/.hg/patches',
1215 pull=opts['pull'],
1215 pull=opts['pull'],
1216 update=not opts['noupdate'],
1216 update=not opts['noupdate'],
1217 stream=opts['uncompressed'])
1217 stream=opts['uncompressed'])
1218 if dr.local():
1218 if dr.local():
1219 if qbase:
1219 if qbase:
1220 ui.note(_('stripping applied patches from destination repo\n'))
1220 ui.note(_('stripping applied patches from destination repo\n'))
1221 reposetup(ui, dr)
1221 reposetup(ui, dr)
1222 dr.mq.strip(dr, qbase, update=False, backup=None)
1222 dr.mq.strip(dr, qbase, update=False, backup=None)
1223 if not opts['noupdate']:
1223 if not opts['noupdate']:
1224 ui.note(_('updating destination repo\n'))
1224 ui.note(_('updating destination repo\n'))
1225 dr.update(dr.changelog.tip())
1225 dr.update(dr.changelog.tip())
1226
1226
1227 def commit(ui, repo, *pats, **opts):
1227 def commit(ui, repo, *pats, **opts):
1228 """commit changes in the queue repository"""
1228 """commit changes in the queue repository"""
1229 q = repo.mq
1229 q = repo.mq
1230 r = q.qrepo()
1230 r = q.qrepo()
1231 if not r: raise util.Abort('no queue repository')
1231 if not r: raise util.Abort('no queue repository')
1232 commands.commit(r.ui, r, *pats, **opts)
1232 commands.commit(r.ui, r, *pats, **opts)
1233
1233
1234 def series(ui, repo, **opts):
1234 def series(ui, repo, **opts):
1235 """print the entire series file"""
1235 """print the entire series file"""
1236 repo.mq.qseries(repo, missing=opts['missing'])
1236 repo.mq.qseries(repo, missing=opts['missing'])
1237 return 0
1237 return 0
1238
1238
1239 def top(ui, repo, **opts):
1239 def top(ui, repo, **opts):
1240 """print the name of the current patch"""
1240 """print the name of the current patch"""
1241 repo.mq.top(repo)
1241 repo.mq.top(repo)
1242 return 0
1242 return 0
1243
1243
1244 def next(ui, repo, **opts):
1244 def next(ui, repo, **opts):
1245 """print the name of the next patch"""
1245 """print the name of the next patch"""
1246 repo.mq.next(repo)
1246 repo.mq.next(repo)
1247 return 0
1247 return 0
1248
1248
1249 def prev(ui, repo, **opts):
1249 def prev(ui, repo, **opts):
1250 """print the name of the previous patch"""
1250 """print the name of the previous patch"""
1251 repo.mq.prev(repo)
1251 repo.mq.prev(repo)
1252 return 0
1252 return 0
1253
1253
1254 def new(ui, repo, patch, **opts):
1254 def new(ui, repo, patch, **opts):
1255 """create a new patch"""
1255 """create a new patch"""
1256 q = repo.mq
1256 q = repo.mq
1257 message=commands.logmessage(**opts)
1257 message=commands.logmessage(**opts)
1258 q.new(repo, patch, msg=message, force=opts['force'])
1258 q.new(repo, patch, msg=message, force=opts['force'])
1259 q.save_dirty()
1259 q.save_dirty()
1260 return 0
1260 return 0
1261
1261
1262 def refresh(ui, repo, **opts):
1262 def refresh(ui, repo, **opts):
1263 """update the current patch"""
1263 """update the current patch"""
1264 q = repo.mq
1264 q = repo.mq
1265 message=commands.logmessage(**opts)
1265 message=commands.logmessage(**opts)
1266 q.refresh(repo, msg=message, short=opts['short'])
1266 q.refresh(repo, msg=message, short=opts['short'])
1267 q.save_dirty()
1267 q.save_dirty()
1268 return 0
1268 return 0
1269
1269
1270 def diff(ui, repo, *files, **opts):
1270 def diff(ui, repo, *files, **opts):
1271 """diff of the current patch"""
1271 """diff of the current patch"""
1272 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1272 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1273 repo.mq.diff(repo, list(files))
1273 repo.mq.diff(repo, list(files))
1274 return 0
1274 return 0
1275
1275
1276 def lastsavename(path):
1276 def lastsavename(path):
1277 (dir, base) = os.path.split(path)
1277 (dir, base) = os.path.split(path)
1278 names = os.listdir(dir)
1278 names = os.listdir(dir)
1279 namere = re.compile("%s.([0-9]+)" % base)
1279 namere = re.compile("%s.([0-9]+)" % base)
1280 max = None
1280 max = None
1281 maxname = None
1281 maxname = None
1282 for f in names:
1282 for f in names:
1283 m = namere.match(f)
1283 m = namere.match(f)
1284 if m:
1284 if m:
1285 index = int(m.group(1))
1285 index = int(m.group(1))
1286 if max == None or index > max:
1286 if max == None or index > max:
1287 max = index
1287 max = index
1288 maxname = f
1288 maxname = f
1289 if maxname:
1289 if maxname:
1290 return (os.path.join(dir, maxname), max)
1290 return (os.path.join(dir, maxname), max)
1291 return (None, None)
1291 return (None, None)
1292
1292
1293 def savename(path):
1293 def savename(path):
1294 (last, index) = lastsavename(path)
1294 (last, index) = lastsavename(path)
1295 if last is None:
1295 if last is None:
1296 index = 0
1296 index = 0
1297 newpath = path + ".%d" % (index + 1)
1297 newpath = path + ".%d" % (index + 1)
1298 return newpath
1298 return newpath
1299
1299
1300 def push(ui, repo, patch=None, **opts):
1300 def push(ui, repo, patch=None, **opts):
1301 """push the next patch onto the stack"""
1301 """push the next patch onto the stack"""
1302 q = repo.mq
1302 q = repo.mq
1303 mergeq = None
1303 mergeq = None
1304
1304
1305 if opts['all']:
1305 if opts['all']:
1306 patch = q.series[-1]
1306 patch = q.series[-1]
1307 if opts['merge']:
1307 if opts['merge']:
1308 if opts['name']:
1308 if opts['name']:
1309 newpath = opts['name']
1309 newpath = opts['name']
1310 else:
1310 else:
1311 newpath, i = lastsavename(q.path)
1311 newpath, i = lastsavename(q.path)
1312 if not newpath:
1312 if not newpath:
1313 ui.warn("no saved queues found, please use -n\n")
1313 ui.warn("no saved queues found, please use -n\n")
1314 return 1
1314 return 1
1315 mergeq = queue(ui, repo.join(""), newpath)
1315 mergeq = queue(ui, repo.join(""), newpath)
1316 ui.warn("merging with queue at: %s\n" % mergeq.path)
1316 ui.warn("merging with queue at: %s\n" % mergeq.path)
1317 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1317 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1318 mergeq=mergeq)
1318 mergeq=mergeq)
1319 q.save_dirty()
1319 q.save_dirty()
1320 return ret
1320 return ret
1321
1321
1322 def pop(ui, repo, patch=None, **opts):
1322 def pop(ui, repo, patch=None, **opts):
1323 """pop the current patch off the stack"""
1323 """pop the current patch off the stack"""
1324 localupdate = True
1324 localupdate = True
1325 if opts['name']:
1325 if opts['name']:
1326 q = queue(ui, repo.join(""), repo.join(opts['name']))
1326 q = queue(ui, repo.join(""), repo.join(opts['name']))
1327 ui.warn('using patch queue: %s\n' % q.path)
1327 ui.warn('using patch queue: %s\n' % q.path)
1328 localupdate = False
1328 localupdate = False
1329 else:
1329 else:
1330 q = repo.mq
1330 q = repo.mq
1331 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1331 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1332 q.save_dirty()
1332 q.save_dirty()
1333 return 0
1333 return 0
1334
1334
1335 def restore(ui, repo, rev, **opts):
1335 def restore(ui, repo, rev, **opts):
1336 """restore the queue state saved by a rev"""
1336 """restore the queue state saved by a rev"""
1337 rev = repo.lookup(rev)
1337 rev = repo.lookup(rev)
1338 q = repo.mq
1338 q = repo.mq
1339 q.restore(repo, rev, delete=opts['delete'],
1339 q.restore(repo, rev, delete=opts['delete'],
1340 qupdate=opts['update'])
1340 qupdate=opts['update'])
1341 q.save_dirty()
1341 q.save_dirty()
1342 return 0
1342 return 0
1343
1343
1344 def save(ui, repo, **opts):
1344 def save(ui, repo, **opts):
1345 """save current queue state"""
1345 """save current queue state"""
1346 q = repo.mq
1346 q = repo.mq
1347 message=commands.logmessage(**opts)
1347 message=commands.logmessage(**opts)
1348 ret = q.save(repo, msg=message)
1348 ret = q.save(repo, msg=message)
1349 if ret:
1349 if ret:
1350 return ret
1350 return ret
1351 q.save_dirty()
1351 q.save_dirty()
1352 if opts['copy']:
1352 if opts['copy']:
1353 path = q.path
1353 path = q.path
1354 if opts['name']:
1354 if opts['name']:
1355 newpath = os.path.join(q.basepath, opts['name'])
1355 newpath = os.path.join(q.basepath, opts['name'])
1356 if os.path.exists(newpath):
1356 if os.path.exists(newpath):
1357 if not os.path.isdir(newpath):
1357 if not os.path.isdir(newpath):
1358 raise util.Abort(_('destination %s exists and is not '
1358 raise util.Abort(_('destination %s exists and is not '
1359 'a directory') % newpath)
1359 'a directory') % newpath)
1360 if not opts['force']:
1360 if not opts['force']:
1361 raise util.Abort(_('destination %s exists, '
1361 raise util.Abort(_('destination %s exists, '
1362 'use -f to force') % newpath)
1362 'use -f to force') % newpath)
1363 else:
1363 else:
1364 newpath = savename(path)
1364 newpath = savename(path)
1365 ui.warn("copy %s to %s\n" % (path, newpath))
1365 ui.warn("copy %s to %s\n" % (path, newpath))
1366 util.copyfiles(path, newpath)
1366 util.copyfiles(path, newpath)
1367 if opts['empty']:
1367 if opts['empty']:
1368 try:
1368 try:
1369 os.unlink(os.path.join(q.path, q.status_path))
1369 os.unlink(os.path.join(q.path, q.status_path))
1370 except:
1370 except:
1371 pass
1371 pass
1372 return 0
1372 return 0
1373
1373
1374 def strip(ui, repo, rev, **opts):
1374 def strip(ui, repo, rev, **opts):
1375 """strip a revision and all later revs on the same branch"""
1375 """strip a revision and all later revs on the same branch"""
1376 rev = repo.lookup(rev)
1376 rev = repo.lookup(rev)
1377 backup = 'all'
1377 backup = 'all'
1378 if opts['backup']:
1378 if opts['backup']:
1379 backup = 'strip'
1379 backup = 'strip'
1380 elif opts['nobackup']:
1380 elif opts['nobackup']:
1381 backup = 'none'
1381 backup = 'none'
1382 repo.mq.strip(repo, rev, backup=backup)
1382 repo.mq.strip(repo, rev, backup=backup)
1383 return 0
1383 return 0
1384
1384
1385 def version(ui, q=None):
1385 def version(ui, q=None):
1386 """print the version number"""
1386 """print the version number"""
1387 ui.write("mq version %s\n" % versionstr)
1387 ui.write("mq version %s\n" % versionstr)
1388 return 0
1388 return 0
1389
1389
1390 def reposetup(ui, repo):
1390 def reposetup(ui, repo):
1391 class MqRepo(repo.__class__):
1391 class MqRepo(repo.__class__):
1392 def tags(self):
1392 def tags(self):
1393 if self.tagscache:
1393 if self.tagscache:
1394 return self.tagscache
1394 return self.tagscache
1395
1395
1396 tagscache = super(self.__class__, self).tags()
1396 tagscache = super(self.__class__, self).tags()
1397
1397
1398 q = self.mq
1398 q = self.mq
1399 if not q.applied:
1399 if not q.applied:
1400 return tagscache
1400 return tagscache
1401
1401
1402 mqtags = [patch.split(':') for patch in q.applied]
1402 mqtags = [patch.split(':') for patch in q.applied]
1403 mqtags.append((mqtags[-1][0], 'qtip'))
1403 mqtags.append((mqtags[-1][0], 'qtip'))
1404 mqtags.append((mqtags[0][0], 'qbase'))
1404 mqtags.append((mqtags[0][0], 'qbase'))
1405 for patch in mqtags:
1405 for patch in mqtags:
1406 if patch[1] in tagscache:
1406 if patch[1] in tagscache:
1407 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
1407 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
1408 else:
1408 else:
1409 tagscache[patch[1]] = revlog.bin(patch[0])
1409 tagscache[patch[1]] = revlog.bin(patch[0])
1410
1410
1411 return tagscache
1411 return tagscache
1412
1412
1413 repo.__class__ = MqRepo
1413 repo.__class__ = MqRepo
1414 repo.mq = queue(ui, repo.join(""))
1414 repo.mq = queue(ui, repo.join(""))
1415
1415
1416 cmdtable = {
1416 cmdtable = {
1417 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1417 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1418 "qclone": (clone,
1418 "qclone": (clone,
1419 [('', 'pull', None, _('use pull protocol to copy metadata')),
1419 [('', 'pull', None, _('use pull protocol to copy metadata')),
1420 ('U', 'noupdate', None, _('do not update the new working directories')),
1420 ('U', 'noupdate', None, _('do not update the new working directories')),
1421 ('', 'uncompressed', None,
1421 ('', 'uncompressed', None,
1422 _('use uncompressed transfer (fast over LAN)')),
1422 _('use uncompressed transfer (fast over LAN)')),
1423 ('e', 'ssh', '', _('specify ssh command to use')),
1423 ('e', 'ssh', '', _('specify ssh command to use')),
1424 ('p', 'patches', '', _('location of source patch repo')),
1424 ('p', 'patches', '', _('location of source patch repo')),
1425 ('', 'remotecmd', '',
1425 ('', 'remotecmd', '',
1426 _('specify hg command to run on the remote side'))],
1426 _('specify hg command to run on the remote side'))],
1427 'hg qclone [OPTION]... SOURCE [DEST]'),
1427 'hg qclone [OPTION]... SOURCE [DEST]'),
1428 "qcommit|qci":
1428 "qcommit|qci":
1429 (commit,
1429 (commit,
1430 commands.table["^commit|ci"][1],
1430 commands.table["^commit|ci"][1],
1431 'hg qcommit [OPTION]... [FILE]...'),
1431 'hg qcommit [OPTION]... [FILE]...'),
1432 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1432 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1433 "qdelete": (delete, [], 'hg qdelete PATCH'),
1433 "qdelete": (delete, [], 'hg qdelete PATCH'),
1434 "^qimport":
1434 "^qimport":
1435 (qimport,
1435 (qimport,
1436 [('e', 'existing', None, 'import file in patch dir'),
1436 [('e', 'existing', None, 'import file in patch dir'),
1437 ('n', 'name', '', 'patch file name'),
1437 ('n', 'name', '', 'patch file name'),
1438 ('f', 'force', None, 'overwrite existing files')],
1438 ('f', 'force', None, 'overwrite existing files')],
1439 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1439 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1440 "^qinit":
1440 "^qinit":
1441 (init,
1441 (init,
1442 [('c', 'create-repo', None, 'create queue repository')],
1442 [('c', 'create-repo', None, 'create queue repository')],
1443 'hg qinit [-c]'),
1443 'hg qinit [-c]'),
1444 "qnew":
1444 "qnew":
1445 (new,
1445 (new,
1446 [('m', 'message', '', _('use <text> as commit message')),
1446 [('m', 'message', '', _('use <text> as commit message')),
1447 ('l', 'logfile', '', _('read the commit message from <file>')),
1447 ('l', 'logfile', '', _('read the commit message from <file>')),
1448 ('f', 'force', None, 'force')],
1448 ('f', 'force', None, 'force')],
1449 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'),
1449 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'),
1450 "qnext": (next, [], 'hg qnext'),
1450 "qnext": (next, [], 'hg qnext'),
1451 "qprev": (prev, [], 'hg qprev'),
1451 "qprev": (prev, [], 'hg qprev'),
1452 "^qpop":
1452 "^qpop":
1453 (pop,
1453 (pop,
1454 [('a', 'all', None, 'pop all patches'),
1454 [('a', 'all', None, 'pop all patches'),
1455 ('n', 'name', '', 'queue name to pop'),
1455 ('n', 'name', '', 'queue name to pop'),
1456 ('f', 'force', None, 'forget any local changes')],
1456 ('f', 'force', None, 'forget any local changes')],
1457 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1457 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1458 "^qpush":
1458 "^qpush":
1459 (push,
1459 (push,
1460 [('f', 'force', None, 'apply if the patch has rejects'),
1460 [('f', 'force', None, 'apply if the patch has rejects'),
1461 ('l', 'list', None, 'list patch name in commit text'),
1461 ('l', 'list', None, 'list patch name in commit text'),
1462 ('a', 'all', None, 'apply all patches'),
1462 ('a', 'all', None, 'apply all patches'),
1463 ('m', 'merge', None, 'merge from another queue'),
1463 ('m', 'merge', None, 'merge from another queue'),
1464 ('n', 'name', '', 'merge queue name')],
1464 ('n', 'name', '', 'merge queue name')],
1465 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1465 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1466 "^qrefresh":
1466 "^qrefresh":
1467 (refresh,
1467 (refresh,
1468 [('m', 'message', '', _('change commit message with <text>')),
1468 [('m', 'message', '', _('change commit message with <text>')),
1469 ('l', 'logfile', '', _('change commit message with <file> content')),
1469 ('l', 'logfile', '', _('change commit message with <file> content')),
1470 ('s', 'short', None, 'short refresh')],
1470 ('s', 'short', None, 'short refresh')],
1471 'hg qrefresh [-m TEXT] [-l FILE] [-s]'),
1471 'hg qrefresh [-m TEXT] [-l FILE] [-s]'),
1472 "qrestore":
1472 "qrestore":
1473 (restore,
1473 (restore,
1474 [('d', 'delete', None, 'delete save entry'),
1474 [('d', 'delete', None, 'delete save entry'),
1475 ('u', 'update', None, 'update queue working dir')],
1475 ('u', 'update', None, 'update queue working dir')],
1476 'hg qrestore [-d] [-u] REV'),
1476 'hg qrestore [-d] [-u] REV'),
1477 "qsave":
1477 "qsave":
1478 (save,
1478 (save,
1479 [('m', 'message', '', _('use <text> as commit message')),
1479 [('m', 'message', '', _('use <text> as commit message')),
1480 ('l', 'logfile', '', _('read the commit message from <file>')),
1480 ('l', 'logfile', '', _('read the commit message from <file>')),
1481 ('c', 'copy', None, 'copy patch directory'),
1481 ('c', 'copy', None, 'copy patch directory'),
1482 ('n', 'name', '', 'copy directory name'),
1482 ('n', 'name', '', 'copy directory name'),
1483 ('e', 'empty', None, 'clear queue status file'),
1483 ('e', 'empty', None, 'clear queue status file'),
1484 ('f', 'force', None, 'force copy')],
1484 ('f', 'force', None, 'force copy')],
1485 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
1485 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
1486 "qseries":
1486 "qseries":
1487 (series,
1487 (series,
1488 [('m', 'missing', None, 'print patches not in series')],
1488 [('m', 'missing', None, 'print patches not in series')],
1489 'hg qseries [-m]'),
1489 'hg qseries [-m]'),
1490 "^strip":
1490 "^strip":
1491 (strip,
1491 (strip,
1492 [('f', 'force', None, 'force multi-head removal'),
1492 [('f', 'force', None, 'force multi-head removal'),
1493 ('b', 'backup', None, 'bundle unrelated changesets'),
1493 ('b', 'backup', None, 'bundle unrelated changesets'),
1494 ('n', 'nobackup', None, 'no backups')],
1494 ('n', 'nobackup', None, 'no backups')],
1495 'hg strip [-f] [-b] [-n] REV'),
1495 'hg strip [-f] [-b] [-n] REV'),
1496 "qtop": (top, [], 'hg qtop'),
1496 "qtop": (top, [], 'hg qtop'),
1497 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1497 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1498 "qversion": (version, [], 'hg qversion')
1498 "qversion": (version, [], 'hg qversion')
1499 }
1499 }
1500
1500
@@ -1,3565 +1,3572 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 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 from demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 demandload(globals(), "fnmatch mdiff random signal tempfile time")
13 demandload(globals(), "fnmatch mdiff random signal tempfile time")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 demandload(globals(), "archival cStringIO changegroup email.Parser")
15 demandload(globals(), "archival cStringIO changegroup email.Parser")
16 demandload(globals(), "hgweb.server sshserver")
16 demandload(globals(), "hgweb.server sshserver")
17
17
18 class UnknownCommand(Exception):
18 class UnknownCommand(Exception):
19 """Exception raised if command is not in the command table."""
19 """Exception raised if command is not in the command table."""
20 class AmbiguousCommand(Exception):
20 class AmbiguousCommand(Exception):
21 """Exception raised if command shortcut matches more than one command."""
21 """Exception raised if command shortcut matches more than one command."""
22
22
23 def bail_if_changed(repo):
23 def bail_if_changed(repo):
24 modified, added, removed, deleted, unknown = repo.changes()
24 modified, added, removed, deleted, unknown = repo.changes()
25 if modified or added or removed or deleted:
25 if modified or added or removed or deleted:
26 raise util.Abort(_("outstanding uncommitted changes"))
26 raise util.Abort(_("outstanding uncommitted changes"))
27
27
28 def filterfiles(filters, files):
28 def filterfiles(filters, files):
29 l = [x for x in files if x in filters]
29 l = [x for x in files if x in filters]
30
30
31 for t in filters:
31 for t in filters:
32 if t and t[-1] != "/":
32 if t and t[-1] != "/":
33 t += "/"
33 t += "/"
34 l += [x for x in files if x.startswith(t)]
34 l += [x for x in files if x.startswith(t)]
35 return l
35 return l
36
36
37 def relpath(repo, args):
37 def relpath(repo, args):
38 cwd = repo.getcwd()
38 cwd = repo.getcwd()
39 if cwd:
39 if cwd:
40 return [util.normpath(os.path.join(cwd, x)) for x in args]
40 return [util.normpath(os.path.join(cwd, x)) for x in args]
41 return args
41 return args
42
42
43 def logmessage(**opts):
43 def logmessage(**opts):
44 """ get the log message according to -m and -l option """
44 """ get the log message according to -m and -l option """
45 message = opts['message']
45 message = opts['message']
46 logfile = opts['logfile']
46 logfile = opts['logfile']
47
47
48 if message and logfile:
48 if message and logfile:
49 raise util.Abort(_('options --message and --logfile are mutually '
49 raise util.Abort(_('options --message and --logfile are mutually '
50 'exclusive'))
50 'exclusive'))
51 if not message and logfile:
51 if not message and logfile:
52 try:
52 try:
53 if logfile == '-':
53 if logfile == '-':
54 message = sys.stdin.read()
54 message = sys.stdin.read()
55 else:
55 else:
56 message = open(logfile).read()
56 message = open(logfile).read()
57 except IOError, inst:
57 except IOError, inst:
58 raise util.Abort(_("can't read commit message '%s': %s") %
58 raise util.Abort(_("can't read commit message '%s': %s") %
59 (logfile, inst.strerror))
59 (logfile, inst.strerror))
60 return message
60 return message
61
61
62 def matchpats(repo, pats=[], opts={}, head=''):
62 def matchpats(repo, pats=[], opts={}, head=''):
63 cwd = repo.getcwd()
63 cwd = repo.getcwd()
64 if not pats and cwd:
64 if not pats and cwd:
65 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
65 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
66 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
66 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
67 cwd = ''
67 cwd = ''
68 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
68 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
69 opts.get('exclude'), head)
69 opts.get('exclude'), head)
70
70
71 def makewalk(repo, pats, opts, node=None, head='', badmatch=None):
71 def makewalk(repo, pats, opts, node=None, head='', badmatch=None):
72 files, matchfn, anypats = matchpats(repo, pats, opts, head)
72 files, matchfn, anypats = matchpats(repo, pats, opts, head)
73 exact = dict(zip(files, files))
73 exact = dict(zip(files, files))
74 def walk():
74 def walk():
75 for src, fn in repo.walk(node=node, files=files, match=matchfn,
75 for src, fn in repo.walk(node=node, files=files, match=matchfn,
76 badmatch=badmatch):
76 badmatch=badmatch):
77 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
77 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
78 return files, matchfn, walk()
78 return files, matchfn, walk()
79
79
80 def walk(repo, pats, opts, node=None, head='', badmatch=None):
80 def walk(repo, pats, opts, node=None, head='', badmatch=None):
81 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
81 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
82 for r in results:
82 for r in results:
83 yield r
83 yield r
84
84
85 def walkchangerevs(ui, repo, pats, opts):
85 def walkchangerevs(ui, repo, pats, opts):
86 '''Iterate over files and the revs they changed in.
86 '''Iterate over files and the revs they changed in.
87
87
88 Callers most commonly need to iterate backwards over the history
88 Callers most commonly need to iterate backwards over the history
89 it is interested in. Doing so has awful (quadratic-looking)
89 it is interested in. Doing so has awful (quadratic-looking)
90 performance, so we use iterators in a "windowed" way.
90 performance, so we use iterators in a "windowed" way.
91
91
92 We walk a window of revisions in the desired order. Within the
92 We walk a window of revisions in the desired order. Within the
93 window, we first walk forwards to gather data, then in the desired
93 window, we first walk forwards to gather data, then in the desired
94 order (usually backwards) to display it.
94 order (usually backwards) to display it.
95
95
96 This function returns an (iterator, getchange, matchfn) tuple. The
96 This function returns an (iterator, getchange, matchfn) tuple. The
97 getchange function returns the changelog entry for a numeric
97 getchange function returns the changelog entry for a numeric
98 revision. The iterator yields 3-tuples. They will be of one of
98 revision. The iterator yields 3-tuples. They will be of one of
99 the following forms:
99 the following forms:
100
100
101 "window", incrementing, lastrev: stepping through a window,
101 "window", incrementing, lastrev: stepping through a window,
102 positive if walking forwards through revs, last rev in the
102 positive if walking forwards through revs, last rev in the
103 sequence iterated over - use to reset state for the current window
103 sequence iterated over - use to reset state for the current window
104
104
105 "add", rev, fns: out-of-order traversal of the given file names
105 "add", rev, fns: out-of-order traversal of the given file names
106 fns, which changed during revision rev - use to gather data for
106 fns, which changed during revision rev - use to gather data for
107 possible display
107 possible display
108
108
109 "iter", rev, None: in-order traversal of the revs earlier iterated
109 "iter", rev, None: in-order traversal of the revs earlier iterated
110 over with "add" - use to display data'''
110 over with "add" - use to display data'''
111
111
112 def increasing_windows(start, end, windowsize=8, sizelimit=512):
112 def increasing_windows(start, end, windowsize=8, sizelimit=512):
113 if start < end:
113 if start < end:
114 while start < end:
114 while start < end:
115 yield start, min(windowsize, end-start)
115 yield start, min(windowsize, end-start)
116 start += windowsize
116 start += windowsize
117 if windowsize < sizelimit:
117 if windowsize < sizelimit:
118 windowsize *= 2
118 windowsize *= 2
119 else:
119 else:
120 while start > end:
120 while start > end:
121 yield start, min(windowsize, start-end-1)
121 yield start, min(windowsize, start-end-1)
122 start -= windowsize
122 start -= windowsize
123 if windowsize < sizelimit:
123 if windowsize < sizelimit:
124 windowsize *= 2
124 windowsize *= 2
125
125
126
126
127 files, matchfn, anypats = matchpats(repo, pats, opts)
127 files, matchfn, anypats = matchpats(repo, pats, opts)
128
128
129 if repo.changelog.count() == 0:
129 if repo.changelog.count() == 0:
130 return [], False, matchfn
130 return [], False, matchfn
131
131
132 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
132 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
133 wanted = {}
133 wanted = {}
134 slowpath = anypats
134 slowpath = anypats
135 fncache = {}
135 fncache = {}
136
136
137 chcache = {}
137 chcache = {}
138 def getchange(rev):
138 def getchange(rev):
139 ch = chcache.get(rev)
139 ch = chcache.get(rev)
140 if ch is None:
140 if ch is None:
141 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
141 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
142 return ch
142 return ch
143
143
144 if not slowpath and not files:
144 if not slowpath and not files:
145 # No files, no patterns. Display all revs.
145 # No files, no patterns. Display all revs.
146 wanted = dict(zip(revs, revs))
146 wanted = dict(zip(revs, revs))
147 if not slowpath:
147 if not slowpath:
148 # Only files, no patterns. Check the history of each file.
148 # Only files, no patterns. Check the history of each file.
149 def filerevgen(filelog):
149 def filerevgen(filelog):
150 cl_count = repo.changelog.count()
150 cl_count = repo.changelog.count()
151 for i, window in increasing_windows(filelog.count()-1, -1):
151 for i, window in increasing_windows(filelog.count()-1, -1):
152 revs = []
152 revs = []
153 for j in xrange(i - window, i + 1):
153 for j in xrange(i - window, i + 1):
154 revs.append(filelog.linkrev(filelog.node(j)))
154 revs.append(filelog.linkrev(filelog.node(j)))
155 revs.reverse()
155 revs.reverse()
156 for rev in revs:
156 for rev in revs:
157 # only yield rev for which we have the changelog, it can
157 # only yield rev for which we have the changelog, it can
158 # happen while doing "hg log" during a pull or commit
158 # happen while doing "hg log" during a pull or commit
159 if rev < cl_count:
159 if rev < cl_count:
160 yield rev
160 yield rev
161
161
162 minrev, maxrev = min(revs), max(revs)
162 minrev, maxrev = min(revs), max(revs)
163 for file_ in files:
163 for file_ in files:
164 filelog = repo.file(file_)
164 filelog = repo.file(file_)
165 # A zero count may be a directory or deleted file, so
165 # A zero count may be a directory or deleted file, so
166 # try to find matching entries on the slow path.
166 # try to find matching entries on the slow path.
167 if filelog.count() == 0:
167 if filelog.count() == 0:
168 slowpath = True
168 slowpath = True
169 break
169 break
170 for rev in filerevgen(filelog):
170 for rev in filerevgen(filelog):
171 if rev <= maxrev:
171 if rev <= maxrev:
172 if rev < minrev:
172 if rev < minrev:
173 break
173 break
174 fncache.setdefault(rev, [])
174 fncache.setdefault(rev, [])
175 fncache[rev].append(file_)
175 fncache[rev].append(file_)
176 wanted[rev] = 1
176 wanted[rev] = 1
177 if slowpath:
177 if slowpath:
178 # The slow path checks files modified in every changeset.
178 # The slow path checks files modified in every changeset.
179 def changerevgen():
179 def changerevgen():
180 for i, window in increasing_windows(repo.changelog.count()-1, -1):
180 for i, window in increasing_windows(repo.changelog.count()-1, -1):
181 for j in xrange(i - window, i + 1):
181 for j in xrange(i - window, i + 1):
182 yield j, getchange(j)[3]
182 yield j, getchange(j)[3]
183
183
184 for rev, changefiles in changerevgen():
184 for rev, changefiles in changerevgen():
185 matches = filter(matchfn, changefiles)
185 matches = filter(matchfn, changefiles)
186 if matches:
186 if matches:
187 fncache[rev] = matches
187 fncache[rev] = matches
188 wanted[rev] = 1
188 wanted[rev] = 1
189
189
190 def iterate():
190 def iterate():
191 for i, window in increasing_windows(0, len(revs)):
191 for i, window in increasing_windows(0, len(revs)):
192 yield 'window', revs[0] < revs[-1], revs[-1]
192 yield 'window', revs[0] < revs[-1], revs[-1]
193 nrevs = [rev for rev in revs[i:i+window]
193 nrevs = [rev for rev in revs[i:i+window]
194 if rev in wanted]
194 if rev in wanted]
195 srevs = list(nrevs)
195 srevs = list(nrevs)
196 srevs.sort()
196 srevs.sort()
197 for rev in srevs:
197 for rev in srevs:
198 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
198 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
199 yield 'add', rev, fns
199 yield 'add', rev, fns
200 for rev in nrevs:
200 for rev in nrevs:
201 yield 'iter', rev, None
201 yield 'iter', rev, None
202 return iterate(), getchange, matchfn
202 return iterate(), getchange, matchfn
203
203
204 revrangesep = ':'
204 revrangesep = ':'
205
205
206 def revfix(repo, val, defval):
206 def revfix(repo, val, defval):
207 '''turn user-level id of changeset into rev number.
207 '''turn user-level id of changeset into rev number.
208 user-level id can be tag, changeset, rev number, or negative rev
208 user-level id can be tag, changeset, rev number, or negative rev
209 number relative to number of revs (-1 is tip, etc).'''
209 number relative to number of revs (-1 is tip, etc).'''
210 if not val:
210 if not val:
211 return defval
211 return defval
212 try:
212 try:
213 num = int(val)
213 num = int(val)
214 if str(num) != val:
214 if str(num) != val:
215 raise ValueError
215 raise ValueError
216 if num < 0:
216 if num < 0:
217 num += repo.changelog.count()
217 num += repo.changelog.count()
218 if num < 0:
218 if num < 0:
219 num = 0
219 num = 0
220 elif num >= repo.changelog.count():
220 elif num >= repo.changelog.count():
221 raise ValueError
221 raise ValueError
222 except ValueError:
222 except ValueError:
223 try:
223 try:
224 num = repo.changelog.rev(repo.lookup(val))
224 num = repo.changelog.rev(repo.lookup(val))
225 except KeyError:
225 except KeyError:
226 raise util.Abort(_('invalid revision identifier %s'), val)
226 raise util.Abort(_('invalid revision identifier %s'), val)
227 return num
227 return num
228
228
229 def revpair(ui, repo, revs):
229 def revpair(ui, repo, revs):
230 '''return pair of nodes, given list of revisions. second item can
230 '''return pair of nodes, given list of revisions. second item can
231 be None, meaning use working dir.'''
231 be None, meaning use working dir.'''
232 if not revs:
232 if not revs:
233 return repo.dirstate.parents()[0], None
233 return repo.dirstate.parents()[0], None
234 end = None
234 end = None
235 if len(revs) == 1:
235 if len(revs) == 1:
236 start = revs[0]
236 start = revs[0]
237 if revrangesep in start:
237 if revrangesep in start:
238 start, end = start.split(revrangesep, 1)
238 start, end = start.split(revrangesep, 1)
239 start = revfix(repo, start, 0)
239 start = revfix(repo, start, 0)
240 end = revfix(repo, end, repo.changelog.count() - 1)
240 end = revfix(repo, end, repo.changelog.count() - 1)
241 else:
241 else:
242 start = revfix(repo, start, None)
242 start = revfix(repo, start, None)
243 elif len(revs) == 2:
243 elif len(revs) == 2:
244 if revrangesep in revs[0] or revrangesep in revs[1]:
244 if revrangesep in revs[0] or revrangesep in revs[1]:
245 raise util.Abort(_('too many revisions specified'))
245 raise util.Abort(_('too many revisions specified'))
246 start = revfix(repo, revs[0], None)
246 start = revfix(repo, revs[0], None)
247 end = revfix(repo, revs[1], None)
247 end = revfix(repo, revs[1], None)
248 else:
248 else:
249 raise util.Abort(_('too many revisions specified'))
249 raise util.Abort(_('too many revisions specified'))
250 if end is not None: end = repo.lookup(str(end))
250 if end is not None: end = repo.lookup(str(end))
251 return repo.lookup(str(start)), end
251 return repo.lookup(str(start)), end
252
252
253 def revrange(ui, repo, revs):
253 def revrange(ui, repo, revs):
254 """Yield revision as strings from a list of revision specifications."""
254 """Yield revision as strings from a list of revision specifications."""
255 seen = {}
255 seen = {}
256 for spec in revs:
256 for spec in revs:
257 if revrangesep in spec:
257 if revrangesep in spec:
258 start, end = spec.split(revrangesep, 1)
258 start, end = spec.split(revrangesep, 1)
259 start = revfix(repo, start, 0)
259 start = revfix(repo, start, 0)
260 end = revfix(repo, end, repo.changelog.count() - 1)
260 end = revfix(repo, end, repo.changelog.count() - 1)
261 step = start > end and -1 or 1
261 step = start > end and -1 or 1
262 for rev in xrange(start, end+step, step):
262 for rev in xrange(start, end+step, step):
263 if rev in seen:
263 if rev in seen:
264 continue
264 continue
265 seen[rev] = 1
265 seen[rev] = 1
266 yield str(rev)
266 yield str(rev)
267 else:
267 else:
268 rev = revfix(repo, spec, None)
268 rev = revfix(repo, spec, None)
269 if rev in seen:
269 if rev in seen:
270 continue
270 continue
271 seen[rev] = 1
271 seen[rev] = 1
272 yield str(rev)
272 yield str(rev)
273
273
274 def make_filename(repo, pat, node,
274 def make_filename(repo, pat, node,
275 total=None, seqno=None, revwidth=None, pathname=None):
275 total=None, seqno=None, revwidth=None, pathname=None):
276 node_expander = {
276 node_expander = {
277 'H': lambda: hex(node),
277 'H': lambda: hex(node),
278 'R': lambda: str(repo.changelog.rev(node)),
278 'R': lambda: str(repo.changelog.rev(node)),
279 'h': lambda: short(node),
279 'h': lambda: short(node),
280 }
280 }
281 expander = {
281 expander = {
282 '%': lambda: '%',
282 '%': lambda: '%',
283 'b': lambda: os.path.basename(repo.root),
283 'b': lambda: os.path.basename(repo.root),
284 }
284 }
285
285
286 try:
286 try:
287 if node:
287 if node:
288 expander.update(node_expander)
288 expander.update(node_expander)
289 if node and revwidth is not None:
289 if node and revwidth is not None:
290 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
290 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
291 if total is not None:
291 if total is not None:
292 expander['N'] = lambda: str(total)
292 expander['N'] = lambda: str(total)
293 if seqno is not None:
293 if seqno is not None:
294 expander['n'] = lambda: str(seqno)
294 expander['n'] = lambda: str(seqno)
295 if total is not None and seqno is not None:
295 if total is not None and seqno is not None:
296 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
296 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
297 if pathname is not None:
297 if pathname is not None:
298 expander['s'] = lambda: os.path.basename(pathname)
298 expander['s'] = lambda: os.path.basename(pathname)
299 expander['d'] = lambda: os.path.dirname(pathname) or '.'
299 expander['d'] = lambda: os.path.dirname(pathname) or '.'
300 expander['p'] = lambda: pathname
300 expander['p'] = lambda: pathname
301
301
302 newname = []
302 newname = []
303 patlen = len(pat)
303 patlen = len(pat)
304 i = 0
304 i = 0
305 while i < patlen:
305 while i < patlen:
306 c = pat[i]
306 c = pat[i]
307 if c == '%':
307 if c == '%':
308 i += 1
308 i += 1
309 c = pat[i]
309 c = pat[i]
310 c = expander[c]()
310 c = expander[c]()
311 newname.append(c)
311 newname.append(c)
312 i += 1
312 i += 1
313 return ''.join(newname)
313 return ''.join(newname)
314 except KeyError, inst:
314 except KeyError, inst:
315 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
315 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
316 inst.args[0])
316 inst.args[0])
317
317
318 def make_file(repo, pat, node=None,
318 def make_file(repo, pat, node=None,
319 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
319 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
320 if not pat or pat == '-':
320 if not pat or pat == '-':
321 return 'w' in mode and sys.stdout or sys.stdin
321 return 'w' in mode and sys.stdout or sys.stdin
322 if hasattr(pat, 'write') and 'w' in mode:
322 if hasattr(pat, 'write') and 'w' in mode:
323 return pat
323 return pat
324 if hasattr(pat, 'read') and 'r' in mode:
324 if hasattr(pat, 'read') and 'r' in mode:
325 return pat
325 return pat
326 return open(make_filename(repo, pat, node, total, seqno, revwidth,
326 return open(make_filename(repo, pat, node, total, seqno, revwidth,
327 pathname),
327 pathname),
328 mode)
328 mode)
329
329
330 def write_bundle(cg, filename=None, compress=True):
330 def write_bundle(cg, filename=None, compress=True):
331 """Write a bundle file and return its filename.
331 """Write a bundle file and return its filename.
332
332
333 Existing files will not be overwritten.
333 Existing files will not be overwritten.
334 If no filename is specified, a temporary file is created.
334 If no filename is specified, a temporary file is created.
335 bz2 compression can be turned off.
335 bz2 compression can be turned off.
336 The bundle file will be deleted in case of errors.
336 The bundle file will be deleted in case of errors.
337 """
337 """
338 class nocompress(object):
338 class nocompress(object):
339 def compress(self, x):
339 def compress(self, x):
340 return x
340 return x
341 def flush(self):
341 def flush(self):
342 return ""
342 return ""
343
343
344 fh = None
344 fh = None
345 cleanup = None
345 cleanup = None
346 try:
346 try:
347 if filename:
347 if filename:
348 if os.path.exists(filename):
348 if os.path.exists(filename):
349 raise util.Abort(_("file '%s' already exists"), filename)
349 raise util.Abort(_("file '%s' already exists"), filename)
350 fh = open(filename, "wb")
350 fh = open(filename, "wb")
351 else:
351 else:
352 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
352 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
353 fh = os.fdopen(fd, "wb")
353 fh = os.fdopen(fd, "wb")
354 cleanup = filename
354 cleanup = filename
355
355
356 if compress:
356 if compress:
357 fh.write("HG10")
357 fh.write("HG10")
358 z = bz2.BZ2Compressor(9)
358 z = bz2.BZ2Compressor(9)
359 else:
359 else:
360 fh.write("HG10UN")
360 fh.write("HG10UN")
361 z = nocompress()
361 z = nocompress()
362 # parse the changegroup data, otherwise we will block
362 # parse the changegroup data, otherwise we will block
363 # in case of sshrepo because we don't know the end of the stream
363 # in case of sshrepo because we don't know the end of the stream
364
364
365 # an empty chunkiter is the end of the changegroup
365 # an empty chunkiter is the end of the changegroup
366 empty = False
366 empty = False
367 while not empty:
367 while not empty:
368 empty = True
368 empty = True
369 for chunk in changegroup.chunkiter(cg):
369 for chunk in changegroup.chunkiter(cg):
370 empty = False
370 empty = False
371 fh.write(z.compress(changegroup.genchunk(chunk)))
371 fh.write(z.compress(changegroup.genchunk(chunk)))
372 fh.write(z.compress(changegroup.closechunk()))
372 fh.write(z.compress(changegroup.closechunk()))
373 fh.write(z.flush())
373 fh.write(z.flush())
374 cleanup = None
374 cleanup = None
375 return filename
375 return filename
376 finally:
376 finally:
377 if fh is not None:
377 if fh is not None:
378 fh.close()
378 fh.close()
379 if cleanup is not None:
379 if cleanup is not None:
380 os.unlink(cleanup)
380 os.unlink(cleanup)
381
381
382 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
382 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
383 changes=None, text=False, opts={}):
383 changes=None, text=False, opts={}):
384 if not node1:
384 if not node1:
385 node1 = repo.dirstate.parents()[0]
385 node1 = repo.dirstate.parents()[0]
386 # reading the data for node1 early allows it to play nicely
386 # reading the data for node1 early allows it to play nicely
387 # with repo.changes and the revlog cache.
387 # with repo.changes and the revlog cache.
388 change = repo.changelog.read(node1)
388 change = repo.changelog.read(node1)
389 mmap = repo.manifest.read(change[0])
389 mmap = repo.manifest.read(change[0])
390 date1 = util.datestr(change[2])
390 date1 = util.datestr(change[2])
391
391
392 if not changes:
392 if not changes:
393 changes = repo.changes(node1, node2, files, match=match)
393 changes = repo.changes(node1, node2, files, match=match)
394 modified, added, removed, deleted, unknown = changes
394 modified, added, removed, deleted, unknown = changes
395 if files:
395 if files:
396 modified, added, removed = map(lambda x: filterfiles(files, x),
396 modified, added, removed = map(lambda x: filterfiles(files, x),
397 (modified, added, removed))
397 (modified, added, removed))
398
398
399 if not modified and not added and not removed:
399 if not modified and not added and not removed:
400 return
400 return
401
401
402 if node2:
402 if node2:
403 change = repo.changelog.read(node2)
403 change = repo.changelog.read(node2)
404 mmap2 = repo.manifest.read(change[0])
404 mmap2 = repo.manifest.read(change[0])
405 _date2 = util.datestr(change[2])
405 _date2 = util.datestr(change[2])
406 def date2(f):
406 def date2(f):
407 return _date2
407 return _date2
408 def read(f):
408 def read(f):
409 return repo.file(f).read(mmap2[f])
409 return repo.file(f).read(mmap2[f])
410 else:
410 else:
411 tz = util.makedate()[1]
411 tz = util.makedate()[1]
412 _date2 = util.datestr()
412 _date2 = util.datestr()
413 def date2(f):
413 def date2(f):
414 try:
414 try:
415 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
415 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
416 except OSError, err:
416 except OSError, err:
417 if err.errno != errno.ENOENT: raise
417 if err.errno != errno.ENOENT: raise
418 return _date2
418 return _date2
419 def read(f):
419 def read(f):
420 return repo.wread(f)
420 return repo.wread(f)
421
421
422 if ui.quiet:
422 if ui.quiet:
423 r = None
423 r = None
424 else:
424 else:
425 hexfunc = ui.verbose and hex or short
425 hexfunc = ui.verbose and hex or short
426 r = [hexfunc(node) for node in [node1, node2] if node]
426 r = [hexfunc(node) for node in [node1, node2] if node]
427
427
428 diffopts = ui.diffopts()
428 diffopts = ui.diffopts()
429 showfunc = opts.get('show_function') or diffopts['showfunc']
429 showfunc = opts.get('show_function') or diffopts['showfunc']
430 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
430 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
431 ignorewsamount = opts.get('ignore_space_change') or \
431 ignorewsamount = opts.get('ignore_space_change') or \
432 diffopts['ignorewsamount']
432 diffopts['ignorewsamount']
433 ignoreblanklines = opts.get('ignore_blank_lines') or \
433 ignoreblanklines = opts.get('ignore_blank_lines') or \
434 diffopts['ignoreblanklines']
434 diffopts['ignoreblanklines']
435
435
436 all = modified + added + removed
436 all = modified + added + removed
437 all.sort()
437 all.sort()
438 for f in all:
438 for f in all:
439 to = None
439 to = None
440 tn = None
440 tn = None
441 if f in mmap:
441 if f in mmap:
442 to = repo.file(f).read(mmap[f])
442 to = repo.file(f).read(mmap[f])
443 if f not in removed:
443 if f not in removed:
444 tn = read(f)
444 tn = read(f)
445 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
445 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
446 showfunc=showfunc, ignorews=ignorews,
446 showfunc=showfunc, ignorews=ignorews,
447 ignorewsamount=ignorewsamount,
447 ignorewsamount=ignorewsamount,
448 ignoreblanklines=ignoreblanklines))
448 ignoreblanklines=ignoreblanklines))
449
449
450 def trimuser(ui, name, rev, revcache):
450 def trimuser(ui, name, rev, revcache):
451 """trim the name of the user who committed a change"""
451 """trim the name of the user who committed a change"""
452 user = revcache.get(rev)
452 user = revcache.get(rev)
453 if user is None:
453 if user is None:
454 user = revcache[rev] = ui.shortuser(name)
454 user = revcache[rev] = ui.shortuser(name)
455 return user
455 return user
456
456
457 class changeset_printer(object):
457 class changeset_printer(object):
458 '''show changeset information when templating not requested.'''
458 '''show changeset information when templating not requested.'''
459
459
460 def __init__(self, ui, repo):
460 def __init__(self, ui, repo):
461 self.ui = ui
461 self.ui = ui
462 self.repo = repo
462 self.repo = repo
463
463
464 def show(self, rev=0, changenode=None, brinfo=None):
464 def show(self, rev=0, changenode=None, brinfo=None):
465 '''show a single changeset or file revision'''
465 '''show a single changeset or file revision'''
466 log = self.repo.changelog
466 log = self.repo.changelog
467 if changenode is None:
467 if changenode is None:
468 changenode = log.node(rev)
468 changenode = log.node(rev)
469 elif not rev:
469 elif not rev:
470 rev = log.rev(changenode)
470 rev = log.rev(changenode)
471
471
472 if self.ui.quiet:
472 if self.ui.quiet:
473 self.ui.write("%d:%s\n" % (rev, short(changenode)))
473 self.ui.write("%d:%s\n" % (rev, short(changenode)))
474 return
474 return
475
475
476 changes = log.read(changenode)
476 changes = log.read(changenode)
477 date = util.datestr(changes[2])
477 date = util.datestr(changes[2])
478
478
479 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
479 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
480 for p in log.parents(changenode)
480 for p in log.parents(changenode)
481 if self.ui.debugflag or p != nullid]
481 if self.ui.debugflag or p != nullid]
482 if (not self.ui.debugflag and len(parents) == 1 and
482 if (not self.ui.debugflag and len(parents) == 1 and
483 parents[0][0] == rev-1):
483 parents[0][0] == rev-1):
484 parents = []
484 parents = []
485
485
486 if self.ui.verbose:
486 if self.ui.verbose:
487 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
487 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
488 else:
488 else:
489 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
489 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
490
490
491 for tag in self.repo.nodetags(changenode):
491 for tag in self.repo.nodetags(changenode):
492 self.ui.status(_("tag: %s\n") % tag)
492 self.ui.status(_("tag: %s\n") % tag)
493 for parent in parents:
493 for parent in parents:
494 self.ui.write(_("parent: %d:%s\n") % parent)
494 self.ui.write(_("parent: %d:%s\n") % parent)
495
495
496 if brinfo and changenode in brinfo:
496 if brinfo and changenode in brinfo:
497 br = brinfo[changenode]
497 br = brinfo[changenode]
498 self.ui.write(_("branch: %s\n") % " ".join(br))
498 self.ui.write(_("branch: %s\n") % " ".join(br))
499
499
500 self.ui.debug(_("manifest: %d:%s\n") %
500 self.ui.debug(_("manifest: %d:%s\n") %
501 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
501 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
502 self.ui.status(_("user: %s\n") % changes[1])
502 self.ui.status(_("user: %s\n") % changes[1])
503 self.ui.status(_("date: %s\n") % date)
503 self.ui.status(_("date: %s\n") % date)
504
504
505 if self.ui.debugflag:
505 if self.ui.debugflag:
506 files = self.repo.changes(log.parents(changenode)[0], changenode)
506 files = self.repo.changes(log.parents(changenode)[0], changenode)
507 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
507 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
508 files):
508 files):
509 if value:
509 if value:
510 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
510 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
511 else:
511 else:
512 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
512 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
513
513
514 description = changes[4].strip()
514 description = changes[4].strip()
515 if description:
515 if description:
516 if self.ui.verbose:
516 if self.ui.verbose:
517 self.ui.status(_("description:\n"))
517 self.ui.status(_("description:\n"))
518 self.ui.status(description)
518 self.ui.status(description)
519 self.ui.status("\n\n")
519 self.ui.status("\n\n")
520 else:
520 else:
521 self.ui.status(_("summary: %s\n") %
521 self.ui.status(_("summary: %s\n") %
522 description.splitlines()[0])
522 description.splitlines()[0])
523 self.ui.status("\n")
523 self.ui.status("\n")
524
524
525 def show_changeset(ui, repo, opts):
525 def show_changeset(ui, repo, opts):
526 '''show one changeset. uses template or regular display. caller
526 '''show one changeset. uses template or regular display. caller
527 can pass in 'style' and 'template' options in opts.'''
527 can pass in 'style' and 'template' options in opts.'''
528
528
529 tmpl = opts.get('template')
529 tmpl = opts.get('template')
530 if tmpl:
530 if tmpl:
531 tmpl = templater.parsestring(tmpl, quoted=False)
531 tmpl = templater.parsestring(tmpl, quoted=False)
532 else:
532 else:
533 tmpl = ui.config('ui', 'logtemplate')
533 tmpl = ui.config('ui', 'logtemplate')
534 if tmpl: tmpl = templater.parsestring(tmpl)
534 if tmpl: tmpl = templater.parsestring(tmpl)
535 mapfile = opts.get('style') or ui.config('ui', 'style')
535 mapfile = opts.get('style') or ui.config('ui', 'style')
536 if tmpl or mapfile:
536 if tmpl or mapfile:
537 if mapfile:
537 if mapfile:
538 if not os.path.isfile(mapfile):
538 if not os.path.isfile(mapfile):
539 mapname = templater.templatepath('map-cmdline.' + mapfile)
539 mapname = templater.templatepath('map-cmdline.' + mapfile)
540 if not mapname: mapname = templater.templatepath(mapfile)
540 if not mapname: mapname = templater.templatepath(mapfile)
541 if mapname: mapfile = mapname
541 if mapname: mapfile = mapname
542 try:
542 try:
543 t = templater.changeset_templater(ui, repo, mapfile)
543 t = templater.changeset_templater(ui, repo, mapfile)
544 except SyntaxError, inst:
544 except SyntaxError, inst:
545 raise util.Abort(inst.args[0])
545 raise util.Abort(inst.args[0])
546 if tmpl: t.use_template(tmpl)
546 if tmpl: t.use_template(tmpl)
547 return t
547 return t
548 return changeset_printer(ui, repo)
548 return changeset_printer(ui, repo)
549
549
550 def setremoteconfig(ui, opts):
551 "copy remote options to ui tree"
552 if opts.get('ssh'):
553 ui.setconfig("ui", "ssh", opts['ssh'])
554 if opts.get('remotecmd'):
555 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
556
550 def show_version(ui):
557 def show_version(ui):
551 """output version and copyright information"""
558 """output version and copyright information"""
552 ui.write(_("Mercurial Distributed SCM (version %s)\n")
559 ui.write(_("Mercurial Distributed SCM (version %s)\n")
553 % version.get_version())
560 % version.get_version())
554 ui.status(_(
561 ui.status(_(
555 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
562 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
556 "This is free software; see the source for copying conditions. "
563 "This is free software; see the source for copying conditions. "
557 "There is NO\nwarranty; "
564 "There is NO\nwarranty; "
558 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
565 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
559 ))
566 ))
560
567
561 def help_(ui, name=None, with_version=False):
568 def help_(ui, name=None, with_version=False):
562 """show help for a command, extension, or list of commands
569 """show help for a command, extension, or list of commands
563
570
564 With no arguments, print a list of commands and short help.
571 With no arguments, print a list of commands and short help.
565
572
566 Given a command name, print help for that command.
573 Given a command name, print help for that command.
567
574
568 Given an extension name, print help for that extension, and the
575 Given an extension name, print help for that extension, and the
569 commands it provides."""
576 commands it provides."""
570 option_lists = []
577 option_lists = []
571
578
572 def helpcmd(name):
579 def helpcmd(name):
573 if with_version:
580 if with_version:
574 show_version(ui)
581 show_version(ui)
575 ui.write('\n')
582 ui.write('\n')
576 aliases, i = findcmd(name)
583 aliases, i = findcmd(name)
577 # synopsis
584 # synopsis
578 ui.write("%s\n\n" % i[2])
585 ui.write("%s\n\n" % i[2])
579
586
580 # description
587 # description
581 doc = i[0].__doc__
588 doc = i[0].__doc__
582 if not doc:
589 if not doc:
583 doc = _("(No help text available)")
590 doc = _("(No help text available)")
584 if ui.quiet:
591 if ui.quiet:
585 doc = doc.splitlines(0)[0]
592 doc = doc.splitlines(0)[0]
586 ui.write("%s\n" % doc.rstrip())
593 ui.write("%s\n" % doc.rstrip())
587
594
588 if not ui.quiet:
595 if not ui.quiet:
589 # aliases
596 # aliases
590 if len(aliases) > 1:
597 if len(aliases) > 1:
591 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
598 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
592
599
593 # options
600 # options
594 if i[1]:
601 if i[1]:
595 option_lists.append(("options", i[1]))
602 option_lists.append(("options", i[1]))
596
603
597 def helplist(select=None):
604 def helplist(select=None):
598 h = {}
605 h = {}
599 cmds = {}
606 cmds = {}
600 for c, e in table.items():
607 for c, e in table.items():
601 f = c.split("|", 1)[0]
608 f = c.split("|", 1)[0]
602 if select and not select(f):
609 if select and not select(f):
603 continue
610 continue
604 if name == "shortlist" and not f.startswith("^"):
611 if name == "shortlist" and not f.startswith("^"):
605 continue
612 continue
606 f = f.lstrip("^")
613 f = f.lstrip("^")
607 if not ui.debugflag and f.startswith("debug"):
614 if not ui.debugflag and f.startswith("debug"):
608 continue
615 continue
609 doc = e[0].__doc__
616 doc = e[0].__doc__
610 if not doc:
617 if not doc:
611 doc = _("(No help text available)")
618 doc = _("(No help text available)")
612 h[f] = doc.splitlines(0)[0].rstrip()
619 h[f] = doc.splitlines(0)[0].rstrip()
613 cmds[f] = c.lstrip("^")
620 cmds[f] = c.lstrip("^")
614
621
615 fns = h.keys()
622 fns = h.keys()
616 fns.sort()
623 fns.sort()
617 m = max(map(len, fns))
624 m = max(map(len, fns))
618 for f in fns:
625 for f in fns:
619 if ui.verbose:
626 if ui.verbose:
620 commands = cmds[f].replace("|",", ")
627 commands = cmds[f].replace("|",", ")
621 ui.write(" %s:\n %s\n"%(commands, h[f]))
628 ui.write(" %s:\n %s\n"%(commands, h[f]))
622 else:
629 else:
623 ui.write(' %-*s %s\n' % (m, f, h[f]))
630 ui.write(' %-*s %s\n' % (m, f, h[f]))
624
631
625 def helpext(name):
632 def helpext(name):
626 try:
633 try:
627 mod = findext(name)
634 mod = findext(name)
628 except KeyError:
635 except KeyError:
629 raise UnknownCommand(name)
636 raise UnknownCommand(name)
630
637
631 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
638 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
632 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
639 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
633 for d in doc[1:]:
640 for d in doc[1:]:
634 ui.write(d, '\n')
641 ui.write(d, '\n')
635
642
636 ui.status('\n')
643 ui.status('\n')
637 if ui.verbose:
644 if ui.verbose:
638 ui.status(_('list of commands:\n\n'))
645 ui.status(_('list of commands:\n\n'))
639 else:
646 else:
640 ui.status(_('list of commands (use "hg help -v %s" '
647 ui.status(_('list of commands (use "hg help -v %s" '
641 'to show aliases and global options):\n\n') % name)
648 'to show aliases and global options):\n\n') % name)
642
649
643 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
650 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
644 helplist(modcmds.has_key)
651 helplist(modcmds.has_key)
645
652
646 if name and name != 'shortlist':
653 if name and name != 'shortlist':
647 try:
654 try:
648 helpcmd(name)
655 helpcmd(name)
649 except UnknownCommand:
656 except UnknownCommand:
650 helpext(name)
657 helpext(name)
651
658
652 else:
659 else:
653 # program name
660 # program name
654 if ui.verbose or with_version:
661 if ui.verbose or with_version:
655 show_version(ui)
662 show_version(ui)
656 else:
663 else:
657 ui.status(_("Mercurial Distributed SCM\n"))
664 ui.status(_("Mercurial Distributed SCM\n"))
658 ui.status('\n')
665 ui.status('\n')
659
666
660 # list of commands
667 # list of commands
661 if name == "shortlist":
668 if name == "shortlist":
662 ui.status(_('basic commands (use "hg help" '
669 ui.status(_('basic commands (use "hg help" '
663 'for the full list or option "-v" for details):\n\n'))
670 'for the full list or option "-v" for details):\n\n'))
664 elif ui.verbose:
671 elif ui.verbose:
665 ui.status(_('list of commands:\n\n'))
672 ui.status(_('list of commands:\n\n'))
666 else:
673 else:
667 ui.status(_('list of commands (use "hg help -v" '
674 ui.status(_('list of commands (use "hg help -v" '
668 'to show aliases and global options):\n\n'))
675 'to show aliases and global options):\n\n'))
669
676
670 helplist()
677 helplist()
671
678
672 # global options
679 # global options
673 if ui.verbose:
680 if ui.verbose:
674 option_lists.append(("global options", globalopts))
681 option_lists.append(("global options", globalopts))
675
682
676 # list all option lists
683 # list all option lists
677 opt_output = []
684 opt_output = []
678 for title, options in option_lists:
685 for title, options in option_lists:
679 opt_output.append(("\n%s:\n" % title, None))
686 opt_output.append(("\n%s:\n" % title, None))
680 for shortopt, longopt, default, desc in options:
687 for shortopt, longopt, default, desc in options:
681 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
688 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
682 longopt and " --%s" % longopt),
689 longopt and " --%s" % longopt),
683 "%s%s" % (desc,
690 "%s%s" % (desc,
684 default
691 default
685 and _(" (default: %s)") % default
692 and _(" (default: %s)") % default
686 or "")))
693 or "")))
687
694
688 if opt_output:
695 if opt_output:
689 opts_len = max([len(line[0]) for line in opt_output if line[1]])
696 opts_len = max([len(line[0]) for line in opt_output if line[1]])
690 for first, second in opt_output:
697 for first, second in opt_output:
691 if second:
698 if second:
692 ui.write(" %-*s %s\n" % (opts_len, first, second))
699 ui.write(" %-*s %s\n" % (opts_len, first, second))
693 else:
700 else:
694 ui.write("%s\n" % first)
701 ui.write("%s\n" % first)
695
702
696 # Commands start here, listed alphabetically
703 # Commands start here, listed alphabetically
697
704
698 def add(ui, repo, *pats, **opts):
705 def add(ui, repo, *pats, **opts):
699 """add the specified files on the next commit
706 """add the specified files on the next commit
700
707
701 Schedule files to be version controlled and added to the repository.
708 Schedule files to be version controlled and added to the repository.
702
709
703 The files will be added to the repository at the next commit.
710 The files will be added to the repository at the next commit.
704
711
705 If no names are given, add all files in the repository.
712 If no names are given, add all files in the repository.
706 """
713 """
707
714
708 names = []
715 names = []
709 for src, abs, rel, exact in walk(repo, pats, opts):
716 for src, abs, rel, exact in walk(repo, pats, opts):
710 if exact:
717 if exact:
711 if ui.verbose:
718 if ui.verbose:
712 ui.status(_('adding %s\n') % rel)
719 ui.status(_('adding %s\n') % rel)
713 names.append(abs)
720 names.append(abs)
714 elif repo.dirstate.state(abs) == '?':
721 elif repo.dirstate.state(abs) == '?':
715 ui.status(_('adding %s\n') % rel)
722 ui.status(_('adding %s\n') % rel)
716 names.append(abs)
723 names.append(abs)
717 if not opts.get('dry_run'):
724 if not opts.get('dry_run'):
718 repo.add(names)
725 repo.add(names)
719
726
720 def addremove(ui, repo, *pats, **opts):
727 def addremove(ui, repo, *pats, **opts):
721 """add all new files, delete all missing files (DEPRECATED)
728 """add all new files, delete all missing files (DEPRECATED)
722
729
723 (DEPRECATED)
730 (DEPRECATED)
724 Add all new files and remove all missing files from the repository.
731 Add all new files and remove all missing files from the repository.
725
732
726 New files are ignored if they match any of the patterns in .hgignore. As
733 New files are ignored if they match any of the patterns in .hgignore. As
727 with add, these changes take effect at the next commit.
734 with add, these changes take effect at the next commit.
728
735
729 This command is now deprecated and will be removed in a future
736 This command is now deprecated and will be removed in a future
730 release. Please use add and remove --after instead.
737 release. Please use add and remove --after instead.
731 """
738 """
732 ui.warn(_('(the addremove command is deprecated; use add and remove '
739 ui.warn(_('(the addremove command is deprecated; use add and remove '
733 '--after instead)\n'))
740 '--after instead)\n'))
734 return addremove_lock(ui, repo, pats, opts)
741 return addremove_lock(ui, repo, pats, opts)
735
742
736 def addremove_lock(ui, repo, pats, opts, wlock=None):
743 def addremove_lock(ui, repo, pats, opts, wlock=None):
737 add, remove = [], []
744 add, remove = [], []
738 for src, abs, rel, exact in walk(repo, pats, opts):
745 for src, abs, rel, exact in walk(repo, pats, opts):
739 if src == 'f' and repo.dirstate.state(abs) == '?':
746 if src == 'f' and repo.dirstate.state(abs) == '?':
740 add.append(abs)
747 add.append(abs)
741 if ui.verbose or not exact:
748 if ui.verbose or not exact:
742 ui.status(_('adding %s\n') % ((pats and rel) or abs))
749 ui.status(_('adding %s\n') % ((pats and rel) or abs))
743 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
750 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
744 remove.append(abs)
751 remove.append(abs)
745 if ui.verbose or not exact:
752 if ui.verbose or not exact:
746 ui.status(_('removing %s\n') % ((pats and rel) or abs))
753 ui.status(_('removing %s\n') % ((pats and rel) or abs))
747 if not opts.get('dry_run'):
754 if not opts.get('dry_run'):
748 repo.add(add, wlock=wlock)
755 repo.add(add, wlock=wlock)
749 repo.remove(remove, wlock=wlock)
756 repo.remove(remove, wlock=wlock)
750
757
751 def annotate(ui, repo, *pats, **opts):
758 def annotate(ui, repo, *pats, **opts):
752 """show changeset information per file line
759 """show changeset information per file line
753
760
754 List changes in files, showing the revision id responsible for each line
761 List changes in files, showing the revision id responsible for each line
755
762
756 This command is useful to discover who did a change or when a change took
763 This command is useful to discover who did a change or when a change took
757 place.
764 place.
758
765
759 Without the -a option, annotate will avoid processing files it
766 Without the -a option, annotate will avoid processing files it
760 detects as binary. With -a, annotate will generate an annotation
767 detects as binary. With -a, annotate will generate an annotation
761 anyway, probably with undesirable results.
768 anyway, probably with undesirable results.
762 """
769 """
763 def getnode(rev):
770 def getnode(rev):
764 return short(repo.changelog.node(rev))
771 return short(repo.changelog.node(rev))
765
772
766 ucache = {}
773 ucache = {}
767 def getname(rev):
774 def getname(rev):
768 try:
775 try:
769 return ucache[rev]
776 return ucache[rev]
770 except:
777 except:
771 u = trimuser(ui, repo.changectx(rev).user(), rev, ucache)
778 u = trimuser(ui, repo.changectx(rev).user(), rev, ucache)
772 ucache[rev] = u
779 ucache[rev] = u
773 return u
780 return u
774
781
775 dcache = {}
782 dcache = {}
776 def getdate(rev):
783 def getdate(rev):
777 datestr = dcache.get(rev)
784 datestr = dcache.get(rev)
778 if datestr is None:
785 if datestr is None:
779 datestr = dcache[rev] = util.datestr(repo.changectx(rev).date())
786 datestr = dcache[rev] = util.datestr(repo.changectx(rev).date())
780 return datestr
787 return datestr
781
788
782 if not pats:
789 if not pats:
783 raise util.Abort(_('at least one file name or pattern required'))
790 raise util.Abort(_('at least one file name or pattern required'))
784
791
785 opmap = [['user', getname], ['number', str], ['changeset', getnode],
792 opmap = [['user', getname], ['number', str], ['changeset', getnode],
786 ['date', getdate]]
793 ['date', getdate]]
787 if not opts['user'] and not opts['changeset'] and not opts['date']:
794 if not opts['user'] and not opts['changeset'] and not opts['date']:
788 opts['number'] = 1
795 opts['number'] = 1
789
796
790 ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0])
797 ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0])
791
798
792 for src, abs, rel, exact in walk(repo, pats, opts, node=ctx.node()):
799 for src, abs, rel, exact in walk(repo, pats, opts, node=ctx.node()):
793 fctx = ctx.filectx(abs)
800 fctx = ctx.filectx(abs)
794 if not opts['text'] and util.binary(fctx.data()):
801 if not opts['text'] and util.binary(fctx.data()):
795 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
802 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
796 continue
803 continue
797
804
798 lines = fctx.annotate()
805 lines = fctx.annotate()
799 pieces = []
806 pieces = []
800
807
801 for o, f in opmap:
808 for o, f in opmap:
802 if opts[o]:
809 if opts[o]:
803 l = [f(n) for n, dummy in lines]
810 l = [f(n) for n, dummy in lines]
804 if l:
811 if l:
805 m = max(map(len, l))
812 m = max(map(len, l))
806 pieces.append(["%*s" % (m, x) for x in l])
813 pieces.append(["%*s" % (m, x) for x in l])
807
814
808 if pieces:
815 if pieces:
809 for p, l in zip(zip(*pieces), lines):
816 for p, l in zip(zip(*pieces), lines):
810 ui.write("%s: %s" % (" ".join(p), l[1]))
817 ui.write("%s: %s" % (" ".join(p), l[1]))
811
818
812 def archive(ui, repo, dest, **opts):
819 def archive(ui, repo, dest, **opts):
813 '''create unversioned archive of a repository revision
820 '''create unversioned archive of a repository revision
814
821
815 By default, the revision used is the parent of the working
822 By default, the revision used is the parent of the working
816 directory; use "-r" to specify a different revision.
823 directory; use "-r" to specify a different revision.
817
824
818 To specify the type of archive to create, use "-t". Valid
825 To specify the type of archive to create, use "-t". Valid
819 types are:
826 types are:
820
827
821 "files" (default): a directory full of files
828 "files" (default): a directory full of files
822 "tar": tar archive, uncompressed
829 "tar": tar archive, uncompressed
823 "tbz2": tar archive, compressed using bzip2
830 "tbz2": tar archive, compressed using bzip2
824 "tgz": tar archive, compressed using gzip
831 "tgz": tar archive, compressed using gzip
825 "uzip": zip archive, uncompressed
832 "uzip": zip archive, uncompressed
826 "zip": zip archive, compressed using deflate
833 "zip": zip archive, compressed using deflate
827
834
828 The exact name of the destination archive or directory is given
835 The exact name of the destination archive or directory is given
829 using a format string; see "hg help export" for details.
836 using a format string; see "hg help export" for details.
830
837
831 Each member added to an archive file has a directory prefix
838 Each member added to an archive file has a directory prefix
832 prepended. Use "-p" to specify a format string for the prefix.
839 prepended. Use "-p" to specify a format string for the prefix.
833 The default is the basename of the archive, with suffixes removed.
840 The default is the basename of the archive, with suffixes removed.
834 '''
841 '''
835
842
836 if opts['rev']:
843 if opts['rev']:
837 node = repo.lookup(opts['rev'])
844 node = repo.lookup(opts['rev'])
838 else:
845 else:
839 node, p2 = repo.dirstate.parents()
846 node, p2 = repo.dirstate.parents()
840 if p2 != nullid:
847 if p2 != nullid:
841 raise util.Abort(_('uncommitted merge - please provide a '
848 raise util.Abort(_('uncommitted merge - please provide a '
842 'specific revision'))
849 'specific revision'))
843
850
844 dest = make_filename(repo, dest, node)
851 dest = make_filename(repo, dest, node)
845 if os.path.realpath(dest) == repo.root:
852 if os.path.realpath(dest) == repo.root:
846 raise util.Abort(_('repository root cannot be destination'))
853 raise util.Abort(_('repository root cannot be destination'))
847 dummy, matchfn, dummy = matchpats(repo, [], opts)
854 dummy, matchfn, dummy = matchpats(repo, [], opts)
848 kind = opts.get('type') or 'files'
855 kind = opts.get('type') or 'files'
849 prefix = opts['prefix']
856 prefix = opts['prefix']
850 if dest == '-':
857 if dest == '-':
851 if kind == 'files':
858 if kind == 'files':
852 raise util.Abort(_('cannot archive plain files to stdout'))
859 raise util.Abort(_('cannot archive plain files to stdout'))
853 dest = sys.stdout
860 dest = sys.stdout
854 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
861 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
855 prefix = make_filename(repo, prefix, node)
862 prefix = make_filename(repo, prefix, node)
856 archival.archive(repo, dest, node, kind, not opts['no_decode'],
863 archival.archive(repo, dest, node, kind, not opts['no_decode'],
857 matchfn, prefix)
864 matchfn, prefix)
858
865
859 def backout(ui, repo, rev, **opts):
866 def backout(ui, repo, rev, **opts):
860 '''reverse effect of earlier changeset
867 '''reverse effect of earlier changeset
861
868
862 Commit the backed out changes as a new changeset. The new
869 Commit the backed out changes as a new changeset. The new
863 changeset is a child of the backed out changeset.
870 changeset is a child of the backed out changeset.
864
871
865 If you back out a changeset other than the tip, a new head is
872 If you back out a changeset other than the tip, a new head is
866 created. This head is the parent of the working directory. If
873 created. This head is the parent of the working directory. If
867 you back out an old changeset, your working directory will appear
874 you back out an old changeset, your working directory will appear
868 old after the backout. You should merge the backout changeset
875 old after the backout. You should merge the backout changeset
869 with another head.
876 with another head.
870
877
871 The --merge option remembers the parent of the working directory
878 The --merge option remembers the parent of the working directory
872 before starting the backout, then merges the new head with that
879 before starting the backout, then merges the new head with that
873 changeset afterwards. This saves you from doing the merge by
880 changeset afterwards. This saves you from doing the merge by
874 hand. The result of this merge is not committed, as for a normal
881 hand. The result of this merge is not committed, as for a normal
875 merge.'''
882 merge.'''
876
883
877 bail_if_changed(repo)
884 bail_if_changed(repo)
878 op1, op2 = repo.dirstate.parents()
885 op1, op2 = repo.dirstate.parents()
879 if op2 != nullid:
886 if op2 != nullid:
880 raise util.Abort(_('outstanding uncommitted merge'))
887 raise util.Abort(_('outstanding uncommitted merge'))
881 node = repo.lookup(rev)
888 node = repo.lookup(rev)
882 p1, p2 = repo.changelog.parents(node)
889 p1, p2 = repo.changelog.parents(node)
883 if p1 == nullid:
890 if p1 == nullid:
884 raise util.Abort(_('cannot back out a change with no parents'))
891 raise util.Abort(_('cannot back out a change with no parents'))
885 if p2 != nullid:
892 if p2 != nullid:
886 if not opts['parent']:
893 if not opts['parent']:
887 raise util.Abort(_('cannot back out a merge changeset without '
894 raise util.Abort(_('cannot back out a merge changeset without '
888 '--parent'))
895 '--parent'))
889 p = repo.lookup(opts['parent'])
896 p = repo.lookup(opts['parent'])
890 if p not in (p1, p2):
897 if p not in (p1, p2):
891 raise util.Abort(_('%s is not a parent of %s' %
898 raise util.Abort(_('%s is not a parent of %s' %
892 (short(p), short(node))))
899 (short(p), short(node))))
893 parent = p
900 parent = p
894 else:
901 else:
895 if opts['parent']:
902 if opts['parent']:
896 raise util.Abort(_('cannot use --parent on non-merge changeset'))
903 raise util.Abort(_('cannot use --parent on non-merge changeset'))
897 parent = p1
904 parent = p1
898 repo.update(node, force=True, show_stats=False)
905 repo.update(node, force=True, show_stats=False)
899 revert_opts = opts.copy()
906 revert_opts = opts.copy()
900 revert_opts['rev'] = hex(parent)
907 revert_opts['rev'] = hex(parent)
901 revert(ui, repo, **revert_opts)
908 revert(ui, repo, **revert_opts)
902 commit_opts = opts.copy()
909 commit_opts = opts.copy()
903 commit_opts['addremove'] = False
910 commit_opts['addremove'] = False
904 if not commit_opts['message'] and not commit_opts['logfile']:
911 if not commit_opts['message'] and not commit_opts['logfile']:
905 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
912 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
906 commit_opts['force_editor'] = True
913 commit_opts['force_editor'] = True
907 commit(ui, repo, **commit_opts)
914 commit(ui, repo, **commit_opts)
908 def nice(node):
915 def nice(node):
909 return '%d:%s' % (repo.changelog.rev(node), short(node))
916 return '%d:%s' % (repo.changelog.rev(node), short(node))
910 ui.status(_('changeset %s backs out changeset %s\n') %
917 ui.status(_('changeset %s backs out changeset %s\n') %
911 (nice(repo.changelog.tip()), nice(node)))
918 (nice(repo.changelog.tip()), nice(node)))
912 if op1 != node:
919 if op1 != node:
913 if opts['merge']:
920 if opts['merge']:
914 ui.status(_('merging with changeset %s\n') % nice(op1))
921 ui.status(_('merging with changeset %s\n') % nice(op1))
915 doupdate(ui, repo, hex(op1), **opts)
922 doupdate(ui, repo, hex(op1), **opts)
916 else:
923 else:
917 ui.status(_('the backout changeset is a new head - '
924 ui.status(_('the backout changeset is a new head - '
918 'do not forget to merge\n'))
925 'do not forget to merge\n'))
919 ui.status(_('(use "backout -m" if you want to auto-merge)\n'))
926 ui.status(_('(use "backout -m" if you want to auto-merge)\n'))
920
927
921 def bundle(ui, repo, fname, dest=None, **opts):
928 def bundle(ui, repo, fname, dest=None, **opts):
922 """create a changegroup file
929 """create a changegroup file
923
930
924 Generate a compressed changegroup file collecting all changesets
931 Generate a compressed changegroup file collecting all changesets
925 not found in the other repository.
932 not found in the other repository.
926
933
927 This file can then be transferred using conventional means and
934 This file can then be transferred using conventional means and
928 applied to another repository with the unbundle command. This is
935 applied to another repository with the unbundle command. This is
929 useful when native push and pull are not available or when
936 useful when native push and pull are not available or when
930 exporting an entire repository is undesirable. The standard file
937 exporting an entire repository is undesirable. The standard file
931 extension is ".hg".
938 extension is ".hg".
932
939
933 Unlike import/export, this exactly preserves all changeset
940 Unlike import/export, this exactly preserves all changeset
934 contents including permissions, rename data, and revision history.
941 contents including permissions, rename data, and revision history.
935 """
942 """
936 dest = ui.expandpath(dest or 'default-push', dest or 'default')
943 dest = ui.expandpath(dest or 'default-push', dest or 'default')
937 other = hg.repository(ui, dest)
944 other = hg.repository(ui, dest)
938 o = repo.findoutgoing(other, force=opts['force'])
945 o = repo.findoutgoing(other, force=opts['force'])
939 cg = repo.changegroup(o, 'bundle')
946 cg = repo.changegroup(o, 'bundle')
940 write_bundle(cg, fname)
947 write_bundle(cg, fname)
941
948
942 def cat(ui, repo, file1, *pats, **opts):
949 def cat(ui, repo, file1, *pats, **opts):
943 """output the latest or given revisions of files
950 """output the latest or given revisions of files
944
951
945 Print the specified files as they were at the given revision.
952 Print the specified files as they were at the given revision.
946 If no revision is given then the tip is used.
953 If no revision is given then the tip is used.
947
954
948 Output may be to a file, in which case the name of the file is
955 Output may be to a file, in which case the name of the file is
949 given using a format string. The formatting rules are the same as
956 given using a format string. The formatting rules are the same as
950 for the export command, with the following additions:
957 for the export command, with the following additions:
951
958
952 %s basename of file being printed
959 %s basename of file being printed
953 %d dirname of file being printed, or '.' if in repo root
960 %d dirname of file being printed, or '.' if in repo root
954 %p root-relative path name of file being printed
961 %p root-relative path name of file being printed
955 """
962 """
956 ctx = repo.changectx(opts['rev'] or "-1")
963 ctx = repo.changectx(opts['rev'] or "-1")
957 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()):
964 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, ctx.node()):
958 fp = make_file(repo, opts['output'], ctx.node(), pathname=abs)
965 fp = make_file(repo, opts['output'], ctx.node(), pathname=abs)
959 fp.write(ctx.filectx(abs).data())
966 fp.write(ctx.filectx(abs).data())
960
967
961 def clone(ui, source, dest=None, **opts):
968 def clone(ui, source, dest=None, **opts):
962 """make a copy of an existing repository
969 """make a copy of an existing repository
963
970
964 Create a copy of an existing repository in a new directory.
971 Create a copy of an existing repository in a new directory.
965
972
966 If no destination directory name is specified, it defaults to the
973 If no destination directory name is specified, it defaults to the
967 basename of the source.
974 basename of the source.
968
975
969 The location of the source is added to the new repository's
976 The location of the source is added to the new repository's
970 .hg/hgrc file, as the default to be used for future pulls.
977 .hg/hgrc file, as the default to be used for future pulls.
971
978
972 For efficiency, hardlinks are used for cloning whenever the source
979 For efficiency, hardlinks are used for cloning whenever the source
973 and destination are on the same filesystem. Some filesystems,
980 and destination are on the same filesystem. Some filesystems,
974 such as AFS, implement hardlinking incorrectly, but do not report
981 such as AFS, implement hardlinking incorrectly, but do not report
975 errors. In these cases, use the --pull option to avoid
982 errors. In these cases, use the --pull option to avoid
976 hardlinking.
983 hardlinking.
977
984
978 See pull for valid source format details.
985 See pull for valid source format details.
979
986
980 It is possible to specify an ssh:// URL as the destination, but no
987 It is possible to specify an ssh:// URL as the destination, but no
981 .hg/hgrc will be created on the remote side. Look at the help text
988 .hg/hgrc will be created on the remote side. Look at the help text
982 for the pull command for important details about ssh:// URLs.
989 for the pull command for important details about ssh:// URLs.
983 """
990 """
984 ui.setconfig_remoteopts(**opts)
991 setremoteconfig(ui, opts)
985 hg.clone(ui, ui.expandpath(source), dest,
992 hg.clone(ui, ui.expandpath(source), dest,
986 pull=opts['pull'],
993 pull=opts['pull'],
987 stream=opts['uncompressed'],
994 stream=opts['uncompressed'],
988 rev=opts['rev'],
995 rev=opts['rev'],
989 update=not opts['noupdate'])
996 update=not opts['noupdate'])
990
997
991 def commit(ui, repo, *pats, **opts):
998 def commit(ui, repo, *pats, **opts):
992 """commit the specified files or all outstanding changes
999 """commit the specified files or all outstanding changes
993
1000
994 Commit changes to the given files into the repository.
1001 Commit changes to the given files into the repository.
995
1002
996 If a list of files is omitted, all changes reported by "hg status"
1003 If a list of files is omitted, all changes reported by "hg status"
997 will be committed.
1004 will be committed.
998
1005
999 If no commit message is specified, the editor configured in your hgrc
1006 If no commit message is specified, the editor configured in your hgrc
1000 or in the EDITOR environment variable is started to enter a message.
1007 or in the EDITOR environment variable is started to enter a message.
1001 """
1008 """
1002 message = logmessage(**opts)
1009 message = logmessage(**opts)
1003
1010
1004 if opts['addremove']:
1011 if opts['addremove']:
1005 addremove_lock(ui, repo, pats, opts)
1012 addremove_lock(ui, repo, pats, opts)
1006 fns, match, anypats = matchpats(repo, pats, opts)
1013 fns, match, anypats = matchpats(repo, pats, opts)
1007 if pats:
1014 if pats:
1008 modified, added, removed, deleted, unknown = (
1015 modified, added, removed, deleted, unknown = (
1009 repo.changes(files=fns, match=match))
1016 repo.changes(files=fns, match=match))
1010 files = modified + added + removed
1017 files = modified + added + removed
1011 else:
1018 else:
1012 files = []
1019 files = []
1013 try:
1020 try:
1014 repo.commit(files, message, opts['user'], opts['date'], match,
1021 repo.commit(files, message, opts['user'], opts['date'], match,
1015 force_editor=opts.get('force_editor'))
1022 force_editor=opts.get('force_editor'))
1016 except ValueError, inst:
1023 except ValueError, inst:
1017 raise util.Abort(str(inst))
1024 raise util.Abort(str(inst))
1018
1025
1019 def docopy(ui, repo, pats, opts, wlock):
1026 def docopy(ui, repo, pats, opts, wlock):
1020 # called with the repo lock held
1027 # called with the repo lock held
1021 cwd = repo.getcwd()
1028 cwd = repo.getcwd()
1022 errors = 0
1029 errors = 0
1023 copied = []
1030 copied = []
1024 targets = {}
1031 targets = {}
1025
1032
1026 def okaytocopy(abs, rel, exact):
1033 def okaytocopy(abs, rel, exact):
1027 reasons = {'?': _('is not managed'),
1034 reasons = {'?': _('is not managed'),
1028 'a': _('has been marked for add'),
1035 'a': _('has been marked for add'),
1029 'r': _('has been marked for remove')}
1036 'r': _('has been marked for remove')}
1030 state = repo.dirstate.state(abs)
1037 state = repo.dirstate.state(abs)
1031 reason = reasons.get(state)
1038 reason = reasons.get(state)
1032 if reason:
1039 if reason:
1033 if state == 'a':
1040 if state == 'a':
1034 origsrc = repo.dirstate.copied(abs)
1041 origsrc = repo.dirstate.copied(abs)
1035 if origsrc is not None:
1042 if origsrc is not None:
1036 return origsrc
1043 return origsrc
1037 if exact:
1044 if exact:
1038 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1045 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1039 else:
1046 else:
1040 return abs
1047 return abs
1041
1048
1042 def copy(origsrc, abssrc, relsrc, target, exact):
1049 def copy(origsrc, abssrc, relsrc, target, exact):
1043 abstarget = util.canonpath(repo.root, cwd, target)
1050 abstarget = util.canonpath(repo.root, cwd, target)
1044 reltarget = util.pathto(cwd, abstarget)
1051 reltarget = util.pathto(cwd, abstarget)
1045 prevsrc = targets.get(abstarget)
1052 prevsrc = targets.get(abstarget)
1046 if prevsrc is not None:
1053 if prevsrc is not None:
1047 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1054 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1048 (reltarget, abssrc, prevsrc))
1055 (reltarget, abssrc, prevsrc))
1049 return
1056 return
1050 if (not opts['after'] and os.path.exists(reltarget) or
1057 if (not opts['after'] and os.path.exists(reltarget) or
1051 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1058 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1052 if not opts['force']:
1059 if not opts['force']:
1053 ui.warn(_('%s: not overwriting - file exists\n') %
1060 ui.warn(_('%s: not overwriting - file exists\n') %
1054 reltarget)
1061 reltarget)
1055 return
1062 return
1056 if not opts['after'] and not opts.get('dry_run'):
1063 if not opts['after'] and not opts.get('dry_run'):
1057 os.unlink(reltarget)
1064 os.unlink(reltarget)
1058 if opts['after']:
1065 if opts['after']:
1059 if not os.path.exists(reltarget):
1066 if not os.path.exists(reltarget):
1060 return
1067 return
1061 else:
1068 else:
1062 targetdir = os.path.dirname(reltarget) or '.'
1069 targetdir = os.path.dirname(reltarget) or '.'
1063 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
1070 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
1064 os.makedirs(targetdir)
1071 os.makedirs(targetdir)
1065 try:
1072 try:
1066 restore = repo.dirstate.state(abstarget) == 'r'
1073 restore = repo.dirstate.state(abstarget) == 'r'
1067 if restore and not opts.get('dry_run'):
1074 if restore and not opts.get('dry_run'):
1068 repo.undelete([abstarget], wlock)
1075 repo.undelete([abstarget], wlock)
1069 try:
1076 try:
1070 if not opts.get('dry_run'):
1077 if not opts.get('dry_run'):
1071 shutil.copyfile(relsrc, reltarget)
1078 shutil.copyfile(relsrc, reltarget)
1072 shutil.copymode(relsrc, reltarget)
1079 shutil.copymode(relsrc, reltarget)
1073 restore = False
1080 restore = False
1074 finally:
1081 finally:
1075 if restore:
1082 if restore:
1076 repo.remove([abstarget], wlock)
1083 repo.remove([abstarget], wlock)
1077 except shutil.Error, inst:
1084 except shutil.Error, inst:
1078 raise util.Abort(str(inst))
1085 raise util.Abort(str(inst))
1079 except IOError, inst:
1086 except IOError, inst:
1080 if inst.errno == errno.ENOENT:
1087 if inst.errno == errno.ENOENT:
1081 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1088 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1082 else:
1089 else:
1083 ui.warn(_('%s: cannot copy - %s\n') %
1090 ui.warn(_('%s: cannot copy - %s\n') %
1084 (relsrc, inst.strerror))
1091 (relsrc, inst.strerror))
1085 errors += 1
1092 errors += 1
1086 return
1093 return
1087 if ui.verbose or not exact:
1094 if ui.verbose or not exact:
1088 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1095 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1089 targets[abstarget] = abssrc
1096 targets[abstarget] = abssrc
1090 if abstarget != origsrc and not opts.get('dry_run'):
1097 if abstarget != origsrc and not opts.get('dry_run'):
1091 repo.copy(origsrc, abstarget, wlock)
1098 repo.copy(origsrc, abstarget, wlock)
1092 copied.append((abssrc, relsrc, exact))
1099 copied.append((abssrc, relsrc, exact))
1093
1100
1094 def targetpathfn(pat, dest, srcs):
1101 def targetpathfn(pat, dest, srcs):
1095 if os.path.isdir(pat):
1102 if os.path.isdir(pat):
1096 abspfx = util.canonpath(repo.root, cwd, pat)
1103 abspfx = util.canonpath(repo.root, cwd, pat)
1097 if destdirexists:
1104 if destdirexists:
1098 striplen = len(os.path.split(abspfx)[0])
1105 striplen = len(os.path.split(abspfx)[0])
1099 else:
1106 else:
1100 striplen = len(abspfx)
1107 striplen = len(abspfx)
1101 if striplen:
1108 if striplen:
1102 striplen += len(os.sep)
1109 striplen += len(os.sep)
1103 res = lambda p: os.path.join(dest, p[striplen:])
1110 res = lambda p: os.path.join(dest, p[striplen:])
1104 elif destdirexists:
1111 elif destdirexists:
1105 res = lambda p: os.path.join(dest, os.path.basename(p))
1112 res = lambda p: os.path.join(dest, os.path.basename(p))
1106 else:
1113 else:
1107 res = lambda p: dest
1114 res = lambda p: dest
1108 return res
1115 return res
1109
1116
1110 def targetpathafterfn(pat, dest, srcs):
1117 def targetpathafterfn(pat, dest, srcs):
1111 if util.patkind(pat, None)[0]:
1118 if util.patkind(pat, None)[0]:
1112 # a mercurial pattern
1119 # a mercurial pattern
1113 res = lambda p: os.path.join(dest, os.path.basename(p))
1120 res = lambda p: os.path.join(dest, os.path.basename(p))
1114 else:
1121 else:
1115 abspfx = util.canonpath(repo.root, cwd, pat)
1122 abspfx = util.canonpath(repo.root, cwd, pat)
1116 if len(abspfx) < len(srcs[0][0]):
1123 if len(abspfx) < len(srcs[0][0]):
1117 # A directory. Either the target path contains the last
1124 # A directory. Either the target path contains the last
1118 # component of the source path or it does not.
1125 # component of the source path or it does not.
1119 def evalpath(striplen):
1126 def evalpath(striplen):
1120 score = 0
1127 score = 0
1121 for s in srcs:
1128 for s in srcs:
1122 t = os.path.join(dest, s[0][striplen:])
1129 t = os.path.join(dest, s[0][striplen:])
1123 if os.path.exists(t):
1130 if os.path.exists(t):
1124 score += 1
1131 score += 1
1125 return score
1132 return score
1126
1133
1127 striplen = len(abspfx)
1134 striplen = len(abspfx)
1128 if striplen:
1135 if striplen:
1129 striplen += len(os.sep)
1136 striplen += len(os.sep)
1130 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1137 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1131 score = evalpath(striplen)
1138 score = evalpath(striplen)
1132 striplen1 = len(os.path.split(abspfx)[0])
1139 striplen1 = len(os.path.split(abspfx)[0])
1133 if striplen1:
1140 if striplen1:
1134 striplen1 += len(os.sep)
1141 striplen1 += len(os.sep)
1135 if evalpath(striplen1) > score:
1142 if evalpath(striplen1) > score:
1136 striplen = striplen1
1143 striplen = striplen1
1137 res = lambda p: os.path.join(dest, p[striplen:])
1144 res = lambda p: os.path.join(dest, p[striplen:])
1138 else:
1145 else:
1139 # a file
1146 # a file
1140 if destdirexists:
1147 if destdirexists:
1141 res = lambda p: os.path.join(dest, os.path.basename(p))
1148 res = lambda p: os.path.join(dest, os.path.basename(p))
1142 else:
1149 else:
1143 res = lambda p: dest
1150 res = lambda p: dest
1144 return res
1151 return res
1145
1152
1146
1153
1147 pats = list(pats)
1154 pats = list(pats)
1148 if not pats:
1155 if not pats:
1149 raise util.Abort(_('no source or destination specified'))
1156 raise util.Abort(_('no source or destination specified'))
1150 if len(pats) == 1:
1157 if len(pats) == 1:
1151 raise util.Abort(_('no destination specified'))
1158 raise util.Abort(_('no destination specified'))
1152 dest = pats.pop()
1159 dest = pats.pop()
1153 destdirexists = os.path.isdir(dest)
1160 destdirexists = os.path.isdir(dest)
1154 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1161 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1155 raise util.Abort(_('with multiple sources, destination must be an '
1162 raise util.Abort(_('with multiple sources, destination must be an '
1156 'existing directory'))
1163 'existing directory'))
1157 if opts['after']:
1164 if opts['after']:
1158 tfn = targetpathafterfn
1165 tfn = targetpathafterfn
1159 else:
1166 else:
1160 tfn = targetpathfn
1167 tfn = targetpathfn
1161 copylist = []
1168 copylist = []
1162 for pat in pats:
1169 for pat in pats:
1163 srcs = []
1170 srcs = []
1164 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1171 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1165 origsrc = okaytocopy(abssrc, relsrc, exact)
1172 origsrc = okaytocopy(abssrc, relsrc, exact)
1166 if origsrc:
1173 if origsrc:
1167 srcs.append((origsrc, abssrc, relsrc, exact))
1174 srcs.append((origsrc, abssrc, relsrc, exact))
1168 if not srcs:
1175 if not srcs:
1169 continue
1176 continue
1170 copylist.append((tfn(pat, dest, srcs), srcs))
1177 copylist.append((tfn(pat, dest, srcs), srcs))
1171 if not copylist:
1178 if not copylist:
1172 raise util.Abort(_('no files to copy'))
1179 raise util.Abort(_('no files to copy'))
1173
1180
1174 for targetpath, srcs in copylist:
1181 for targetpath, srcs in copylist:
1175 for origsrc, abssrc, relsrc, exact in srcs:
1182 for origsrc, abssrc, relsrc, exact in srcs:
1176 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1183 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1177
1184
1178 if errors:
1185 if errors:
1179 ui.warn(_('(consider using --after)\n'))
1186 ui.warn(_('(consider using --after)\n'))
1180 return errors, copied
1187 return errors, copied
1181
1188
1182 def copy(ui, repo, *pats, **opts):
1189 def copy(ui, repo, *pats, **opts):
1183 """mark files as copied for the next commit
1190 """mark files as copied for the next commit
1184
1191
1185 Mark dest as having copies of source files. If dest is a
1192 Mark dest as having copies of source files. If dest is a
1186 directory, copies are put in that directory. If dest is a file,
1193 directory, copies are put in that directory. If dest is a file,
1187 there can only be one source.
1194 there can only be one source.
1188
1195
1189 By default, this command copies the contents of files as they
1196 By default, this command copies the contents of files as they
1190 stand in the working directory. If invoked with --after, the
1197 stand in the working directory. If invoked with --after, the
1191 operation is recorded, but no copying is performed.
1198 operation is recorded, but no copying is performed.
1192
1199
1193 This command takes effect in the next commit.
1200 This command takes effect in the next commit.
1194
1201
1195 NOTE: This command should be treated as experimental. While it
1202 NOTE: This command should be treated as experimental. While it
1196 should properly record copied files, this information is not yet
1203 should properly record copied files, this information is not yet
1197 fully used by merge, nor fully reported by log.
1204 fully used by merge, nor fully reported by log.
1198 """
1205 """
1199 wlock = repo.wlock(0)
1206 wlock = repo.wlock(0)
1200 errs, copied = docopy(ui, repo, pats, opts, wlock)
1207 errs, copied = docopy(ui, repo, pats, opts, wlock)
1201 return errs
1208 return errs
1202
1209
1203 def debugancestor(ui, index, rev1, rev2):
1210 def debugancestor(ui, index, rev1, rev2):
1204 """find the ancestor revision of two revisions in a given index"""
1211 """find the ancestor revision of two revisions in a given index"""
1205 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1212 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1206 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1213 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1207 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1214 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1208
1215
1209 def debugcomplete(ui, cmd='', **opts):
1216 def debugcomplete(ui, cmd='', **opts):
1210 """returns the completion list associated with the given command"""
1217 """returns the completion list associated with the given command"""
1211
1218
1212 if opts['options']:
1219 if opts['options']:
1213 options = []
1220 options = []
1214 otables = [globalopts]
1221 otables = [globalopts]
1215 if cmd:
1222 if cmd:
1216 aliases, entry = findcmd(cmd)
1223 aliases, entry = findcmd(cmd)
1217 otables.append(entry[1])
1224 otables.append(entry[1])
1218 for t in otables:
1225 for t in otables:
1219 for o in t:
1226 for o in t:
1220 if o[0]:
1227 if o[0]:
1221 options.append('-%s' % o[0])
1228 options.append('-%s' % o[0])
1222 options.append('--%s' % o[1])
1229 options.append('--%s' % o[1])
1223 ui.write("%s\n" % "\n".join(options))
1230 ui.write("%s\n" % "\n".join(options))
1224 return
1231 return
1225
1232
1226 clist = findpossible(cmd).keys()
1233 clist = findpossible(cmd).keys()
1227 clist.sort()
1234 clist.sort()
1228 ui.write("%s\n" % "\n".join(clist))
1235 ui.write("%s\n" % "\n".join(clist))
1229
1236
1230 def debugrebuildstate(ui, repo, rev=None):
1237 def debugrebuildstate(ui, repo, rev=None):
1231 """rebuild the dirstate as it would look like for the given revision"""
1238 """rebuild the dirstate as it would look like for the given revision"""
1232 if not rev:
1239 if not rev:
1233 rev = repo.changelog.tip()
1240 rev = repo.changelog.tip()
1234 else:
1241 else:
1235 rev = repo.lookup(rev)
1242 rev = repo.lookup(rev)
1236 change = repo.changelog.read(rev)
1243 change = repo.changelog.read(rev)
1237 n = change[0]
1244 n = change[0]
1238 files = repo.manifest.readflags(n)
1245 files = repo.manifest.readflags(n)
1239 wlock = repo.wlock()
1246 wlock = repo.wlock()
1240 repo.dirstate.rebuild(rev, files.iteritems())
1247 repo.dirstate.rebuild(rev, files.iteritems())
1241
1248
1242 def debugcheckstate(ui, repo):
1249 def debugcheckstate(ui, repo):
1243 """validate the correctness of the current dirstate"""
1250 """validate the correctness of the current dirstate"""
1244 parent1, parent2 = repo.dirstate.parents()
1251 parent1, parent2 = repo.dirstate.parents()
1245 repo.dirstate.read()
1252 repo.dirstate.read()
1246 dc = repo.dirstate.map
1253 dc = repo.dirstate.map
1247 keys = dc.keys()
1254 keys = dc.keys()
1248 keys.sort()
1255 keys.sort()
1249 m1n = repo.changelog.read(parent1)[0]
1256 m1n = repo.changelog.read(parent1)[0]
1250 m2n = repo.changelog.read(parent2)[0]
1257 m2n = repo.changelog.read(parent2)[0]
1251 m1 = repo.manifest.read(m1n)
1258 m1 = repo.manifest.read(m1n)
1252 m2 = repo.manifest.read(m2n)
1259 m2 = repo.manifest.read(m2n)
1253 errors = 0
1260 errors = 0
1254 for f in dc:
1261 for f in dc:
1255 state = repo.dirstate.state(f)
1262 state = repo.dirstate.state(f)
1256 if state in "nr" and f not in m1:
1263 if state in "nr" and f not in m1:
1257 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1264 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1258 errors += 1
1265 errors += 1
1259 if state in "a" and f in m1:
1266 if state in "a" and f in m1:
1260 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1267 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1261 errors += 1
1268 errors += 1
1262 if state in "m" and f not in m1 and f not in m2:
1269 if state in "m" and f not in m1 and f not in m2:
1263 ui.warn(_("%s in state %s, but not in either manifest\n") %
1270 ui.warn(_("%s in state %s, but not in either manifest\n") %
1264 (f, state))
1271 (f, state))
1265 errors += 1
1272 errors += 1
1266 for f in m1:
1273 for f in m1:
1267 state = repo.dirstate.state(f)
1274 state = repo.dirstate.state(f)
1268 if state not in "nrm":
1275 if state not in "nrm":
1269 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1276 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1270 errors += 1
1277 errors += 1
1271 if errors:
1278 if errors:
1272 error = _(".hg/dirstate inconsistent with current parent's manifest")
1279 error = _(".hg/dirstate inconsistent with current parent's manifest")
1273 raise util.Abort(error)
1280 raise util.Abort(error)
1274
1281
1275 def debugconfig(ui, repo, *values):
1282 def debugconfig(ui, repo, *values):
1276 """show combined config settings from all hgrc files
1283 """show combined config settings from all hgrc files
1277
1284
1278 With no args, print names and values of all config items.
1285 With no args, print names and values of all config items.
1279
1286
1280 With one arg of the form section.name, print just the value of
1287 With one arg of the form section.name, print just the value of
1281 that config item.
1288 that config item.
1282
1289
1283 With multiple args, print names and values of all config items
1290 With multiple args, print names and values of all config items
1284 with matching section names."""
1291 with matching section names."""
1285
1292
1286 if values:
1293 if values:
1287 if len([v for v in values if '.' in v]) > 1:
1294 if len([v for v in values if '.' in v]) > 1:
1288 raise util.Abort(_('only one config item permitted'))
1295 raise util.Abort(_('only one config item permitted'))
1289 for section, name, value in ui.walkconfig():
1296 for section, name, value in ui.walkconfig():
1290 sectname = section + '.' + name
1297 sectname = section + '.' + name
1291 if values:
1298 if values:
1292 for v in values:
1299 for v in values:
1293 if v == section:
1300 if v == section:
1294 ui.write('%s=%s\n' % (sectname, value))
1301 ui.write('%s=%s\n' % (sectname, value))
1295 elif v == sectname:
1302 elif v == sectname:
1296 ui.write(value, '\n')
1303 ui.write(value, '\n')
1297 else:
1304 else:
1298 ui.write('%s=%s\n' % (sectname, value))
1305 ui.write('%s=%s\n' % (sectname, value))
1299
1306
1300 def debugsetparents(ui, repo, rev1, rev2=None):
1307 def debugsetparents(ui, repo, rev1, rev2=None):
1301 """manually set the parents of the current working directory
1308 """manually set the parents of the current working directory
1302
1309
1303 This is useful for writing repository conversion tools, but should
1310 This is useful for writing repository conversion tools, but should
1304 be used with care.
1311 be used with care.
1305 """
1312 """
1306
1313
1307 if not rev2:
1314 if not rev2:
1308 rev2 = hex(nullid)
1315 rev2 = hex(nullid)
1309
1316
1310 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1317 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1311
1318
1312 def debugstate(ui, repo):
1319 def debugstate(ui, repo):
1313 """show the contents of the current dirstate"""
1320 """show the contents of the current dirstate"""
1314 repo.dirstate.read()
1321 repo.dirstate.read()
1315 dc = repo.dirstate.map
1322 dc = repo.dirstate.map
1316 keys = dc.keys()
1323 keys = dc.keys()
1317 keys.sort()
1324 keys.sort()
1318 for file_ in keys:
1325 for file_ in keys:
1319 ui.write("%c %3o %10d %s %s\n"
1326 ui.write("%c %3o %10d %s %s\n"
1320 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1327 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1321 time.strftime("%x %X",
1328 time.strftime("%x %X",
1322 time.localtime(dc[file_][3])), file_))
1329 time.localtime(dc[file_][3])), file_))
1323 for f in repo.dirstate.copies:
1330 for f in repo.dirstate.copies:
1324 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1331 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1325
1332
1326 def debugdata(ui, file_, rev):
1333 def debugdata(ui, file_, rev):
1327 """dump the contents of an data file revision"""
1334 """dump the contents of an data file revision"""
1328 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1335 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1329 file_[:-2] + ".i", file_, 0)
1336 file_[:-2] + ".i", file_, 0)
1330 try:
1337 try:
1331 ui.write(r.revision(r.lookup(rev)))
1338 ui.write(r.revision(r.lookup(rev)))
1332 except KeyError:
1339 except KeyError:
1333 raise util.Abort(_('invalid revision identifier %s'), rev)
1340 raise util.Abort(_('invalid revision identifier %s'), rev)
1334
1341
1335 def debugindex(ui, file_):
1342 def debugindex(ui, file_):
1336 """dump the contents of an index file"""
1343 """dump the contents of an index file"""
1337 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1344 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1338 ui.write(" rev offset length base linkrev" +
1345 ui.write(" rev offset length base linkrev" +
1339 " nodeid p1 p2\n")
1346 " nodeid p1 p2\n")
1340 for i in range(r.count()):
1347 for i in range(r.count()):
1341 node = r.node(i)
1348 node = r.node(i)
1342 pp = r.parents(node)
1349 pp = r.parents(node)
1343 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1350 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1344 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1351 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1345 short(node), short(pp[0]), short(pp[1])))
1352 short(node), short(pp[0]), short(pp[1])))
1346
1353
1347 def debugindexdot(ui, file_):
1354 def debugindexdot(ui, file_):
1348 """dump an index DAG as a .dot file"""
1355 """dump an index DAG as a .dot file"""
1349 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1356 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1350 ui.write("digraph G {\n")
1357 ui.write("digraph G {\n")
1351 for i in range(r.count()):
1358 for i in range(r.count()):
1352 node = r.node(i)
1359 node = r.node(i)
1353 pp = r.parents(node)
1360 pp = r.parents(node)
1354 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1361 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1355 if pp[1] != nullid:
1362 if pp[1] != nullid:
1356 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1363 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1357 ui.write("}\n")
1364 ui.write("}\n")
1358
1365
1359 def debugrename(ui, repo, file, rev=None):
1366 def debugrename(ui, repo, file, rev=None):
1360 """dump rename information"""
1367 """dump rename information"""
1361 r = repo.file(relpath(repo, [file])[0])
1368 r = repo.file(relpath(repo, [file])[0])
1362 if rev:
1369 if rev:
1363 try:
1370 try:
1364 # assume all revision numbers are for changesets
1371 # assume all revision numbers are for changesets
1365 n = repo.lookup(rev)
1372 n = repo.lookup(rev)
1366 change = repo.changelog.read(n)
1373 change = repo.changelog.read(n)
1367 m = repo.manifest.read(change[0])
1374 m = repo.manifest.read(change[0])
1368 n = m[relpath(repo, [file])[0]]
1375 n = m[relpath(repo, [file])[0]]
1369 except (hg.RepoError, KeyError):
1376 except (hg.RepoError, KeyError):
1370 n = r.lookup(rev)
1377 n = r.lookup(rev)
1371 else:
1378 else:
1372 n = r.tip()
1379 n = r.tip()
1373 m = r.renamed(n)
1380 m = r.renamed(n)
1374 if m:
1381 if m:
1375 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1382 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1376 else:
1383 else:
1377 ui.write(_("not renamed\n"))
1384 ui.write(_("not renamed\n"))
1378
1385
1379 def debugwalk(ui, repo, *pats, **opts):
1386 def debugwalk(ui, repo, *pats, **opts):
1380 """show how files match on given patterns"""
1387 """show how files match on given patterns"""
1381 items = list(walk(repo, pats, opts))
1388 items = list(walk(repo, pats, opts))
1382 if not items:
1389 if not items:
1383 return
1390 return
1384 fmt = '%%s %%-%ds %%-%ds %%s' % (
1391 fmt = '%%s %%-%ds %%-%ds %%s' % (
1385 max([len(abs) for (src, abs, rel, exact) in items]),
1392 max([len(abs) for (src, abs, rel, exact) in items]),
1386 max([len(rel) for (src, abs, rel, exact) in items]))
1393 max([len(rel) for (src, abs, rel, exact) in items]))
1387 for src, abs, rel, exact in items:
1394 for src, abs, rel, exact in items:
1388 line = fmt % (src, abs, rel, exact and 'exact' or '')
1395 line = fmt % (src, abs, rel, exact and 'exact' or '')
1389 ui.write("%s\n" % line.rstrip())
1396 ui.write("%s\n" % line.rstrip())
1390
1397
1391 def diff(ui, repo, *pats, **opts):
1398 def diff(ui, repo, *pats, **opts):
1392 """diff repository (or selected files)
1399 """diff repository (or selected files)
1393
1400
1394 Show differences between revisions for the specified files.
1401 Show differences between revisions for the specified files.
1395
1402
1396 Differences between files are shown using the unified diff format.
1403 Differences between files are shown using the unified diff format.
1397
1404
1398 When two revision arguments are given, then changes are shown
1405 When two revision arguments are given, then changes are shown
1399 between those revisions. If only one revision is specified then
1406 between those revisions. If only one revision is specified then
1400 that revision is compared to the working directory, and, when no
1407 that revision is compared to the working directory, and, when no
1401 revisions are specified, the working directory files are compared
1408 revisions are specified, the working directory files are compared
1402 to its parent.
1409 to its parent.
1403
1410
1404 Without the -a option, diff will avoid generating diffs of files
1411 Without the -a option, diff will avoid generating diffs of files
1405 it detects as binary. With -a, diff will generate a diff anyway,
1412 it detects as binary. With -a, diff will generate a diff anyway,
1406 probably with undesirable results.
1413 probably with undesirable results.
1407 """
1414 """
1408 node1, node2 = revpair(ui, repo, opts['rev'])
1415 node1, node2 = revpair(ui, repo, opts['rev'])
1409
1416
1410 fns, matchfn, anypats = matchpats(repo, pats, opts)
1417 fns, matchfn, anypats = matchpats(repo, pats, opts)
1411
1418
1412 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1419 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1413 text=opts['text'], opts=opts)
1420 text=opts['text'], opts=opts)
1414
1421
1415 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1422 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1416 node = repo.lookup(changeset)
1423 node = repo.lookup(changeset)
1417 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1424 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1418 if opts['switch_parent']:
1425 if opts['switch_parent']:
1419 parents.reverse()
1426 parents.reverse()
1420 prev = (parents and parents[0]) or nullid
1427 prev = (parents and parents[0]) or nullid
1421 change = repo.changelog.read(node)
1428 change = repo.changelog.read(node)
1422
1429
1423 fp = make_file(repo, opts['output'], node, total=total, seqno=seqno,
1430 fp = make_file(repo, opts['output'], node, total=total, seqno=seqno,
1424 revwidth=revwidth)
1431 revwidth=revwidth)
1425 if fp != sys.stdout:
1432 if fp != sys.stdout:
1426 ui.note("%s\n" % fp.name)
1433 ui.note("%s\n" % fp.name)
1427
1434
1428 fp.write("# HG changeset patch\n")
1435 fp.write("# HG changeset patch\n")
1429 fp.write("# User %s\n" % change[1])
1436 fp.write("# User %s\n" % change[1])
1430 fp.write("# Date %d %d\n" % change[2])
1437 fp.write("# Date %d %d\n" % change[2])
1431 fp.write("# Node ID %s\n" % hex(node))
1438 fp.write("# Node ID %s\n" % hex(node))
1432 fp.write("# Parent %s\n" % hex(prev))
1439 fp.write("# Parent %s\n" % hex(prev))
1433 if len(parents) > 1:
1440 if len(parents) > 1:
1434 fp.write("# Parent %s\n" % hex(parents[1]))
1441 fp.write("# Parent %s\n" % hex(parents[1]))
1435 fp.write(change[4].rstrip())
1442 fp.write(change[4].rstrip())
1436 fp.write("\n\n")
1443 fp.write("\n\n")
1437
1444
1438 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1445 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1439 if fp != sys.stdout:
1446 if fp != sys.stdout:
1440 fp.close()
1447 fp.close()
1441
1448
1442 def export(ui, repo, *changesets, **opts):
1449 def export(ui, repo, *changesets, **opts):
1443 """dump the header and diffs for one or more changesets
1450 """dump the header and diffs for one or more changesets
1444
1451
1445 Print the changeset header and diffs for one or more revisions.
1452 Print the changeset header and diffs for one or more revisions.
1446
1453
1447 The information shown in the changeset header is: author,
1454 The information shown in the changeset header is: author,
1448 changeset hash, parent and commit comment.
1455 changeset hash, parent and commit comment.
1449
1456
1450 Output may be to a file, in which case the name of the file is
1457 Output may be to a file, in which case the name of the file is
1451 given using a format string. The formatting rules are as follows:
1458 given using a format string. The formatting rules are as follows:
1452
1459
1453 %% literal "%" character
1460 %% literal "%" character
1454 %H changeset hash (40 bytes of hexadecimal)
1461 %H changeset hash (40 bytes of hexadecimal)
1455 %N number of patches being generated
1462 %N number of patches being generated
1456 %R changeset revision number
1463 %R changeset revision number
1457 %b basename of the exporting repository
1464 %b basename of the exporting repository
1458 %h short-form changeset hash (12 bytes of hexadecimal)
1465 %h short-form changeset hash (12 bytes of hexadecimal)
1459 %n zero-padded sequence number, starting at 1
1466 %n zero-padded sequence number, starting at 1
1460 %r zero-padded changeset revision number
1467 %r zero-padded changeset revision number
1461
1468
1462 Without the -a option, export will avoid generating diffs of files
1469 Without the -a option, export will avoid generating diffs of files
1463 it detects as binary. With -a, export will generate a diff anyway,
1470 it detects as binary. With -a, export will generate a diff anyway,
1464 probably with undesirable results.
1471 probably with undesirable results.
1465
1472
1466 With the --switch-parent option, the diff will be against the second
1473 With the --switch-parent option, the diff will be against the second
1467 parent. It can be useful to review a merge.
1474 parent. It can be useful to review a merge.
1468 """
1475 """
1469 if not changesets:
1476 if not changesets:
1470 raise util.Abort(_("export requires at least one changeset"))
1477 raise util.Abort(_("export requires at least one changeset"))
1471 seqno = 0
1478 seqno = 0
1472 revs = list(revrange(ui, repo, changesets))
1479 revs = list(revrange(ui, repo, changesets))
1473 total = len(revs)
1480 total = len(revs)
1474 revwidth = max(map(len, revs))
1481 revwidth = max(map(len, revs))
1475 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1482 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1476 ui.note(msg)
1483 ui.note(msg)
1477 for cset in revs:
1484 for cset in revs:
1478 seqno += 1
1485 seqno += 1
1479 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1486 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1480
1487
1481 def forget(ui, repo, *pats, **opts):
1488 def forget(ui, repo, *pats, **opts):
1482 """don't add the specified files on the next commit (DEPRECATED)
1489 """don't add the specified files on the next commit (DEPRECATED)
1483
1490
1484 (DEPRECATED)
1491 (DEPRECATED)
1485 Undo an 'hg add' scheduled for the next commit.
1492 Undo an 'hg add' scheduled for the next commit.
1486
1493
1487 This command is now deprecated and will be removed in a future
1494 This command is now deprecated and will be removed in a future
1488 release. Please use revert instead.
1495 release. Please use revert instead.
1489 """
1496 """
1490 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1497 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1491 forget = []
1498 forget = []
1492 for src, abs, rel, exact in walk(repo, pats, opts):
1499 for src, abs, rel, exact in walk(repo, pats, opts):
1493 if repo.dirstate.state(abs) == 'a':
1500 if repo.dirstate.state(abs) == 'a':
1494 forget.append(abs)
1501 forget.append(abs)
1495 if ui.verbose or not exact:
1502 if ui.verbose or not exact:
1496 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1503 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1497 repo.forget(forget)
1504 repo.forget(forget)
1498
1505
1499 def grep(ui, repo, pattern, *pats, **opts):
1506 def grep(ui, repo, pattern, *pats, **opts):
1500 """search for a pattern in specified files and revisions
1507 """search for a pattern in specified files and revisions
1501
1508
1502 Search revisions of files for a regular expression.
1509 Search revisions of files for a regular expression.
1503
1510
1504 This command behaves differently than Unix grep. It only accepts
1511 This command behaves differently than Unix grep. It only accepts
1505 Python/Perl regexps. It searches repository history, not the
1512 Python/Perl regexps. It searches repository history, not the
1506 working directory. It always prints the revision number in which
1513 working directory. It always prints the revision number in which
1507 a match appears.
1514 a match appears.
1508
1515
1509 By default, grep only prints output for the first revision of a
1516 By default, grep only prints output for the first revision of a
1510 file in which it finds a match. To get it to print every revision
1517 file in which it finds a match. To get it to print every revision
1511 that contains a change in match status ("-" for a match that
1518 that contains a change in match status ("-" for a match that
1512 becomes a non-match, or "+" for a non-match that becomes a match),
1519 becomes a non-match, or "+" for a non-match that becomes a match),
1513 use the --all flag.
1520 use the --all flag.
1514 """
1521 """
1515 reflags = 0
1522 reflags = 0
1516 if opts['ignore_case']:
1523 if opts['ignore_case']:
1517 reflags |= re.I
1524 reflags |= re.I
1518 regexp = re.compile(pattern, reflags)
1525 regexp = re.compile(pattern, reflags)
1519 sep, eol = ':', '\n'
1526 sep, eol = ':', '\n'
1520 if opts['print0']:
1527 if opts['print0']:
1521 sep = eol = '\0'
1528 sep = eol = '\0'
1522
1529
1523 fcache = {}
1530 fcache = {}
1524 def getfile(fn):
1531 def getfile(fn):
1525 if fn not in fcache:
1532 if fn not in fcache:
1526 fcache[fn] = repo.file(fn)
1533 fcache[fn] = repo.file(fn)
1527 return fcache[fn]
1534 return fcache[fn]
1528
1535
1529 def matchlines(body):
1536 def matchlines(body):
1530 begin = 0
1537 begin = 0
1531 linenum = 0
1538 linenum = 0
1532 while True:
1539 while True:
1533 match = regexp.search(body, begin)
1540 match = regexp.search(body, begin)
1534 if not match:
1541 if not match:
1535 break
1542 break
1536 mstart, mend = match.span()
1543 mstart, mend = match.span()
1537 linenum += body.count('\n', begin, mstart) + 1
1544 linenum += body.count('\n', begin, mstart) + 1
1538 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1545 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1539 lend = body.find('\n', mend)
1546 lend = body.find('\n', mend)
1540 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1547 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1541 begin = lend + 1
1548 begin = lend + 1
1542
1549
1543 class linestate(object):
1550 class linestate(object):
1544 def __init__(self, line, linenum, colstart, colend):
1551 def __init__(self, line, linenum, colstart, colend):
1545 self.line = line
1552 self.line = line
1546 self.linenum = linenum
1553 self.linenum = linenum
1547 self.colstart = colstart
1554 self.colstart = colstart
1548 self.colend = colend
1555 self.colend = colend
1549 def __eq__(self, other):
1556 def __eq__(self, other):
1550 return self.line == other.line
1557 return self.line == other.line
1551 def __hash__(self):
1558 def __hash__(self):
1552 return hash(self.line)
1559 return hash(self.line)
1553
1560
1554 matches = {}
1561 matches = {}
1555 def grepbody(fn, rev, body):
1562 def grepbody(fn, rev, body):
1556 matches[rev].setdefault(fn, {})
1563 matches[rev].setdefault(fn, {})
1557 m = matches[rev][fn]
1564 m = matches[rev][fn]
1558 for lnum, cstart, cend, line in matchlines(body):
1565 for lnum, cstart, cend, line in matchlines(body):
1559 s = linestate(line, lnum, cstart, cend)
1566 s = linestate(line, lnum, cstart, cend)
1560 m[s] = s
1567 m[s] = s
1561
1568
1562 # FIXME: prev isn't used, why ?
1569 # FIXME: prev isn't used, why ?
1563 prev = {}
1570 prev = {}
1564 ucache = {}
1571 ucache = {}
1565 def display(fn, rev, states, prevstates):
1572 def display(fn, rev, states, prevstates):
1566 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1573 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1567 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1574 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1568 counts = {'-': 0, '+': 0}
1575 counts = {'-': 0, '+': 0}
1569 filerevmatches = {}
1576 filerevmatches = {}
1570 for l in diff:
1577 for l in diff:
1571 if incrementing or not opts['all']:
1578 if incrementing or not opts['all']:
1572 change = ((l in prevstates) and '-') or '+'
1579 change = ((l in prevstates) and '-') or '+'
1573 r = rev
1580 r = rev
1574 else:
1581 else:
1575 change = ((l in states) and '-') or '+'
1582 change = ((l in states) and '-') or '+'
1576 r = prev[fn]
1583 r = prev[fn]
1577 cols = [fn, str(rev)]
1584 cols = [fn, str(rev)]
1578 if opts['line_number']:
1585 if opts['line_number']:
1579 cols.append(str(l.linenum))
1586 cols.append(str(l.linenum))
1580 if opts['all']:
1587 if opts['all']:
1581 cols.append(change)
1588 cols.append(change)
1582 if opts['user']:
1589 if opts['user']:
1583 cols.append(trimuser(ui, getchange(rev)[1], rev,
1590 cols.append(trimuser(ui, getchange(rev)[1], rev,
1584 ucache))
1591 ucache))
1585 if opts['files_with_matches']:
1592 if opts['files_with_matches']:
1586 c = (fn, rev)
1593 c = (fn, rev)
1587 if c in filerevmatches:
1594 if c in filerevmatches:
1588 continue
1595 continue
1589 filerevmatches[c] = 1
1596 filerevmatches[c] = 1
1590 else:
1597 else:
1591 cols.append(l.line)
1598 cols.append(l.line)
1592 ui.write(sep.join(cols), eol)
1599 ui.write(sep.join(cols), eol)
1593 counts[change] += 1
1600 counts[change] += 1
1594 return counts['+'], counts['-']
1601 return counts['+'], counts['-']
1595
1602
1596 fstate = {}
1603 fstate = {}
1597 skip = {}
1604 skip = {}
1598 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1605 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1599 count = 0
1606 count = 0
1600 incrementing = False
1607 incrementing = False
1601 for st, rev, fns in changeiter:
1608 for st, rev, fns in changeiter:
1602 if st == 'window':
1609 if st == 'window':
1603 incrementing = rev
1610 incrementing = rev
1604 matches.clear()
1611 matches.clear()
1605 elif st == 'add':
1612 elif st == 'add':
1606 change = repo.changelog.read(repo.lookup(str(rev)))
1613 change = repo.changelog.read(repo.lookup(str(rev)))
1607 mf = repo.manifest.read(change[0])
1614 mf = repo.manifest.read(change[0])
1608 matches[rev] = {}
1615 matches[rev] = {}
1609 for fn in fns:
1616 for fn in fns:
1610 if fn in skip:
1617 if fn in skip:
1611 continue
1618 continue
1612 fstate.setdefault(fn, {})
1619 fstate.setdefault(fn, {})
1613 try:
1620 try:
1614 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1621 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1615 except KeyError:
1622 except KeyError:
1616 pass
1623 pass
1617 elif st == 'iter':
1624 elif st == 'iter':
1618 states = matches[rev].items()
1625 states = matches[rev].items()
1619 states.sort()
1626 states.sort()
1620 for fn, m in states:
1627 for fn, m in states:
1621 if fn in skip:
1628 if fn in skip:
1622 continue
1629 continue
1623 if incrementing or not opts['all'] or fstate[fn]:
1630 if incrementing or not opts['all'] or fstate[fn]:
1624 pos, neg = display(fn, rev, m, fstate[fn])
1631 pos, neg = display(fn, rev, m, fstate[fn])
1625 count += pos + neg
1632 count += pos + neg
1626 if pos and not opts['all']:
1633 if pos and not opts['all']:
1627 skip[fn] = True
1634 skip[fn] = True
1628 fstate[fn] = m
1635 fstate[fn] = m
1629 prev[fn] = rev
1636 prev[fn] = rev
1630
1637
1631 if not incrementing:
1638 if not incrementing:
1632 fstate = fstate.items()
1639 fstate = fstate.items()
1633 fstate.sort()
1640 fstate.sort()
1634 for fn, state in fstate:
1641 for fn, state in fstate:
1635 if fn in skip:
1642 if fn in skip:
1636 continue
1643 continue
1637 display(fn, rev, {}, state)
1644 display(fn, rev, {}, state)
1638 return (count == 0 and 1) or 0
1645 return (count == 0 and 1) or 0
1639
1646
1640 def heads(ui, repo, **opts):
1647 def heads(ui, repo, **opts):
1641 """show current repository heads
1648 """show current repository heads
1642
1649
1643 Show all repository head changesets.
1650 Show all repository head changesets.
1644
1651
1645 Repository "heads" are changesets that don't have children
1652 Repository "heads" are changesets that don't have children
1646 changesets. They are where development generally takes place and
1653 changesets. They are where development generally takes place and
1647 are the usual targets for update and merge operations.
1654 are the usual targets for update and merge operations.
1648 """
1655 """
1649 if opts['rev']:
1656 if opts['rev']:
1650 heads = repo.heads(repo.lookup(opts['rev']))
1657 heads = repo.heads(repo.lookup(opts['rev']))
1651 else:
1658 else:
1652 heads = repo.heads()
1659 heads = repo.heads()
1653 br = None
1660 br = None
1654 if opts['branches']:
1661 if opts['branches']:
1655 br = repo.branchlookup(heads)
1662 br = repo.branchlookup(heads)
1656 displayer = show_changeset(ui, repo, opts)
1663 displayer = show_changeset(ui, repo, opts)
1657 for n in heads:
1664 for n in heads:
1658 displayer.show(changenode=n, brinfo=br)
1665 displayer.show(changenode=n, brinfo=br)
1659
1666
1660 def identify(ui, repo):
1667 def identify(ui, repo):
1661 """print information about the working copy
1668 """print information about the working copy
1662
1669
1663 Print a short summary of the current state of the repo.
1670 Print a short summary of the current state of the repo.
1664
1671
1665 This summary identifies the repository state using one or two parent
1672 This summary identifies the repository state using one or two parent
1666 hash identifiers, followed by a "+" if there are uncommitted changes
1673 hash identifiers, followed by a "+" if there are uncommitted changes
1667 in the working directory, followed by a list of tags for this revision.
1674 in the working directory, followed by a list of tags for this revision.
1668 """
1675 """
1669 parents = [p for p in repo.dirstate.parents() if p != nullid]
1676 parents = [p for p in repo.dirstate.parents() if p != nullid]
1670 if not parents:
1677 if not parents:
1671 ui.write(_("unknown\n"))
1678 ui.write(_("unknown\n"))
1672 return
1679 return
1673
1680
1674 hexfunc = ui.verbose and hex or short
1681 hexfunc = ui.verbose and hex or short
1675 modified, added, removed, deleted, unknown = repo.changes()
1682 modified, added, removed, deleted, unknown = repo.changes()
1676 output = ["%s%s" %
1683 output = ["%s%s" %
1677 ('+'.join([hexfunc(parent) for parent in parents]),
1684 ('+'.join([hexfunc(parent) for parent in parents]),
1678 (modified or added or removed or deleted) and "+" or "")]
1685 (modified or added or removed or deleted) and "+" or "")]
1679
1686
1680 if not ui.quiet:
1687 if not ui.quiet:
1681 # multiple tags for a single parent separated by '/'
1688 # multiple tags for a single parent separated by '/'
1682 parenttags = ['/'.join(tags)
1689 parenttags = ['/'.join(tags)
1683 for tags in map(repo.nodetags, parents) if tags]
1690 for tags in map(repo.nodetags, parents) if tags]
1684 # tags for multiple parents separated by ' + '
1691 # tags for multiple parents separated by ' + '
1685 if parenttags:
1692 if parenttags:
1686 output.append(' + '.join(parenttags))
1693 output.append(' + '.join(parenttags))
1687
1694
1688 ui.write("%s\n" % ' '.join(output))
1695 ui.write("%s\n" % ' '.join(output))
1689
1696
1690 def import_(ui, repo, patch1, *patches, **opts):
1697 def import_(ui, repo, patch1, *patches, **opts):
1691 """import an ordered set of patches
1698 """import an ordered set of patches
1692
1699
1693 Import a list of patches and commit them individually.
1700 Import a list of patches and commit them individually.
1694
1701
1695 If there are outstanding changes in the working directory, import
1702 If there are outstanding changes in the working directory, import
1696 will abort unless given the -f flag.
1703 will abort unless given the -f flag.
1697
1704
1698 You can import a patch straight from a mail message. Even patches
1705 You can import a patch straight from a mail message. Even patches
1699 as attachments work (body part must be type text/plain or
1706 as attachments work (body part must be type text/plain or
1700 text/x-patch to be used). From and Subject headers of email
1707 text/x-patch to be used). From and Subject headers of email
1701 message are used as default committer and commit message. All
1708 message are used as default committer and commit message. All
1702 text/plain body parts before first diff are added to commit
1709 text/plain body parts before first diff are added to commit
1703 message.
1710 message.
1704
1711
1705 If imported patch was generated by hg export, user and description
1712 If imported patch was generated by hg export, user and description
1706 from patch override values from message headers and body. Values
1713 from patch override values from message headers and body. Values
1707 given on command line with -m and -u override these.
1714 given on command line with -m and -u override these.
1708
1715
1709 To read a patch from standard input, use patch name "-".
1716 To read a patch from standard input, use patch name "-".
1710 """
1717 """
1711 patches = (patch1,) + patches
1718 patches = (patch1,) + patches
1712
1719
1713 if not opts['force']:
1720 if not opts['force']:
1714 bail_if_changed(repo)
1721 bail_if_changed(repo)
1715
1722
1716 d = opts["base"]
1723 d = opts["base"]
1717 strip = opts["strip"]
1724 strip = opts["strip"]
1718
1725
1719 mailre = re.compile(r'(?:From |[\w-]+:)')
1726 mailre = re.compile(r'(?:From |[\w-]+:)')
1720
1727
1721 # attempt to detect the start of a patch
1728 # attempt to detect the start of a patch
1722 # (this heuristic is borrowed from quilt)
1729 # (this heuristic is borrowed from quilt)
1723 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1730 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1724 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1731 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1725 '(---|\*\*\*)[ \t])', re.MULTILINE)
1732 '(---|\*\*\*)[ \t])', re.MULTILINE)
1726
1733
1727 for patch in patches:
1734 for patch in patches:
1728 pf = os.path.join(d, patch)
1735 pf = os.path.join(d, patch)
1729
1736
1730 message = None
1737 message = None
1731 user = None
1738 user = None
1732 date = None
1739 date = None
1733 hgpatch = False
1740 hgpatch = False
1734
1741
1735 p = email.Parser.Parser()
1742 p = email.Parser.Parser()
1736 if pf == '-':
1743 if pf == '-':
1737 msg = p.parse(sys.stdin)
1744 msg = p.parse(sys.stdin)
1738 ui.status(_("applying patch from stdin\n"))
1745 ui.status(_("applying patch from stdin\n"))
1739 else:
1746 else:
1740 msg = p.parse(file(pf))
1747 msg = p.parse(file(pf))
1741 ui.status(_("applying %s\n") % patch)
1748 ui.status(_("applying %s\n") % patch)
1742
1749
1743 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
1750 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
1744 tmpfp = os.fdopen(fd, 'w')
1751 tmpfp = os.fdopen(fd, 'w')
1745 try:
1752 try:
1746 message = msg['Subject']
1753 message = msg['Subject']
1747 if message:
1754 if message:
1748 message = message.replace('\n\t', ' ')
1755 message = message.replace('\n\t', ' ')
1749 ui.debug('Subject: %s\n' % message)
1756 ui.debug('Subject: %s\n' % message)
1750 user = msg['From']
1757 user = msg['From']
1751 if user:
1758 if user:
1752 ui.debug('From: %s\n' % user)
1759 ui.debug('From: %s\n' % user)
1753 diffs_seen = 0
1760 diffs_seen = 0
1754 ok_types = ('text/plain', 'text/x-patch')
1761 ok_types = ('text/plain', 'text/x-patch')
1755 for part in msg.walk():
1762 for part in msg.walk():
1756 content_type = part.get_content_type()
1763 content_type = part.get_content_type()
1757 ui.debug('Content-Type: %s\n' % content_type)
1764 ui.debug('Content-Type: %s\n' % content_type)
1758 if content_type not in ok_types:
1765 if content_type not in ok_types:
1759 continue
1766 continue
1760 payload = part.get_payload(decode=True)
1767 payload = part.get_payload(decode=True)
1761 m = diffre.search(payload)
1768 m = diffre.search(payload)
1762 if m:
1769 if m:
1763 ui.debug(_('found patch at byte %d\n') % m.start(0))
1770 ui.debug(_('found patch at byte %d\n') % m.start(0))
1764 diffs_seen += 1
1771 diffs_seen += 1
1765 hgpatch = False
1772 hgpatch = False
1766 fp = cStringIO.StringIO()
1773 fp = cStringIO.StringIO()
1767 if message:
1774 if message:
1768 fp.write(message)
1775 fp.write(message)
1769 fp.write('\n')
1776 fp.write('\n')
1770 for line in payload[:m.start(0)].splitlines():
1777 for line in payload[:m.start(0)].splitlines():
1771 if line.startswith('# HG changeset patch'):
1778 if line.startswith('# HG changeset patch'):
1772 ui.debug(_('patch generated by hg export\n'))
1779 ui.debug(_('patch generated by hg export\n'))
1773 hgpatch = True
1780 hgpatch = True
1774 # drop earlier commit message content
1781 # drop earlier commit message content
1775 fp.seek(0)
1782 fp.seek(0)
1776 fp.truncate()
1783 fp.truncate()
1777 elif hgpatch:
1784 elif hgpatch:
1778 if line.startswith('# User '):
1785 if line.startswith('# User '):
1779 user = line[7:]
1786 user = line[7:]
1780 ui.debug('From: %s\n' % user)
1787 ui.debug('From: %s\n' % user)
1781 elif line.startswith("# Date "):
1788 elif line.startswith("# Date "):
1782 date = line[7:]
1789 date = line[7:]
1783 if not line.startswith('# '):
1790 if not line.startswith('# '):
1784 fp.write(line)
1791 fp.write(line)
1785 fp.write('\n')
1792 fp.write('\n')
1786 message = fp.getvalue()
1793 message = fp.getvalue()
1787 if tmpfp:
1794 if tmpfp:
1788 tmpfp.write(payload)
1795 tmpfp.write(payload)
1789 if not payload.endswith('\n'):
1796 if not payload.endswith('\n'):
1790 tmpfp.write('\n')
1797 tmpfp.write('\n')
1791 elif not diffs_seen and message and content_type == 'text/plain':
1798 elif not diffs_seen and message and content_type == 'text/plain':
1792 message += '\n' + payload
1799 message += '\n' + payload
1793
1800
1794 if opts['message']:
1801 if opts['message']:
1795 # pickup the cmdline msg
1802 # pickup the cmdline msg
1796 message = opts['message']
1803 message = opts['message']
1797 elif message:
1804 elif message:
1798 # pickup the patch msg
1805 # pickup the patch msg
1799 message = message.strip()
1806 message = message.strip()
1800 else:
1807 else:
1801 # launch the editor
1808 # launch the editor
1802 message = None
1809 message = None
1803 ui.debug(_('message:\n%s\n') % message)
1810 ui.debug(_('message:\n%s\n') % message)
1804
1811
1805 tmpfp.close()
1812 tmpfp.close()
1806 if not diffs_seen:
1813 if not diffs_seen:
1807 raise util.Abort(_('no diffs found'))
1814 raise util.Abort(_('no diffs found'))
1808
1815
1809 files = util.patch(strip, tmpname, ui)
1816 files = util.patch(strip, tmpname, ui)
1810 if len(files) > 0:
1817 if len(files) > 0:
1811 addremove_lock(ui, repo, files, {})
1818 addremove_lock(ui, repo, files, {})
1812 repo.commit(files, message, user, date)
1819 repo.commit(files, message, user, date)
1813 finally:
1820 finally:
1814 os.unlink(tmpname)
1821 os.unlink(tmpname)
1815
1822
1816 def incoming(ui, repo, source="default", **opts):
1823 def incoming(ui, repo, source="default", **opts):
1817 """show new changesets found in source
1824 """show new changesets found in source
1818
1825
1819 Show new changesets found in the specified path/URL or the default
1826 Show new changesets found in the specified path/URL or the default
1820 pull location. These are the changesets that would be pulled if a pull
1827 pull location. These are the changesets that would be pulled if a pull
1821 was requested.
1828 was requested.
1822
1829
1823 For remote repository, using --bundle avoids downloading the changesets
1830 For remote repository, using --bundle avoids downloading the changesets
1824 twice if the incoming is followed by a pull.
1831 twice if the incoming is followed by a pull.
1825
1832
1826 See pull for valid source format details.
1833 See pull for valid source format details.
1827 """
1834 """
1828 source = ui.expandpath(source)
1835 source = ui.expandpath(source)
1829 ui.setconfig_remoteopts(**opts)
1836 setremoteconfig(ui, opts)
1830
1837
1831 other = hg.repository(ui, source)
1838 other = hg.repository(ui, source)
1832 incoming = repo.findincoming(other, force=opts["force"])
1839 incoming = repo.findincoming(other, force=opts["force"])
1833 if not incoming:
1840 if not incoming:
1834 ui.status(_("no changes found\n"))
1841 ui.status(_("no changes found\n"))
1835 return
1842 return
1836
1843
1837 cleanup = None
1844 cleanup = None
1838 try:
1845 try:
1839 fname = opts["bundle"]
1846 fname = opts["bundle"]
1840 if fname or not other.local():
1847 if fname or not other.local():
1841 # create a bundle (uncompressed if other repo is not local)
1848 # create a bundle (uncompressed if other repo is not local)
1842 cg = other.changegroup(incoming, "incoming")
1849 cg = other.changegroup(incoming, "incoming")
1843 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1850 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1844 # keep written bundle?
1851 # keep written bundle?
1845 if opts["bundle"]:
1852 if opts["bundle"]:
1846 cleanup = None
1853 cleanup = None
1847 if not other.local():
1854 if not other.local():
1848 # use the created uncompressed bundlerepo
1855 # use the created uncompressed bundlerepo
1849 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1856 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1850
1857
1851 revs = None
1858 revs = None
1852 if opts['rev']:
1859 if opts['rev']:
1853 revs = [other.lookup(rev) for rev in opts['rev']]
1860 revs = [other.lookup(rev) for rev in opts['rev']]
1854 o = other.changelog.nodesbetween(incoming, revs)[0]
1861 o = other.changelog.nodesbetween(incoming, revs)[0]
1855 if opts['newest_first']:
1862 if opts['newest_first']:
1856 o.reverse()
1863 o.reverse()
1857 displayer = show_changeset(ui, other, opts)
1864 displayer = show_changeset(ui, other, opts)
1858 for n in o:
1865 for n in o:
1859 parents = [p for p in other.changelog.parents(n) if p != nullid]
1866 parents = [p for p in other.changelog.parents(n) if p != nullid]
1860 if opts['no_merges'] and len(parents) == 2:
1867 if opts['no_merges'] and len(parents) == 2:
1861 continue
1868 continue
1862 displayer.show(changenode=n)
1869 displayer.show(changenode=n)
1863 if opts['patch']:
1870 if opts['patch']:
1864 prev = (parents and parents[0]) or nullid
1871 prev = (parents and parents[0]) or nullid
1865 dodiff(ui, ui, other, prev, n)
1872 dodiff(ui, ui, other, prev, n)
1866 ui.write("\n")
1873 ui.write("\n")
1867 finally:
1874 finally:
1868 if hasattr(other, 'close'):
1875 if hasattr(other, 'close'):
1869 other.close()
1876 other.close()
1870 if cleanup:
1877 if cleanup:
1871 os.unlink(cleanup)
1878 os.unlink(cleanup)
1872
1879
1873 def init(ui, dest=".", **opts):
1880 def init(ui, dest=".", **opts):
1874 """create a new repository in the given directory
1881 """create a new repository in the given directory
1875
1882
1876 Initialize a new repository in the given directory. If the given
1883 Initialize a new repository in the given directory. If the given
1877 directory does not exist, it is created.
1884 directory does not exist, it is created.
1878
1885
1879 If no directory is given, the current directory is used.
1886 If no directory is given, the current directory is used.
1880
1887
1881 It is possible to specify an ssh:// URL as the destination.
1888 It is possible to specify an ssh:// URL as the destination.
1882 Look at the help text for the pull command for important details
1889 Look at the help text for the pull command for important details
1883 about ssh:// URLs.
1890 about ssh:// URLs.
1884 """
1891 """
1885 ui.setconfig_remoteopts(**opts)
1892 setremoteconfig(ui, opts)
1886 hg.repository(ui, dest, create=1)
1893 hg.repository(ui, dest, create=1)
1887
1894
1888 def locate(ui, repo, *pats, **opts):
1895 def locate(ui, repo, *pats, **opts):
1889 """locate files matching specific patterns
1896 """locate files matching specific patterns
1890
1897
1891 Print all files under Mercurial control whose names match the
1898 Print all files under Mercurial control whose names match the
1892 given patterns.
1899 given patterns.
1893
1900
1894 This command searches the current directory and its
1901 This command searches the current directory and its
1895 subdirectories. To search an entire repository, move to the root
1902 subdirectories. To search an entire repository, move to the root
1896 of the repository.
1903 of the repository.
1897
1904
1898 If no patterns are given to match, this command prints all file
1905 If no patterns are given to match, this command prints all file
1899 names.
1906 names.
1900
1907
1901 If you want to feed the output of this command into the "xargs"
1908 If you want to feed the output of this command into the "xargs"
1902 command, use the "-0" option to both this command and "xargs".
1909 command, use the "-0" option to both this command and "xargs".
1903 This will avoid the problem of "xargs" treating single filenames
1910 This will avoid the problem of "xargs" treating single filenames
1904 that contain white space as multiple filenames.
1911 that contain white space as multiple filenames.
1905 """
1912 """
1906 end = opts['print0'] and '\0' or '\n'
1913 end = opts['print0'] and '\0' or '\n'
1907 rev = opts['rev']
1914 rev = opts['rev']
1908 if rev:
1915 if rev:
1909 node = repo.lookup(rev)
1916 node = repo.lookup(rev)
1910 else:
1917 else:
1911 node = None
1918 node = None
1912
1919
1913 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1920 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1914 head='(?:.*/|)'):
1921 head='(?:.*/|)'):
1915 if not node and repo.dirstate.state(abs) == '?':
1922 if not node and repo.dirstate.state(abs) == '?':
1916 continue
1923 continue
1917 if opts['fullpath']:
1924 if opts['fullpath']:
1918 ui.write(os.path.join(repo.root, abs), end)
1925 ui.write(os.path.join(repo.root, abs), end)
1919 else:
1926 else:
1920 ui.write(((pats and rel) or abs), end)
1927 ui.write(((pats and rel) or abs), end)
1921
1928
1922 def log(ui, repo, *pats, **opts):
1929 def log(ui, repo, *pats, **opts):
1923 """show revision history of entire repository or files
1930 """show revision history of entire repository or files
1924
1931
1925 Print the revision history of the specified files or the entire project.
1932 Print the revision history of the specified files or the entire project.
1926
1933
1927 By default this command outputs: changeset id and hash, tags,
1934 By default this command outputs: changeset id and hash, tags,
1928 non-trivial parents, user, date and time, and a summary for each
1935 non-trivial parents, user, date and time, and a summary for each
1929 commit. When the -v/--verbose switch is used, the list of changed
1936 commit. When the -v/--verbose switch is used, the list of changed
1930 files and full commit message is shown.
1937 files and full commit message is shown.
1931 """
1938 """
1932 class dui(object):
1939 class dui(object):
1933 # Implement and delegate some ui protocol. Save hunks of
1940 # Implement and delegate some ui protocol. Save hunks of
1934 # output for later display in the desired order.
1941 # output for later display in the desired order.
1935 def __init__(self, ui):
1942 def __init__(self, ui):
1936 self.ui = ui
1943 self.ui = ui
1937 self.hunk = {}
1944 self.hunk = {}
1938 self.header = {}
1945 self.header = {}
1939 def bump(self, rev):
1946 def bump(self, rev):
1940 self.rev = rev
1947 self.rev = rev
1941 self.hunk[rev] = []
1948 self.hunk[rev] = []
1942 self.header[rev] = []
1949 self.header[rev] = []
1943 def note(self, *args):
1950 def note(self, *args):
1944 if self.verbose:
1951 if self.verbose:
1945 self.write(*args)
1952 self.write(*args)
1946 def status(self, *args):
1953 def status(self, *args):
1947 if not self.quiet:
1954 if not self.quiet:
1948 self.write(*args)
1955 self.write(*args)
1949 def write(self, *args):
1956 def write(self, *args):
1950 self.hunk[self.rev].append(args)
1957 self.hunk[self.rev].append(args)
1951 def write_header(self, *args):
1958 def write_header(self, *args):
1952 self.header[self.rev].append(args)
1959 self.header[self.rev].append(args)
1953 def debug(self, *args):
1960 def debug(self, *args):
1954 if self.debugflag:
1961 if self.debugflag:
1955 self.write(*args)
1962 self.write(*args)
1956 def __getattr__(self, key):
1963 def __getattr__(self, key):
1957 return getattr(self.ui, key)
1964 return getattr(self.ui, key)
1958
1965
1959 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1966 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1960
1967
1961 if opts['limit']:
1968 if opts['limit']:
1962 try:
1969 try:
1963 limit = int(opts['limit'])
1970 limit = int(opts['limit'])
1964 except ValueError:
1971 except ValueError:
1965 raise util.Abort(_('limit must be a positive integer'))
1972 raise util.Abort(_('limit must be a positive integer'))
1966 if limit <= 0: raise util.Abort(_('limit must be positive'))
1973 if limit <= 0: raise util.Abort(_('limit must be positive'))
1967 else:
1974 else:
1968 limit = sys.maxint
1975 limit = sys.maxint
1969 count = 0
1976 count = 0
1970
1977
1971 displayer = show_changeset(ui, repo, opts)
1978 displayer = show_changeset(ui, repo, opts)
1972 for st, rev, fns in changeiter:
1979 for st, rev, fns in changeiter:
1973 if st == 'window':
1980 if st == 'window':
1974 du = dui(ui)
1981 du = dui(ui)
1975 displayer.ui = du
1982 displayer.ui = du
1976 elif st == 'add':
1983 elif st == 'add':
1977 du.bump(rev)
1984 du.bump(rev)
1978 changenode = repo.changelog.node(rev)
1985 changenode = repo.changelog.node(rev)
1979 parents = [p for p in repo.changelog.parents(changenode)
1986 parents = [p for p in repo.changelog.parents(changenode)
1980 if p != nullid]
1987 if p != nullid]
1981 if opts['no_merges'] and len(parents) == 2:
1988 if opts['no_merges'] and len(parents) == 2:
1982 continue
1989 continue
1983 if opts['only_merges'] and len(parents) != 2:
1990 if opts['only_merges'] and len(parents) != 2:
1984 continue
1991 continue
1985
1992
1986 if opts['keyword']:
1993 if opts['keyword']:
1987 changes = getchange(rev)
1994 changes = getchange(rev)
1988 miss = 0
1995 miss = 0
1989 for k in [kw.lower() for kw in opts['keyword']]:
1996 for k in [kw.lower() for kw in opts['keyword']]:
1990 if not (k in changes[1].lower() or
1997 if not (k in changes[1].lower() or
1991 k in changes[4].lower() or
1998 k in changes[4].lower() or
1992 k in " ".join(changes[3][:20]).lower()):
1999 k in " ".join(changes[3][:20]).lower()):
1993 miss = 1
2000 miss = 1
1994 break
2001 break
1995 if miss:
2002 if miss:
1996 continue
2003 continue
1997
2004
1998 br = None
2005 br = None
1999 if opts['branches']:
2006 if opts['branches']:
2000 br = repo.branchlookup([repo.changelog.node(rev)])
2007 br = repo.branchlookup([repo.changelog.node(rev)])
2001
2008
2002 displayer.show(rev, brinfo=br)
2009 displayer.show(rev, brinfo=br)
2003 if opts['patch']:
2010 if opts['patch']:
2004 prev = (parents and parents[0]) or nullid
2011 prev = (parents and parents[0]) or nullid
2005 dodiff(du, du, repo, prev, changenode, match=matchfn)
2012 dodiff(du, du, repo, prev, changenode, match=matchfn)
2006 du.write("\n\n")
2013 du.write("\n\n")
2007 elif st == 'iter':
2014 elif st == 'iter':
2008 if count == limit: break
2015 if count == limit: break
2009 if du.header[rev]:
2016 if du.header[rev]:
2010 for args in du.header[rev]:
2017 for args in du.header[rev]:
2011 ui.write_header(*args)
2018 ui.write_header(*args)
2012 if du.hunk[rev]:
2019 if du.hunk[rev]:
2013 count += 1
2020 count += 1
2014 for args in du.hunk[rev]:
2021 for args in du.hunk[rev]:
2015 ui.write(*args)
2022 ui.write(*args)
2016
2023
2017 def manifest(ui, repo, rev=None):
2024 def manifest(ui, repo, rev=None):
2018 """output the latest or given revision of the project manifest
2025 """output the latest or given revision of the project manifest
2019
2026
2020 Print a list of version controlled files for the given revision.
2027 Print a list of version controlled files for the given revision.
2021
2028
2022 The manifest is the list of files being version controlled. If no revision
2029 The manifest is the list of files being version controlled. If no revision
2023 is given then the tip is used.
2030 is given then the tip is used.
2024 """
2031 """
2025 if rev:
2032 if rev:
2026 try:
2033 try:
2027 # assume all revision numbers are for changesets
2034 # assume all revision numbers are for changesets
2028 n = repo.lookup(rev)
2035 n = repo.lookup(rev)
2029 change = repo.changelog.read(n)
2036 change = repo.changelog.read(n)
2030 n = change[0]
2037 n = change[0]
2031 except hg.RepoError:
2038 except hg.RepoError:
2032 n = repo.manifest.lookup(rev)
2039 n = repo.manifest.lookup(rev)
2033 else:
2040 else:
2034 n = repo.manifest.tip()
2041 n = repo.manifest.tip()
2035 m = repo.manifest.read(n)
2042 m = repo.manifest.read(n)
2036 mf = repo.manifest.readflags(n)
2043 mf = repo.manifest.readflags(n)
2037 files = m.keys()
2044 files = m.keys()
2038 files.sort()
2045 files.sort()
2039
2046
2040 for f in files:
2047 for f in files:
2041 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
2048 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
2042
2049
2043 def merge(ui, repo, node=None, **opts):
2050 def merge(ui, repo, node=None, **opts):
2044 """Merge working directory with another revision
2051 """Merge working directory with another revision
2045
2052
2046 Merge the contents of the current working directory and the
2053 Merge the contents of the current working directory and the
2047 requested revision. Files that changed between either parent are
2054 requested revision. Files that changed between either parent are
2048 marked as changed for the next commit and a commit must be
2055 marked as changed for the next commit and a commit must be
2049 performed before any further updates are allowed.
2056 performed before any further updates are allowed.
2050 """
2057 """
2051 return doupdate(ui, repo, node=node, merge=True, **opts)
2058 return doupdate(ui, repo, node=node, merge=True, **opts)
2052
2059
2053 def outgoing(ui, repo, dest=None, **opts):
2060 def outgoing(ui, repo, dest=None, **opts):
2054 """show changesets not found in destination
2061 """show changesets not found in destination
2055
2062
2056 Show changesets not found in the specified destination repository or
2063 Show changesets not found in the specified destination repository or
2057 the default push location. These are the changesets that would be pushed
2064 the default push location. These are the changesets that would be pushed
2058 if a push was requested.
2065 if a push was requested.
2059
2066
2060 See pull for valid destination format details.
2067 See pull for valid destination format details.
2061 """
2068 """
2062 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2069 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2063 ui.setconfig_remoteopts(**opts)
2070 setremoteconfig(ui, opts)
2064 revs = None
2071 revs = None
2065 if opts['rev']:
2072 if opts['rev']:
2066 revs = [repo.lookup(rev) for rev in opts['rev']]
2073 revs = [repo.lookup(rev) for rev in opts['rev']]
2067
2074
2068 other = hg.repository(ui, dest)
2075 other = hg.repository(ui, dest)
2069 o = repo.findoutgoing(other, force=opts['force'])
2076 o = repo.findoutgoing(other, force=opts['force'])
2070 if not o:
2077 if not o:
2071 ui.status(_("no changes found\n"))
2078 ui.status(_("no changes found\n"))
2072 return
2079 return
2073 o = repo.changelog.nodesbetween(o, revs)[0]
2080 o = repo.changelog.nodesbetween(o, revs)[0]
2074 if opts['newest_first']:
2081 if opts['newest_first']:
2075 o.reverse()
2082 o.reverse()
2076 displayer = show_changeset(ui, repo, opts)
2083 displayer = show_changeset(ui, repo, opts)
2077 for n in o:
2084 for n in o:
2078 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2085 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2079 if opts['no_merges'] and len(parents) == 2:
2086 if opts['no_merges'] and len(parents) == 2:
2080 continue
2087 continue
2081 displayer.show(changenode=n)
2088 displayer.show(changenode=n)
2082 if opts['patch']:
2089 if opts['patch']:
2083 prev = (parents and parents[0]) or nullid
2090 prev = (parents and parents[0]) or nullid
2084 dodiff(ui, ui, repo, prev, n)
2091 dodiff(ui, ui, repo, prev, n)
2085 ui.write("\n")
2092 ui.write("\n")
2086
2093
2087 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
2094 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
2088 """show the parents of the working dir or revision
2095 """show the parents of the working dir or revision
2089
2096
2090 Print the working directory's parent revisions.
2097 Print the working directory's parent revisions.
2091 """
2098 """
2092 # legacy
2099 # legacy
2093 if file_ and not rev:
2100 if file_ and not rev:
2094 try:
2101 try:
2095 rev = repo.lookup(file_)
2102 rev = repo.lookup(file_)
2096 file_ = None
2103 file_ = None
2097 except hg.RepoError:
2104 except hg.RepoError:
2098 pass
2105 pass
2099 else:
2106 else:
2100 ui.warn(_("'hg parent REV' is deprecated, "
2107 ui.warn(_("'hg parent REV' is deprecated, "
2101 "please use 'hg parents -r REV instead\n"))
2108 "please use 'hg parents -r REV instead\n"))
2102
2109
2103 if rev:
2110 if rev:
2104 if file_:
2111 if file_:
2105 ctx = repo.filectx(file_, changeid=rev)
2112 ctx = repo.filectx(file_, changeid=rev)
2106 else:
2113 else:
2107 ctx = repo.changectx(rev)
2114 ctx = repo.changectx(rev)
2108 p = [cp.node() for cp in ctx.parents()]
2115 p = [cp.node() for cp in ctx.parents()]
2109 else:
2116 else:
2110 p = repo.dirstate.parents()
2117 p = repo.dirstate.parents()
2111
2118
2112 br = None
2119 br = None
2113 if branches is not None:
2120 if branches is not None:
2114 br = repo.branchlookup(p)
2121 br = repo.branchlookup(p)
2115 displayer = show_changeset(ui, repo, opts)
2122 displayer = show_changeset(ui, repo, opts)
2116 for n in p:
2123 for n in p:
2117 if n != nullid:
2124 if n != nullid:
2118 displayer.show(changenode=n, brinfo=br)
2125 displayer.show(changenode=n, brinfo=br)
2119
2126
2120 def paths(ui, repo, search=None):
2127 def paths(ui, repo, search=None):
2121 """show definition of symbolic path names
2128 """show definition of symbolic path names
2122
2129
2123 Show definition of symbolic path name NAME. If no name is given, show
2130 Show definition of symbolic path name NAME. If no name is given, show
2124 definition of available names.
2131 definition of available names.
2125
2132
2126 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2133 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2127 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2134 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2128 """
2135 """
2129 if search:
2136 if search:
2130 for name, path in ui.configitems("paths"):
2137 for name, path in ui.configitems("paths"):
2131 if name == search:
2138 if name == search:
2132 ui.write("%s\n" % path)
2139 ui.write("%s\n" % path)
2133 return
2140 return
2134 ui.warn(_("not found!\n"))
2141 ui.warn(_("not found!\n"))
2135 return 1
2142 return 1
2136 else:
2143 else:
2137 for name, path in ui.configitems("paths"):
2144 for name, path in ui.configitems("paths"):
2138 ui.write("%s = %s\n" % (name, path))
2145 ui.write("%s = %s\n" % (name, path))
2139
2146
2140 def postincoming(ui, repo, modheads, optupdate):
2147 def postincoming(ui, repo, modheads, optupdate):
2141 if modheads == 0:
2148 if modheads == 0:
2142 return
2149 return
2143 if optupdate:
2150 if optupdate:
2144 if modheads == 1:
2151 if modheads == 1:
2145 return doupdate(ui, repo)
2152 return doupdate(ui, repo)
2146 else:
2153 else:
2147 ui.status(_("not updating, since new heads added\n"))
2154 ui.status(_("not updating, since new heads added\n"))
2148 if modheads > 1:
2155 if modheads > 1:
2149 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2156 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2150 else:
2157 else:
2151 ui.status(_("(run 'hg update' to get a working copy)\n"))
2158 ui.status(_("(run 'hg update' to get a working copy)\n"))
2152
2159
2153 def pull(ui, repo, source="default", **opts):
2160 def pull(ui, repo, source="default", **opts):
2154 """pull changes from the specified source
2161 """pull changes from the specified source
2155
2162
2156 Pull changes from a remote repository to a local one.
2163 Pull changes from a remote repository to a local one.
2157
2164
2158 This finds all changes from the repository at the specified path
2165 This finds all changes from the repository at the specified path
2159 or URL and adds them to the local repository. By default, this
2166 or URL and adds them to the local repository. By default, this
2160 does not update the copy of the project in the working directory.
2167 does not update the copy of the project in the working directory.
2161
2168
2162 Valid URLs are of the form:
2169 Valid URLs are of the form:
2163
2170
2164 local/filesystem/path
2171 local/filesystem/path
2165 http://[user@]host[:port]/[path]
2172 http://[user@]host[:port]/[path]
2166 https://[user@]host[:port]/[path]
2173 https://[user@]host[:port]/[path]
2167 ssh://[user@]host[:port]/[path]
2174 ssh://[user@]host[:port]/[path]
2168
2175
2169 Some notes about using SSH with Mercurial:
2176 Some notes about using SSH with Mercurial:
2170 - SSH requires an accessible shell account on the destination machine
2177 - SSH requires an accessible shell account on the destination machine
2171 and a copy of hg in the remote path or specified with as remotecmd.
2178 and a copy of hg in the remote path or specified with as remotecmd.
2172 - path is relative to the remote user's home directory by default.
2179 - path is relative to the remote user's home directory by default.
2173 Use an extra slash at the start of a path to specify an absolute path:
2180 Use an extra slash at the start of a path to specify an absolute path:
2174 ssh://example.com//tmp/repository
2181 ssh://example.com//tmp/repository
2175 - Mercurial doesn't use its own compression via SSH; the right thing
2182 - Mercurial doesn't use its own compression via SSH; the right thing
2176 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2183 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2177 Host *.mylocalnetwork.example.com
2184 Host *.mylocalnetwork.example.com
2178 Compression off
2185 Compression off
2179 Host *
2186 Host *
2180 Compression on
2187 Compression on
2181 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2188 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2182 with the --ssh command line option.
2189 with the --ssh command line option.
2183 """
2190 """
2184 source = ui.expandpath(source)
2191 source = ui.expandpath(source)
2185 ui.setconfig_remoteopts(**opts)
2192 setremoteconfig(ui, opts)
2186
2193
2187 other = hg.repository(ui, source)
2194 other = hg.repository(ui, source)
2188 ui.status(_('pulling from %s\n') % (source))
2195 ui.status(_('pulling from %s\n') % (source))
2189 revs = None
2196 revs = None
2190 if opts['rev'] and not other.local():
2197 if opts['rev'] and not other.local():
2191 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2198 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2192 elif opts['rev']:
2199 elif opts['rev']:
2193 revs = [other.lookup(rev) for rev in opts['rev']]
2200 revs = [other.lookup(rev) for rev in opts['rev']]
2194 modheads = repo.pull(other, heads=revs, force=opts['force'])
2201 modheads = repo.pull(other, heads=revs, force=opts['force'])
2195 return postincoming(ui, repo, modheads, opts['update'])
2202 return postincoming(ui, repo, modheads, opts['update'])
2196
2203
2197 def push(ui, repo, dest=None, **opts):
2204 def push(ui, repo, dest=None, **opts):
2198 """push changes to the specified destination
2205 """push changes to the specified destination
2199
2206
2200 Push changes from the local repository to the given destination.
2207 Push changes from the local repository to the given destination.
2201
2208
2202 This is the symmetrical operation for pull. It helps to move
2209 This is the symmetrical operation for pull. It helps to move
2203 changes from the current repository to a different one. If the
2210 changes from the current repository to a different one. If the
2204 destination is local this is identical to a pull in that directory
2211 destination is local this is identical to a pull in that directory
2205 from the current one.
2212 from the current one.
2206
2213
2207 By default, push will refuse to run if it detects the result would
2214 By default, push will refuse to run if it detects the result would
2208 increase the number of remote heads. This generally indicates the
2215 increase the number of remote heads. This generally indicates the
2209 the client has forgotten to sync and merge before pushing.
2216 the client has forgotten to sync and merge before pushing.
2210
2217
2211 Valid URLs are of the form:
2218 Valid URLs are of the form:
2212
2219
2213 local/filesystem/path
2220 local/filesystem/path
2214 ssh://[user@]host[:port]/[path]
2221 ssh://[user@]host[:port]/[path]
2215
2222
2216 Look at the help text for the pull command for important details
2223 Look at the help text for the pull command for important details
2217 about ssh:// URLs.
2224 about ssh:// URLs.
2218
2225
2219 Pushing to http:// and https:// URLs is possible, too, if this
2226 Pushing to http:// and https:// URLs is possible, too, if this
2220 feature is enabled on the remote Mercurial server.
2227 feature is enabled on the remote Mercurial server.
2221 """
2228 """
2222 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2229 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2223 ui.setconfig_remoteopts(**opts)
2230 setremoteconfig(ui, opts)
2224
2231
2225 other = hg.repository(ui, dest)
2232 other = hg.repository(ui, dest)
2226 ui.status('pushing to %s\n' % (dest))
2233 ui.status('pushing to %s\n' % (dest))
2227 revs = None
2234 revs = None
2228 if opts['rev']:
2235 if opts['rev']:
2229 revs = [repo.lookup(rev) for rev in opts['rev']]
2236 revs = [repo.lookup(rev) for rev in opts['rev']]
2230 r = repo.push(other, opts['force'], revs=revs)
2237 r = repo.push(other, opts['force'], revs=revs)
2231 return r == 0
2238 return r == 0
2232
2239
2233 def rawcommit(ui, repo, *flist, **rc):
2240 def rawcommit(ui, repo, *flist, **rc):
2234 """raw commit interface (DEPRECATED)
2241 """raw commit interface (DEPRECATED)
2235
2242
2236 (DEPRECATED)
2243 (DEPRECATED)
2237 Lowlevel commit, for use in helper scripts.
2244 Lowlevel commit, for use in helper scripts.
2238
2245
2239 This command is not intended to be used by normal users, as it is
2246 This command is not intended to be used by normal users, as it is
2240 primarily useful for importing from other SCMs.
2247 primarily useful for importing from other SCMs.
2241
2248
2242 This command is now deprecated and will be removed in a future
2249 This command is now deprecated and will be removed in a future
2243 release, please use debugsetparents and commit instead.
2250 release, please use debugsetparents and commit instead.
2244 """
2251 """
2245
2252
2246 ui.warn(_("(the rawcommit command is deprecated)\n"))
2253 ui.warn(_("(the rawcommit command is deprecated)\n"))
2247
2254
2248 message = rc['message']
2255 message = rc['message']
2249 if not message and rc['logfile']:
2256 if not message and rc['logfile']:
2250 try:
2257 try:
2251 message = open(rc['logfile']).read()
2258 message = open(rc['logfile']).read()
2252 except IOError:
2259 except IOError:
2253 pass
2260 pass
2254 if not message and not rc['logfile']:
2261 if not message and not rc['logfile']:
2255 raise util.Abort(_("missing commit message"))
2262 raise util.Abort(_("missing commit message"))
2256
2263
2257 files = relpath(repo, list(flist))
2264 files = relpath(repo, list(flist))
2258 if rc['files']:
2265 if rc['files']:
2259 files += open(rc['files']).read().splitlines()
2266 files += open(rc['files']).read().splitlines()
2260
2267
2261 rc['parent'] = map(repo.lookup, rc['parent'])
2268 rc['parent'] = map(repo.lookup, rc['parent'])
2262
2269
2263 try:
2270 try:
2264 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2271 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2265 except ValueError, inst:
2272 except ValueError, inst:
2266 raise util.Abort(str(inst))
2273 raise util.Abort(str(inst))
2267
2274
2268 def recover(ui, repo):
2275 def recover(ui, repo):
2269 """roll back an interrupted transaction
2276 """roll back an interrupted transaction
2270
2277
2271 Recover from an interrupted commit or pull.
2278 Recover from an interrupted commit or pull.
2272
2279
2273 This command tries to fix the repository status after an interrupted
2280 This command tries to fix the repository status after an interrupted
2274 operation. It should only be necessary when Mercurial suggests it.
2281 operation. It should only be necessary when Mercurial suggests it.
2275 """
2282 """
2276 if repo.recover():
2283 if repo.recover():
2277 return repo.verify()
2284 return repo.verify()
2278 return 1
2285 return 1
2279
2286
2280 def remove(ui, repo, *pats, **opts):
2287 def remove(ui, repo, *pats, **opts):
2281 """remove the specified files on the next commit
2288 """remove the specified files on the next commit
2282
2289
2283 Schedule the indicated files for removal from the repository.
2290 Schedule the indicated files for removal from the repository.
2284
2291
2285 This command schedules the files to be removed at the next commit.
2292 This command schedules the files to be removed at the next commit.
2286 This only removes files from the current branch, not from the
2293 This only removes files from the current branch, not from the
2287 entire project history. If the files still exist in the working
2294 entire project history. If the files still exist in the working
2288 directory, they will be deleted from it. If invoked with --after,
2295 directory, they will be deleted from it. If invoked with --after,
2289 files that have been manually deleted are marked as removed.
2296 files that have been manually deleted are marked as removed.
2290
2297
2291 Modified files and added files are not removed by default. To
2298 Modified files and added files are not removed by default. To
2292 remove them, use the -f/--force option.
2299 remove them, use the -f/--force option.
2293 """
2300 """
2294 names = []
2301 names = []
2295 if not opts['after'] and not pats:
2302 if not opts['after'] and not pats:
2296 raise util.Abort(_('no files specified'))
2303 raise util.Abort(_('no files specified'))
2297 files, matchfn, anypats = matchpats(repo, pats, opts)
2304 files, matchfn, anypats = matchpats(repo, pats, opts)
2298 exact = dict.fromkeys(files)
2305 exact = dict.fromkeys(files)
2299 mardu = map(dict.fromkeys, repo.changes(files=files, match=matchfn))
2306 mardu = map(dict.fromkeys, repo.changes(files=files, match=matchfn))
2300 modified, added, removed, deleted, unknown = mardu
2307 modified, added, removed, deleted, unknown = mardu
2301 remove, forget = [], []
2308 remove, forget = [], []
2302 for src, abs, rel, exact in walk(repo, pats, opts):
2309 for src, abs, rel, exact in walk(repo, pats, opts):
2303 reason = None
2310 reason = None
2304 if abs not in deleted and opts['after']:
2311 if abs not in deleted and opts['after']:
2305 reason = _('is still present')
2312 reason = _('is still present')
2306 elif abs in modified and not opts['force']:
2313 elif abs in modified and not opts['force']:
2307 reason = _('is modified (use -f to force removal)')
2314 reason = _('is modified (use -f to force removal)')
2308 elif abs in added:
2315 elif abs in added:
2309 if opts['force']:
2316 if opts['force']:
2310 forget.append(abs)
2317 forget.append(abs)
2311 continue
2318 continue
2312 reason = _('has been marked for add (use -f to force removal)')
2319 reason = _('has been marked for add (use -f to force removal)')
2313 elif abs in unknown:
2320 elif abs in unknown:
2314 reason = _('is not managed')
2321 reason = _('is not managed')
2315 elif abs in removed:
2322 elif abs in removed:
2316 continue
2323 continue
2317 if reason:
2324 if reason:
2318 if exact:
2325 if exact:
2319 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2326 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2320 else:
2327 else:
2321 if ui.verbose or not exact:
2328 if ui.verbose or not exact:
2322 ui.status(_('removing %s\n') % rel)
2329 ui.status(_('removing %s\n') % rel)
2323 remove.append(abs)
2330 remove.append(abs)
2324 repo.forget(forget)
2331 repo.forget(forget)
2325 repo.remove(remove, unlink=not opts['after'])
2332 repo.remove(remove, unlink=not opts['after'])
2326
2333
2327 def rename(ui, repo, *pats, **opts):
2334 def rename(ui, repo, *pats, **opts):
2328 """rename files; equivalent of copy + remove
2335 """rename files; equivalent of copy + remove
2329
2336
2330 Mark dest as copies of sources; mark sources for deletion. If
2337 Mark dest as copies of sources; mark sources for deletion. If
2331 dest is a directory, copies are put in that directory. If dest is
2338 dest is a directory, copies are put in that directory. If dest is
2332 a file, there can only be one source.
2339 a file, there can only be one source.
2333
2340
2334 By default, this command copies the contents of files as they
2341 By default, this command copies the contents of files as they
2335 stand in the working directory. If invoked with --after, the
2342 stand in the working directory. If invoked with --after, the
2336 operation is recorded, but no copying is performed.
2343 operation is recorded, but no copying is performed.
2337
2344
2338 This command takes effect in the next commit.
2345 This command takes effect in the next commit.
2339
2346
2340 NOTE: This command should be treated as experimental. While it
2347 NOTE: This command should be treated as experimental. While it
2341 should properly record rename files, this information is not yet
2348 should properly record rename files, this information is not yet
2342 fully used by merge, nor fully reported by log.
2349 fully used by merge, nor fully reported by log.
2343 """
2350 """
2344 wlock = repo.wlock(0)
2351 wlock = repo.wlock(0)
2345 errs, copied = docopy(ui, repo, pats, opts, wlock)
2352 errs, copied = docopy(ui, repo, pats, opts, wlock)
2346 names = []
2353 names = []
2347 for abs, rel, exact in copied:
2354 for abs, rel, exact in copied:
2348 if ui.verbose or not exact:
2355 if ui.verbose or not exact:
2349 ui.status(_('removing %s\n') % rel)
2356 ui.status(_('removing %s\n') % rel)
2350 names.append(abs)
2357 names.append(abs)
2351 if not opts.get('dry_run'):
2358 if not opts.get('dry_run'):
2352 repo.remove(names, True, wlock)
2359 repo.remove(names, True, wlock)
2353 return errs
2360 return errs
2354
2361
2355 def revert(ui, repo, *pats, **opts):
2362 def revert(ui, repo, *pats, **opts):
2356 """revert files or dirs to their states as of some revision
2363 """revert files or dirs to their states as of some revision
2357
2364
2358 With no revision specified, revert the named files or directories
2365 With no revision specified, revert the named files or directories
2359 to the contents they had in the parent of the working directory.
2366 to the contents they had in the parent of the working directory.
2360 This restores the contents of the affected files to an unmodified
2367 This restores the contents of the affected files to an unmodified
2361 state. If the working directory has two parents, you must
2368 state. If the working directory has two parents, you must
2362 explicitly specify the revision to revert to.
2369 explicitly specify the revision to revert to.
2363
2370
2364 Modified files are saved with a .orig suffix before reverting.
2371 Modified files are saved with a .orig suffix before reverting.
2365 To disable these backups, use --no-backup.
2372 To disable these backups, use --no-backup.
2366
2373
2367 Using the -r option, revert the given files or directories to
2374 Using the -r option, revert the given files or directories to
2368 their contents as of a specific revision. This can be helpful to"roll
2375 their contents as of a specific revision. This can be helpful to"roll
2369 back" some or all of a change that should not have been committed.
2376 back" some or all of a change that should not have been committed.
2370
2377
2371 Revert modifies the working directory. It does not commit any
2378 Revert modifies the working directory. It does not commit any
2372 changes, or change the parent of the working directory. If you
2379 changes, or change the parent of the working directory. If you
2373 revert to a revision other than the parent of the working
2380 revert to a revision other than the parent of the working
2374 directory, the reverted files will thus appear modified
2381 directory, the reverted files will thus appear modified
2375 afterwards.
2382 afterwards.
2376
2383
2377 If a file has been deleted, it is recreated. If the executable
2384 If a file has been deleted, it is recreated. If the executable
2378 mode of a file was changed, it is reset.
2385 mode of a file was changed, it is reset.
2379
2386
2380 If names are given, all files matching the names are reverted.
2387 If names are given, all files matching the names are reverted.
2381
2388
2382 If no arguments are given, all files in the repository are reverted.
2389 If no arguments are given, all files in the repository are reverted.
2383 """
2390 """
2384 parent, p2 = repo.dirstate.parents()
2391 parent, p2 = repo.dirstate.parents()
2385 if opts['rev']:
2392 if opts['rev']:
2386 node = repo.lookup(opts['rev'])
2393 node = repo.lookup(opts['rev'])
2387 elif p2 != nullid:
2394 elif p2 != nullid:
2388 raise util.Abort(_('working dir has two parents; '
2395 raise util.Abort(_('working dir has two parents; '
2389 'you must specify the revision to revert to'))
2396 'you must specify the revision to revert to'))
2390 else:
2397 else:
2391 node = parent
2398 node = parent
2392 mf = repo.manifest.read(repo.changelog.read(node)[0])
2399 mf = repo.manifest.read(repo.changelog.read(node)[0])
2393 if node == parent:
2400 if node == parent:
2394 pmf = mf
2401 pmf = mf
2395 else:
2402 else:
2396 pmf = None
2403 pmf = None
2397
2404
2398 wlock = repo.wlock()
2405 wlock = repo.wlock()
2399
2406
2400 # need all matching names in dirstate and manifest of target rev,
2407 # need all matching names in dirstate and manifest of target rev,
2401 # so have to walk both. do not print errors if files exist in one
2408 # so have to walk both. do not print errors if files exist in one
2402 # but not other.
2409 # but not other.
2403
2410
2404 names = {}
2411 names = {}
2405 target_only = {}
2412 target_only = {}
2406
2413
2407 # walk dirstate.
2414 # walk dirstate.
2408
2415
2409 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key):
2416 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key):
2410 names[abs] = (rel, exact)
2417 names[abs] = (rel, exact)
2411 if src == 'b':
2418 if src == 'b':
2412 target_only[abs] = True
2419 target_only[abs] = True
2413
2420
2414 # walk target manifest.
2421 # walk target manifest.
2415
2422
2416 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
2423 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
2417 badmatch=names.has_key):
2424 badmatch=names.has_key):
2418 if abs in names: continue
2425 if abs in names: continue
2419 names[abs] = (rel, exact)
2426 names[abs] = (rel, exact)
2420 target_only[abs] = True
2427 target_only[abs] = True
2421
2428
2422 changes = repo.changes(match=names.has_key, wlock=wlock)
2429 changes = repo.changes(match=names.has_key, wlock=wlock)
2423 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2430 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2424
2431
2425 revert = ([], _('reverting %s\n'))
2432 revert = ([], _('reverting %s\n'))
2426 add = ([], _('adding %s\n'))
2433 add = ([], _('adding %s\n'))
2427 remove = ([], _('removing %s\n'))
2434 remove = ([], _('removing %s\n'))
2428 forget = ([], _('forgetting %s\n'))
2435 forget = ([], _('forgetting %s\n'))
2429 undelete = ([], _('undeleting %s\n'))
2436 undelete = ([], _('undeleting %s\n'))
2430 update = {}
2437 update = {}
2431
2438
2432 disptable = (
2439 disptable = (
2433 # dispatch table:
2440 # dispatch table:
2434 # file state
2441 # file state
2435 # action if in target manifest
2442 # action if in target manifest
2436 # action if not in target manifest
2443 # action if not in target manifest
2437 # make backup if in target manifest
2444 # make backup if in target manifest
2438 # make backup if not in target manifest
2445 # make backup if not in target manifest
2439 (modified, revert, remove, True, True),
2446 (modified, revert, remove, True, True),
2440 (added, revert, forget, True, False),
2447 (added, revert, forget, True, False),
2441 (removed, undelete, None, False, False),
2448 (removed, undelete, None, False, False),
2442 (deleted, revert, remove, False, False),
2449 (deleted, revert, remove, False, False),
2443 (unknown, add, None, True, False),
2450 (unknown, add, None, True, False),
2444 (target_only, add, None, False, False),
2451 (target_only, add, None, False, False),
2445 )
2452 )
2446
2453
2447 entries = names.items()
2454 entries = names.items()
2448 entries.sort()
2455 entries.sort()
2449
2456
2450 for abs, (rel, exact) in entries:
2457 for abs, (rel, exact) in entries:
2451 mfentry = mf.get(abs)
2458 mfentry = mf.get(abs)
2452 def handle(xlist, dobackup):
2459 def handle(xlist, dobackup):
2453 xlist[0].append(abs)
2460 xlist[0].append(abs)
2454 update[abs] = 1
2461 update[abs] = 1
2455 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2462 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2456 bakname = "%s.orig" % rel
2463 bakname = "%s.orig" % rel
2457 ui.note(_('saving current version of %s as %s\n') %
2464 ui.note(_('saving current version of %s as %s\n') %
2458 (rel, bakname))
2465 (rel, bakname))
2459 if not opts.get('dry_run'):
2466 if not opts.get('dry_run'):
2460 shutil.copyfile(rel, bakname)
2467 shutil.copyfile(rel, bakname)
2461 shutil.copymode(rel, bakname)
2468 shutil.copymode(rel, bakname)
2462 if ui.verbose or not exact:
2469 if ui.verbose or not exact:
2463 ui.status(xlist[1] % rel)
2470 ui.status(xlist[1] % rel)
2464 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2471 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2465 if abs not in table: continue
2472 if abs not in table: continue
2466 # file has changed in dirstate
2473 # file has changed in dirstate
2467 if mfentry:
2474 if mfentry:
2468 handle(hitlist, backuphit)
2475 handle(hitlist, backuphit)
2469 elif misslist is not None:
2476 elif misslist is not None:
2470 handle(misslist, backupmiss)
2477 handle(misslist, backupmiss)
2471 else:
2478 else:
2472 if exact: ui.warn(_('file not managed: %s\n' % rel))
2479 if exact: ui.warn(_('file not managed: %s\n' % rel))
2473 break
2480 break
2474 else:
2481 else:
2475 # file has not changed in dirstate
2482 # file has not changed in dirstate
2476 if node == parent:
2483 if node == parent:
2477 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2484 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2478 continue
2485 continue
2479 if pmf is None:
2486 if pmf is None:
2480 # only need parent manifest in this unlikely case,
2487 # only need parent manifest in this unlikely case,
2481 # so do not read by default
2488 # so do not read by default
2482 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2489 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2483 if abs in pmf:
2490 if abs in pmf:
2484 if mfentry:
2491 if mfentry:
2485 # if version of file is same in parent and target
2492 # if version of file is same in parent and target
2486 # manifests, do nothing
2493 # manifests, do nothing
2487 if pmf[abs] != mfentry:
2494 if pmf[abs] != mfentry:
2488 handle(revert, False)
2495 handle(revert, False)
2489 else:
2496 else:
2490 handle(remove, False)
2497 handle(remove, False)
2491
2498
2492 if not opts.get('dry_run'):
2499 if not opts.get('dry_run'):
2493 repo.dirstate.forget(forget[0])
2500 repo.dirstate.forget(forget[0])
2494 r = repo.update(node, False, True, update.has_key, False, wlock=wlock,
2501 r = repo.update(node, False, True, update.has_key, False, wlock=wlock,
2495 show_stats=False)
2502 show_stats=False)
2496 repo.dirstate.update(add[0], 'a')
2503 repo.dirstate.update(add[0], 'a')
2497 repo.dirstate.update(undelete[0], 'n')
2504 repo.dirstate.update(undelete[0], 'n')
2498 repo.dirstate.update(remove[0], 'r')
2505 repo.dirstate.update(remove[0], 'r')
2499 return r
2506 return r
2500
2507
2501 def rollback(ui, repo):
2508 def rollback(ui, repo):
2502 """roll back the last transaction in this repository
2509 """roll back the last transaction in this repository
2503
2510
2504 Roll back the last transaction in this repository, restoring the
2511 Roll back the last transaction in this repository, restoring the
2505 project to its state prior to the transaction.
2512 project to its state prior to the transaction.
2506
2513
2507 Transactions are used to encapsulate the effects of all commands
2514 Transactions are used to encapsulate the effects of all commands
2508 that create new changesets or propagate existing changesets into a
2515 that create new changesets or propagate existing changesets into a
2509 repository. For example, the following commands are transactional,
2516 repository. For example, the following commands are transactional,
2510 and their effects can be rolled back:
2517 and their effects can be rolled back:
2511
2518
2512 commit
2519 commit
2513 import
2520 import
2514 pull
2521 pull
2515 push (with this repository as destination)
2522 push (with this repository as destination)
2516 unbundle
2523 unbundle
2517
2524
2518 This command should be used with care. There is only one level of
2525 This command should be used with care. There is only one level of
2519 rollback, and there is no way to undo a rollback.
2526 rollback, and there is no way to undo a rollback.
2520
2527
2521 This command is not intended for use on public repositories. Once
2528 This command is not intended for use on public repositories. Once
2522 changes are visible for pull by other users, rolling a transaction
2529 changes are visible for pull by other users, rolling a transaction
2523 back locally is ineffective (someone else may already have pulled
2530 back locally is ineffective (someone else may already have pulled
2524 the changes). Furthermore, a race is possible with readers of the
2531 the changes). Furthermore, a race is possible with readers of the
2525 repository; for example an in-progress pull from the repository
2532 repository; for example an in-progress pull from the repository
2526 may fail if a rollback is performed.
2533 may fail if a rollback is performed.
2527 """
2534 """
2528 repo.rollback()
2535 repo.rollback()
2529
2536
2530 def root(ui, repo):
2537 def root(ui, repo):
2531 """print the root (top) of the current working dir
2538 """print the root (top) of the current working dir
2532
2539
2533 Print the root directory of the current repository.
2540 Print the root directory of the current repository.
2534 """
2541 """
2535 ui.write(repo.root + "\n")
2542 ui.write(repo.root + "\n")
2536
2543
2537 def serve(ui, repo, **opts):
2544 def serve(ui, repo, **opts):
2538 """export the repository via HTTP
2545 """export the repository via HTTP
2539
2546
2540 Start a local HTTP repository browser and pull server.
2547 Start a local HTTP repository browser and pull server.
2541
2548
2542 By default, the server logs accesses to stdout and errors to
2549 By default, the server logs accesses to stdout and errors to
2543 stderr. Use the "-A" and "-E" options to log to files.
2550 stderr. Use the "-A" and "-E" options to log to files.
2544 """
2551 """
2545
2552
2546 if opts["stdio"]:
2553 if opts["stdio"]:
2547 if repo is None:
2554 if repo is None:
2548 raise hg.RepoError(_('no repo found'))
2555 raise hg.RepoError(_('no repo found'))
2549 s = sshserver.sshserver(ui, repo)
2556 s = sshserver.sshserver(ui, repo)
2550 s.serve_forever()
2557 s.serve_forever()
2551
2558
2552 optlist = ("name templates style address port ipv6"
2559 optlist = ("name templates style address port ipv6"
2553 " accesslog errorlog webdir_conf")
2560 " accesslog errorlog webdir_conf")
2554 for o in optlist.split():
2561 for o in optlist.split():
2555 if opts[o]:
2562 if opts[o]:
2556 ui.setconfig("web", o, opts[o])
2563 ui.setconfig("web", o, opts[o])
2557
2564
2558 if repo is None and not ui.config("web", "webdir_conf"):
2565 if repo is None and not ui.config("web", "webdir_conf"):
2559 raise hg.RepoError(_('no repo found'))
2566 raise hg.RepoError(_('no repo found'))
2560
2567
2561 if opts['daemon'] and not opts['daemon_pipefds']:
2568 if opts['daemon'] and not opts['daemon_pipefds']:
2562 rfd, wfd = os.pipe()
2569 rfd, wfd = os.pipe()
2563 args = sys.argv[:]
2570 args = sys.argv[:]
2564 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2571 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2565 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2572 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2566 args[0], args)
2573 args[0], args)
2567 os.close(wfd)
2574 os.close(wfd)
2568 os.read(rfd, 1)
2575 os.read(rfd, 1)
2569 os._exit(0)
2576 os._exit(0)
2570
2577
2571 try:
2578 try:
2572 httpd = hgweb.server.create_server(ui, repo)
2579 httpd = hgweb.server.create_server(ui, repo)
2573 except socket.error, inst:
2580 except socket.error, inst:
2574 raise util.Abort(_('cannot start server: ') + inst.args[1])
2581 raise util.Abort(_('cannot start server: ') + inst.args[1])
2575
2582
2576 if ui.verbose:
2583 if ui.verbose:
2577 addr, port = httpd.socket.getsockname()
2584 addr, port = httpd.socket.getsockname()
2578 if addr == '0.0.0.0':
2585 if addr == '0.0.0.0':
2579 addr = socket.gethostname()
2586 addr = socket.gethostname()
2580 else:
2587 else:
2581 try:
2588 try:
2582 addr = socket.gethostbyaddr(addr)[0]
2589 addr = socket.gethostbyaddr(addr)[0]
2583 except socket.error:
2590 except socket.error:
2584 pass
2591 pass
2585 if port != 80:
2592 if port != 80:
2586 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2593 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2587 else:
2594 else:
2588 ui.status(_('listening at http://%s/\n') % addr)
2595 ui.status(_('listening at http://%s/\n') % addr)
2589
2596
2590 if opts['pid_file']:
2597 if opts['pid_file']:
2591 fp = open(opts['pid_file'], 'w')
2598 fp = open(opts['pid_file'], 'w')
2592 fp.write(str(os.getpid()) + '\n')
2599 fp.write(str(os.getpid()) + '\n')
2593 fp.close()
2600 fp.close()
2594
2601
2595 if opts['daemon_pipefds']:
2602 if opts['daemon_pipefds']:
2596 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2603 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2597 os.close(rfd)
2604 os.close(rfd)
2598 os.write(wfd, 'y')
2605 os.write(wfd, 'y')
2599 os.close(wfd)
2606 os.close(wfd)
2600 sys.stdout.flush()
2607 sys.stdout.flush()
2601 sys.stderr.flush()
2608 sys.stderr.flush()
2602 fd = os.open(util.nulldev, os.O_RDWR)
2609 fd = os.open(util.nulldev, os.O_RDWR)
2603 if fd != 0: os.dup2(fd, 0)
2610 if fd != 0: os.dup2(fd, 0)
2604 if fd != 1: os.dup2(fd, 1)
2611 if fd != 1: os.dup2(fd, 1)
2605 if fd != 2: os.dup2(fd, 2)
2612 if fd != 2: os.dup2(fd, 2)
2606 if fd not in (0, 1, 2): os.close(fd)
2613 if fd not in (0, 1, 2): os.close(fd)
2607
2614
2608 httpd.serve_forever()
2615 httpd.serve_forever()
2609
2616
2610 def status(ui, repo, *pats, **opts):
2617 def status(ui, repo, *pats, **opts):
2611 """show changed files in the working directory
2618 """show changed files in the working directory
2612
2619
2613 Show status of files in the repository. If names are given, only
2620 Show status of files in the repository. If names are given, only
2614 files that match are shown. Files that are clean or ignored, are
2621 files that match are shown. Files that are clean or ignored, are
2615 not listed unless -c (clean), -i (ignored) or -A is given.
2622 not listed unless -c (clean), -i (ignored) or -A is given.
2616
2623
2617 The codes used to show the status of files are:
2624 The codes used to show the status of files are:
2618 M = modified
2625 M = modified
2619 A = added
2626 A = added
2620 R = removed
2627 R = removed
2621 C = clean
2628 C = clean
2622 ! = deleted, but still tracked
2629 ! = deleted, but still tracked
2623 ? = not tracked
2630 ? = not tracked
2624 I = ignored (not shown by default)
2631 I = ignored (not shown by default)
2625 = the previous added file was copied from here
2632 = the previous added file was copied from here
2626 """
2633 """
2627
2634
2628 all = opts['all']
2635 all = opts['all']
2629
2636
2630 files, matchfn, anypats = matchpats(repo, pats, opts)
2637 files, matchfn, anypats = matchpats(repo, pats, opts)
2631 cwd = (pats and repo.getcwd()) or ''
2638 cwd = (pats and repo.getcwd()) or ''
2632 modified, added, removed, deleted, unknown, ignored, clean = [
2639 modified, added, removed, deleted, unknown, ignored, clean = [
2633 [util.pathto(cwd, x) for x in n]
2640 [util.pathto(cwd, x) for x in n]
2634 for n in repo.status(files=files, match=matchfn,
2641 for n in repo.status(files=files, match=matchfn,
2635 list_ignored=all or opts['ignored'],
2642 list_ignored=all or opts['ignored'],
2636 list_clean=all or opts['clean'])]
2643 list_clean=all or opts['clean'])]
2637
2644
2638 changetypes = (('modified', 'M', modified),
2645 changetypes = (('modified', 'M', modified),
2639 ('added', 'A', added),
2646 ('added', 'A', added),
2640 ('removed', 'R', removed),
2647 ('removed', 'R', removed),
2641 ('deleted', '!', deleted),
2648 ('deleted', '!', deleted),
2642 ('unknown', '?', unknown),
2649 ('unknown', '?', unknown),
2643 ('ignored', 'I', ignored))
2650 ('ignored', 'I', ignored))
2644
2651
2645 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2652 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2646
2653
2647 end = opts['print0'] and '\0' or '\n'
2654 end = opts['print0'] and '\0' or '\n'
2648
2655
2649 for opt, char, changes in ([ct for ct in explicit_changetypes
2656 for opt, char, changes in ([ct for ct in explicit_changetypes
2650 if all or opts[ct[0]]]
2657 if all or opts[ct[0]]]
2651 or changetypes):
2658 or changetypes):
2652 if opts['no_status']:
2659 if opts['no_status']:
2653 format = "%%s%s" % end
2660 format = "%%s%s" % end
2654 else:
2661 else:
2655 format = "%s %%s%s" % (char, end)
2662 format = "%s %%s%s" % (char, end)
2656
2663
2657 for f in changes:
2664 for f in changes:
2658 ui.write(format % f)
2665 ui.write(format % f)
2659 if ((all or opts.get('copies')) and not opts.get('no_status')
2666 if ((all or opts.get('copies')) and not opts.get('no_status')
2660 and opt == 'added' and repo.dirstate.copies.has_key(f)):
2667 and opt == 'added' and repo.dirstate.copies.has_key(f)):
2661 ui.write(' %s%s' % (repo.dirstate.copies[f], end))
2668 ui.write(' %s%s' % (repo.dirstate.copies[f], end))
2662
2669
2663 def tag(ui, repo, name, rev_=None, **opts):
2670 def tag(ui, repo, name, rev_=None, **opts):
2664 """add a tag for the current tip or a given revision
2671 """add a tag for the current tip or a given revision
2665
2672
2666 Name a particular revision using <name>.
2673 Name a particular revision using <name>.
2667
2674
2668 Tags are used to name particular revisions of the repository and are
2675 Tags are used to name particular revisions of the repository and are
2669 very useful to compare different revision, to go back to significant
2676 very useful to compare different revision, to go back to significant
2670 earlier versions or to mark branch points as releases, etc.
2677 earlier versions or to mark branch points as releases, etc.
2671
2678
2672 If no revision is given, the parent of the working directory is used.
2679 If no revision is given, the parent of the working directory is used.
2673
2680
2674 To facilitate version control, distribution, and merging of tags,
2681 To facilitate version control, distribution, and merging of tags,
2675 they are stored as a file named ".hgtags" which is managed
2682 they are stored as a file named ".hgtags" which is managed
2676 similarly to other project files and can be hand-edited if
2683 similarly to other project files and can be hand-edited if
2677 necessary. The file '.hg/localtags' is used for local tags (not
2684 necessary. The file '.hg/localtags' is used for local tags (not
2678 shared among repositories).
2685 shared among repositories).
2679 """
2686 """
2680 if name == "tip":
2687 if name == "tip":
2681 raise util.Abort(_("the name 'tip' is reserved"))
2688 raise util.Abort(_("the name 'tip' is reserved"))
2682 if rev_ is not None:
2689 if rev_ is not None:
2683 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2690 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2684 "please use 'hg tag [-r REV] NAME' instead\n"))
2691 "please use 'hg tag [-r REV] NAME' instead\n"))
2685 if opts['rev']:
2692 if opts['rev']:
2686 raise util.Abort(_("use only one form to specify the revision"))
2693 raise util.Abort(_("use only one form to specify the revision"))
2687 if opts['rev']:
2694 if opts['rev']:
2688 rev_ = opts['rev']
2695 rev_ = opts['rev']
2689 if rev_:
2696 if rev_:
2690 r = hex(repo.lookup(rev_))
2697 r = hex(repo.lookup(rev_))
2691 else:
2698 else:
2692 p1, p2 = repo.dirstate.parents()
2699 p1, p2 = repo.dirstate.parents()
2693 if p1 == nullid:
2700 if p1 == nullid:
2694 raise util.Abort(_('no revision to tag'))
2701 raise util.Abort(_('no revision to tag'))
2695 if p2 != nullid:
2702 if p2 != nullid:
2696 raise util.Abort(_('outstanding uncommitted merges'))
2703 raise util.Abort(_('outstanding uncommitted merges'))
2697 r = hex(p1)
2704 r = hex(p1)
2698
2705
2699 repo.tag(name, r, opts['local'], opts['message'], opts['user'],
2706 repo.tag(name, r, opts['local'], opts['message'], opts['user'],
2700 opts['date'])
2707 opts['date'])
2701
2708
2702 def tags(ui, repo):
2709 def tags(ui, repo):
2703 """list repository tags
2710 """list repository tags
2704
2711
2705 List the repository tags.
2712 List the repository tags.
2706
2713
2707 This lists both regular and local tags.
2714 This lists both regular and local tags.
2708 """
2715 """
2709
2716
2710 l = repo.tagslist()
2717 l = repo.tagslist()
2711 l.reverse()
2718 l.reverse()
2712 for t, n in l:
2719 for t, n in l:
2713 try:
2720 try:
2714 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2721 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2715 except KeyError:
2722 except KeyError:
2716 r = " ?:?"
2723 r = " ?:?"
2717 if ui.quiet:
2724 if ui.quiet:
2718 ui.write("%s\n" % t)
2725 ui.write("%s\n" % t)
2719 else:
2726 else:
2720 ui.write("%-30s %s\n" % (t, r))
2727 ui.write("%-30s %s\n" % (t, r))
2721
2728
2722 def tip(ui, repo, **opts):
2729 def tip(ui, repo, **opts):
2723 """show the tip revision
2730 """show the tip revision
2724
2731
2725 Show the tip revision.
2732 Show the tip revision.
2726 """
2733 """
2727 n = repo.changelog.tip()
2734 n = repo.changelog.tip()
2728 br = None
2735 br = None
2729 if opts['branches']:
2736 if opts['branches']:
2730 br = repo.branchlookup([n])
2737 br = repo.branchlookup([n])
2731 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2738 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2732 if opts['patch']:
2739 if opts['patch']:
2733 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2740 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2734
2741
2735 def unbundle(ui, repo, fname, **opts):
2742 def unbundle(ui, repo, fname, **opts):
2736 """apply a changegroup file
2743 """apply a changegroup file
2737
2744
2738 Apply a compressed changegroup file generated by the bundle
2745 Apply a compressed changegroup file generated by the bundle
2739 command.
2746 command.
2740 """
2747 """
2741 f = urllib.urlopen(fname)
2748 f = urllib.urlopen(fname)
2742
2749
2743 header = f.read(6)
2750 header = f.read(6)
2744 if not header.startswith("HG"):
2751 if not header.startswith("HG"):
2745 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2752 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2746 elif not header.startswith("HG10"):
2753 elif not header.startswith("HG10"):
2747 raise util.Abort(_("%s: unknown bundle version") % fname)
2754 raise util.Abort(_("%s: unknown bundle version") % fname)
2748 elif header == "HG10BZ":
2755 elif header == "HG10BZ":
2749 def generator(f):
2756 def generator(f):
2750 zd = bz2.BZ2Decompressor()
2757 zd = bz2.BZ2Decompressor()
2751 zd.decompress("BZ")
2758 zd.decompress("BZ")
2752 for chunk in f:
2759 for chunk in f:
2753 yield zd.decompress(chunk)
2760 yield zd.decompress(chunk)
2754 elif header == "HG10UN":
2761 elif header == "HG10UN":
2755 def generator(f):
2762 def generator(f):
2756 for chunk in f:
2763 for chunk in f:
2757 yield chunk
2764 yield chunk
2758 else:
2765 else:
2759 raise util.Abort(_("%s: unknown bundle compression type")
2766 raise util.Abort(_("%s: unknown bundle compression type")
2760 % fname)
2767 % fname)
2761 gen = generator(util.filechunkiter(f, 4096))
2768 gen = generator(util.filechunkiter(f, 4096))
2762 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2769 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle',
2763 'bundle:' + fname)
2770 'bundle:' + fname)
2764 return postincoming(ui, repo, modheads, opts['update'])
2771 return postincoming(ui, repo, modheads, opts['update'])
2765
2772
2766 def undo(ui, repo):
2773 def undo(ui, repo):
2767 """undo the last commit or pull (DEPRECATED)
2774 """undo the last commit or pull (DEPRECATED)
2768
2775
2769 (DEPRECATED)
2776 (DEPRECATED)
2770 This command is now deprecated and will be removed in a future
2777 This command is now deprecated and will be removed in a future
2771 release. Please use the rollback command instead. For usage
2778 release. Please use the rollback command instead. For usage
2772 instructions, see the rollback command.
2779 instructions, see the rollback command.
2773 """
2780 """
2774 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2781 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2775 repo.rollback()
2782 repo.rollback()
2776
2783
2777 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2784 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2778 branch=None, **opts):
2785 branch=None, **opts):
2779 """update or merge working directory
2786 """update or merge working directory
2780
2787
2781 Update the working directory to the specified revision.
2788 Update the working directory to the specified revision.
2782
2789
2783 If there are no outstanding changes in the working directory and
2790 If there are no outstanding changes in the working directory and
2784 there is a linear relationship between the current version and the
2791 there is a linear relationship between the current version and the
2785 requested version, the result is the requested version.
2792 requested version, the result is the requested version.
2786
2793
2787 To merge the working directory with another revision, use the
2794 To merge the working directory with another revision, use the
2788 merge command.
2795 merge command.
2789
2796
2790 By default, update will refuse to run if doing so would require
2797 By default, update will refuse to run if doing so would require
2791 merging or discarding local changes.
2798 merging or discarding local changes.
2792 """
2799 """
2793 if merge:
2800 if merge:
2794 ui.warn(_('(the -m/--merge option is deprecated; '
2801 ui.warn(_('(the -m/--merge option is deprecated; '
2795 'use the merge command instead)\n'))
2802 'use the merge command instead)\n'))
2796 return doupdate(ui, repo, node, merge, clean, force, branch, **opts)
2803 return doupdate(ui, repo, node, merge, clean, force, branch, **opts)
2797
2804
2798 def doupdate(ui, repo, node=None, merge=False, clean=False, force=None,
2805 def doupdate(ui, repo, node=None, merge=False, clean=False, force=None,
2799 branch=None, **opts):
2806 branch=None, **opts):
2800 if branch:
2807 if branch:
2801 br = repo.branchlookup(branch=branch)
2808 br = repo.branchlookup(branch=branch)
2802 found = []
2809 found = []
2803 for x in br:
2810 for x in br:
2804 if branch in br[x]:
2811 if branch in br[x]:
2805 found.append(x)
2812 found.append(x)
2806 if len(found) > 1:
2813 if len(found) > 1:
2807 ui.warn(_("Found multiple heads for %s\n") % branch)
2814 ui.warn(_("Found multiple heads for %s\n") % branch)
2808 for x in found:
2815 for x in found:
2809 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2816 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2810 return 1
2817 return 1
2811 if len(found) == 1:
2818 if len(found) == 1:
2812 node = found[0]
2819 node = found[0]
2813 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2820 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2814 else:
2821 else:
2815 ui.warn(_("branch %s not found\n") % (branch))
2822 ui.warn(_("branch %s not found\n") % (branch))
2816 return 1
2823 return 1
2817 else:
2824 else:
2818 node = node and repo.lookup(node) or repo.changelog.tip()
2825 node = node and repo.lookup(node) or repo.changelog.tip()
2819 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2826 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2820
2827
2821 def verify(ui, repo):
2828 def verify(ui, repo):
2822 """verify the integrity of the repository
2829 """verify the integrity of the repository
2823
2830
2824 Verify the integrity of the current repository.
2831 Verify the integrity of the current repository.
2825
2832
2826 This will perform an extensive check of the repository's
2833 This will perform an extensive check of the repository's
2827 integrity, validating the hashes and checksums of each entry in
2834 integrity, validating the hashes and checksums of each entry in
2828 the changelog, manifest, and tracked files, as well as the
2835 the changelog, manifest, and tracked files, as well as the
2829 integrity of their crosslinks and indices.
2836 integrity of their crosslinks and indices.
2830 """
2837 """
2831 return repo.verify()
2838 return repo.verify()
2832
2839
2833 # Command options and aliases are listed here, alphabetically
2840 # Command options and aliases are listed here, alphabetically
2834
2841
2835 table = {
2842 table = {
2836 "^add":
2843 "^add":
2837 (add,
2844 (add,
2838 [('I', 'include', [], _('include names matching the given patterns')),
2845 [('I', 'include', [], _('include names matching the given patterns')),
2839 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2846 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2840 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2847 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2841 _('hg add [OPTION]... [FILE]...')),
2848 _('hg add [OPTION]... [FILE]...')),
2842 "debugaddremove|addremove":
2849 "debugaddremove|addremove":
2843 (addremove,
2850 (addremove,
2844 [('I', 'include', [], _('include names matching the given patterns')),
2851 [('I', 'include', [], _('include names matching the given patterns')),
2845 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2852 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2846 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2853 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2847 _('hg addremove [OPTION]... [FILE]...')),
2854 _('hg addremove [OPTION]... [FILE]...')),
2848 "^annotate":
2855 "^annotate":
2849 (annotate,
2856 (annotate,
2850 [('r', 'rev', '', _('annotate the specified revision')),
2857 [('r', 'rev', '', _('annotate the specified revision')),
2851 ('a', 'text', None, _('treat all files as text')),
2858 ('a', 'text', None, _('treat all files as text')),
2852 ('u', 'user', None, _('list the author')),
2859 ('u', 'user', None, _('list the author')),
2853 ('d', 'date', None, _('list the date')),
2860 ('d', 'date', None, _('list the date')),
2854 ('n', 'number', None, _('list the revision number (default)')),
2861 ('n', 'number', None, _('list the revision number (default)')),
2855 ('c', 'changeset', None, _('list the changeset')),
2862 ('c', 'changeset', None, _('list the changeset')),
2856 ('I', 'include', [], _('include names matching the given patterns')),
2863 ('I', 'include', [], _('include names matching the given patterns')),
2857 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2864 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2858 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2865 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2859 "archive":
2866 "archive":
2860 (archive,
2867 (archive,
2861 [('', 'no-decode', None, _('do not pass files through decoders')),
2868 [('', 'no-decode', None, _('do not pass files through decoders')),
2862 ('p', 'prefix', '', _('directory prefix for files in archive')),
2869 ('p', 'prefix', '', _('directory prefix for files in archive')),
2863 ('r', 'rev', '', _('revision to distribute')),
2870 ('r', 'rev', '', _('revision to distribute')),
2864 ('t', 'type', '', _('type of distribution to create')),
2871 ('t', 'type', '', _('type of distribution to create')),
2865 ('I', 'include', [], _('include names matching the given patterns')),
2872 ('I', 'include', [], _('include names matching the given patterns')),
2866 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2873 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2867 _('hg archive [OPTION]... DEST')),
2874 _('hg archive [OPTION]... DEST')),
2868 "backout":
2875 "backout":
2869 (backout,
2876 (backout,
2870 [('', 'merge', None,
2877 [('', 'merge', None,
2871 _('merge with old dirstate parent after backout')),
2878 _('merge with old dirstate parent after backout')),
2872 ('m', 'message', '', _('use <text> as commit message')),
2879 ('m', 'message', '', _('use <text> as commit message')),
2873 ('l', 'logfile', '', _('read commit message from <file>')),
2880 ('l', 'logfile', '', _('read commit message from <file>')),
2874 ('d', 'date', '', _('record datecode as commit date')),
2881 ('d', 'date', '', _('record datecode as commit date')),
2875 ('', 'parent', '', _('parent to choose when backing out merge')),
2882 ('', 'parent', '', _('parent to choose when backing out merge')),
2876 ('u', 'user', '', _('record user as committer')),
2883 ('u', 'user', '', _('record user as committer')),
2877 ('I', 'include', [], _('include names matching the given patterns')),
2884 ('I', 'include', [], _('include names matching the given patterns')),
2878 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2885 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2879 _('hg backout [OPTION]... REV')),
2886 _('hg backout [OPTION]... REV')),
2880 "bundle":
2887 "bundle":
2881 (bundle,
2888 (bundle,
2882 [('f', 'force', None,
2889 [('f', 'force', None,
2883 _('run even when remote repository is unrelated'))],
2890 _('run even when remote repository is unrelated'))],
2884 _('hg bundle FILE DEST')),
2891 _('hg bundle FILE DEST')),
2885 "cat":
2892 "cat":
2886 (cat,
2893 (cat,
2887 [('o', 'output', '', _('print output to file with formatted name')),
2894 [('o', 'output', '', _('print output to file with formatted name')),
2888 ('r', 'rev', '', _('print the given revision')),
2895 ('r', 'rev', '', _('print the given revision')),
2889 ('I', 'include', [], _('include names matching the given patterns')),
2896 ('I', 'include', [], _('include names matching the given patterns')),
2890 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2897 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2891 _('hg cat [OPTION]... FILE...')),
2898 _('hg cat [OPTION]... FILE...')),
2892 "^clone":
2899 "^clone":
2893 (clone,
2900 (clone,
2894 [('U', 'noupdate', None, _('do not update the new working directory')),
2901 [('U', 'noupdate', None, _('do not update the new working directory')),
2895 ('r', 'rev', [],
2902 ('r', 'rev', [],
2896 _('a changeset you would like to have after cloning')),
2903 _('a changeset you would like to have after cloning')),
2897 ('', 'pull', None, _('use pull protocol to copy metadata')),
2904 ('', 'pull', None, _('use pull protocol to copy metadata')),
2898 ('', 'uncompressed', None,
2905 ('', 'uncompressed', None,
2899 _('use uncompressed transfer (fast over LAN)')),
2906 _('use uncompressed transfer (fast over LAN)')),
2900 ('e', 'ssh', '', _('specify ssh command to use')),
2907 ('e', 'ssh', '', _('specify ssh command to use')),
2901 ('', 'remotecmd', '',
2908 ('', 'remotecmd', '',
2902 _('specify hg command to run on the remote side'))],
2909 _('specify hg command to run on the remote side'))],
2903 _('hg clone [OPTION]... SOURCE [DEST]')),
2910 _('hg clone [OPTION]... SOURCE [DEST]')),
2904 "^commit|ci":
2911 "^commit|ci":
2905 (commit,
2912 (commit,
2906 [('A', 'addremove', None,
2913 [('A', 'addremove', None,
2907 _('mark new/missing files as added/removed before committing')),
2914 _('mark new/missing files as added/removed before committing')),
2908 ('m', 'message', '', _('use <text> as commit message')),
2915 ('m', 'message', '', _('use <text> as commit message')),
2909 ('l', 'logfile', '', _('read the commit message from <file>')),
2916 ('l', 'logfile', '', _('read the commit message from <file>')),
2910 ('d', 'date', '', _('record datecode as commit date')),
2917 ('d', 'date', '', _('record datecode as commit date')),
2911 ('u', 'user', '', _('record user as commiter')),
2918 ('u', 'user', '', _('record user as commiter')),
2912 ('I', 'include', [], _('include names matching the given patterns')),
2919 ('I', 'include', [], _('include names matching the given patterns')),
2913 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2920 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2914 _('hg commit [OPTION]... [FILE]...')),
2921 _('hg commit [OPTION]... [FILE]...')),
2915 "copy|cp":
2922 "copy|cp":
2916 (copy,
2923 (copy,
2917 [('A', 'after', None, _('record a copy that has already occurred')),
2924 [('A', 'after', None, _('record a copy that has already occurred')),
2918 ('f', 'force', None,
2925 ('f', 'force', None,
2919 _('forcibly copy over an existing managed file')),
2926 _('forcibly copy over an existing managed file')),
2920 ('I', 'include', [], _('include names matching the given patterns')),
2927 ('I', 'include', [], _('include names matching the given patterns')),
2921 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2928 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2922 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2929 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2923 _('hg copy [OPTION]... [SOURCE]... DEST')),
2930 _('hg copy [OPTION]... [SOURCE]... DEST')),
2924 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2931 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2925 "debugcomplete":
2932 "debugcomplete":
2926 (debugcomplete,
2933 (debugcomplete,
2927 [('o', 'options', None, _('show the command options'))],
2934 [('o', 'options', None, _('show the command options'))],
2928 _('debugcomplete [-o] CMD')),
2935 _('debugcomplete [-o] CMD')),
2929 "debugrebuildstate":
2936 "debugrebuildstate":
2930 (debugrebuildstate,
2937 (debugrebuildstate,
2931 [('r', 'rev', '', _('revision to rebuild to'))],
2938 [('r', 'rev', '', _('revision to rebuild to'))],
2932 _('debugrebuildstate [-r REV] [REV]')),
2939 _('debugrebuildstate [-r REV] [REV]')),
2933 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2940 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2934 "debugconfig": (debugconfig, [], _('debugconfig [NAME]...')),
2941 "debugconfig": (debugconfig, [], _('debugconfig [NAME]...')),
2935 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2942 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2936 "debugstate": (debugstate, [], _('debugstate')),
2943 "debugstate": (debugstate, [], _('debugstate')),
2937 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2944 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2938 "debugindex": (debugindex, [], _('debugindex FILE')),
2945 "debugindex": (debugindex, [], _('debugindex FILE')),
2939 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2946 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2940 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2947 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2941 "debugwalk":
2948 "debugwalk":
2942 (debugwalk,
2949 (debugwalk,
2943 [('I', 'include', [], _('include names matching the given patterns')),
2950 [('I', 'include', [], _('include names matching the given patterns')),
2944 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2951 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2945 _('debugwalk [OPTION]... [FILE]...')),
2952 _('debugwalk [OPTION]... [FILE]...')),
2946 "^diff":
2953 "^diff":
2947 (diff,
2954 (diff,
2948 [('r', 'rev', [], _('revision')),
2955 [('r', 'rev', [], _('revision')),
2949 ('a', 'text', None, _('treat all files as text')),
2956 ('a', 'text', None, _('treat all files as text')),
2950 ('p', 'show-function', None,
2957 ('p', 'show-function', None,
2951 _('show which function each change is in')),
2958 _('show which function each change is in')),
2952 ('w', 'ignore-all-space', None,
2959 ('w', 'ignore-all-space', None,
2953 _('ignore white space when comparing lines')),
2960 _('ignore white space when comparing lines')),
2954 ('b', 'ignore-space-change', None,
2961 ('b', 'ignore-space-change', None,
2955 _('ignore changes in the amount of white space')),
2962 _('ignore changes in the amount of white space')),
2956 ('B', 'ignore-blank-lines', None,
2963 ('B', 'ignore-blank-lines', None,
2957 _('ignore changes whose lines are all blank')),
2964 _('ignore changes whose lines are all blank')),
2958 ('I', 'include', [], _('include names matching the given patterns')),
2965 ('I', 'include', [], _('include names matching the given patterns')),
2959 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2966 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2960 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2967 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2961 "^export":
2968 "^export":
2962 (export,
2969 (export,
2963 [('o', 'output', '', _('print output to file with formatted name')),
2970 [('o', 'output', '', _('print output to file with formatted name')),
2964 ('a', 'text', None, _('treat all files as text')),
2971 ('a', 'text', None, _('treat all files as text')),
2965 ('', 'switch-parent', None, _('diff against the second parent'))],
2972 ('', 'switch-parent', None, _('diff against the second parent'))],
2966 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2973 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2967 "debugforget|forget":
2974 "debugforget|forget":
2968 (forget,
2975 (forget,
2969 [('I', 'include', [], _('include names matching the given patterns')),
2976 [('I', 'include', [], _('include names matching the given patterns')),
2970 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2977 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2971 _('hg forget [OPTION]... FILE...')),
2978 _('hg forget [OPTION]... FILE...')),
2972 "grep":
2979 "grep":
2973 (grep,
2980 (grep,
2974 [('0', 'print0', None, _('end fields with NUL')),
2981 [('0', 'print0', None, _('end fields with NUL')),
2975 ('', 'all', None, _('print all revisions that match')),
2982 ('', 'all', None, _('print all revisions that match')),
2976 ('i', 'ignore-case', None, _('ignore case when matching')),
2983 ('i', 'ignore-case', None, _('ignore case when matching')),
2977 ('l', 'files-with-matches', None,
2984 ('l', 'files-with-matches', None,
2978 _('print only filenames and revs that match')),
2985 _('print only filenames and revs that match')),
2979 ('n', 'line-number', None, _('print matching line numbers')),
2986 ('n', 'line-number', None, _('print matching line numbers')),
2980 ('r', 'rev', [], _('search in given revision range')),
2987 ('r', 'rev', [], _('search in given revision range')),
2981 ('u', 'user', None, _('print user who committed change')),
2988 ('u', 'user', None, _('print user who committed change')),
2982 ('I', 'include', [], _('include names matching the given patterns')),
2989 ('I', 'include', [], _('include names matching the given patterns')),
2983 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2990 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2984 _('hg grep [OPTION]... PATTERN [FILE]...')),
2991 _('hg grep [OPTION]... PATTERN [FILE]...')),
2985 "heads":
2992 "heads":
2986 (heads,
2993 (heads,
2987 [('b', 'branches', None, _('show branches')),
2994 [('b', 'branches', None, _('show branches')),
2988 ('', 'style', '', _('display using template map file')),
2995 ('', 'style', '', _('display using template map file')),
2989 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2996 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2990 ('', 'template', '', _('display with template'))],
2997 ('', 'template', '', _('display with template'))],
2991 _('hg heads [-b] [-r <rev>]')),
2998 _('hg heads [-b] [-r <rev>]')),
2992 "help": (help_, [], _('hg help [COMMAND]')),
2999 "help": (help_, [], _('hg help [COMMAND]')),
2993 "identify|id": (identify, [], _('hg identify')),
3000 "identify|id": (identify, [], _('hg identify')),
2994 "import|patch":
3001 "import|patch":
2995 (import_,
3002 (import_,
2996 [('p', 'strip', 1,
3003 [('p', 'strip', 1,
2997 _('directory strip option for patch. This has the same\n'
3004 _('directory strip option for patch. This has the same\n'
2998 'meaning as the corresponding patch option')),
3005 'meaning as the corresponding patch option')),
2999 ('m', 'message', '', _('use <text> as commit message')),
3006 ('m', 'message', '', _('use <text> as commit message')),
3000 ('b', 'base', '', _('base path')),
3007 ('b', 'base', '', _('base path')),
3001 ('f', 'force', None,
3008 ('f', 'force', None,
3002 _('skip check for outstanding uncommitted changes'))],
3009 _('skip check for outstanding uncommitted changes'))],
3003 _('hg import [-p NUM] [-b BASE] [-m MESSAGE] [-f] PATCH...')),
3010 _('hg import [-p NUM] [-b BASE] [-m MESSAGE] [-f] PATCH...')),
3004 "incoming|in": (incoming,
3011 "incoming|in": (incoming,
3005 [('M', 'no-merges', None, _('do not show merges')),
3012 [('M', 'no-merges', None, _('do not show merges')),
3006 ('f', 'force', None,
3013 ('f', 'force', None,
3007 _('run even when remote repository is unrelated')),
3014 _('run even when remote repository is unrelated')),
3008 ('', 'style', '', _('display using template map file')),
3015 ('', 'style', '', _('display using template map file')),
3009 ('n', 'newest-first', None, _('show newest record first')),
3016 ('n', 'newest-first', None, _('show newest record first')),
3010 ('', 'bundle', '', _('file to store the bundles into')),
3017 ('', 'bundle', '', _('file to store the bundles into')),
3011 ('p', 'patch', None, _('show patch')),
3018 ('p', 'patch', None, _('show patch')),
3012 ('r', 'rev', [], _('a specific revision you would like to pull')),
3019 ('r', 'rev', [], _('a specific revision you would like to pull')),
3013 ('', 'template', '', _('display with template')),
3020 ('', 'template', '', _('display with template')),
3014 ('e', 'ssh', '', _('specify ssh command to use')),
3021 ('e', 'ssh', '', _('specify ssh command to use')),
3015 ('', 'remotecmd', '',
3022 ('', 'remotecmd', '',
3016 _('specify hg command to run on the remote side'))],
3023 _('specify hg command to run on the remote side'))],
3017 _('hg incoming [-p] [-n] [-M] [-r REV]...'
3024 _('hg incoming [-p] [-n] [-M] [-r REV]...'
3018 ' [--bundle FILENAME] [SOURCE]')),
3025 ' [--bundle FILENAME] [SOURCE]')),
3019 "^init":
3026 "^init":
3020 (init,
3027 (init,
3021 [('e', 'ssh', '', _('specify ssh command to use')),
3028 [('e', 'ssh', '', _('specify ssh command to use')),
3022 ('', 'remotecmd', '',
3029 ('', 'remotecmd', '',
3023 _('specify hg command to run on the remote side'))],
3030 _('specify hg command to run on the remote side'))],
3024 _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
3031 _('hg init [-e FILE] [--remotecmd FILE] [DEST]')),
3025 "locate":
3032 "locate":
3026 (locate,
3033 (locate,
3027 [('r', 'rev', '', _('search the repository as it stood at rev')),
3034 [('r', 'rev', '', _('search the repository as it stood at rev')),
3028 ('0', 'print0', None,
3035 ('0', 'print0', None,
3029 _('end filenames with NUL, for use with xargs')),
3036 _('end filenames with NUL, for use with xargs')),
3030 ('f', 'fullpath', None,
3037 ('f', 'fullpath', None,
3031 _('print complete paths from the filesystem root')),
3038 _('print complete paths from the filesystem root')),
3032 ('I', 'include', [], _('include names matching the given patterns')),
3039 ('I', 'include', [], _('include names matching the given patterns')),
3033 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3040 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3034 _('hg locate [OPTION]... [PATTERN]...')),
3041 _('hg locate [OPTION]... [PATTERN]...')),
3035 "^log|history":
3042 "^log|history":
3036 (log,
3043 (log,
3037 [('b', 'branches', None, _('show branches')),
3044 [('b', 'branches', None, _('show branches')),
3038 ('k', 'keyword', [], _('search for a keyword')),
3045 ('k', 'keyword', [], _('search for a keyword')),
3039 ('l', 'limit', '', _('limit number of changes displayed')),
3046 ('l', 'limit', '', _('limit number of changes displayed')),
3040 ('r', 'rev', [], _('show the specified revision or range')),
3047 ('r', 'rev', [], _('show the specified revision or range')),
3041 ('M', 'no-merges', None, _('do not show merges')),
3048 ('M', 'no-merges', None, _('do not show merges')),
3042 ('', 'style', '', _('display using template map file')),
3049 ('', 'style', '', _('display using template map file')),
3043 ('m', 'only-merges', None, _('show only merges')),
3050 ('m', 'only-merges', None, _('show only merges')),
3044 ('p', 'patch', None, _('show patch')),
3051 ('p', 'patch', None, _('show patch')),
3045 ('', 'template', '', _('display with template')),
3052 ('', 'template', '', _('display with template')),
3046 ('I', 'include', [], _('include names matching the given patterns')),
3053 ('I', 'include', [], _('include names matching the given patterns')),
3047 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3054 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3048 _('hg log [OPTION]... [FILE]')),
3055 _('hg log [OPTION]... [FILE]')),
3049 "manifest": (manifest, [], _('hg manifest [REV]')),
3056 "manifest": (manifest, [], _('hg manifest [REV]')),
3050 "merge":
3057 "merge":
3051 (merge,
3058 (merge,
3052 [('b', 'branch', '', _('merge with head of a specific branch')),
3059 [('b', 'branch', '', _('merge with head of a specific branch')),
3053 ('f', 'force', None, _('force a merge with outstanding changes'))],
3060 ('f', 'force', None, _('force a merge with outstanding changes'))],
3054 _('hg merge [-b TAG] [-f] [REV]')),
3061 _('hg merge [-b TAG] [-f] [REV]')),
3055 "outgoing|out": (outgoing,
3062 "outgoing|out": (outgoing,
3056 [('M', 'no-merges', None, _('do not show merges')),
3063 [('M', 'no-merges', None, _('do not show merges')),
3057 ('f', 'force', None,
3064 ('f', 'force', None,
3058 _('run even when remote repository is unrelated')),
3065 _('run even when remote repository is unrelated')),
3059 ('p', 'patch', None, _('show patch')),
3066 ('p', 'patch', None, _('show patch')),
3060 ('', 'style', '', _('display using template map file')),
3067 ('', 'style', '', _('display using template map file')),
3061 ('r', 'rev', [], _('a specific revision you would like to push')),
3068 ('r', 'rev', [], _('a specific revision you would like to push')),
3062 ('n', 'newest-first', None, _('show newest record first')),
3069 ('n', 'newest-first', None, _('show newest record first')),
3063 ('', 'template', '', _('display with template')),
3070 ('', 'template', '', _('display with template')),
3064 ('e', 'ssh', '', _('specify ssh command to use')),
3071 ('e', 'ssh', '', _('specify ssh command to use')),
3065 ('', 'remotecmd', '',
3072 ('', 'remotecmd', '',
3066 _('specify hg command to run on the remote side'))],
3073 _('specify hg command to run on the remote side'))],
3067 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3074 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3068 "^parents":
3075 "^parents":
3069 (parents,
3076 (parents,
3070 [('b', 'branches', None, _('show branches')),
3077 [('b', 'branches', None, _('show branches')),
3071 ('r', 'rev', '', _('show parents from the specified rev')),
3078 ('r', 'rev', '', _('show parents from the specified rev')),
3072 ('', 'style', '', _('display using template map file')),
3079 ('', 'style', '', _('display using template map file')),
3073 ('', 'template', '', _('display with template'))],
3080 ('', 'template', '', _('display with template'))],
3074 _('hg parents [-b] [-r REV] [FILE]')),
3081 _('hg parents [-b] [-r REV] [FILE]')),
3075 "paths": (paths, [], _('hg paths [NAME]')),
3082 "paths": (paths, [], _('hg paths [NAME]')),
3076 "^pull":
3083 "^pull":
3077 (pull,
3084 (pull,
3078 [('u', 'update', None,
3085 [('u', 'update', None,
3079 _('update the working directory to tip after pull')),
3086 _('update the working directory to tip after pull')),
3080 ('e', 'ssh', '', _('specify ssh command to use')),
3087 ('e', 'ssh', '', _('specify ssh command to use')),
3081 ('f', 'force', None,
3088 ('f', 'force', None,
3082 _('run even when remote repository is unrelated')),
3089 _('run even when remote repository is unrelated')),
3083 ('r', 'rev', [], _('a specific revision you would like to pull')),
3090 ('r', 'rev', [], _('a specific revision you would like to pull')),
3084 ('', 'remotecmd', '',
3091 ('', 'remotecmd', '',
3085 _('specify hg command to run on the remote side'))],
3092 _('specify hg command to run on the remote side'))],
3086 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
3093 _('hg pull [-u] [-r REV]... [-e FILE] [--remotecmd FILE] [SOURCE]')),
3087 "^push":
3094 "^push":
3088 (push,
3095 (push,
3089 [('f', 'force', None, _('force push')),
3096 [('f', 'force', None, _('force push')),
3090 ('e', 'ssh', '', _('specify ssh command to use')),
3097 ('e', 'ssh', '', _('specify ssh command to use')),
3091 ('r', 'rev', [], _('a specific revision you would like to push')),
3098 ('r', 'rev', [], _('a specific revision you would like to push')),
3092 ('', 'remotecmd', '',
3099 ('', 'remotecmd', '',
3093 _('specify hg command to run on the remote side'))],
3100 _('specify hg command to run on the remote side'))],
3094 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
3101 _('hg push [-f] [-r REV]... [-e FILE] [--remotecmd FILE] [DEST]')),
3095 "debugrawcommit|rawcommit":
3102 "debugrawcommit|rawcommit":
3096 (rawcommit,
3103 (rawcommit,
3097 [('p', 'parent', [], _('parent')),
3104 [('p', 'parent', [], _('parent')),
3098 ('d', 'date', '', _('date code')),
3105 ('d', 'date', '', _('date code')),
3099 ('u', 'user', '', _('user')),
3106 ('u', 'user', '', _('user')),
3100 ('F', 'files', '', _('file list')),
3107 ('F', 'files', '', _('file list')),
3101 ('m', 'message', '', _('commit message')),
3108 ('m', 'message', '', _('commit message')),
3102 ('l', 'logfile', '', _('commit message file'))],
3109 ('l', 'logfile', '', _('commit message file'))],
3103 _('hg debugrawcommit [OPTION]... [FILE]...')),
3110 _('hg debugrawcommit [OPTION]... [FILE]...')),
3104 "recover": (recover, [], _('hg recover')),
3111 "recover": (recover, [], _('hg recover')),
3105 "^remove|rm":
3112 "^remove|rm":
3106 (remove,
3113 (remove,
3107 [('A', 'after', None, _('record remove that has already occurred')),
3114 [('A', 'after', None, _('record remove that has already occurred')),
3108 ('f', 'force', None, _('remove file even if modified')),
3115 ('f', 'force', None, _('remove file even if modified')),
3109 ('I', 'include', [], _('include names matching the given patterns')),
3116 ('I', 'include', [], _('include names matching the given patterns')),
3110 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3117 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3111 _('hg remove [OPTION]... FILE...')),
3118 _('hg remove [OPTION]... FILE...')),
3112 "rename|mv":
3119 "rename|mv":
3113 (rename,
3120 (rename,
3114 [('A', 'after', None, _('record a rename that has already occurred')),
3121 [('A', 'after', None, _('record a rename that has already occurred')),
3115 ('f', 'force', None,
3122 ('f', 'force', None,
3116 _('forcibly copy over an existing managed file')),
3123 _('forcibly copy over an existing managed file')),
3117 ('I', 'include', [], _('include names matching the given patterns')),
3124 ('I', 'include', [], _('include names matching the given patterns')),
3118 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3125 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3119 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3126 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3120 _('hg rename [OPTION]... SOURCE... DEST')),
3127 _('hg rename [OPTION]... SOURCE... DEST')),
3121 "^revert":
3128 "^revert":
3122 (revert,
3129 (revert,
3123 [('r', 'rev', '', _('revision to revert to')),
3130 [('r', 'rev', '', _('revision to revert to')),
3124 ('', 'no-backup', None, _('do not save backup copies of files')),
3131 ('', 'no-backup', None, _('do not save backup copies of files')),
3125 ('I', 'include', [], _('include names matching given patterns')),
3132 ('I', 'include', [], _('include names matching given patterns')),
3126 ('X', 'exclude', [], _('exclude names matching given patterns')),
3133 ('X', 'exclude', [], _('exclude names matching given patterns')),
3127 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3134 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3128 _('hg revert [-r REV] [NAME]...')),
3135 _('hg revert [-r REV] [NAME]...')),
3129 "rollback": (rollback, [], _('hg rollback')),
3136 "rollback": (rollback, [], _('hg rollback')),
3130 "root": (root, [], _('hg root')),
3137 "root": (root, [], _('hg root')),
3131 "^serve":
3138 "^serve":
3132 (serve,
3139 (serve,
3133 [('A', 'accesslog', '', _('name of access log file to write to')),
3140 [('A', 'accesslog', '', _('name of access log file to write to')),
3134 ('d', 'daemon', None, _('run server in background')),
3141 ('d', 'daemon', None, _('run server in background')),
3135 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3142 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3136 ('E', 'errorlog', '', _('name of error log file to write to')),
3143 ('E', 'errorlog', '', _('name of error log file to write to')),
3137 ('p', 'port', 0, _('port to use (default: 8000)')),
3144 ('p', 'port', 0, _('port to use (default: 8000)')),
3138 ('a', 'address', '', _('address to use')),
3145 ('a', 'address', '', _('address to use')),
3139 ('n', 'name', '',
3146 ('n', 'name', '',
3140 _('name to show in web pages (default: working dir)')),
3147 _('name to show in web pages (default: working dir)')),
3141 ('', 'webdir-conf', '', _('name of the webdir config file'
3148 ('', 'webdir-conf', '', _('name of the webdir config file'
3142 ' (serve more than one repo)')),
3149 ' (serve more than one repo)')),
3143 ('', 'pid-file', '', _('name of file to write process ID to')),
3150 ('', 'pid-file', '', _('name of file to write process ID to')),
3144 ('', 'stdio', None, _('for remote clients')),
3151 ('', 'stdio', None, _('for remote clients')),
3145 ('t', 'templates', '', _('web templates to use')),
3152 ('t', 'templates', '', _('web templates to use')),
3146 ('', 'style', '', _('template style to use')),
3153 ('', 'style', '', _('template style to use')),
3147 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3154 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3148 _('hg serve [OPTION]...')),
3155 _('hg serve [OPTION]...')),
3149 "^status|st":
3156 "^status|st":
3150 (status,
3157 (status,
3151 [('A', 'all', None, _('show status of all files')),
3158 [('A', 'all', None, _('show status of all files')),
3152 ('m', 'modified', None, _('show only modified files')),
3159 ('m', 'modified', None, _('show only modified files')),
3153 ('a', 'added', None, _('show only added files')),
3160 ('a', 'added', None, _('show only added files')),
3154 ('r', 'removed', None, _('show only removed files')),
3161 ('r', 'removed', None, _('show only removed files')),
3155 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3162 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3156 ('c', 'clean', None, _('show only files without changes')),
3163 ('c', 'clean', None, _('show only files without changes')),
3157 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3164 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3158 ('i', 'ignored', None, _('show ignored files')),
3165 ('i', 'ignored', None, _('show ignored files')),
3159 ('n', 'no-status', None, _('hide status prefix')),
3166 ('n', 'no-status', None, _('hide status prefix')),
3160 ('C', 'copies', None, _('show source of copied files')),
3167 ('C', 'copies', None, _('show source of copied files')),
3161 ('0', 'print0', None,
3168 ('0', 'print0', None,
3162 _('end filenames with NUL, for use with xargs')),
3169 _('end filenames with NUL, for use with xargs')),
3163 ('I', 'include', [], _('include names matching the given patterns')),
3170 ('I', 'include', [], _('include names matching the given patterns')),
3164 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3171 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3165 _('hg status [OPTION]... [FILE]...')),
3172 _('hg status [OPTION]... [FILE]...')),
3166 "tag":
3173 "tag":
3167 (tag,
3174 (tag,
3168 [('l', 'local', None, _('make the tag local')),
3175 [('l', 'local', None, _('make the tag local')),
3169 ('m', 'message', '', _('message for tag commit log entry')),
3176 ('m', 'message', '', _('message for tag commit log entry')),
3170 ('d', 'date', '', _('record datecode as commit date')),
3177 ('d', 'date', '', _('record datecode as commit date')),
3171 ('u', 'user', '', _('record user as commiter')),
3178 ('u', 'user', '', _('record user as commiter')),
3172 ('r', 'rev', '', _('revision to tag'))],
3179 ('r', 'rev', '', _('revision to tag'))],
3173 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3180 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3174 "tags": (tags, [], _('hg tags')),
3181 "tags": (tags, [], _('hg tags')),
3175 "tip":
3182 "tip":
3176 (tip,
3183 (tip,
3177 [('b', 'branches', None, _('show branches')),
3184 [('b', 'branches', None, _('show branches')),
3178 ('', 'style', '', _('display using template map file')),
3185 ('', 'style', '', _('display using template map file')),
3179 ('p', 'patch', None, _('show patch')),
3186 ('p', 'patch', None, _('show patch')),
3180 ('', 'template', '', _('display with template'))],
3187 ('', 'template', '', _('display with template'))],
3181 _('hg tip [-b] [-p]')),
3188 _('hg tip [-b] [-p]')),
3182 "unbundle":
3189 "unbundle":
3183 (unbundle,
3190 (unbundle,
3184 [('u', 'update', None,
3191 [('u', 'update', None,
3185 _('update the working directory to tip after unbundle'))],
3192 _('update the working directory to tip after unbundle'))],
3186 _('hg unbundle [-u] FILE')),
3193 _('hg unbundle [-u] FILE')),
3187 "debugundo|undo": (undo, [], _('hg undo')),
3194 "debugundo|undo": (undo, [], _('hg undo')),
3188 "^update|up|checkout|co":
3195 "^update|up|checkout|co":
3189 (update,
3196 (update,
3190 [('b', 'branch', '', _('checkout the head of a specific branch')),
3197 [('b', 'branch', '', _('checkout the head of a specific branch')),
3191 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3198 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3192 ('C', 'clean', None, _('overwrite locally modified files')),
3199 ('C', 'clean', None, _('overwrite locally modified files')),
3193 ('f', 'force', None, _('force a merge with outstanding changes'))],
3200 ('f', 'force', None, _('force a merge with outstanding changes'))],
3194 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3201 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3195 "verify": (verify, [], _('hg verify')),
3202 "verify": (verify, [], _('hg verify')),
3196 "version": (show_version, [], _('hg version')),
3203 "version": (show_version, [], _('hg version')),
3197 }
3204 }
3198
3205
3199 globalopts = [
3206 globalopts = [
3200 ('R', 'repository', '',
3207 ('R', 'repository', '',
3201 _('repository root directory or symbolic path name')),
3208 _('repository root directory or symbolic path name')),
3202 ('', 'cwd', '', _('change working directory')),
3209 ('', 'cwd', '', _('change working directory')),
3203 ('y', 'noninteractive', None,
3210 ('y', 'noninteractive', None,
3204 _('do not prompt, assume \'yes\' for any required answers')),
3211 _('do not prompt, assume \'yes\' for any required answers')),
3205 ('q', 'quiet', None, _('suppress output')),
3212 ('q', 'quiet', None, _('suppress output')),
3206 ('v', 'verbose', None, _('enable additional output')),
3213 ('v', 'verbose', None, _('enable additional output')),
3207 ('', 'config', [], _('set/override config option')),
3214 ('', 'config', [], _('set/override config option')),
3208 ('', 'debug', None, _('enable debugging output')),
3215 ('', 'debug', None, _('enable debugging output')),
3209 ('', 'debugger', None, _('start debugger')),
3216 ('', 'debugger', None, _('start debugger')),
3210 ('', 'lsprof', None, _('print improved command execution profile')),
3217 ('', 'lsprof', None, _('print improved command execution profile')),
3211 ('', 'traceback', None, _('print traceback on exception')),
3218 ('', 'traceback', None, _('print traceback on exception')),
3212 ('', 'time', None, _('time how long the command takes')),
3219 ('', 'time', None, _('time how long the command takes')),
3213 ('', 'profile', None, _('print command execution profile')),
3220 ('', 'profile', None, _('print command execution profile')),
3214 ('', 'version', None, _('output version information and exit')),
3221 ('', 'version', None, _('output version information and exit')),
3215 ('h', 'help', None, _('display help and exit')),
3222 ('h', 'help', None, _('display help and exit')),
3216 ]
3223 ]
3217
3224
3218 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3225 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3219 " debugindex debugindexdot")
3226 " debugindex debugindexdot")
3220 optionalrepo = ("paths serve debugconfig")
3227 optionalrepo = ("paths serve debugconfig")
3221
3228
3222 def findpossible(cmd):
3229 def findpossible(cmd):
3223 """
3230 """
3224 Return cmd -> (aliases, command table entry)
3231 Return cmd -> (aliases, command table entry)
3225 for each matching command.
3232 for each matching command.
3226 Return debug commands (or their aliases) only if no normal command matches.
3233 Return debug commands (or their aliases) only if no normal command matches.
3227 """
3234 """
3228 choice = {}
3235 choice = {}
3229 debugchoice = {}
3236 debugchoice = {}
3230 for e in table.keys():
3237 for e in table.keys():
3231 aliases = e.lstrip("^").split("|")
3238 aliases = e.lstrip("^").split("|")
3232 found = None
3239 found = None
3233 if cmd in aliases:
3240 if cmd in aliases:
3234 found = cmd
3241 found = cmd
3235 else:
3242 else:
3236 for a in aliases:
3243 for a in aliases:
3237 if a.startswith(cmd):
3244 if a.startswith(cmd):
3238 found = a
3245 found = a
3239 break
3246 break
3240 if found is not None:
3247 if found is not None:
3241 if aliases[0].startswith("debug"):
3248 if aliases[0].startswith("debug"):
3242 debugchoice[found] = (aliases, table[e])
3249 debugchoice[found] = (aliases, table[e])
3243 else:
3250 else:
3244 choice[found] = (aliases, table[e])
3251 choice[found] = (aliases, table[e])
3245
3252
3246 if not choice and debugchoice:
3253 if not choice and debugchoice:
3247 choice = debugchoice
3254 choice = debugchoice
3248
3255
3249 return choice
3256 return choice
3250
3257
3251 def findcmd(cmd):
3258 def findcmd(cmd):
3252 """Return (aliases, command table entry) for command string."""
3259 """Return (aliases, command table entry) for command string."""
3253 choice = findpossible(cmd)
3260 choice = findpossible(cmd)
3254
3261
3255 if choice.has_key(cmd):
3262 if choice.has_key(cmd):
3256 return choice[cmd]
3263 return choice[cmd]
3257
3264
3258 if len(choice) > 1:
3265 if len(choice) > 1:
3259 clist = choice.keys()
3266 clist = choice.keys()
3260 clist.sort()
3267 clist.sort()
3261 raise AmbiguousCommand(cmd, clist)
3268 raise AmbiguousCommand(cmd, clist)
3262
3269
3263 if choice:
3270 if choice:
3264 return choice.values()[0]
3271 return choice.values()[0]
3265
3272
3266 raise UnknownCommand(cmd)
3273 raise UnknownCommand(cmd)
3267
3274
3268 def catchterm(*args):
3275 def catchterm(*args):
3269 raise util.SignalInterrupt
3276 raise util.SignalInterrupt
3270
3277
3271 def run():
3278 def run():
3272 sys.exit(dispatch(sys.argv[1:]))
3279 sys.exit(dispatch(sys.argv[1:]))
3273
3280
3274 class ParseError(Exception):
3281 class ParseError(Exception):
3275 """Exception raised on errors in parsing the command line."""
3282 """Exception raised on errors in parsing the command line."""
3276
3283
3277 def parse(ui, args):
3284 def parse(ui, args):
3278 options = {}
3285 options = {}
3279 cmdoptions = {}
3286 cmdoptions = {}
3280
3287
3281 try:
3288 try:
3282 args = fancyopts.fancyopts(args, globalopts, options)
3289 args = fancyopts.fancyopts(args, globalopts, options)
3283 except fancyopts.getopt.GetoptError, inst:
3290 except fancyopts.getopt.GetoptError, inst:
3284 raise ParseError(None, inst)
3291 raise ParseError(None, inst)
3285
3292
3286 if args:
3293 if args:
3287 cmd, args = args[0], args[1:]
3294 cmd, args = args[0], args[1:]
3288 aliases, i = findcmd(cmd)
3295 aliases, i = findcmd(cmd)
3289 cmd = aliases[0]
3296 cmd = aliases[0]
3290 defaults = ui.config("defaults", cmd)
3297 defaults = ui.config("defaults", cmd)
3291 if defaults:
3298 if defaults:
3292 args = defaults.split() + args
3299 args = defaults.split() + args
3293 c = list(i[1])
3300 c = list(i[1])
3294 else:
3301 else:
3295 cmd = None
3302 cmd = None
3296 c = []
3303 c = []
3297
3304
3298 # combine global options into local
3305 # combine global options into local
3299 for o in globalopts:
3306 for o in globalopts:
3300 c.append((o[0], o[1], options[o[1]], o[3]))
3307 c.append((o[0], o[1], options[o[1]], o[3]))
3301
3308
3302 try:
3309 try:
3303 args = fancyopts.fancyopts(args, c, cmdoptions)
3310 args = fancyopts.fancyopts(args, c, cmdoptions)
3304 except fancyopts.getopt.GetoptError, inst:
3311 except fancyopts.getopt.GetoptError, inst:
3305 raise ParseError(cmd, inst)
3312 raise ParseError(cmd, inst)
3306
3313
3307 # separate global options back out
3314 # separate global options back out
3308 for o in globalopts:
3315 for o in globalopts:
3309 n = o[1]
3316 n = o[1]
3310 options[n] = cmdoptions[n]
3317 options[n] = cmdoptions[n]
3311 del cmdoptions[n]
3318 del cmdoptions[n]
3312
3319
3313 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3320 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3314
3321
3315 external = {}
3322 external = {}
3316
3323
3317 def findext(name):
3324 def findext(name):
3318 '''return module with given extension name'''
3325 '''return module with given extension name'''
3319 try:
3326 try:
3320 return sys.modules[external[name]]
3327 return sys.modules[external[name]]
3321 except KeyError:
3328 except KeyError:
3322 dotname = '.' + name
3329 dotname = '.' + name
3323 for k, v in external.iteritems():
3330 for k, v in external.iteritems():
3324 if k.endswith('.' + name) or v == name:
3331 if k.endswith('.' + name) or v == name:
3325 return sys.modules[v]
3332 return sys.modules[v]
3326 raise KeyError(name)
3333 raise KeyError(name)
3327
3334
3328 def dispatch(args):
3335 def dispatch(args):
3329 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3336 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3330 num = getattr(signal, name, None)
3337 num = getattr(signal, name, None)
3331 if num: signal.signal(num, catchterm)
3338 if num: signal.signal(num, catchterm)
3332
3339
3333 try:
3340 try:
3334 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3341 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3335 except util.Abort, inst:
3342 except util.Abort, inst:
3336 sys.stderr.write(_("abort: %s\n") % inst)
3343 sys.stderr.write(_("abort: %s\n") % inst)
3337 return -1
3344 return -1
3338
3345
3339 for ext_name, load_from_name in u.extensions():
3346 for ext_name, load_from_name in u.extensions():
3340 try:
3347 try:
3341 if load_from_name:
3348 if load_from_name:
3342 # the module will be loaded in sys.modules
3349 # the module will be loaded in sys.modules
3343 # choose an unique name so that it doesn't
3350 # choose an unique name so that it doesn't
3344 # conflicts with other modules
3351 # conflicts with other modules
3345 module_name = "hgext_%s" % ext_name.replace('.', '_')
3352 module_name = "hgext_%s" % ext_name.replace('.', '_')
3346 mod = imp.load_source(module_name, load_from_name)
3353 mod = imp.load_source(module_name, load_from_name)
3347 else:
3354 else:
3348 def importh(name):
3355 def importh(name):
3349 mod = __import__(name)
3356 mod = __import__(name)
3350 components = name.split('.')
3357 components = name.split('.')
3351 for comp in components[1:]:
3358 for comp in components[1:]:
3352 mod = getattr(mod, comp)
3359 mod = getattr(mod, comp)
3353 return mod
3360 return mod
3354 try:
3361 try:
3355 mod = importh("hgext.%s" % ext_name)
3362 mod = importh("hgext.%s" % ext_name)
3356 except ImportError:
3363 except ImportError:
3357 mod = importh(ext_name)
3364 mod = importh(ext_name)
3358 external[ext_name] = mod.__name__
3365 external[ext_name] = mod.__name__
3359 except (util.SignalInterrupt, KeyboardInterrupt):
3366 except (util.SignalInterrupt, KeyboardInterrupt):
3360 raise
3367 raise
3361 except Exception, inst:
3368 except Exception, inst:
3362 u.warn(_("*** failed to import extension %s: %s\n") % (ext_name, inst))
3369 u.warn(_("*** failed to import extension %s: %s\n") % (ext_name, inst))
3363 if u.print_exc():
3370 if u.print_exc():
3364 return 1
3371 return 1
3365
3372
3366 for name in external.itervalues():
3373 for name in external.itervalues():
3367 mod = sys.modules[name]
3374 mod = sys.modules[name]
3368 uisetup = getattr(mod, 'uisetup', None)
3375 uisetup = getattr(mod, 'uisetup', None)
3369 if uisetup:
3376 if uisetup:
3370 uisetup(u)
3377 uisetup(u)
3371 cmdtable = getattr(mod, 'cmdtable', {})
3378 cmdtable = getattr(mod, 'cmdtable', {})
3372 for t in cmdtable:
3379 for t in cmdtable:
3373 if t in table:
3380 if t in table:
3374 u.warn(_("module %s overrides %s\n") % (name, t))
3381 u.warn(_("module %s overrides %s\n") % (name, t))
3375 table.update(cmdtable)
3382 table.update(cmdtable)
3376
3383
3377 try:
3384 try:
3378 cmd, func, args, options, cmdoptions = parse(u, args)
3385 cmd, func, args, options, cmdoptions = parse(u, args)
3379 if options["time"]:
3386 if options["time"]:
3380 def get_times():
3387 def get_times():
3381 t = os.times()
3388 t = os.times()
3382 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3389 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3383 t = (t[0], t[1], t[2], t[3], time.clock())
3390 t = (t[0], t[1], t[2], t[3], time.clock())
3384 return t
3391 return t
3385 s = get_times()
3392 s = get_times()
3386 def print_time():
3393 def print_time():
3387 t = get_times()
3394 t = get_times()
3388 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3395 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3389 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3396 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3390 atexit.register(print_time)
3397 atexit.register(print_time)
3391
3398
3392 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3399 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3393 not options["noninteractive"], options["traceback"],
3400 not options["noninteractive"], options["traceback"],
3394 options["config"])
3401 options["config"])
3395
3402
3396 # enter the debugger before command execution
3403 # enter the debugger before command execution
3397 if options['debugger']:
3404 if options['debugger']:
3398 pdb.set_trace()
3405 pdb.set_trace()
3399
3406
3400 try:
3407 try:
3401 if options['cwd']:
3408 if options['cwd']:
3402 try:
3409 try:
3403 os.chdir(options['cwd'])
3410 os.chdir(options['cwd'])
3404 except OSError, inst:
3411 except OSError, inst:
3405 raise util.Abort('%s: %s' %
3412 raise util.Abort('%s: %s' %
3406 (options['cwd'], inst.strerror))
3413 (options['cwd'], inst.strerror))
3407
3414
3408 path = u.expandpath(options["repository"]) or ""
3415 path = u.expandpath(options["repository"]) or ""
3409 repo = path and hg.repository(u, path=path) or None
3416 repo = path and hg.repository(u, path=path) or None
3410
3417
3411 if options['help']:
3418 if options['help']:
3412 return help_(u, cmd, options['version'])
3419 return help_(u, cmd, options['version'])
3413 elif options['version']:
3420 elif options['version']:
3414 return show_version(u)
3421 return show_version(u)
3415 elif not cmd:
3422 elif not cmd:
3416 return help_(u, 'shortlist')
3423 return help_(u, 'shortlist')
3417
3424
3418 if cmd not in norepo.split():
3425 if cmd not in norepo.split():
3419 try:
3426 try:
3420 if not repo:
3427 if not repo:
3421 repo = hg.repository(u, path=path)
3428 repo = hg.repository(u, path=path)
3422 u = repo.ui
3429 u = repo.ui
3423 for name in external.itervalues():
3430 for name in external.itervalues():
3424 mod = sys.modules[name]
3431 mod = sys.modules[name]
3425 if hasattr(mod, 'reposetup'):
3432 if hasattr(mod, 'reposetup'):
3426 mod.reposetup(u, repo)
3433 mod.reposetup(u, repo)
3427 except hg.RepoError:
3434 except hg.RepoError:
3428 if cmd not in optionalrepo.split():
3435 if cmd not in optionalrepo.split():
3429 raise
3436 raise
3430 d = lambda: func(u, repo, *args, **cmdoptions)
3437 d = lambda: func(u, repo, *args, **cmdoptions)
3431 else:
3438 else:
3432 d = lambda: func(u, *args, **cmdoptions)
3439 d = lambda: func(u, *args, **cmdoptions)
3433
3440
3434 # reupdate the options, repo/.hg/hgrc may have changed them
3441 # reupdate the options, repo/.hg/hgrc may have changed them
3435 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3442 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3436 not options["noninteractive"], options["traceback"],
3443 not options["noninteractive"], options["traceback"],
3437 options["config"])
3444 options["config"])
3438
3445
3439 try:
3446 try:
3440 if options['profile']:
3447 if options['profile']:
3441 import hotshot, hotshot.stats
3448 import hotshot, hotshot.stats
3442 prof = hotshot.Profile("hg.prof")
3449 prof = hotshot.Profile("hg.prof")
3443 try:
3450 try:
3444 try:
3451 try:
3445 return prof.runcall(d)
3452 return prof.runcall(d)
3446 except:
3453 except:
3447 try:
3454 try:
3448 u.warn(_('exception raised - generating '
3455 u.warn(_('exception raised - generating '
3449 'profile anyway\n'))
3456 'profile anyway\n'))
3450 except:
3457 except:
3451 pass
3458 pass
3452 raise
3459 raise
3453 finally:
3460 finally:
3454 prof.close()
3461 prof.close()
3455 stats = hotshot.stats.load("hg.prof")
3462 stats = hotshot.stats.load("hg.prof")
3456 stats.strip_dirs()
3463 stats.strip_dirs()
3457 stats.sort_stats('time', 'calls')
3464 stats.sort_stats('time', 'calls')
3458 stats.print_stats(40)
3465 stats.print_stats(40)
3459 elif options['lsprof']:
3466 elif options['lsprof']:
3460 try:
3467 try:
3461 from mercurial import lsprof
3468 from mercurial import lsprof
3462 except ImportError:
3469 except ImportError:
3463 raise util.Abort(_(
3470 raise util.Abort(_(
3464 'lsprof not available - install from '
3471 'lsprof not available - install from '
3465 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3472 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3466 p = lsprof.Profiler()
3473 p = lsprof.Profiler()
3467 p.enable(subcalls=True)
3474 p.enable(subcalls=True)
3468 try:
3475 try:
3469 return d()
3476 return d()
3470 finally:
3477 finally:
3471 p.disable()
3478 p.disable()
3472 stats = lsprof.Stats(p.getstats())
3479 stats = lsprof.Stats(p.getstats())
3473 stats.sort()
3480 stats.sort()
3474 stats.pprint(top=10, file=sys.stderr, climit=5)
3481 stats.pprint(top=10, file=sys.stderr, climit=5)
3475 else:
3482 else:
3476 return d()
3483 return d()
3477 finally:
3484 finally:
3478 u.flush()
3485 u.flush()
3479 except:
3486 except:
3480 # enter the debugger when we hit an exception
3487 # enter the debugger when we hit an exception
3481 if options['debugger']:
3488 if options['debugger']:
3482 pdb.post_mortem(sys.exc_info()[2])
3489 pdb.post_mortem(sys.exc_info()[2])
3483 u.print_exc()
3490 u.print_exc()
3484 raise
3491 raise
3485 except ParseError, inst:
3492 except ParseError, inst:
3486 if inst.args[0]:
3493 if inst.args[0]:
3487 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3494 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3488 help_(u, inst.args[0])
3495 help_(u, inst.args[0])
3489 else:
3496 else:
3490 u.warn(_("hg: %s\n") % inst.args[1])
3497 u.warn(_("hg: %s\n") % inst.args[1])
3491 help_(u, 'shortlist')
3498 help_(u, 'shortlist')
3492 except AmbiguousCommand, inst:
3499 except AmbiguousCommand, inst:
3493 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3500 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3494 (inst.args[0], " ".join(inst.args[1])))
3501 (inst.args[0], " ".join(inst.args[1])))
3495 except UnknownCommand, inst:
3502 except UnknownCommand, inst:
3496 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3503 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3497 help_(u, 'shortlist')
3504 help_(u, 'shortlist')
3498 except hg.RepoError, inst:
3505 except hg.RepoError, inst:
3499 u.warn(_("abort: %s!\n") % inst)
3506 u.warn(_("abort: %s!\n") % inst)
3500 except lock.LockHeld, inst:
3507 except lock.LockHeld, inst:
3501 if inst.errno == errno.ETIMEDOUT:
3508 if inst.errno == errno.ETIMEDOUT:
3502 reason = _('timed out waiting for lock held by %s') % inst.locker
3509 reason = _('timed out waiting for lock held by %s') % inst.locker
3503 else:
3510 else:
3504 reason = _('lock held by %s') % inst.locker
3511 reason = _('lock held by %s') % inst.locker
3505 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3512 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3506 except lock.LockUnavailable, inst:
3513 except lock.LockUnavailable, inst:
3507 u.warn(_("abort: could not lock %s: %s\n") %
3514 u.warn(_("abort: could not lock %s: %s\n") %
3508 (inst.desc or inst.filename, inst.strerror))
3515 (inst.desc or inst.filename, inst.strerror))
3509 except revlog.RevlogError, inst:
3516 except revlog.RevlogError, inst:
3510 u.warn(_("abort: "), inst, "!\n")
3517 u.warn(_("abort: "), inst, "!\n")
3511 except util.SignalInterrupt:
3518 except util.SignalInterrupt:
3512 u.warn(_("killed!\n"))
3519 u.warn(_("killed!\n"))
3513 except KeyboardInterrupt:
3520 except KeyboardInterrupt:
3514 try:
3521 try:
3515 u.warn(_("interrupted!\n"))
3522 u.warn(_("interrupted!\n"))
3516 except IOError, inst:
3523 except IOError, inst:
3517 if inst.errno == errno.EPIPE:
3524 if inst.errno == errno.EPIPE:
3518 if u.debugflag:
3525 if u.debugflag:
3519 u.warn(_("\nbroken pipe\n"))
3526 u.warn(_("\nbroken pipe\n"))
3520 else:
3527 else:
3521 raise
3528 raise
3522 except IOError, inst:
3529 except IOError, inst:
3523 if hasattr(inst, "code"):
3530 if hasattr(inst, "code"):
3524 u.warn(_("abort: %s\n") % inst)
3531 u.warn(_("abort: %s\n") % inst)
3525 elif hasattr(inst, "reason"):
3532 elif hasattr(inst, "reason"):
3526 u.warn(_("abort: error: %s\n") % inst.reason[1])
3533 u.warn(_("abort: error: %s\n") % inst.reason[1])
3527 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3534 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3528 if u.debugflag:
3535 if u.debugflag:
3529 u.warn(_("broken pipe\n"))
3536 u.warn(_("broken pipe\n"))
3530 elif getattr(inst, "strerror", None):
3537 elif getattr(inst, "strerror", None):
3531 if getattr(inst, "filename", None):
3538 if getattr(inst, "filename", None):
3532 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3539 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3533 else:
3540 else:
3534 u.warn(_("abort: %s\n") % inst.strerror)
3541 u.warn(_("abort: %s\n") % inst.strerror)
3535 else:
3542 else:
3536 raise
3543 raise
3537 except OSError, inst:
3544 except OSError, inst:
3538 if hasattr(inst, "filename"):
3545 if hasattr(inst, "filename"):
3539 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3546 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3540 else:
3547 else:
3541 u.warn(_("abort: %s\n") % inst.strerror)
3548 u.warn(_("abort: %s\n") % inst.strerror)
3542 except util.Abort, inst:
3549 except util.Abort, inst:
3543 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3550 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3544 except TypeError, inst:
3551 except TypeError, inst:
3545 # was this an argument error?
3552 # was this an argument error?
3546 tb = traceback.extract_tb(sys.exc_info()[2])
3553 tb = traceback.extract_tb(sys.exc_info()[2])
3547 if len(tb) > 2: # no
3554 if len(tb) > 2: # no
3548 raise
3555 raise
3549 u.debug(inst, "\n")
3556 u.debug(inst, "\n")
3550 u.warn(_("%s: invalid arguments\n") % cmd)
3557 u.warn(_("%s: invalid arguments\n") % cmd)
3551 help_(u, cmd)
3558 help_(u, cmd)
3552 except SystemExit, inst:
3559 except SystemExit, inst:
3553 # Commands shouldn't sys.exit directly, but give a return code.
3560 # Commands shouldn't sys.exit directly, but give a return code.
3554 # Just in case catch this and and pass exit code to caller.
3561 # Just in case catch this and and pass exit code to caller.
3555 return inst.code
3562 return inst.code
3556 except:
3563 except:
3557 u.warn(_("** unknown exception encountered, details follow\n"))
3564 u.warn(_("** unknown exception encountered, details follow\n"))
3558 u.warn(_("** report bug details to "
3565 u.warn(_("** report bug details to "
3559 "http://www.selenic.com/mercurial/bts\n"))
3566 "http://www.selenic.com/mercurial/bts\n"))
3560 u.warn(_("** or mercurial@selenic.com\n"))
3567 u.warn(_("** or mercurial@selenic.com\n"))
3561 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3568 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3562 % version.get_version())
3569 % version.get_version())
3563 raise
3570 raise
3564
3571
3565 return -1
3572 return -1
@@ -1,363 +1,357 b''
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 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 from i18n import gettext as _
8 from i18n import gettext as _
9 from demandload import *
9 from demandload import *
10 demandload(globals(), "errno getpass os re smtplib socket sys tempfile")
10 demandload(globals(), "errno getpass os re smtplib socket sys tempfile")
11 demandload(globals(), "ConfigParser templater traceback util")
11 demandload(globals(), "ConfigParser templater traceback util")
12
12
13 class ui(object):
13 class ui(object):
14 def __init__(self, verbose=False, debug=False, quiet=False,
14 def __init__(self, verbose=False, debug=False, quiet=False,
15 interactive=True, traceback=False, parentui=None):
15 interactive=True, traceback=False, parentui=None):
16 self.overlay = {}
16 self.overlay = {}
17 if parentui is None:
17 if parentui is None:
18 # this is the parent of all ui children
18 # this is the parent of all ui children
19 self.parentui = None
19 self.parentui = None
20 self.cdata = ConfigParser.SafeConfigParser()
20 self.cdata = ConfigParser.SafeConfigParser()
21 self.readconfig(util.rcpath())
21 self.readconfig(util.rcpath())
22
22
23 self.quiet = self.configbool("ui", "quiet")
23 self.quiet = self.configbool("ui", "quiet")
24 self.verbose = self.configbool("ui", "verbose")
24 self.verbose = self.configbool("ui", "verbose")
25 self.debugflag = self.configbool("ui", "debug")
25 self.debugflag = self.configbool("ui", "debug")
26 self.interactive = self.configbool("ui", "interactive", True)
26 self.interactive = self.configbool("ui", "interactive", True)
27 self.traceback = traceback
27 self.traceback = traceback
28
28
29 self.updateopts(verbose, debug, quiet, interactive)
29 self.updateopts(verbose, debug, quiet, interactive)
30 self.diffcache = None
30 self.diffcache = None
31 self.header = []
31 self.header = []
32 self.prev_header = []
32 self.prev_header = []
33 self.revlogopts = self.configrevlog()
33 self.revlogopts = self.configrevlog()
34 else:
34 else:
35 # parentui may point to an ui object which is already a child
35 # parentui may point to an ui object which is already a child
36 self.parentui = parentui.parentui or parentui
36 self.parentui = parentui.parentui or parentui
37 parent_cdata = self.parentui.cdata
37 parent_cdata = self.parentui.cdata
38 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
38 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
39 # make interpolation work
39 # make interpolation work
40 for section in parent_cdata.sections():
40 for section in parent_cdata.sections():
41 self.cdata.add_section(section)
41 self.cdata.add_section(section)
42 for name, value in parent_cdata.items(section, raw=True):
42 for name, value in parent_cdata.items(section, raw=True):
43 self.cdata.set(section, name, value)
43 self.cdata.set(section, name, value)
44
44
45 def __getattr__(self, key):
45 def __getattr__(self, key):
46 return getattr(self.parentui, key)
46 return getattr(self.parentui, key)
47
47
48 def updateopts(self, verbose=False, debug=False, quiet=False,
48 def updateopts(self, verbose=False, debug=False, quiet=False,
49 interactive=True, traceback=False, config=[]):
49 interactive=True, traceback=False, config=[]):
50 self.quiet = (self.quiet or quiet) and not verbose and not debug
50 self.quiet = (self.quiet or quiet) and not verbose and not debug
51 self.verbose = (self.verbose or verbose) or debug
51 self.verbose = (self.verbose or verbose) or debug
52 self.debugflag = (self.debugflag or debug)
52 self.debugflag = (self.debugflag or debug)
53 self.interactive = (self.interactive and interactive)
53 self.interactive = (self.interactive and interactive)
54 self.traceback = self.traceback or traceback
54 self.traceback = self.traceback or traceback
55 for cfg in config:
55 for cfg in config:
56 try:
56 try:
57 name, value = cfg.split('=', 1)
57 name, value = cfg.split('=', 1)
58 section, name = name.split('.', 1)
58 section, name = name.split('.', 1)
59 if not self.cdata.has_section(section):
59 if not self.cdata.has_section(section):
60 self.cdata.add_section(section)
60 self.cdata.add_section(section)
61 if not section or not name:
61 if not section or not name:
62 raise IndexError
62 raise IndexError
63 self.cdata.set(section, name, value)
63 self.cdata.set(section, name, value)
64 except (IndexError, ValueError):
64 except (IndexError, ValueError):
65 raise util.Abort(_('malformed --config option: %s') % cfg)
65 raise util.Abort(_('malformed --config option: %s') % cfg)
66
66
67 def readconfig(self, fn, root=None):
67 def readconfig(self, fn, root=None):
68 if isinstance(fn, basestring):
68 if isinstance(fn, basestring):
69 fn = [fn]
69 fn = [fn]
70 for f in fn:
70 for f in fn:
71 try:
71 try:
72 self.cdata.read(f)
72 self.cdata.read(f)
73 except ConfigParser.ParsingError, inst:
73 except ConfigParser.ParsingError, inst:
74 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
74 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
75 # translate paths relative to root (or home) into absolute paths
75 # translate paths relative to root (or home) into absolute paths
76 if root is None:
76 if root is None:
77 root = os.path.expanduser('~')
77 root = os.path.expanduser('~')
78 for name, path in self.configitems("paths"):
78 for name, path in self.configitems("paths"):
79 if path and "://" not in path and not os.path.isabs(path):
79 if path and "://" not in path and not os.path.isabs(path):
80 self.cdata.set("paths", name, os.path.join(root, path))
80 self.cdata.set("paths", name, os.path.join(root, path))
81
81
82 def setconfig(self, section, name, val):
82 def setconfig(self, section, name, val):
83 self.overlay[(section, name)] = val
83 self.overlay[(section, name)] = val
84
84
85 def config(self, section, name, default=None):
85 def config(self, section, name, default=None):
86 if self.overlay.has_key((section, name)):
86 if self.overlay.has_key((section, name)):
87 return self.overlay[(section, name)]
87 return self.overlay[(section, name)]
88 if self.cdata.has_option(section, name):
88 if self.cdata.has_option(section, name):
89 try:
89 try:
90 return self.cdata.get(section, name)
90 return self.cdata.get(section, name)
91 except ConfigParser.InterpolationError, inst:
91 except ConfigParser.InterpolationError, inst:
92 raise util.Abort(_("Error in configuration:\n%s") % inst)
92 raise util.Abort(_("Error in configuration:\n%s") % inst)
93 if self.parentui is None:
93 if self.parentui is None:
94 return default
94 return default
95 else:
95 else:
96 return self.parentui.config(section, name, default)
96 return self.parentui.config(section, name, default)
97
97
98 def configlist(self, section, name, default=None):
98 def configlist(self, section, name, default=None):
99 """Return a list of comma/space separated strings"""
99 """Return a list of comma/space separated strings"""
100 result = self.config(section, name)
100 result = self.config(section, name)
101 if result is None:
101 if result is None:
102 result = default or []
102 result = default or []
103 if isinstance(result, basestring):
103 if isinstance(result, basestring):
104 result = result.replace(",", " ").split()
104 result = result.replace(",", " ").split()
105 return result
105 return result
106
106
107 def configbool(self, section, name, default=False):
107 def configbool(self, section, name, default=False):
108 if self.overlay.has_key((section, name)):
108 if self.overlay.has_key((section, name)):
109 return self.overlay[(section, name)]
109 return self.overlay[(section, name)]
110 if self.cdata.has_option(section, name):
110 if self.cdata.has_option(section, name):
111 try:
111 try:
112 return self.cdata.getboolean(section, name)
112 return self.cdata.getboolean(section, name)
113 except ConfigParser.InterpolationError, inst:
113 except ConfigParser.InterpolationError, inst:
114 raise util.Abort(_("Error in configuration:\n%s") % inst)
114 raise util.Abort(_("Error in configuration:\n%s") % inst)
115 if self.parentui is None:
115 if self.parentui is None:
116 return default
116 return default
117 else:
117 else:
118 return self.parentui.configbool(section, name, default)
118 return self.parentui.configbool(section, name, default)
119
119
120 def has_config(self, section):
120 def has_config(self, section):
121 '''tell whether section exists in config.'''
121 '''tell whether section exists in config.'''
122 return self.cdata.has_section(section)
122 return self.cdata.has_section(section)
123
123
124 def configitems(self, section):
124 def configitems(self, section):
125 items = {}
125 items = {}
126 if self.parentui is not None:
126 if self.parentui is not None:
127 items = dict(self.parentui.configitems(section))
127 items = dict(self.parentui.configitems(section))
128 if self.cdata.has_section(section):
128 if self.cdata.has_section(section):
129 try:
129 try:
130 items.update(dict(self.cdata.items(section)))
130 items.update(dict(self.cdata.items(section)))
131 except ConfigParser.InterpolationError, inst:
131 except ConfigParser.InterpolationError, inst:
132 raise util.Abort(_("Error in configuration:\n%s") % inst)
132 raise util.Abort(_("Error in configuration:\n%s") % inst)
133 x = items.items()
133 x = items.items()
134 x.sort()
134 x.sort()
135 return x
135 return x
136
136
137 def walkconfig(self, seen=None):
137 def walkconfig(self, seen=None):
138 if seen is None:
138 if seen is None:
139 seen = {}
139 seen = {}
140 for (section, name), value in self.overlay.iteritems():
140 for (section, name), value in self.overlay.iteritems():
141 yield section, name, value
141 yield section, name, value
142 seen[section, name] = 1
142 seen[section, name] = 1
143 for section in self.cdata.sections():
143 for section in self.cdata.sections():
144 for name, value in self.cdata.items(section):
144 for name, value in self.cdata.items(section):
145 if (section, name) in seen: continue
145 if (section, name) in seen: continue
146 yield section, name, value.replace('\n', '\\n')
146 yield section, name, value.replace('\n', '\\n')
147 seen[section, name] = 1
147 seen[section, name] = 1
148 if self.parentui is not None:
148 if self.parentui is not None:
149 for parent in self.parentui.walkconfig(seen):
149 for parent in self.parentui.walkconfig(seen):
150 yield parent
150 yield parent
151
151
152 def extensions(self):
152 def extensions(self):
153 result = self.configitems("extensions")
153 result = self.configitems("extensions")
154 for i, (key, value) in enumerate(result):
154 for i, (key, value) in enumerate(result):
155 if value:
155 if value:
156 result[i] = (key, os.path.expanduser(value))
156 result[i] = (key, os.path.expanduser(value))
157 return result
157 return result
158
158
159 def hgignorefiles(self):
159 def hgignorefiles(self):
160 result = []
160 result = []
161 for key, value in self.configitems("ui"):
161 for key, value in self.configitems("ui"):
162 if key == 'ignore' or key.startswith('ignore.'):
162 if key == 'ignore' or key.startswith('ignore.'):
163 result.append(os.path.expanduser(value))
163 result.append(os.path.expanduser(value))
164 return result
164 return result
165
165
166 def configrevlog(self):
166 def configrevlog(self):
167 result = {}
167 result = {}
168 for key, value in self.configitems("revlog"):
168 for key, value in self.configitems("revlog"):
169 result[key.lower()] = value
169 result[key.lower()] = value
170 return result
170 return result
171
171
172 def diffopts(self):
172 def diffopts(self):
173 if self.diffcache:
173 if self.diffcache:
174 return self.diffcache
174 return self.diffcache
175 result = {'showfunc': True, 'ignorews': False,
175 result = {'showfunc': True, 'ignorews': False,
176 'ignorewsamount': False, 'ignoreblanklines': False}
176 'ignorewsamount': False, 'ignoreblanklines': False}
177 for key, value in self.configitems("diff"):
177 for key, value in self.configitems("diff"):
178 if value:
178 if value:
179 result[key.lower()] = (value.lower() == 'true')
179 result[key.lower()] = (value.lower() == 'true')
180 self.diffcache = result
180 self.diffcache = result
181 return result
181 return result
182
182
183 def username(self):
183 def username(self):
184 """Return default username to be used in commits.
184 """Return default username to be used in commits.
185
185
186 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
186 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
187 and stop searching if one of these is set.
187 and stop searching if one of these is set.
188 Abort if found username is an empty string to force specifying
188 Abort if found username is an empty string to force specifying
189 the commit user elsewhere, e.g. with line option or repo hgrc.
189 the commit user elsewhere, e.g. with line option or repo hgrc.
190 If not found, use ($LOGNAME or $USER or $LNAME or
190 If not found, use ($LOGNAME or $USER or $LNAME or
191 $USERNAME) +"@full.hostname".
191 $USERNAME) +"@full.hostname".
192 """
192 """
193 user = os.environ.get("HGUSER")
193 user = os.environ.get("HGUSER")
194 if user is None:
194 if user is None:
195 user = self.config("ui", "username")
195 user = self.config("ui", "username")
196 if user is None:
196 if user is None:
197 user = os.environ.get("EMAIL")
197 user = os.environ.get("EMAIL")
198 if user is None:
198 if user is None:
199 try:
199 try:
200 user = '%s@%s' % (util.getuser(), socket.getfqdn())
200 user = '%s@%s' % (util.getuser(), socket.getfqdn())
201 except KeyError:
201 except KeyError:
202 raise util.Abort(_("Please specify a username."))
202 raise util.Abort(_("Please specify a username."))
203 return user
203 return user
204
204
205 def shortuser(self, user):
205 def shortuser(self, user):
206 """Return a short representation of a user name or email address."""
206 """Return a short representation of a user name or email address."""
207 if not self.verbose: user = util.shortuser(user)
207 if not self.verbose: user = util.shortuser(user)
208 return user
208 return user
209
209
210 def expandpath(self, loc, default=None):
210 def expandpath(self, loc, default=None):
211 """Return repository location relative to cwd or from [paths]"""
211 """Return repository location relative to cwd or from [paths]"""
212 if "://" in loc or os.path.isdir(loc):
212 if "://" in loc or os.path.isdir(loc):
213 return loc
213 return loc
214
214
215 path = self.config("paths", loc)
215 path = self.config("paths", loc)
216 if not path and default is not None:
216 if not path and default is not None:
217 path = self.config("paths", default)
217 path = self.config("paths", default)
218 return path or loc
218 return path or loc
219
219
220 def setconfig_remoteopts(self, **opts):
221 if opts.get('ssh'):
222 self.setconfig("ui", "ssh", opts['ssh'])
223 if opts.get('remotecmd'):
224 self.setconfig("ui", "remotecmd", opts['remotecmd'])
225
226 def write(self, *args):
220 def write(self, *args):
227 if self.header:
221 if self.header:
228 if self.header != self.prev_header:
222 if self.header != self.prev_header:
229 self.prev_header = self.header
223 self.prev_header = self.header
230 self.write(*self.header)
224 self.write(*self.header)
231 self.header = []
225 self.header = []
232 for a in args:
226 for a in args:
233 sys.stdout.write(str(a))
227 sys.stdout.write(str(a))
234
228
235 def write_header(self, *args):
229 def write_header(self, *args):
236 for a in args:
230 for a in args:
237 self.header.append(str(a))
231 self.header.append(str(a))
238
232
239 def write_err(self, *args):
233 def write_err(self, *args):
240 try:
234 try:
241 if not sys.stdout.closed: sys.stdout.flush()
235 if not sys.stdout.closed: sys.stdout.flush()
242 for a in args:
236 for a in args:
243 sys.stderr.write(str(a))
237 sys.stderr.write(str(a))
244 except IOError, inst:
238 except IOError, inst:
245 if inst.errno != errno.EPIPE:
239 if inst.errno != errno.EPIPE:
246 raise
240 raise
247
241
248 def flush(self):
242 def flush(self):
249 try: sys.stdout.flush()
243 try: sys.stdout.flush()
250 except: pass
244 except: pass
251 try: sys.stderr.flush()
245 try: sys.stderr.flush()
252 except: pass
246 except: pass
253
247
254 def readline(self):
248 def readline(self):
255 return sys.stdin.readline()[:-1]
249 return sys.stdin.readline()[:-1]
256 def prompt(self, msg, pat=None, default="y"):
250 def prompt(self, msg, pat=None, default="y"):
257 if not self.interactive: return default
251 if not self.interactive: return default
258 while 1:
252 while 1:
259 self.write(msg, " ")
253 self.write(msg, " ")
260 r = self.readline()
254 r = self.readline()
261 if not pat or re.match(pat, r):
255 if not pat or re.match(pat, r):
262 return r
256 return r
263 else:
257 else:
264 self.write(_("unrecognized response\n"))
258 self.write(_("unrecognized response\n"))
265 def getpass(self, prompt=None, default=None):
259 def getpass(self, prompt=None, default=None):
266 if not self.interactive: return default
260 if not self.interactive: return default
267 return getpass.getpass(prompt or _('password: '))
261 return getpass.getpass(prompt or _('password: '))
268 def status(self, *msg):
262 def status(self, *msg):
269 if not self.quiet: self.write(*msg)
263 if not self.quiet: self.write(*msg)
270 def warn(self, *msg):
264 def warn(self, *msg):
271 self.write_err(*msg)
265 self.write_err(*msg)
272 def note(self, *msg):
266 def note(self, *msg):
273 if self.verbose: self.write(*msg)
267 if self.verbose: self.write(*msg)
274 def debug(self, *msg):
268 def debug(self, *msg):
275 if self.debugflag: self.write(*msg)
269 if self.debugflag: self.write(*msg)
276 def edit(self, text, user):
270 def edit(self, text, user):
277 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
271 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
278 text=True)
272 text=True)
279 try:
273 try:
280 f = os.fdopen(fd, "w")
274 f = os.fdopen(fd, "w")
281 f.write(text)
275 f.write(text)
282 f.close()
276 f.close()
283
277
284 editor = (os.environ.get("HGEDITOR") or
278 editor = (os.environ.get("HGEDITOR") or
285 self.config("ui", "editor") or
279 self.config("ui", "editor") or
286 os.environ.get("EDITOR", "vi"))
280 os.environ.get("EDITOR", "vi"))
287
281
288 util.system("%s \"%s\"" % (editor, name),
282 util.system("%s \"%s\"" % (editor, name),
289 environ={'HGUSER': user},
283 environ={'HGUSER': user},
290 onerr=util.Abort, errprefix=_("edit failed"))
284 onerr=util.Abort, errprefix=_("edit failed"))
291
285
292 f = open(name)
286 f = open(name)
293 t = f.read()
287 t = f.read()
294 f.close()
288 f.close()
295 t = re.sub("(?m)^HG:.*\n", "", t)
289 t = re.sub("(?m)^HG:.*\n", "", t)
296 finally:
290 finally:
297 os.unlink(name)
291 os.unlink(name)
298
292
299 return t
293 return t
300
294
301 def sendmail(self):
295 def sendmail(self):
302 '''send mail message. object returned has one method, sendmail.
296 '''send mail message. object returned has one method, sendmail.
303 call as sendmail(sender, list-of-recipients, msg).'''
297 call as sendmail(sender, list-of-recipients, msg).'''
304
298
305 def smtp():
299 def smtp():
306 '''send mail using smtp.'''
300 '''send mail using smtp.'''
307
301
308 local_hostname = self.config('smtp', 'local_hostname')
302 local_hostname = self.config('smtp', 'local_hostname')
309 s = smtplib.SMTP(local_hostname=local_hostname)
303 s = smtplib.SMTP(local_hostname=local_hostname)
310 mailhost = self.config('smtp', 'host')
304 mailhost = self.config('smtp', 'host')
311 if not mailhost:
305 if not mailhost:
312 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
306 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
313 mailport = int(self.config('smtp', 'port', 25))
307 mailport = int(self.config('smtp', 'port', 25))
314 self.note(_('sending mail: smtp host %s, port %s\n') %
308 self.note(_('sending mail: smtp host %s, port %s\n') %
315 (mailhost, mailport))
309 (mailhost, mailport))
316 s.connect(host=mailhost, port=mailport)
310 s.connect(host=mailhost, port=mailport)
317 if self.configbool('smtp', 'tls'):
311 if self.configbool('smtp', 'tls'):
318 self.note(_('(using tls)\n'))
312 self.note(_('(using tls)\n'))
319 s.ehlo()
313 s.ehlo()
320 s.starttls()
314 s.starttls()
321 s.ehlo()
315 s.ehlo()
322 username = self.config('smtp', 'username')
316 username = self.config('smtp', 'username')
323 password = self.config('smtp', 'password')
317 password = self.config('smtp', 'password')
324 if username and password:
318 if username and password:
325 self.note(_('(authenticating to mail server as %s)\n') %
319 self.note(_('(authenticating to mail server as %s)\n') %
326 (username))
320 (username))
327 s.login(username, password)
321 s.login(username, password)
328 return s
322 return s
329
323
330 class sendmail(object):
324 class sendmail(object):
331 '''send mail using sendmail.'''
325 '''send mail using sendmail.'''
332
326
333 def __init__(self, ui, program):
327 def __init__(self, ui, program):
334 self.ui = ui
328 self.ui = ui
335 self.program = program
329 self.program = program
336
330
337 def sendmail(self, sender, recipients, msg):
331 def sendmail(self, sender, recipients, msg):
338 cmdline = '%s -f %s %s' % (
332 cmdline = '%s -f %s %s' % (
339 self.program, templater.email(sender),
333 self.program, templater.email(sender),
340 ' '.join(map(templater.email, recipients)))
334 ' '.join(map(templater.email, recipients)))
341 self.ui.note(_('sending mail: %s\n') % cmdline)
335 self.ui.note(_('sending mail: %s\n') % cmdline)
342 fp = os.popen(cmdline, 'w')
336 fp = os.popen(cmdline, 'w')
343 fp.write(msg)
337 fp.write(msg)
344 ret = fp.close()
338 ret = fp.close()
345 if ret:
339 if ret:
346 raise util.Abort('%s %s' % (
340 raise util.Abort('%s %s' % (
347 os.path.basename(self.program.split(None, 1)[0]),
341 os.path.basename(self.program.split(None, 1)[0]),
348 util.explain_exit(ret)[0]))
342 util.explain_exit(ret)[0]))
349
343
350 method = self.config('email', 'method', 'smtp')
344 method = self.config('email', 'method', 'smtp')
351 if method == 'smtp':
345 if method == 'smtp':
352 mail = smtp()
346 mail = smtp()
353 else:
347 else:
354 mail = sendmail(self, method)
348 mail = sendmail(self, method)
355 return mail
349 return mail
356
350
357 def print_exc(self):
351 def print_exc(self):
358 '''print exception traceback if traceback printing enabled.
352 '''print exception traceback if traceback printing enabled.
359 only to call in exception handler. returns true if traceback
353 only to call in exception handler. returns true if traceback
360 printed.'''
354 printed.'''
361 if self.traceback:
355 if self.traceback:
362 traceback.print_exc()
356 traceback.print_exc()
363 return self.traceback
357 return self.traceback
General Comments 0
You need to be logged in to leave comments. Login now