~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to patch.py

  • Committer: Aaron Bentley
  • Date: 2006-11-23 18:06:43 UTC
  • Revision ID: abentley@panoramicfeedback.com-20061123180643-6ry3xqozdbn4cfhy
Update docs

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
#    You should have received a copy of the GNU General Public License
15
15
#    along with this program; if not, write to the Free Software
16
16
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
 
import sys
 
17
import sys, os
18
18
from subprocess import Popen, PIPE
19
 
 
20
 
from bzrlib import urlutils
21
19
from bzrlib.transport import get_transport
 
20
from urlparse import urlsplit, urlunsplit
22
21
from bzrlib.workingtree import WorkingTree
23
22
import bzrlib.add
24
 
 
25
 
def patch(tree, location, strip, quiet=False):
 
23
import bzrlib.transform
 
24
import bzrlib.branch
 
25
import bzrlib.bzrdir
 
26
 
 
27
class BzrPatchProc:
 
28
    """This class process the bzr patch
 
29
 
 
30
        TODO:
 
31
          error handling
 
32
          execute attribute support
 
33
    """
 
34
    def __init__(self, tt, tree, bzrdir):
 
35
        self.link = None
 
36
        self.changes = []
 
37
        self.mode = None
 
38
        self.command = None
 
39
        self.target_id = None
 
40
        self.tt = tt
 
41
        self.tree = tree
 
42
        self.skipchange = True
 
43
 
 
44
        self.bzrdir = bzrdir
 
45
        self.branch =  self.bzrdir.open_branch( )
 
46
        self.repo = self.bzrdir.find_repository( )
 
47
        h = self.branch.revision_history( )
 
48
        self.inv = self.repo.get_inventory(h[-1])
 
49
 
 
50
 
 
51
    def get_transid(self, path):
 
52
        return self.tt.trans_id_tree_path(path)
 
53
 
 
54
 
 
55
    def _extractname(self, s):
 
56
        x = s[0]
 
57
        i = 1
 
58
        ls = len(s)
 
59
 
 
60
        while i < ls:
 
61
            if s[i] == '\\':
 
62
                assert(i+1 < ls )
 
63
                i += 2
 
64
                continue
 
65
 
 
66
            if s[i] == x:
 
67
                return s[1:i],i+1
 
68
 
 
69
            i += 1
 
70
 
 
71
        assert(False)
 
72
 
 
73
 
 
74
    def extractname(self, s):
 
75
        space=-1
 
76
        for i in range(0,3):
 
77
            space = s.find(" ",space+1)    # find the 2nd space
 
78
        assert(space>=0)
 
79
        return self._extractname(s[space+1:])[0]
 
80
 
 
81
 
 
82
    def extractnames(self, s):
 
83
        space=-1
 
84
        for i in range(0,3):
 
85
            space = s.find(" ",space+1)    # find the 2nd space
 
86
        assert(space>=0)
 
87
        s=s[space+1:]
 
88
        source, pos = self._extractname(s)
 
89
        assert( pos +4 < len(s) )
 
90
        dest,  dummy = self._extractname(s[pos+4:])
 
91
 
 
92
        return source,dest
 
93
 
 
94
 
 
95
    def flush(self):
 
96
        self.process( )
 
97
 
 
98
 
 
99
    def do_patch(self, changes, targetid):
 
100
        # apply change to target_id
 
101
        if 1:
 
102
            cmd = ['patch', '-o', self.tt._limbo_name(targetid),
 
103
                '--directory', self.branch.base, '-p1']
 
104
            #sys.stdout.write("# %r\n"%cmd)
 
105
            child_proc = Popen(cmd, stdin=PIPE)
 
106
            for line in changes:
 
107
                #sys.stdout.write("# %s\n"%line)
 
108
                child_proc.stdin.write(line+'\n')
 
109
            child_proc.stdin.close()
 
110
            r = child_proc.wait()
 
111
 
 
112
        self.changes = []
 
113
 
 
114
    def process(self, cmd = None):
 
115
 
 
116
        while cmd and len(cmd) and cmd[-1] in "\n\r":
 
117
            cmd = cmd[:-1]
 
118
 
 
119
        if ( cmd == None or cmd.startswith("===") ) and self.changes:
 
120
            assert(self.target_id)
 
121
 
 
122
            #print "********** mode=",self.mode
 
123
 
 
124
            if self.mode == "mv" or self.mode == "mod":
 
125
                self.tt.delete_contents(self.target_id)
 
126
 
 
127
            if self.mode == "add" or self.mode == "mod" or self.mode == "mv":
 
128
                self.tt.create_file([],self.target_id)
 
129
                self.do_patch(self.changes, self.target_id)
 
130
 
 
131
            self.target_id = None
 
132
            self.changes = []
 
133
            self.mode = None
 
134
 
 
135
        if cmd == None: return
 
136
        if not cmd.startswith("==="):
 
137
            if self.skipchange: return
 
138
            assert(self.target_id)
 
139
            assert(self.mode)
 
140
            self.changes.append(cmd)
 
141
            return
 
142
 
 
143
        # thr target_id is set during a rename; but there is possibility
 
144
        # that after a rename, is not to do a change
 
145
        self.target_id = None
 
146
        # skip change
 
147
        self.skipchange = False
 
148
        self.mode = None
 
149
 
 
150
        if ( cmd.startswith("=== removed file") or
 
151
             cmd.startswith("=== removed directory") or
 
152
             cmd.startswith("=== removed symlink") ):
 
153
 
 
154
            target = self.extractname(cmd)[2:]
 
155
            tid = self.get_transid(target)
 
156
            self.tt.delete_versioned(tid)
 
157
            print "removing '%s'"%target
 
158
            self.skipchange = True
 
159
            self.mode = "rm"
 
160
 
 
161
        elif cmd.startswith("=== added file"):
 
162
            
 
163
            target = self.extractname(cmd)[2:]
 
164
            self.target_id = self.get_transid(target)
 
165
            #self.tt.create_file([],self.target_id)
 
166
            print "adding '%s'"%target
 
167
            self.mode = "add"
 
168
 
 
169
        elif cmd.startswith("=== modified file"):
 
170
            target = self.extractname(cmd)[2:]
 
171
            self.target_id = self.get_transid(target)
 
172
            self.mode = "mod"
 
173
            print "patching '%s'"%target
 
174
 
 
175
        elif cmd.startswith("=== added directory"):
 
176
            target = self.extractname(cmd)[2:]
 
177
            target_id = self.get_transid(target)
 
178
            self.tt.create_directory(target_id)
 
179
            print "adding directory '%s'"%target
 
180
 
 
181
        elif cmd.startswith("=== added symlink"):
 
182
            assert(not self.link)
 
183
            self.link = self.extractname(cmd)[2:]
 
184
 
 
185
        elif cmd.startswith("=== target is"):
 
186
            assert(self.link)
 
187
            target = self.extractname(cmd)
 
188
            link_id = self.get_transid(self.link)
 
189
            
 
190
            print "symlinking '%s' => '%s'\n"%(target, link_id)
 
191
            self.tt.create_symlink(target, link_id)
 
192
            self.link = None
 
193
 
 
194
        elif ( cmd.startswith("=== renamed symlink") or
 
195
               cmd.startswith("=== renamed file") or
 
196
               cmd.startswith("=== renamed directory") ):
 
197
 
 
198
            source,dest = self.extractnames(cmd)
 
199
            source = source[2:]
 
200
            dest = dest[2:]
 
201
            sourceid = self.get_transid(source)
 
202
            pdir,pname = os.path.split(dest)
 
203
            pdir_id = self.get_transid(pdir)
 
204
            self.tt.adjust_path(pname, pdir_id, sourceid)
 
205
 
 
206
            if cmd.startswith("=== renamed file"):
 
207
                self.target_id = sourceid
 
208
                self.mode = "mv"
 
209
 
 
210
            print "renaming '%s' => '%s'"%(source,dest)
 
211
 
 
212
 
 
213
        else:
 
214
            sys.stderr.write("Unsupported tag: '%s'\n"%cmd)
 
215
 
 
216
    def apply(self):
 
217
        self.tt.apply( )
 
218
 
 
219
 
 
220
 
 
221
def do_patch(p):
 
222
 
 
223
    tree = WorkingTree.open_containing(u'.')[0]
 
224
    tt = bzrlib.transform.TreeTransform(tree)
 
225
    bzrdir = bzrlib.bzrdir.BzrDir.open(".")
 
226
    bzr_tags_proc = BzrPatchProc(tt, tree, bzrdir)
 
227
 
 
228
    for l in p:
 
229
        # we have to remove the \n\r
 
230
        bzr_tags_proc.process(l)
 
231
 
 
232
    bzr_tags_proc.flush( )
 
233
    bzr_tags_proc.apply( )
 
234
 
 
235
def patch(tree, location, strip, legacy):
26
236
    """Apply a patch to a branch, using patch(1).  URLs may be used."""
27
237
    my_file = None
28
238
    if location is None:
29
239
        my_file = sys.stdin
30
240
    else:
31
 
        location = urlutils.normalize_url(location)
32
 
        dirname, basename = urlutils.split(location)
33
 
        my_file = get_transport(dirname).get(basename)
 
241
        for prefix in ('http://', 'sftp://', 'file://'):
 
242
            if not location.startswith(prefix):
 
243
                continue
 
244
            (scheme, loc, path, query, fragment) = urlsplit(location)
 
245
            loc_start = urlunsplit((scheme, loc, '/', '', ''))
 
246
            my_file = get_transport(loc_start).get(path[1:])
 
247
        if my_file is None:
 
248
            my_file = file(location, 'rb')
34
249
    cmd = ['patch', '--directory', tree.basedir, '--strip', str(strip)]
35
 
    if quiet:
36
 
        cmd.append('--quiet')
37
250
    r = 0
38
 
    child_proc = Popen(cmd, stdin=PIPE)
39
 
    for line in my_file:
40
 
        child_proc.stdin.write(line)
41
 
    child_proc.stdin.close()
42
 
    r = child_proc.wait()
 
251
    if legacy:
 
252
        child_proc = Popen(cmd, stdin=PIPE)
 
253
        for line in my_file:
 
254
            child_proc.stdin.write(line)
 
255
        child_proc.stdin.close()
 
256
        r = child_proc.wait()
 
257
    else:
 
258
 
 
259
        do_patch(sys.stdin.readlines( ))
43
260
    return r