|
86
|
1 # Copyright (C) 2008 Canonical Ltd |
|
|
2 # |
|
|
3 # This program is free software; you can redistribute it and/or modify |
|
|
4 # it under the terms of the GNU General Public License as published by |
|
|
5 # the Free Software Foundation; either version 2 of the License, or |
|
|
6 # (at your option) any later version. |
|
|
7 # |
|
|
8 # This program is distributed in the hope that it will be useful, |
|
|
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
11 # GNU General Public License for more details. |
|
|
12 # |
|
|
13 # You should have received a copy of the GNU General Public License |
|
|
14 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
15 |
|
|
16 """Processor for fast-import commands. |
|
|
17 |
|
|
18 This module provides the skeleton of a fast-import backend. |
|
|
19 To import from a fast-import stream to your version-control system: |
|
|
20 |
|
|
21 - derive a class from the abstract ImportProcessor class and |
|
|
22 implement the *_helper methods. |
|
|
23 |
|
|
24 - parse a fast-import stream into a sequence of commands, for example |
|
|
25 using the helpers from the parser module. |
|
|
26 |
|
|
27 - pass that command sequence to the process method of your processor. |
|
|
28 |
|
|
29 See git-fast-import.1 for the meaning of each command and the |
|
|
30 processors package for examples. |
|
|
31 """ |
|
|
32 import sys |
|
|
33 import time |
|
|
34 |
|
|
35 from fastimport import errors |
|
|
36 from fastimport.helpers import newobject as object |
|
|
37 |
|
|
38 |
|
|
39 class ImportProcessor(object): |
|
|
40 """Base class for fast-import stream processors. |
|
|
41 |
|
|
42 Subclasses should override the pre_*, post_* and *_handler |
|
|
43 methods as appropriate. |
|
|
44 """ |
|
|
45 |
|
|
46 known_params = [] |
|
|
47 |
|
|
48 def __init__(self, params=None, verbose=False, outf=None): |
|
|
49 if outf is None: |
|
|
50 self.outf = sys.stdout |
|
|
51 else: |
|
|
52 self.outf = outf |
|
|
53 self.verbose = verbose |
|
|
54 if params is None: |
|
|
55 self.params = {} |
|
|
56 else: |
|
|
57 self.params = params |
|
|
58 self.validate_parameters() |
|
|
59 |
|
|
60 # Handlers can set this to request exiting cleanly without |
|
|
61 # iterating through the remaining commands |
|
|
62 self.finished = False |
|
|
63 |
|
|
64 def validate_parameters(self): |
|
|
65 """Validate that the parameters are correctly specified.""" |
|
|
66 for p in self.params: |
|
|
67 if p not in self.known_params: |
|
|
68 raise errors.UnknownParameter(p, self.known_params) |
|
|
69 |
|
|
70 def process(self, command_iter): |
|
|
71 """Import data into Bazaar by processing a stream of commands. |
|
|
72 |
|
|
73 :param command_iter: an iterator providing commands |
|
|
74 """ |
|
|
75 self._process(command_iter) |
|
|
76 |
|
|
77 def _process(self, command_iter): |
|
|
78 self.pre_process() |
|
|
79 for cmd in command_iter(): |
|
|
80 try: |
|
|
81 name = (cmd.name + b'_handler').decode('utf8') |
|
|
82 handler = getattr(self.__class__, name) |
|
|
83 except KeyError: |
|
|
84 raise errors.MissingHandler(cmd.name) |
|
|
85 else: |
|
|
86 self.pre_handler(cmd) |
|
|
87 handler(self, cmd) |
|
|
88 self.post_handler(cmd) |
|
|
89 if self.finished: |
|
|
90 break |
|
|
91 self.post_process() |
|
|
92 |
|
|
93 def warning(self, msg, *args): |
|
|
94 """Output a warning but timestamp it.""" |
|
|
95 pass |
|
|
96 |
|
|
97 def debug(self, mgs, *args): |
|
|
98 """Output a debug message.""" |
|
|
99 pass |
|
|
100 |
|
|
101 def _time_of_day(self): |
|
|
102 """Time of day as a string.""" |
|
|
103 # Note: this is a separate method so tests can patch in a fixed value |
|
|
104 return time.strftime("%H:%M:%S") |
|
|
105 |
|
|
106 def pre_process(self): |
|
|
107 """Hook for logic at start of processing.""" |
|
|
108 pass |
|
|
109 |
|
|
110 def post_process(self): |
|
|
111 """Hook for logic at end of processing.""" |
|
|
112 pass |
|
|
113 |
|
|
114 def pre_handler(self, cmd): |
|
|
115 """Hook for logic before each handler starts.""" |
|
|
116 pass |
|
|
117 |
|
|
118 def post_handler(self, cmd): |
|
|
119 """Hook for logic after each handler finishes.""" |
|
|
120 pass |
|
|
121 |
|
|
122 def progress_handler(self, cmd): |
|
|
123 """Process a ProgressCommand.""" |
|
|
124 raise NotImplementedError(self.progress_handler) |
|
|
125 |
|
|
126 def blob_handler(self, cmd): |
|
|
127 """Process a BlobCommand.""" |
|
|
128 raise NotImplementedError(self.blob_handler) |
|
|
129 |
|
|
130 def checkpoint_handler(self, cmd): |
|
|
131 """Process a CheckpointCommand.""" |
|
|
132 raise NotImplementedError(self.checkpoint_handler) |
|
|
133 |
|
|
134 def commit_handler(self, cmd): |
|
|
135 """Process a CommitCommand.""" |
|
|
136 raise NotImplementedError(self.commit_handler) |
|
|
137 |
|
|
138 def reset_handler(self, cmd): |
|
|
139 """Process a ResetCommand.""" |
|
|
140 raise NotImplementedError(self.reset_handler) |
|
|
141 |
|
|
142 def tag_handler(self, cmd): |
|
|
143 """Process a TagCommand.""" |
|
|
144 raise NotImplementedError(self.tag_handler) |
|
|
145 |
|
|
146 def feature_handler(self, cmd): |
|
|
147 """Process a FeatureCommand.""" |
|
|
148 raise NotImplementedError(self.feature_handler) |
|
|
149 |
|
|
150 |
|
|
151 class CommitHandler(object): |
|
|
152 """Base class for commit handling. |
|
|
153 |
|
|
154 Subclasses should override the pre_*, post_* and *_handler |
|
|
155 methods as appropriate. |
|
|
156 """ |
|
|
157 |
|
|
158 def __init__(self, command): |
|
|
159 self.command = command |
|
|
160 |
|
|
161 def process(self): |
|
|
162 self.pre_process_files() |
|
|
163 for fc in self.command.iter_files(): |
|
|
164 try: |
|
|
165 name = (fc.name[4:] + b'_handler').decode('utf8') |
|
|
166 handler = getattr(self.__class__, name) |
|
|
167 except KeyError: |
|
|
168 raise errors.MissingHandler(fc.name) |
|
|
169 else: |
|
|
170 handler(self, fc) |
|
|
171 self.post_process_files() |
|
|
172 |
|
|
173 def warning(self, msg, *args): |
|
|
174 """Output a warning but add context.""" |
|
|
175 pass |
|
|
176 |
|
|
177 def pre_process_files(self): |
|
|
178 """Prepare for committing.""" |
|
|
179 pass |
|
|
180 |
|
|
181 def post_process_files(self): |
|
|
182 """Save the revision.""" |
|
|
183 pass |
|
|
184 |
|
|
185 def modify_handler(self, filecmd): |
|
|
186 """Handle a filemodify command.""" |
|
|
187 raise NotImplementedError(self.modify_handler) |
|
|
188 |
|
|
189 def delete_handler(self, filecmd): |
|
|
190 """Handle a filedelete command.""" |
|
|
191 raise NotImplementedError(self.delete_handler) |
|
|
192 |
|
|
193 def copy_handler(self, filecmd): |
|
|
194 """Handle a filecopy command.""" |
|
|
195 raise NotImplementedError(self.copy_handler) |
|
|
196 |
|
|
197 def rename_handler(self, filecmd): |
|
|
198 """Handle a filerename command.""" |
|
|
199 raise NotImplementedError(self.rename_handler) |
|
|
200 |
|
|
201 def deleteall_handler(self, filecmd): |
|
|
202 """Handle a filedeleteall command.""" |
|
|
203 raise NotImplementedError(self.deleteall_handler) |