Mercurial > public > mercurial-scm > hg
comparison mercurial/patch.py @ 7152:f0055cec8446
patch: pass linereader to scangitpatch(), extract from iterhunks()
It's good because it unifies the patch file access methods.
author | Patrick Mezard <pmezard@gmail.com> |
---|---|
date | Sat, 18 Oct 2008 23:45:46 +0200 |
parents | b5bc5293021c |
children | 353141d74ca8 |
comparison
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 |