##// END OF EJS Templates
record: some docs...
Kirill Smelkov -
r5826:cc43d9f3 default
parent child Browse files
Show More
@@ -1,415 +1,450 b''
1 # record.py
1 # record.py
2 #
2 #
3 # Copyright 2007 Bryan O'Sullivan <bos@serpentine.com>
3 # Copyright 2007 Bryan O'Sullivan <bos@serpentine.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of
5 # This software may be used and distributed according to the terms of
6 # the GNU General Public License, incorporated herein by reference.
6 # the GNU General Public License, incorporated herein by reference.
7
7
8 '''interactive change selection during commit'''
8 '''interactive change selection during commit'''
9
9
10 from mercurial.i18n import _
10 from mercurial.i18n import _
11 from mercurial import cmdutil, commands, cmdutil, hg, mdiff, patch, revlog
11 from mercurial import cmdutil, commands, cmdutil, hg, mdiff, patch, revlog
12 from mercurial import util
12 from mercurial import util
13 import copy, cStringIO, errno, operator, os, re, shutil, tempfile
13 import copy, cStringIO, errno, operator, os, re, shutil, tempfile
14
14
15 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
15 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
16
16
17 def scanpatch(fp):
17 def scanpatch(fp):
18 """like patch.iterhunks, but yield different events
19
20 - ('file', [header_lines + fromfile + tofile])
21 - ('context', [context_lines])
22 - ('hunk', [hunk_lines])
23 - ('range', (-start,len, +start,len, diffp))
24 """
18 lr = patch.linereader(fp)
25 lr = patch.linereader(fp)
19
26
20 def scanwhile(first, p):
27 def scanwhile(first, p):
28 """scan lr while predicate holds"""
21 lines = [first]
29 lines = [first]
22 while True:
30 while True:
23 line = lr.readline()
31 line = lr.readline()
24 if not line:
32 if not line:
25 break
33 break
26 if p(line):
34 if p(line):
27 lines.append(line)
35 lines.append(line)
28 else:
36 else:
29 lr.push(line)
37 lr.push(line)
30 break
38 break
31 return lines
39 return lines
32
40
33 while True:
41 while True:
34 line = lr.readline()
42 line = lr.readline()
35 if not line:
43 if not line:
36 break
44 break
37 if line.startswith('diff --git a/'):
45 if line.startswith('diff --git a/'):
38 def notheader(line):
46 def notheader(line):
39 s = line.split(None, 1)
47 s = line.split(None, 1)
40 return not s or s[0] not in ('---', 'diff')
48 return not s or s[0] not in ('---', 'diff')
41 header = scanwhile(line, notheader)
49 header = scanwhile(line, notheader)
42 fromfile = lr.readline()
50 fromfile = lr.readline()
43 if fromfile.startswith('---'):
51 if fromfile.startswith('---'):
44 tofile = lr.readline()
52 tofile = lr.readline()
45 header += [fromfile, tofile]
53 header += [fromfile, tofile]
46 else:
54 else:
47 lr.push(fromfile)
55 lr.push(fromfile)
48 yield 'file', header
56 yield 'file', header
49 elif line[0] == ' ':
57 elif line[0] == ' ':
50 yield 'context', scanwhile(line, lambda l: l[0] in ' \\')
58 yield 'context', scanwhile(line, lambda l: l[0] in ' \\')
51 elif line[0] in '-+':
59 elif line[0] in '-+':
52 yield 'hunk', scanwhile(line, lambda l: l[0] in '-+\\')
60 yield 'hunk', scanwhile(line, lambda l: l[0] in '-+\\')
53 else:
61 else:
54 m = lines_re.match(line)
62 m = lines_re.match(line)
55 if m:
63 if m:
56 yield 'range', m.groups()
64 yield 'range', m.groups()
57 else:
65 else:
58 raise patch.PatchError('unknown patch content: %r' % line)
66 raise patch.PatchError('unknown patch content: %r' % line)
59
67
60 class header(object):
68 class header(object):
69 """patch header
70
71 XXX shoudn't we move this to mercurial/patch.py ?
72 """
61 diff_re = re.compile('diff --git a/(.*) b/(.*)$')
73 diff_re = re.compile('diff --git a/(.*) b/(.*)$')
62 allhunks_re = re.compile('(?:index|new file|deleted file) ')
74 allhunks_re = re.compile('(?:index|new file|deleted file) ')
63 pretty_re = re.compile('(?:new file|deleted file) ')
75 pretty_re = re.compile('(?:new file|deleted file) ')
64 special_re = re.compile('(?:index|new|deleted|copy|rename) ')
76 special_re = re.compile('(?:index|new|deleted|copy|rename) ')
65
77
66 def __init__(self, header):
78 def __init__(self, header):
67 self.header = header
79 self.header = header
68 self.hunks = []
80 self.hunks = []
69
81
70 def binary(self):
82 def binary(self):
71 for h in self.header:
83 for h in self.header:
72 if h.startswith('index '):
84 if h.startswith('index '):
73 return True
85 return True
74
86
75 def pretty(self, fp):
87 def pretty(self, fp):
76 for h in self.header:
88 for h in self.header:
77 if h.startswith('index '):
89 if h.startswith('index '):
78 fp.write(_('this modifies a binary file (all or nothing)\n'))
90 fp.write(_('this modifies a binary file (all or nothing)\n'))
79 break
91 break
80 if self.pretty_re.match(h):
92 if self.pretty_re.match(h):
81 fp.write(h)
93 fp.write(h)
82 if self.binary():
94 if self.binary():
83 fp.write(_('this is a binary file\n'))
95 fp.write(_('this is a binary file\n'))
84 break
96 break
85 if h.startswith('---'):
97 if h.startswith('---'):
86 fp.write(_('%d hunks, %d lines changed\n') %
98 fp.write(_('%d hunks, %d lines changed\n') %
87 (len(self.hunks),
99 (len(self.hunks),
88 sum([h.added + h.removed for h in self.hunks])))
100 sum([h.added + h.removed for h in self.hunks])))
89 break
101 break
90 fp.write(h)
102 fp.write(h)
91
103
92 def write(self, fp):
104 def write(self, fp):
93 fp.write(''.join(self.header))
105 fp.write(''.join(self.header))
94
106
95 def allhunks(self):
107 def allhunks(self):
96 for h in self.header:
108 for h in self.header:
97 if self.allhunks_re.match(h):
109 if self.allhunks_re.match(h):
98 return True
110 return True
99
111
100 def files(self):
112 def files(self):
101 fromfile, tofile = self.diff_re.match(self.header[0]).groups()
113 fromfile, tofile = self.diff_re.match(self.header[0]).groups()
102 if fromfile == tofile:
114 if fromfile == tofile:
103 return [fromfile]
115 return [fromfile]
104 return [fromfile, tofile]
116 return [fromfile, tofile]
105
117
106 def filename(self):
118 def filename(self):
107 return self.files()[-1]
119 return self.files()[-1]
108
120
109 def __repr__(self):
121 def __repr__(self):
110 return '<header %s>' % (' '.join(map(repr, self.files())))
122 return '<header %s>' % (' '.join(map(repr, self.files())))
111
123
112 def special(self):
124 def special(self):
113 for h in self.header:
125 for h in self.header:
114 if self.special_re.match(h):
126 if self.special_re.match(h):
115 return True
127 return True
116
128
117 def countchanges(hunk):
129 def countchanges(hunk):
130 """hunk -> (n+,n-)"""
118 add = len([h for h in hunk if h[0] == '+'])
131 add = len([h for h in hunk if h[0] == '+'])
119 rem = len([h for h in hunk if h[0] == '-'])
132 rem = len([h for h in hunk if h[0] == '-'])
120 return add, rem
133 return add, rem
121
134
122 class hunk(object):
135 class hunk(object):
136 """patch hunk
137
138 XXX shouldn't we merge this with patch.hunk ?
139 """
123 maxcontext = 3
140 maxcontext = 3
124
141
125 def __init__(self, header, fromline, toline, proc, before, hunk, after):
142 def __init__(self, header, fromline, toline, proc, before, hunk, after):
126 def trimcontext(number, lines):
143 def trimcontext(number, lines):
127 delta = len(lines) - self.maxcontext
144 delta = len(lines) - self.maxcontext
128 if False and delta > 0:
145 if False and delta > 0:
129 return number + delta, lines[:self.maxcontext]
146 return number + delta, lines[:self.maxcontext]
130 return number, lines
147 return number, lines
131
148
132 self.header = header
149 self.header = header
133 self.fromline, self.before = trimcontext(fromline, before)
150 self.fromline, self.before = trimcontext(fromline, before)
134 self.toline, self.after = trimcontext(toline, after)
151 self.toline, self.after = trimcontext(toline, after)
135 self.proc = proc
152 self.proc = proc
136 self.hunk = hunk
153 self.hunk = hunk
137 self.added, self.removed = countchanges(self.hunk)
154 self.added, self.removed = countchanges(self.hunk)
138
155
139 def write(self, fp):
156 def write(self, fp):
140 delta = len(self.before) + len(self.after)
157 delta = len(self.before) + len(self.after)
141 fromlen = delta + self.removed
158 fromlen = delta + self.removed
142 tolen = delta + self.added
159 tolen = delta + self.added
143 fp.write('@@ -%d,%d +%d,%d @@%s\n' %
160 fp.write('@@ -%d,%d +%d,%d @@%s\n' %
144 (self.fromline, fromlen, self.toline, tolen,
161 (self.fromline, fromlen, self.toline, tolen,
145 self.proc and (' ' + self.proc)))
162 self.proc and (' ' + self.proc)))
146 fp.write(''.join(self.before + self.hunk + self.after))
163 fp.write(''.join(self.before + self.hunk + self.after))
147
164
148 pretty = write
165 pretty = write
149
166
150 def filename(self):
167 def filename(self):
151 return self.header.filename()
168 return self.header.filename()
152
169
153 def __repr__(self):
170 def __repr__(self):
154 return '<hunk %r@%d>' % (self.filename(), self.fromline)
171 return '<hunk %r@%d>' % (self.filename(), self.fromline)
155
172
156 def parsepatch(fp):
173 def parsepatch(fp):
174 """patch -> [] of hunks """
157 class parser(object):
175 class parser(object):
176 """patch parsing state machine"""
158 def __init__(self):
177 def __init__(self):
159 self.fromline = 0
178 self.fromline = 0
160 self.toline = 0
179 self.toline = 0
161 self.proc = ''
180 self.proc = ''
162 self.header = None
181 self.header = None
163 self.context = []
182 self.context = []
164 self.before = []
183 self.before = []
165 self.hunk = []
184 self.hunk = []
166 self.stream = []
185 self.stream = []
167
186
168 def addrange(self, (fromstart, fromend, tostart, toend, proc)):
187 def addrange(self, (fromstart, fromend, tostart, toend, proc)):
169 self.fromline = int(fromstart)
188 self.fromline = int(fromstart)
170 self.toline = int(tostart)
189 self.toline = int(tostart)
171 self.proc = proc
190 self.proc = proc
172
191
173 def addcontext(self, context):
192 def addcontext(self, context):
174 if self.hunk:
193 if self.hunk:
175 h = hunk(self.header, self.fromline, self.toline, self.proc,
194 h = hunk(self.header, self.fromline, self.toline, self.proc,
176 self.before, self.hunk, context)
195 self.before, self.hunk, context)
177 self.header.hunks.append(h)
196 self.header.hunks.append(h)
178 self.stream.append(h)
197 self.stream.append(h)
179 self.fromline += len(self.before) + h.removed
198 self.fromline += len(self.before) + h.removed
180 self.toline += len(self.before) + h.added
199 self.toline += len(self.before) + h.added
181 self.before = []
200 self.before = []
182 self.hunk = []
201 self.hunk = []
183 self.proc = ''
202 self.proc = ''
184 self.context = context
203 self.context = context
185
204
186 def addhunk(self, hunk):
205 def addhunk(self, hunk):
187 if self.context:
206 if self.context:
188 self.before = self.context
207 self.before = self.context
189 self.context = []
208 self.context = []
190 self.hunk = data
209 self.hunk = data
191
210
192 def newfile(self, hdr):
211 def newfile(self, hdr):
193 self.addcontext([])
212 self.addcontext([])
194 h = header(hdr)
213 h = header(hdr)
195 self.stream.append(h)
214 self.stream.append(h)
196 self.header = h
215 self.header = h
197
216
198 def finished(self):
217 def finished(self):
199 self.addcontext([])
218 self.addcontext([])
200 return self.stream
219 return self.stream
201
220
202 transitions = {
221 transitions = {
203 'file': {'context': addcontext,
222 'file': {'context': addcontext,
204 'file': newfile,
223 'file': newfile,
205 'hunk': addhunk,
224 'hunk': addhunk,
206 'range': addrange},
225 'range': addrange},
207 'context': {'file': newfile,
226 'context': {'file': newfile,
208 'hunk': addhunk,
227 'hunk': addhunk,
209 'range': addrange},
228 'range': addrange},
210 'hunk': {'context': addcontext,
229 'hunk': {'context': addcontext,
211 'file': newfile,
230 'file': newfile,
212 'range': addrange},
231 'range': addrange},
213 'range': {'context': addcontext,
232 'range': {'context': addcontext,
214 'hunk': addhunk},
233 'hunk': addhunk},
215 }
234 }
216
235
217 p = parser()
236 p = parser()
218
237
219 state = 'context'
238 state = 'context'
220 for newstate, data in scanpatch(fp):
239 for newstate, data in scanpatch(fp):
221 try:
240 try:
222 p.transitions[state][newstate](p, data)
241 p.transitions[state][newstate](p, data)
223 except KeyError:
242 except KeyError:
224 raise patch.PatchError('unhandled transition: %s -> %s' %
243 raise patch.PatchError('unhandled transition: %s -> %s' %
225 (state, newstate))
244 (state, newstate))
226 state = newstate
245 state = newstate
227 return p.finished()
246 return p.finished()
228
247
229 def filterpatch(ui, chunks):
248 def filterpatch(ui, chunks):
249 """Interactively filter patch chunks into applied-only chunks"""
230 chunks = list(chunks)
250 chunks = list(chunks)
231 chunks.reverse()
251 chunks.reverse()
232 seen = {}
252 seen = {}
233 def consumefile():
253 def consumefile():
254 """fetch next portion from chunks until a 'header' is seen
255 NB: header == new-file mark
256 """
234 consumed = []
257 consumed = []
235 while chunks:
258 while chunks:
236 if isinstance(chunks[-1], header):
259 if isinstance(chunks[-1], header):
237 break
260 break
238 else:
261 else:
239 consumed.append(chunks.pop())
262 consumed.append(chunks.pop())
240 return consumed
263 return consumed
241 resp_all = [None]
264
242 resp_file = [None]
265 resp_all = [None] # this two are changed from inside prompt,
243 applied = {}
266 resp_file = [None] # so can't be usual variables
267 applied = {} # 'filename' -> [] of chunks
244 def prompt(query):
268 def prompt(query):
269 """prompt query, and process base inputs
270
271 - y/n for the rest of file
272 - y/n for the rest
273 - ? (help)
274 - q (quit)
275
276 else, input is returned to the caller.
277 """
245 if resp_all[0] is not None:
278 if resp_all[0] is not None:
246 return resp_all[0]
279 return resp_all[0]
247 if resp_file[0] is not None:
280 if resp_file[0] is not None:
248 return resp_file[0]
281 return resp_file[0]
249 while True:
282 while True:
250 r = (ui.prompt(query + _(' [Ynsfdaq?] '), '(?i)[Ynsfdaq?]?$')
283 r = (ui.prompt(query + _(' [Ynsfdaq?] '), '(?i)[Ynsfdaq?]?$')
251 or 'y').lower()
284 or 'y').lower()
252 if r == '?':
285 if r == '?':
253 c = record.__doc__.find('y - record this change')
286 c = record.__doc__.find('y - record this change')
254 for l in record.__doc__[c:].splitlines():
287 for l in record.__doc__[c:].splitlines():
255 if l: ui.write(_(l.strip()), '\n')
288 if l: ui.write(_(l.strip()), '\n')
256 continue
289 continue
257 elif r == 's':
290 elif r == 's':
258 r = resp_file[0] = 'n'
291 r = resp_file[0] = 'n'
259 elif r == 'f':
292 elif r == 'f':
260 r = resp_file[0] = 'y'
293 r = resp_file[0] = 'y'
261 elif r == 'd':
294 elif r == 'd':
262 r = resp_all[0] = 'n'
295 r = resp_all[0] = 'n'
263 elif r == 'a':
296 elif r == 'a':
264 r = resp_all[0] = 'y'
297 r = resp_all[0] = 'y'
265 elif r == 'q':
298 elif r == 'q':
266 raise util.Abort(_('user quit'))
299 raise util.Abort(_('user quit'))
267 return r
300 return r
268 while chunks:
301 while chunks:
269 chunk = chunks.pop()
302 chunk = chunks.pop()
270 if isinstance(chunk, header):
303 if isinstance(chunk, header):
304 # new-file mark
271 resp_file = [None]
305 resp_file = [None]
272 fixoffset = 0
306 fixoffset = 0
273 hdr = ''.join(chunk.header)
307 hdr = ''.join(chunk.header)
274 if hdr in seen:
308 if hdr in seen:
275 consumefile()
309 consumefile()
276 continue
310 continue
277 seen[hdr] = True
311 seen[hdr] = True
278 if resp_all[0] is None:
312 if resp_all[0] is None:
279 chunk.pretty(ui)
313 chunk.pretty(ui)
280 r = prompt(_('examine changes to %s?') %
314 r = prompt(_('examine changes to %s?') %
281 _(' and ').join(map(repr, chunk.files())))
315 _(' and ').join(map(repr, chunk.files())))
282 if r == 'y':
316 if r == 'y':
283 applied[chunk.filename()] = [chunk]
317 applied[chunk.filename()] = [chunk]
284 if chunk.allhunks():
318 if chunk.allhunks():
285 applied[chunk.filename()] += consumefile()
319 applied[chunk.filename()] += consumefile()
286 else:
320 else:
287 consumefile()
321 consumefile()
288 else:
322 else:
323 # new hunk
289 if resp_file[0] is None and resp_all[0] is None:
324 if resp_file[0] is None and resp_all[0] is None:
290 chunk.pretty(ui)
325 chunk.pretty(ui)
291 r = prompt(_('record this change to %r?') %
326 r = prompt(_('record this change to %r?') %
292 chunk.filename())
327 chunk.filename())
293 if r == 'y':
328 if r == 'y':
294 if fixoffset:
329 if fixoffset:
295 chunk = copy.copy(chunk)
330 chunk = copy.copy(chunk)
296 chunk.toline += fixoffset
331 chunk.toline += fixoffset
297 applied[chunk.filename()].append(chunk)
332 applied[chunk.filename()].append(chunk)
298 else:
333 else:
299 fixoffset += chunk.removed - chunk.added
334 fixoffset += chunk.removed - chunk.added
300 return reduce(operator.add, [h for h in applied.itervalues()
335 return reduce(operator.add, [h for h in applied.itervalues()
301 if h[0].special() or len(h) > 1], [])
336 if h[0].special() or len(h) > 1], [])
302
337
303 def record(ui, repo, *pats, **opts):
338 def record(ui, repo, *pats, **opts):
304 '''interactively select changes to commit
339 '''interactively select changes to commit
305
340
306 If a list of files is omitted, all changes reported by "hg status"
341 If a list of files is omitted, all changes reported by "hg status"
307 will be candidates for recording.
342 will be candidates for recording.
308
343
309 You will be prompted for whether to record changes to each
344 You will be prompted for whether to record changes to each
310 modified file, and for files with multiple changes, for each
345 modified file, and for files with multiple changes, for each
311 change to use. For each query, the following responses are
346 change to use. For each query, the following responses are
312 possible:
347 possible:
313
348
314 y - record this change
349 y - record this change
315 n - skip this change
350 n - skip this change
316
351
317 s - skip remaining changes to this file
352 s - skip remaining changes to this file
318 f - record remaining changes to this file
353 f - record remaining changes to this file
319
354
320 d - done, skip remaining changes and files
355 d - done, skip remaining changes and files
321 a - record all changes to all remaining files
356 a - record all changes to all remaining files
322 q - quit, recording no changes
357 q - quit, recording no changes
323
358
324 ? - display help'''
359 ? - display help'''
325
360
326 if not ui.interactive:
361 if not ui.interactive:
327 raise util.Abort(_('running non-interactively, use commit instead'))
362 raise util.Abort(_('running non-interactively, use commit instead'))
328
363
329 def recordfunc(ui, repo, files, message, match, opts):
364 def recordfunc(ui, repo, files, message, match, opts):
330 if files:
365 if files:
331 changes = None
366 changes = None
332 else:
367 else:
333 changes = repo.status(files=files, match=match)[:5]
368 changes = repo.status(files=files, match=match)[:5]
334 modified, added, removed = changes[:3]
369 modified, added, removed = changes[:3]
335 files = modified + added + removed
370 files = modified + added + removed
336 diffopts = mdiff.diffopts(git=True, nodates=True)
371 diffopts = mdiff.diffopts(git=True, nodates=True)
337 fp = cStringIO.StringIO()
372 fp = cStringIO.StringIO()
338 patch.diff(repo, repo.dirstate.parents()[0], files=files,
373 patch.diff(repo, repo.dirstate.parents()[0], files=files,
339 match=match, changes=changes, opts=diffopts, fp=fp)
374 match=match, changes=changes, opts=diffopts, fp=fp)
340 fp.seek(0)
375 fp.seek(0)
341
376
342 chunks = filterpatch(ui, parsepatch(fp))
377 chunks = filterpatch(ui, parsepatch(fp))
343 del fp
378 del fp
344
379
345 contenders = {}
380 contenders = {}
346 for h in chunks:
381 for h in chunks:
347 try: contenders.update(dict.fromkeys(h.files()))
382 try: contenders.update(dict.fromkeys(h.files()))
348 except AttributeError: pass
383 except AttributeError: pass
349
384
350 newfiles = [f for f in files if f in contenders]
385 newfiles = [f for f in files if f in contenders]
351
386
352 if not newfiles:
387 if not newfiles:
353 ui.status(_('no changes to record\n'))
388 ui.status(_('no changes to record\n'))
354 return 0
389 return 0
355
390
356 if changes is None:
391 if changes is None:
357 changes = repo.status(files=newfiles, match=match)[:5]
392 changes = repo.status(files=newfiles, match=match)[:5]
358 modified = dict.fromkeys(changes[0])
393 modified = dict.fromkeys(changes[0])
359
394
360 backups = {}
395 backups = {}
361 backupdir = repo.join('record-backups')
396 backupdir = repo.join('record-backups')
362 try:
397 try:
363 os.mkdir(backupdir)
398 os.mkdir(backupdir)
364 except OSError, err:
399 except OSError, err:
365 if err.errno != errno.EEXIST:
400 if err.errno != errno.EEXIST:
366 raise
401 raise
367 try:
402 try:
368 for f in newfiles:
403 for f in newfiles:
369 if f not in modified:
404 if f not in modified:
370 continue
405 continue
371 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
406 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
372 dir=backupdir)
407 dir=backupdir)
373 os.close(fd)
408 os.close(fd)
374 ui.debug('backup %r as %r\n' % (f, tmpname))
409 ui.debug('backup %r as %r\n' % (f, tmpname))
375 util.copyfile(repo.wjoin(f), tmpname)
410 util.copyfile(repo.wjoin(f), tmpname)
376 backups[f] = tmpname
411 backups[f] = tmpname
377
412
378 fp = cStringIO.StringIO()
413 fp = cStringIO.StringIO()
379 for c in chunks:
414 for c in chunks:
380 if c.filename() in backups:
415 if c.filename() in backups:
381 c.write(fp)
416 c.write(fp)
382 dopatch = fp.tell()
417 dopatch = fp.tell()
383 fp.seek(0)
418 fp.seek(0)
384
419
385 if backups:
420 if backups:
386 hg.revert(repo, repo.dirstate.parents()[0], backups.has_key)
421 hg.revert(repo, repo.dirstate.parents()[0], backups.has_key)
387
422
388 if dopatch:
423 if dopatch:
389 ui.debug('applying patch\n')
424 ui.debug('applying patch\n')
390 ui.debug(fp.getvalue())
425 ui.debug(fp.getvalue())
391 patch.internalpatch(fp, ui, 1, repo.root)
426 patch.internalpatch(fp, ui, 1, repo.root)
392 del fp
427 del fp
393
428
394 repo.commit(newfiles, message, opts['user'], opts['date'], match,
429 repo.commit(newfiles, message, opts['user'], opts['date'], match,
395 force_editor=opts.get('force_editor'))
430 force_editor=opts.get('force_editor'))
396 return 0
431 return 0
397 finally:
432 finally:
398 try:
433 try:
399 for realname, tmpname in backups.iteritems():
434 for realname, tmpname in backups.iteritems():
400 ui.debug('restoring %r to %r\n' % (tmpname, realname))
435 ui.debug('restoring %r to %r\n' % (tmpname, realname))
401 util.copyfile(tmpname, repo.wjoin(realname))
436 util.copyfile(tmpname, repo.wjoin(realname))
402 os.unlink(tmpname)
437 os.unlink(tmpname)
403 os.rmdir(backupdir)
438 os.rmdir(backupdir)
404 except OSError:
439 except OSError:
405 pass
440 pass
406 return cmdutil.commit(ui, repo, recordfunc, pats, opts)
441 return cmdutil.commit(ui, repo, recordfunc, pats, opts)
407
442
408 cmdtable = {
443 cmdtable = {
409 "record":
444 "record":
410 (record,
445 (record,
411 [('A', 'addremove', None,
446 [('A', 'addremove', None,
412 _('mark new/missing files as added/removed before committing')),
447 _('mark new/missing files as added/removed before committing')),
413 ] + commands.walkopts + commands.commitopts + commands.commitopts2,
448 ] + commands.walkopts + commands.commitopts + commands.commitopts2,
414 _('hg record [OPTION]... [FILE]...')),
449 _('hg record [OPTION]... [FILE]...')),
415 }
450 }
General Comments 0
You need to be logged in to leave comments. Login now