mercurial/patch.py
changeset 7152 f0055cec8446
parent 7151 b5bc5293021c
child 7153 353141d74ca8
equal deleted inserted replaced
7151:b5bc5293021c 7152:f0055cec8446
   164     def setmode(self, mode):
   164     def setmode(self, mode):
   165         islink = mode & 020000
   165         islink = mode & 020000
   166         isexec = mode & 0100
   166         isexec = mode & 0100
   167         self.mode = (islink, isexec)
   167         self.mode = (islink, isexec)
   168 
   168 
   169 def readgitpatch(fp, firstline=None):
   169 def readgitpatch(lr):
   170     """extract git-style metadata about patches from <patchname>"""
   170     """extract git-style metadata about patches from <patchname>"""
   171 
       
   172     def reader(fp, firstline):
       
   173         if firstline is not None:
       
   174             yield firstline
       
   175         for line in fp:
       
   176             yield line
       
   177 
   171 
   178     # Filter patch for git information
   172     # Filter patch for git information
   179     gitre = re.compile('diff --git a/(.*) b/(.*)')
   173     gitre = re.compile('diff --git a/(.*) b/(.*)')
   180     gp = None
   174     gp = None
   181     gitpatches = []
   175     gitpatches = []
   182     # Can have a git patch with only metadata, causing patch to complain
   176     # Can have a git patch with only metadata, causing patch to complain
   183     dopatch = 0
   177     dopatch = 0
   184 
   178 
   185     lineno = 0
   179     lineno = 0
   186     for line in reader(fp, firstline):
   180     for line in lr:
   187         lineno += 1
   181         lineno += 1
   188         if line.startswith('diff --git'):
   182         if line.startswith('diff --git'):
   189             m = gitre.match(line)
   183             m = gitre.match(line)
   190             if m:
   184             if m:
   191                 if gp:
   185                 if gp:
   459             self.lenb = 1
   453             self.lenb = 1
   460         else:
   454         else:
   461             self.lenb = int(self.lenb)
   455             self.lenb = int(self.lenb)
   462         self.starta = int(self.starta)
   456         self.starta = int(self.starta)
   463         self.startb = int(self.startb)
   457         self.startb = int(self.startb)
   464         diffhelpers.addlines(lr.fp, self.hunk, self.lena, self.lenb, self.a, self.b)
   458         diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
   465         # if we hit eof before finishing out the hunk, the last line will
   459         # if we hit eof before finishing out the hunk, the last line will
   466         # be zero length.  Lets try to fix it up.
   460         # be zero length.  Lets try to fix it up.
   467         while len(self.hunk[-1]) == 0:
   461         while len(self.hunk[-1]) == 0:
   468             del self.hunk[-1]
   462             del self.hunk[-1]
   469             del self.a[-1]
   463             del self.a[-1]
   769     def __init__(self, fp):
   763     def __init__(self, fp):
   770         self.fp = fp
   764         self.fp = fp
   771         self.buf = []
   765         self.buf = []
   772 
   766 
   773     def push(self, line):
   767     def push(self, line):
   774         self.buf.append(line)
   768         if line is not None:
       
   769             self.buf.append(line)
   775 
   770 
   776     def readline(self):
   771     def readline(self):
   777         if self.buf:
   772         if self.buf:
   778             l = self.buf[0]
   773             l = self.buf[0]
   779             del self.buf[0]
   774             del self.buf[0]
   780             return l
   775             return l
   781         return self.fp.readline()
   776         return self.fp.readline()
       
   777 
       
   778     def __iter__(self):
       
   779         while 1:
       
   780             l = self.readline()
       
   781             if not l:
       
   782                 break
       
   783             yield l
       
   784 
       
   785 def scangitpatch(lr, firstline):
       
   786     """        
       
   787     Git patches can emit:
       
   788     - rename a to b
       
   789     - change b
       
   790     - copy a to c
       
   791     - change c
       
   792         
       
   793     We cannot apply this sequence as-is, the renamed 'a' could not be
       
   794     found for it would have been renamed already. And we cannot copy
       
   795     from 'b' instead because 'b' would have been changed already. So
       
   796     we scan the git patch for copy and rename commands so we can
       
   797     perform the copies ahead of time.
       
   798     """
       
   799     pos = 0
       
   800     try:
       
   801         pos = lr.fp.tell()
       
   802         fp = lr.fp
       
   803     except IOError:
       
   804         fp = cStringIO.StringIO(lr.fp.read())
       
   805     gitlr = linereader(fp)
       
   806     gitlr.push(firstline)
       
   807     (dopatch, gitpatches) = readgitpatch(gitlr)
       
   808     fp.seek(pos)
       
   809     return fp, dopatch, gitpatches
   782 
   810 
   783 def iterhunks(ui, fp, sourcefile=None):
   811 def iterhunks(ui, fp, sourcefile=None):
   784     """Read a patch and yield the following events:
   812     """Read a patch and yield the following events:
   785     - ("file", afile, bfile, firsthunk): select a new target file.
   813     - ("file", afile, bfile, firsthunk): select a new target file.
   786     - ("hunk", hunk): a new hunk is ready to be applied, follows a
   814     - ("hunk", hunk): a new hunk is ready to be applied, follows a
   787     "file" event.
   815     "file" event.
   788     - ("git", gitchanges): current diff is in git format, gitchanges
   816     - ("git", gitchanges): current diff is in git format, gitchanges
   789     maps filenames to gitpatch records. Unique event.
   817     maps filenames to gitpatch records. Unique event.
   790     """
   818     """
   791 
       
   792     def scangitpatch(fp, firstline):
       
   793         '''git patches can modify a file, then copy that file to
       
   794         a new file, but expect the source to be the unmodified form.
       
   795         So we scan the patch looking for that case so we can do
       
   796         the copies ahead of time.'''
       
   797 
       
   798         pos = 0
       
   799         try:
       
   800             pos = fp.tell()
       
   801         except IOError:
       
   802             fp = cStringIO.StringIO(fp.read())
       
   803 
       
   804         (dopatch, gitpatches) = readgitpatch(fp, firstline)
       
   805         fp.seek(pos)
       
   806 
       
   807         return fp, dopatch, gitpatches
       
   808 
       
   809     changed = {}
   819     changed = {}
   810     current_hunk = None
   820     current_hunk = None
   811     afile = ""
   821     afile = ""
   812     bfile = ""
   822     bfile = ""
   813     state = None
   823     state = None
   867             m = gitre.match(x)
   877             m = gitre.match(x)
   868             if m:
   878             if m:
   869                 afile, bfile = m.group(1, 2)
   879                 afile, bfile = m.group(1, 2)
   870                 if not git:
   880                 if not git:
   871                     git = True
   881                     git = True
   872                     fp, dopatch, gitpatches = scangitpatch(fp, x)
   882                     fp, dopatch, gitpatches = scangitpatch(lr, x)
   873                     yield 'git', gitpatches
   883                     yield 'git', gitpatches
   874                     for gp in gitpatches:
   884                     for gp in gitpatches:
   875                         changed[gp.path] = gp
   885                         changed[gp.path] = gp
   876                 # else error?
   886                 # else error?
   877                 # copy/rename + modify should modify target, not source
   887                 # copy/rename + modify should modify target, not source