##// END OF EJS Templates
mq: correct the use of super...
Alexis S. L. Carvalho -
r2742:2f13f8d3 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 commands.setremoteconfig(**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(MqRepo, 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
General Comments 0
You need to be logged in to leave comments. Login now