Mercurial > public > mercurial-scm > hg
comparison mercurial/patch.py @ 10384:832f35386067
import: import each patch in a file or stream as a separate change
Supports hg export <revrange>, mail messages, and mailboxes.
Does not support multiple patches in a single MIME attachment.
Closes issue167.
author | Brendan Cully <brendan@kublai.com> |
---|---|
date | Sun, 07 Feb 2010 18:06:52 +0100 |
parents | 08a0f04b56bd |
children | d1f209bb9564 |
comparison
equal
deleted
inserted
replaced
10383:f83291e5643e | 10384:832f35386067 |
---|---|
38 % dst) | 38 % dst) |
39 | 39 |
40 util.copyfile(abssrc, absdst) | 40 util.copyfile(abssrc, absdst) |
41 | 41 |
42 # public functions | 42 # public functions |
43 | |
44 def split(stream): | |
45 '''return an iterator of individual patches from a stream''' | |
46 def isheader(line, inheader): | |
47 if inheader and line[0] in (' ', '\t'): | |
48 # continuation | |
49 return True | |
50 l = line.split(': ', 1) | |
51 return len(l) == 2 and ' ' not in l[0] | |
52 | |
53 def chunk(lines): | |
54 return cStringIO.StringIO(''.join(lines)) | |
55 | |
56 def hgsplit(stream, cur): | |
57 inheader = True | |
58 | |
59 for line in stream: | |
60 if not line.strip(): | |
61 inheader = False | |
62 if not inheader and line.startswith('# HG changeset patch'): | |
63 yield chunk(cur) | |
64 cur = [] | |
65 inheader = True | |
66 | |
67 cur.append(line) | |
68 | |
69 if cur: | |
70 yield chunk(cur) | |
71 | |
72 def mboxsplit(stream, cur): | |
73 for line in stream: | |
74 if line.startswith('From '): | |
75 for c in split(chunk(cur[1:])): | |
76 yield c | |
77 cur = [] | |
78 | |
79 cur.append(line) | |
80 | |
81 if cur: | |
82 for c in split(chunk(cur[1:])): | |
83 yield c | |
84 | |
85 def mimesplit(stream, cur): | |
86 def msgfp(m): | |
87 fp = cStringIO.StringIO() | |
88 g = email.Generator.Generator(fp, mangle_from_=False) | |
89 g.flatten(m) | |
90 fp.seek(0) | |
91 return fp | |
92 | |
93 for line in stream: | |
94 cur.append(line) | |
95 c = chunk(cur) | |
96 | |
97 m = email.Parser.Parser().parse(c) | |
98 if not m.is_multipart(): | |
99 yield msgfp(m) | |
100 else: | |
101 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch') | |
102 for part in m.walk(): | |
103 ct = part.get_content_type() | |
104 if ct not in ok_types: | |
105 continue | |
106 yield msgfp(part) | |
107 | |
108 def headersplit(stream, cur): | |
109 inheader = False | |
110 | |
111 for line in stream: | |
112 if not inheader and isheader(line, inheader): | |
113 yield chunk(cur) | |
114 cur = [] | |
115 inheader = True | |
116 if inheader and not isheader(line, inheader): | |
117 inheader = False | |
118 | |
119 cur.append(line) | |
120 | |
121 if cur: | |
122 yield chunk(cur) | |
123 | |
124 def remainder(cur): | |
125 yield chunk(cur) | |
126 | |
127 class fiter(object): | |
128 def __init__(self, fp): | |
129 self.fp = fp | |
130 | |
131 def __iter__(self): | |
132 return self | |
133 | |
134 def next(self): | |
135 l = self.fp.readline() | |
136 if not l: | |
137 raise StopIteration | |
138 return l | |
139 | |
140 inheader = False | |
141 cur = [] | |
142 | |
143 mimeheaders = ['content-type'] | |
144 | |
145 if not hasattr(stream, 'next'): | |
146 # http responses, for example, have readline but not next | |
147 stream = fiter(stream) | |
148 | |
149 for line in stream: | |
150 cur.append(line) | |
151 if line.startswith('# HG changeset patch'): | |
152 return hgsplit(stream, cur) | |
153 elif line.startswith('From '): | |
154 return mboxsplit(stream, cur) | |
155 elif isheader(line, inheader): | |
156 inheader = True | |
157 if line.split(':', 1)[0].lower() in mimeheaders: | |
158 # let email parser handle this | |
159 return mimesplit(stream, cur) | |
160 elif inheader: | |
161 # No evil headers seen, split by hand | |
162 return headersplit(stream, cur) | |
163 # Not enough info, keep reading | |
164 | |
165 # if we are here, we have a very plain patch | |
166 return remainder(cur) | |
43 | 167 |
44 def extract(ui, fileobj): | 168 def extract(ui, fileobj): |
45 '''extract patch from data read from fileobj. | 169 '''extract patch from data read from fileobj. |
46 | 170 |
47 patch can be a normal patch or contained in an email message. | 171 patch can be a normal patch or contained in an email message. |