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