Show More
@@ -0,0 +1,165 b'' | |||||
|
1 | # patch.py - patch file parsing routines | |||
|
2 | # | |||
|
3 | # This software may be used and distributed according to the terms | |||
|
4 | # of the GNU General Public License, incorporated herein by reference. | |||
|
5 | ||||
|
6 | from demandload import demandload | |||
|
7 | demandload(globals(), "util") | |||
|
8 | demandload(globals(), "os re shutil tempfile") | |||
|
9 | ||||
|
10 | def readgitpatch(patchname): | |||
|
11 | """extract git-style metadata about patches from <patchname>""" | |||
|
12 | class gitpatch: | |||
|
13 | "op is one of ADD, DELETE, RENAME, MODIFY or COPY" | |||
|
14 | def __init__(self, path): | |||
|
15 | self.path = path | |||
|
16 | self.oldpath = None | |||
|
17 | self.mode = None | |||
|
18 | self.op = 'MODIFY' | |||
|
19 | self.copymod = False | |||
|
20 | self.lineno = 0 | |||
|
21 | ||||
|
22 | # Filter patch for git information | |||
|
23 | gitre = re.compile('diff --git a/(.*) b/(.*)') | |||
|
24 | pf = file(patchname) | |||
|
25 | gp = None | |||
|
26 | gitpatches = [] | |||
|
27 | # Can have a git patch with only metadata, causing patch to complain | |||
|
28 | dopatch = False | |||
|
29 | ||||
|
30 | lineno = 0 | |||
|
31 | for line in pf: | |||
|
32 | lineno += 1 | |||
|
33 | if line.startswith('diff --git'): | |||
|
34 | m = gitre.match(line) | |||
|
35 | if m: | |||
|
36 | if gp: | |||
|
37 | gitpatches.append(gp) | |||
|
38 | src, dst = m.group(1,2) | |||
|
39 | gp = gitpatch(dst) | |||
|
40 | gp.lineno = lineno | |||
|
41 | elif gp: | |||
|
42 | if line.startswith('--- '): | |||
|
43 | if gp.op in ('COPY', 'RENAME'): | |||
|
44 | gp.copymod = True | |||
|
45 | dopatch = 'filter' | |||
|
46 | gitpatches.append(gp) | |||
|
47 | gp = None | |||
|
48 | if not dopatch: | |||
|
49 | dopatch = True | |||
|
50 | continue | |||
|
51 | if line.startswith('rename from '): | |||
|
52 | gp.op = 'RENAME' | |||
|
53 | gp.oldpath = line[12:].rstrip() | |||
|
54 | elif line.startswith('rename to '): | |||
|
55 | gp.path = line[10:].rstrip() | |||
|
56 | elif line.startswith('copy from '): | |||
|
57 | gp.op = 'COPY' | |||
|
58 | gp.oldpath = line[10:].rstrip() | |||
|
59 | elif line.startswith('copy to '): | |||
|
60 | gp.path = line[8:].rstrip() | |||
|
61 | elif line.startswith('deleted file'): | |||
|
62 | gp.op = 'DELETE' | |||
|
63 | elif line.startswith('new file mode '): | |||
|
64 | gp.op = 'ADD' | |||
|
65 | gp.mode = int(line.rstrip()[-3:], 8) | |||
|
66 | elif line.startswith('new mode '): | |||
|
67 | gp.mode = int(line.rstrip()[-3:], 8) | |||
|
68 | if gp: | |||
|
69 | gitpatches.append(gp) | |||
|
70 | ||||
|
71 | if not gitpatches: | |||
|
72 | dopatch = True | |||
|
73 | ||||
|
74 | return (dopatch, gitpatches) | |||
|
75 | ||||
|
76 | def dogitpatch(patchname, gitpatches): | |||
|
77 | """Preprocess git patch so that vanilla patch can handle it""" | |||
|
78 | pf = file(patchname) | |||
|
79 | pfline = 1 | |||
|
80 | ||||
|
81 | fd, patchname = tempfile.mkstemp(prefix='hg-patch-') | |||
|
82 | tmpfp = os.fdopen(fd, 'w') | |||
|
83 | ||||
|
84 | try: | |||
|
85 | for i in range(len(gitpatches)): | |||
|
86 | p = gitpatches[i] | |||
|
87 | if not p.copymod: | |||
|
88 | continue | |||
|
89 | ||||
|
90 | if os.path.exists(p.path): | |||
|
91 | raise util.Abort(_("cannot create %s: destination already exists") % | |||
|
92 | p.path) | |||
|
93 | ||||
|
94 | (src, dst) = [os.path.join(os.getcwd(), n) | |||
|
95 | for n in (p.oldpath, p.path)] | |||
|
96 | ||||
|
97 | print "copying %s to %s" % (src, dst) | |||
|
98 | targetdir = os.path.dirname(dst) | |||
|
99 | if not os.path.isdir(targetdir): | |||
|
100 | os.makedirs(targetdir) | |||
|
101 | try: | |||
|
102 | shutil.copyfile(src, dst) | |||
|
103 | shutil.copymode(src, dst) | |||
|
104 | except shutil.Error, inst: | |||
|
105 | raise util.Abort(str(inst)) | |||
|
106 | ||||
|
107 | # rewrite patch hunk | |||
|
108 | while pfline < p.lineno: | |||
|
109 | tmpfp.write(pf.readline()) | |||
|
110 | pfline += 1 | |||
|
111 | tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path)) | |||
|
112 | line = pf.readline() | |||
|
113 | pfline += 1 | |||
|
114 | while not line.startswith('--- a/'): | |||
|
115 | tmpfp.write(line) | |||
|
116 | line = pf.readline() | |||
|
117 | pfline += 1 | |||
|
118 | tmpfp.write('--- a/%s\n' % p.path) | |||
|
119 | ||||
|
120 | line = pf.readline() | |||
|
121 | while line: | |||
|
122 | tmpfp.write(line) | |||
|
123 | line = pf.readline() | |||
|
124 | except: | |||
|
125 | tmpfp.close() | |||
|
126 | os.unlink(patchname) | |||
|
127 | raise | |||
|
128 | ||||
|
129 | tmpfp.close() | |||
|
130 | return patchname | |||
|
131 | ||||
|
132 | def patch(strip, patchname, ui, cwd=None): | |||
|
133 | """apply the patch <patchname> to the working directory. | |||
|
134 | a list of patched files is returned""" | |||
|
135 | ||||
|
136 | (dopatch, gitpatches) = readgitpatch(patchname) | |||
|
137 | ||||
|
138 | files = {} | |||
|
139 | if dopatch: | |||
|
140 | if dopatch == 'filter': | |||
|
141 | patchname = dogitpatch(patchname, gitpatches) | |||
|
142 | patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch') | |||
|
143 | args = [] | |||
|
144 | if cwd: | |||
|
145 | args.append('-d %s' % util.shellquote(cwd)) | |||
|
146 | fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip, | |||
|
147 | util.shellquote(patchname))) | |||
|
148 | ||||
|
149 | if dopatch == 'filter': | |||
|
150 | False and os.unlink(patchname) | |||
|
151 | ||||
|
152 | for line in fp: | |||
|
153 | line = line.rstrip() | |||
|
154 | ui.status("%s\n" % line) | |||
|
155 | if line.startswith('patching file '): | |||
|
156 | pf = util.parse_patch_output(line) | |||
|
157 | files.setdefault(pf, (None, None)) | |||
|
158 | code = fp.close() | |||
|
159 | if code: | |||
|
160 | raise util.Abort(_("patch command failed: %s") % explain_exit(code)[0]) | |||
|
161 | ||||
|
162 | for gp in gitpatches: | |||
|
163 | files[gp.path] = (gp.op, gp) | |||
|
164 | ||||
|
165 | return files |
@@ -10,7 +10,7 b' from node import *' | |||||
10 | from i18n import gettext as _ |
|
10 | from i18n import gettext as _ | |
11 | demandload(globals(), "os re sys signal shutil imp urllib pdb") |
|
11 | demandload(globals(), "os re sys signal shutil imp urllib pdb") | |
12 | demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo") |
|
12 | demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo") | |
13 | demandload(globals(), "fnmatch mdiff random signal tempfile time") |
|
13 | demandload(globals(), "fnmatch mdiff patch random signal tempfile time") | |
14 | demandload(globals(), "traceback errno socket version struct atexit sets bz2") |
|
14 | demandload(globals(), "traceback errno socket version struct atexit sets bz2") | |
15 | demandload(globals(), "archival cStringIO changegroup email.Parser") |
|
15 | demandload(globals(), "archival cStringIO changegroup email.Parser") | |
16 | demandload(globals(), "hgweb.server sshserver") |
|
16 | demandload(globals(), "hgweb.server sshserver") | |
@@ -1826,21 +1826,21 b' def import_(ui, repo, patch1, *patches, ' | |||||
1826 | lock = repo.lock() |
|
1826 | lock = repo.lock() | |
1827 |
|
1827 | |||
1828 | wlock = repo.wlock() |
|
1828 | wlock = repo.wlock() | |
1829 |
for p |
|
1829 | for p in patches: | |
1830 |
pf = os.path.join(d, p |
|
1830 | pf = os.path.join(d, p) | |
1831 |
|
1831 | |||
1832 | message = None |
|
1832 | message = None | |
1833 | user = None |
|
1833 | user = None | |
1834 | date = None |
|
1834 | date = None | |
1835 | hgpatch = False |
|
1835 | hgpatch = False | |
1836 |
|
1836 | |||
1837 | p = email.Parser.Parser() |
|
1837 | parser = email.Parser.Parser() | |
1838 | if pf == '-': |
|
1838 | if pf == '-': | |
1839 | msg = p.parse(sys.stdin) |
|
1839 | msg = parser.parse(sys.stdin) | |
1840 | ui.status(_("applying patch from stdin\n")) |
|
1840 | ui.status(_("applying patch from stdin\n")) | |
1841 | else: |
|
1841 | else: | |
1842 | msg = p.parse(file(pf)) |
|
1842 | msg = parser.parse(file(pf)) | |
1843 |
ui.status(_("applying %s\n") % p |
|
1843 | ui.status(_("applying %s\n") % p) | |
1844 |
|
1844 | |||
1845 | fd, tmpname = tempfile.mkstemp(prefix='hg-patch-') |
|
1845 | fd, tmpname = tempfile.mkstemp(prefix='hg-patch-') | |
1846 | tmpfp = os.fdopen(fd, 'w') |
|
1846 | tmpfp = os.fdopen(fd, 'w') | |
@@ -1908,7 +1908,7 b' def import_(ui, repo, patch1, *patches, ' | |||||
1908 | if not diffs_seen: |
|
1908 | if not diffs_seen: | |
1909 | raise util.Abort(_('no diffs found')) |
|
1909 | raise util.Abort(_('no diffs found')) | |
1910 |
|
1910 | |||
1911 |
files = |
|
1911 | files = patch.patch(strip, tmpname, ui, cwd=repo.root) | |
1912 | removes = [] |
|
1912 | removes = [] | |
1913 | if len(files) > 0: |
|
1913 | if len(files) > 0: | |
1914 | cfiles = files.keys() |
|
1914 | cfiles = files.keys() |
@@ -93,163 +93,6 b' def find_in_path(name, path, default=Non' | |||||
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 |
|
||||
218 | def patch(strip, patchname, ui, cwd=None): |
|
|||
219 | """apply the patch <patchname> to the working directory. |
|
|||
220 | a list of patched files is returned""" |
|
|||
221 |
|
||||
222 | (dopatch, gitpatches) = readgitpatch(patchname) |
|
|||
223 |
|
||||
224 | files = {} |
|
|||
225 | if dopatch: |
|
|||
226 | if dopatch == 'filter': |
|
|||
227 | patchname = dogitpatch(patchname, gitpatches) |
|
|||
228 | patcher = find_in_path('gpatch', os.environ.get('PATH', ''), 'patch') |
|
|||
229 | args = [] |
|
|||
230 | if cwd: |
|
|||
231 | args.append('-d %s' % shellquote(cwd)) |
|
|||
232 | fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip, |
|
|||
233 | shellquote(patchname))) |
|
|||
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 |
|
|||
252 |
|
||||
253 | def binary(s): |
|
96 | def binary(s): | |
254 | """return true if a string is binary data using diff's heuristic""" |
|
97 | """return true if a string is binary data using diff's heuristic""" | |
255 | if s and '\0' in s[:4096]: |
|
98 | if s and '\0' in s[:4096]: |
General Comments 0
You need to be logged in to leave comments.
Login now