##// END OF EJS Templates
Teach import to understand git diff extensions....
Brendan Cully -
r2860:b3d1145e default
parent child Browse files
Show More
@@ -1825,6 +1825,7 b' def import_(ui, repo, patch1, *patches, '
1825 wlock = repo.wlock()
1825 wlock = repo.wlock()
1826 lock = repo.lock()
1826 lock = repo.lock()
1827
1827
1828 wlock = repo.wlock()
1828 for patch in patches:
1829 for patch in patches:
1829 pf = os.path.join(d, patch)
1830 pf = os.path.join(d, patch)
1830
1831
@@ -1908,12 +1909,44 b' def import_(ui, repo, patch1, *patches, '
1908 raise util.Abort(_('no diffs found'))
1909 raise util.Abort(_('no diffs found'))
1909
1910
1910 files = util.patch(strip, tmpname, ui, cwd=repo.root)
1911 files = util.patch(strip, tmpname, ui, cwd=repo.root)
1912 removes = []
1911 if len(files) > 0:
1913 if len(files) > 0:
1912 cfiles = files
1914 cfiles = files.keys()
1915 copies = []
1916 copts = {'after': False, 'force': False}
1913 cwd = repo.getcwd()
1917 cwd = repo.getcwd()
1914 if cwd:
1918 if cwd:
1915 cfiles = [util.pathto(cwd, f) for f in files]
1919 cfiles = [util.pathto(cwd, f) for f in files.keys()]
1920 for f in files:
1921 ctype, gp = files[f]
1922 if ctype == 'RENAME':
1923 copies.append((gp.oldpath, gp.path, gp.copymod))
1924 removes.append(gp.oldpath)
1925 elif ctype == 'COPY':
1926 copies.append((gp.oldpath, gp.path, gp.copymod))
1927 elif ctype == 'DELETE':
1928 removes.append(gp.path)
1929 for src, dst, after in copies:
1930 absdst = os.path.join(repo.root, dst)
1931 if not after and os.path.exists(absdst):
1932 raise util.Abort(_('patch creates existing file %s') % dst)
1933 if cwd:
1934 src, dst = [util.pathto(cwd, f) for f in (src, dst)]
1935 copts['after'] = after
1936 errs, copied = docopy(ui, repo, (src, dst), copts, wlock=wlock)
1937 if errs:
1938 raise util.Abort(errs)
1939 if removes:
1940 repo.remove(removes, True, wlock=wlock)
1941 for f in files:
1942 ctype, gp = files[f]
1943 if gp and gp.mode:
1944 x = gp.mode & 0100 != 0
1945 dst = os.path.join(repo.root, gp.path)
1946 util.set_exec(dst, x)
1916 addremove_lock(ui, repo, cfiles, {}, wlock=wlock)
1947 addremove_lock(ui, repo, cfiles, {}, wlock=wlock)
1948 files = files.keys()
1949 files.extend([r for r in removes if r not in files])
1917 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1950 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1918 finally:
1951 finally:
1919 os.unlink(tmpname)
1952 os.unlink(tmpname)
@@ -93,26 +93,162 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
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"""
General Comments 0
You need to be logged in to leave comments. Login now