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