##// END OF EJS Templates
Add option -e/--edit to qrefresh, to edit the existing header.
Brendan Cully -
r2746:0503eb5c default
parent child Browse files
Show More
@@ -1,1510 +1,1517 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 msg = msg.rstrip()
814 msg = msg.rstrip()
815 if msg:
815 if msg:
816 if comments:
816 if comments:
817 # Remove existing message.
817 # Remove existing message.
818 ci = 0
818 ci = 0
819 for mi in range(len(message)):
819 for mi in range(len(message)):
820 while message[mi] != comments[ci]:
820 while message[mi] != comments[ci]:
821 ci += 1
821 ci += 1
822 del comments[ci]
822 del comments[ci]
823 comments.append(msg)
823 comments.append(msg)
824 if comments:
824 if comments:
825 comments = "\n".join(comments) + '\n\n'
825 comments = "\n".join(comments) + '\n\n'
826 patchf.write(comments)
826 patchf.write(comments)
827
827
828 tip = repo.changelog.tip()
828 tip = repo.changelog.tip()
829 if top == tip:
829 if top == tip:
830 # if the top of our patch queue is also the tip, there is an
830 # if the top of our patch queue is also the tip, there is an
831 # optimization here. We update the dirstate in place and strip
831 # optimization here. We update the dirstate in place and strip
832 # off the tip commit. Then just commit the current directory
832 # off the tip commit. Then just commit the current directory
833 # tree. We can also send repo.commit the list of files
833 # tree. We can also send repo.commit the list of files
834 # changed to speed up the diff
834 # changed to speed up the diff
835 #
835 #
836 # in short mode, we only diff the files included in the
836 # in short mode, we only diff the files included in the
837 # patch already
837 # patch already
838 #
838 #
839 # this should really read:
839 # this should really read:
840 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
840 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
841 # but we do it backwards to take advantage of manifest/chlog
841 # but we do it backwards to take advantage of manifest/chlog
842 # caching against the next repo.changes call
842 # caching against the next repo.changes call
843 #
843 #
844 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
844 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
845 if short:
845 if short:
846 filelist = cc + aa + dd
846 filelist = cc + aa + dd
847 else:
847 else:
848 filelist = None
848 filelist = None
849 (c, a, r, d, u) = repo.changes(None, None, filelist)
849 (c, a, r, d, u) = repo.changes(None, None, filelist)
850
850
851 # we might end up with files that were added between tip and
851 # we might end up with files that were added between tip and
852 # the dirstate parent, but then changed in the local dirstate.
852 # the dirstate parent, but then changed in the local dirstate.
853 # in this case, we want them to only show up in the added section
853 # in this case, we want them to only show up in the added section
854 for x in c:
854 for x in c:
855 if x not in aa:
855 if x not in aa:
856 cc.append(x)
856 cc.append(x)
857 # we might end up with files added by the local dirstate that
857 # we might end up with files added by the local dirstate that
858 # were deleted by the patch. In this case, they should only
858 # were deleted by the patch. In this case, they should only
859 # show up in the changed section.
859 # show up in the changed section.
860 for x in a:
860 for x in a:
861 if x in dd:
861 if x in dd:
862 del dd[dd.index(x)]
862 del dd[dd.index(x)]
863 cc.append(x)
863 cc.append(x)
864 else:
864 else:
865 aa.append(x)
865 aa.append(x)
866 # make sure any files deleted in the local dirstate
866 # make sure any files deleted in the local dirstate
867 # are not in the add or change column of the patch
867 # are not in the add or change column of the patch
868 forget = []
868 forget = []
869 for x in d + r:
869 for x in d + r:
870 if x in aa:
870 if x in aa:
871 del aa[aa.index(x)]
871 del aa[aa.index(x)]
872 forget.append(x)
872 forget.append(x)
873 continue
873 continue
874 elif x in cc:
874 elif x in cc:
875 del cc[cc.index(x)]
875 del cc[cc.index(x)]
876 dd.append(x)
876 dd.append(x)
877
877
878 c = list(util.unique(cc))
878 c = list(util.unique(cc))
879 r = list(util.unique(dd))
879 r = list(util.unique(dd))
880 a = list(util.unique(aa))
880 a = list(util.unique(aa))
881 filelist = list(util.unique(c + r + a ))
881 filelist = list(util.unique(c + r + a ))
882 commands.dodiff(patchf, self.ui, repo, patchparent, None,
882 commands.dodiff(patchf, self.ui, repo, patchparent, None,
883 filelist, changes=(c, a, r, [], u))
883 filelist, changes=(c, a, r, [], u))
884 patchf.close()
884 patchf.close()
885
885
886 changes = repo.changelog.read(tip)
886 changes = repo.changelog.read(tip)
887 repo.dirstate.setparents(*cparents)
887 repo.dirstate.setparents(*cparents)
888 repo.dirstate.update(a, 'a')
888 repo.dirstate.update(a, 'a')
889 repo.dirstate.update(r, 'r')
889 repo.dirstate.update(r, 'r')
890 repo.dirstate.update(c, 'n')
890 repo.dirstate.update(c, 'n')
891 repo.dirstate.forget(forget)
891 repo.dirstate.forget(forget)
892
892
893 if not msg:
893 if not msg:
894 if not message:
894 if not message:
895 message = "patch queue: %s\n" % patch
895 message = "patch queue: %s\n" % patch
896 else:
896 else:
897 message = "\n".join(message)
897 message = "\n".join(message)
898 else:
898 else:
899 message = msg
899 message = msg
900
900
901 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
901 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
902 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
902 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
903 self.applied[-1] = revlog.hex(n) + ':' + patch
903 self.applied[-1] = revlog.hex(n) + ':' + patch
904 self.applied_dirty = 1
904 self.applied_dirty = 1
905 else:
905 else:
906 commands.dodiff(patchf, self.ui, repo, patchparent, None)
906 commands.dodiff(patchf, self.ui, repo, patchparent, None)
907 patchf.close()
907 patchf.close()
908 self.pop(repo, force=True, wlock=wlock)
908 self.pop(repo, force=True, wlock=wlock)
909 self.push(repo, force=True, wlock=wlock)
909 self.push(repo, force=True, wlock=wlock)
910
910
911 def init(self, repo, create=False):
911 def init(self, repo, create=False):
912 if os.path.isdir(self.path):
912 if os.path.isdir(self.path):
913 raise util.Abort(_("patch queue directory already exists"))
913 raise util.Abort(_("patch queue directory already exists"))
914 os.mkdir(self.path)
914 os.mkdir(self.path)
915 if create:
915 if create:
916 return self.qrepo(create=True)
916 return self.qrepo(create=True)
917
917
918 def unapplied(self, repo, patch=None):
918 def unapplied(self, repo, patch=None):
919 if patch and patch not in self.series:
919 if patch and patch not in self.series:
920 raise util.Abort(_("patch %s is not in series file") % patch)
920 raise util.Abort(_("patch %s is not in series file") % patch)
921 if not patch:
921 if not patch:
922 start = self.series_end()
922 start = self.series_end()
923 else:
923 else:
924 start = self.series.index(patch) + 1
924 start = self.series.index(patch) + 1
925 for p in self.series[start:]:
925 for p in self.series[start:]:
926 if self.ui.verbose:
926 if self.ui.verbose:
927 self.ui.write("%d " % self.series.index(p))
927 self.ui.write("%d " % self.series.index(p))
928 self.ui.write("%s\n" % p)
928 self.ui.write("%s\n" % p)
929
929
930 def qseries(self, repo, missing=None):
930 def qseries(self, repo, missing=None):
931 start = self.series_end()
931 start = self.series_end()
932 if not missing:
932 if not missing:
933 for p in self.series[:start]:
933 for p in self.series[:start]:
934 if self.ui.verbose:
934 if self.ui.verbose:
935 self.ui.write("%d A " % self.series.index(p))
935 self.ui.write("%d A " % self.series.index(p))
936 self.ui.write("%s\n" % p)
936 self.ui.write("%s\n" % p)
937 for p in self.series[start:]:
937 for p in self.series[start:]:
938 if self.ui.verbose:
938 if self.ui.verbose:
939 self.ui.write("%d U " % self.series.index(p))
939 self.ui.write("%d U " % self.series.index(p))
940 self.ui.write("%s\n" % p)
940 self.ui.write("%s\n" % p)
941 else:
941 else:
942 list = []
942 list = []
943 for root, dirs, files in os.walk(self.path):
943 for root, dirs, files in os.walk(self.path):
944 d = root[len(self.path) + 1:]
944 d = root[len(self.path) + 1:]
945 for f in files:
945 for f in files:
946 fl = os.path.join(d, f)
946 fl = os.path.join(d, f)
947 if (fl not in self.series and
947 if (fl not in self.series and
948 fl not in (self.status_path, self.series_path)
948 fl not in (self.status_path, self.series_path)
949 and not fl.startswith('.')):
949 and not fl.startswith('.')):
950 list.append(fl)
950 list.append(fl)
951 list.sort()
951 list.sort()
952 if list:
952 if list:
953 for x in list:
953 for x in list:
954 if self.ui.verbose:
954 if self.ui.verbose:
955 self.ui.write("D ")
955 self.ui.write("D ")
956 self.ui.write("%s\n" % x)
956 self.ui.write("%s\n" % x)
957
957
958 def issaveline(self, l):
958 def issaveline(self, l):
959 name = l.split(':')[1]
959 name = l.split(':')[1]
960 if name == '.hg.patches.save.line':
960 if name == '.hg.patches.save.line':
961 return True
961 return True
962
962
963 def qrepo(self, create=False):
963 def qrepo(self, create=False):
964 if create or os.path.isdir(os.path.join(self.path, ".hg")):
964 if create or os.path.isdir(os.path.join(self.path, ".hg")):
965 return hg.repository(self.ui, path=self.path, create=create)
965 return hg.repository(self.ui, path=self.path, create=create)
966
966
967 def restore(self, repo, rev, delete=None, qupdate=None):
967 def restore(self, repo, rev, delete=None, qupdate=None):
968 c = repo.changelog.read(rev)
968 c = repo.changelog.read(rev)
969 desc = c[4].strip()
969 desc = c[4].strip()
970 lines = desc.splitlines()
970 lines = desc.splitlines()
971 i = 0
971 i = 0
972 datastart = None
972 datastart = None
973 series = []
973 series = []
974 applied = []
974 applied = []
975 qpp = None
975 qpp = None
976 for i in xrange(0, len(lines)):
976 for i in xrange(0, len(lines)):
977 if lines[i] == 'Patch Data:':
977 if lines[i] == 'Patch Data:':
978 datastart = i + 1
978 datastart = i + 1
979 elif lines[i].startswith('Dirstate:'):
979 elif lines[i].startswith('Dirstate:'):
980 l = lines[i].rstrip()
980 l = lines[i].rstrip()
981 l = l[10:].split(' ')
981 l = l[10:].split(' ')
982 qpp = [ hg.bin(x) for x in l ]
982 qpp = [ hg.bin(x) for x in l ]
983 elif datastart != None:
983 elif datastart != None:
984 l = lines[i].rstrip()
984 l = lines[i].rstrip()
985 index = l.index(':')
985 index = l.index(':')
986 id = l[:index]
986 id = l[:index]
987 file = l[index + 1:]
987 file = l[index + 1:]
988 if id:
988 if id:
989 applied.append(l)
989 applied.append(l)
990 series.append(file)
990 series.append(file)
991 if datastart == None:
991 if datastart == None:
992 self.ui.warn("No saved patch data found\n")
992 self.ui.warn("No saved patch data found\n")
993 return 1
993 return 1
994 self.ui.warn("restoring status: %s\n" % lines[0])
994 self.ui.warn("restoring status: %s\n" % lines[0])
995 self.full_series = series
995 self.full_series = series
996 self.applied = applied
996 self.applied = applied
997 self.read_series(self.full_series)
997 self.read_series(self.full_series)
998 self.series_dirty = 1
998 self.series_dirty = 1
999 self.applied_dirty = 1
999 self.applied_dirty = 1
1000 heads = repo.changelog.heads()
1000 heads = repo.changelog.heads()
1001 if delete:
1001 if delete:
1002 if rev not in heads:
1002 if rev not in heads:
1003 self.ui.warn("save entry has children, leaving it alone\n")
1003 self.ui.warn("save entry has children, leaving it alone\n")
1004 else:
1004 else:
1005 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1005 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1006 pp = repo.dirstate.parents()
1006 pp = repo.dirstate.parents()
1007 if rev in pp:
1007 if rev in pp:
1008 update = True
1008 update = True
1009 else:
1009 else:
1010 update = False
1010 update = False
1011 self.strip(repo, rev, update=update, backup='strip')
1011 self.strip(repo, rev, update=update, backup='strip')
1012 if qpp:
1012 if qpp:
1013 self.ui.warn("saved queue repository parents: %s %s\n" %
1013 self.ui.warn("saved queue repository parents: %s %s\n" %
1014 (hg.short(qpp[0]), hg.short(qpp[1])))
1014 (hg.short(qpp[0]), hg.short(qpp[1])))
1015 if qupdate:
1015 if qupdate:
1016 print "queue directory updating"
1016 print "queue directory updating"
1017 r = self.qrepo()
1017 r = self.qrepo()
1018 if not r:
1018 if not r:
1019 self.ui.warn("Unable to load queue repository\n")
1019 self.ui.warn("Unable to load queue repository\n")
1020 return 1
1020 return 1
1021 r.update(qpp[0], allow=False, force=True)
1021 r.update(qpp[0], allow=False, force=True)
1022
1022
1023 def save(self, repo, msg=None):
1023 def save(self, repo, msg=None):
1024 if len(self.applied) == 0:
1024 if len(self.applied) == 0:
1025 self.ui.warn("save: no patches applied, exiting\n")
1025 self.ui.warn("save: no patches applied, exiting\n")
1026 return 1
1026 return 1
1027 if self.issaveline(self.applied[-1]):
1027 if self.issaveline(self.applied[-1]):
1028 self.ui.warn("status is already saved\n")
1028 self.ui.warn("status is already saved\n")
1029 return 1
1029 return 1
1030
1030
1031 ar = [ ':' + x for x in self.full_series ]
1031 ar = [ ':' + x for x in self.full_series ]
1032 if not msg:
1032 if not msg:
1033 msg = "hg patches saved state"
1033 msg = "hg patches saved state"
1034 else:
1034 else:
1035 msg = "hg patches: " + msg.rstrip('\r\n')
1035 msg = "hg patches: " + msg.rstrip('\r\n')
1036 r = self.qrepo()
1036 r = self.qrepo()
1037 if r:
1037 if r:
1038 pp = r.dirstate.parents()
1038 pp = r.dirstate.parents()
1039 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1039 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1040 msg += "\n\nPatch Data:\n"
1040 msg += "\n\nPatch Data:\n"
1041 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
1041 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
1042 + '\n' or "")
1042 + '\n' or "")
1043 n = repo.commit(None, text, user=None, force=1)
1043 n = repo.commit(None, text, user=None, force=1)
1044 if not n:
1044 if not n:
1045 self.ui.warn("repo commit failed\n")
1045 self.ui.warn("repo commit failed\n")
1046 return 1
1046 return 1
1047 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
1047 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
1048 self.applied_dirty = 1
1048 self.applied_dirty = 1
1049
1049
1050 def full_series_end(self):
1050 def full_series_end(self):
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 end = self.find_series(p)
1053 end = self.find_series(p)
1054 if end == None:
1054 if end == None:
1055 return len(self.full_series)
1055 return len(self.full_series)
1056 return end + 1
1056 return end + 1
1057 return 0
1057 return 0
1058
1058
1059 def series_end(self):
1059 def series_end(self):
1060 end = 0
1060 end = 0
1061 if len(self.applied) > 0:
1061 if len(self.applied) > 0:
1062 (top, p) = self.applied[-1].split(':')
1062 (top, p) = self.applied[-1].split(':')
1063 try:
1063 try:
1064 end = self.series.index(p)
1064 end = self.series.index(p)
1065 except ValueError:
1065 except ValueError:
1066 return 0
1066 return 0
1067 return end + 1
1067 return end + 1
1068 return end
1068 return end
1069
1069
1070 def qapplied(self, repo, patch=None):
1070 def qapplied(self, repo, patch=None):
1071 if patch and patch not in self.series:
1071 if patch and patch not in self.series:
1072 raise util.Abort(_("patch %s is not in series file") % patch)
1072 raise util.Abort(_("patch %s is not in series file") % patch)
1073 if not patch:
1073 if not patch:
1074 end = len(self.applied)
1074 end = len(self.applied)
1075 else:
1075 else:
1076 end = self.series.index(patch) + 1
1076 end = self.series.index(patch) + 1
1077 for x in xrange(end):
1077 for x in xrange(end):
1078 p = self.appliedname(x)
1078 p = self.appliedname(x)
1079 self.ui.write("%s\n" % p)
1079 self.ui.write("%s\n" % p)
1080
1080
1081 def appliedname(self, index):
1081 def appliedname(self, index):
1082 p = self.applied[index]
1082 p = self.applied[index]
1083 pname = p.split(':')[1]
1083 pname = p.split(':')[1]
1084 if not self.ui.verbose:
1084 if not self.ui.verbose:
1085 p = pname
1085 p = pname
1086 else:
1086 else:
1087 p = str(self.series.index(pname)) + " " + p
1087 p = str(self.series.index(pname)) + " " + p
1088 return p
1088 return p
1089
1089
1090 def top(self, repo):
1090 def top(self, repo):
1091 if len(self.applied):
1091 if len(self.applied):
1092 p = self.appliedname(-1)
1092 p = self.appliedname(-1)
1093 self.ui.write(p + '\n')
1093 self.ui.write(p + '\n')
1094 else:
1094 else:
1095 self.ui.write("No patches applied\n")
1095 self.ui.write("No patches applied\n")
1096
1096
1097 def next(self, repo):
1097 def next(self, repo):
1098 end = self.series_end()
1098 end = self.series_end()
1099 if end == len(self.series):
1099 if end == len(self.series):
1100 self.ui.write("All patches applied\n")
1100 self.ui.write("All patches applied\n")
1101 else:
1101 else:
1102 p = self.series[end]
1102 p = self.series[end]
1103 if self.ui.verbose:
1103 if self.ui.verbose:
1104 self.ui.write("%d " % self.series.index(p))
1104 self.ui.write("%d " % self.series.index(p))
1105 self.ui.write(p + '\n')
1105 self.ui.write(p + '\n')
1106
1106
1107 def prev(self, repo):
1107 def prev(self, repo):
1108 if len(self.applied) > 1:
1108 if len(self.applied) > 1:
1109 p = self.appliedname(-2)
1109 p = self.appliedname(-2)
1110 self.ui.write(p + '\n')
1110 self.ui.write(p + '\n')
1111 elif len(self.applied) == 1:
1111 elif len(self.applied) == 1:
1112 self.ui.write("Only one patch applied\n")
1112 self.ui.write("Only one patch applied\n")
1113 else:
1113 else:
1114 self.ui.write("No patches applied\n")
1114 self.ui.write("No patches applied\n")
1115
1115
1116 def qimport(self, repo, files, patch=None, existing=None, force=None):
1116 def qimport(self, repo, files, patch=None, existing=None, force=None):
1117 if len(files) > 1 and patch:
1117 if len(files) > 1 and patch:
1118 raise util.Abort(_('option "-n" not valid when importing multiple '
1118 raise util.Abort(_('option "-n" not valid when importing multiple '
1119 'files'))
1119 'files'))
1120 i = 0
1120 i = 0
1121 added = []
1121 added = []
1122 for filename in files:
1122 for filename in files:
1123 if existing:
1123 if existing:
1124 if not patch:
1124 if not patch:
1125 patch = filename
1125 patch = filename
1126 if not os.path.isfile(os.path.join(self.path, patch)):
1126 if not os.path.isfile(os.path.join(self.path, patch)):
1127 raise util.Abort(_("patch %s does not exist") % patch)
1127 raise util.Abort(_("patch %s does not exist") % patch)
1128 else:
1128 else:
1129 try:
1129 try:
1130 text = file(filename).read()
1130 text = file(filename).read()
1131 except IOError:
1131 except IOError:
1132 raise util.Abort(_("unable to read %s") % patch)
1132 raise util.Abort(_("unable to read %s") % patch)
1133 if not patch:
1133 if not patch:
1134 patch = os.path.split(filename)[1]
1134 patch = os.path.split(filename)[1]
1135 if not force and os.path.exists(os.path.join(self.path, patch)):
1135 if not force and os.path.exists(os.path.join(self.path, patch)):
1136 raise util.Abort(_('patch "%s" already exists') % patch)
1136 raise util.Abort(_('patch "%s" already exists') % patch)
1137 patchf = self.opener(patch, "w")
1137 patchf = self.opener(patch, "w")
1138 patchf.write(text)
1138 patchf.write(text)
1139 if patch in self.series:
1139 if patch in self.series:
1140 raise util.Abort(_('patch %s is already in the series file')
1140 raise util.Abort(_('patch %s is already in the series file')
1141 % patch)
1141 % patch)
1142 index = self.full_series_end() + i
1142 index = self.full_series_end() + i
1143 self.full_series[index:index] = [patch]
1143 self.full_series[index:index] = [patch]
1144 self.read_series(self.full_series)
1144 self.read_series(self.full_series)
1145 self.ui.warn("adding %s to series file\n" % patch)
1145 self.ui.warn("adding %s to series file\n" % patch)
1146 i += 1
1146 i += 1
1147 added.append(patch)
1147 added.append(patch)
1148 patch = None
1148 patch = None
1149 self.series_dirty = 1
1149 self.series_dirty = 1
1150 qrepo = self.qrepo()
1150 qrepo = self.qrepo()
1151 if qrepo:
1151 if qrepo:
1152 qrepo.add(added)
1152 qrepo.add(added)
1153
1153
1154 def delete(ui, repo, patch, **opts):
1154 def delete(ui, repo, patch, **opts):
1155 """remove a patch from the series file"""
1155 """remove a patch from the series file"""
1156 q = repo.mq
1156 q = repo.mq
1157 q.delete(repo, patch)
1157 q.delete(repo, patch)
1158 q.save_dirty()
1158 q.save_dirty()
1159 return 0
1159 return 0
1160
1160
1161 def applied(ui, repo, patch=None, **opts):
1161 def applied(ui, repo, patch=None, **opts):
1162 """print the patches already applied"""
1162 """print the patches already applied"""
1163 repo.mq.qapplied(repo, patch)
1163 repo.mq.qapplied(repo, patch)
1164 return 0
1164 return 0
1165
1165
1166 def unapplied(ui, repo, patch=None, **opts):
1166 def unapplied(ui, repo, patch=None, **opts):
1167 """print the patches not yet applied"""
1167 """print the patches not yet applied"""
1168 repo.mq.unapplied(repo, patch)
1168 repo.mq.unapplied(repo, patch)
1169 return 0
1169 return 0
1170
1170
1171 def qimport(ui, repo, *filename, **opts):
1171 def qimport(ui, repo, *filename, **opts):
1172 """import a patch"""
1172 """import a patch"""
1173 q = repo.mq
1173 q = repo.mq
1174 q.qimport(repo, filename, patch=opts['name'],
1174 q.qimport(repo, filename, patch=opts['name'],
1175 existing=opts['existing'], force=opts['force'])
1175 existing=opts['existing'], force=opts['force'])
1176 q.save_dirty()
1176 q.save_dirty()
1177 return 0
1177 return 0
1178
1178
1179 def init(ui, repo, **opts):
1179 def init(ui, repo, **opts):
1180 """init a new queue repository"""
1180 """init a new queue repository"""
1181 q = repo.mq
1181 q = repo.mq
1182 r = q.init(repo, create=opts['create_repo'])
1182 r = q.init(repo, create=opts['create_repo'])
1183 q.save_dirty()
1183 q.save_dirty()
1184 if r:
1184 if r:
1185 fp = r.wopener('.hgignore', 'w')
1185 fp = r.wopener('.hgignore', 'w')
1186 print >> fp, 'syntax: glob'
1186 print >> fp, 'syntax: glob'
1187 print >> fp, 'status'
1187 print >> fp, 'status'
1188 fp.close()
1188 fp.close()
1189 r.wopener('series', 'w').close()
1189 r.wopener('series', 'w').close()
1190 r.add(['.hgignore', 'series'])
1190 r.add(['.hgignore', 'series'])
1191 return 0
1191 return 0
1192
1192
1193 def clone(ui, source, dest=None, **opts):
1193 def clone(ui, source, dest=None, **opts):
1194 '''clone main and patch repository at same time
1194 '''clone main and patch repository at same time
1195
1195
1196 If source is local, destination will have no patches applied. If
1196 If source is local, destination will have no patches applied. If
1197 source is remote, this command can not check if patches are
1197 source is remote, this command can not check if patches are
1198 applied in source, so cannot guarantee that patches are not
1198 applied in source, so cannot guarantee that patches are not
1199 applied in destination. If you clone remote repository, be sure
1199 applied in destination. If you clone remote repository, be sure
1200 before that it has no patches applied.
1200 before that it has no patches applied.
1201
1201
1202 Source patch repository is looked for in <src>/.hg/patches by
1202 Source patch repository is looked for in <src>/.hg/patches by
1203 default. Use -p <url> to change.
1203 default. Use -p <url> to change.
1204 '''
1204 '''
1205 commands.setremoteconfig(**opts)
1205 commands.setremoteconfig(**opts)
1206 if dest is None:
1206 if dest is None:
1207 dest = hg.defaultdest(source)
1207 dest = hg.defaultdest(source)
1208 sr = hg.repository(ui, ui.expandpath(source))
1208 sr = hg.repository(ui, ui.expandpath(source))
1209 qbase, destrev = None, None
1209 qbase, destrev = None, None
1210 if sr.local():
1210 if sr.local():
1211 reposetup(ui, sr)
1211 reposetup(ui, sr)
1212 if sr.mq.applied:
1212 if sr.mq.applied:
1213 qbase = revlog.bin(sr.mq.applied[0].split(':')[0])
1213 qbase = revlog.bin(sr.mq.applied[0].split(':')[0])
1214 if not hg.islocal(dest):
1214 if not hg.islocal(dest):
1215 destrev = sr.parents(qbase)[0]
1215 destrev = sr.parents(qbase)[0]
1216 ui.note(_('cloning main repo\n'))
1216 ui.note(_('cloning main repo\n'))
1217 sr, dr = hg.clone(ui, sr, dest,
1217 sr, dr = hg.clone(ui, sr, dest,
1218 pull=opts['pull'],
1218 pull=opts['pull'],
1219 rev=destrev,
1219 rev=destrev,
1220 update=False,
1220 update=False,
1221 stream=opts['uncompressed'])
1221 stream=opts['uncompressed'])
1222 ui.note(_('cloning patch repo\n'))
1222 ui.note(_('cloning patch repo\n'))
1223 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1223 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1224 dr.url() + '/.hg/patches',
1224 dr.url() + '/.hg/patches',
1225 pull=opts['pull'],
1225 pull=opts['pull'],
1226 update=not opts['noupdate'],
1226 update=not opts['noupdate'],
1227 stream=opts['uncompressed'])
1227 stream=opts['uncompressed'])
1228 if dr.local():
1228 if dr.local():
1229 if qbase:
1229 if qbase:
1230 ui.note(_('stripping applied patches from destination repo\n'))
1230 ui.note(_('stripping applied patches from destination repo\n'))
1231 reposetup(ui, dr)
1231 reposetup(ui, dr)
1232 dr.mq.strip(dr, qbase, update=False, backup=None)
1232 dr.mq.strip(dr, qbase, update=False, backup=None)
1233 if not opts['noupdate']:
1233 if not opts['noupdate']:
1234 ui.note(_('updating destination repo\n'))
1234 ui.note(_('updating destination repo\n'))
1235 dr.update(dr.changelog.tip())
1235 dr.update(dr.changelog.tip())
1236
1236
1237 def commit(ui, repo, *pats, **opts):
1237 def commit(ui, repo, *pats, **opts):
1238 """commit changes in the queue repository"""
1238 """commit changes in the queue repository"""
1239 q = repo.mq
1239 q = repo.mq
1240 r = q.qrepo()
1240 r = q.qrepo()
1241 if not r: raise util.Abort('no queue repository')
1241 if not r: raise util.Abort('no queue repository')
1242 commands.commit(r.ui, r, *pats, **opts)
1242 commands.commit(r.ui, r, *pats, **opts)
1243
1243
1244 def series(ui, repo, **opts):
1244 def series(ui, repo, **opts):
1245 """print the entire series file"""
1245 """print the entire series file"""
1246 repo.mq.qseries(repo, missing=opts['missing'])
1246 repo.mq.qseries(repo, missing=opts['missing'])
1247 return 0
1247 return 0
1248
1248
1249 def top(ui, repo, **opts):
1249 def top(ui, repo, **opts):
1250 """print the name of the current patch"""
1250 """print the name of the current patch"""
1251 repo.mq.top(repo)
1251 repo.mq.top(repo)
1252 return 0
1252 return 0
1253
1253
1254 def next(ui, repo, **opts):
1254 def next(ui, repo, **opts):
1255 """print the name of the next patch"""
1255 """print the name of the next patch"""
1256 repo.mq.next(repo)
1256 repo.mq.next(repo)
1257 return 0
1257 return 0
1258
1258
1259 def prev(ui, repo, **opts):
1259 def prev(ui, repo, **opts):
1260 """print the name of the previous patch"""
1260 """print the name of the previous patch"""
1261 repo.mq.prev(repo)
1261 repo.mq.prev(repo)
1262 return 0
1262 return 0
1263
1263
1264 def new(ui, repo, patch, **opts):
1264 def new(ui, repo, patch, **opts):
1265 """create a new patch"""
1265 """create a new patch"""
1266 q = repo.mq
1266 q = repo.mq
1267 message=commands.logmessage(**opts)
1267 message=commands.logmessage(**opts)
1268 q.new(repo, patch, msg=message, force=opts['force'])
1268 q.new(repo, patch, msg=message, force=opts['force'])
1269 q.save_dirty()
1269 q.save_dirty()
1270 return 0
1270 return 0
1271
1271
1272 def refresh(ui, repo, **opts):
1272 def refresh(ui, repo, **opts):
1273 """update the current patch"""
1273 """update the current patch"""
1274 q = repo.mq
1274 q = repo.mq
1275 message=commands.logmessage(**opts)
1275 message=commands.logmessage(**opts)
1276 if opts['edit']:
1277 if message:
1278 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1279 patch = q.applied[-1].split(':')[1]
1280 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1281 message = ui.edit('\n'.join(message), user or ui.username())
1276 q.refresh(repo, msg=message, short=opts['short'])
1282 q.refresh(repo, msg=message, short=opts['short'])
1277 q.save_dirty()
1283 q.save_dirty()
1278 return 0
1284 return 0
1279
1285
1280 def diff(ui, repo, *files, **opts):
1286 def diff(ui, repo, *files, **opts):
1281 """diff of the current patch"""
1287 """diff of the current patch"""
1282 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1288 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1283 repo.mq.diff(repo, list(files))
1289 repo.mq.diff(repo, list(files))
1284 return 0
1290 return 0
1285
1291
1286 def lastsavename(path):
1292 def lastsavename(path):
1287 (dir, base) = os.path.split(path)
1293 (dir, base) = os.path.split(path)
1288 names = os.listdir(dir)
1294 names = os.listdir(dir)
1289 namere = re.compile("%s.([0-9]+)" % base)
1295 namere = re.compile("%s.([0-9]+)" % base)
1290 max = None
1296 max = None
1291 maxname = None
1297 maxname = None
1292 for f in names:
1298 for f in names:
1293 m = namere.match(f)
1299 m = namere.match(f)
1294 if m:
1300 if m:
1295 index = int(m.group(1))
1301 index = int(m.group(1))
1296 if max == None or index > max:
1302 if max == None or index > max:
1297 max = index
1303 max = index
1298 maxname = f
1304 maxname = f
1299 if maxname:
1305 if maxname:
1300 return (os.path.join(dir, maxname), max)
1306 return (os.path.join(dir, maxname), max)
1301 return (None, None)
1307 return (None, None)
1302
1308
1303 def savename(path):
1309 def savename(path):
1304 (last, index) = lastsavename(path)
1310 (last, index) = lastsavename(path)
1305 if last is None:
1311 if last is None:
1306 index = 0
1312 index = 0
1307 newpath = path + ".%d" % (index + 1)
1313 newpath = path + ".%d" % (index + 1)
1308 return newpath
1314 return newpath
1309
1315
1310 def push(ui, repo, patch=None, **opts):
1316 def push(ui, repo, patch=None, **opts):
1311 """push the next patch onto the stack"""
1317 """push the next patch onto the stack"""
1312 q = repo.mq
1318 q = repo.mq
1313 mergeq = None
1319 mergeq = None
1314
1320
1315 if opts['all']:
1321 if opts['all']:
1316 patch = q.series[-1]
1322 patch = q.series[-1]
1317 if opts['merge']:
1323 if opts['merge']:
1318 if opts['name']:
1324 if opts['name']:
1319 newpath = opts['name']
1325 newpath = opts['name']
1320 else:
1326 else:
1321 newpath, i = lastsavename(q.path)
1327 newpath, i = lastsavename(q.path)
1322 if not newpath:
1328 if not newpath:
1323 ui.warn("no saved queues found, please use -n\n")
1329 ui.warn("no saved queues found, please use -n\n")
1324 return 1
1330 return 1
1325 mergeq = queue(ui, repo.join(""), newpath)
1331 mergeq = queue(ui, repo.join(""), newpath)
1326 ui.warn("merging with queue at: %s\n" % mergeq.path)
1332 ui.warn("merging with queue at: %s\n" % mergeq.path)
1327 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1333 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1328 mergeq=mergeq)
1334 mergeq=mergeq)
1329 q.save_dirty()
1335 q.save_dirty()
1330 return ret
1336 return ret
1331
1337
1332 def pop(ui, repo, patch=None, **opts):
1338 def pop(ui, repo, patch=None, **opts):
1333 """pop the current patch off the stack"""
1339 """pop the current patch off the stack"""
1334 localupdate = True
1340 localupdate = True
1335 if opts['name']:
1341 if opts['name']:
1336 q = queue(ui, repo.join(""), repo.join(opts['name']))
1342 q = queue(ui, repo.join(""), repo.join(opts['name']))
1337 ui.warn('using patch queue: %s\n' % q.path)
1343 ui.warn('using patch queue: %s\n' % q.path)
1338 localupdate = False
1344 localupdate = False
1339 else:
1345 else:
1340 q = repo.mq
1346 q = repo.mq
1341 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1347 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1342 q.save_dirty()
1348 q.save_dirty()
1343 return 0
1349 return 0
1344
1350
1345 def restore(ui, repo, rev, **opts):
1351 def restore(ui, repo, rev, **opts):
1346 """restore the queue state saved by a rev"""
1352 """restore the queue state saved by a rev"""
1347 rev = repo.lookup(rev)
1353 rev = repo.lookup(rev)
1348 q = repo.mq
1354 q = repo.mq
1349 q.restore(repo, rev, delete=opts['delete'],
1355 q.restore(repo, rev, delete=opts['delete'],
1350 qupdate=opts['update'])
1356 qupdate=opts['update'])
1351 q.save_dirty()
1357 q.save_dirty()
1352 return 0
1358 return 0
1353
1359
1354 def save(ui, repo, **opts):
1360 def save(ui, repo, **opts):
1355 """save current queue state"""
1361 """save current queue state"""
1356 q = repo.mq
1362 q = repo.mq
1357 message=commands.logmessage(**opts)
1363 message=commands.logmessage(**opts)
1358 ret = q.save(repo, msg=message)
1364 ret = q.save(repo, msg=message)
1359 if ret:
1365 if ret:
1360 return ret
1366 return ret
1361 q.save_dirty()
1367 q.save_dirty()
1362 if opts['copy']:
1368 if opts['copy']:
1363 path = q.path
1369 path = q.path
1364 if opts['name']:
1370 if opts['name']:
1365 newpath = os.path.join(q.basepath, opts['name'])
1371 newpath = os.path.join(q.basepath, opts['name'])
1366 if os.path.exists(newpath):
1372 if os.path.exists(newpath):
1367 if not os.path.isdir(newpath):
1373 if not os.path.isdir(newpath):
1368 raise util.Abort(_('destination %s exists and is not '
1374 raise util.Abort(_('destination %s exists and is not '
1369 'a directory') % newpath)
1375 'a directory') % newpath)
1370 if not opts['force']:
1376 if not opts['force']:
1371 raise util.Abort(_('destination %s exists, '
1377 raise util.Abort(_('destination %s exists, '
1372 'use -f to force') % newpath)
1378 'use -f to force') % newpath)
1373 else:
1379 else:
1374 newpath = savename(path)
1380 newpath = savename(path)
1375 ui.warn("copy %s to %s\n" % (path, newpath))
1381 ui.warn("copy %s to %s\n" % (path, newpath))
1376 util.copyfiles(path, newpath)
1382 util.copyfiles(path, newpath)
1377 if opts['empty']:
1383 if opts['empty']:
1378 try:
1384 try:
1379 os.unlink(os.path.join(q.path, q.status_path))
1385 os.unlink(os.path.join(q.path, q.status_path))
1380 except:
1386 except:
1381 pass
1387 pass
1382 return 0
1388 return 0
1383
1389
1384 def strip(ui, repo, rev, **opts):
1390 def strip(ui, repo, rev, **opts):
1385 """strip a revision and all later revs on the same branch"""
1391 """strip a revision and all later revs on the same branch"""
1386 rev = repo.lookup(rev)
1392 rev = repo.lookup(rev)
1387 backup = 'all'
1393 backup = 'all'
1388 if opts['backup']:
1394 if opts['backup']:
1389 backup = 'strip'
1395 backup = 'strip'
1390 elif opts['nobackup']:
1396 elif opts['nobackup']:
1391 backup = 'none'
1397 backup = 'none'
1392 repo.mq.strip(repo, rev, backup=backup)
1398 repo.mq.strip(repo, rev, backup=backup)
1393 return 0
1399 return 0
1394
1400
1395 def version(ui, q=None):
1401 def version(ui, q=None):
1396 """print the version number"""
1402 """print the version number"""
1397 ui.write("mq version %s\n" % versionstr)
1403 ui.write("mq version %s\n" % versionstr)
1398 return 0
1404 return 0
1399
1405
1400 def reposetup(ui, repo):
1406 def reposetup(ui, repo):
1401 class MqRepo(repo.__class__):
1407 class MqRepo(repo.__class__):
1402 def tags(self):
1408 def tags(self):
1403 if self.tagscache:
1409 if self.tagscache:
1404 return self.tagscache
1410 return self.tagscache
1405
1411
1406 tagscache = super(MqRepo, self).tags()
1412 tagscache = super(MqRepo, self).tags()
1407
1413
1408 q = self.mq
1414 q = self.mq
1409 if not q.applied:
1415 if not q.applied:
1410 return tagscache
1416 return tagscache
1411
1417
1412 mqtags = [patch.split(':') for patch in q.applied]
1418 mqtags = [patch.split(':') for patch in q.applied]
1413 mqtags.append((mqtags[-1][0], 'qtip'))
1419 mqtags.append((mqtags[-1][0], 'qtip'))
1414 mqtags.append((mqtags[0][0], 'qbase'))
1420 mqtags.append((mqtags[0][0], 'qbase'))
1415 for patch in mqtags:
1421 for patch in mqtags:
1416 if patch[1] in tagscache:
1422 if patch[1] in tagscache:
1417 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
1423 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
1418 else:
1424 else:
1419 tagscache[patch[1]] = revlog.bin(patch[0])
1425 tagscache[patch[1]] = revlog.bin(patch[0])
1420
1426
1421 return tagscache
1427 return tagscache
1422
1428
1423 repo.__class__ = MqRepo
1429 repo.__class__ = MqRepo
1424 repo.mq = queue(ui, repo.join(""))
1430 repo.mq = queue(ui, repo.join(""))
1425
1431
1426 cmdtable = {
1432 cmdtable = {
1427 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1433 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1428 "qclone": (clone,
1434 "qclone": (clone,
1429 [('', 'pull', None, _('use pull protocol to copy metadata')),
1435 [('', 'pull', None, _('use pull protocol to copy metadata')),
1430 ('U', 'noupdate', None, _('do not update the new working directories')),
1436 ('U', 'noupdate', None, _('do not update the new working directories')),
1431 ('', 'uncompressed', None,
1437 ('', 'uncompressed', None,
1432 _('use uncompressed transfer (fast over LAN)')),
1438 _('use uncompressed transfer (fast over LAN)')),
1433 ('e', 'ssh', '', _('specify ssh command to use')),
1439 ('e', 'ssh', '', _('specify ssh command to use')),
1434 ('p', 'patches', '', _('location of source patch repo')),
1440 ('p', 'patches', '', _('location of source patch repo')),
1435 ('', 'remotecmd', '',
1441 ('', 'remotecmd', '',
1436 _('specify hg command to run on the remote side'))],
1442 _('specify hg command to run on the remote side'))],
1437 'hg qclone [OPTION]... SOURCE [DEST]'),
1443 'hg qclone [OPTION]... SOURCE [DEST]'),
1438 "qcommit|qci":
1444 "qcommit|qci":
1439 (commit,
1445 (commit,
1440 commands.table["^commit|ci"][1],
1446 commands.table["^commit|ci"][1],
1441 'hg qcommit [OPTION]... [FILE]...'),
1447 'hg qcommit [OPTION]... [FILE]...'),
1442 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1448 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1443 "qdelete": (delete, [], 'hg qdelete PATCH'),
1449 "qdelete": (delete, [], 'hg qdelete PATCH'),
1444 "^qimport":
1450 "^qimport":
1445 (qimport,
1451 (qimport,
1446 [('e', 'existing', None, 'import file in patch dir'),
1452 [('e', 'existing', None, 'import file in patch dir'),
1447 ('n', 'name', '', 'patch file name'),
1453 ('n', 'name', '', 'patch file name'),
1448 ('f', 'force', None, 'overwrite existing files')],
1454 ('f', 'force', None, 'overwrite existing files')],
1449 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1455 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1450 "^qinit":
1456 "^qinit":
1451 (init,
1457 (init,
1452 [('c', 'create-repo', None, 'create queue repository')],
1458 [('c', 'create-repo', None, 'create queue repository')],
1453 'hg qinit [-c]'),
1459 'hg qinit [-c]'),
1454 "qnew":
1460 "qnew":
1455 (new,
1461 (new,
1456 [('m', 'message', '', _('use <text> as commit message')),
1462 [('m', 'message', '', _('use <text> as commit message')),
1457 ('l', 'logfile', '', _('read the commit message from <file>')),
1463 ('l', 'logfile', '', _('read the commit message from <file>')),
1458 ('f', 'force', None, 'force')],
1464 ('f', 'force', None, 'force')],
1459 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'),
1465 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'),
1460 "qnext": (next, [], 'hg qnext'),
1466 "qnext": (next, [], 'hg qnext'),
1461 "qprev": (prev, [], 'hg qprev'),
1467 "qprev": (prev, [], 'hg qprev'),
1462 "^qpop":
1468 "^qpop":
1463 (pop,
1469 (pop,
1464 [('a', 'all', None, 'pop all patches'),
1470 [('a', 'all', None, 'pop all patches'),
1465 ('n', 'name', '', 'queue name to pop'),
1471 ('n', 'name', '', 'queue name to pop'),
1466 ('f', 'force', None, 'forget any local changes')],
1472 ('f', 'force', None, 'forget any local changes')],
1467 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1473 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1468 "^qpush":
1474 "^qpush":
1469 (push,
1475 (push,
1470 [('f', 'force', None, 'apply if the patch has rejects'),
1476 [('f', 'force', None, 'apply if the patch has rejects'),
1471 ('l', 'list', None, 'list patch name in commit text'),
1477 ('l', 'list', None, 'list patch name in commit text'),
1472 ('a', 'all', None, 'apply all patches'),
1478 ('a', 'all', None, 'apply all patches'),
1473 ('m', 'merge', None, 'merge from another queue'),
1479 ('m', 'merge', None, 'merge from another queue'),
1474 ('n', 'name', '', 'merge queue name')],
1480 ('n', 'name', '', 'merge queue name')],
1475 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1481 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1476 "^qrefresh":
1482 "^qrefresh":
1477 (refresh,
1483 (refresh,
1478 [('m', 'message', '', _('change commit message with <text>')),
1484 [('e', 'edit', None, _('edit commit message')),
1485 ('m', 'message', '', _('change commit message with <text>')),
1479 ('l', 'logfile', '', _('change commit message with <file> content')),
1486 ('l', 'logfile', '', _('change commit message with <file> content')),
1480 ('s', 'short', None, 'short refresh')],
1487 ('s', 'short', None, 'short refresh')],
1481 'hg qrefresh [-m TEXT] [-l FILE] [-s]'),
1488 'hg qrefresh [-e] [-m TEXT] [-l FILE] [-s]'),
1482 "qrestore":
1489 "qrestore":
1483 (restore,
1490 (restore,
1484 [('d', 'delete', None, 'delete save entry'),
1491 [('d', 'delete', None, 'delete save entry'),
1485 ('u', 'update', None, 'update queue working dir')],
1492 ('u', 'update', None, 'update queue working dir')],
1486 'hg qrestore [-d] [-u] REV'),
1493 'hg qrestore [-d] [-u] REV'),
1487 "qsave":
1494 "qsave":
1488 (save,
1495 (save,
1489 [('m', 'message', '', _('use <text> as commit message')),
1496 [('m', 'message', '', _('use <text> as commit message')),
1490 ('l', 'logfile', '', _('read the commit message from <file>')),
1497 ('l', 'logfile', '', _('read the commit message from <file>')),
1491 ('c', 'copy', None, 'copy patch directory'),
1498 ('c', 'copy', None, 'copy patch directory'),
1492 ('n', 'name', '', 'copy directory name'),
1499 ('n', 'name', '', 'copy directory name'),
1493 ('e', 'empty', None, 'clear queue status file'),
1500 ('e', 'empty', None, 'clear queue status file'),
1494 ('f', 'force', None, 'force copy')],
1501 ('f', 'force', None, 'force copy')],
1495 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
1502 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
1496 "qseries":
1503 "qseries":
1497 (series,
1504 (series,
1498 [('m', 'missing', None, 'print patches not in series')],
1505 [('m', 'missing', None, 'print patches not in series')],
1499 'hg qseries [-m]'),
1506 'hg qseries [-m]'),
1500 "^strip":
1507 "^strip":
1501 (strip,
1508 (strip,
1502 [('f', 'force', None, 'force multi-head removal'),
1509 [('f', 'force', None, 'force multi-head removal'),
1503 ('b', 'backup', None, 'bundle unrelated changesets'),
1510 ('b', 'backup', None, 'bundle unrelated changesets'),
1504 ('n', 'nobackup', None, 'no backups')],
1511 ('n', 'nobackup', None, 'no backups')],
1505 'hg strip [-f] [-b] [-n] REV'),
1512 'hg strip [-f] [-b] [-n] REV'),
1506 "qtop": (top, [], 'hg qtop'),
1513 "qtop": (top, [], 'hg qtop'),
1507 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1514 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1508 "qversion": (version, [], 'hg qversion')
1515 "qversion": (version, [], 'hg qversion')
1509 }
1516 }
1510
1517
General Comments 0
You need to be logged in to leave comments. Login now