##// END OF EJS Templates
mq: update to handle repomap not longer used
Vadim Gelfer -
r2725:9ffee4f0 default
parent child Browse files
Show More
@@ -1,1498 +1,1496 b''
1 # queue.py - patch queues for mercurial
1 # queue.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005 Chris Mason <mason@suse.com>
3 # Copyright 2005 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 '''patch management and development
8 '''patch management and development
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use "hg help command" for more details):
17 Common tasks (use "hg help command" for more details):
18
18
19 prepare repository to work with patches qinit
19 prepare repository to work with patches qinit
20 create new patch qnew
20 create new patch qnew
21 import existing patch qimport
21 import existing patch qimport
22
22
23 print patch series qseries
23 print patch series qseries
24 print applied patches qapplied
24 print applied patches qapplied
25 print name of top applied patch qtop
25 print name of top applied patch qtop
26
26
27 add known patch to applied stack qpush
27 add known patch to applied stack qpush
28 remove patch from applied stack qpop
28 remove patch from applied stack qpop
29 refresh contents of top applied patch qrefresh
29 refresh contents of top applied patch qrefresh
30 '''
30 '''
31
31
32 from mercurial.demandload import *
32 from mercurial.demandload import *
33 demandload(globals(), "os sys re struct traceback errno bz2")
33 demandload(globals(), "os sys re struct traceback errno bz2")
34 from mercurial.i18n import gettext as _
34 from mercurial.i18n import gettext as _
35 from mercurial import ui, hg, revlog, commands, util
35 from mercurial import ui, hg, revlog, commands, util
36
36
37 versionstr = "0.45"
37 versionstr = "0.45"
38
38
39 commands.norepo += " qclone qversion"
39 commands.norepo += " qclone qversion"
40
40
41 class queue:
41 class queue:
42 def __init__(self, ui, path, patchdir=None):
42 def __init__(self, ui, path, patchdir=None):
43 self.basepath = path
43 self.basepath = path
44 if patchdir:
44 if patchdir:
45 self.path = patchdir
45 self.path = patchdir
46 else:
46 else:
47 self.path = os.path.join(path, "patches")
47 self.path = os.path.join(path, "patches")
48 self.opener = util.opener(self.path)
48 self.opener = util.opener(self.path)
49 self.ui = ui
49 self.ui = ui
50 self.applied = []
50 self.applied = []
51 self.full_series = []
51 self.full_series = []
52 self.applied_dirty = 0
52 self.applied_dirty = 0
53 self.series_dirty = 0
53 self.series_dirty = 0
54 self.series_path = "series"
54 self.series_path = "series"
55 self.status_path = "status"
55 self.status_path = "status"
56
56
57 if os.path.exists(os.path.join(self.path, self.series_path)):
57 if os.path.exists(os.path.join(self.path, self.series_path)):
58 self.full_series = self.opener(self.series_path).read().splitlines()
58 self.full_series = self.opener(self.series_path).read().splitlines()
59 self.read_series(self.full_series)
59 self.read_series(self.full_series)
60
60
61 if os.path.exists(os.path.join(self.path, self.status_path)):
61 if os.path.exists(os.path.join(self.path, self.status_path)):
62 self.applied = self.opener(self.status_path).read().splitlines()
62 self.applied = self.opener(self.status_path).read().splitlines()
63
63
64 def find_series(self, patch):
64 def find_series(self, patch):
65 pre = re.compile("(\s*)([^#]+)")
65 pre = re.compile("(\s*)([^#]+)")
66 index = 0
66 index = 0
67 for l in self.full_series:
67 for l in self.full_series:
68 m = pre.match(l)
68 m = pre.match(l)
69 if m:
69 if m:
70 s = m.group(2)
70 s = m.group(2)
71 s = s.rstrip()
71 s = s.rstrip()
72 if s == patch:
72 if s == patch:
73 return index
73 return index
74 index += 1
74 index += 1
75 return None
75 return None
76
76
77 def read_series(self, list):
77 def read_series(self, list):
78 def matcher(list):
78 def matcher(list):
79 pre = re.compile("(\s*)([^#]+)")
79 pre = re.compile("(\s*)([^#]+)")
80 for l in list:
80 for l in list:
81 m = pre.match(l)
81 m = pre.match(l)
82 if m:
82 if m:
83 s = m.group(2)
83 s = m.group(2)
84 s = s.rstrip()
84 s = s.rstrip()
85 if len(s) > 0:
85 if len(s) > 0:
86 yield s
86 yield s
87 self.series = []
87 self.series = []
88 self.series = [ x for x in matcher(list) ]
88 self.series = [ x for x in matcher(list) ]
89
89
90 def save_dirty(self):
90 def save_dirty(self):
91 if self.applied_dirty:
91 if self.applied_dirty:
92 if len(self.applied) > 0:
92 if len(self.applied) > 0:
93 nl = "\n"
93 nl = "\n"
94 else:
94 else:
95 nl = ""
95 nl = ""
96 f = self.opener(self.status_path, "w")
96 f = self.opener(self.status_path, "w")
97 f.write("\n".join(self.applied) + nl)
97 f.write("\n".join(self.applied) + nl)
98 if self.series_dirty:
98 if self.series_dirty:
99 if len(self.full_series) > 0:
99 if len(self.full_series) > 0:
100 nl = "\n"
100 nl = "\n"
101 else:
101 else:
102 nl = ""
102 nl = ""
103 f = self.opener(self.series_path, "w")
103 f = self.opener(self.series_path, "w")
104 f.write("\n".join(self.full_series) + nl)
104 f.write("\n".join(self.full_series) + nl)
105
105
106 def readheaders(self, patch):
106 def readheaders(self, patch):
107 def eatdiff(lines):
107 def eatdiff(lines):
108 while lines:
108 while lines:
109 l = lines[-1]
109 l = lines[-1]
110 if (l.startswith("diff -") or
110 if (l.startswith("diff -") or
111 l.startswith("Index:") or
111 l.startswith("Index:") or
112 l.startswith("===========")):
112 l.startswith("===========")):
113 del lines[-1]
113 del lines[-1]
114 else:
114 else:
115 break
115 break
116 def eatempty(lines):
116 def eatempty(lines):
117 while lines:
117 while lines:
118 l = lines[-1]
118 l = lines[-1]
119 if re.match('\s*$', l):
119 if re.match('\s*$', l):
120 del lines[-1]
120 del lines[-1]
121 else:
121 else:
122 break
122 break
123
123
124 pf = os.path.join(self.path, patch)
124 pf = os.path.join(self.path, patch)
125 message = []
125 message = []
126 comments = []
126 comments = []
127 user = None
127 user = None
128 date = None
128 date = None
129 format = None
129 format = None
130 subject = None
130 subject = None
131 diffstart = 0
131 diffstart = 0
132
132
133 for line in file(pf):
133 for line in file(pf):
134 line = line.rstrip()
134 line = line.rstrip()
135 if diffstart:
135 if diffstart:
136 if line.startswith('+++ '):
136 if line.startswith('+++ '):
137 diffstart = 2
137 diffstart = 2
138 break
138 break
139 if line.startswith("--- "):
139 if line.startswith("--- "):
140 diffstart = 1
140 diffstart = 1
141 continue
141 continue
142 elif format == "hgpatch":
142 elif format == "hgpatch":
143 # parse values when importing the result of an hg export
143 # parse values when importing the result of an hg export
144 if line.startswith("# User "):
144 if line.startswith("# User "):
145 user = line[7:]
145 user = line[7:]
146 elif line.startswith("# Date "):
146 elif line.startswith("# Date "):
147 date = line[7:]
147 date = line[7:]
148 elif not line.startswith("# ") and line:
148 elif not line.startswith("# ") and line:
149 message.append(line)
149 message.append(line)
150 format = None
150 format = None
151 elif line == '# HG changeset patch':
151 elif line == '# HG changeset patch':
152 format = "hgpatch"
152 format = "hgpatch"
153 elif (format != "tagdone" and (line.startswith("Subject: ") or
153 elif (format != "tagdone" and (line.startswith("Subject: ") or
154 line.startswith("subject: "))):
154 line.startswith("subject: "))):
155 subject = line[9:]
155 subject = line[9:]
156 format = "tag"
156 format = "tag"
157 elif (format != "tagdone" and (line.startswith("From: ") or
157 elif (format != "tagdone" and (line.startswith("From: ") or
158 line.startswith("from: "))):
158 line.startswith("from: "))):
159 user = line[6:]
159 user = line[6:]
160 format = "tag"
160 format = "tag"
161 elif format == "tag" and line == "":
161 elif format == "tag" and line == "":
162 # when looking for tags (subject: from: etc) they
162 # when looking for tags (subject: from: etc) they
163 # end once you find a blank line in the source
163 # end once you find a blank line in the source
164 format = "tagdone"
164 format = "tagdone"
165 elif message or line:
165 elif message or line:
166 message.append(line)
166 message.append(line)
167 comments.append(line)
167 comments.append(line)
168
168
169 eatdiff(message)
169 eatdiff(message)
170 eatdiff(comments)
170 eatdiff(comments)
171 eatempty(message)
171 eatempty(message)
172 eatempty(comments)
172 eatempty(comments)
173
173
174 # make sure message isn't empty
174 # make sure message isn't empty
175 if format and format.startswith("tag") and subject:
175 if format and format.startswith("tag") and subject:
176 message.insert(0, "")
176 message.insert(0, "")
177 message.insert(0, subject)
177 message.insert(0, subject)
178 return (message, comments, user, date, diffstart > 1)
178 return (message, comments, user, date, diffstart > 1)
179
179
180 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
180 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
181 # first try just applying the patch
181 # first try just applying the patch
182 (err, n) = self.apply(repo, [ patch ], update_status=False,
182 (err, n) = self.apply(repo, [ patch ], update_status=False,
183 strict=True, merge=rev, wlock=wlock)
183 strict=True, merge=rev, wlock=wlock)
184
184
185 if err == 0:
185 if err == 0:
186 return (err, n)
186 return (err, n)
187
187
188 if n is None:
188 if n is None:
189 raise util.Abort(_("apply failed for patch %s") % patch)
189 raise util.Abort(_("apply failed for patch %s") % patch)
190
190
191 self.ui.warn("patch didn't work out, merging %s\n" % patch)
191 self.ui.warn("patch didn't work out, merging %s\n" % patch)
192
192
193 # apply failed, strip away that rev and merge.
193 # apply failed, strip away that rev and merge.
194 repo.update(head, allow=False, force=True, wlock=wlock)
194 repo.update(head, allow=False, force=True, wlock=wlock)
195 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
195 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
196
196
197 c = repo.changelog.read(rev)
197 c = repo.changelog.read(rev)
198 ret = repo.update(rev, allow=True, wlock=wlock)
198 ret = repo.update(rev, allow=True, wlock=wlock)
199 if ret:
199 if ret:
200 raise util.Abort(_("update returned %d") % ret)
200 raise util.Abort(_("update returned %d") % ret)
201 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
201 n = repo.commit(None, c[4], c[1], force=1, wlock=wlock)
202 if n == None:
202 if n == None:
203 raise util.Abort(_("repo commit failed"))
203 raise util.Abort(_("repo commit failed"))
204 try:
204 try:
205 message, comments, user, date, patchfound = mergeq.readheaders(patch)
205 message, comments, user, date, patchfound = mergeq.readheaders(patch)
206 except:
206 except:
207 raise util.Abort(_("unable to read %s") % patch)
207 raise util.Abort(_("unable to read %s") % patch)
208
208
209 patchf = self.opener(patch, "w")
209 patchf = self.opener(patch, "w")
210 if comments:
210 if comments:
211 comments = "\n".join(comments) + '\n\n'
211 comments = "\n".join(comments) + '\n\n'
212 patchf.write(comments)
212 patchf.write(comments)
213 commands.dodiff(patchf, self.ui, repo, head, n)
213 commands.dodiff(patchf, self.ui, repo, head, n)
214 patchf.close()
214 patchf.close()
215 return (0, n)
215 return (0, n)
216
216
217 def qparents(self, repo, rev=None):
217 def qparents(self, repo, rev=None):
218 if rev is None:
218 if rev is None:
219 (p1, p2) = repo.dirstate.parents()
219 (p1, p2) = repo.dirstate.parents()
220 if p2 == revlog.nullid:
220 if p2 == revlog.nullid:
221 return p1
221 return p1
222 if len(self.applied) == 0:
222 if len(self.applied) == 0:
223 return None
223 return None
224 (top, patch) = self.applied[-1].split(':')
224 (top, patch) = self.applied[-1].split(':')
225 top = revlog.bin(top)
225 top = revlog.bin(top)
226 return top
226 return top
227 pp = repo.changelog.parents(rev)
227 pp = repo.changelog.parents(rev)
228 if pp[1] != revlog.nullid:
228 if pp[1] != revlog.nullid:
229 arevs = [ x.split(':')[0] for x in self.applied ]
229 arevs = [ x.split(':')[0] for x in self.applied ]
230 p0 = revlog.hex(pp[0])
230 p0 = revlog.hex(pp[0])
231 p1 = revlog.hex(pp[1])
231 p1 = revlog.hex(pp[1])
232 if p0 in arevs:
232 if p0 in arevs:
233 return pp[0]
233 return pp[0]
234 if p1 in arevs:
234 if p1 in arevs:
235 return pp[1]
235 return pp[1]
236 return pp[0]
236 return pp[0]
237
237
238 def mergepatch(self, repo, mergeq, series, wlock):
238 def mergepatch(self, repo, mergeq, series, wlock):
239 if len(self.applied) == 0:
239 if len(self.applied) == 0:
240 # each of the patches merged in will have two parents. This
240 # each of the patches merged in will have two parents. This
241 # can confuse the qrefresh, qdiff, and strip code because it
241 # can confuse the qrefresh, qdiff, and strip code because it
242 # needs to know which parent is actually in the patch queue.
242 # needs to know which parent is actually in the patch queue.
243 # so, we insert a merge marker with only one parent. This way
243 # so, we insert a merge marker with only one parent. This way
244 # the first patch in the queue is never a merge patch
244 # the first patch in the queue is never a merge patch
245 #
245 #
246 pname = ".hg.patches.merge.marker"
246 pname = ".hg.patches.merge.marker"
247 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
247 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
248 wlock=wlock)
248 wlock=wlock)
249 self.applied.append(revlog.hex(n) + ":" + pname)
249 self.applied.append(revlog.hex(n) + ":" + pname)
250 self.applied_dirty = 1
250 self.applied_dirty = 1
251
251
252 head = self.qparents(repo)
252 head = self.qparents(repo)
253
253
254 for patch in series:
254 for patch in series:
255 patch = mergeq.lookup(patch, strict=True)
255 patch = mergeq.lookup(patch, strict=True)
256 if not patch:
256 if not patch:
257 self.ui.warn("patch %s does not exist\n" % patch)
257 self.ui.warn("patch %s does not exist\n" % patch)
258 return (1, None)
258 return (1, None)
259
259
260 info = mergeq.isapplied(patch)
260 info = mergeq.isapplied(patch)
261 if not info:
261 if not info:
262 self.ui.warn("patch %s is not applied\n" % patch)
262 self.ui.warn("patch %s is not applied\n" % patch)
263 return (1, None)
263 return (1, None)
264 rev = revlog.bin(info[1])
264 rev = revlog.bin(info[1])
265 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
265 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
266 if head:
266 if head:
267 self.applied.append(revlog.hex(head) + ":" + patch)
267 self.applied.append(revlog.hex(head) + ":" + patch)
268 self.applied_dirty = 1
268 self.applied_dirty = 1
269 if err:
269 if err:
270 return (err, head)
270 return (err, head)
271 return (0, head)
271 return (0, head)
272
272
273 def apply(self, repo, series, list=False, update_status=True,
273 def apply(self, repo, series, list=False, update_status=True,
274 strict=False, patchdir=None, merge=None, wlock=None):
274 strict=False, patchdir=None, merge=None, wlock=None):
275 # TODO unify with commands.py
275 # TODO unify with commands.py
276 if not patchdir:
276 if not patchdir:
277 patchdir = self.path
277 patchdir = self.path
278 err = 0
278 err = 0
279 if not wlock:
279 if not wlock:
280 wlock = repo.wlock()
280 wlock = repo.wlock()
281 lock = repo.lock()
281 lock = repo.lock()
282 tr = repo.transaction()
282 tr = repo.transaction()
283 n = None
283 n = None
284 for patch in series:
284 for patch in series:
285 self.ui.warn("applying %s\n" % patch)
285 self.ui.warn("applying %s\n" % patch)
286 pf = os.path.join(patchdir, patch)
286 pf = os.path.join(patchdir, patch)
287
287
288 try:
288 try:
289 message, comments, user, date, patchfound = self.readheaders(patch)
289 message, comments, user, date, patchfound = self.readheaders(patch)
290 except:
290 except:
291 self.ui.warn("Unable to read %s\n" % pf)
291 self.ui.warn("Unable to read %s\n" % pf)
292 err = 1
292 err = 1
293 break
293 break
294
294
295 if not message:
295 if not message:
296 message = "imported patch %s\n" % patch
296 message = "imported patch %s\n" % patch
297 else:
297 else:
298 if list:
298 if list:
299 message.append("\nimported patch %s" % patch)
299 message.append("\nimported patch %s" % patch)
300 message = '\n'.join(message)
300 message = '\n'.join(message)
301
301
302 try:
302 try:
303 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
303 pp = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
304 f = os.popen("%s -d '%s' -p1 --no-backup-if-mismatch < '%s'" %
304 f = os.popen("%s -d '%s' -p1 --no-backup-if-mismatch < '%s'" %
305 (pp, repo.root, pf))
305 (pp, repo.root, pf))
306 except:
306 except:
307 self.ui.warn("patch failed, unable to continue (try -v)\n")
307 self.ui.warn("patch failed, unable to continue (try -v)\n")
308 err = 1
308 err = 1
309 break
309 break
310 files = []
310 files = []
311 fuzz = False
311 fuzz = False
312 for l in f:
312 for l in f:
313 l = l.rstrip('\r\n');
313 l = l.rstrip('\r\n');
314 if self.ui.verbose:
314 if self.ui.verbose:
315 self.ui.warn(l + "\n")
315 self.ui.warn(l + "\n")
316 if l[:14] == 'patching file ':
316 if l[:14] == 'patching file ':
317 pf = os.path.normpath(l[14:])
317 pf = os.path.normpath(l[14:])
318 # when patch finds a space in the file name, it puts
318 # when patch finds a space in the file name, it puts
319 # single quotes around the filename. strip them off
319 # single quotes around the filename. strip them off
320 if pf[0] == "'" and pf[-1] == "'":
320 if pf[0] == "'" and pf[-1] == "'":
321 pf = pf[1:-1]
321 pf = pf[1:-1]
322 if pf not in files:
322 if pf not in files:
323 files.append(pf)
323 files.append(pf)
324 printed_file = False
324 printed_file = False
325 file_str = l
325 file_str = l
326 elif l.find('with fuzz') >= 0:
326 elif l.find('with fuzz') >= 0:
327 if not printed_file:
327 if not printed_file:
328 self.ui.warn(file_str + '\n')
328 self.ui.warn(file_str + '\n')
329 printed_file = True
329 printed_file = True
330 self.ui.warn(l + '\n')
330 self.ui.warn(l + '\n')
331 fuzz = True
331 fuzz = True
332 elif l.find('saving rejects to file') >= 0:
332 elif l.find('saving rejects to file') >= 0:
333 self.ui.warn(l + '\n')
333 self.ui.warn(l + '\n')
334 elif l.find('FAILED') >= 0:
334 elif l.find('FAILED') >= 0:
335 if not printed_file:
335 if not printed_file:
336 self.ui.warn(file_str + '\n')
336 self.ui.warn(file_str + '\n')
337 printed_file = True
337 printed_file = True
338 self.ui.warn(l + '\n')
338 self.ui.warn(l + '\n')
339 patcherr = f.close()
339 patcherr = f.close()
340
340
341 if merge and len(files) > 0:
341 if merge and len(files) > 0:
342 # Mark as merged and update dirstate parent info
342 # Mark as merged and update dirstate parent info
343 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
343 repo.dirstate.update(repo.dirstate.filterfiles(files), 'm')
344 p1, p2 = repo.dirstate.parents()
344 p1, p2 = repo.dirstate.parents()
345 repo.dirstate.setparents(p1, merge)
345 repo.dirstate.setparents(p1, merge)
346 if len(files) > 0:
346 if len(files) > 0:
347 commands.addremove_lock(self.ui, repo, files,
347 commands.addremove_lock(self.ui, repo, files,
348 opts={}, wlock=wlock)
348 opts={}, wlock=wlock)
349 n = repo.commit(files, message, user, date, force=1, lock=lock,
349 n = repo.commit(files, message, user, date, force=1, lock=lock,
350 wlock=wlock)
350 wlock=wlock)
351
351
352 if n == None:
352 if n == None:
353 raise util.Abort(_("repo commit failed"))
353 raise util.Abort(_("repo commit failed"))
354
354
355 if update_status:
355 if update_status:
356 self.applied.append(revlog.hex(n) + ":" + patch)
356 self.applied.append(revlog.hex(n) + ":" + patch)
357
357
358 if patcherr:
358 if patcherr:
359 if not patchfound:
359 if not patchfound:
360 self.ui.warn("patch %s is empty\n" % patch)
360 self.ui.warn("patch %s is empty\n" % patch)
361 err = 0
361 err = 0
362 else:
362 else:
363 self.ui.warn("patch failed, rejects left in working dir\n")
363 self.ui.warn("patch failed, rejects left in working dir\n")
364 err = 1
364 err = 1
365 break
365 break
366
366
367 if fuzz and strict:
367 if fuzz and strict:
368 self.ui.warn("fuzz found when applying patch, stopping\n")
368 self.ui.warn("fuzz found when applying patch, stopping\n")
369 err = 1
369 err = 1
370 break
370 break
371 tr.close()
371 tr.close()
372 return (err, n)
372 return (err, n)
373
373
374 def delete(self, repo, patch):
374 def delete(self, repo, patch):
375 patch = self.lookup(patch, strict=True)
375 patch = self.lookup(patch, strict=True)
376 info = self.isapplied(patch)
376 info = self.isapplied(patch)
377 if info:
377 if info:
378 raise util.Abort(_("cannot delete applied patch %s") % patch)
378 raise util.Abort(_("cannot delete applied patch %s") % patch)
379 if patch not in self.series:
379 if patch not in self.series:
380 raise util.Abort(_("patch %s not in series file") % patch)
380 raise util.Abort(_("patch %s not in series file") % patch)
381 i = self.find_series(patch)
381 i = self.find_series(patch)
382 del self.full_series[i]
382 del self.full_series[i]
383 self.read_series(self.full_series)
383 self.read_series(self.full_series)
384 self.series_dirty = 1
384 self.series_dirty = 1
385
385
386 def check_toppatch(self, repo):
386 def check_toppatch(self, repo):
387 if len(self.applied) > 0:
387 if len(self.applied) > 0:
388 (top, patch) = self.applied[-1].split(':')
388 (top, patch) = self.applied[-1].split(':')
389 top = revlog.bin(top)
389 top = revlog.bin(top)
390 pp = repo.dirstate.parents()
390 pp = repo.dirstate.parents()
391 if top not in pp:
391 if top not in pp:
392 raise util.Abort(_("queue top not at same revision as working directory"))
392 raise util.Abort(_("queue top not at same revision as working directory"))
393 return top
393 return top
394 return None
394 return None
395 def check_localchanges(self, repo):
395 def check_localchanges(self, repo):
396 (c, a, r, d, u) = repo.changes(None, None)
396 (c, a, r, d, u) = repo.changes(None, None)
397 if c or a or d or r:
397 if c or a or d or r:
398 raise util.Abort(_("local changes found, refresh first"))
398 raise util.Abort(_("local changes found, refresh first"))
399 def new(self, repo, patch, msg=None, force=None):
399 def new(self, repo, patch, msg=None, force=None):
400 if os.path.exists(os.path.join(self.path, patch)):
400 if os.path.exists(os.path.join(self.path, patch)):
401 raise util.Abort(_('patch "%s" already exists') % patch)
401 raise util.Abort(_('patch "%s" already exists') % patch)
402 commitfiles = []
402 commitfiles = []
403 (c, a, r, d, u) = repo.changes(None, None)
403 (c, a, r, d, u) = repo.changes(None, None)
404 if c or a or d or r:
404 if c or a or d or r:
405 if not force:
405 if not force:
406 raise util.Abort(_("local changes found, refresh first"))
406 raise util.Abort(_("local changes found, refresh first"))
407 commitfiles = c + a + r
407 commitfiles = c + a + r
408 self.check_toppatch(repo)
408 self.check_toppatch(repo)
409 wlock = repo.wlock()
409 wlock = repo.wlock()
410 insert = self.full_series_end()
410 insert = self.full_series_end()
411 if msg:
411 if msg:
412 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
412 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
413 wlock=wlock)
413 wlock=wlock)
414 else:
414 else:
415 n = repo.commit(commitfiles,
415 n = repo.commit(commitfiles,
416 "New patch: %s" % patch, force=True, wlock=wlock)
416 "New patch: %s" % patch, force=True, wlock=wlock)
417 if n == None:
417 if n == None:
418 raise util.Abort(_("repo commit failed"))
418 raise util.Abort(_("repo commit failed"))
419 self.full_series[insert:insert] = [patch]
419 self.full_series[insert:insert] = [patch]
420 self.applied.append(revlog.hex(n) + ":" + patch)
420 self.applied.append(revlog.hex(n) + ":" + patch)
421 self.read_series(self.full_series)
421 self.read_series(self.full_series)
422 self.series_dirty = 1
422 self.series_dirty = 1
423 self.applied_dirty = 1
423 self.applied_dirty = 1
424 p = self.opener(patch, "w")
424 p = self.opener(patch, "w")
425 if msg:
425 if msg:
426 msg = msg + "\n"
426 msg = msg + "\n"
427 p.write(msg)
427 p.write(msg)
428 p.close()
428 p.close()
429 wlock = None
429 wlock = None
430 r = self.qrepo()
430 r = self.qrepo()
431 if r: r.add([patch])
431 if r: r.add([patch])
432 if commitfiles:
432 if commitfiles:
433 self.refresh(repo, msg=None, short=True)
433 self.refresh(repo, msg=None, short=True)
434
434
435 def strip(self, repo, rev, update=True, backup="all", wlock=None):
435 def strip(self, repo, rev, update=True, backup="all", wlock=None):
436 def limitheads(chlog, stop):
436 def limitheads(chlog, stop):
437 """return the list of all nodes that have no children"""
437 """return the list of all nodes that have no children"""
438 p = {}
438 p = {}
439 h = []
439 h = []
440 stoprev = 0
440 stoprev = 0
441 if stop in chlog.nodemap:
441 if stop in chlog.nodemap:
442 stoprev = chlog.rev(stop)
442 stoprev = chlog.rev(stop)
443
443
444 for r in range(chlog.count() - 1, -1, -1):
444 for r in range(chlog.count() - 1, -1, -1):
445 n = chlog.node(r)
445 n = chlog.node(r)
446 if n not in p:
446 if n not in p:
447 h.append(n)
447 h.append(n)
448 if n == stop:
448 if n == stop:
449 break
449 break
450 if r < stoprev:
450 if r < stoprev:
451 break
451 break
452 for pn in chlog.parents(n):
452 for pn in chlog.parents(n):
453 p[pn] = 1
453 p[pn] = 1
454 return h
454 return h
455
455
456 def bundle(cg):
456 def bundle(cg):
457 backupdir = repo.join("strip-backup")
457 backupdir = repo.join("strip-backup")
458 if not os.path.isdir(backupdir):
458 if not os.path.isdir(backupdir):
459 os.mkdir(backupdir)
459 os.mkdir(backupdir)
460 name = os.path.join(backupdir, "%s" % revlog.short(rev))
460 name = os.path.join(backupdir, "%s" % revlog.short(rev))
461 name = savename(name)
461 name = savename(name)
462 self.ui.warn("saving bundle to %s\n" % name)
462 self.ui.warn("saving bundle to %s\n" % name)
463 # TODO, exclusive open
463 # TODO, exclusive open
464 f = open(name, "wb")
464 f = open(name, "wb")
465 try:
465 try:
466 f.write("HG10")
466 f.write("HG10")
467 z = bz2.BZ2Compressor(9)
467 z = bz2.BZ2Compressor(9)
468 while 1:
468 while 1:
469 chunk = cg.read(4096)
469 chunk = cg.read(4096)
470 if not chunk:
470 if not chunk:
471 break
471 break
472 f.write(z.compress(chunk))
472 f.write(z.compress(chunk))
473 f.write(z.flush())
473 f.write(z.flush())
474 except:
474 except:
475 os.unlink(name)
475 os.unlink(name)
476 raise
476 raise
477 f.close()
477 f.close()
478 return name
478 return name
479
479
480 def stripall(rev, revnum):
480 def stripall(rev, revnum):
481 cl = repo.changelog
481 cl = repo.changelog
482 c = cl.read(rev)
482 c = cl.read(rev)
483 mm = repo.manifest.read(c[0])
483 mm = repo.manifest.read(c[0])
484 seen = {}
484 seen = {}
485
485
486 for x in xrange(revnum, cl.count()):
486 for x in xrange(revnum, cl.count()):
487 c = cl.read(cl.node(x))
487 c = cl.read(cl.node(x))
488 for f in c[3]:
488 for f in c[3]:
489 if f in seen:
489 if f in seen:
490 continue
490 continue
491 seen[f] = 1
491 seen[f] = 1
492 if f in mm:
492 if f in mm:
493 filerev = mm[f]
493 filerev = mm[f]
494 else:
494 else:
495 filerev = 0
495 filerev = 0
496 seen[f] = filerev
496 seen[f] = filerev
497 # we go in two steps here so the strip loop happens in a
497 # we go in two steps here so the strip loop happens in a
498 # sensible order. When stripping many files, this helps keep
498 # sensible order. When stripping many files, this helps keep
499 # our disk access patterns under control.
499 # our disk access patterns under control.
500 list = seen.keys()
500 list = seen.keys()
501 list.sort()
501 list.sort()
502 for f in list:
502 for f in list:
503 ff = repo.file(f)
503 ff = repo.file(f)
504 filerev = seen[f]
504 filerev = seen[f]
505 if filerev != 0:
505 if filerev != 0:
506 if filerev in ff.nodemap:
506 if filerev in ff.nodemap:
507 filerev = ff.rev(filerev)
507 filerev = ff.rev(filerev)
508 else:
508 else:
509 filerev = 0
509 filerev = 0
510 ff.strip(filerev, revnum)
510 ff.strip(filerev, revnum)
511
511
512 if not wlock:
512 if not wlock:
513 wlock = repo.wlock()
513 wlock = repo.wlock()
514 lock = repo.lock()
514 lock = repo.lock()
515 chlog = repo.changelog
515 chlog = repo.changelog
516 # TODO delete the undo files, and handle undo of merge sets
516 # TODO delete the undo files, and handle undo of merge sets
517 pp = chlog.parents(rev)
517 pp = chlog.parents(rev)
518 revnum = chlog.rev(rev)
518 revnum = chlog.rev(rev)
519
519
520 if update:
520 if update:
521 (c, a, r, d, u) = repo.changes(None, None)
521 (c, a, r, d, u) = repo.changes(None, None)
522 if c or a or d or r:
522 if c or a or d or r:
523 raise util.Abort(_("local changes found"))
523 raise util.Abort(_("local changes found"))
524 urev = self.qparents(repo, rev)
524 urev = self.qparents(repo, rev)
525 repo.update(urev, allow=False, force=True, wlock=wlock)
525 repo.update(urev, allow=False, force=True, wlock=wlock)
526 repo.dirstate.write()
526 repo.dirstate.write()
527
527
528 # save is a list of all the branches we are truncating away
528 # save is a list of all the branches we are truncating away
529 # that we actually want to keep. changegroup will be used
529 # that we actually want to keep. changegroup will be used
530 # to preserve them and add them back after the truncate
530 # to preserve them and add them back after the truncate
531 saveheads = []
531 saveheads = []
532 savebases = {}
532 savebases = {}
533
533
534 tip = chlog.tip()
534 tip = chlog.tip()
535 heads = limitheads(chlog, rev)
535 heads = limitheads(chlog, rev)
536 seen = {}
536 seen = {}
537
537
538 # search through all the heads, finding those where the revision
538 # search through all the heads, finding those where the revision
539 # we want to strip away is an ancestor. Also look for merges
539 # we want to strip away is an ancestor. Also look for merges
540 # that might be turned into new heads by the strip.
540 # that might be turned into new heads by the strip.
541 while heads:
541 while heads:
542 h = heads.pop()
542 h = heads.pop()
543 n = h
543 n = h
544 while True:
544 while True:
545 seen[n] = 1
545 seen[n] = 1
546 pp = chlog.parents(n)
546 pp = chlog.parents(n)
547 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
547 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
548 if pp[1] not in seen:
548 if pp[1] not in seen:
549 heads.append(pp[1])
549 heads.append(pp[1])
550 if pp[0] == revlog.nullid:
550 if pp[0] == revlog.nullid:
551 break
551 break
552 if chlog.rev(pp[0]) < revnum:
552 if chlog.rev(pp[0]) < revnum:
553 break
553 break
554 n = pp[0]
554 n = pp[0]
555 if n == rev:
555 if n == rev:
556 break
556 break
557 r = chlog.reachable(h, rev)
557 r = chlog.reachable(h, rev)
558 if rev not in r:
558 if rev not in r:
559 saveheads.append(h)
559 saveheads.append(h)
560 for x in r:
560 for x in r:
561 if chlog.rev(x) > revnum:
561 if chlog.rev(x) > revnum:
562 savebases[x] = 1
562 savebases[x] = 1
563
563
564 # create a changegroup for all the branches we need to keep
564 # create a changegroup for all the branches we need to keep
565 if backup is "all":
565 if backup is "all":
566 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
566 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
567 bundle(backupch)
567 bundle(backupch)
568 if saveheads:
568 if saveheads:
569 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
569 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
570 chgrpfile = bundle(backupch)
570 chgrpfile = bundle(backupch)
571
571
572 stripall(rev, revnum)
572 stripall(rev, revnum)
573
573
574 change = chlog.read(rev)
574 change = chlog.read(rev)
575 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
575 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
576 chlog.strip(revnum, revnum)
576 chlog.strip(revnum, revnum)
577 if saveheads:
577 if saveheads:
578 self.ui.status("adding branch\n")
578 self.ui.status("adding branch\n")
579 commands.unbundle(self.ui, repo, chgrpfile, update=False)
579 commands.unbundle(self.ui, repo, chgrpfile, update=False)
580 if backup is not "strip":
580 if backup is not "strip":
581 os.unlink(chgrpfile)
581 os.unlink(chgrpfile)
582
582
583 def isapplied(self, patch):
583 def isapplied(self, patch):
584 """returns (index, rev, patch)"""
584 """returns (index, rev, patch)"""
585 for i in xrange(len(self.applied)):
585 for i in xrange(len(self.applied)):
586 p = self.applied[i]
586 p = self.applied[i]
587 a = p.split(':')
587 a = p.split(':')
588 if a[1] == patch:
588 if a[1] == patch:
589 return (i, a[0], a[1])
589 return (i, a[0], a[1])
590 return None
590 return None
591
591
592 # if the exact patch name does not exist, we try a few
592 # if the exact patch name does not exist, we try a few
593 # variations. If strict is passed, we try only #1
593 # variations. If strict is passed, we try only #1
594 #
594 #
595 # 1) a number to indicate an offset in the series file
595 # 1) a number to indicate an offset in the series file
596 # 2) a unique substring of the patch name was given
596 # 2) a unique substring of the patch name was given
597 # 3) patchname[-+]num to indicate an offset in the series file
597 # 3) patchname[-+]num to indicate an offset in the series file
598 def lookup(self, patch, strict=False):
598 def lookup(self, patch, strict=False):
599 def partial_name(s):
599 def partial_name(s):
600 count = 0
600 count = 0
601 if s in self.series:
601 if s in self.series:
602 return s
602 return s
603 for x in self.series:
603 for x in self.series:
604 if s in x:
604 if s in x:
605 count += 1
605 count += 1
606 last = x
606 last = x
607 if count > 1:
607 if count > 1:
608 return None
608 return None
609 if count:
609 if count:
610 return last
610 return last
611 if len(self.series) > 0 and len(self.applied) > 0:
611 if len(self.series) > 0 and len(self.applied) > 0:
612 if s == 'qtip':
612 if s == 'qtip':
613 return self.series[self.series_end()-1]
613 return self.series[self.series_end()-1]
614 if s == 'qbase':
614 if s == 'qbase':
615 return self.series[0]
615 return self.series[0]
616 return None
616 return None
617 if patch == None:
617 if patch == None:
618 return None
618 return None
619
619
620 # we don't want to return a partial match until we make
620 # we don't want to return a partial match until we make
621 # sure the file name passed in does not exist (checked below)
621 # sure the file name passed in does not exist (checked below)
622 res = partial_name(patch)
622 res = partial_name(patch)
623 if res and res == patch:
623 if res and res == patch:
624 return res
624 return res
625
625
626 if not os.path.isfile(os.path.join(self.path, patch)):
626 if not os.path.isfile(os.path.join(self.path, patch)):
627 try:
627 try:
628 sno = int(patch)
628 sno = int(patch)
629 except(ValueError, OverflowError):
629 except(ValueError, OverflowError):
630 pass
630 pass
631 else:
631 else:
632 if sno < len(self.series):
632 if sno < len(self.series):
633 patch = self.series[sno]
633 patch = self.series[sno]
634 return patch
634 return patch
635 if not strict:
635 if not strict:
636 # return any partial match made above
636 # return any partial match made above
637 if res:
637 if res:
638 return res
638 return res
639 minus = patch.rsplit('-', 1)
639 minus = patch.rsplit('-', 1)
640 if len(minus) > 1:
640 if len(minus) > 1:
641 res = partial_name(minus[0])
641 res = partial_name(minus[0])
642 if res:
642 if res:
643 i = self.series.index(res)
643 i = self.series.index(res)
644 try:
644 try:
645 off = int(minus[1] or 1)
645 off = int(minus[1] or 1)
646 except(ValueError, OverflowError):
646 except(ValueError, OverflowError):
647 pass
647 pass
648 else:
648 else:
649 if i - off >= 0:
649 if i - off >= 0:
650 return self.series[i - off]
650 return self.series[i - off]
651 plus = patch.rsplit('+', 1)
651 plus = patch.rsplit('+', 1)
652 if len(plus) > 1:
652 if len(plus) > 1:
653 res = partial_name(plus[0])
653 res = partial_name(plus[0])
654 if res:
654 if res:
655 i = self.series.index(res)
655 i = self.series.index(res)
656 try:
656 try:
657 off = int(plus[1] or 1)
657 off = int(plus[1] or 1)
658 except(ValueError, OverflowError):
658 except(ValueError, OverflowError):
659 pass
659 pass
660 else:
660 else:
661 if i + off < len(self.series):
661 if i + off < len(self.series):
662 return self.series[i + off]
662 return self.series[i + off]
663 raise util.Abort(_("patch %s not in series") % patch)
663 raise util.Abort(_("patch %s not in series") % patch)
664
664
665 def push(self, repo, patch=None, force=False, list=False,
665 def push(self, repo, patch=None, force=False, list=False,
666 mergeq=None, wlock=None):
666 mergeq=None, wlock=None):
667 if not wlock:
667 if not wlock:
668 wlock = repo.wlock()
668 wlock = repo.wlock()
669 patch = self.lookup(patch)
669 patch = self.lookup(patch)
670 if patch and self.isapplied(patch):
670 if patch and self.isapplied(patch):
671 self.ui.warn(_("patch %s is already applied\n") % patch)
671 self.ui.warn(_("patch %s is already applied\n") % patch)
672 sys.exit(1)
672 sys.exit(1)
673 if self.series_end() == len(self.series):
673 if self.series_end() == len(self.series):
674 self.ui.warn(_("patch series fully applied\n"))
674 self.ui.warn(_("patch series fully applied\n"))
675 sys.exit(1)
675 sys.exit(1)
676 if not force:
676 if not force:
677 self.check_localchanges(repo)
677 self.check_localchanges(repo)
678
678
679 self.applied_dirty = 1;
679 self.applied_dirty = 1;
680 start = self.series_end()
680 start = self.series_end()
681 if start > 0:
681 if start > 0:
682 self.check_toppatch(repo)
682 self.check_toppatch(repo)
683 if not patch:
683 if not patch:
684 patch = self.series[start]
684 patch = self.series[start]
685 end = start + 1
685 end = start + 1
686 else:
686 else:
687 end = self.series.index(patch, start) + 1
687 end = self.series.index(patch, start) + 1
688 s = self.series[start:end]
688 s = self.series[start:end]
689 if mergeq:
689 if mergeq:
690 ret = self.mergepatch(repo, mergeq, s, wlock)
690 ret = self.mergepatch(repo, mergeq, s, wlock)
691 else:
691 else:
692 ret = self.apply(repo, s, list, wlock=wlock)
692 ret = self.apply(repo, s, list, wlock=wlock)
693 top = self.applied[-1].split(':')[1]
693 top = self.applied[-1].split(':')[1]
694 if ret[0]:
694 if ret[0]:
695 self.ui.write("Errors during apply, please fix and refresh %s\n" %
695 self.ui.write("Errors during apply, please fix and refresh %s\n" %
696 top)
696 top)
697 else:
697 else:
698 self.ui.write("Now at: %s\n" % top)
698 self.ui.write("Now at: %s\n" % top)
699 return ret[0]
699 return ret[0]
700
700
701 def pop(self, repo, patch=None, force=False, update=True, all=False,
701 def pop(self, repo, patch=None, force=False, update=True, all=False,
702 wlock=None):
702 wlock=None):
703 def getfile(f, rev):
703 def getfile(f, rev):
704 t = repo.file(f).read(rev)
704 t = repo.file(f).read(rev)
705 try:
705 try:
706 repo.wfile(f, "w").write(t)
706 repo.wfile(f, "w").write(t)
707 except IOError:
707 except IOError:
708 try:
708 try:
709 os.makedirs(os.path.dirname(repo.wjoin(f)))
709 os.makedirs(os.path.dirname(repo.wjoin(f)))
710 except OSError, err:
710 except OSError, err:
711 if err.errno != errno.EEXIST: raise
711 if err.errno != errno.EEXIST: raise
712 repo.wfile(f, "w").write(t)
712 repo.wfile(f, "w").write(t)
713
713
714 if not wlock:
714 if not wlock:
715 wlock = repo.wlock()
715 wlock = repo.wlock()
716 if patch:
716 if patch:
717 # index, rev, patch
717 # index, rev, patch
718 info = self.isapplied(patch)
718 info = self.isapplied(patch)
719 if not info:
719 if not info:
720 patch = self.lookup(patch)
720 patch = self.lookup(patch)
721 info = self.isapplied(patch)
721 info = self.isapplied(patch)
722 if not info:
722 if not info:
723 raise util.Abort(_("patch %s is not applied") % patch)
723 raise util.Abort(_("patch %s is not applied") % patch)
724 if len(self.applied) == 0:
724 if len(self.applied) == 0:
725 self.ui.warn(_("no patches applied\n"))
725 self.ui.warn(_("no patches applied\n"))
726 sys.exit(1)
726 sys.exit(1)
727
727
728 if not update:
728 if not update:
729 parents = repo.dirstate.parents()
729 parents = repo.dirstate.parents()
730 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
730 rr = [ revlog.bin(x.split(':')[0]) for x in self.applied ]
731 for p in parents:
731 for p in parents:
732 if p in rr:
732 if p in rr:
733 self.ui.warn("qpop: forcing dirstate update\n")
733 self.ui.warn("qpop: forcing dirstate update\n")
734 update = True
734 update = True
735
735
736 if not force and update:
736 if not force and update:
737 self.check_localchanges(repo)
737 self.check_localchanges(repo)
738
738
739 self.applied_dirty = 1;
739 self.applied_dirty = 1;
740 end = len(self.applied)
740 end = len(self.applied)
741 if not patch:
741 if not patch:
742 if all:
742 if all:
743 popi = 0
743 popi = 0
744 else:
744 else:
745 popi = len(self.applied) - 1
745 popi = len(self.applied) - 1
746 else:
746 else:
747 popi = info[0] + 1
747 popi = info[0] + 1
748 if popi >= end:
748 if popi >= end:
749 self.ui.warn("qpop: %s is already at the top\n" % patch)
749 self.ui.warn("qpop: %s is already at the top\n" % patch)
750 return
750 return
751 info = [ popi ] + self.applied[popi].split(':')
751 info = [ popi ] + self.applied[popi].split(':')
752
752
753 start = info[0]
753 start = info[0]
754 rev = revlog.bin(info[1])
754 rev = revlog.bin(info[1])
755
755
756 # we know there are no local changes, so we can make a simplified
756 # we know there are no local changes, so we can make a simplified
757 # form of hg.update.
757 # form of hg.update.
758 if update:
758 if update:
759 top = self.check_toppatch(repo)
759 top = self.check_toppatch(repo)
760 qp = self.qparents(repo, rev)
760 qp = self.qparents(repo, rev)
761 changes = repo.changelog.read(qp)
761 changes = repo.changelog.read(qp)
762 mf1 = repo.manifest.readflags(changes[0])
762 mf1 = repo.manifest.readflags(changes[0])
763 mmap = repo.manifest.read(changes[0])
763 mmap = repo.manifest.read(changes[0])
764 (c, a, r, d, u) = repo.changes(qp, top)
764 (c, a, r, d, u) = repo.changes(qp, top)
765 if d:
765 if d:
766 raise util.Abort("deletions found between repo revs")
766 raise util.Abort("deletions found between repo revs")
767 for f in c:
767 for f in c:
768 getfile(f, mmap[f])
768 getfile(f, mmap[f])
769 for f in r:
769 for f in r:
770 getfile(f, mmap[f])
770 getfile(f, mmap[f])
771 util.set_exec(repo.wjoin(f), mf1[f])
771 util.set_exec(repo.wjoin(f), mf1[f])
772 repo.dirstate.update(c + r, 'n')
772 repo.dirstate.update(c + r, 'n')
773 for f in a:
773 for f in a:
774 try: os.unlink(repo.wjoin(f))
774 try: os.unlink(repo.wjoin(f))
775 except: raise
775 except: raise
776 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
776 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
777 except: pass
777 except: pass
778 if a:
778 if a:
779 repo.dirstate.forget(a)
779 repo.dirstate.forget(a)
780 repo.dirstate.setparents(qp, revlog.nullid)
780 repo.dirstate.setparents(qp, revlog.nullid)
781 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
781 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
782 del self.applied[start:end]
782 del self.applied[start:end]
783 if len(self.applied):
783 if len(self.applied):
784 self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
784 self.ui.write("Now at: %s\n" % self.applied[-1].split(':')[1])
785 else:
785 else:
786 self.ui.write("Patch queue now empty\n")
786 self.ui.write("Patch queue now empty\n")
787
787
788 def diff(self, repo, files):
788 def diff(self, repo, files):
789 top = self.check_toppatch(repo)
789 top = self.check_toppatch(repo)
790 if not top:
790 if not top:
791 self.ui.write("No patches applied\n")
791 self.ui.write("No patches applied\n")
792 return
792 return
793 qp = self.qparents(repo, top)
793 qp = self.qparents(repo, top)
794 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
794 commands.dodiff(sys.stdout, self.ui, repo, qp, None, files)
795
795
796 def refresh(self, repo, msg=None, short=False):
796 def refresh(self, repo, msg=None, short=False):
797 if len(self.applied) == 0:
797 if len(self.applied) == 0:
798 self.ui.write("No patches applied\n")
798 self.ui.write("No patches applied\n")
799 return
799 return
800 wlock = repo.wlock()
800 wlock = repo.wlock()
801 self.check_toppatch(repo)
801 self.check_toppatch(repo)
802 qp = self.qparents(repo)
802 qp = self.qparents(repo)
803 (top, patch) = self.applied[-1].split(':')
803 (top, patch) = self.applied[-1].split(':')
804 top = revlog.bin(top)
804 top = revlog.bin(top)
805 cparents = repo.changelog.parents(top)
805 cparents = repo.changelog.parents(top)
806 patchparent = self.qparents(repo, top)
806 patchparent = self.qparents(repo, top)
807 message, comments, user, date, patchfound = self.readheaders(patch)
807 message, comments, user, date, patchfound = self.readheaders(patch)
808
808
809 patchf = self.opener(patch, "w")
809 patchf = self.opener(patch, "w")
810 if comments:
810 if comments:
811 comments = "\n".join(comments) + '\n\n'
811 comments = "\n".join(comments) + '\n\n'
812 patchf.write(comments)
812 patchf.write(comments)
813
813
814 tip = repo.changelog.tip()
814 tip = repo.changelog.tip()
815 if top == tip:
815 if top == tip:
816 # if the top of our patch queue is also the tip, there is an
816 # if the top of our patch queue is also the tip, there is an
817 # optimization here. We update the dirstate in place and strip
817 # optimization here. We update the dirstate in place and strip
818 # off the tip commit. Then just commit the current directory
818 # off the tip commit. Then just commit the current directory
819 # tree. We can also send repo.commit the list of files
819 # tree. We can also send repo.commit the list of files
820 # changed to speed up the diff
820 # changed to speed up the diff
821 #
821 #
822 # in short mode, we only diff the files included in the
822 # in short mode, we only diff the files included in the
823 # patch already
823 # patch already
824 #
824 #
825 # this should really read:
825 # this should really read:
826 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
826 #(cc, dd, aa, aa2, uu) = repo.changes(tip, patchparent)
827 # but we do it backwards to take advantage of manifest/chlog
827 # but we do it backwards to take advantage of manifest/chlog
828 # caching against the next repo.changes call
828 # caching against the next repo.changes call
829 #
829 #
830 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
830 (cc, aa, dd, aa2, uu) = repo.changes(patchparent, tip)
831 if short:
831 if short:
832 filelist = cc + aa + dd
832 filelist = cc + aa + dd
833 else:
833 else:
834 filelist = None
834 filelist = None
835 (c, a, r, d, u) = repo.changes(None, None, filelist)
835 (c, a, r, d, u) = repo.changes(None, None, filelist)
836
836
837 # we might end up with files that were added between tip and
837 # we might end up with files that were added between tip and
838 # the dirstate parent, but then changed in the local dirstate.
838 # the dirstate parent, but then changed in the local dirstate.
839 # in this case, we want them to only show up in the added section
839 # in this case, we want them to only show up in the added section
840 for x in c:
840 for x in c:
841 if x not in aa:
841 if x not in aa:
842 cc.append(x)
842 cc.append(x)
843 # we might end up with files added by the local dirstate that
843 # we might end up with files added by the local dirstate that
844 # were deleted by the patch. In this case, they should only
844 # were deleted by the patch. In this case, they should only
845 # show up in the changed section.
845 # show up in the changed section.
846 for x in a:
846 for x in a:
847 if x in dd:
847 if x in dd:
848 del dd[dd.index(x)]
848 del dd[dd.index(x)]
849 cc.append(x)
849 cc.append(x)
850 else:
850 else:
851 aa.append(x)
851 aa.append(x)
852 # make sure any files deleted in the local dirstate
852 # make sure any files deleted in the local dirstate
853 # are not in the add or change column of the patch
853 # are not in the add or change column of the patch
854 forget = []
854 forget = []
855 for x in d + r:
855 for x in d + r:
856 if x in aa:
856 if x in aa:
857 del aa[aa.index(x)]
857 del aa[aa.index(x)]
858 forget.append(x)
858 forget.append(x)
859 continue
859 continue
860 elif x in cc:
860 elif x in cc:
861 del cc[cc.index(x)]
861 del cc[cc.index(x)]
862 dd.append(x)
862 dd.append(x)
863
863
864 c = list(util.unique(cc))
864 c = list(util.unique(cc))
865 r = list(util.unique(dd))
865 r = list(util.unique(dd))
866 a = list(util.unique(aa))
866 a = list(util.unique(aa))
867 filelist = list(util.unique(c + r + a ))
867 filelist = list(util.unique(c + r + a ))
868 commands.dodiff(patchf, self.ui, repo, patchparent, None,
868 commands.dodiff(patchf, self.ui, repo, patchparent, None,
869 filelist, changes=(c, a, r, [], u))
869 filelist, changes=(c, a, r, [], u))
870 patchf.close()
870 patchf.close()
871
871
872 changes = repo.changelog.read(tip)
872 changes = repo.changelog.read(tip)
873 repo.dirstate.setparents(*cparents)
873 repo.dirstate.setparents(*cparents)
874 repo.dirstate.update(a, 'a')
874 repo.dirstate.update(a, 'a')
875 repo.dirstate.update(r, 'r')
875 repo.dirstate.update(r, 'r')
876 repo.dirstate.update(c, 'n')
876 repo.dirstate.update(c, 'n')
877 repo.dirstate.forget(forget)
877 repo.dirstate.forget(forget)
878
878
879 if not msg:
879 if not msg:
880 if not message:
880 if not message:
881 message = "patch queue: %s\n" % patch
881 message = "patch queue: %s\n" % patch
882 else:
882 else:
883 message = "\n".join(message)
883 message = "\n".join(message)
884 else:
884 else:
885 message = msg
885 message = msg
886
886
887 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
887 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
888 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
888 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
889 self.applied[-1] = revlog.hex(n) + ':' + patch
889 self.applied[-1] = revlog.hex(n) + ':' + patch
890 self.applied_dirty = 1
890 self.applied_dirty = 1
891 else:
891 else:
892 commands.dodiff(patchf, self.ui, repo, patchparent, None)
892 commands.dodiff(patchf, self.ui, repo, patchparent, None)
893 patchf.close()
893 patchf.close()
894 self.pop(repo, force=True, wlock=wlock)
894 self.pop(repo, force=True, wlock=wlock)
895 self.push(repo, force=True, wlock=wlock)
895 self.push(repo, force=True, wlock=wlock)
896
896
897 def init(self, repo, create=False):
897 def init(self, repo, create=False):
898 if os.path.isdir(self.path):
898 if os.path.isdir(self.path):
899 raise util.Abort(_("patch queue directory already exists"))
899 raise util.Abort(_("patch queue directory already exists"))
900 os.mkdir(self.path)
900 os.mkdir(self.path)
901 if create:
901 if create:
902 return self.qrepo(create=True)
902 return self.qrepo(create=True)
903
903
904 def unapplied(self, repo, patch=None):
904 def unapplied(self, repo, patch=None):
905 if patch and patch not in self.series:
905 if patch and patch not in self.series:
906 raise util.Abort(_("patch %s is not in series file") % patch)
906 raise util.Abort(_("patch %s is not in series file") % patch)
907 if not patch:
907 if not patch:
908 start = self.series_end()
908 start = self.series_end()
909 else:
909 else:
910 start = self.series.index(patch) + 1
910 start = self.series.index(patch) + 1
911 for p in self.series[start:]:
911 for p in self.series[start:]:
912 if self.ui.verbose:
912 if self.ui.verbose:
913 self.ui.write("%d " % self.series.index(p))
913 self.ui.write("%d " % self.series.index(p))
914 self.ui.write("%s\n" % p)
914 self.ui.write("%s\n" % p)
915
915
916 def qseries(self, repo, missing=None):
916 def qseries(self, repo, missing=None):
917 start = self.series_end()
917 start = self.series_end()
918 if not missing:
918 if not missing:
919 for p in self.series[:start]:
919 for p in self.series[:start]:
920 if self.ui.verbose:
920 if self.ui.verbose:
921 self.ui.write("%d A " % self.series.index(p))
921 self.ui.write("%d A " % self.series.index(p))
922 self.ui.write("%s\n" % p)
922 self.ui.write("%s\n" % p)
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 U " % self.series.index(p))
925 self.ui.write("%d U " % self.series.index(p))
926 self.ui.write("%s\n" % p)
926 self.ui.write("%s\n" % p)
927 else:
927 else:
928 list = []
928 list = []
929 for root, dirs, files in os.walk(self.path):
929 for root, dirs, files in os.walk(self.path):
930 d = root[len(self.path) + 1:]
930 d = root[len(self.path) + 1:]
931 for f in files:
931 for f in files:
932 fl = os.path.join(d, f)
932 fl = os.path.join(d, f)
933 if (fl not in self.series and
933 if (fl not in self.series and
934 fl not in (self.status_path, self.series_path)
934 fl not in (self.status_path, self.series_path)
935 and not fl.startswith('.')):
935 and not fl.startswith('.')):
936 list.append(fl)
936 list.append(fl)
937 list.sort()
937 list.sort()
938 if list:
938 if list:
939 for x in list:
939 for x in list:
940 if self.ui.verbose:
940 if self.ui.verbose:
941 self.ui.write("D ")
941 self.ui.write("D ")
942 self.ui.write("%s\n" % x)
942 self.ui.write("%s\n" % x)
943
943
944 def issaveline(self, l):
944 def issaveline(self, l):
945 name = l.split(':')[1]
945 name = l.split(':')[1]
946 if name == '.hg.patches.save.line':
946 if name == '.hg.patches.save.line':
947 return True
947 return True
948
948
949 def qrepo(self, create=False):
949 def qrepo(self, create=False):
950 if create or os.path.isdir(os.path.join(self.path, ".hg")):
950 if create or os.path.isdir(os.path.join(self.path, ".hg")):
951 return hg.repository(self.ui, path=self.path, create=create)
951 return hg.repository(self.ui, path=self.path, create=create)
952
952
953 def restore(self, repo, rev, delete=None, qupdate=None):
953 def restore(self, repo, rev, delete=None, qupdate=None):
954 c = repo.changelog.read(rev)
954 c = repo.changelog.read(rev)
955 desc = c[4].strip()
955 desc = c[4].strip()
956 lines = desc.splitlines()
956 lines = desc.splitlines()
957 i = 0
957 i = 0
958 datastart = None
958 datastart = None
959 series = []
959 series = []
960 applied = []
960 applied = []
961 qpp = None
961 qpp = None
962 for i in xrange(0, len(lines)):
962 for i in xrange(0, len(lines)):
963 if lines[i] == 'Patch Data:':
963 if lines[i] == 'Patch Data:':
964 datastart = i + 1
964 datastart = i + 1
965 elif lines[i].startswith('Dirstate:'):
965 elif lines[i].startswith('Dirstate:'):
966 l = lines[i].rstrip()
966 l = lines[i].rstrip()
967 l = l[10:].split(' ')
967 l = l[10:].split(' ')
968 qpp = [ hg.bin(x) for x in l ]
968 qpp = [ hg.bin(x) for x in l ]
969 elif datastart != None:
969 elif datastart != None:
970 l = lines[i].rstrip()
970 l = lines[i].rstrip()
971 index = l.index(':')
971 index = l.index(':')
972 id = l[:index]
972 id = l[:index]
973 file = l[index + 1:]
973 file = l[index + 1:]
974 if id:
974 if id:
975 applied.append(l)
975 applied.append(l)
976 series.append(file)
976 series.append(file)
977 if datastart == None:
977 if datastart == None:
978 self.ui.warn("No saved patch data found\n")
978 self.ui.warn("No saved patch data found\n")
979 return 1
979 return 1
980 self.ui.warn("restoring status: %s\n" % lines[0])
980 self.ui.warn("restoring status: %s\n" % lines[0])
981 self.full_series = series
981 self.full_series = series
982 self.applied = applied
982 self.applied = applied
983 self.read_series(self.full_series)
983 self.read_series(self.full_series)
984 self.series_dirty = 1
984 self.series_dirty = 1
985 self.applied_dirty = 1
985 self.applied_dirty = 1
986 heads = repo.changelog.heads()
986 heads = repo.changelog.heads()
987 if delete:
987 if delete:
988 if rev not in heads:
988 if rev not in heads:
989 self.ui.warn("save entry has children, leaving it alone\n")
989 self.ui.warn("save entry has children, leaving it alone\n")
990 else:
990 else:
991 self.ui.warn("removing save entry %s\n" % hg.short(rev))
991 self.ui.warn("removing save entry %s\n" % hg.short(rev))
992 pp = repo.dirstate.parents()
992 pp = repo.dirstate.parents()
993 if rev in pp:
993 if rev in pp:
994 update = True
994 update = True
995 else:
995 else:
996 update = False
996 update = False
997 self.strip(repo, rev, update=update, backup='strip')
997 self.strip(repo, rev, update=update, backup='strip')
998 if qpp:
998 if qpp:
999 self.ui.warn("saved queue repository parents: %s %s\n" %
999 self.ui.warn("saved queue repository parents: %s %s\n" %
1000 (hg.short(qpp[0]), hg.short(qpp[1])))
1000 (hg.short(qpp[0]), hg.short(qpp[1])))
1001 if qupdate:
1001 if qupdate:
1002 print "queue directory updating"
1002 print "queue directory updating"
1003 r = self.qrepo()
1003 r = self.qrepo()
1004 if not r:
1004 if not r:
1005 self.ui.warn("Unable to load queue repository\n")
1005 self.ui.warn("Unable to load queue repository\n")
1006 return 1
1006 return 1
1007 r.update(qpp[0], allow=False, force=True)
1007 r.update(qpp[0], allow=False, force=True)
1008
1008
1009 def save(self, repo, msg=None):
1009 def save(self, repo, msg=None):
1010 if len(self.applied) == 0:
1010 if len(self.applied) == 0:
1011 self.ui.warn("save: no patches applied, exiting\n")
1011 self.ui.warn("save: no patches applied, exiting\n")
1012 return 1
1012 return 1
1013 if self.issaveline(self.applied[-1]):
1013 if self.issaveline(self.applied[-1]):
1014 self.ui.warn("status is already saved\n")
1014 self.ui.warn("status is already saved\n")
1015 return 1
1015 return 1
1016
1016
1017 ar = [ ':' + x for x in self.full_series ]
1017 ar = [ ':' + x for x in self.full_series ]
1018 if not msg:
1018 if not msg:
1019 msg = "hg patches saved state"
1019 msg = "hg patches saved state"
1020 else:
1020 else:
1021 msg = "hg patches: " + msg.rstrip('\r\n')
1021 msg = "hg patches: " + msg.rstrip('\r\n')
1022 r = self.qrepo()
1022 r = self.qrepo()
1023 if r:
1023 if r:
1024 pp = r.dirstate.parents()
1024 pp = r.dirstate.parents()
1025 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1025 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1026 msg += "\n\nPatch Data:\n"
1026 msg += "\n\nPatch Data:\n"
1027 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
1027 text = msg + "\n".join(self.applied) + '\n' + (ar and "\n".join(ar)
1028 + '\n' or "")
1028 + '\n' or "")
1029 n = repo.commit(None, text, user=None, force=1)
1029 n = repo.commit(None, text, user=None, force=1)
1030 if not n:
1030 if not n:
1031 self.ui.warn("repo commit failed\n")
1031 self.ui.warn("repo commit failed\n")
1032 return 1
1032 return 1
1033 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
1033 self.applied.append(revlog.hex(n) + ":" + '.hg.patches.save.line')
1034 self.applied_dirty = 1
1034 self.applied_dirty = 1
1035
1035
1036 def full_series_end(self):
1036 def full_series_end(self):
1037 if len(self.applied) > 0:
1037 if len(self.applied) > 0:
1038 (top, p) = self.applied[-1].split(':')
1038 (top, p) = self.applied[-1].split(':')
1039 end = self.find_series(p)
1039 end = self.find_series(p)
1040 if end == None:
1040 if end == None:
1041 return len(self.full_series)
1041 return len(self.full_series)
1042 return end + 1
1042 return end + 1
1043 return 0
1043 return 0
1044
1044
1045 def series_end(self):
1045 def series_end(self):
1046 end = 0
1046 end = 0
1047 if len(self.applied) > 0:
1047 if len(self.applied) > 0:
1048 (top, p) = self.applied[-1].split(':')
1048 (top, p) = self.applied[-1].split(':')
1049 try:
1049 try:
1050 end = self.series.index(p)
1050 end = self.series.index(p)
1051 except ValueError:
1051 except ValueError:
1052 return 0
1052 return 0
1053 return end + 1
1053 return end + 1
1054 return end
1054 return end
1055
1055
1056 def qapplied(self, repo, patch=None):
1056 def qapplied(self, repo, patch=None):
1057 if patch and patch not in self.series:
1057 if patch and patch not in self.series:
1058 raise util.Abort(_("patch %s is not in series file") % patch)
1058 raise util.Abort(_("patch %s is not in series file") % patch)
1059 if not patch:
1059 if not patch:
1060 end = len(self.applied)
1060 end = len(self.applied)
1061 else:
1061 else:
1062 end = self.series.index(patch) + 1
1062 end = self.series.index(patch) + 1
1063 for x in xrange(end):
1063 for x in xrange(end):
1064 p = self.appliedname(x)
1064 p = self.appliedname(x)
1065 self.ui.write("%s\n" % p)
1065 self.ui.write("%s\n" % p)
1066
1066
1067 def appliedname(self, index):
1067 def appliedname(self, index):
1068 p = self.applied[index]
1068 p = self.applied[index]
1069 pname = p.split(':')[1]
1069 pname = p.split(':')[1]
1070 if not self.ui.verbose:
1070 if not self.ui.verbose:
1071 p = pname
1071 p = pname
1072 else:
1072 else:
1073 p = str(self.series.index(pname)) + " " + p
1073 p = str(self.series.index(pname)) + " " + p
1074 return p
1074 return p
1075
1075
1076 def top(self, repo):
1076 def top(self, repo):
1077 if len(self.applied):
1077 if len(self.applied):
1078 p = self.appliedname(-1)
1078 p = self.appliedname(-1)
1079 self.ui.write(p + '\n')
1079 self.ui.write(p + '\n')
1080 else:
1080 else:
1081 self.ui.write("No patches applied\n")
1081 self.ui.write("No patches applied\n")
1082
1082
1083 def next(self, repo):
1083 def next(self, repo):
1084 end = self.series_end()
1084 end = self.series_end()
1085 if end == len(self.series):
1085 if end == len(self.series):
1086 self.ui.write("All patches applied\n")
1086 self.ui.write("All patches applied\n")
1087 else:
1087 else:
1088 p = self.series[end]
1088 p = self.series[end]
1089 if self.ui.verbose:
1089 if self.ui.verbose:
1090 self.ui.write("%d " % self.series.index(p))
1090 self.ui.write("%d " % self.series.index(p))
1091 self.ui.write(p + '\n')
1091 self.ui.write(p + '\n')
1092
1092
1093 def prev(self, repo):
1093 def prev(self, repo):
1094 if len(self.applied) > 1:
1094 if len(self.applied) > 1:
1095 p = self.appliedname(-2)
1095 p = self.appliedname(-2)
1096 self.ui.write(p + '\n')
1096 self.ui.write(p + '\n')
1097 elif len(self.applied) == 1:
1097 elif len(self.applied) == 1:
1098 self.ui.write("Only one patch applied\n")
1098 self.ui.write("Only one patch applied\n")
1099 else:
1099 else:
1100 self.ui.write("No patches applied\n")
1100 self.ui.write("No patches applied\n")
1101
1101
1102 def qimport(self, repo, files, patch=None, existing=None, force=None):
1102 def qimport(self, repo, files, patch=None, existing=None, force=None):
1103 if len(files) > 1 and patch:
1103 if len(files) > 1 and patch:
1104 raise util.Abort(_('option "-n" not valid when importing multiple '
1104 raise util.Abort(_('option "-n" not valid when importing multiple '
1105 'files'))
1105 'files'))
1106 i = 0
1106 i = 0
1107 added = []
1107 added = []
1108 for filename in files:
1108 for filename in files:
1109 if existing:
1109 if existing:
1110 if not patch:
1110 if not patch:
1111 patch = filename
1111 patch = filename
1112 if not os.path.isfile(os.path.join(self.path, patch)):
1112 if not os.path.isfile(os.path.join(self.path, patch)):
1113 raise util.Abort(_("patch %s does not exist") % patch)
1113 raise util.Abort(_("patch %s does not exist") % patch)
1114 else:
1114 else:
1115 try:
1115 try:
1116 text = file(filename).read()
1116 text = file(filename).read()
1117 except IOError:
1117 except IOError:
1118 raise util.Abort(_("unable to read %s") % patch)
1118 raise util.Abort(_("unable to read %s") % patch)
1119 if not patch:
1119 if not patch:
1120 patch = os.path.split(filename)[1]
1120 patch = os.path.split(filename)[1]
1121 if not force and os.path.exists(os.path.join(self.path, patch)):
1121 if not force and os.path.exists(os.path.join(self.path, patch)):
1122 raise util.Abort(_('patch "%s" already exists') % patch)
1122 raise util.Abort(_('patch "%s" already exists') % patch)
1123 patchf = self.opener(patch, "w")
1123 patchf = self.opener(patch, "w")
1124 patchf.write(text)
1124 patchf.write(text)
1125 if patch in self.series:
1125 if patch in self.series:
1126 raise util.Abort(_('patch %s is already in the series file')
1126 raise util.Abort(_('patch %s is already in the series file')
1127 % patch)
1127 % patch)
1128 index = self.full_series_end() + i
1128 index = self.full_series_end() + i
1129 self.full_series[index:index] = [patch]
1129 self.full_series[index:index] = [patch]
1130 self.read_series(self.full_series)
1130 self.read_series(self.full_series)
1131 self.ui.warn("adding %s to series file\n" % patch)
1131 self.ui.warn("adding %s to series file\n" % patch)
1132 i += 1
1132 i += 1
1133 added.append(patch)
1133 added.append(patch)
1134 patch = None
1134 patch = None
1135 self.series_dirty = 1
1135 self.series_dirty = 1
1136 qrepo = self.qrepo()
1136 qrepo = self.qrepo()
1137 if qrepo:
1137 if qrepo:
1138 qrepo.add(added)
1138 qrepo.add(added)
1139
1139
1140 def delete(ui, repo, patch, **opts):
1140 def delete(ui, repo, patch, **opts):
1141 """remove a patch from the series file"""
1141 """remove a patch from the series file"""
1142 q = repo.mq
1142 q = repo.mq
1143 q.delete(repo, patch)
1143 q.delete(repo, patch)
1144 q.save_dirty()
1144 q.save_dirty()
1145 return 0
1145 return 0
1146
1146
1147 def applied(ui, repo, patch=None, **opts):
1147 def applied(ui, repo, patch=None, **opts):
1148 """print the patches already applied"""
1148 """print the patches already applied"""
1149 repo.mq.qapplied(repo, patch)
1149 repo.mq.qapplied(repo, patch)
1150 return 0
1150 return 0
1151
1151
1152 def unapplied(ui, repo, patch=None, **opts):
1152 def unapplied(ui, repo, patch=None, **opts):
1153 """print the patches not yet applied"""
1153 """print the patches not yet applied"""
1154 repo.mq.unapplied(repo, patch)
1154 repo.mq.unapplied(repo, patch)
1155 return 0
1155 return 0
1156
1156
1157 def qimport(ui, repo, *filename, **opts):
1157 def qimport(ui, repo, *filename, **opts):
1158 """import a patch"""
1158 """import a patch"""
1159 q = repo.mq
1159 q = repo.mq
1160 q.qimport(repo, filename, patch=opts['name'],
1160 q.qimport(repo, filename, patch=opts['name'],
1161 existing=opts['existing'], force=opts['force'])
1161 existing=opts['existing'], force=opts['force'])
1162 q.save_dirty()
1162 q.save_dirty()
1163 return 0
1163 return 0
1164
1164
1165 def init(ui, repo, **opts):
1165 def init(ui, repo, **opts):
1166 """init a new queue repository"""
1166 """init a new queue repository"""
1167 q = repo.mq
1167 q = repo.mq
1168 r = q.init(repo, create=opts['create_repo'])
1168 r = q.init(repo, create=opts['create_repo'])
1169 q.save_dirty()
1169 q.save_dirty()
1170 if r:
1170 if r:
1171 fp = r.wopener('.hgignore', 'w')
1171 fp = r.wopener('.hgignore', 'w')
1172 print >> fp, 'syntax: glob'
1172 print >> fp, 'syntax: glob'
1173 print >> fp, 'status'
1173 print >> fp, 'status'
1174 fp.close()
1174 fp.close()
1175 r.wopener('series', 'w').close()
1175 r.wopener('series', 'w').close()
1176 r.add(['.hgignore', 'series'])
1176 r.add(['.hgignore', 'series'])
1177 return 0
1177 return 0
1178
1178
1179 def clone(ui, source, dest=None, **opts):
1179 def clone(ui, source, dest=None, **opts):
1180 '''clone main and patch repository at same time
1180 '''clone main and patch repository at same time
1181
1181
1182 If source is local, destination will have no patches applied. If
1182 If source is local, destination will have no patches applied. If
1183 source is remote, this command can not check if patches are
1183 source is remote, this command can not check if patches are
1184 applied in source, so cannot guarantee that patches are not
1184 applied in source, so cannot guarantee that patches are not
1185 applied in destination. If you clone remote repository, be sure
1185 applied in destination. If you clone remote repository, be sure
1186 before that it has no patches applied.
1186 before that it has no patches applied.
1187
1187
1188 Source patch repository is looked for in <src>/.hg/patches by
1188 Source patch repository is looked for in <src>/.hg/patches by
1189 default. Use -p <url> to change.
1189 default. Use -p <url> to change.
1190 '''
1190 '''
1191 ui.setconfig_remoteopts(**opts)
1191 ui.setconfig_remoteopts(**opts)
1192 if dest is None:
1192 if dest is None:
1193 dest = hg.defaultdest(source)
1193 dest = hg.defaultdest(source)
1194 sr = hg.repository(ui, ui.expandpath(source))
1194 sr = hg.repository(ui, ui.expandpath(source))
1195 qbase, destrev = None, None
1195 qbase, destrev = None, None
1196 if sr.local():
1196 if sr.local():
1197 reposetup(ui, sr)
1197 reposetup(ui, sr)
1198 sq = repomap[sr]
1198 if sr.mq.applied:
1199 if sq.applied:
1199 qbase = revlog.bin(sr.mq.applied[0].split(':')[0])
1200 qbase = revlog.bin(sq.applied[0].split(':')[0])
1201 if not hg.islocal(dest):
1200 if not hg.islocal(dest):
1202 destrev = sr.parents(qbase)[0]
1201 destrev = sr.parents(qbase)[0]
1203 ui.note(_('cloning main repo\n'))
1202 ui.note(_('cloning main repo\n'))
1204 sr, dr = hg.clone(ui, sr, dest,
1203 sr, dr = hg.clone(ui, sr, dest,
1205 pull=opts['pull'],
1204 pull=opts['pull'],
1206 rev=destrev,
1205 rev=destrev,
1207 update=False,
1206 update=False,
1208 stream=opts['uncompressed'])
1207 stream=opts['uncompressed'])
1209 ui.note(_('cloning patch repo\n'))
1208 ui.note(_('cloning patch repo\n'))
1210 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1209 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1211 dr.url() + '/.hg/patches',
1210 dr.url() + '/.hg/patches',
1212 pull=opts['pull'],
1211 pull=opts['pull'],
1213 update=not opts['noupdate'],
1212 update=not opts['noupdate'],
1214 stream=opts['uncompressed'])
1213 stream=opts['uncompressed'])
1215 if dr.local():
1214 if dr.local():
1216 if qbase:
1215 if qbase:
1217 ui.note(_('stripping applied patches from destination repo\n'))
1216 ui.note(_('stripping applied patches from destination repo\n'))
1218 reposetup(ui, dr)
1217 reposetup(ui, dr)
1219 dq = repomap[dr]
1218 dr.mq.strip(dr, qbase, update=False, backup=None)
1220 dq.strip(dr, qbase, update=False, backup=None)
1221 if not opts['noupdate']:
1219 if not opts['noupdate']:
1222 ui.note(_('updating destination repo\n'))
1220 ui.note(_('updating destination repo\n'))
1223 dr.update(dr.changelog.tip())
1221 dr.update(dr.changelog.tip())
1224
1222
1225 def commit(ui, repo, *pats, **opts):
1223 def commit(ui, repo, *pats, **opts):
1226 """commit changes in the queue repository"""
1224 """commit changes in the queue repository"""
1227 q = repo.mq
1225 q = repo.mq
1228 r = q.qrepo()
1226 r = q.qrepo()
1229 if not r: raise util.Abort('no queue repository')
1227 if not r: raise util.Abort('no queue repository')
1230 commands.commit(r.ui, r, *pats, **opts)
1228 commands.commit(r.ui, r, *pats, **opts)
1231
1229
1232 def series(ui, repo, **opts):
1230 def series(ui, repo, **opts):
1233 """print the entire series file"""
1231 """print the entire series file"""
1234 repo.mq.qseries(repo, missing=opts['missing'])
1232 repo.mq.qseries(repo, missing=opts['missing'])
1235 return 0
1233 return 0
1236
1234
1237 def top(ui, repo, **opts):
1235 def top(ui, repo, **opts):
1238 """print the name of the current patch"""
1236 """print the name of the current patch"""
1239 repo.mq.top(repo)
1237 repo.mq.top(repo)
1240 return 0
1238 return 0
1241
1239
1242 def next(ui, repo, **opts):
1240 def next(ui, repo, **opts):
1243 """print the name of the next patch"""
1241 """print the name of the next patch"""
1244 repo.mq.next(repo)
1242 repo.mq.next(repo)
1245 return 0
1243 return 0
1246
1244
1247 def prev(ui, repo, **opts):
1245 def prev(ui, repo, **opts):
1248 """print the name of the previous patch"""
1246 """print the name of the previous patch"""
1249 repo.mq.prev(repo)
1247 repo.mq.prev(repo)
1250 return 0
1248 return 0
1251
1249
1252 def new(ui, repo, patch, **opts):
1250 def new(ui, repo, patch, **opts):
1253 """create a new patch"""
1251 """create a new patch"""
1254 q = repo.mq
1252 q = repo.mq
1255 message=commands.logmessage(**opts)
1253 message=commands.logmessage(**opts)
1256 q.new(repo, patch, msg=message, force=opts['force'])
1254 q.new(repo, patch, msg=message, force=opts['force'])
1257 q.save_dirty()
1255 q.save_dirty()
1258 return 0
1256 return 0
1259
1257
1260 def refresh(ui, repo, **opts):
1258 def refresh(ui, repo, **opts):
1261 """update the current patch"""
1259 """update the current patch"""
1262 q = repo.mq
1260 q = repo.mq
1263 message=commands.logmessage(**opts)
1261 message=commands.logmessage(**opts)
1264 q.refresh(repo, msg=message, short=opts['short'])
1262 q.refresh(repo, msg=message, short=opts['short'])
1265 q.save_dirty()
1263 q.save_dirty()
1266 return 0
1264 return 0
1267
1265
1268 def diff(ui, repo, *files, **opts):
1266 def diff(ui, repo, *files, **opts):
1269 """diff of the current patch"""
1267 """diff of the current patch"""
1270 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1268 # deep in the dirstate code, the walkhelper method wants a list, not a tuple
1271 repo.mq.diff(repo, list(files))
1269 repo.mq.diff(repo, list(files))
1272 return 0
1270 return 0
1273
1271
1274 def lastsavename(path):
1272 def lastsavename(path):
1275 (dir, base) = os.path.split(path)
1273 (dir, base) = os.path.split(path)
1276 names = os.listdir(dir)
1274 names = os.listdir(dir)
1277 namere = re.compile("%s.([0-9]+)" % base)
1275 namere = re.compile("%s.([0-9]+)" % base)
1278 max = None
1276 max = None
1279 maxname = None
1277 maxname = None
1280 for f in names:
1278 for f in names:
1281 m = namere.match(f)
1279 m = namere.match(f)
1282 if m:
1280 if m:
1283 index = int(m.group(1))
1281 index = int(m.group(1))
1284 if max == None or index > max:
1282 if max == None or index > max:
1285 max = index
1283 max = index
1286 maxname = f
1284 maxname = f
1287 if maxname:
1285 if maxname:
1288 return (os.path.join(dir, maxname), max)
1286 return (os.path.join(dir, maxname), max)
1289 return (None, None)
1287 return (None, None)
1290
1288
1291 def savename(path):
1289 def savename(path):
1292 (last, index) = lastsavename(path)
1290 (last, index) = lastsavename(path)
1293 if last is None:
1291 if last is None:
1294 index = 0
1292 index = 0
1295 newpath = path + ".%d" % (index + 1)
1293 newpath = path + ".%d" % (index + 1)
1296 return newpath
1294 return newpath
1297
1295
1298 def push(ui, repo, patch=None, **opts):
1296 def push(ui, repo, patch=None, **opts):
1299 """push the next patch onto the stack"""
1297 """push the next patch onto the stack"""
1300 q = repo.mq
1298 q = repo.mq
1301 mergeq = None
1299 mergeq = None
1302
1300
1303 if opts['all']:
1301 if opts['all']:
1304 patch = q.series[-1]
1302 patch = q.series[-1]
1305 if opts['merge']:
1303 if opts['merge']:
1306 if opts['name']:
1304 if opts['name']:
1307 newpath = opts['name']
1305 newpath = opts['name']
1308 else:
1306 else:
1309 newpath, i = lastsavename(q.path)
1307 newpath, i = lastsavename(q.path)
1310 if not newpath:
1308 if not newpath:
1311 ui.warn("no saved queues found, please use -n\n")
1309 ui.warn("no saved queues found, please use -n\n")
1312 return 1
1310 return 1
1313 mergeq = queue(ui, repo.join(""), newpath)
1311 mergeq = queue(ui, repo.join(""), newpath)
1314 ui.warn("merging with queue at: %s\n" % mergeq.path)
1312 ui.warn("merging with queue at: %s\n" % mergeq.path)
1315 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1313 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1316 mergeq=mergeq)
1314 mergeq=mergeq)
1317 q.save_dirty()
1315 q.save_dirty()
1318 return ret
1316 return ret
1319
1317
1320 def pop(ui, repo, patch=None, **opts):
1318 def pop(ui, repo, patch=None, **opts):
1321 """pop the current patch off the stack"""
1319 """pop the current patch off the stack"""
1322 localupdate = True
1320 localupdate = True
1323 if opts['name']:
1321 if opts['name']:
1324 q = queue(ui, repo.join(""), repo.join(opts['name']))
1322 q = queue(ui, repo.join(""), repo.join(opts['name']))
1325 ui.warn('using patch queue: %s\n' % q.path)
1323 ui.warn('using patch queue: %s\n' % q.path)
1326 localupdate = False
1324 localupdate = False
1327 else:
1325 else:
1328 q = repo.mq
1326 q = repo.mq
1329 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1327 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1330 q.save_dirty()
1328 q.save_dirty()
1331 return 0
1329 return 0
1332
1330
1333 def restore(ui, repo, rev, **opts):
1331 def restore(ui, repo, rev, **opts):
1334 """restore the queue state saved by a rev"""
1332 """restore the queue state saved by a rev"""
1335 rev = repo.lookup(rev)
1333 rev = repo.lookup(rev)
1336 q = repo.mq
1334 q = repo.mq
1337 q.restore(repo, rev, delete=opts['delete'],
1335 q.restore(repo, rev, delete=opts['delete'],
1338 qupdate=opts['update'])
1336 qupdate=opts['update'])
1339 q.save_dirty()
1337 q.save_dirty()
1340 return 0
1338 return 0
1341
1339
1342 def save(ui, repo, **opts):
1340 def save(ui, repo, **opts):
1343 """save current queue state"""
1341 """save current queue state"""
1344 q = repo.mq
1342 q = repo.mq
1345 message=commands.logmessage(**opts)
1343 message=commands.logmessage(**opts)
1346 ret = q.save(repo, msg=message)
1344 ret = q.save(repo, msg=message)
1347 if ret:
1345 if ret:
1348 return ret
1346 return ret
1349 q.save_dirty()
1347 q.save_dirty()
1350 if opts['copy']:
1348 if opts['copy']:
1351 path = q.path
1349 path = q.path
1352 if opts['name']:
1350 if opts['name']:
1353 newpath = os.path.join(q.basepath, opts['name'])
1351 newpath = os.path.join(q.basepath, opts['name'])
1354 if os.path.exists(newpath):
1352 if os.path.exists(newpath):
1355 if not os.path.isdir(newpath):
1353 if not os.path.isdir(newpath):
1356 raise util.Abort(_('destination %s exists and is not '
1354 raise util.Abort(_('destination %s exists and is not '
1357 'a directory') % newpath)
1355 'a directory') % newpath)
1358 if not opts['force']:
1356 if not opts['force']:
1359 raise util.Abort(_('destination %s exists, '
1357 raise util.Abort(_('destination %s exists, '
1360 'use -f to force') % newpath)
1358 'use -f to force') % newpath)
1361 else:
1359 else:
1362 newpath = savename(path)
1360 newpath = savename(path)
1363 ui.warn("copy %s to %s\n" % (path, newpath))
1361 ui.warn("copy %s to %s\n" % (path, newpath))
1364 util.copyfiles(path, newpath)
1362 util.copyfiles(path, newpath)
1365 if opts['empty']:
1363 if opts['empty']:
1366 try:
1364 try:
1367 os.unlink(os.path.join(q.path, q.status_path))
1365 os.unlink(os.path.join(q.path, q.status_path))
1368 except:
1366 except:
1369 pass
1367 pass
1370 return 0
1368 return 0
1371
1369
1372 def strip(ui, repo, rev, **opts):
1370 def strip(ui, repo, rev, **opts):
1373 """strip a revision and all later revs on the same branch"""
1371 """strip a revision and all later revs on the same branch"""
1374 rev = repo.lookup(rev)
1372 rev = repo.lookup(rev)
1375 backup = 'all'
1373 backup = 'all'
1376 if opts['backup']:
1374 if opts['backup']:
1377 backup = 'strip'
1375 backup = 'strip'
1378 elif opts['nobackup']:
1376 elif opts['nobackup']:
1379 backup = 'none'
1377 backup = 'none'
1380 repo.mq.strip(repo, rev, backup=backup)
1378 repo.mq.strip(repo, rev, backup=backup)
1381 return 0
1379 return 0
1382
1380
1383 def version(ui, q=None):
1381 def version(ui, q=None):
1384 """print the version number"""
1382 """print the version number"""
1385 ui.write("mq version %s\n" % versionstr)
1383 ui.write("mq version %s\n" % versionstr)
1386 return 0
1384 return 0
1387
1385
1388 def reposetup(ui, repo):
1386 def reposetup(ui, repo):
1389 class MqRepo(repo.__class__):
1387 class MqRepo(repo.__class__):
1390 def tags(self):
1388 def tags(self):
1391 if self.tagscache:
1389 if self.tagscache:
1392 return self.tagscache
1390 return self.tagscache
1393
1391
1394 tagscache = super(self.__class__, self).tags()
1392 tagscache = super(self.__class__, self).tags()
1395
1393
1396 q = self.mq
1394 q = self.mq
1397 if not q.applied:
1395 if not q.applied:
1398 return tagscache
1396 return tagscache
1399
1397
1400 mqtags = [patch.split(':') for patch in q.applied]
1398 mqtags = [patch.split(':') for patch in q.applied]
1401 mqtags.append((mqtags[-1][0], 'qtip'))
1399 mqtags.append((mqtags[-1][0], 'qtip'))
1402 mqtags.append((mqtags[0][0], 'qbase'))
1400 mqtags.append((mqtags[0][0], 'qbase'))
1403 for patch in mqtags:
1401 for patch in mqtags:
1404 if patch[1] in tagscache:
1402 if patch[1] in tagscache:
1405 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
1403 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
1406 else:
1404 else:
1407 tagscache[patch[1]] = revlog.bin(patch[0])
1405 tagscache[patch[1]] = revlog.bin(patch[0])
1408
1406
1409 return tagscache
1407 return tagscache
1410
1408
1411 repo.__class__ = MqRepo
1409 repo.__class__ = MqRepo
1412 repo.mq = queue(ui, repo.join(""))
1410 repo.mq = queue(ui, repo.join(""))
1413
1411
1414 cmdtable = {
1412 cmdtable = {
1415 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1413 "qapplied": (applied, [], 'hg qapplied [PATCH]'),
1416 "qclone": (clone,
1414 "qclone": (clone,
1417 [('', 'pull', None, _('use pull protocol to copy metadata')),
1415 [('', 'pull', None, _('use pull protocol to copy metadata')),
1418 ('U', 'noupdate', None, _('do not update the new working directories')),
1416 ('U', 'noupdate', None, _('do not update the new working directories')),
1419 ('', 'uncompressed', None,
1417 ('', 'uncompressed', None,
1420 _('use uncompressed transfer (fast over LAN)')),
1418 _('use uncompressed transfer (fast over LAN)')),
1421 ('e', 'ssh', '', _('specify ssh command to use')),
1419 ('e', 'ssh', '', _('specify ssh command to use')),
1422 ('p', 'patches', '', _('location of source patch repo')),
1420 ('p', 'patches', '', _('location of source patch repo')),
1423 ('', 'remotecmd', '',
1421 ('', 'remotecmd', '',
1424 _('specify hg command to run on the remote side'))],
1422 _('specify hg command to run on the remote side'))],
1425 'hg qclone [OPTION]... SOURCE [DEST]'),
1423 'hg qclone [OPTION]... SOURCE [DEST]'),
1426 "qcommit|qci":
1424 "qcommit|qci":
1427 (commit,
1425 (commit,
1428 commands.table["^commit|ci"][1],
1426 commands.table["^commit|ci"][1],
1429 'hg qcommit [OPTION]... [FILE]...'),
1427 'hg qcommit [OPTION]... [FILE]...'),
1430 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1428 "^qdiff": (diff, [], 'hg qdiff [FILE]...'),
1431 "qdelete": (delete, [], 'hg qdelete PATCH'),
1429 "qdelete": (delete, [], 'hg qdelete PATCH'),
1432 "^qimport":
1430 "^qimport":
1433 (qimport,
1431 (qimport,
1434 [('e', 'existing', None, 'import file in patch dir'),
1432 [('e', 'existing', None, 'import file in patch dir'),
1435 ('n', 'name', '', 'patch file name'),
1433 ('n', 'name', '', 'patch file name'),
1436 ('f', 'force', None, 'overwrite existing files')],
1434 ('f', 'force', None, 'overwrite existing files')],
1437 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1435 'hg qimport [-e] [-n NAME] [-f] FILE...'),
1438 "^qinit":
1436 "^qinit":
1439 (init,
1437 (init,
1440 [('c', 'create-repo', None, 'create queue repository')],
1438 [('c', 'create-repo', None, 'create queue repository')],
1441 'hg qinit [-c]'),
1439 'hg qinit [-c]'),
1442 "qnew":
1440 "qnew":
1443 (new,
1441 (new,
1444 [('m', 'message', '', _('use <text> as commit message')),
1442 [('m', 'message', '', _('use <text> as commit message')),
1445 ('l', 'logfile', '', _('read the commit message from <file>')),
1443 ('l', 'logfile', '', _('read the commit message from <file>')),
1446 ('f', 'force', None, 'force')],
1444 ('f', 'force', None, 'force')],
1447 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'),
1445 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'),
1448 "qnext": (next, [], 'hg qnext'),
1446 "qnext": (next, [], 'hg qnext'),
1449 "qprev": (prev, [], 'hg qprev'),
1447 "qprev": (prev, [], 'hg qprev'),
1450 "^qpop":
1448 "^qpop":
1451 (pop,
1449 (pop,
1452 [('a', 'all', None, 'pop all patches'),
1450 [('a', 'all', None, 'pop all patches'),
1453 ('n', 'name', '', 'queue name to pop'),
1451 ('n', 'name', '', 'queue name to pop'),
1454 ('f', 'force', None, 'forget any local changes')],
1452 ('f', 'force', None, 'forget any local changes')],
1455 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1453 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
1456 "^qpush":
1454 "^qpush":
1457 (push,
1455 (push,
1458 [('f', 'force', None, 'apply if the patch has rejects'),
1456 [('f', 'force', None, 'apply if the patch has rejects'),
1459 ('l', 'list', None, 'list patch name in commit text'),
1457 ('l', 'list', None, 'list patch name in commit text'),
1460 ('a', 'all', None, 'apply all patches'),
1458 ('a', 'all', None, 'apply all patches'),
1461 ('m', 'merge', None, 'merge from another queue'),
1459 ('m', 'merge', None, 'merge from another queue'),
1462 ('n', 'name', '', 'merge queue name')],
1460 ('n', 'name', '', 'merge queue name')],
1463 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1461 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
1464 "^qrefresh":
1462 "^qrefresh":
1465 (refresh,
1463 (refresh,
1466 [('m', 'message', '', _('change commit message with <text>')),
1464 [('m', 'message', '', _('change commit message with <text>')),
1467 ('l', 'logfile', '', _('change commit message with <file> content')),
1465 ('l', 'logfile', '', _('change commit message with <file> content')),
1468 ('s', 'short', None, 'short refresh')],
1466 ('s', 'short', None, 'short refresh')],
1469 'hg qrefresh [-m TEXT] [-l FILE] [-s]'),
1467 'hg qrefresh [-m TEXT] [-l FILE] [-s]'),
1470 "qrestore":
1468 "qrestore":
1471 (restore,
1469 (restore,
1472 [('d', 'delete', None, 'delete save entry'),
1470 [('d', 'delete', None, 'delete save entry'),
1473 ('u', 'update', None, 'update queue working dir')],
1471 ('u', 'update', None, 'update queue working dir')],
1474 'hg qrestore [-d] [-u] REV'),
1472 'hg qrestore [-d] [-u] REV'),
1475 "qsave":
1473 "qsave":
1476 (save,
1474 (save,
1477 [('m', 'message', '', _('use <text> as commit message')),
1475 [('m', 'message', '', _('use <text> as commit message')),
1478 ('l', 'logfile', '', _('read the commit message from <file>')),
1476 ('l', 'logfile', '', _('read the commit message from <file>')),
1479 ('c', 'copy', None, 'copy patch directory'),
1477 ('c', 'copy', None, 'copy patch directory'),
1480 ('n', 'name', '', 'copy directory name'),
1478 ('n', 'name', '', 'copy directory name'),
1481 ('e', 'empty', None, 'clear queue status file'),
1479 ('e', 'empty', None, 'clear queue status file'),
1482 ('f', 'force', None, 'force copy')],
1480 ('f', 'force', None, 'force copy')],
1483 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
1481 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
1484 "qseries":
1482 "qseries":
1485 (series,
1483 (series,
1486 [('m', 'missing', None, 'print patches not in series')],
1484 [('m', 'missing', None, 'print patches not in series')],
1487 'hg qseries [-m]'),
1485 'hg qseries [-m]'),
1488 "^strip":
1486 "^strip":
1489 (strip,
1487 (strip,
1490 [('f', 'force', None, 'force multi-head removal'),
1488 [('f', 'force', None, 'force multi-head removal'),
1491 ('b', 'backup', None, 'bundle unrelated changesets'),
1489 ('b', 'backup', None, 'bundle unrelated changesets'),
1492 ('n', 'nobackup', None, 'no backups')],
1490 ('n', 'nobackup', None, 'no backups')],
1493 'hg strip [-f] [-b] [-n] REV'),
1491 'hg strip [-f] [-b] [-n] REV'),
1494 "qtop": (top, [], 'hg qtop'),
1492 "qtop": (top, [], 'hg qtop'),
1495 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1493 "qunapplied": (unapplied, [], 'hg qunapplied [PATCH]'),
1496 "qversion": (version, [], 'hg qversion')
1494 "qversion": (version, [], 'hg qversion')
1497 }
1495 }
1498
1496
General Comments 0
You need to be logged in to leave comments. Login now