1 # (Be in -*- python -*- mode.)
3 # ====================================================================
4 # Copyright (c) 2007 CollabNet. All rights reserved.
6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution. The terms
8 # are also available at http://subversion.tigris.org/license-1.html.
9 # If newer versions of this license are posted there, you may use a
10 # newer version instead, at your option.
12 # This software consists of voluntary contributions made by many
13 # individuals. For exact contribution history, see the revision
14 # history and logs, available at http://cvs2svn.tigris.org/.
15 # ====================================================================
17 """This module processes RCS diffs (deltas)."""
23 """Split S into an array of lines.
25 Only \n is a line separator. The line endings are part of the lines."""
27 # return s.splitlines(True) clobbers \r
28 re
= [ i
+ "\n" for i
in s
.split("\n") ]
35 class MalformedDeltaException(Exception):
36 """A malformed RCS delta was encountered."""
41 """This class represents a single file object to which RCS deltas can be
42 applied in various ways."""
44 ad_command
= re
.compile(r
'^([ad])(\d+)\s(\d+)\n$')
45 a_command
= re
.compile(r
'^a(\d+)\s(\d+)\n$')
47 def __init__(self
, text
):
48 """Instantiate and initialize the file content with TEXT."""
50 self
._texts
= msplit(text
)
53 """Return the current file content."""
55 return "".join(self
._texts
)
57 def apply_diff(self
, diff
):
58 """Apply the RCS diff DIFF to the current file content."""
65 admatch
= self
.ad_command
.match(diffs
[i
])
67 raise MalformedDeltaException('Bad ed command')
69 sl
= int(admatch
.group(2))
70 cn
= int(admatch
.group(3))
71 if admatch
.group(1) == 'd': # "d" - Delete command
74 raise MalformedDeltaException('Deletion before last edit')
75 if sl
> len(self
._texts
):
76 raise MalformedDeltaException('Deletion past file end')
77 if sl
+ cn
> len(self
._texts
):
78 raise MalformedDeltaException('Deletion beyond file end')
79 ntexts
+= self
._texts
[ooff
:sl
]
81 else: # "a" - Add command
82 if sl
< ooff
: # Also catches same place
83 raise MalformedDeltaException('Insertion before last edit')
84 if sl
> len(self
._texts
):
85 raise MalformedDeltaException('Insertion past file end')
86 ntexts
+= self
._texts
[ooff
:sl
] + diffs
[i
:i
+ cn
]
89 self
._texts
= ntexts
+ self
._texts
[ooff
:]
91 def invert_diff(self
, diff
):
92 """Apply the RCS diff DIFF to the current file content and simultaneously
93 generate an RCS diff suitable for reverting the change."""
101 while i
< len(diffs
):
102 admatch
= self
.ad_command
.match(diffs
[i
])
104 raise MalformedDeltaException('Bad ed command')
106 sl
= int(admatch
.group(2))
107 cn
= int(admatch
.group(3))
108 if admatch
.group(1) == 'd': # "d" - Delete command
111 raise MalformedDeltaException('Deletion before last edit')
112 if sl
> len(self
._texts
):
113 raise MalformedDeltaException('Deletion past file end')
114 if sl
+ cn
> len(self
._texts
):
115 raise MalformedDeltaException('Deletion beyond file end')
116 # Handle substitution explicitly, as add must come after del
117 # (last add may end in no newline, so no command can follow).
119 amatch
= self
.a_command
.match(diffs
[i
])
122 if amatch
and int(amatch
.group(1)) == sl
+ cn
:
123 cn2
= int(amatch
.group(2))
125 ndiffs
+= ["d%d %d\na%d %d\n" % \
126 (sl
+ 1 + adjust
, cn2
, sl
+ adjust
+ cn2
, cn
)] + \
127 self
._texts
[sl
:sl
+ cn
]
128 ntexts
+= self
._texts
[ooff
:sl
] + diffs
[i
:i
+ cn2
]
132 ndiffs
+= ["a%d %d\n" % (sl
+ adjust
, cn
)] + \
133 self
._texts
[sl
:sl
+ cn
]
134 ntexts
+= self
._texts
[ooff
:sl
]
137 else: # "a" - Add command
138 if sl
< ooff
: # Also catches same place
139 raise MalformedDeltaException('Insertion before last edit')
140 if sl
> len(self
._texts
):
141 raise MalformedDeltaException('Insertion past file end')
142 ndiffs
+= ["d%d %d\n" % (sl
+ 1 + adjust
, cn
)]
143 ntexts
+= self
._texts
[ooff
:sl
] + diffs
[i
:i
+ cn
]
147 self
._texts
= ntexts
+ self
._texts
[ooff
:]
148 return "".join(ndiffs
)