91 p_name = os.path.join(p, name) |
91 p_name = os.path.join(p, name) |
92 if os.path.exists(p_name): |
92 if os.path.exists(p_name): |
93 return p_name |
93 return p_name |
94 return default |
94 return default |
95 |
95 |
|
96 def readgitpatch(patchname): |
|
97 """extract git-style metadata about patches from <patchname>""" |
|
98 class gitpatch: |
|
99 "op is one of ADD, DELETE, RENAME, MODIFY or COPY" |
|
100 def __init__(self, path): |
|
101 self.path = path |
|
102 self.oldpath = None |
|
103 self.mode = None |
|
104 self.op = 'MODIFY' |
|
105 self.copymod = False |
|
106 self.lineno = 0 |
|
107 |
|
108 # Filter patch for git information |
|
109 gitre = re.compile('diff --git a/(.*) b/(.*)') |
|
110 pf = file(patchname) |
|
111 gp = None |
|
112 gitpatches = [] |
|
113 # Can have a git patch with only metadata, causing patch to complain |
|
114 dopatch = False |
|
115 |
|
116 lineno = 0 |
|
117 for line in pf: |
|
118 lineno += 1 |
|
119 if line.startswith('diff --git'): |
|
120 m = gitre.match(line) |
|
121 if m: |
|
122 if gp: |
|
123 gitpatches.append(gp) |
|
124 src, dst = m.group(1,2) |
|
125 gp = gitpatch(dst) |
|
126 gp.lineno = lineno |
|
127 elif gp: |
|
128 if line.startswith('--- '): |
|
129 if gp.op in ('COPY', 'RENAME'): |
|
130 gp.copymod = True |
|
131 dopatch = 'filter' |
|
132 gitpatches.append(gp) |
|
133 gp = None |
|
134 if not dopatch: |
|
135 dopatch = True |
|
136 continue |
|
137 if line.startswith('rename from '): |
|
138 gp.op = 'RENAME' |
|
139 gp.oldpath = line[12:].rstrip() |
|
140 elif line.startswith('rename to '): |
|
141 gp.path = line[10:].rstrip() |
|
142 elif line.startswith('copy from '): |
|
143 gp.op = 'COPY' |
|
144 gp.oldpath = line[10:].rstrip() |
|
145 elif line.startswith('copy to '): |
|
146 gp.path = line[8:].rstrip() |
|
147 elif line.startswith('deleted file'): |
|
148 gp.op = 'DELETE' |
|
149 elif line.startswith('new file mode '): |
|
150 gp.op = 'ADD' |
|
151 gp.mode = int(line.rstrip()[-3:], 8) |
|
152 elif line.startswith('new mode '): |
|
153 gp.mode = int(line.rstrip()[-3:], 8) |
|
154 if gp: |
|
155 gitpatches.append(gp) |
|
156 |
|
157 if not gitpatches: |
|
158 dopatch = True |
|
159 |
|
160 return (dopatch, gitpatches) |
|
161 |
|
162 def dogitpatch(patchname, gitpatches): |
|
163 """Preprocess git patch so that vanilla patch can handle it""" |
|
164 pf = file(patchname) |
|
165 pfline = 1 |
|
166 |
|
167 fd, patchname = tempfile.mkstemp(prefix='hg-patch-') |
|
168 tmpfp = os.fdopen(fd, 'w') |
|
169 |
|
170 try: |
|
171 for i in range(len(gitpatches)): |
|
172 p = gitpatches[i] |
|
173 if not p.copymod: |
|
174 continue |
|
175 |
|
176 if os.path.exists(p.path): |
|
177 raise Abort(_("cannot create %s: destination already exists") % |
|
178 p.path) |
|
179 |
|
180 (src, dst) = [os.path.join(os.getcwd(), n) |
|
181 for n in (p.oldpath, p.path)] |
|
182 |
|
183 print "copying %s to %s" % (src, dst) |
|
184 targetdir = os.path.dirname(dst) |
|
185 if not os.path.isdir(targetdir): |
|
186 os.makedirs(targetdir) |
|
187 try: |
|
188 shutil.copyfile(src, dst) |
|
189 shutil.copymode(src, dst) |
|
190 except shutil.Error, inst: |
|
191 raise Abort(str(inst)) |
|
192 |
|
193 # rewrite patch hunk |
|
194 while pfline < p.lineno: |
|
195 tmpfp.write(pf.readline()) |
|
196 pfline += 1 |
|
197 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path)) |
|
198 line = pf.readline() |
|
199 pfline += 1 |
|
200 while not line.startswith('--- a/'): |
|
201 tmpfp.write(line) |
|
202 line = pf.readline() |
|
203 pfline += 1 |
|
204 tmpfp.write('--- a/%s\n' % p.path) |
|
205 |
|
206 line = pf.readline() |
|
207 while line: |
|
208 tmpfp.write(line) |
|
209 line = pf.readline() |
|
210 except: |
|
211 tmpfp.close() |
|
212 os.unlink(patchname) |
|
213 raise |
|
214 |
|
215 tmpfp.close() |
|
216 return patchname |
|
217 |
96 def patch(strip, patchname, ui, cwd=None): |
218 def patch(strip, patchname, ui, cwd=None): |
97 """apply the patch <patchname> to the working directory. |
219 """apply the patch <patchname> to the working directory. |
98 a list of patched files is returned""" |
220 a list of patched files is returned""" |
99 patcher = find_in_path('gpatch', os.environ.get('PATH', ''), 'patch') |
221 |
100 args = [] |
222 (dopatch, gitpatches) = readgitpatch(patchname) |
101 if cwd: |
223 |
102 args.append('-d %s' % shellquote(cwd)) |
|
103 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip, |
|
104 shellquote(patchname))) |
|
105 files = {} |
224 files = {} |
106 for line in fp: |
225 if dopatch: |
107 line = line.rstrip() |
226 if dopatch == 'filter': |
108 ui.status("%s\n" % line) |
227 patchname = dogitpatch(patchname, gitpatches) |
109 if line.startswith('patching file '): |
228 patcher = find_in_path('gpatch', os.environ.get('PATH', ''), 'patch') |
110 pf = parse_patch_output(line) |
229 args = [] |
111 files.setdefault(pf, 1) |
230 if cwd: |
112 code = fp.close() |
231 args.append('-d %s' % shellquote(cwd)) |
113 if code: |
232 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip, |
114 raise Abort(_("patch command failed: %s") % explain_exit(code)[0]) |
233 shellquote(patchname))) |
115 return files.keys() |
234 |
|
235 if dopatch == 'filter': |
|
236 False and os.unlink(patchname) |
|
237 |
|
238 for line in fp: |
|
239 line = line.rstrip() |
|
240 ui.status("%s\n" % line) |
|
241 if line.startswith('patching file '): |
|
242 pf = parse_patch_output(line) |
|
243 files.setdefault(pf, (None, None)) |
|
244 code = fp.close() |
|
245 if code: |
|
246 raise Abort(_("patch command failed: %s") % explain_exit(code)[0]) |
|
247 |
|
248 for gp in gitpatches: |
|
249 files[gp.path] = (gp.op, gp) |
|
250 |
|
251 return files |
116 |
252 |
117 def binary(s): |
253 def binary(s): |
118 """return true if a string is binary data using diff's heuristic""" |
254 """return true if a string is binary data using diff's heuristic""" |
119 if s and '\0' in s[:4096]: |
255 if s and '\0' in s[:4096]: |
120 return True |
256 return True |