##// END OF EJS Templates
mq: allow to apply patches in subdir of repo again...
Vadim Gelfer -
r2728:5d134f04 default
parent child Browse files
Show More
@@ -1,1496 +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 commands.addremove_lock(self.ui, repo, files,
347 cwd = repo.getcwd()
348 cfiles = files
349 if cwd:
350 cfiles = [util.pathto(cwd, f) for f in files]
351 commands.addremove_lock(self.ui, repo, cfiles,
348 opts={}, wlock=wlock)
352 opts={}, wlock=wlock)
349 n = repo.commit(files, message, user, date, force=1, lock=lock,
353 n = repo.commit(files, message, user, date, force=1, lock=lock,
350 wlock=wlock)
354 wlock=wlock)
351
355
352 if n == None:
356 if n == None:
353 raise util.Abort(_("repo commit failed"))
357 raise util.Abort(_("repo commit failed"))
354
358
355 if update_status:
359 if update_status:
356 self.applied.append(revlog.hex(n) + ":" + patch)
360 self.applied.append(revlog.hex(n) + ":" + patch)
357
361
358 if patcherr:
362 if patcherr:
359 if not patchfound:
363 if not patchfound:
360 self.ui.warn("patch %s is empty\n" % patch)
364 self.ui.warn("patch %s is empty\n" % patch)
361 err = 0
365 err = 0
362 else:
366 else:
363 self.ui.warn("patch failed, rejects left in working dir\n")
367 self.ui.warn("patch failed, rejects left in working dir\n")
364 err = 1
368 err = 1
365 break
369 break
366
370
367 if fuzz and strict:
371 if fuzz and strict:
368 self.ui.warn("fuzz found when applying patch, stopping\n")
372 self.ui.warn("fuzz found when applying patch, stopping\n")
369 err = 1
373 err = 1
370 break
374 break
371 tr.close()
375 tr.close()
372 return (err, n)
376 return (err, n)
373
377
374 def delete(self, repo, patch):
378 def delete(self, repo, patch):
375 patch = self.lookup(patch, strict=True)
379 patch = self.lookup(patch, strict=True)
376 info = self.isapplied(patch)
380 info = self.isapplied(patch)
377 if info:
381 if info:
378 raise util.Abort(_("cannot delete applied patch %s") % patch)
382 raise util.Abort(_("cannot delete applied patch %s") % patch)
379 if patch not in self.series:
383 if patch not in self.series:
380 raise util.Abort(_("patch %s not in series file") % patch)
384 raise util.Abort(_("patch %s not in series file") % patch)
381 i = self.find_series(patch)
385 i = self.find_series(patch)
382 del self.full_series[i]
386 del self.full_series[i]
383 self.read_series(self.full_series)
387 self.read_series(self.full_series)
384 self.series_dirty = 1
388 self.series_dirty = 1
385
389
386 def check_toppatch(self, repo):
390 def check_toppatch(self, repo):
387 if len(self.applied) > 0:
391 if len(self.applied) > 0:
388 (top, patch) = self.applied[-1].split(':')
392 (top, patch) = self.applied[-1].split(':')
389 top = revlog.bin(top)
393 top = revlog.bin(top)
390 pp = repo.dirstate.parents()
394 pp = repo.dirstate.parents()
391 if top not in pp:
395 if top not in pp:
392 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"))
393 return top
397 return top
394 return None
398 return None
395 def check_localchanges(self, repo):
399 def check_localchanges(self, repo):
396 (c, a, r, d, u) = repo.changes(None, None)
400 (c, a, r, d, u) = repo.changes(None, None)
397 if c or a or d or r:
401 if c or a or d or r:
398 raise util.Abort(_("local changes found, refresh first"))
402 raise util.Abort(_("local changes found, refresh first"))
399 def new(self, repo, patch, msg=None, force=None):
403 def new(self, repo, patch, msg=None, force=None):
400 if os.path.exists(os.path.join(self.path, patch)):
404 if os.path.exists(os.path.join(self.path, patch)):
401 raise util.Abort(_('patch "%s" already exists') % patch)
405 raise util.Abort(_('patch "%s" already exists') % patch)
402 commitfiles = []
406 commitfiles = []
403 (c, a, r, d, u) = repo.changes(None, None)
407 (c, a, r, d, u) = repo.changes(None, None)
404 if c or a or d or r:
408 if c or a or d or r:
405 if not force:
409 if not force:
406 raise util.Abort(_("local changes found, refresh first"))
410 raise util.Abort(_("local changes found, refresh first"))
407 commitfiles = c + a + r
411 commitfiles = c + a + r
408 self.check_toppatch(repo)
412 self.check_toppatch(repo)
409 wlock = repo.wlock()
413 wlock = repo.wlock()
410 insert = self.full_series_end()
414 insert = self.full_series_end()
411 if msg:
415 if msg:
412 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
416 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
413 wlock=wlock)
417 wlock=wlock)
414 else:
418 else:
415 n = repo.commit(commitfiles,
419 n = repo.commit(commitfiles,
416 "New patch: %s" % patch, force=True, wlock=wlock)
420 "New patch: %s" % patch, force=True, wlock=wlock)
417 if n == None:
421 if n == None:
418 raise util.Abort(_("repo commit failed"))
422 raise util.Abort(_("repo commit failed"))
419 self.full_series[insert:insert] = [patch]
423 self.full_series[insert:insert] = [patch]
420 self.applied.append(revlog.hex(n) + ":" + patch)
424 self.applied.append(revlog.hex(n) + ":" + patch)
421 self.read_series(self.full_series)
425 self.read_series(self.full_series)
422 self.series_dirty = 1
426 self.series_dirty = 1
423 self.applied_dirty = 1
427 self.applied_dirty = 1
424 p = self.opener(patch, "w")
428 p = self.opener(patch, "w")
425 if msg:
429 if msg:
426 msg = msg + "\n"
430 msg = msg + "\n"
427 p.write(msg)
431 p.write(msg)
428 p.close()
432 p.close()
429 wlock = None
433 wlock = None
430 r = self.qrepo()
434 r = self.qrepo()
431 if r: r.add([patch])
435 if r: r.add([patch])
432 if commitfiles:
436 if commitfiles:
433 self.refresh(repo, msg=None, short=True)
437 self.refresh(repo, msg=None, short=True)
434
438
435 def strip(self, repo, rev, update=True, backup="all", wlock=None):
439 def strip(self, repo, rev, update=True, backup="all", wlock=None):
436 def limitheads(chlog, stop):
440 def limitheads(chlog, stop):
437 """return the list of all nodes that have no children"""
441 """return the list of all nodes that have no children"""
438 p = {}
442 p = {}
439 h = []
443 h = []
440 stoprev = 0
444 stoprev = 0
441 if stop in chlog.nodemap:
445 if stop in chlog.nodemap:
442 stoprev = chlog.rev(stop)
446 stoprev = chlog.rev(stop)
443
447
444 for r in range(chlog.count() - 1, -1, -1):
448 for r in range(chlog.count() - 1, -1, -1):
445 n = chlog.node(r)
449 n = chlog.node(r)
446 if n not in p:
450 if n not in p:
447 h.append(n)
451 h.append(n)
448 if n == stop:
452 if n == stop:
449 break
453 break
450 if r < stoprev:
454 if r < stoprev:
451 break
455 break
452 for pn in chlog.parents(n):
456 for pn in chlog.parents(n):
453 p[pn] = 1
457 p[pn] = 1
454 return h
458 return h
455
459
456 def bundle(cg):
460 def bundle(cg):
457 backupdir = repo.join("strip-backup")
461 backupdir = repo.join("strip-backup")
458 if not os.path.isdir(backupdir):
462 if not os.path.isdir(backupdir):
459 os.mkdir(backupdir)
463 os.mkdir(backupdir)
460 name = os.path.join(backupdir, "%s" % revlog.short(rev))
464 name = os.path.join(backupdir, "%s" % revlog.short(rev))
461 name = savename(name)
465 name = savename(name)
462 self.ui.warn("saving bundle to %s\n" % name)
466 self.ui.warn("saving bundle to %s\n" % name)
463 # TODO, exclusive open
467 # TODO, exclusive open
464 f = open(name, "wb")
468 f = open(name, "wb")
465 try:
469 try:
466 f.write("HG10")
470 f.write("HG10")
467 z = bz2.BZ2Compressor(9)
471 z = bz2.BZ2Compressor(9)
468 while 1:
472 while 1:
469 chunk = cg.read(4096)
473 chunk = cg.read(4096)
470 if not chunk:
474 if not chunk:
471 break
475 break
472 f.write(z.compress(chunk))
476 f.write(z.compress(chunk))
473 f.write(z.flush())
477 f.write(z.flush())
474 except:
478 except:
475 os.unlink(name)
479 os.unlink(name)
476 raise
480 raise
477 f.close()
481 f.close()
478 return name
482 return name
479
483
480 def stripall(rev, revnum):
484 def stripall(rev, revnum):
481 cl = repo.changelog
485 cl = repo.changelog
482 c = cl.read(rev)
486 c = cl.read(rev)
483 mm = repo.manifest.read(c[0])
487 mm = repo.manifest.read(c[0])
484 seen = {}
488 seen = {}
485
489
486 for x in xrange(revnum, cl.count()):
490 for x in xrange(revnum, cl.count()):
487 c = cl.read(cl.node(x))
491 c = cl.read(cl.node(x))
488 for f in c[3]:
492 for f in c[3]:
489 if f in seen:
493 if f in seen:
490 continue
494 continue
491 seen[f] = 1
495 seen[f] = 1
492 if f in mm:
496 if f in mm:
493 filerev = mm[f]
497 filerev = mm[f]
494 else:
498 else:
495 filerev = 0
499 filerev = 0
496 seen[f] = filerev
500 seen[f] = filerev
497 # 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
498 # sensible order. When stripping many files, this helps keep
502 # sensible order. When stripping many files, this helps keep
499 # our disk access patterns under control.
503 # our disk access patterns under control.
500 list = seen.keys()
504 list = seen.keys()
501 list.sort()
505 list.sort()
502 for f in list:
506 for f in list:
503 ff = repo.file(f)
507 ff = repo.file(f)
504 filerev = seen[f]
508 filerev = seen[f]
505 if filerev != 0:
509 if filerev != 0:
506 if filerev in ff.nodemap:
510 if filerev in ff.nodemap:
507 filerev = ff.rev(filerev)
511 filerev = ff.rev(filerev)
508 else:
512 else:
509 filerev = 0
513 filerev = 0
510 ff.strip(filerev, revnum)
514 ff.strip(filerev, revnum)
511
515
512 if not wlock:
516 if not wlock:
513 wlock = repo.wlock()
517 wlock = repo.wlock()
514 lock = repo.lock()
518 lock = repo.lock()
515 chlog = repo.changelog
519 chlog = repo.changelog
516 # TODO delete the undo files, and handle undo of merge sets
520 # TODO delete the undo files, and handle undo of merge sets
517 pp = chlog.parents(rev)
521 pp = chlog.parents(rev)
518 revnum = chlog.rev(rev)
522 revnum = chlog.rev(rev)
519
523
520 if update:
524 if update:
521 (c, a, r, d, u) = repo.changes(None, None)
525 (c, a, r, d, u) = repo.changes(None, None)
522 if c or a or d or r:
526 if c or a or d or r:
523 raise util.Abort(_("local changes found"))
527 raise util.Abort(_("local changes found"))
524 urev = self.qparents(repo, rev)
528 urev = self.qparents(repo, rev)
525 repo.update(urev, allow=False, force=True, wlock=wlock)
529 repo.update(urev, allow=False, force=True, wlock=wlock)
526 repo.dirstate.write()
530 repo.dirstate.write()
527
531
528 # 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
529 # that we actually want to keep. changegroup will be used
533 # that we actually want to keep. changegroup will be used
530 # to preserve them and add them back after the truncate
534 # to preserve them and add them back after the truncate
531 saveheads = []
535 saveheads = []
532 savebases = {}
536 savebases = {}
533
537
534 tip = chlog.tip()
538 tip = chlog.tip()
535 heads = limitheads(chlog, rev)
539 heads = limitheads(chlog, rev)
536 seen = {}
540 seen = {}
537
541
538 # search through all the heads, finding those where the revision
542 # search through all the heads, finding those where the revision
539 # 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
540 # that might be turned into new heads by the strip.
544 # that might be turned into new heads by the strip.
541 while heads:
545 while heads:
542 h = heads.pop()
546 h = heads.pop()
543 n = h
547 n = h
544 while True:
548 while True:
545 seen[n] = 1
549 seen[n] = 1
546 pp = chlog.parents(n)
550 pp = chlog.parents(n)
547 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
551 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
548 if pp[1] not in seen:
552 if pp[1] not in seen:
549 heads.append(pp[1])
553 heads.append(pp[1])
550 if pp[0] == revlog.nullid:
554 if pp[0] == revlog.nullid:
551 break
555 break
552 if chlog.rev(pp[0]) < revnum:
556 if chlog.rev(pp[0]) < revnum:
553 break
557 break
554 n = pp[0]
558 n = pp[0]
555 if n == rev:
559 if n == rev:
556 break
560 break
557 r = chlog.reachable(h, rev)
561 r = chlog.reachable(h, rev)
558 if rev not in r:
562 if rev not in r:
559 saveheads.append(h)
563 saveheads.append(h)
560 for x in r:
564 for x in r:
561 if chlog.rev(x) > revnum:
565 if chlog.rev(x) > revnum:
562 savebases[x] = 1
566 savebases[x] = 1
563
567
564 # create a changegroup for all the branches we need to keep
568 # create a changegroup for all the branches we need to keep
565 if backup is "all":
569 if backup is "all":
566 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
570 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
567 bundle(backupch)
571 bundle(backupch)
568 if saveheads:
572 if saveheads:
569 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
573 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
570 chgrpfile = bundle(backupch)
574 chgrpfile = bundle(backupch)
571
575
572 stripall(rev, revnum)
576 stripall(rev, revnum)
573
577
574 change = chlog.read(rev)
578 change = chlog.read(rev)
575 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
579 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
576 chlog.strip(revnum, revnum)
580 chlog.strip(revnum, revnum)
577 if saveheads:
581 if saveheads:
578 self.ui.status("adding branch\n")
582 self.ui.status("adding branch\n")
579 commands.unbundle(self.ui, repo, chgrpfile, update=False)
583 commands.unbundle(self.ui, repo, chgrpfile, update=False)
580 if backup is not "strip":
584 if backup is not "strip":
581 os.unlink(chgrpfile)
585 os.unlink(chgrpfile)
582
586
583 def isapplied(self, patch):
587 def isapplied(self, patch):
584 """returns (index, rev, patch)"""
588 """returns (index, rev, patch)"""
585 for i in xrange(len(self.applied)):
589 for i in xrange(len(self.applied)):
586 p = self.applied[i]
590 p = self.applied[i]
587 a = p.split(':')
591 a = p.split(':')
588 if a[1] == patch:
592 if a[1] == patch:
589 return (i, a[0], a[1])
593 return (i, a[0], a[1])
590 return None
594 return None
591
595
592 # 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
593 # variations. If strict is passed, we try only #1
597 # variations. If strict is passed, we try only #1
594 #
598 #
595 # 1) a number to indicate an offset in the series file
599 # 1) a number to indicate an offset in the series file
596 # 2) a unique substring of the patch name was given
600 # 2) a unique substring of the patch name was given
597 # 3) patchname[-+]num to indicate an offset in the series file
601 # 3) patchname[-+]num to indicate an offset in the series file
598 def lookup(self, patch, strict=False):
602 def lookup(self, patch, strict=False):
599 def partial_name(s):
603 def partial_name(s):
600 count = 0
604 count = 0
601 if s in self.series:
605 if s in self.series:
602 return s
606 return s
603 for x in self.series:
607 for x in self.series:
604 if s in x:
608 if s in x:
605 count += 1
609 count += 1
606 last = x
610 last = x
607 if count > 1:
611 if count > 1:
608 return None
612 return None
609 if count:
613 if count:
610 return last
614 return last
611 if len(self.series) > 0 and len(self.applied) > 0:
615 if len(self.series) > 0 and len(self.applied) > 0:
612 if s == 'qtip':
616 if s == 'qtip':
613 return self.series[self.series_end()-1]
617 return self.series[self.series_end()-1]
614 if s == 'qbase':
618 if s == 'qbase':
615 return self.series[0]
619 return self.series[0]
616 return None
620 return None
617 if patch == None:
621 if patch == None:
618 return None
622 return None
619
623
620 # 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
621 # sure the file name passed in does not exist (checked below)
625 # sure the file name passed in does not exist (checked below)
622 res = partial_name(patch)
626 res = partial_name(patch)
623 if res and res == patch:
627 if res and res == patch:
624 return res
628 return res
625
629
626 if not os.path.isfile(os.path.join(self.path, patch)):
630 if not os.path.isfile(os.path.join(self.path, patch)):
627 try:
631 try:
628 sno = int(patch)
632 sno = int(patch)
629 except(ValueError, OverflowError):
633 except(ValueError, OverflowError):
630 pass
634 pass
631 else:
635 else:
632 if sno < len(self.series):
636 if sno < len(self.series):
633 patch = self.series[sno]
637 patch = self.series[sno]
634 return patch
638 return patch
635 if not strict:
639 if not strict:
636 # return any partial match made above
640 # return any partial match made above
637 if res:
641 if res:
638 return res
642 return res
639 minus = patch.rsplit('-', 1)
643 minus = patch.rsplit('-', 1)
640 if len(minus) > 1:
644 if len(minus) > 1:
641 res = partial_name(minus[0])
645 res = partial_name(minus[0])
642 if res:
646 if res:
643 i = self.series.index(res)
647 i = self.series.index(res)
644 try:
648 try:
645 off = int(minus[1] or 1)
649 off = int(minus[1] or 1)
646 except(ValueError, OverflowError):
650 except(ValueError, OverflowError):
647 pass
651 pass
648 else:
652 else:
649 if i - off >= 0:
653 if i - off >= 0:
650 return self.series[i - off]
654 return self.series[i - off]
651 plus = patch.rsplit('+', 1)
655 plus = patch.rsplit('+', 1)
652 if len(plus) > 1:
656 if len(plus) > 1:
653 res = partial_name(plus[0])
657 res = partial_name(plus[0])
654 if res:
658 if res:
655 i = self.series.index(res)
659 i = self.series.index(res)
656 try:
660 try:
657 off = int(plus[1] or 1)
661 off = int(plus[1] or 1)
658 except(ValueError, OverflowError):
662 except(ValueError, OverflowError):
659 pass
663 pass
660 else:
664 else:
661 if i + off < len(self.series):
665 if i + off < len(self.series):
662 return self.series[i + off]
666 return self.series[i + off]
663 raise util.Abort(_("patch %s not in series") % patch)
667 raise util.Abort(_("patch %s not in series") % patch)
664
668
665 def push(self, repo, patch=None, force=False, list=False,
669 def push(self, repo, patch=None, force=False, list=False,
666 mergeq=None, wlock=None):
670 mergeq=None, wlock=None):
667 if not wlock:
671 if not wlock:
668 wlock = repo.wlock()
672 wlock = repo.wlock()
669 patch = self.lookup(patch)
673 patch = self.lookup(patch)
670 if patch and self.isapplied(patch):
674 if patch and self.isapplied(patch):
671 self.ui.warn(_("patch %s is already applied\n") % patch)
675 self.ui.warn(_("patch %s is already applied\n") % patch)
672 sys.exit(1)
676 sys.exit(1)
673 if self.series_end() == len(self.series):
677 if self.series_end() == len(self.series):
674 self.ui.warn(_("patch series fully applied\n"))
678 self.ui.warn(_("patch series fully applied\n"))
675 sys.exit(1)
679 sys.exit(1)
676 if not force:
680 if not force:
677 self.check_localchanges(repo)
681 self.check_localchanges(repo)
678
682
679 self.applied_dirty = 1;
683 self.applied_dirty = 1;
680 start = self.series_end()
684 start = self.series_end()
681 if start > 0:
685 if start > 0:
682 self.check_toppatch(repo)
686 self.check_toppatch(repo)
683 if not patch:
687 if not patch:
684 patch = self.series[start]
688 patch = self.series[start]
685 end = start + 1
689 end = start + 1
686 else:
690 else:
687 end = self.series.index(patch, start) + 1
691 end = self.series.index(patch, start) + 1
688 s = self.series[start:end]
692 s = self.series[start:end]
689 if mergeq:
693 if mergeq:
690 ret = self.mergepatch(repo, mergeq, s, wlock)
694 ret = self.mergepatch(repo, mergeq, s, wlock)
691 else:
695 else:
692 ret = self.apply(repo, s, list, wlock=wlock)
696 ret = self.apply(repo, s, list, wlock=wlock)
693 top = self.applied[-1].split(':')[1]
697 top = self.applied[-1].split(':')[1]
694 if ret[0]:
698 if ret[0]:
695 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" %
696 top)
700 top)
697 else:
701 else:
698 self.ui.write("Now at: %s\n" % top)
702 self.ui.write("Now at: %s\n" % top)
699 return ret[0]
703 return ret[0]
700
704
701 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,
702 wlock=None):
706 wlock=None):
703 def getfile(f, rev):
707 def getfile(f, rev):
704 t = repo.file(f).read(rev)
708 t = repo.file(f).read(rev)
705 try:
709 try:
706 repo.wfile(f, "w").write(t)
710 repo.wfile(f, "w").write(t)
707 except IOError:
711 except IOError:
708 try:
712 try:
709 os.makedirs(os.path.dirname(repo.wjoin(f)))
713 os.makedirs(os.path.dirname(repo.wjoin(f)))
710 except OSError, err:
714 except OSError, err:
711 if err.errno != errno.EEXIST: raise
715 if err.errno != errno.EEXIST: raise
712 repo.wfile(f, "w").write(t)
716 repo.wfile(f, "w").write(t)
713
717
714 if not wlock:
718 if not wlock:
715 wlock = repo.wlock()
719 wlock = repo.wlock()
716 if patch:
720 if patch:
717 # index, rev, patch
721 # index, rev, patch
718 info = self.isapplied(patch)
722 info = self.isapplied(patch)
719 if not info:
723 if not info:
720 patch = self.lookup(patch)
724 patch = self.lookup(patch)
721 info = self.isapplied(patch)
725 info = self.isapplied(patch)
722 if not info:
726 if not info:
723 raise util.Abort(_("patch %s is not applied") % patch)
727 raise util.Abort(_("patch %s is not applied") % patch)
724 if len(self.applied) == 0:
728 if len(self.applied) == 0:
725 self.ui.warn(_("no patches applied\n"))
729 self.ui.warn(_("no patches applied\n"))
726 sys.exit(1)
730 sys.exit(1)
727
731
728 if not update:
732 if not update:
729 parents = repo.dirstate.parents()
733 parents = repo.dirstate.parents()
730 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
734 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
731 for p in parents:
735 for p in parents:
732 if p in rr:
736 if p in rr:
733 self.ui.warn("qpop: forcing dirstate update\n")
737 self.ui.warn("qpop: forcing dirstate update\n")
734 update = True
738 update = True
735
739
736 if not force and update:
740 if not force and update:
737 self.check_localchanges(repo)
741 self.check_localchanges(repo)
738
742
739 self.applied_dirty = 1;
743 self.applied_dirty = 1;
740 end = len(self.applied)
744 end = len(self.applied)
741 if not patch:
745 if not patch:
742 if all:
746 if all:
743 popi = 0
747 popi = 0
744 else:
748 else:
745 popi = len(self.applied) - 1
749 popi = len(self.applied) - 1
746 else:
750 else:
747 popi = info[0] + 1
751 popi = info[0] + 1
748 if popi >= end:
752 if popi >= end:
749 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)
750 return
754 return
751 info = [ popi ] + self.applied[popi].split(':')
755 info = [ popi ] + self.applied[popi].split(':')
752
756
753 start = info[0]
757 start = info[0]
754 rev = revlog.bin(info[1])
758 rev = revlog.bin(info[1])
755
759
756 # 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
757 # form of hg.update.
761 # form of hg.update.
758 if update:
762 if update:
759 top = self.check_toppatch(repo)
763 top = self.check_toppatch(repo)
760 qp = self.qparents(repo, rev)
764 qp = self.qparents(repo, rev)
761 changes = repo.changelog.read(qp)
765 changes = repo.changelog.read(qp)
762 mf1 = repo.manifest.readflags(changes[0])
766 mf1 = repo.manifest.readflags(changes[0])
763 mmap = repo.manifest.read(changes[0])
767 mmap = repo.manifest.read(changes[0])
764 (c, a, r, d, u) = repo.changes(qp, top)
768 (c, a, r, d, u) = repo.changes(qp, top)
765 if d:
769 if d:
766 raise util.Abort("deletions found between repo revs")
770 raise util.Abort("deletions found between repo revs")
767 for f in c:
771 for f in c:
768 getfile(f, mmap[f])
772 getfile(f, mmap[f])
769 for f in r:
773 for f in r:
770 getfile(f, mmap[f])
774 getfile(f, mmap[f])
771 util.set_exec(repo.wjoin(f), mf1[f])
775 util.set_exec(repo.wjoin(f), mf1[f])
772 repo.dirstate.update(c + r, 'n')
776 repo.dirstate.update(c + r, 'n')
773 for f in a:
777 for f in a:
774 try: os.unlink(repo.wjoin(f))
778 try: os.unlink(repo.wjoin(f))
775 except: raise
779 except: raise
776 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
780 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
777 except: pass
781 except: pass
778 if a:
782 if a:
779 repo.dirstate.forget(a)
783 repo.dirstate.forget(a)
780 repo.dirstate.setparents(qp, revlog.nullid)
784 repo.dirstate.setparents(qp, revlog.nullid)
781 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
785 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
782 del self.applied[start:end]
786 del self.applied[start:end]
783 if len(self.applied):
787 if len(self.applied):
784 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])
785 else:
789 else:
786 self.ui.write("Patch queue now empty\n")
790 self.ui.write("Patch queue now empty\n")
787
791
788 def diff(self, repo, files):
792 def diff(self, repo, files):
789 top = self.check_toppatch(repo)
793 top = self.check_toppatch(repo)
790 if not top:
794 if not top:
791 self.ui.write("No patches applied\n")
795 self.ui.write("No patches applied\n")
792 return
796 return
793 qp = self.qparents(repo, top)
797 qp = self.qparents(repo, top)
794 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
798 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
795
799
796 def refresh(self, repo, msg=None, short=False):
800 def refresh(self, repo, msg=None, short=False):
797 if len(self.applied) == 0:
801 if len(self.applied) == 0:
798 self.ui.write("No patches applied\n")
802 self.ui.write("No patches applied\n")
799 return
803 return
800 wlock = repo.wlock()
804 wlock = repo.wlock()
801 self.check_toppatch(repo)
805 self.check_toppatch(repo)
802 qp = self.qparents(repo)
806 qp = self.qparents(repo)
803 (top, patch) = self.applied[-1].split(':')
807 (top, patch) = self.applied[-1].split(':')
804 top = revlog.bin(top)
808 top = revlog.bin(top)
805 cparents = repo.changelog.parents(top)
809 cparents = repo.changelog.parents(top)
806 patchparent = self.qparents(repo, top)
810 patchparent = self.qparents(repo, top)
807 message, comments, user, date, patchfound = self.readheaders(patch)
811 message, comments, user, date, patchfound = self.readheaders(patch)
808
812
809 patchf = self.opener(patch, "w")
813 patchf = self.opener(patch, "w")
810 if comments:
814 if comments:
811 comments = "\n".join(comments) + '\n\n'
815 comments = "\n".join(comments) + '\n\n'
812 patchf.write(comments)
816 patchf.write(comments)
813
817
814 tip = repo.changelog.tip()
818 tip = repo.changelog.tip()
815 if top == tip:
819 if top == tip:
816 # 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
817 # optimization here. We update the dirstate in place and strip
821 # optimization here. We update the dirstate in place and strip
818 # off the tip commit. Then just commit the current directory
822 # off the tip commit. Then just commit the current directory
819 # tree. We can also send repo.commit the list of files
823 # tree. We can also send repo.commit the list of files
820 # changed to speed up the diff
824 # changed to speed up the diff
821 #
825 #
822 # in short mode, we only diff the files included in the
826 # in short mode, we only diff the files included in the
823 # patch already
827 # patch already
824 #
828 #
825 # this should really read:
829 # this should really read:
826 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
830 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
827 # but we do it backwards to take advantage of manifest/chlog
831 # but we do it backwards to take advantage of manifest/chlog
828 # caching against the next repo.changes call
832 # caching against the next repo.changes call
829 #
833 #
830 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
834 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
831 if short:
835 if short:
832 filelist = cc + aa + dd
836 filelist = cc + aa + dd
833 else:
837 else:
834 filelist = None
838 filelist = None
835 (c, a, r, d, u) = repo.changes(None, None, filelist)
839 (c, a, r, d, u) = repo.changes(None, None, filelist)
836
840
837 # 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
838 # the dirstate parent, but then changed in the local dirstate.
842 # the dirstate parent, but then changed in the local dirstate.
839 # 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
840 for x in c:
844 for x in c:
841 if x not in aa:
845 if x not in aa:
842 cc.append(x)
846 cc.append(x)
843 # 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
844 # were deleted by the patch. In this case, they should only
848 # were deleted by the patch. In this case, they should only
845 # show up in the changed section.
849 # show up in the changed section.
846 for x in a:
850 for x in a:
847 if x in dd:
851 if x in dd:
848 del dd[dd.index(x)]
852 del dd[dd.index(x)]
849 cc.append(x)
853 cc.append(x)
850 else:
854 else:
851 aa.append(x)
855 aa.append(x)
852 # make sure any files deleted in the local dirstate
856 # make sure any files deleted in the local dirstate
853 # are not in the add or change column of the patch
857 # are not in the add or change column of the patch
854 forget = []
858 forget = []
855 for x in d + r:
859 for x in d + r:
856 if x in aa:
860 if x in aa:
857 del aa[aa.index(x)]
861 del aa[aa.index(x)]
858 forget.append(x)
862 forget.append(x)
859 continue
863 continue
860 elif x in cc:
864 elif x in cc:
861 del cc[cc.index(x)]
865 del cc[cc.index(x)]
862 dd.append(x)
866 dd.append(x)
863
867
864 c = list(util.unique(cc))
868 c = list(util.unique(cc))
865 r = list(util.unique(dd))
869 r = list(util.unique(dd))
866 a = list(util.unique(aa))
870 a = list(util.unique(aa))
867 filelist = list(util.unique(c + r + a ))
871 filelist = list(util.unique(c + r + a ))
868 commands.dodiff(patchf, self.ui, repo, patchparent, None,
872 commands.dodiff(patchf, self.ui, repo, patchparent, None,
869 filelist, changes=(c, a, r, [], u))
873 filelist, changes=(c, a, r, [], u))
870 patchf.close()
874 patchf.close()
871
875
872 changes = repo.changelog.read(tip)
876 changes = repo.changelog.read(tip)
873 repo.dirstate.setparents(*cparents)
877 repo.dirstate.setparents(*cparents)
874 repo.dirstate.update(a, 'a')
878 repo.dirstate.update(a, 'a')
875 repo.dirstate.update(r, 'r')
879 repo.dirstate.update(r, 'r')
876 repo.dirstate.update(c, 'n')
880 repo.dirstate.update(c, 'n')
877 repo.dirstate.forget(forget)
881 repo.dirstate.forget(forget)
878
882
879 if not msg:
883 if not msg:
880 if not message:
884 if not message:
881 message = "patch queue: %s\n" % patch
885 message = "patch queue: %s\n" % patch
882 else:
886 else:
883 message = "\n".join(message)
887 message = "\n".join(message)
884 else:
888 else:
885 message = msg
889 message = msg
886
890
887 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
891 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
888 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
892 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
889 self.applied[-1] = revlog.hex(n) + ':' + patch
893 self.applied[-1] = revlog.hex(n) + ':' + patch
890 self.applied_dirty = 1
894 self.applied_dirty = 1
891 else:
895 else:
892 commands.dodiff(patchf, self.ui, repo, patchparent, None)
896 commands.dodiff(patchf, self.ui, repo, patchparent, None)
893 patchf.close()
897 patchf.close()
894 self.pop(repo, force=True, wlock=wlock)
898 self.pop(repo, force=True, wlock=wlock)
895 self.push(repo, force=True, wlock=wlock)
899 self.push(repo, force=True, wlock=wlock)
896
900
897 def init(self, repo, create=False):
901 def init(self, repo, create=False):
898 if os.path.isdir(self.path):
902 if os.path.isdir(self.path):
899 raise util.Abort(_("patch queue directory already exists"))
903 raise util.Abort(_("patch queue directory already exists"))
900 os.mkdir(self.path)
904 os.mkdir(self.path)
901 if create:
905 if create:
902 return self.qrepo(create=True)
906 return self.qrepo(create=True)
903
907
904 def unapplied(self, repo, patch=None):
908 def unapplied(self, repo, patch=None):
905 if patch and patch not in self.series:
909 if patch and patch not in self.series:
906 raise util.Abort(_("patch %s is not in series file") % patch)
910 raise util.Abort(_("patch %s is not in series file") % patch)
907 if not patch:
911 if not patch:
908 start = self.series_end()
912 start = self.series_end()
909 else:
913 else:
910 start = self.series.index(patch) + 1
914 start = self.series.index(patch) + 1
911 for p in self.series[start:]:
915 for p in self.series[start:]:
912 if self.ui.verbose:
916 if self.ui.verbose:
913 self.ui.write("%d " % self.series.index(p))
917 self.ui.write("%d " % self.series.index(p))
914 self.ui.write("%s\n" % p)
918 self.ui.write("%s\n" % p)
915
919
916 def qseries(self, repo, missing=None):
920 def qseries(self, repo, missing=None):
917 start = self.series_end()
921 start = self.series_end()
918 if not missing:
922 if not missing:
919 for p in self.series[:start]:
923 for p in self.series[:start]:
920 if self.ui.verbose:
924 if self.ui.verbose:
921 self.ui.write("%d A " % self.series.index(p))
925 self.ui.write("%d A " % self.series.index(p))
922 self.ui.write("%s\n" % p)
926 self.ui.write("%s\n" % p)
923 for p in self.series[start:]:
927 for p in self.series[start:]:
924 if self.ui.verbose:
928 if self.ui.verbose:
925 self.ui.write("%d U " % self.series.index(p))
929 self.ui.write("%d U " % self.series.index(p))
926 self.ui.write("%s\n" % p)
930 self.ui.write("%s\n" % p)
927 else:
931 else:
928 list = []
932 list = []
929 for root, dirs, files in os.walk(self.path):
933 for root, dirs, files in os.walk(self.path):
930 d = root[len(self.path) + 1:]
934 d = root[len(self.path) + 1:]
931 for f in files:
935 for f in files:
932 fl = os.path.join(d, f)
936 fl = os.path.join(d, f)
933 if (fl not in self.series and
937 if (fl not in self.series and
934 fl not in (self.status_path, self.series_path)
938 fl not in (self.status_path, self.series_path)
935 and not fl.startswith('.')):
939 and not fl.startswith('.')):
936 list.append(fl)
940 list.append(fl)
937 list.sort()
941 list.sort()
938 if list:
942 if list:
939 for x in list:
943 for x in list:
940 if self.ui.verbose:
944 if self.ui.verbose:
941 self.ui.write("D ")
945 self.ui.write("D ")
942 self.ui.write("%s\n" % x)
946 self.ui.write("%s\n" % x)
943
947
944 def issaveline(self, l):
948 def issaveline(self, l):
945 name = l.split(':')[1]
949 name = l.split(':')[1]
946 if name == '.hg.patches.save.line':
950 if name == '.hg.patches.save.line':
947 return True
951 return True
948
952
949 def qrepo(self, create=False):
953 def qrepo(self, create=False):
950 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")):
951 return hg.repository(self.ui, path=self.path, create=create)
955 return hg.repository(self.ui, path=self.path, create=create)
952
956
953 def restore(self, repo, rev, delete=None, qupdate=None):
957 def restore(self, repo, rev, delete=None, qupdate=None):
954 c = repo.changelog.read(rev)
958 c = repo.changelog.read(rev)
955 desc = c[4].strip()
959 desc = c[4].strip()
956 lines = desc.splitlines()
960 lines = desc.splitlines()
957 i = 0
961 i = 0
958 datastart = None
962 datastart = None
959 series = []
963 series = []
960 applied = []
964 applied = []
961 qpp = None
965 qpp = None
962 for i in xrange(0, len(lines)):
966 for i in xrange(0, len(lines)):
963 if lines[i] == 'Patch Data:':
967 if lines[i] == 'Patch Data:':
964 datastart = i + 1
968 datastart = i + 1
965 elif lines[i].startswith('Dirstate:'):
969 elif lines[i].startswith('Dirstate:'):
966 l = lines[i].rstrip()
970 l = lines[i].rstrip()
967 l = l[10:].split(' ')
971 l = l[10:].split(' ')
968 qpp = [ hg.bin(x) for x in l ]
972 qpp = [ hg.bin(x) for x in l ]
969 elif datastart != None:
973 elif datastart != None:
970 l = lines[i].rstrip()
974 l = lines[i].rstrip()
971 index = l.index(':')
975 index = l.index(':')
972 id = l[:index]
976 id = l[:index]
973 file = l[index + 1:]
977 file = l[index + 1:]
974 if id:
978 if id:
975 applied.append(l)
979 applied.append(l)
976 series.append(file)
980 series.append(file)
977 if datastart == None:
981 if datastart == None:
978 self.ui.warn("No saved patch data found\n")
982 self.ui.warn("No saved patch data found\n")
979 return 1
983 return 1
980 self.ui.warn("restoring status: %s\n" % lines[0])
984 self.ui.warn("restoring status: %s\n" % lines[0])
981 self.full_series = series
985 self.full_series = series
982 self.applied = applied
986 self.applied = applied
983 self.read_series(self.full_series)
987 self.read_series(self.full_series)
984 self.series_dirty = 1
988 self.series_dirty = 1
985 self.applied_dirty = 1
989 self.applied_dirty = 1
986 heads = repo.changelog.heads()
990 heads = repo.changelog.heads()
987 if delete:
991 if delete:
988 if rev not in heads:
992 if rev not in heads:
989 self.ui.warn("save entry has children, leaving it alone\n")
993 self.ui.warn("save entry has children, leaving it alone\n")
990 else:
994 else:
991 self.ui.warn("removing save entry %s\n" % hg.short(rev))
995 self.ui.warn("removing save entry %s\n" % hg.short(rev))
992 pp = repo.dirstate.parents()
996 pp = repo.dirstate.parents()
993 if rev in pp:
997 if rev in pp:
994 update = True
998 update = True
995 else:
999 else:
996 update = False
1000 update = False
997 self.strip(repo, rev, update=update, backup='strip')
1001 self.strip(repo, rev, update=update, backup='strip')
998 if qpp:
1002 if qpp:
999 self.ui.warn("saved queue repository parents: %s %s\n" %
1003 self.ui.warn("saved queue repository parents: %s %s\n" %
1000 (hg.short(qpp[0]), hg.short(qpp[1])))
1004 (hg.short(qpp[0]), hg.short(qpp[1])))
1001 if qupdate:
1005 if qupdate:
1002 print "queue directory updating"
1006 print "queue directory updating"
1003 r = self.qrepo()
1007 r = self.qrepo()
1004 if not r:
1008 if not r:
1005 self.ui.warn("Unable to load queue repository\n")
1009 self.ui.warn("Unable to load queue repository\n")
1006 return 1
1010 return 1
1007 r.update(qpp[0], allow=False, force=True)
1011 r.update(qpp[0], allow=False, force=True)
1008
1012
1009 def save(self, repo, msg=None):
1013 def save(self, repo, msg=None):
1010 if len(self.applied) == 0:
1014 if len(self.applied) == 0:
1011 self.ui.warn("save: no patches applied, exiting\n")
1015 self.ui.warn("save: no patches applied, exiting\n")
1012 return 1
1016 return 1
1013 if self.issaveline(self.applied[-1]):
1017 if self.issaveline(self.applied[-1]):
1014 self.ui.warn("status is already saved\n")
1018 self.ui.warn("status is already saved\n")
1015 return 1
1019 return 1
1016
1020
1017 ar = [ ':' + x for x in self.full_series ]
1021 ar = [ ':' + x for x in self.full_series ]
1018 if not msg:
1022 if not msg:
1019 msg = "hg patches saved state"
1023 msg = "hg patches saved state"
1020 else:
1024 else:
1021 msg = "hg patches: " + msg.rstrip('\r\n')
1025 msg = "hg patches: " + msg.rstrip('\r\n')
1022 r = self.qrepo()
1026 r = self.qrepo()
1023 if r:
1027 if r:
1024 pp = r.dirstate.parents()
1028 pp = r.dirstate.parents()
1025 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]))
1026 msg += "\n\nPatch Data:\n"
1030 msg += "\n\nPatch Data:\n"
1027 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)
1028 + '\n' or "")
1032 + '\n' or "")
1029 n = repo.commit(None, text, user=None, force=1)
1033 n = repo.commit(None, text, user=None, force=1)
1030 if not n:
1034 if not n:
1031 self.ui.warn("repo commit failed\n")
1035 self.ui.warn("repo commit failed\n")
1032 return 1
1036 return 1
1033 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
1037 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
1034 self.applied_dirty = 1
1038 self.applied_dirty = 1
1035
1039
1036 def full_series_end(self):
1040 def full_series_end(self):
1037 if len(self.applied) > 0:
1041 if len(self.applied) > 0:
1038 (top, p) = self.applied[-1].split(':')
1042 (top, p) = self.applied[-1].split(':')
1039 end = self.find_series(p)
1043 end = self.find_series(p)
1040 if end == None:
1044 if end == None:
1041 return len(self.full_series)
1045 return len(self.full_series)
1042 return end + 1
1046 return end + 1
1043 return 0
1047 return 0
1044
1048
1045 def series_end(self):
1049 def series_end(self):
1046 end = 0
1050 end = 0
1047 if len(self.applied) > 0:
1051 if len(self.applied) > 0:
1048 (top, p) = self.applied[-1].split(':')
1052 (top, p) = self.applied[-1].split(':')
1049 try:
1053 try:
1050 end = self.series.index(p)
1054 end = self.series.index(p)
1051 except ValueError:
1055 except ValueError:
1052 return 0
1056 return 0
1053 return end + 1
1057 return end + 1
1054 return end
1058 return end
1055
1059
1056 def qapplied(self, repo, patch=None):
1060 def qapplied(self, repo, patch=None):
1057 if patch and patch not in self.series:
1061 if patch and patch not in self.series:
1058 raise util.Abort(_("patch %s is not in series file") % patch)
1062 raise util.Abort(_("patch %s is not in series file") % patch)
1059 if not patch:
1063 if not patch:
1060 end = len(self.applied)
1064 end = len(self.applied)
1061 else:
1065 else:
1062 end = self.series.index(patch) + 1
1066 end = self.series.index(patch) + 1
1063 for x in xrange(end):
1067 for x in xrange(end):
1064 p = self.appliedname(x)
1068 p = self.appliedname(x)
1065 self.ui.write("%s\n" % p)
1069 self.ui.write("%s\n" % p)
1066
1070
1067 def appliedname(self, index):
1071 def appliedname(self, index):
1068 p = self.applied[index]
1072 p = self.applied[index]
1069 pname = p.split(':')[1]
1073 pname = p.split(':')[1]
1070 if not self.ui.verbose:
1074 if not self.ui.verbose:
1071 p = pname
1075 p = pname
1072 else:
1076 else:
1073 p = str(self.series.index(pname)) + " " + p
1077 p = str(self.series.index(pname)) + " " + p
1074 return p
1078 return p
1075
1079
1076 def top(self, repo):
1080 def top(self, repo):
1077 if len(self.applied):
1081 if len(self.applied):
1078 p = self.appliedname(-1)
1082 p = self.appliedname(-1)
1079 self.ui.write(p + '\n')
1083 self.ui.write(p + '\n')
1080 else:
1084 else:
1081 self.ui.write("No patches applied\n")
1085 self.ui.write("No patches applied\n")
1082
1086
1083 def next(self, repo):
1087 def next(self, repo):
1084 end = self.series_end()
1088 end = self.series_end()
1085 if end == len(self.series):
1089 if end == len(self.series):
1086 self.ui.write("All patches applied\n")
1090 self.ui.write("All patches applied\n")
1087 else:
1091 else:
1088 p = self.series[end]
1092 p = self.series[end]
1089 if self.ui.verbose:
1093 if self.ui.verbose:
1090 self.ui.write("%d " % self.series.index(p))
1094 self.ui.write("%d " % self.series.index(p))
1091 self.ui.write(p + '\n')
1095 self.ui.write(p + '\n')
1092
1096
1093 def prev(self, repo):
1097 def prev(self, repo):
1094 if len(self.applied) > 1:
1098 if len(self.applied) > 1:
1095 p = self.appliedname(-2)
1099 p = self.appliedname(-2)
1096 self.ui.write(p + '\n')
1100 self.ui.write(p + '\n')
1097 elif len(self.applied) == 1:
1101 elif len(self.applied) == 1:
1098 self.ui.write("Only one patch applied\n")
1102 self.ui.write("Only one patch applied\n")
1099 else:
1103 else:
1100 self.ui.write("No patches applied\n")
1104 self.ui.write("No patches applied\n")
1101
1105
1102 def qimport(self, repo, files, patch=None, existing=None, force=None):
1106 def qimport(self, repo, files, patch=None, existing=None, force=None):
1103 if len(files) > 1 and patch:
1107 if len(files) > 1 and patch:
1104 raise util.Abort(_('option "-n" not valid when importing multiple '
1108 raise util.Abort(_('option "-n" not valid when importing multiple '
1105 'files'))
1109 'files'))
1106 i = 0
1110 i = 0
1107 added = []
1111 added = []
1108 for filename in files:
1112 for filename in files:
1109 if existing:
1113 if existing:
1110 if not patch:
1114 if not patch:
1111 patch = filename
1115 patch = filename
1112 if not os.path.isfile(os.path.join(self.path, patch)):
1116 if not os.path.isfile(os.path.join(self.path, patch)):
1113 raise util.Abort(_("patch %s does not exist") % patch)
1117 raise util.Abort(_("patch %s does not exist") % patch)
1114 else:
1118 else:
1115 try:
1119 try:
1116 text = file(filename).read()
1120 text = file(filename).read()
1117 except IOError:
1121 except IOError:
1118 raise util.Abort(_("unable to read %s") % patch)
1122 raise util.Abort(_("unable to read %s") % patch)
1119 if not patch:
1123 if not patch:
1120 patch = os.path.split(filename)[1]
1124 patch = os.path.split(filename)[1]
1121 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)):
1122 raise util.Abort(_('patch "%s" already exists') % patch)
1126 raise util.Abort(_('patch "%s" already exists') % patch)
1123 patchf = self.opener(patch, "w")
1127 patchf = self.opener(patch, "w")
1124 patchf.write(text)
1128 patchf.write(text)
1125 if patch in self.series:
1129 if patch in self.series:
1126 raise util.Abort(_('patch %s is already in the series file')
1130 raise util.Abort(_('patch %s is already in the series file')
1127 % patch)
1131 % patch)
1128 index = self.full_series_end() + i
1132 index = self.full_series_end() + i
1129 self.full_series[index:index] = [patch]
1133 self.full_series[index:index] = [patch]
1130 self.read_series(self.full_series)
1134 self.read_series(self.full_series)
1131 self.ui.warn("adding %s to series file\n" % patch)
1135 self.ui.warn("adding %s to series file\n" % patch)
1132 i += 1
1136 i += 1
1133 added.append(patch)
1137 added.append(patch)
1134 patch = None
1138 patch = None
1135 self.series_dirty = 1
1139 self.series_dirty = 1
1136 qrepo = self.qrepo()
1140 qrepo = self.qrepo()
1137 if qrepo:
1141 if qrepo:
1138 qrepo.add(added)
1142 qrepo.add(added)
1139
1143
1140 def delete(ui, repo, patch, **opts):
1144 def delete(ui, repo, patch, **opts):
1141 """remove a patch from the series file"""
1145 """remove a patch from the series file"""
1142 q = repo.mq
1146 q = repo.mq
1143 q.delete(repo, patch)
1147 q.delete(repo, patch)
1144 q.save_dirty()
1148 q.save_dirty()
1145 return 0
1149 return 0
1146
1150
1147 def applied(ui, repo, patch=None, **opts):
1151 def applied(ui, repo, patch=None, **opts):
1148 """print the patches already applied"""
1152 """print the patches already applied"""
1149 repo.mq.qapplied(repo, patch)
1153 repo.mq.qapplied(repo, patch)
1150 return 0
1154 return 0
1151
1155
1152 def unapplied(ui, repo, patch=None, **opts):
1156 def unapplied(ui, repo, patch=None, **opts):
1153 """print the patches not yet applied"""
1157 """print the patches not yet applied"""
1154 repo.mq.unapplied(repo, patch)
1158 repo.mq.unapplied(repo, patch)
1155 return 0
1159 return 0
1156
1160
1157 def qimport(ui, repo, *filename, **opts):
1161 def qimport(ui, repo, *filename, **opts):
1158 """import a patch"""
1162 """import a patch"""
1159 q = repo.mq
1163 q = repo.mq
1160 q.qimport(repo, filename, patch=opts['name'],
1164 q.qimport(repo, filename, patch=opts['name'],
1161 existing=opts['existing'], force=opts['force'])
1165 existing=opts['existing'], force=opts['force'])
1162 q.save_dirty()
1166 q.save_dirty()
1163 return 0
1167 return 0
1164
1168
1165 def init(ui, repo, **opts):
1169 def init(ui, repo, **opts):
1166 """init a new queue repository"""
1170 """init a new queue repository"""
1167 q = repo.mq
1171 q = repo.mq
1168 r = q.init(repo, create=opts['create_repo'])
1172 r = q.init(repo, create=opts['create_repo'])
1169 q.save_dirty()
1173 q.save_dirty()
1170 if r:
1174 if r:
1171 fp = r.wopener('.hgignore', 'w')
1175 fp = r.wopener('.hgignore', 'w')
1172 print >> fp, 'syntax: glob'
1176 print >> fp, 'syntax: glob'
1173 print >> fp, 'status'
1177 print >> fp, 'status'
1174 fp.close()
1178 fp.close()
1175 r.wopener('series', 'w').close()
1179 r.wopener('series', 'w').close()
1176 r.add(['.hgignore', 'series'])
1180 r.add(['.hgignore', 'series'])
1177 return 0
1181 return 0
1178
1182
1179 def clone(ui, source, dest=None, **opts):
1183 def clone(ui, source, dest=None, **opts):
1180 '''clone main and patch repository at same time
1184 '''clone main and patch repository at same time
1181
1185
1182 If source is local, destination will have no patches applied. If
1186 If source is local, destination will have no patches applied. If
1183 source is remote, this command can not check if patches are
1187 source is remote, this command can not check if patches are
1184 applied in source, so cannot guarantee that patches are not
1188 applied in source, so cannot guarantee that patches are not
1185 applied in destination. If you clone remote repository, be sure
1189 applied in destination. If you clone remote repository, be sure
1186 before that it has no patches applied.
1190 before that it has no patches applied.
1187
1191
1188 Source patch repository is looked for in <src>/.hg/patches by
1192 Source patch repository is looked for in <src>/.hg/patches by
1189 default. Use -p <url> to change.
1193 default. Use -p <url> to change.
1190 '''
1194 '''
1191 ui.setconfig_remoteopts(**opts)
1195 ui.setconfig_remoteopts(**opts)
1192 if dest is None:
1196 if dest is None:
1193 dest = hg.defaultdest(source)
1197 dest = hg.defaultdest(source)
1194 sr = hg.repository(ui, ui.expandpath(source))
1198 sr = hg.repository(ui, ui.expandpath(source))
1195 qbase, destrev = None, None
1199 qbase, destrev = None, None
1196 if sr.local():
1200 if sr.local():
1197 reposetup(ui, sr)
1201 reposetup(ui, sr)
1198 if sr.mq.applied:
1202 if sr.mq.applied:
1199 qbase = revlog.bin(sr.mq.applied[0].split(':')[0])
1203 qbase = revlog.bin(sr.mq.applied[0].split(':')[0])
1200 if not hg.islocal(dest):
1204 if not hg.islocal(dest):
1201 destrev = sr.parents(qbase)[0]
1205 destrev = sr.parents(qbase)[0]
1202 ui.note(_('cloning main repo\n'))
1206 ui.note(_('cloning main repo\n'))
1203 sr, dr = hg.clone(ui, sr, dest,
1207 sr, dr = hg.clone(ui, sr, dest,
1204 pull=opts['pull'],
1208 pull=opts['pull'],
1205 rev=destrev,
1209 rev=destrev,
1206 update=False,
1210 update=False,
1207 stream=opts['uncompressed'])
1211 stream=opts['uncompressed'])
1208 ui.note(_('cloning patch repo\n'))
1212 ui.note(_('cloning patch repo\n'))
1209 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'),
1210 dr.url() + '/.hg/patches',
1214 dr.url() + '/.hg/patches',
1211 pull=opts['pull'],
1215 pull=opts['pull'],
1212 update=not opts['noupdate'],
1216 update=not opts['noupdate'],
1213 stream=opts['uncompressed'])
1217 stream=opts['uncompressed'])
1214 if dr.local():
1218 if dr.local():
1215 if qbase:
1219 if qbase:
1216 ui.note(_('stripping applied patches from destination repo\n'))
1220 ui.note(_('stripping applied patches from destination repo\n'))
1217 reposetup(ui, dr)
1221 reposetup(ui, dr)
1218 dr.mq.strip(dr, qbase, update=False, backup=None)
1222 dr.mq.strip(dr, qbase, update=False, backup=None)
1219 if not opts['noupdate']:
1223 if not opts['noupdate']:
1220 ui.note(_('updating destination repo\n'))
1224 ui.note(_('updating destination repo\n'))
1221 dr.update(dr.changelog.tip())
1225 dr.update(dr.changelog.tip())
1222
1226
1223 def commit(ui, repo, *pats, **opts):
1227 def commit(ui, repo, *pats, **opts):
1224 """commit changes in the queue repository"""
1228 """commit changes in the queue repository"""
1225 q = repo.mq
1229 q = repo.mq
1226 r = q.qrepo()
1230 r = q.qrepo()
1227 if not r: raise util.Abort('no queue repository')
1231 if not r: raise util.Abort('no queue repository')
1228 commands.commit(r.ui, r, *pats, **opts)
1232 commands.commit(r.ui, r, *pats, **opts)
1229
1233
1230 def series(ui, repo, **opts):
1234 def series(ui, repo, **opts):
1231 """print the entire series file"""
1235 """print the entire series file"""
1232 repo.mq.qseries(repo, missing=opts['missing'])
1236 repo.mq.qseries(repo, missing=opts['missing'])
1233 return 0
1237 return 0
1234
1238
1235 def top(ui, repo, **opts):
1239 def top(ui, repo, **opts):
1236 """print the name of the current patch"""
1240 """print the name of the current patch"""
1237 repo.mq.top(repo)
1241 repo.mq.top(repo)
1238 return 0
1242 return 0
1239
1243
1240 def next(ui, repo, **opts):
1244 def next(ui, repo, **opts):
1241 """print the name of the next patch"""
1245 """print the name of the next patch"""
1242 repo.mq.next(repo)
1246 repo.mq.next(repo)
1243 return 0
1247 return 0
1244
1248
1245 def prev(ui, repo, **opts):
1249 def prev(ui, repo, **opts):
1246 """print the name of the previous patch"""
1250 """print the name of the previous patch"""
1247 repo.mq.prev(repo)
1251 repo.mq.prev(repo)
1248 return 0
1252 return 0
1249
1253
1250 def new(ui, repo, patch, **opts):
1254 def new(ui, repo, patch, **opts):
1251 """create a new patch"""
1255 """create a new patch"""
1252 q = repo.mq
1256 q = repo.mq
1253 message=commands.logmessage(**opts)
1257 message=commands.logmessage(**opts)
1254 q.new(repo, patch, msg=message, force=opts['force'])
1258 q.new(repo, patch, msg=message, force=opts['force'])
1255 q.save_dirty()
1259 q.save_dirty()
1256 return 0
1260 return 0
1257
1261
1258 def refresh(ui, repo, **opts):
1262 def refresh(ui, repo, **opts):
1259 """update the current patch"""
1263 """update the current patch"""
1260 q = repo.mq
1264 q = repo.mq
1261 message=commands.logmessage(**opts)
1265 message=commands.logmessage(**opts)
1262 q.refresh(repo, msg=message, short=opts['short'])
1266 q.refresh(repo, msg=message, short=opts['short'])
1263 q.save_dirty()
1267 q.save_dirty()
1264 return 0
1268 return 0
1265
1269
1266 def diff(ui, repo, *files, **opts):
1270 def diff(ui, repo, *files, **opts):
1267 """diff of the current patch"""
1271 """diff of the current patch"""
1268 # 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
1269 repo.mq.diff(repo, list(files))
1273 repo.mq.diff(repo, list(files))
1270 return 0
1274 return 0
1271
1275
1272 def lastsavename(path):
1276 def lastsavename(path):
1273 (dir, base) = os.path.split(path)
1277 (dir, base) = os.path.split(path)
1274 names = os.listdir(dir)
1278 names = os.listdir(dir)
1275 namere = re.compile("%s.([0-9]+)" % base)
1279 namere = re.compile("%s.([0-9]+)" % base)
1276 max = None
1280 max = None
1277 maxname = None
1281 maxname = None
1278 for f in names:
1282 for f in names:
1279 m = namere.match(f)
1283 m = namere.match(f)
1280 if m:
1284 if m:
1281 index = int(m.group(1))
1285 index = int(m.group(1))
1282 if max == None or index > max:
1286 if max == None or index > max:
1283 max = index
1287 max = index
1284 maxname = f
1288 maxname = f
1285 if maxname:
1289 if maxname:
1286 return (os.path.join(dir, maxname), max)
1290 return (os.path.join(dir, maxname), max)
1287 return (None, None)
1291 return (None, None)
1288
1292
1289 def savename(path):
1293 def savename(path):
1290 (last, index) = lastsavename(path)
1294 (last, index) = lastsavename(path)
1291 if last is None:
1295 if last is None:
1292 index = 0
1296 index = 0
1293 newpath = path + ".%d" % (index + 1)
1297 newpath = path + ".%d" % (index + 1)
1294 return newpath
1298 return newpath
1295
1299
1296 def push(ui, repo, patch=None, **opts):
1300 def push(ui, repo, patch=None, **opts):
1297 """push the next patch onto the stack"""
1301 """push the next patch onto the stack"""
1298 q = repo.mq
1302 q = repo.mq
1299 mergeq = None
1303 mergeq = None
1300
1304
1301 if opts['all']:
1305 if opts['all']:
1302 patch = q.series[-1]
1306 patch = q.series[-1]
1303 if opts['merge']:
1307 if opts['merge']:
1304 if opts['name']:
1308 if opts['name']:
1305 newpath = opts['name']
1309 newpath = opts['name']
1306 else:
1310 else:
1307 newpath, i = lastsavename(q.path)
1311 newpath, i = lastsavename(q.path)
1308 if not newpath:
1312 if not newpath:
1309 ui.warn("no saved queues found, please use -n\n")
1313 ui.warn("no saved queues found, please use -n\n")
1310 return 1
1314 return 1
1311 mergeq = queue(ui, repo.join(""), newpath)
1315 mergeq = queue(ui, repo.join(""), newpath)
1312 ui.warn("merging with queue at: %s\n" % mergeq.path)
1316 ui.warn("merging with queue at: %s\n" % mergeq.path)
1313 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1317 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1314 mergeq=mergeq)
1318 mergeq=mergeq)
1315 q.save_dirty()
1319 q.save_dirty()
1316 return ret
1320 return ret
1317
1321
1318 def pop(ui, repo, patch=None, **opts):
1322 def pop(ui, repo, patch=None, **opts):
1319 """pop the current patch off the stack"""
1323 """pop the current patch off the stack"""
1320 localupdate = True
1324 localupdate = True
1321 if opts['name']:
1325 if opts['name']:
1322 q = queue(ui, repo.join(""), repo.join(opts['name']))
1326 q = queue(ui, repo.join(""), repo.join(opts['name']))
1323 ui.warn('using patch queue: %s\n' % q.path)
1327 ui.warn('using patch queue: %s\n' % q.path)
1324 localupdate = False
1328 localupdate = False
1325 else:
1329 else:
1326 q = repo.mq
1330 q = repo.mq
1327 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'])
1328 q.save_dirty()
1332 q.save_dirty()
1329 return 0
1333 return 0
1330
1334
1331 def restore(ui, repo, rev, **opts):
1335 def restore(ui, repo, rev, **opts):
1332 """restore the queue state saved by a rev"""
1336 """restore the queue state saved by a rev"""
1333 rev = repo.lookup(rev)
1337 rev = repo.lookup(rev)
1334 q = repo.mq
1338 q = repo.mq
1335 q.restore(repo, rev, delete=opts['delete'],
1339 q.restore(repo, rev, delete=opts['delete'],
1336 qupdate=opts['update'])
1340 qupdate=opts['update'])
1337 q.save_dirty()
1341 q.save_dirty()
1338 return 0
1342 return 0
1339
1343
1340 def save(ui, repo, **opts):
1344 def save(ui, repo, **opts):
1341 """save current queue state"""
1345 """save current queue state"""
1342 q = repo.mq
1346 q = repo.mq
1343 message=commands.logmessage(**opts)
1347 message=commands.logmessage(**opts)
1344 ret = q.save(repo, msg=message)
1348 ret = q.save(repo, msg=message)
1345 if ret:
1349 if ret:
1346 return ret
1350 return ret
1347 q.save_dirty()
1351 q.save_dirty()
1348 if opts['copy']:
1352 if opts['copy']:
1349 path = q.path
1353 path = q.path
1350 if opts['name']:
1354 if opts['name']:
1351 newpath = os.path.join(q.basepath, opts['name'])
1355 newpath = os.path.join(q.basepath, opts['name'])
1352 if os.path.exists(newpath):
1356 if os.path.exists(newpath):
1353 if not os.path.isdir(newpath):
1357 if not os.path.isdir(newpath):
1354 raise util.Abort(_('destination %s exists and is not '
1358 raise util.Abort(_('destination %s exists and is not '
1355 'a directory') % newpath)
1359 'a directory') % newpath)
1356 if not opts['force']:
1360 if not opts['force']:
1357 raise util.Abort(_('destination %s exists, '
1361 raise util.Abort(_('destination %s exists, '
1358 'use -f to force') % newpath)
1362 'use -f to force') % newpath)
1359 else:
1363 else:
1360 newpath = savename(path)
1364 newpath = savename(path)
1361 ui.warn("copy %s to %s\n" % (path, newpath))
1365 ui.warn("copy %s to %s\n" % (path, newpath))
1362 util.copyfiles(path, newpath)
1366 util.copyfiles(path, newpath)
1363 if opts['empty']:
1367 if opts['empty']:
1364 try:
1368 try:
1365 os.unlink(os.path.join(q.path, q.status_path))
1369 os.unlink(os.path.join(q.path, q.status_path))
1366 except:
1370 except:
1367 pass
1371 pass
1368 return 0
1372 return 0
1369
1373
1370 def strip(ui, repo, rev, **opts):
1374 def strip(ui, repo, rev, **opts):
1371 """strip a revision and all later revs on the same branch"""
1375 """strip a revision and all later revs on the same branch"""
1372 rev = repo.lookup(rev)
1376 rev = repo.lookup(rev)
1373 backup = 'all'
1377 backup = 'all'
1374 if opts['backup']:
1378 if opts['backup']:
1375 backup = 'strip'
1379 backup = 'strip'
1376 elif opts['nobackup']:
1380 elif opts['nobackup']:
1377 backup = 'none'
1381 backup = 'none'
1378 repo.mq.strip(repo, rev, backup=backup)
1382 repo.mq.strip(repo, rev, backup=backup)
1379 return 0
1383 return 0
1380
1384
1381 def version(ui, q=None):
1385 def version(ui, q=None):
1382 """print the version number"""
1386 """print the version number"""
1383 ui.write("mq version %s\n" % versionstr)
1387 ui.write("mq version %s\n" % versionstr)
1384 return 0
1388 return 0
1385
1389
1386 def reposetup(ui, repo):
1390 def reposetup(ui, repo):
1387 class MqRepo(repo.__class__):
1391 class MqRepo(repo.__class__):
1388 def tags(self):
1392 def tags(self):
1389 if self.tagscache:
1393 if self.tagscache:
1390 return self.tagscache
1394 return self.tagscache
1391
1395
1392 tagscache = super(self.__class__, self).tags()
1396 tagscache = super(self.__class__, self).tags()
1393
1397
1394 q = self.mq
1398 q = self.mq
1395 if not q.applied:
1399 if not q.applied:
1396 return tagscache
1400 return tagscache
1397
1401
1398 mqtags = [patch.split(':') for patch in q.applied]
1402 mqtags = [patch.split(':') for patch in q.applied]
1399 mqtags.append((mqtags[-1][0], 'qtip'))
1403 mqtags.append((mqtags[-1][0], 'qtip'))
1400 mqtags.append((mqtags[0][0], 'qbase'))
1404 mqtags.append((mqtags[0][0], 'qbase'))
1401 for patch in mqtags:
1405 for patch in mqtags:
1402 if patch[1] in tagscache:
1406 if patch[1] in tagscache:
1403 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])
1404 else:
1408 else:
1405 tagscache[patch[1]] = revlog.bin(patch[0])
1409 tagscache[patch[1]] = revlog.bin(patch[0])
1406
1410
1407 return tagscache
1411 return tagscache
1408
1412
1409 repo.__class__ = MqRepo
1413 repo.__class__ = MqRepo
1410 repo.mq = queue(ui, repo.join(""))
1414 repo.mq = queue(ui, repo.join(""))
1411
1415
1412 cmdtable = {
1416 cmdtable = {
1413 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1417 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1414 "qclone": (clone,
1418 "qclone": (clone,
1415 [('', 'pull', None, _('use pull protocol to copy metadata')),
1419 [('', 'pull', None, _('use pull protocol to copy metadata')),
1416 ('U', 'noupdate', None, _('do not update the new working directories')),
1420 ('U', 'noupdate', None, _('do not update the new working directories')),
1417 ('', 'uncompressed', None,
1421 ('', 'uncompressed', None,
1418 _('use uncompressed transfer (fast over LAN)')),
1422 _('use uncompressed transfer (fast over LAN)')),
1419 ('e', 'ssh', '', _('specify ssh command to use')),
1423 ('e', 'ssh', '', _('specify ssh command to use')),
1420 ('p', 'patches', '', _('location of source patch repo')),
1424 ('p', 'patches', '', _('location of source patch repo')),
1421 ('', 'remotecmd', '',
1425 ('', 'remotecmd', '',
1422 _('specify hg command to run on the remote side'))],
1426 _('specify hg command to run on the remote side'))],
1423 'hg qclone [OPTION]... SOURCE [DEST]'),
1427 'hg qclone [OPTION]... SOURCE [DEST]'),
1424 "qcommit|qci":
1428 "qcommit|qci":
1425 (commit,
1429 (commit,
1426 commands.table["^commit|ci"][1],
1430 commands.table["^commit|ci"][1],
1427 'hg qcommit [OPTION]... [FILE]...'),
1431 'hg qcommit [OPTION]... [FILE]...'),
1428 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1432 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1429 "qdelete": (delete, [], 'hg qdelete PATCH'),
1433 "qdelete": (delete, [], 'hg qdelete PATCH'),
1430 "^qimport":
1434 "^qimport":
1431 (qimport,
1435 (qimport,
1432 [('e', 'existing', None, 'import file in patch dir'),
1436 [('e', 'existing', None, 'import file in patch dir'),
1433 ('n', 'name', '', 'patch file name'),
1437 ('n', 'name', '', 'patch file name'),
1434 ('f', 'force', None, 'overwrite existing files')],
1438 ('f', 'force', None, 'overwrite existing files')],
1435 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1439 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1436 "^qinit":
1440 "^qinit":
1437 (init,
1441 (init,
1438 [('c', 'create-repo', None, 'create queue repository')],
1442 [('c', 'create-repo', None, 'create queue repository')],
1439 'hg qinit [-c]'),
1443 'hg qinit [-c]'),
1440 "qnew":
1444 "qnew":
1441 (new,
1445 (new,
1442 [('m', 'message', '', _('use <text> as commit message')),
1446 [('m', 'message', '', _('use <text> as commit message')),
1443 ('l', 'logfile', '', _('read the commit message from <file>')),
1447 ('l', 'logfile', '', _('read the commit message from <file>')),
1444 ('f', 'force', None, 'force')],
1448 ('f', 'force', None, 'force')],
1445 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'),
1449 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'),
1446 "qnext": (next, [], 'hg qnext'),
1450 "qnext": (next, [], 'hg qnext'),
1447 "qprev": (prev, [], 'hg qprev'),
1451 "qprev": (prev, [], 'hg qprev'),
1448 "^qpop":
1452 "^qpop":
1449 (pop,
1453 (pop,
1450 [('a', 'all', None, 'pop all patches'),
1454 [('a', 'all', None, 'pop all patches'),
1451 ('n', 'name', '', 'queue name to pop'),
1455 ('n', 'name', '', 'queue name to pop'),
1452 ('f', 'force', None, 'forget any local changes')],
1456 ('f', 'force', None, 'forget any local changes')],
1453 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1457 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1454 "^qpush":
1458 "^qpush":
1455 (push,
1459 (push,
1456 [('f', 'force', None, 'apply if the patch has rejects'),
1460 [('f', 'force', None, 'apply if the patch has rejects'),
1457 ('l', 'list', None, 'list patch name in commit text'),
1461 ('l', 'list', None, 'list patch name in commit text'),
1458 ('a', 'all', None, 'apply all patches'),
1462 ('a', 'all', None, 'apply all patches'),
1459 ('m', 'merge', None, 'merge from another queue'),
1463 ('m', 'merge', None, 'merge from another queue'),
1460 ('n', 'name', '', 'merge queue name')],
1464 ('n', 'name', '', 'merge queue name')],
1461 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1465 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1462 "^qrefresh":
1466 "^qrefresh":
1463 (refresh,
1467 (refresh,
1464 [('m', 'message', '', _('change commit message with <text>')),
1468 [('m', 'message', '', _('change commit message with <text>')),
1465 ('l', 'logfile', '', _('change commit message with <file> content')),
1469 ('l', 'logfile', '', _('change commit message with <file> content')),
1466 ('s', 'short', None, 'short refresh')],
1470 ('s', 'short', None, 'short refresh')],
1467 'hg qrefresh [-m TEXT] [-l FILE] [-s]'),
1471 'hg qrefresh [-m TEXT] [-l FILE] [-s]'),
1468 "qrestore":
1472 "qrestore":
1469 (restore,
1473 (restore,
1470 [('d', 'delete', None, 'delete save entry'),
1474 [('d', 'delete', None, 'delete save entry'),
1471 ('u', 'update', None, 'update queue working dir')],
1475 ('u', 'update', None, 'update queue working dir')],
1472 'hg qrestore [-d] [-u] REV'),
1476 'hg qrestore [-d] [-u] REV'),
1473 "qsave":
1477 "qsave":
1474 (save,
1478 (save,
1475 [('m', 'message', '', _('use <text> as commit message')),
1479 [('m', 'message', '', _('use <text> as commit message')),
1476 ('l', 'logfile', '', _('read the commit message from <file>')),
1480 ('l', 'logfile', '', _('read the commit message from <file>')),
1477 ('c', 'copy', None, 'copy patch directory'),
1481 ('c', 'copy', None, 'copy patch directory'),
1478 ('n', 'name', '', 'copy directory name'),
1482 ('n', 'name', '', 'copy directory name'),
1479 ('e', 'empty', None, 'clear queue status file'),
1483 ('e', 'empty', None, 'clear queue status file'),
1480 ('f', 'force', None, 'force copy')],
1484 ('f', 'force', None, 'force copy')],
1481 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
1485 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
1482 "qseries":
1486 "qseries":
1483 (series,
1487 (series,
1484 [('m', 'missing', None, 'print patches not in series')],
1488 [('m', 'missing', None, 'print patches not in series')],
1485 'hg qseries [-m]'),
1489 'hg qseries [-m]'),
1486 "^strip":
1490 "^strip":
1487 (strip,
1491 (strip,
1488 [('f', 'force', None, 'force multi-head removal'),
1492 [('f', 'force', None, 'force multi-head removal'),
1489 ('b', 'backup', None, 'bundle unrelated changesets'),
1493 ('b', 'backup', None, 'bundle unrelated changesets'),
1490 ('n', 'nobackup', None, 'no backups')],
1494 ('n', 'nobackup', None, 'no backups')],
1491 'hg strip [-f] [-b] [-n] REV'),
1495 'hg strip [-f] [-b] [-n] REV'),
1492 "qtop": (top, [], 'hg qtop'),
1496 "qtop": (top, [], 'hg qtop'),
1493 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1497 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1494 "qversion": (version, [], 'hg qversion')
1498 "qversion": (version, [], 'hg qversion')
1495 }
1499 }
1496
1500
General Comments 0
You need to be logged in to leave comments. Login now