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