##// END OF EJS Templates
handle files with both git binary patches and copy/rename ops
Alexis S. L. Carvalho -
r3716:ab560042 default
parent child Browse files
Show More
@@ -1,672 +1,675
1 # patch.py - patch file parsing routines
1 # patch.py - patch file parsing routines
2 #
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006 Brendan Cully <brendan@kublai.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 demandload import demandload
8 from demandload import demandload
9 from i18n import gettext as _
9 from i18n import gettext as _
10 from node import *
10 from node import *
11 demandload(globals(), "base85 cmdutil mdiff util")
11 demandload(globals(), "base85 cmdutil mdiff util")
12 demandload(globals(), "cStringIO email.Parser errno os popen2 re shutil sha")
12 demandload(globals(), "cStringIO email.Parser errno os popen2 re shutil sha")
13 demandload(globals(), "sys tempfile zlib")
13 demandload(globals(), "sys tempfile zlib")
14
14
15 # helper functions
15 # helper functions
16
16
17 def copyfile(src, dst, basedir=None):
17 def copyfile(src, dst, basedir=None):
18 if not basedir:
18 if not basedir:
19 basedir = os.getcwd()
19 basedir = os.getcwd()
20
20
21 abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)]
21 abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)]
22 if os.path.exists(absdst):
22 if os.path.exists(absdst):
23 raise util.Abort(_("cannot create %s: destination already exists") %
23 raise util.Abort(_("cannot create %s: destination already exists") %
24 dst)
24 dst)
25
25
26 targetdir = os.path.dirname(absdst)
26 targetdir = os.path.dirname(absdst)
27 if not os.path.isdir(targetdir):
27 if not os.path.isdir(targetdir):
28 os.makedirs(targetdir)
28 os.makedirs(targetdir)
29
29
30 util.copyfile(abssrc, absdst)
30 util.copyfile(abssrc, absdst)
31
31
32 # public functions
32 # public functions
33
33
34 def extract(ui, fileobj):
34 def extract(ui, fileobj):
35 '''extract patch from data read from fileobj.
35 '''extract patch from data read from fileobj.
36
36
37 patch can be normal patch or contained in email message.
37 patch can be normal patch or contained in email message.
38
38
39 return tuple (filename, message, user, date). any item in returned
39 return tuple (filename, message, user, date). any item in returned
40 tuple can be None. if filename is None, fileobj did not contain
40 tuple can be None. if filename is None, fileobj did not contain
41 patch. caller must unlink filename when done.'''
41 patch. caller must unlink filename when done.'''
42
42
43 # attempt to detect the start of a patch
43 # attempt to detect the start of a patch
44 # (this heuristic is borrowed from quilt)
44 # (this heuristic is borrowed from quilt)
45 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
45 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
46 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
46 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
47 '(---|\*\*\*)[ \t])', re.MULTILINE)
47 '(---|\*\*\*)[ \t])', re.MULTILINE)
48
48
49 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
49 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
50 tmpfp = os.fdopen(fd, 'w')
50 tmpfp = os.fdopen(fd, 'w')
51 try:
51 try:
52 hgpatch = False
52 hgpatch = False
53
53
54 msg = email.Parser.Parser().parse(fileobj)
54 msg = email.Parser.Parser().parse(fileobj)
55
55
56 message = msg['Subject']
56 message = msg['Subject']
57 user = msg['From']
57 user = msg['From']
58 # should try to parse msg['Date']
58 # should try to parse msg['Date']
59 date = None
59 date = None
60
60
61 if message:
61 if message:
62 message = message.replace('\n\t', ' ')
62 message = message.replace('\n\t', ' ')
63 ui.debug('Subject: %s\n' % message)
63 ui.debug('Subject: %s\n' % message)
64 if user:
64 if user:
65 ui.debug('From: %s\n' % user)
65 ui.debug('From: %s\n' % user)
66 diffs_seen = 0
66 diffs_seen = 0
67 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
67 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
68
68
69 for part in msg.walk():
69 for part in msg.walk():
70 content_type = part.get_content_type()
70 content_type = part.get_content_type()
71 ui.debug('Content-Type: %s\n' % content_type)
71 ui.debug('Content-Type: %s\n' % content_type)
72 if content_type not in ok_types:
72 if content_type not in ok_types:
73 continue
73 continue
74 payload = part.get_payload(decode=True)
74 payload = part.get_payload(decode=True)
75 m = diffre.search(payload)
75 m = diffre.search(payload)
76 if m:
76 if m:
77 ui.debug(_('found patch at byte %d\n') % m.start(0))
77 ui.debug(_('found patch at byte %d\n') % m.start(0))
78 diffs_seen += 1
78 diffs_seen += 1
79 cfp = cStringIO.StringIO()
79 cfp = cStringIO.StringIO()
80 if message:
80 if message:
81 cfp.write(message)
81 cfp.write(message)
82 cfp.write('\n')
82 cfp.write('\n')
83 for line in payload[:m.start(0)].splitlines():
83 for line in payload[:m.start(0)].splitlines():
84 if line.startswith('# HG changeset patch'):
84 if line.startswith('# HG changeset patch'):
85 ui.debug(_('patch generated by hg export\n'))
85 ui.debug(_('patch generated by hg export\n'))
86 hgpatch = True
86 hgpatch = True
87 # drop earlier commit message content
87 # drop earlier commit message content
88 cfp.seek(0)
88 cfp.seek(0)
89 cfp.truncate()
89 cfp.truncate()
90 elif hgpatch:
90 elif hgpatch:
91 if line.startswith('# User '):
91 if line.startswith('# User '):
92 user = line[7:]
92 user = line[7:]
93 ui.debug('From: %s\n' % user)
93 ui.debug('From: %s\n' % user)
94 elif line.startswith("# Date "):
94 elif line.startswith("# Date "):
95 date = line[7:]
95 date = line[7:]
96 if not line.startswith('# '):
96 if not line.startswith('# '):
97 cfp.write(line)
97 cfp.write(line)
98 cfp.write('\n')
98 cfp.write('\n')
99 message = cfp.getvalue()
99 message = cfp.getvalue()
100 if tmpfp:
100 if tmpfp:
101 tmpfp.write(payload)
101 tmpfp.write(payload)
102 if not payload.endswith('\n'):
102 if not payload.endswith('\n'):
103 tmpfp.write('\n')
103 tmpfp.write('\n')
104 elif not diffs_seen and message and content_type == 'text/plain':
104 elif not diffs_seen and message and content_type == 'text/plain':
105 message += '\n' + payload
105 message += '\n' + payload
106 except:
106 except:
107 tmpfp.close()
107 tmpfp.close()
108 os.unlink(tmpname)
108 os.unlink(tmpname)
109 raise
109 raise
110
110
111 tmpfp.close()
111 tmpfp.close()
112 if not diffs_seen:
112 if not diffs_seen:
113 os.unlink(tmpname)
113 os.unlink(tmpname)
114 return None, message, user, date
114 return None, message, user, date
115 return tmpname, message, user, date
115 return tmpname, message, user, date
116
116
117 GP_PATCH = 1 << 0 # we have to run patch
118 GP_FILTER = 1 << 1 # there's some copy/rename operation
119 GP_BINARY = 1 << 2 # there's a binary patch
120
117 def readgitpatch(patchname):
121 def readgitpatch(patchname):
118 """extract git-style metadata about patches from <patchname>"""
122 """extract git-style metadata about patches from <patchname>"""
119 class gitpatch:
123 class gitpatch:
120 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
124 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
121 def __init__(self, path):
125 def __init__(self, path):
122 self.path = path
126 self.path = path
123 self.oldpath = None
127 self.oldpath = None
124 self.mode = None
128 self.mode = None
125 self.op = 'MODIFY'
129 self.op = 'MODIFY'
126 self.copymod = False
130 self.copymod = False
127 self.lineno = 0
131 self.lineno = 0
128 self.binary = False
132 self.binary = False
129
133
130 # Filter patch for git information
134 # Filter patch for git information
131 gitre = re.compile('diff --git a/(.*) b/(.*)')
135 gitre = re.compile('diff --git a/(.*) b/(.*)')
132 pf = file(patchname)
136 pf = file(patchname)
133 gp = None
137 gp = None
134 gitpatches = []
138 gitpatches = []
135 # Can have a git patch with only metadata, causing patch to complain
139 # Can have a git patch with only metadata, causing patch to complain
136 dopatch = False
140 dopatch = 0
137
141
138 lineno = 0
142 lineno = 0
139 for line in pf:
143 for line in pf:
140 lineno += 1
144 lineno += 1
141 if line.startswith('diff --git'):
145 if line.startswith('diff --git'):
142 m = gitre.match(line)
146 m = gitre.match(line)
143 if m:
147 if m:
144 if gp:
148 if gp:
145 gitpatches.append(gp)
149 gitpatches.append(gp)
146 src, dst = m.group(1, 2)
150 src, dst = m.group(1, 2)
147 gp = gitpatch(dst)
151 gp = gitpatch(dst)
148 gp.lineno = lineno
152 gp.lineno = lineno
149 elif gp:
153 elif gp:
150 if line.startswith('--- '):
154 if line.startswith('--- '):
151 if gp.op in ('COPY', 'RENAME'):
155 if gp.op in ('COPY', 'RENAME'):
152 gp.copymod = True
156 gp.copymod = True
153 dopatch = 'filter'
157 dopatch |= GP_FILTER
154 gitpatches.append(gp)
158 gitpatches.append(gp)
155 gp = None
159 gp = None
156 if not dopatch:
160 dopatch |= GP_PATCH
157 dopatch = True
158 continue
161 continue
159 if line.startswith('rename from '):
162 if line.startswith('rename from '):
160 gp.op = 'RENAME'
163 gp.op = 'RENAME'
161 gp.oldpath = line[12:].rstrip()
164 gp.oldpath = line[12:].rstrip()
162 elif line.startswith('rename to '):
165 elif line.startswith('rename to '):
163 gp.path = line[10:].rstrip()
166 gp.path = line[10:].rstrip()
164 elif line.startswith('copy from '):
167 elif line.startswith('copy from '):
165 gp.op = 'COPY'
168 gp.op = 'COPY'
166 gp.oldpath = line[10:].rstrip()
169 gp.oldpath = line[10:].rstrip()
167 elif line.startswith('copy to '):
170 elif line.startswith('copy to '):
168 gp.path = line[8:].rstrip()
171 gp.path = line[8:].rstrip()
169 elif line.startswith('deleted file'):
172 elif line.startswith('deleted file'):
170 gp.op = 'DELETE'
173 gp.op = 'DELETE'
171 elif line.startswith('new file mode '):
174 elif line.startswith('new file mode '):
172 gp.op = 'ADD'
175 gp.op = 'ADD'
173 gp.mode = int(line.rstrip()[-3:], 8)
176 gp.mode = int(line.rstrip()[-3:], 8)
174 elif line.startswith('new mode '):
177 elif line.startswith('new mode '):
175 gp.mode = int(line.rstrip()[-3:], 8)
178 gp.mode = int(line.rstrip()[-3:], 8)
176 elif line.startswith('GIT binary patch'):
179 elif line.startswith('GIT binary patch'):
177 if not dopatch:
180 dopatch |= GP_BINARY
178 dopatch = 'binary'
179 gp.binary = True
181 gp.binary = True
180 if gp:
182 if gp:
181 gitpatches.append(gp)
183 gitpatches.append(gp)
182
184
183 if not gitpatches:
185 if not gitpatches:
184 dopatch = True
186 dopatch = GP_PATCH
185
187
186 return (dopatch, gitpatches)
188 return (dopatch, gitpatches)
187
189
188 def dogitpatch(patchname, gitpatches, cwd=None):
190 def dogitpatch(patchname, gitpatches, cwd=None):
189 """Preprocess git patch so that vanilla patch can handle it"""
191 """Preprocess git patch so that vanilla patch can handle it"""
190 def extractbin(fp):
192 def extractbin(fp):
191 line = fp.readline().rstrip()
193 line = fp.readline().rstrip()
192 while line and not line.startswith('literal '):
194 while line and not line.startswith('literal '):
193 line = fp.readline().rstrip()
195 line = fp.readline().rstrip()
194 if not line:
196 if not line:
195 return
197 return
196 size = int(line[8:])
198 size = int(line[8:])
197 dec = []
199 dec = []
198 line = fp.readline().rstrip()
200 line = fp.readline().rstrip()
199 while line:
201 while line:
200 l = line[0]
202 l = line[0]
201 if l <= 'Z' and l >= 'A':
203 if l <= 'Z' and l >= 'A':
202 l = ord(l) - ord('A') + 1
204 l = ord(l) - ord('A') + 1
203 else:
205 else:
204 l = ord(l) - ord('a') + 27
206 l = ord(l) - ord('a') + 27
205 dec.append(base85.b85decode(line[1:])[:l])
207 dec.append(base85.b85decode(line[1:])[:l])
206 line = fp.readline().rstrip()
208 line = fp.readline().rstrip()
207 text = zlib.decompress(''.join(dec))
209 text = zlib.decompress(''.join(dec))
208 if len(text) != size:
210 if len(text) != size:
209 raise util.Abort(_('binary patch is %d bytes, not %d') %
211 raise util.Abort(_('binary patch is %d bytes, not %d') %
210 (len(text), size))
212 (len(text), size))
211 return text
213 return text
212
214
213 pf = file(patchname)
215 pf = file(patchname)
214 pfline = 1
216 pfline = 1
215
217
216 fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
218 fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
217 tmpfp = os.fdopen(fd, 'w')
219 tmpfp = os.fdopen(fd, 'w')
218
220
219 try:
221 try:
220 for i in xrange(len(gitpatches)):
222 for i in xrange(len(gitpatches)):
221 p = gitpatches[i]
223 p = gitpatches[i]
222 if not p.copymod and not p.binary:
224 if not p.copymod and not p.binary:
223 continue
225 continue
224
226
225 # rewrite patch hunk
227 # rewrite patch hunk
226 while pfline < p.lineno:
228 while pfline < p.lineno:
227 tmpfp.write(pf.readline())
229 tmpfp.write(pf.readline())
228 pfline += 1
230 pfline += 1
229
231
230 if p.binary:
232 if p.binary:
231 text = extractbin(pf)
233 text = extractbin(pf)
232 if not text:
234 if not text:
233 raise util.Abort(_('binary patch extraction failed'))
235 raise util.Abort(_('binary patch extraction failed'))
234 if not cwd:
236 if not cwd:
235 cwd = os.getcwd()
237 cwd = os.getcwd()
236 absdst = os.path.join(cwd, p.path)
238 absdst = os.path.join(cwd, p.path)
237 basedir = os.path.dirname(absdst)
239 basedir = os.path.dirname(absdst)
238 if not os.path.isdir(basedir):
240 if not os.path.isdir(basedir):
239 os.makedirs(basedir)
241 os.makedirs(basedir)
240 out = file(absdst, 'wb')
242 out = file(absdst, 'wb')
241 out.write(text)
243 out.write(text)
242 out.close()
244 out.close()
243 elif p.copymod:
245 elif p.copymod:
244 copyfile(p.oldpath, p.path, basedir=cwd)
246 copyfile(p.oldpath, p.path, basedir=cwd)
245 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
247 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
246 line = pf.readline()
248 line = pf.readline()
247 pfline += 1
249 pfline += 1
248 while not line.startswith('--- a/'):
250 while not line.startswith('--- a/'):
249 tmpfp.write(line)
251 tmpfp.write(line)
250 line = pf.readline()
252 line = pf.readline()
251 pfline += 1
253 pfline += 1
252 tmpfp.write('--- a/%s\n' % p.path)
254 tmpfp.write('--- a/%s\n' % p.path)
253
255
254 line = pf.readline()
256 line = pf.readline()
255 while line:
257 while line:
256 tmpfp.write(line)
258 tmpfp.write(line)
257 line = pf.readline()
259 line = pf.readline()
258 except:
260 except:
259 tmpfp.close()
261 tmpfp.close()
260 os.unlink(patchname)
262 os.unlink(patchname)
261 raise
263 raise
262
264
263 tmpfp.close()
265 tmpfp.close()
264 return patchname
266 return patchname
265
267
266 def patch(patchname, ui, strip=1, cwd=None, files={}):
268 def patch(patchname, ui, strip=1, cwd=None, files={}):
267 """apply the patch <patchname> to the working directory.
269 """apply the patch <patchname> to the working directory.
268 a list of patched files is returned"""
270 a list of patched files is returned"""
269
271
270 # helper function
272 # helper function
271 def __patch(patchname):
273 def __patch(patchname):
272 """patch and updates the files and fuzz variables"""
274 """patch and updates the files and fuzz variables"""
273 fuzz = False
275 fuzz = False
274
276
275 patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''),
277 patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''),
276 'patch')
278 'patch')
277 args = []
279 args = []
278 if cwd:
280 if cwd:
279 args.append('-d %s' % util.shellquote(cwd))
281 args.append('-d %s' % util.shellquote(cwd))
280 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
282 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
281 util.shellquote(patchname)))
283 util.shellquote(patchname)))
282
284
283 for line in fp:
285 for line in fp:
284 line = line.rstrip()
286 line = line.rstrip()
285 ui.note(line + '\n')
287 ui.note(line + '\n')
286 if line.startswith('patching file '):
288 if line.startswith('patching file '):
287 pf = util.parse_patch_output(line)
289 pf = util.parse_patch_output(line)
288 printed_file = False
290 printed_file = False
289 files.setdefault(pf, (None, None))
291 files.setdefault(pf, (None, None))
290 elif line.find('with fuzz') >= 0:
292 elif line.find('with fuzz') >= 0:
291 fuzz = True
293 fuzz = True
292 if not printed_file:
294 if not printed_file:
293 ui.warn(pf + '\n')
295 ui.warn(pf + '\n')
294 printed_file = True
296 printed_file = True
295 ui.warn(line + '\n')
297 ui.warn(line + '\n')
296 elif line.find('saving rejects to file') >= 0:
298 elif line.find('saving rejects to file') >= 0:
297 ui.warn(line + '\n')
299 ui.warn(line + '\n')
298 elif line.find('FAILED') >= 0:
300 elif line.find('FAILED') >= 0:
299 if not printed_file:
301 if not printed_file:
300 ui.warn(pf + '\n')
302 ui.warn(pf + '\n')
301 printed_file = True
303 printed_file = True
302 ui.warn(line + '\n')
304 ui.warn(line + '\n')
303 code = fp.close()
305 code = fp.close()
304 if code:
306 if code:
305 raise util.Abort(_("patch command failed: %s") %
307 raise util.Abort(_("patch command failed: %s") %
306 util.explain_exit(code)[0])
308 util.explain_exit(code)[0])
307 return fuzz
309 return fuzz
308
310
309 (dopatch, gitpatches) = readgitpatch(patchname)
311 (dopatch, gitpatches) = readgitpatch(patchname)
310 for gp in gitpatches:
312 for gp in gitpatches:
311 files[gp.path] = (gp.op, gp)
313 files[gp.path] = (gp.op, gp)
312
314
313 fuzz = False
315 fuzz = False
314 if dopatch:
316 if dopatch:
315 if dopatch in ('filter', 'binary'):
317 filterpatch = dopatch & (GP_FILTER | GP_BINARY)
318 if filterpatch:
316 patchname = dogitpatch(patchname, gitpatches, cwd=cwd)
319 patchname = dogitpatch(patchname, gitpatches, cwd=cwd)
317 try:
320 try:
318 if dopatch != 'binary':
321 if dopatch & GP_PATCH:
319 fuzz = __patch(patchname)
322 fuzz = __patch(patchname)
320 finally:
323 finally:
321 if dopatch == 'filter':
324 if filterpatch:
322 os.unlink(patchname)
325 os.unlink(patchname)
323
326
324 return fuzz
327 return fuzz
325
328
326 def diffopts(ui, opts={}, untrusted=False):
329 def diffopts(ui, opts={}, untrusted=False):
327 def get(key, name=None):
330 def get(key, name=None):
328 return (opts.get(key) or
331 return (opts.get(key) or
329 ui.configbool('diff', name or key, None, untrusted=untrusted))
332 ui.configbool('diff', name or key, None, untrusted=untrusted))
330 return mdiff.diffopts(
333 return mdiff.diffopts(
331 text=opts.get('text'),
334 text=opts.get('text'),
332 git=get('git'),
335 git=get('git'),
333 nodates=get('nodates'),
336 nodates=get('nodates'),
334 showfunc=get('show_function', 'showfunc'),
337 showfunc=get('show_function', 'showfunc'),
335 ignorews=get('ignore_all_space', 'ignorews'),
338 ignorews=get('ignore_all_space', 'ignorews'),
336 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
339 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
337 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'))
340 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'))
338
341
339 def updatedir(ui, repo, patches, wlock=None):
342 def updatedir(ui, repo, patches, wlock=None):
340 '''Update dirstate after patch application according to metadata'''
343 '''Update dirstate after patch application according to metadata'''
341 if not patches:
344 if not patches:
342 return
345 return
343 copies = []
346 copies = []
344 removes = {}
347 removes = {}
345 cfiles = patches.keys()
348 cfiles = patches.keys()
346 cwd = repo.getcwd()
349 cwd = repo.getcwd()
347 if cwd:
350 if cwd:
348 cfiles = [util.pathto(cwd, f) for f in patches.keys()]
351 cfiles = [util.pathto(cwd, f) for f in patches.keys()]
349 for f in patches:
352 for f in patches:
350 ctype, gp = patches[f]
353 ctype, gp = patches[f]
351 if ctype == 'RENAME':
354 if ctype == 'RENAME':
352 copies.append((gp.oldpath, gp.path, gp.copymod))
355 copies.append((gp.oldpath, gp.path, gp.copymod))
353 removes[gp.oldpath] = 1
356 removes[gp.oldpath] = 1
354 elif ctype == 'COPY':
357 elif ctype == 'COPY':
355 copies.append((gp.oldpath, gp.path, gp.copymod))
358 copies.append((gp.oldpath, gp.path, gp.copymod))
356 elif ctype == 'DELETE':
359 elif ctype == 'DELETE':
357 removes[gp.path] = 1
360 removes[gp.path] = 1
358 for src, dst, after in copies:
361 for src, dst, after in copies:
359 if not after:
362 if not after:
360 copyfile(src, dst, repo.root)
363 copyfile(src, dst, repo.root)
361 repo.copy(src, dst, wlock=wlock)
364 repo.copy(src, dst, wlock=wlock)
362 removes = removes.keys()
365 removes = removes.keys()
363 if removes:
366 if removes:
364 removes.sort()
367 removes.sort()
365 repo.remove(removes, True, wlock=wlock)
368 repo.remove(removes, True, wlock=wlock)
366 for f in patches:
369 for f in patches:
367 ctype, gp = patches[f]
370 ctype, gp = patches[f]
368 if gp and gp.mode:
371 if gp and gp.mode:
369 x = gp.mode & 0100 != 0
372 x = gp.mode & 0100 != 0
370 dst = os.path.join(repo.root, gp.path)
373 dst = os.path.join(repo.root, gp.path)
371 # patch won't create empty files
374 # patch won't create empty files
372 if ctype == 'ADD' and not os.path.exists(dst):
375 if ctype == 'ADD' and not os.path.exists(dst):
373 repo.wwrite(gp.path, '')
376 repo.wwrite(gp.path, '')
374 util.set_exec(dst, x)
377 util.set_exec(dst, x)
375 cmdutil.addremove(repo, cfiles, wlock=wlock)
378 cmdutil.addremove(repo, cfiles, wlock=wlock)
376 files = patches.keys()
379 files = patches.keys()
377 files.extend([r for r in removes if r not in files])
380 files.extend([r for r in removes if r not in files])
378 files.sort()
381 files.sort()
379
382
380 return files
383 return files
381
384
382 def b85diff(fp, to, tn):
385 def b85diff(fp, to, tn):
383 '''print base85-encoded binary diff'''
386 '''print base85-encoded binary diff'''
384 def gitindex(text):
387 def gitindex(text):
385 if not text:
388 if not text:
386 return '0' * 40
389 return '0' * 40
387 l = len(text)
390 l = len(text)
388 s = sha.new('blob %d\0' % l)
391 s = sha.new('blob %d\0' % l)
389 s.update(text)
392 s.update(text)
390 return s.hexdigest()
393 return s.hexdigest()
391
394
392 def fmtline(line):
395 def fmtline(line):
393 l = len(line)
396 l = len(line)
394 if l <= 26:
397 if l <= 26:
395 l = chr(ord('A') + l - 1)
398 l = chr(ord('A') + l - 1)
396 else:
399 else:
397 l = chr(l - 26 + ord('a') - 1)
400 l = chr(l - 26 + ord('a') - 1)
398 return '%c%s\n' % (l, base85.b85encode(line, True))
401 return '%c%s\n' % (l, base85.b85encode(line, True))
399
402
400 def chunk(text, csize=52):
403 def chunk(text, csize=52):
401 l = len(text)
404 l = len(text)
402 i = 0
405 i = 0
403 while i < l:
406 while i < l:
404 yield text[i:i+csize]
407 yield text[i:i+csize]
405 i += csize
408 i += csize
406
409
407 # TODO: deltas
410 # TODO: deltas
408 l = len(tn)
411 l = len(tn)
409 fp.write('index %s..%s\nGIT binary patch\nliteral %s\n' %
412 fp.write('index %s..%s\nGIT binary patch\nliteral %s\n' %
410 (gitindex(to), gitindex(tn), len(tn)))
413 (gitindex(to), gitindex(tn), len(tn)))
411
414
412 tn = ''.join([fmtline(l) for l in chunk(zlib.compress(tn))])
415 tn = ''.join([fmtline(l) for l in chunk(zlib.compress(tn))])
413 fp.write(tn)
416 fp.write(tn)
414 fp.write('\n')
417 fp.write('\n')
415
418
416 def diff(repo, node1=None, node2=None, files=None, match=util.always,
419 def diff(repo, node1=None, node2=None, files=None, match=util.always,
417 fp=None, changes=None, opts=None):
420 fp=None, changes=None, opts=None):
418 '''print diff of changes to files between two nodes, or node and
421 '''print diff of changes to files between two nodes, or node and
419 working directory.
422 working directory.
420
423
421 if node1 is None, use first dirstate parent instead.
424 if node1 is None, use first dirstate parent instead.
422 if node2 is None, compare node1 with working directory.'''
425 if node2 is None, compare node1 with working directory.'''
423
426
424 if opts is None:
427 if opts is None:
425 opts = mdiff.defaultopts
428 opts = mdiff.defaultopts
426 if fp is None:
429 if fp is None:
427 fp = repo.ui
430 fp = repo.ui
428
431
429 if not node1:
432 if not node1:
430 node1 = repo.dirstate.parents()[0]
433 node1 = repo.dirstate.parents()[0]
431
434
432 clcache = {}
435 clcache = {}
433 def getchangelog(n):
436 def getchangelog(n):
434 if n not in clcache:
437 if n not in clcache:
435 clcache[n] = repo.changelog.read(n)
438 clcache[n] = repo.changelog.read(n)
436 return clcache[n]
439 return clcache[n]
437 mcache = {}
440 mcache = {}
438 def getmanifest(n):
441 def getmanifest(n):
439 if n not in mcache:
442 if n not in mcache:
440 mcache[n] = repo.manifest.read(n)
443 mcache[n] = repo.manifest.read(n)
441 return mcache[n]
444 return mcache[n]
442 fcache = {}
445 fcache = {}
443 def getfile(f):
446 def getfile(f):
444 if f not in fcache:
447 if f not in fcache:
445 fcache[f] = repo.file(f)
448 fcache[f] = repo.file(f)
446 return fcache[f]
449 return fcache[f]
447
450
448 # reading the data for node1 early allows it to play nicely
451 # reading the data for node1 early allows it to play nicely
449 # with repo.status and the revlog cache.
452 # with repo.status and the revlog cache.
450 change = getchangelog(node1)
453 change = getchangelog(node1)
451 mmap = getmanifest(change[0])
454 mmap = getmanifest(change[0])
452 date1 = util.datestr(change[2])
455 date1 = util.datestr(change[2])
453
456
454 if not changes:
457 if not changes:
455 changes = repo.status(node1, node2, files, match=match)[:5]
458 changes = repo.status(node1, node2, files, match=match)[:5]
456 modified, added, removed, deleted, unknown = changes
459 modified, added, removed, deleted, unknown = changes
457 if files:
460 if files:
458 def filterfiles(filters):
461 def filterfiles(filters):
459 l = [x for x in filters if x in files]
462 l = [x for x in filters if x in files]
460
463
461 for t in files:
464 for t in files:
462 if not t.endswith("/"):
465 if not t.endswith("/"):
463 t += "/"
466 t += "/"
464 l += [x for x in filters if x.startswith(t)]
467 l += [x for x in filters if x.startswith(t)]
465 return l
468 return l
466
469
467 modified, added, removed = map(filterfiles, (modified, added, removed))
470 modified, added, removed = map(filterfiles, (modified, added, removed))
468
471
469 if not modified and not added and not removed:
472 if not modified and not added and not removed:
470 return
473 return
471
474
472 # returns False if there was no rename between n1 and n2
475 # returns False if there was no rename between n1 and n2
473 # returns None if the file was created between n1 and n2
476 # returns None if the file was created between n1 and n2
474 # returns the (file, node) present in n1 that was renamed to f in n2
477 # returns the (file, node) present in n1 that was renamed to f in n2
475 def renamedbetween(f, n1, n2):
478 def renamedbetween(f, n1, n2):
476 r1, r2 = map(repo.changelog.rev, (n1, n2))
479 r1, r2 = map(repo.changelog.rev, (n1, n2))
477 orig = f
480 orig = f
478 src = None
481 src = None
479 while r2 > r1:
482 while r2 > r1:
480 cl = getchangelog(n2)
483 cl = getchangelog(n2)
481 if f in cl[3]:
484 if f in cl[3]:
482 m = getmanifest(cl[0])
485 m = getmanifest(cl[0])
483 try:
486 try:
484 src = getfile(f).renamed(m[f])
487 src = getfile(f).renamed(m[f])
485 except KeyError:
488 except KeyError:
486 return None
489 return None
487 if src:
490 if src:
488 f = src[0]
491 f = src[0]
489 n2 = repo.changelog.parents(n2)[0]
492 n2 = repo.changelog.parents(n2)[0]
490 r2 = repo.changelog.rev(n2)
493 r2 = repo.changelog.rev(n2)
491 cl = getchangelog(n1)
494 cl = getchangelog(n1)
492 m = getmanifest(cl[0])
495 m = getmanifest(cl[0])
493 if f not in m:
496 if f not in m:
494 return None
497 return None
495 if f == orig:
498 if f == orig:
496 return False
499 return False
497 return f, m[f]
500 return f, m[f]
498
501
499 if node2:
502 if node2:
500 change = getchangelog(node2)
503 change = getchangelog(node2)
501 mmap2 = getmanifest(change[0])
504 mmap2 = getmanifest(change[0])
502 _date2 = util.datestr(change[2])
505 _date2 = util.datestr(change[2])
503 def date2(f):
506 def date2(f):
504 return _date2
507 return _date2
505 def read(f):
508 def read(f):
506 return getfile(f).read(mmap2[f])
509 return getfile(f).read(mmap2[f])
507 def renamed(f):
510 def renamed(f):
508 return renamedbetween(f, node1, node2)
511 return renamedbetween(f, node1, node2)
509 else:
512 else:
510 tz = util.makedate()[1]
513 tz = util.makedate()[1]
511 _date2 = util.datestr()
514 _date2 = util.datestr()
512 def date2(f):
515 def date2(f):
513 try:
516 try:
514 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
517 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
515 except OSError, err:
518 except OSError, err:
516 if err.errno != errno.ENOENT: raise
519 if err.errno != errno.ENOENT: raise
517 return _date2
520 return _date2
518 def read(f):
521 def read(f):
519 return repo.wread(f)
522 return repo.wread(f)
520 def renamed(f):
523 def renamed(f):
521 src = repo.dirstate.copied(f)
524 src = repo.dirstate.copied(f)
522 parent = repo.dirstate.parents()[0]
525 parent = repo.dirstate.parents()[0]
523 if src:
526 if src:
524 f = src
527 f = src
525 of = renamedbetween(f, node1, parent)
528 of = renamedbetween(f, node1, parent)
526 if of or of is None:
529 if of or of is None:
527 return of
530 return of
528 elif src:
531 elif src:
529 cl = getchangelog(parent)[0]
532 cl = getchangelog(parent)[0]
530 return (src, getmanifest(cl)[src])
533 return (src, getmanifest(cl)[src])
531 else:
534 else:
532 return None
535 return None
533
536
534 if repo.ui.quiet:
537 if repo.ui.quiet:
535 r = None
538 r = None
536 else:
539 else:
537 hexfunc = repo.ui.debugflag and hex or short
540 hexfunc = repo.ui.debugflag and hex or short
538 r = [hexfunc(node) for node in [node1, node2] if node]
541 r = [hexfunc(node) for node in [node1, node2] if node]
539
542
540 if opts.git:
543 if opts.git:
541 copied = {}
544 copied = {}
542 for f in added:
545 for f in added:
543 src = renamed(f)
546 src = renamed(f)
544 if src:
547 if src:
545 copied[f] = src
548 copied[f] = src
546 srcs = [x[1][0] for x in copied.items()]
549 srcs = [x[1][0] for x in copied.items()]
547
550
548 all = modified + added + removed
551 all = modified + added + removed
549 all.sort()
552 all.sort()
550 gone = {}
553 gone = {}
551 for f in all:
554 for f in all:
552 to = None
555 to = None
553 tn = None
556 tn = None
554 dodiff = True
557 dodiff = True
555 header = []
558 header = []
556 if f in mmap:
559 if f in mmap:
557 to = getfile(f).read(mmap[f])
560 to = getfile(f).read(mmap[f])
558 if f not in removed:
561 if f not in removed:
559 tn = read(f)
562 tn = read(f)
560 if opts.git:
563 if opts.git:
561 def gitmode(x):
564 def gitmode(x):
562 return x and '100755' or '100644'
565 return x and '100755' or '100644'
563 def addmodehdr(header, omode, nmode):
566 def addmodehdr(header, omode, nmode):
564 if omode != nmode:
567 if omode != nmode:
565 header.append('old mode %s\n' % omode)
568 header.append('old mode %s\n' % omode)
566 header.append('new mode %s\n' % nmode)
569 header.append('new mode %s\n' % nmode)
567
570
568 a, b = f, f
571 a, b = f, f
569 if f in added:
572 if f in added:
570 if node2:
573 if node2:
571 mode = gitmode(mmap2.execf(f))
574 mode = gitmode(mmap2.execf(f))
572 else:
575 else:
573 mode = gitmode(util.is_exec(repo.wjoin(f), None))
576 mode = gitmode(util.is_exec(repo.wjoin(f), None))
574 if f in copied:
577 if f in copied:
575 a, arev = copied[f]
578 a, arev = copied[f]
576 omode = gitmode(mmap.execf(a))
579 omode = gitmode(mmap.execf(a))
577 addmodehdr(header, omode, mode)
580 addmodehdr(header, omode, mode)
578 if a in removed and a not in gone:
581 if a in removed and a not in gone:
579 op = 'rename'
582 op = 'rename'
580 gone[a] = 1
583 gone[a] = 1
581 else:
584 else:
582 op = 'copy'
585 op = 'copy'
583 header.append('%s from %s\n' % (op, a))
586 header.append('%s from %s\n' % (op, a))
584 header.append('%s to %s\n' % (op, f))
587 header.append('%s to %s\n' % (op, f))
585 to = getfile(a).read(arev)
588 to = getfile(a).read(arev)
586 else:
589 else:
587 header.append('new file mode %s\n' % mode)
590 header.append('new file mode %s\n' % mode)
588 if util.binary(tn):
591 if util.binary(tn):
589 dodiff = 'binary'
592 dodiff = 'binary'
590 elif f in removed:
593 elif f in removed:
591 if f in srcs:
594 if f in srcs:
592 dodiff = False
595 dodiff = False
593 else:
596 else:
594 mode = gitmode(mmap.execf(f))
597 mode = gitmode(mmap.execf(f))
595 header.append('deleted file mode %s\n' % mode)
598 header.append('deleted file mode %s\n' % mode)
596 else:
599 else:
597 omode = gitmode(mmap.execf(f))
600 omode = gitmode(mmap.execf(f))
598 if node2:
601 if node2:
599 nmode = gitmode(mmap2.execf(f))
602 nmode = gitmode(mmap2.execf(f))
600 else:
603 else:
601 nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
604 nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
602 addmodehdr(header, omode, nmode)
605 addmodehdr(header, omode, nmode)
603 if util.binary(to) or util.binary(tn):
606 if util.binary(to) or util.binary(tn):
604 dodiff = 'binary'
607 dodiff = 'binary'
605 r = None
608 r = None
606 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
609 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
607 if dodiff == 'binary':
610 if dodiff == 'binary':
608 fp.write(''.join(header))
611 fp.write(''.join(header))
609 b85diff(fp, to, tn)
612 b85diff(fp, to, tn)
610 elif dodiff:
613 elif dodiff:
611 text = mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts)
614 text = mdiff.unidiff(to, date1, tn, date2(f), f, r, opts=opts)
612 if text or len(header) > 1:
615 if text or len(header) > 1:
613 fp.write(''.join(header))
616 fp.write(''.join(header))
614 fp.write(text)
617 fp.write(text)
615
618
616 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
619 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
617 opts=None):
620 opts=None):
618 '''export changesets as hg patches.'''
621 '''export changesets as hg patches.'''
619
622
620 total = len(revs)
623 total = len(revs)
621 revwidth = max(map(len, revs))
624 revwidth = max(map(len, revs))
622
625
623 def single(node, seqno, fp):
626 def single(node, seqno, fp):
624 parents = [p for p in repo.changelog.parents(node) if p != nullid]
627 parents = [p for p in repo.changelog.parents(node) if p != nullid]
625 if switch_parent:
628 if switch_parent:
626 parents.reverse()
629 parents.reverse()
627 prev = (parents and parents[0]) or nullid
630 prev = (parents and parents[0]) or nullid
628 change = repo.changelog.read(node)
631 change = repo.changelog.read(node)
629
632
630 if not fp:
633 if not fp:
631 fp = cmdutil.make_file(repo, template, node, total=total,
634 fp = cmdutil.make_file(repo, template, node, total=total,
632 seqno=seqno, revwidth=revwidth)
635 seqno=seqno, revwidth=revwidth)
633 if fp not in (sys.stdout, repo.ui):
636 if fp not in (sys.stdout, repo.ui):
634 repo.ui.note("%s\n" % fp.name)
637 repo.ui.note("%s\n" % fp.name)
635
638
636 fp.write("# HG changeset patch\n")
639 fp.write("# HG changeset patch\n")
637 fp.write("# User %s\n" % change[1])
640 fp.write("# User %s\n" % change[1])
638 fp.write("# Date %d %d\n" % change[2])
641 fp.write("# Date %d %d\n" % change[2])
639 fp.write("# Node ID %s\n" % hex(node))
642 fp.write("# Node ID %s\n" % hex(node))
640 fp.write("# Parent %s\n" % hex(prev))
643 fp.write("# Parent %s\n" % hex(prev))
641 if len(parents) > 1:
644 if len(parents) > 1:
642 fp.write("# Parent %s\n" % hex(parents[1]))
645 fp.write("# Parent %s\n" % hex(parents[1]))
643 fp.write(change[4].rstrip())
646 fp.write(change[4].rstrip())
644 fp.write("\n\n")
647 fp.write("\n\n")
645
648
646 diff(repo, prev, node, fp=fp, opts=opts)
649 diff(repo, prev, node, fp=fp, opts=opts)
647 if fp not in (sys.stdout, repo.ui):
650 if fp not in (sys.stdout, repo.ui):
648 fp.close()
651 fp.close()
649
652
650 for seqno, cset in enumerate(revs):
653 for seqno, cset in enumerate(revs):
651 single(cset, seqno, fp)
654 single(cset, seqno, fp)
652
655
653 def diffstat(patchlines):
656 def diffstat(patchlines):
654 fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
657 fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
655 try:
658 try:
656 p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
659 p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
657 try:
660 try:
658 for line in patchlines: print >> p.tochild, line
661 for line in patchlines: print >> p.tochild, line
659 p.tochild.close()
662 p.tochild.close()
660 if p.wait(): return
663 if p.wait(): return
661 fp = os.fdopen(fd, 'r')
664 fp = os.fdopen(fd, 'r')
662 stat = []
665 stat = []
663 for line in fp: stat.append(line.lstrip())
666 for line in fp: stat.append(line.lstrip())
664 last = stat.pop()
667 last = stat.pop()
665 stat.insert(0, last)
668 stat.insert(0, last)
666 stat = ''.join(stat)
669 stat = ''.join(stat)
667 if stat.startswith('0 files'): raise ValueError
670 if stat.startswith('0 files'): raise ValueError
668 return stat
671 return stat
669 except: raise
672 except: raise
670 finally:
673 finally:
671 try: os.unlink(name)
674 try: os.unlink(name)
672 except: pass
675 except: pass
@@ -1,145 +1,164
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init a
3 hg init a
4 cd a
4 cd a
5
5
6 echo % new file
6 echo % new file
7 hg import -mnew - <<EOF
7 hg import -mnew - <<EOF
8 diff --git a/new b/new
8 diff --git a/new b/new
9 new file mode 100644
9 new file mode 100644
10 index 0000000..7898192
10 index 0000000..7898192
11 --- /dev/null
11 --- /dev/null
12 +++ b/new
12 +++ b/new
13 @@ -0,0 +1 @@
13 @@ -0,0 +1 @@
14 +a
14 +a
15 EOF
15 EOF
16
16
17 echo % new empty file
17 echo % new empty file
18 hg import -mempty - <<EOF
18 hg import -mempty - <<EOF
19 diff --git a/empty b/empty
19 diff --git a/empty b/empty
20 new file mode 100644
20 new file mode 100644
21 EOF
21 EOF
22 hg locate empty
22 hg locate empty
23
23
24 echo % chmod +x
24 echo % chmod +x
25 hg import -msetx - <<EOF
25 hg import -msetx - <<EOF
26 diff --git a/new b/new
26 diff --git a/new b/new
27 old mode 100644
27 old mode 100644
28 new mode 100755
28 new mode 100755
29 EOF
29 EOF
30
30
31 test -x new || echo failed
31 test -x new || echo failed
32
32
33 echo % copy
33 echo % copy
34 hg import -mcopy - <<EOF
34 hg import -mcopy - <<EOF
35 diff --git a/new b/copy
35 diff --git a/new b/copy
36 old mode 100755
36 old mode 100755
37 new mode 100644
37 new mode 100644
38 similarity index 100%
38 similarity index 100%
39 copy from new
39 copy from new
40 copy to copy
40 copy to copy
41 diff --git a/new b/copyx
41 diff --git a/new b/copyx
42 similarity index 100%
42 similarity index 100%
43 copy from new
43 copy from new
44 copy to copyx
44 copy to copyx
45 EOF
45 EOF
46
46
47 test -f copy -a ! -x copy || echo failed
47 test -f copy -a ! -x copy || echo failed
48 test -x copyx || echo failed
48 test -x copyx || echo failed
49 cat copy
49 cat copy
50 hg cat copy
50 hg cat copy
51
51
52 echo % rename
52 echo % rename
53 hg import -mrename - <<EOF
53 hg import -mrename - <<EOF
54 diff --git a/copy b/rename
54 diff --git a/copy b/rename
55 similarity index 100%
55 similarity index 100%
56 rename from copy
56 rename from copy
57 rename to rename
57 rename to rename
58 EOF
58 EOF
59
59
60 hg locate
60 hg locate
61
61
62 echo % delete
62 echo % delete
63 hg import -mdelete - <<EOF
63 hg import -mdelete - <<EOF
64 diff --git a/copyx b/copyx
64 diff --git a/copyx b/copyx
65 deleted file mode 100755
65 deleted file mode 100755
66 index 7898192..0000000
66 index 7898192..0000000
67 --- a/copyx
67 --- a/copyx
68 +++ /dev/null
68 +++ /dev/null
69 @@ -1 +0,0 @@
69 @@ -1 +0,0 @@
70 -a
70 -a
71 EOF
71 EOF
72
72
73 hg locate
73 hg locate
74 test -f copyx && echo failed || true
74 test -f copyx && echo failed || true
75
75
76 echo % regular diff
76 echo % regular diff
77 hg import -mregular - <<EOF
77 hg import -mregular - <<EOF
78 diff --git a/rename b/rename
78 diff --git a/rename b/rename
79 index 7898192..72e1fe3 100644
79 index 7898192..72e1fe3 100644
80 --- a/rename
80 --- a/rename
81 +++ b/rename
81 +++ b/rename
82 @@ -1 +1,5 @@
82 @@ -1 +1,5 @@
83 a
83 a
84 +a
84 +a
85 +a
85 +a
86 +a
86 +a
87 +a
87 +a
88 EOF
88 EOF
89
89
90 echo % copy and modify
90 echo % copy and modify
91 hg import -mcopymod - <<EOF
91 hg import -mcopymod - <<EOF
92 diff --git a/rename b/copy2
92 diff --git a/rename b/copy2
93 similarity index 80%
93 similarity index 80%
94 copy from rename
94 copy from rename
95 copy to copy2
95 copy to copy2
96 index 72e1fe3..b53c148 100644
96 index 72e1fe3..b53c148 100644
97 --- a/rename
97 --- a/rename
98 +++ b/copy2
98 +++ b/copy2
99 @@ -1,5 +1,5 @@
99 @@ -1,5 +1,5 @@
100 a
100 a
101 a
101 a
102 -a
102 -a
103 +b
103 +b
104 a
104 a
105 a
105 a
106 EOF
106 EOF
107
107
108 hg cat copy2
108 hg cat copy2
109
109
110 echo % rename and modify
110 echo % rename and modify
111 hg import -mrenamemod - <<EOF
111 hg import -mrenamemod - <<EOF
112 diff --git a/copy2 b/rename2
112 diff --git a/copy2 b/rename2
113 similarity index 80%
113 similarity index 80%
114 rename from copy2
114 rename from copy2
115 rename to rename2
115 rename to rename2
116 index b53c148..8f81e29 100644
116 index b53c148..8f81e29 100644
117 --- a/copy2
117 --- a/copy2
118 +++ b/rename2
118 +++ b/rename2
119 @@ -1,5 +1,5 @@
119 @@ -1,5 +1,5 @@
120 a
120 a
121 a
121 a
122 b
122 b
123 -a
123 -a
124 +c
124 +c
125 a
125 a
126 EOF
126 EOF
127
127
128 hg locate copy2
128 hg locate copy2
129 hg cat rename2
129 hg cat rename2
130
130
131 echo % one file renamed multiple times
131 echo % one file renamed multiple times
132 hg import -mmultirenames - <<EOF
132 hg import -mmultirenames - <<EOF
133 diff --git a/rename2 b/rename3
133 diff --git a/rename2 b/rename3
134 rename from rename2
134 rename from rename2
135 rename to rename3
135 rename to rename3
136 diff --git a/rename2 b/rename3-2
136 diff --git a/rename2 b/rename3-2
137 rename from rename2
137 rename from rename2
138 rename to rename3-2
138 rename to rename3-2
139 EOF
139 EOF
140 hg log -vCr. --template '{rev} {files} / {file_copies%filecopy}\n'
140 hg log -vCr. --template '{rev} {files} / {file_copies%filecopy}\n'
141
141
142 hg locate rename2 rename3 rename3-2
142 hg locate rename2 rename3 rename3-2
143 hg cat rename3
143 hg cat rename3
144 echo
144 echo
145 hg cat rename3-2
145 hg cat rename3-2
146
147 echo foo > foo
148 hg add foo
149 hg ci -m 'add foo'
150 echo % binary files and regular patch hunks
151 hg import -m binaryregular - <<EOF
152 diff --git a/binary b/binary
153 new file mode 100644
154 index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4
155 GIT binary patch
156 literal 4
157 Lc\${NkU|;|M00aO5
158
159 diff --git a/foo b/foo2
160 rename from foo
161 rename to foo2
162 EOF
163 cat foo2
164 hg manifest | grep binary
@@ -1,56 +1,60
1 % new file
1 % new file
2 applying patch from stdin
2 applying patch from stdin
3 % new empty file
3 % new empty file
4 applying patch from stdin
4 applying patch from stdin
5 empty
5 empty
6 % chmod +x
6 % chmod +x
7 applying patch from stdin
7 applying patch from stdin
8 % copy
8 % copy
9 applying patch from stdin
9 applying patch from stdin
10 a
10 a
11 a
11 a
12 % rename
12 % rename
13 applying patch from stdin
13 applying patch from stdin
14 copyx
14 copyx
15 empty
15 empty
16 new
16 new
17 rename
17 rename
18 % delete
18 % delete
19 applying patch from stdin
19 applying patch from stdin
20 empty
20 empty
21 new
21 new
22 rename
22 rename
23 % regular diff
23 % regular diff
24 applying patch from stdin
24 applying patch from stdin
25 % copy and modify
25 % copy and modify
26 applying patch from stdin
26 applying patch from stdin
27 a
27 a
28 a
28 a
29 b
29 b
30 a
30 a
31 a
31 a
32 % rename and modify
32 % rename and modify
33 applying patch from stdin
33 applying patch from stdin
34 copy2: No such file or directory
34 copy2: No such file or directory
35 a
35 a
36 a
36 a
37 b
37 b
38 c
38 c
39 a
39 a
40 % one file renamed multiple times
40 % one file renamed multiple times
41 applying patch from stdin
41 applying patch from stdin
42 9 rename2 rename3 rename3-2 / rename3 (rename2)rename3-2 (rename2)
42 9 rename2 rename3 rename3-2 / rename3 (rename2)rename3-2 (rename2)
43 rename2: No such file or directory
43 rename2: No such file or directory
44 rename3
44 rename3
45 rename3-2
45 rename3-2
46 a
46 a
47 a
47 a
48 b
48 b
49 c
49 c
50 a
50 a
51
51
52 a
52 a
53 a
53 a
54 b
54 b
55 c
55 c
56 a
56 a
57 % binary files and regular patch hunks
58 applying patch from stdin
59 foo
60 045c85ba38952325e126c70962cc0f9d9077bc67 644 binary
General Comments 0
You need to be logged in to leave comments. Login now