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