Target:
Source:
Actions:
Time | Author | Commit | Description | |
---|---|---|---|---|
|
r1106:490ebeeb75af
|
|
||
|
r1107:6bc055e1504d
|
|
||
|
r1108:ebe0247cd154
|
|
@@ -1,1545 +1,1549 | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2014-2016 RhodeCode GmbH |
|
3 | # Copyright (C) 2014-2016 RhodeCode GmbH | |
4 | # |
|
4 | # | |
5 | # This program is free software: you can redistribute it and/or modify |
|
5 | # This program is free software: you can redistribute it and/or modify | |
6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
7 | # (only), as published by the Free Software Foundation. |
|
7 | # (only), as published by the Free Software Foundation. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU Affero General Public License |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | # |
|
16 | # | |
17 | # This program is dual-licensed. If you wish to learn more about the |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
20 |
|
20 | |||
21 | """ |
|
21 | """ | |
22 | Base module for all VCS systems |
|
22 | Base module for all VCS systems | |
23 | """ |
|
23 | """ | |
24 |
|
24 | |||
25 | import collections |
|
25 | import collections | |
26 | import datetime |
|
26 | import datetime | |
27 | import itertools |
|
27 | import itertools | |
28 | import logging |
|
28 | import logging | |
29 | import os |
|
29 | import os | |
30 | import time |
|
30 | import time | |
31 | import warnings |
|
31 | import warnings | |
32 |
|
32 | |||
33 | from zope.cachedescriptors.property import Lazy as LazyProperty |
|
33 | from zope.cachedescriptors.property import Lazy as LazyProperty | |
34 |
|
34 | |||
35 | from rhodecode.lib.utils2 import safe_str, safe_unicode |
|
35 | from rhodecode.lib.utils2 import safe_str, safe_unicode | |
36 | from rhodecode.lib.vcs import connection |
|
36 | from rhodecode.lib.vcs import connection | |
37 | from rhodecode.lib.vcs.utils import author_name, author_email |
|
37 | from rhodecode.lib.vcs.utils import author_name, author_email | |
38 | from rhodecode.lib.vcs.conf import settings |
|
38 | from rhodecode.lib.vcs.conf import settings | |
39 | from rhodecode.lib.vcs.exceptions import ( |
|
39 | from rhodecode.lib.vcs.exceptions import ( | |
40 | CommitError, EmptyRepositoryError, NodeAlreadyAddedError, |
|
40 | CommitError, EmptyRepositoryError, NodeAlreadyAddedError, | |
41 | NodeAlreadyChangedError, NodeAlreadyExistsError, NodeAlreadyRemovedError, |
|
41 | NodeAlreadyChangedError, NodeAlreadyExistsError, NodeAlreadyRemovedError, | |
42 | NodeDoesNotExistError, NodeNotChangedError, VCSError, |
|
42 | NodeDoesNotExistError, NodeNotChangedError, VCSError, | |
43 | ImproperArchiveTypeError, BranchDoesNotExistError, CommitDoesNotExistError, |
|
43 | ImproperArchiveTypeError, BranchDoesNotExistError, CommitDoesNotExistError, | |
44 | RepositoryError) |
|
44 | RepositoryError) | |
45 |
|
45 | |||
46 |
|
46 | |||
47 | log = logging.getLogger(__name__) |
|
47 | log = logging.getLogger(__name__) | |
48 |
|
48 | |||
49 |
|
49 | |||
50 | FILEMODE_DEFAULT = 0100644 |
|
50 | FILEMODE_DEFAULT = 0100644 | |
51 | FILEMODE_EXECUTABLE = 0100755 |
|
51 | FILEMODE_EXECUTABLE = 0100755 | |
52 |
|
52 | |||
53 | Reference = collections.namedtuple('Reference', ('type', 'name', 'commit_id')) |
|
53 | Reference = collections.namedtuple('Reference', ('type', 'name', 'commit_id')) | |
54 | MergeResponse = collections.namedtuple( |
|
54 | MergeResponse = collections.namedtuple( | |
55 | 'MergeResponse', |
|
55 | 'MergeResponse', | |
56 | ('possible', 'executed', 'merge_ref', 'failure_reason')) |
|
56 | ('possible', 'executed', 'merge_ref', 'failure_reason')) | |
57 |
|
57 | |||
58 |
|
58 | |||
59 | class MergeFailureReason(object): |
|
59 | class MergeFailureReason(object): | |
60 | """ |
|
60 | """ | |
61 | Enumeration with all the reasons why the server side merge could fail. |
|
61 | Enumeration with all the reasons why the server side merge could fail. | |
62 |
|
62 | |||
63 | DO NOT change the number of the reasons, as they may be stored in the |
|
63 | DO NOT change the number of the reasons, as they may be stored in the | |
64 | database. |
|
64 | database. | |
65 |
|
65 | |||
66 | Changing the name of a reason is acceptable and encouraged to deprecate old |
|
66 | Changing the name of a reason is acceptable and encouraged to deprecate old | |
67 | reasons. |
|
67 | reasons. | |
68 | """ |
|
68 | """ | |
69 |
|
69 | |||
70 | # Everything went well. |
|
70 | # Everything went well. | |
71 | NONE = 0 |
|
71 | NONE = 0 | |
72 |
|
72 | |||
73 | # An unexpected exception was raised. Check the logs for more details. |
|
73 | # An unexpected exception was raised. Check the logs for more details. | |
74 | UNKNOWN = 1 |
|
74 | UNKNOWN = 1 | |
75 |
|
75 | |||
76 | # The merge was not successful, there are conflicts. |
|
76 | # The merge was not successful, there are conflicts. | |
77 | MERGE_FAILED = 2 |
|
77 | MERGE_FAILED = 2 | |
78 |
|
78 | |||
79 | # The merge succeeded but we could not push it to the target repository. |
|
79 | # The merge succeeded but we could not push it to the target repository. | |
80 | PUSH_FAILED = 3 |
|
80 | PUSH_FAILED = 3 | |
81 |
|
81 | |||
82 | # The specified target is not a head in the target repository. |
|
82 | # The specified target is not a head in the target repository. | |
83 | TARGET_IS_NOT_HEAD = 4 |
|
83 | TARGET_IS_NOT_HEAD = 4 | |
84 |
|
84 | |||
85 | # The source repository contains more branches than the target. Pushing |
|
85 | # The source repository contains more branches than the target. Pushing | |
86 | # the merge will create additional branches in the target. |
|
86 | # the merge will create additional branches in the target. | |
87 | HG_SOURCE_HAS_MORE_BRANCHES = 5 |
|
87 | HG_SOURCE_HAS_MORE_BRANCHES = 5 | |
88 |
|
88 | |||
89 | # The target reference has multiple heads. That does not allow to correctly |
|
89 | # The target reference has multiple heads. That does not allow to correctly | |
90 | # identify the target location. This could only happen for mercurial |
|
90 | # identify the target location. This could only happen for mercurial | |
91 | # branches. |
|
91 | # branches. | |
92 | HG_TARGET_HAS_MULTIPLE_HEADS = 6 |
|
92 | HG_TARGET_HAS_MULTIPLE_HEADS = 6 | |
93 |
|
93 | |||
94 | # The target repository is locked |
|
94 | # The target repository is locked | |
95 | TARGET_IS_LOCKED = 7 |
|
95 | TARGET_IS_LOCKED = 7 | |
96 |
|
96 | |||
97 | # Deprecated, use MISSING_TARGET_REF or MISSING_SOURCE_REF instead. |
|
97 | # Deprecated, use MISSING_TARGET_REF or MISSING_SOURCE_REF instead. | |
98 | # A involved commit could not be found. |
|
98 | # A involved commit could not be found. | |
99 | _DEPRECATED_MISSING_COMMIT = 8 |
|
99 | _DEPRECATED_MISSING_COMMIT = 8 | |
100 |
|
100 | |||
101 | # The target repo reference is missing. |
|
101 | # The target repo reference is missing. | |
102 | MISSING_TARGET_REF = 9 |
|
102 | MISSING_TARGET_REF = 9 | |
103 |
|
103 | |||
104 | # The source repo reference is missing. |
|
104 | # The source repo reference is missing. | |
105 | MISSING_SOURCE_REF = 10 |
|
105 | MISSING_SOURCE_REF = 10 | |
106 |
|
106 | |||
|
107 | # The merge was not successful, there are conflicts related to sub | |||
|
108 | # repositories. | |||
|
109 | SUBREPO_MERGE_FAILED = 11 | |||
|
110 | ||||
107 |
|
111 | |||
108 | class UpdateFailureReason(object): |
|
112 | class UpdateFailureReason(object): | |
109 | """ |
|
113 | """ | |
110 | Enumeration with all the reasons why the pull request update could fail. |
|
114 | Enumeration with all the reasons why the pull request update could fail. | |
111 |
|
115 | |||
112 | DO NOT change the number of the reasons, as they may be stored in the |
|
116 | DO NOT change the number of the reasons, as they may be stored in the | |
113 | database. |
|
117 | database. | |
114 |
|
118 | |||
115 | Changing the name of a reason is acceptable and encouraged to deprecate old |
|
119 | Changing the name of a reason is acceptable and encouraged to deprecate old | |
116 | reasons. |
|
120 | reasons. | |
117 | """ |
|
121 | """ | |
118 |
|
122 | |||
119 | # Everything went well. |
|
123 | # Everything went well. | |
120 | NONE = 0 |
|
124 | NONE = 0 | |
121 |
|
125 | |||
122 | # An unexpected exception was raised. Check the logs for more details. |
|
126 | # An unexpected exception was raised. Check the logs for more details. | |
123 | UNKNOWN = 1 |
|
127 | UNKNOWN = 1 | |
124 |
|
128 | |||
125 | # The pull request is up to date. |
|
129 | # The pull request is up to date. | |
126 | NO_CHANGE = 2 |
|
130 | NO_CHANGE = 2 | |
127 |
|
131 | |||
128 | # The pull request has a reference type that is not supported for update. |
|
132 | # The pull request has a reference type that is not supported for update. | |
129 | WRONG_REF_TPYE = 3 |
|
133 | WRONG_REF_TPYE = 3 | |
130 |
|
134 | |||
131 | # Update failed because the target reference is missing. |
|
135 | # Update failed because the target reference is missing. | |
132 | MISSING_TARGET_REF = 4 |
|
136 | MISSING_TARGET_REF = 4 | |
133 |
|
137 | |||
134 | # Update failed because the source reference is missing. |
|
138 | # Update failed because the source reference is missing. | |
135 | MISSING_SOURCE_REF = 5 |
|
139 | MISSING_SOURCE_REF = 5 | |
136 |
|
140 | |||
137 |
|
141 | |||
138 | class BaseRepository(object): |
|
142 | class BaseRepository(object): | |
139 | """ |
|
143 | """ | |
140 | Base Repository for final backends |
|
144 | Base Repository for final backends | |
141 |
|
145 | |||
142 | .. attribute:: DEFAULT_BRANCH_NAME |
|
146 | .. attribute:: DEFAULT_BRANCH_NAME | |
143 |
|
147 | |||
144 | name of default branch (i.e. "trunk" for svn, "master" for git etc. |
|
148 | name of default branch (i.e. "trunk" for svn, "master" for git etc. | |
145 |
|
149 | |||
146 | .. attribute:: commit_ids |
|
150 | .. attribute:: commit_ids | |
147 |
|
151 | |||
148 | list of all available commit ids, in ascending order |
|
152 | list of all available commit ids, in ascending order | |
149 |
|
153 | |||
150 | .. attribute:: path |
|
154 | .. attribute:: path | |
151 |
|
155 | |||
152 | absolute path to the repository |
|
156 | absolute path to the repository | |
153 |
|
157 | |||
154 | .. attribute:: bookmarks |
|
158 | .. attribute:: bookmarks | |
155 |
|
159 | |||
156 | Mapping from name to :term:`Commit ID` of the bookmark. Empty in case |
|
160 | Mapping from name to :term:`Commit ID` of the bookmark. Empty in case | |
157 | there are no bookmarks or the backend implementation does not support |
|
161 | there are no bookmarks or the backend implementation does not support | |
158 | bookmarks. |
|
162 | bookmarks. | |
159 |
|
163 | |||
160 | .. attribute:: tags |
|
164 | .. attribute:: tags | |
161 |
|
165 | |||
162 | Mapping from name to :term:`Commit ID` of the tag. |
|
166 | Mapping from name to :term:`Commit ID` of the tag. | |
163 |
|
167 | |||
164 | """ |
|
168 | """ | |
165 |
|
169 | |||
166 | DEFAULT_BRANCH_NAME = None |
|
170 | DEFAULT_BRANCH_NAME = None | |
167 | DEFAULT_CONTACT = u"Unknown" |
|
171 | DEFAULT_CONTACT = u"Unknown" | |
168 | DEFAULT_DESCRIPTION = u"unknown" |
|
172 | DEFAULT_DESCRIPTION = u"unknown" | |
169 | EMPTY_COMMIT_ID = '0' * 40 |
|
173 | EMPTY_COMMIT_ID = '0' * 40 | |
170 |
|
174 | |||
171 | path = None |
|
175 | path = None | |
172 |
|
176 | |||
173 | def __init__(self, repo_path, config=None, create=False, **kwargs): |
|
177 | def __init__(self, repo_path, config=None, create=False, **kwargs): | |
174 | """ |
|
178 | """ | |
175 | Initializes repository. Raises RepositoryError if repository could |
|
179 | Initializes repository. Raises RepositoryError if repository could | |
176 | not be find at the given ``repo_path`` or directory at ``repo_path`` |
|
180 | not be find at the given ``repo_path`` or directory at ``repo_path`` | |
177 | exists and ``create`` is set to True. |
|
181 | exists and ``create`` is set to True. | |
178 |
|
182 | |||
179 | :param repo_path: local path of the repository |
|
183 | :param repo_path: local path of the repository | |
180 | :param config: repository configuration |
|
184 | :param config: repository configuration | |
181 | :param create=False: if set to True, would try to create repository. |
|
185 | :param create=False: if set to True, would try to create repository. | |
182 | :param src_url=None: if set, should be proper url from which repository |
|
186 | :param src_url=None: if set, should be proper url from which repository | |
183 | would be cloned; requires ``create`` parameter to be set to True - |
|
187 | would be cloned; requires ``create`` parameter to be set to True - | |
184 | raises RepositoryError if src_url is set and create evaluates to |
|
188 | raises RepositoryError if src_url is set and create evaluates to | |
185 | False |
|
189 | False | |
186 | """ |
|
190 | """ | |
187 | raise NotImplementedError |
|
191 | raise NotImplementedError | |
188 |
|
192 | |||
189 | def __repr__(self): |
|
193 | def __repr__(self): | |
190 | return '<%s at %s>' % (self.__class__.__name__, self.path) |
|
194 | return '<%s at %s>' % (self.__class__.__name__, self.path) | |
191 |
|
195 | |||
192 | def __len__(self): |
|
196 | def __len__(self): | |
193 | return self.count() |
|
197 | return self.count() | |
194 |
|
198 | |||
195 | def __eq__(self, other): |
|
199 | def __eq__(self, other): | |
196 | same_instance = isinstance(other, self.__class__) |
|
200 | same_instance = isinstance(other, self.__class__) | |
197 | return same_instance and other.path == self.path |
|
201 | return same_instance and other.path == self.path | |
198 |
|
202 | |||
199 | def __ne__(self, other): |
|
203 | def __ne__(self, other): | |
200 | return not self.__eq__(other) |
|
204 | return not self.__eq__(other) | |
201 |
|
205 | |||
202 | @LazyProperty |
|
206 | @LazyProperty | |
203 | def EMPTY_COMMIT(self): |
|
207 | def EMPTY_COMMIT(self): | |
204 | return EmptyCommit(self.EMPTY_COMMIT_ID) |
|
208 | return EmptyCommit(self.EMPTY_COMMIT_ID) | |
205 |
|
209 | |||
206 | @LazyProperty |
|
210 | @LazyProperty | |
207 | def alias(self): |
|
211 | def alias(self): | |
208 | for k, v in settings.BACKENDS.items(): |
|
212 | for k, v in settings.BACKENDS.items(): | |
209 | if v.split('.')[-1] == str(self.__class__.__name__): |
|
213 | if v.split('.')[-1] == str(self.__class__.__name__): | |
210 | return k |
|
214 | return k | |
211 |
|
215 | |||
212 | @LazyProperty |
|
216 | @LazyProperty | |
213 | def name(self): |
|
217 | def name(self): | |
214 | return safe_unicode(os.path.basename(self.path)) |
|
218 | return safe_unicode(os.path.basename(self.path)) | |
215 |
|
219 | |||
216 | @LazyProperty |
|
220 | @LazyProperty | |
217 | def description(self): |
|
221 | def description(self): | |
218 | raise NotImplementedError |
|
222 | raise NotImplementedError | |
219 |
|
223 | |||
220 | def refs(self): |
|
224 | def refs(self): | |
221 | """ |
|
225 | """ | |
222 | returns a `dict` with branches, bookmarks, tags, and closed_branches |
|
226 | returns a `dict` with branches, bookmarks, tags, and closed_branches | |
223 | for this repository |
|
227 | for this repository | |
224 | """ |
|
228 | """ | |
225 | raise NotImplementedError |
|
229 | raise NotImplementedError | |
226 |
|
230 | |||
227 | @LazyProperty |
|
231 | @LazyProperty | |
228 | def branches(self): |
|
232 | def branches(self): | |
229 | """ |
|
233 | """ | |
230 | A `dict` which maps branch names to commit ids. |
|
234 | A `dict` which maps branch names to commit ids. | |
231 | """ |
|
235 | """ | |
232 | raise NotImplementedError |
|
236 | raise NotImplementedError | |
233 |
|
237 | |||
234 | @LazyProperty |
|
238 | @LazyProperty | |
235 | def size(self): |
|
239 | def size(self): | |
236 | """ |
|
240 | """ | |
237 | Returns combined size in bytes for all repository files |
|
241 | Returns combined size in bytes for all repository files | |
238 | """ |
|
242 | """ | |
239 | tip = self.get_commit() |
|
243 | tip = self.get_commit() | |
240 | return tip.size |
|
244 | return tip.size | |
241 |
|
245 | |||
242 | def size_at_commit(self, commit_id): |
|
246 | def size_at_commit(self, commit_id): | |
243 | commit = self.get_commit(commit_id) |
|
247 | commit = self.get_commit(commit_id) | |
244 | return commit.size |
|
248 | return commit.size | |
245 |
|
249 | |||
246 | def is_empty(self): |
|
250 | def is_empty(self): | |
247 | return not bool(self.commit_ids) |
|
251 | return not bool(self.commit_ids) | |
248 |
|
252 | |||
249 | @staticmethod |
|
253 | @staticmethod | |
250 | def check_url(url, config): |
|
254 | def check_url(url, config): | |
251 | """ |
|
255 | """ | |
252 | Function will check given url and try to verify if it's a valid |
|
256 | Function will check given url and try to verify if it's a valid | |
253 | link. |
|
257 | link. | |
254 | """ |
|
258 | """ | |
255 | raise NotImplementedError |
|
259 | raise NotImplementedError | |
256 |
|
260 | |||
257 | @staticmethod |
|
261 | @staticmethod | |
258 | def is_valid_repository(path): |
|
262 | def is_valid_repository(path): | |
259 | """ |
|
263 | """ | |
260 | Check if given `path` contains a valid repository of this backend |
|
264 | Check if given `path` contains a valid repository of this backend | |
261 | """ |
|
265 | """ | |
262 | raise NotImplementedError |
|
266 | raise NotImplementedError | |
263 |
|
267 | |||
264 | # ========================================================================== |
|
268 | # ========================================================================== | |
265 | # COMMITS |
|
269 | # COMMITS | |
266 | # ========================================================================== |
|
270 | # ========================================================================== | |
267 |
|
271 | |||
268 | def get_commit(self, commit_id=None, commit_idx=None, pre_load=None): |
|
272 | def get_commit(self, commit_id=None, commit_idx=None, pre_load=None): | |
269 | """ |
|
273 | """ | |
270 | Returns instance of `BaseCommit` class. If `commit_id` and `commit_idx` |
|
274 | Returns instance of `BaseCommit` class. If `commit_id` and `commit_idx` | |
271 | are both None, most recent commit is returned. |
|
275 | are both None, most recent commit is returned. | |
272 |
|
276 | |||
273 | :param pre_load: Optional. List of commit attributes to load. |
|
277 | :param pre_load: Optional. List of commit attributes to load. | |
274 |
|
278 | |||
275 | :raises ``EmptyRepositoryError``: if there are no commits |
|
279 | :raises ``EmptyRepositoryError``: if there are no commits | |
276 | """ |
|
280 | """ | |
277 | raise NotImplementedError |
|
281 | raise NotImplementedError | |
278 |
|
282 | |||
279 | def __iter__(self): |
|
283 | def __iter__(self): | |
280 | for commit_id in self.commit_ids: |
|
284 | for commit_id in self.commit_ids: | |
281 | yield self.get_commit(commit_id=commit_id) |
|
285 | yield self.get_commit(commit_id=commit_id) | |
282 |
|
286 | |||
283 | def get_commits( |
|
287 | def get_commits( | |
284 | self, start_id=None, end_id=None, start_date=None, end_date=None, |
|
288 | self, start_id=None, end_id=None, start_date=None, end_date=None, | |
285 | branch_name=None, pre_load=None): |
|
289 | branch_name=None, pre_load=None): | |
286 | """ |
|
290 | """ | |
287 | Returns iterator of `BaseCommit` objects from start to end |
|
291 | Returns iterator of `BaseCommit` objects from start to end | |
288 | not inclusive. This should behave just like a list, ie. end is not |
|
292 | not inclusive. This should behave just like a list, ie. end is not | |
289 | inclusive. |
|
293 | inclusive. | |
290 |
|
294 | |||
291 | :param start_id: None or str, must be a valid commit id |
|
295 | :param start_id: None or str, must be a valid commit id | |
292 | :param end_id: None or str, must be a valid commit id |
|
296 | :param end_id: None or str, must be a valid commit id | |
293 | :param start_date: |
|
297 | :param start_date: | |
294 | :param end_date: |
|
298 | :param end_date: | |
295 | :param branch_name: |
|
299 | :param branch_name: | |
296 | :param pre_load: |
|
300 | :param pre_load: | |
297 | """ |
|
301 | """ | |
298 | raise NotImplementedError |
|
302 | raise NotImplementedError | |
299 |
|
303 | |||
300 | def __getitem__(self, key): |
|
304 | def __getitem__(self, key): | |
301 | """ |
|
305 | """ | |
302 | Allows index based access to the commit objects of this repository. |
|
306 | Allows index based access to the commit objects of this repository. | |
303 | """ |
|
307 | """ | |
304 | pre_load = ["author", "branch", "date", "message", "parents"] |
|
308 | pre_load = ["author", "branch", "date", "message", "parents"] | |
305 | if isinstance(key, slice): |
|
309 | if isinstance(key, slice): | |
306 | return self._get_range(key, pre_load) |
|
310 | return self._get_range(key, pre_load) | |
307 | return self.get_commit(commit_idx=key, pre_load=pre_load) |
|
311 | return self.get_commit(commit_idx=key, pre_load=pre_load) | |
308 |
|
312 | |||
309 | def _get_range(self, slice_obj, pre_load): |
|
313 | def _get_range(self, slice_obj, pre_load): | |
310 | for commit_id in self.commit_ids.__getitem__(slice_obj): |
|
314 | for commit_id in self.commit_ids.__getitem__(slice_obj): | |
311 | yield self.get_commit(commit_id=commit_id, pre_load=pre_load) |
|
315 | yield self.get_commit(commit_id=commit_id, pre_load=pre_load) | |
312 |
|
316 | |||
313 | def count(self): |
|
317 | def count(self): | |
314 | return len(self.commit_ids) |
|
318 | return len(self.commit_ids) | |
315 |
|
319 | |||
316 | def tag(self, name, user, commit_id=None, message=None, date=None, **opts): |
|
320 | def tag(self, name, user, commit_id=None, message=None, date=None, **opts): | |
317 | """ |
|
321 | """ | |
318 | Creates and returns a tag for the given ``commit_id``. |
|
322 | Creates and returns a tag for the given ``commit_id``. | |
319 |
|
323 | |||
320 | :param name: name for new tag |
|
324 | :param name: name for new tag | |
321 | :param user: full username, i.e.: "Joe Doe <joe.doe@example.com>" |
|
325 | :param user: full username, i.e.: "Joe Doe <joe.doe@example.com>" | |
322 | :param commit_id: commit id for which new tag would be created |
|
326 | :param commit_id: commit id for which new tag would be created | |
323 | :param message: message of the tag's commit |
|
327 | :param message: message of the tag's commit | |
324 | :param date: date of tag's commit |
|
328 | :param date: date of tag's commit | |
325 |
|
329 | |||
326 | :raises TagAlreadyExistError: if tag with same name already exists |
|
330 | :raises TagAlreadyExistError: if tag with same name already exists | |
327 | """ |
|
331 | """ | |
328 | raise NotImplementedError |
|
332 | raise NotImplementedError | |
329 |
|
333 | |||
330 | def remove_tag(self, name, user, message=None, date=None): |
|
334 | def remove_tag(self, name, user, message=None, date=None): | |
331 | """ |
|
335 | """ | |
332 | Removes tag with the given ``name``. |
|
336 | Removes tag with the given ``name``. | |
333 |
|
337 | |||
334 | :param name: name of the tag to be removed |
|
338 | :param name: name of the tag to be removed | |
335 | :param user: full username, i.e.: "Joe Doe <joe.doe@example.com>" |
|
339 | :param user: full username, i.e.: "Joe Doe <joe.doe@example.com>" | |
336 | :param message: message of the tag's removal commit |
|
340 | :param message: message of the tag's removal commit | |
337 | :param date: date of tag's removal commit |
|
341 | :param date: date of tag's removal commit | |
338 |
|
342 | |||
339 | :raises TagDoesNotExistError: if tag with given name does not exists |
|
343 | :raises TagDoesNotExistError: if tag with given name does not exists | |
340 | """ |
|
344 | """ | |
341 | raise NotImplementedError |
|
345 | raise NotImplementedError | |
342 |
|
346 | |||
343 | def get_diff( |
|
347 | def get_diff( | |
344 | self, commit1, commit2, path=None, ignore_whitespace=False, |
|
348 | self, commit1, commit2, path=None, ignore_whitespace=False, | |
345 | context=3, path1=None): |
|
349 | context=3, path1=None): | |
346 | """ |
|
350 | """ | |
347 | Returns (git like) *diff*, as plain text. Shows changes introduced by |
|
351 | Returns (git like) *diff*, as plain text. Shows changes introduced by | |
348 | `commit2` since `commit1`. |
|
352 | `commit2` since `commit1`. | |
349 |
|
353 | |||
350 | :param commit1: Entry point from which diff is shown. Can be |
|
354 | :param commit1: Entry point from which diff is shown. Can be | |
351 | ``self.EMPTY_COMMIT`` - in this case, patch showing all |
|
355 | ``self.EMPTY_COMMIT`` - in this case, patch showing all | |
352 | the changes since empty state of the repository until `commit2` |
|
356 | the changes since empty state of the repository until `commit2` | |
353 | :param commit2: Until which commit changes should be shown. |
|
357 | :param commit2: Until which commit changes should be shown. | |
354 | :param path: Can be set to a path of a file to create a diff of that |
|
358 | :param path: Can be set to a path of a file to create a diff of that | |
355 | file. If `path1` is also set, this value is only associated to |
|
359 | file. If `path1` is also set, this value is only associated to | |
356 | `commit2`. |
|
360 | `commit2`. | |
357 | :param ignore_whitespace: If set to ``True``, would not show whitespace |
|
361 | :param ignore_whitespace: If set to ``True``, would not show whitespace | |
358 | changes. Defaults to ``False``. |
|
362 | changes. Defaults to ``False``. | |
359 | :param context: How many lines before/after changed lines should be |
|
363 | :param context: How many lines before/after changed lines should be | |
360 | shown. Defaults to ``3``. |
|
364 | shown. Defaults to ``3``. | |
361 | :param path1: Can be set to a path to associate with `commit1`. This |
|
365 | :param path1: Can be set to a path to associate with `commit1`. This | |
362 | parameter works only for backends which support diff generation for |
|
366 | parameter works only for backends which support diff generation for | |
363 | different paths. Other backends will raise a `ValueError` if `path1` |
|
367 | different paths. Other backends will raise a `ValueError` if `path1` | |
364 | is set and has a different value than `path`. |
|
368 | is set and has a different value than `path`. | |
365 | """ |
|
369 | """ | |
366 | raise NotImplementedError |
|
370 | raise NotImplementedError | |
367 |
|
371 | |||
368 | def strip(self, commit_id, branch=None): |
|
372 | def strip(self, commit_id, branch=None): | |
369 | """ |
|
373 | """ | |
370 | Strip given commit_id from the repository |
|
374 | Strip given commit_id from the repository | |
371 | """ |
|
375 | """ | |
372 | raise NotImplementedError |
|
376 | raise NotImplementedError | |
373 |
|
377 | |||
374 | def get_common_ancestor(self, commit_id1, commit_id2, repo2): |
|
378 | def get_common_ancestor(self, commit_id1, commit_id2, repo2): | |
375 | """ |
|
379 | """ | |
376 | Return a latest common ancestor commit if one exists for this repo |
|
380 | Return a latest common ancestor commit if one exists for this repo | |
377 | `commit_id1` vs `commit_id2` from `repo2`. |
|
381 | `commit_id1` vs `commit_id2` from `repo2`. | |
378 |
|
382 | |||
379 | :param commit_id1: Commit it from this repository to use as a |
|
383 | :param commit_id1: Commit it from this repository to use as a | |
380 | target for the comparison. |
|
384 | target for the comparison. | |
381 | :param commit_id2: Source commit id to use for comparison. |
|
385 | :param commit_id2: Source commit id to use for comparison. | |
382 | :param repo2: Source repository to use for comparison. |
|
386 | :param repo2: Source repository to use for comparison. | |
383 | """ |
|
387 | """ | |
384 | raise NotImplementedError |
|
388 | raise NotImplementedError | |
385 |
|
389 | |||
386 | def compare(self, commit_id1, commit_id2, repo2, merge, pre_load=None): |
|
390 | def compare(self, commit_id1, commit_id2, repo2, merge, pre_load=None): | |
387 | """ |
|
391 | """ | |
388 | Compare this repository's revision `commit_id1` with `commit_id2`. |
|
392 | Compare this repository's revision `commit_id1` with `commit_id2`. | |
389 |
|
393 | |||
390 | Returns a tuple(commits, ancestor) that would be merged from |
|
394 | Returns a tuple(commits, ancestor) that would be merged from | |
391 | `commit_id2`. Doing a normal compare (``merge=False``), ``None`` |
|
395 | `commit_id2`. Doing a normal compare (``merge=False``), ``None`` | |
392 | will be returned as ancestor. |
|
396 | will be returned as ancestor. | |
393 |
|
397 | |||
394 | :param commit_id1: Commit it from this repository to use as a |
|
398 | :param commit_id1: Commit it from this repository to use as a | |
395 | target for the comparison. |
|
399 | target for the comparison. | |
396 | :param commit_id2: Source commit id to use for comparison. |
|
400 | :param commit_id2: Source commit id to use for comparison. | |
397 | :param repo2: Source repository to use for comparison. |
|
401 | :param repo2: Source repository to use for comparison. | |
398 | :param merge: If set to ``True`` will do a merge compare which also |
|
402 | :param merge: If set to ``True`` will do a merge compare which also | |
399 | returns the common ancestor. |
|
403 | returns the common ancestor. | |
400 | :param pre_load: Optional. List of commit attributes to load. |
|
404 | :param pre_load: Optional. List of commit attributes to load. | |
401 | """ |
|
405 | """ | |
402 | raise NotImplementedError |
|
406 | raise NotImplementedError | |
403 |
|
407 | |||
404 | def merge(self, target_ref, source_repo, source_ref, workspace_id, |
|
408 | def merge(self, target_ref, source_repo, source_ref, workspace_id, | |
405 | user_name='', user_email='', message='', dry_run=False, |
|
409 | user_name='', user_email='', message='', dry_run=False, | |
406 | use_rebase=False): |
|
410 | use_rebase=False): | |
407 | """ |
|
411 | """ | |
408 | Merge the revisions specified in `source_ref` from `source_repo` |
|
412 | Merge the revisions specified in `source_ref` from `source_repo` | |
409 | onto the `target_ref` of this repository. |
|
413 | onto the `target_ref` of this repository. | |
410 |
|
414 | |||
411 | `source_ref` and `target_ref` are named tupls with the following |
|
415 | `source_ref` and `target_ref` are named tupls with the following | |
412 | fields `type`, `name` and `commit_id`. |
|
416 | fields `type`, `name` and `commit_id`. | |
413 |
|
417 | |||
414 | Returns a MergeResponse named tuple with the following fields |
|
418 | Returns a MergeResponse named tuple with the following fields | |
415 | 'possible', 'executed', 'source_commit', 'target_commit', |
|
419 | 'possible', 'executed', 'source_commit', 'target_commit', | |
416 | 'merge_commit'. |
|
420 | 'merge_commit'. | |
417 |
|
421 | |||
418 | :param target_ref: `target_ref` points to the commit on top of which |
|
422 | :param target_ref: `target_ref` points to the commit on top of which | |
419 | the `source_ref` should be merged. |
|
423 | the `source_ref` should be merged. | |
420 | :param source_repo: The repository that contains the commits to be |
|
424 | :param source_repo: The repository that contains the commits to be | |
421 | merged. |
|
425 | merged. | |
422 | :param source_ref: `source_ref` points to the topmost commit from |
|
426 | :param source_ref: `source_ref` points to the topmost commit from | |
423 | the `source_repo` which should be merged. |
|
427 | the `source_repo` which should be merged. | |
424 | :param workspace_id: `workspace_id` unique identifier. |
|
428 | :param workspace_id: `workspace_id` unique identifier. | |
425 | :param user_name: Merge commit `user_name`. |
|
429 | :param user_name: Merge commit `user_name`. | |
426 | :param user_email: Merge commit `user_email`. |
|
430 | :param user_email: Merge commit `user_email`. | |
427 | :param message: Merge commit `message`. |
|
431 | :param message: Merge commit `message`. | |
428 | :param dry_run: If `True` the merge will not take place. |
|
432 | :param dry_run: If `True` the merge will not take place. | |
429 | :param use_rebase: If `True` commits from the source will be rebased |
|
433 | :param use_rebase: If `True` commits from the source will be rebased | |
430 | on top of the target instead of being merged. |
|
434 | on top of the target instead of being merged. | |
431 | """ |
|
435 | """ | |
432 | if dry_run: |
|
436 | if dry_run: | |
433 | message = message or 'dry_run_merge_message' |
|
437 | message = message or 'dry_run_merge_message' | |
434 | user_email = user_email or 'dry-run-merge@rhodecode.com' |
|
438 | user_email = user_email or 'dry-run-merge@rhodecode.com' | |
435 | user_name = user_name or 'Dry-Run User' |
|
439 | user_name = user_name or 'Dry-Run User' | |
436 | else: |
|
440 | else: | |
437 | if not user_name: |
|
441 | if not user_name: | |
438 | raise ValueError('user_name cannot be empty') |
|
442 | raise ValueError('user_name cannot be empty') | |
439 | if not user_email: |
|
443 | if not user_email: | |
440 | raise ValueError('user_email cannot be empty') |
|
444 | raise ValueError('user_email cannot be empty') | |
441 | if not message: |
|
445 | if not message: | |
442 | raise ValueError('message cannot be empty') |
|
446 | raise ValueError('message cannot be empty') | |
443 |
|
447 | |||
444 | shadow_repository_path = self._maybe_prepare_merge_workspace( |
|
448 | shadow_repository_path = self._maybe_prepare_merge_workspace( | |
445 | workspace_id, target_ref) |
|
449 | workspace_id, target_ref) | |
446 |
|
450 | |||
447 | try: |
|
451 | try: | |
448 | return self._merge_repo( |
|
452 | return self._merge_repo( | |
449 | shadow_repository_path, target_ref, source_repo, |
|
453 | shadow_repository_path, target_ref, source_repo, | |
450 | source_ref, message, user_name, user_email, dry_run=dry_run, |
|
454 | source_ref, message, user_name, user_email, dry_run=dry_run, | |
451 | use_rebase=use_rebase) |
|
455 | use_rebase=use_rebase) | |
452 | except RepositoryError: |
|
456 | except RepositoryError: | |
453 | log.exception( |
|
457 | log.exception( | |
454 | 'Unexpected failure when running merge, dry-run=%s', |
|
458 | 'Unexpected failure when running merge, dry-run=%s', | |
455 | dry_run) |
|
459 | dry_run) | |
456 | return MergeResponse( |
|
460 | return MergeResponse( | |
457 | False, False, None, MergeFailureReason.UNKNOWN) |
|
461 | False, False, None, MergeFailureReason.UNKNOWN) | |
458 |
|
462 | |||
459 | def _merge_repo(self, shadow_repository_path, target_ref, |
|
463 | def _merge_repo(self, shadow_repository_path, target_ref, | |
460 | source_repo, source_ref, merge_message, |
|
464 | source_repo, source_ref, merge_message, | |
461 | merger_name, merger_email, dry_run=False, use_rebase=False): |
|
465 | merger_name, merger_email, dry_run=False, use_rebase=False): | |
462 | """Internal implementation of merge.""" |
|
466 | """Internal implementation of merge.""" | |
463 | raise NotImplementedError |
|
467 | raise NotImplementedError | |
464 |
|
468 | |||
465 | def _maybe_prepare_merge_workspace(self, workspace_id, target_ref): |
|
469 | def _maybe_prepare_merge_workspace(self, workspace_id, target_ref): | |
466 | """ |
|
470 | """ | |
467 | Create the merge workspace. |
|
471 | Create the merge workspace. | |
468 |
|
472 | |||
469 | :param workspace_id: `workspace_id` unique identifier. |
|
473 | :param workspace_id: `workspace_id` unique identifier. | |
470 | """ |
|
474 | """ | |
471 | raise NotImplementedError |
|
475 | raise NotImplementedError | |
472 |
|
476 | |||
473 | def cleanup_merge_workspace(self, workspace_id): |
|
477 | def cleanup_merge_workspace(self, workspace_id): | |
474 | """ |
|
478 | """ | |
475 | Remove merge workspace. |
|
479 | Remove merge workspace. | |
476 |
|
480 | |||
477 | This function MUST not fail in case there is no workspace associated to |
|
481 | This function MUST not fail in case there is no workspace associated to | |
478 | the given `workspace_id`. |
|
482 | the given `workspace_id`. | |
479 |
|
483 | |||
480 | :param workspace_id: `workspace_id` unique identifier. |
|
484 | :param workspace_id: `workspace_id` unique identifier. | |
481 | """ |
|
485 | """ | |
482 | raise NotImplementedError |
|
486 | raise NotImplementedError | |
483 |
|
487 | |||
484 | # ========== # |
|
488 | # ========== # | |
485 | # COMMIT API # |
|
489 | # COMMIT API # | |
486 | # ========== # |
|
490 | # ========== # | |
487 |
|
491 | |||
488 | @LazyProperty |
|
492 | @LazyProperty | |
489 | def in_memory_commit(self): |
|
493 | def in_memory_commit(self): | |
490 | """ |
|
494 | """ | |
491 | Returns :class:`InMemoryCommit` object for this repository. |
|
495 | Returns :class:`InMemoryCommit` object for this repository. | |
492 | """ |
|
496 | """ | |
493 | raise NotImplementedError |
|
497 | raise NotImplementedError | |
494 |
|
498 | |||
495 | # ======================== # |
|
499 | # ======================== # | |
496 | # UTILITIES FOR SUBCLASSES # |
|
500 | # UTILITIES FOR SUBCLASSES # | |
497 | # ======================== # |
|
501 | # ======================== # | |
498 |
|
502 | |||
499 | def _validate_diff_commits(self, commit1, commit2): |
|
503 | def _validate_diff_commits(self, commit1, commit2): | |
500 | """ |
|
504 | """ | |
501 | Validates that the given commits are related to this repository. |
|
505 | Validates that the given commits are related to this repository. | |
502 |
|
506 | |||
503 | Intended as a utility for sub classes to have a consistent validation |
|
507 | Intended as a utility for sub classes to have a consistent validation | |
504 | of input parameters in methods like :meth:`get_diff`. |
|
508 | of input parameters in methods like :meth:`get_diff`. | |
505 | """ |
|
509 | """ | |
506 | self._validate_commit(commit1) |
|
510 | self._validate_commit(commit1) | |
507 | self._validate_commit(commit2) |
|
511 | self._validate_commit(commit2) | |
508 | if (isinstance(commit1, EmptyCommit) and |
|
512 | if (isinstance(commit1, EmptyCommit) and | |
509 | isinstance(commit2, EmptyCommit)): |
|
513 | isinstance(commit2, EmptyCommit)): | |
510 | raise ValueError("Cannot compare two empty commits") |
|
514 | raise ValueError("Cannot compare two empty commits") | |
511 |
|
515 | |||
512 | def _validate_commit(self, commit): |
|
516 | def _validate_commit(self, commit): | |
513 | if not isinstance(commit, BaseCommit): |
|
517 | if not isinstance(commit, BaseCommit): | |
514 | raise TypeError( |
|
518 | raise TypeError( | |
515 | "%s is not of type BaseCommit" % repr(commit)) |
|
519 | "%s is not of type BaseCommit" % repr(commit)) | |
516 | if commit.repository != self and not isinstance(commit, EmptyCommit): |
|
520 | if commit.repository != self and not isinstance(commit, EmptyCommit): | |
517 | raise ValueError( |
|
521 | raise ValueError( | |
518 | "Commit %s must be a valid commit from this repository %s, " |
|
522 | "Commit %s must be a valid commit from this repository %s, " | |
519 | "related to this repository instead %s." % |
|
523 | "related to this repository instead %s." % | |
520 | (commit, self, commit.repository)) |
|
524 | (commit, self, commit.repository)) | |
521 |
|
525 | |||
522 | def _validate_commit_id(self, commit_id): |
|
526 | def _validate_commit_id(self, commit_id): | |
523 | if not isinstance(commit_id, basestring): |
|
527 | if not isinstance(commit_id, basestring): | |
524 | raise TypeError("commit_id must be a string value") |
|
528 | raise TypeError("commit_id must be a string value") | |
525 |
|
529 | |||
526 | def _validate_commit_idx(self, commit_idx): |
|
530 | def _validate_commit_idx(self, commit_idx): | |
527 | if not isinstance(commit_idx, (int, long)): |
|
531 | if not isinstance(commit_idx, (int, long)): | |
528 | raise TypeError("commit_idx must be a numeric value") |
|
532 | raise TypeError("commit_idx must be a numeric value") | |
529 |
|
533 | |||
530 | def _validate_branch_name(self, branch_name): |
|
534 | def _validate_branch_name(self, branch_name): | |
531 | if branch_name and branch_name not in self.branches_all: |
|
535 | if branch_name and branch_name not in self.branches_all: | |
532 | msg = ("Branch %s not found in %s" % (branch_name, self)) |
|
536 | msg = ("Branch %s not found in %s" % (branch_name, self)) | |
533 | raise BranchDoesNotExistError(msg) |
|
537 | raise BranchDoesNotExistError(msg) | |
534 |
|
538 | |||
535 | # |
|
539 | # | |
536 | # Supporting deprecated API parts |
|
540 | # Supporting deprecated API parts | |
537 | # TODO: johbo: consider to move this into a mixin |
|
541 | # TODO: johbo: consider to move this into a mixin | |
538 | # |
|
542 | # | |
539 |
|
543 | |||
540 | @property |
|
544 | @property | |
541 | def EMPTY_CHANGESET(self): |
|
545 | def EMPTY_CHANGESET(self): | |
542 | warnings.warn( |
|
546 | warnings.warn( | |
543 | "Use EMPTY_COMMIT or EMPTY_COMMIT_ID instead", DeprecationWarning) |
|
547 | "Use EMPTY_COMMIT or EMPTY_COMMIT_ID instead", DeprecationWarning) | |
544 | return self.EMPTY_COMMIT_ID |
|
548 | return self.EMPTY_COMMIT_ID | |
545 |
|
549 | |||
546 | @property |
|
550 | @property | |
547 | def revisions(self): |
|
551 | def revisions(self): | |
548 | warnings.warn("Use commits attribute instead", DeprecationWarning) |
|
552 | warnings.warn("Use commits attribute instead", DeprecationWarning) | |
549 | return self.commit_ids |
|
553 | return self.commit_ids | |
550 |
|
554 | |||
551 | @revisions.setter |
|
555 | @revisions.setter | |
552 | def revisions(self, value): |
|
556 | def revisions(self, value): | |
553 | warnings.warn("Use commits attribute instead", DeprecationWarning) |
|
557 | warnings.warn("Use commits attribute instead", DeprecationWarning) | |
554 | self.commit_ids = value |
|
558 | self.commit_ids = value | |
555 |
|
559 | |||
556 | def get_changeset(self, revision=None, pre_load=None): |
|
560 | def get_changeset(self, revision=None, pre_load=None): | |
557 | warnings.warn("Use get_commit instead", DeprecationWarning) |
|
561 | warnings.warn("Use get_commit instead", DeprecationWarning) | |
558 | commit_id = None |
|
562 | commit_id = None | |
559 | commit_idx = None |
|
563 | commit_idx = None | |
560 | if isinstance(revision, basestring): |
|
564 | if isinstance(revision, basestring): | |
561 | commit_id = revision |
|
565 | commit_id = revision | |
562 | else: |
|
566 | else: | |
563 | commit_idx = revision |
|
567 | commit_idx = revision | |
564 | return self.get_commit( |
|
568 | return self.get_commit( | |
565 | commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load) |
|
569 | commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load) | |
566 |
|
570 | |||
567 | def get_changesets( |
|
571 | def get_changesets( | |
568 | self, start=None, end=None, start_date=None, end_date=None, |
|
572 | self, start=None, end=None, start_date=None, end_date=None, | |
569 | branch_name=None, pre_load=None): |
|
573 | branch_name=None, pre_load=None): | |
570 | warnings.warn("Use get_commits instead", DeprecationWarning) |
|
574 | warnings.warn("Use get_commits instead", DeprecationWarning) | |
571 | start_id = self._revision_to_commit(start) |
|
575 | start_id = self._revision_to_commit(start) | |
572 | end_id = self._revision_to_commit(end) |
|
576 | end_id = self._revision_to_commit(end) | |
573 | return self.get_commits( |
|
577 | return self.get_commits( | |
574 | start_id=start_id, end_id=end_id, start_date=start_date, |
|
578 | start_id=start_id, end_id=end_id, start_date=start_date, | |
575 | end_date=end_date, branch_name=branch_name, pre_load=pre_load) |
|
579 | end_date=end_date, branch_name=branch_name, pre_load=pre_load) | |
576 |
|
580 | |||
577 | def _revision_to_commit(self, revision): |
|
581 | def _revision_to_commit(self, revision): | |
578 | """ |
|
582 | """ | |
579 | Translates a revision to a commit_id |
|
583 | Translates a revision to a commit_id | |
580 |
|
584 | |||
581 | Helps to support the old changeset based API which allows to use |
|
585 | Helps to support the old changeset based API which allows to use | |
582 | commit ids and commit indices interchangeable. |
|
586 | commit ids and commit indices interchangeable. | |
583 | """ |
|
587 | """ | |
584 | if revision is None: |
|
588 | if revision is None: | |
585 | return revision |
|
589 | return revision | |
586 |
|
590 | |||
587 | if isinstance(revision, basestring): |
|
591 | if isinstance(revision, basestring): | |
588 | commit_id = revision |
|
592 | commit_id = revision | |
589 | else: |
|
593 | else: | |
590 | commit_id = self.commit_ids[revision] |
|
594 | commit_id = self.commit_ids[revision] | |
591 | return commit_id |
|
595 | return commit_id | |
592 |
|
596 | |||
593 | @property |
|
597 | @property | |
594 | def in_memory_changeset(self): |
|
598 | def in_memory_changeset(self): | |
595 | warnings.warn("Use in_memory_commit instead", DeprecationWarning) |
|
599 | warnings.warn("Use in_memory_commit instead", DeprecationWarning) | |
596 | return self.in_memory_commit |
|
600 | return self.in_memory_commit | |
597 |
|
601 | |||
598 |
|
602 | |||
599 | class BaseCommit(object): |
|
603 | class BaseCommit(object): | |
600 | """ |
|
604 | """ | |
601 | Each backend should implement it's commit representation. |
|
605 | Each backend should implement it's commit representation. | |
602 |
|
606 | |||
603 | **Attributes** |
|
607 | **Attributes** | |
604 |
|
608 | |||
605 | ``repository`` |
|
609 | ``repository`` | |
606 | repository object within which commit exists |
|
610 | repository object within which commit exists | |
607 |
|
611 | |||
608 | ``id`` |
|
612 | ``id`` | |
609 | The commit id, may be ``raw_id`` or i.e. for mercurial's tip |
|
613 | The commit id, may be ``raw_id`` or i.e. for mercurial's tip | |
610 | just ``tip``. |
|
614 | just ``tip``. | |
611 |
|
615 | |||
612 | ``raw_id`` |
|
616 | ``raw_id`` | |
613 | raw commit representation (i.e. full 40 length sha for git |
|
617 | raw commit representation (i.e. full 40 length sha for git | |
614 | backend) |
|
618 | backend) | |
615 |
|
619 | |||
616 | ``short_id`` |
|
620 | ``short_id`` | |
617 | shortened (if apply) version of ``raw_id``; it would be simple |
|
621 | shortened (if apply) version of ``raw_id``; it would be simple | |
618 | shortcut for ``raw_id[:12]`` for git/mercurial backends or same |
|
622 | shortcut for ``raw_id[:12]`` for git/mercurial backends or same | |
619 | as ``raw_id`` for subversion |
|
623 | as ``raw_id`` for subversion | |
620 |
|
624 | |||
621 | ``idx`` |
|
625 | ``idx`` | |
622 | commit index |
|
626 | commit index | |
623 |
|
627 | |||
624 | ``files`` |
|
628 | ``files`` | |
625 | list of ``FileNode`` (``Node`` with NodeKind.FILE) objects |
|
629 | list of ``FileNode`` (``Node`` with NodeKind.FILE) objects | |
626 |
|
630 | |||
627 | ``dirs`` |
|
631 | ``dirs`` | |
628 | list of ``DirNode`` (``Node`` with NodeKind.DIR) objects |
|
632 | list of ``DirNode`` (``Node`` with NodeKind.DIR) objects | |
629 |
|
633 | |||
630 | ``nodes`` |
|
634 | ``nodes`` | |
631 | combined list of ``Node`` objects |
|
635 | combined list of ``Node`` objects | |
632 |
|
636 | |||
633 | ``author`` |
|
637 | ``author`` | |
634 | author of the commit, as unicode |
|
638 | author of the commit, as unicode | |
635 |
|
639 | |||
636 | ``message`` |
|
640 | ``message`` | |
637 | message of the commit, as unicode |
|
641 | message of the commit, as unicode | |
638 |
|
642 | |||