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 |