##// END OF EJS Templates
util: qualify name properly.
Vadim Gelfer -
r2868:9a2a481e default
parent child Browse files
Show More
@@ -1,250 +1,251 b''
1 1 # patch.py - patch file parsing routines
2 2 #
3 3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import demandload
9 9 from i18n import gettext as _
10 10 demandload(globals(), "util")
11 11 demandload(globals(), "cStringIO email.Parser os re shutil tempfile")
12 12
13 13 def extract(ui, fileobj):
14 14 '''extract patch from data read from fileobj.
15 15
16 16 patch can be normal patch or contained in email message.
17 17
18 18 return tuple (filename, message, user, date). any item in returned
19 19 tuple can be None. if filename is None, fileobj did not contain
20 20 patch. caller must unlink filename when done.'''
21 21
22 22 # attempt to detect the start of a patch
23 23 # (this heuristic is borrowed from quilt)
24 24 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
25 25 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
26 26 '(---|\*\*\*)[ \t])', re.MULTILINE)
27 27
28 28 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
29 29 tmpfp = os.fdopen(fd, 'w')
30 30 try:
31 31 hgpatch = False
32 32
33 33 msg = email.Parser.Parser().parse(fileobj)
34 34
35 35 message = msg['Subject']
36 36 user = msg['From']
37 37 # should try to parse msg['Date']
38 38 date = None
39 39
40 40 if message:
41 41 message = message.replace('\n\t', ' ')
42 42 ui.debug('Subject: %s\n' % message)
43 43 if user:
44 44 ui.debug('From: %s\n' % user)
45 45 diffs_seen = 0
46 46 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
47 47
48 48 for part in msg.walk():
49 49 content_type = part.get_content_type()
50 50 ui.debug('Content-Type: %s\n' % content_type)
51 51 if content_type not in ok_types:
52 52 continue
53 53 payload = part.get_payload(decode=True)
54 54 m = diffre.search(payload)
55 55 if m:
56 56 ui.debug(_('found patch at byte %d\n') % m.start(0))
57 57 diffs_seen += 1
58 58 cfp = cStringIO.StringIO()
59 59 if message:
60 60 cfp.write(message)
61 61 cfp.write('\n')
62 62 for line in payload[:m.start(0)].splitlines():
63 63 if line.startswith('# HG changeset patch'):
64 64 ui.debug(_('patch generated by hg export\n'))
65 65 hgpatch = True
66 66 # drop earlier commit message content
67 67 cfp.seek(0)
68 68 cfp.truncate()
69 69 elif hgpatch:
70 70 if line.startswith('# User '):
71 71 user = line[7:]
72 72 ui.debug('From: %s\n' % user)
73 73 elif line.startswith("# Date "):
74 74 date = line[7:]
75 75 if not line.startswith('# '):
76 76 cfp.write(line)
77 77 cfp.write('\n')
78 78 message = cfp.getvalue()
79 79 if tmpfp:
80 80 tmpfp.write(payload)
81 81 if not payload.endswith('\n'):
82 82 tmpfp.write('\n')
83 83 elif not diffs_seen and message and content_type == 'text/plain':
84 84 message += '\n' + payload
85 85 except:
86 86 tmpfp.close()
87 87 os.unlink(tmpname)
88 88 raise
89 89
90 90 tmpfp.close()
91 91 if not diffs_seen:
92 92 os.unlink(tmpname)
93 93 return None, message, user, date
94 94 return tmpname, message, user, date
95 95
96 96 def readgitpatch(patchname):
97 97 """extract git-style metadata about patches from <patchname>"""
98 98 class gitpatch:
99 99 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
100 100 def __init__(self, path):
101 101 self.path = path
102 102 self.oldpath = None
103 103 self.mode = None
104 104 self.op = 'MODIFY'
105 105 self.copymod = False
106 106 self.lineno = 0
107 107
108 108 # Filter patch for git information
109 109 gitre = re.compile('diff --git a/(.*) b/(.*)')
110 110 pf = file(patchname)
111 111 gp = None
112 112 gitpatches = []
113 113 # Can have a git patch with only metadata, causing patch to complain
114 114 dopatch = False
115 115
116 116 lineno = 0
117 117 for line in pf:
118 118 lineno += 1
119 119 if line.startswith('diff --git'):
120 120 m = gitre.match(line)
121 121 if m:
122 122 if gp:
123 123 gitpatches.append(gp)
124 124 src, dst = m.group(1,2)
125 125 gp = gitpatch(dst)
126 126 gp.lineno = lineno
127 127 elif gp:
128 128 if line.startswith('--- '):
129 129 if gp.op in ('COPY', 'RENAME'):
130 130 gp.copymod = True
131 131 dopatch = 'filter'
132 132 gitpatches.append(gp)
133 133 gp = None
134 134 if not dopatch:
135 135 dopatch = True
136 136 continue
137 137 if line.startswith('rename from '):
138 138 gp.op = 'RENAME'
139 139 gp.oldpath = line[12:].rstrip()
140 140 elif line.startswith('rename to '):
141 141 gp.path = line[10:].rstrip()
142 142 elif line.startswith('copy from '):
143 143 gp.op = 'COPY'
144 144 gp.oldpath = line[10:].rstrip()
145 145 elif line.startswith('copy to '):
146 146 gp.path = line[8:].rstrip()
147 147 elif line.startswith('deleted file'):
148 148 gp.op = 'DELETE'
149 149 elif line.startswith('new file mode '):
150 150 gp.op = 'ADD'
151 151 gp.mode = int(line.rstrip()[-3:], 8)
152 152 elif line.startswith('new mode '):
153 153 gp.mode = int(line.rstrip()[-3:], 8)
154 154 if gp:
155 155 gitpatches.append(gp)
156 156
157 157 if not gitpatches:
158 158 dopatch = True
159 159
160 160 return (dopatch, gitpatches)
161 161
162 162 def dogitpatch(patchname, gitpatches):
163 163 """Preprocess git patch so that vanilla patch can handle it"""
164 164 pf = file(patchname)
165 165 pfline = 1
166 166
167 167 fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
168 168 tmpfp = os.fdopen(fd, 'w')
169 169
170 170 try:
171 171 for i in range(len(gitpatches)):
172 172 p = gitpatches[i]
173 173 if not p.copymod:
174 174 continue
175 175
176 176 if os.path.exists(p.path):
177 177 raise util.Abort(_("cannot create %s: destination already exists") %
178 178 p.path)
179 179
180 180 (src, dst) = [os.path.join(os.getcwd(), n)
181 181 for n in (p.oldpath, p.path)]
182 182
183 183 targetdir = os.path.dirname(dst)
184 184 if not os.path.isdir(targetdir):
185 185 os.makedirs(targetdir)
186 186 try:
187 187 shutil.copyfile(src, dst)
188 188 shutil.copymode(src, dst)
189 189 except shutil.Error, inst:
190 190 raise util.Abort(str(inst))
191 191
192 192 # rewrite patch hunk
193 193 while pfline < p.lineno:
194 194 tmpfp.write(pf.readline())
195 195 pfline += 1
196 196 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
197 197 line = pf.readline()
198 198 pfline += 1
199 199 while not line.startswith('--- a/'):
200 200 tmpfp.write(line)
201 201 line = pf.readline()
202 202 pfline += 1
203 203 tmpfp.write('--- a/%s\n' % p.path)
204 204
205 205 line = pf.readline()
206 206 while line:
207 207 tmpfp.write(line)
208 208 line = pf.readline()
209 209 except:
210 210 tmpfp.close()
211 211 os.unlink(patchname)
212 212 raise
213 213
214 214 tmpfp.close()
215 215 return patchname
216 216
217 217 def patch(strip, patchname, ui, cwd=None):
218 218 """apply the patch <patchname> to the working directory.
219 219 a list of patched files is returned"""
220 220
221 221 (dopatch, gitpatches) = readgitpatch(patchname)
222 222
223 223 files = {}
224 224 if dopatch:
225 225 if dopatch == 'filter':
226 226 patchname = dogitpatch(patchname, gitpatches)
227 227 patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
228 228 args = []
229 229 if cwd:
230 230 args.append('-d %s' % util.shellquote(cwd))
231 231 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
232 232 util.shellquote(patchname)))
233 233
234 234 if dopatch == 'filter':
235 235 False and os.unlink(patchname)
236 236
237 237 for line in fp:
238 238 line = line.rstrip()
239 239 ui.status("%s\n" % line)
240 240 if line.startswith('patching file '):
241 241 pf = util.parse_patch_output(line)
242 242 files.setdefault(pf, (None, None))
243 243 code = fp.close()
244 244 if code:
245 raise util.Abort(_("patch command failed: %s") % explain_exit(code)[0])
245 raise util.Abort(_("patch command failed: %s") %
246 util.explain_exit(code)[0])
246 247
247 248 for gp in gitpatches:
248 249 files[gp.path] = (gp.op, gp)
249 250
250 251 return files
General Comments 0
You need to be logged in to leave comments. Login now