mercurial/patch.py
changeset 10384 832f35386067
parent 10282 08a0f04b56bd
child 10466 d1f209bb9564
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.