Merge lp:~jelmer/bzr-git/sprout into lp:~launchpad-pqm/bzr-git/devel
- sprout
- Merge into devel
Proposed by
Jelmer Vernooij
Status: | Superseded | ||||||||
---|---|---|---|---|---|---|---|---|---|
Proposed branch: | lp:~jelmer/bzr-git/sprout | ||||||||
Merge into: | lp:~launchpad-pqm/bzr-git/devel | ||||||||
Diff against target: |
4188 lines (+1585/-509) (has conflicts) 43 files modified
.bzrignore (+1/-0) .testr.conf (+4/-0) NEWS (+54/-3) README (+4/-4) TODO (+2/-2) __init__.py (+197/-107) branch.py (+216/-78) bzr-receive-pack (+7/-12) bzr-upload-pack (+8/-18) cache.py (+65/-24) commands.py (+58/-19) commit.py (+18/-4) dir.py (+266/-23) fetch.py (+74/-19) help.py (+33/-0) info.py (+3/-3) inventory.py (+7/-6) mapping.py (+17/-10) object_store.py (+41/-25) push.py (+54/-27) refs.py (+77/-6) remote.py (+67/-18) repository.py (+68/-23) roundtrip.py (+15/-3) send.py (+1/-6) tests/__init__.py (+15/-14) tests/test_blackbox.py (+6/-5) tests/test_branch.py (+17/-2) tests/test_builder.py (+3/-3) tests/test_cache.py (+16/-7) tests/test_dir.py (+2/-2) tests/test_fetch.py (+1/-1) tests/test_mapping.py (+28/-11) tests/test_object_store.py (+17/-1) tests/test_push.py (+2/-2) tests/test_refs.py (+3/-3) tests/test_repository.py (+4/-3) tests/test_roundtrip.py (+10/-0) tests/test_transportgit.py (+4/-0) tests/test_versionedfiles.py (+50/-0) transportgit.py (+25/-2) versionedfiles.py (+1/-1) workingtree.py (+24/-12) Text conflict in __init__.py |
||||||||
To merge this branch: | bzr merge lp:~jelmer/bzr-git/sprout | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Launchpad PQM Bot | Pending | ||
Review via email: mp+54595@code.launchpad.net |
Commit message
Description of the change
Fix compatibility with 2.4 by providing a custom ControlDir.sprout() implementation.
Fetch all revisions referenced by tags, even if those revisions are not in the
ancestry of tip.
To post a comment you must log in.
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2009-01-30 14:58:20 +0000 |
3 | +++ .bzrignore 2011-03-23 20:12:35 +0000 |
4 | @@ -5,3 +5,4 @@ |
5 | build |
6 | .plugins |
7 | ./_lib |
8 | +.testrepository |
9 | |
10 | === added file '.testr.conf' |
11 | --- .testr.conf 1970-01-01 00:00:00 +0000 |
12 | +++ .testr.conf 2011-03-23 20:12:35 +0000 |
13 | @@ -0,0 +1,4 @@ |
14 | +[DEFAULT] |
15 | +test_command=BZR_PLUGINS_AT=git@`pwd` bzr selftest ^bzrlib.plugins.git. Git --subunit $IDOPTION $LISTOPT |
16 | +test_id_option=--load-list $IDFILE |
17 | +test_list_option=--list |
18 | |
19 | === modified file 'NEWS' |
20 | --- NEWS 2010-08-12 20:25:43 +0000 |
21 | +++ NEWS 2011-03-23 20:12:35 +0000 |
22 | @@ -1,17 +1,68 @@ |
23 | -0.5.3 UNRELEASED |
24 | +0.5.5 UNRELEASED |
25 | + |
26 | + BUG FIXES |
27 | + |
28 | + * Fix encoding handling in Git working trees. (Jelmer Vernooij, #393038) |
29 | + |
30 | + * Use transports internally in "bzr git-import". |
31 | + (Jelmer Vernooij, #733919) |
32 | + |
33 | + * Provide custom GitDir.sprout() implementation for compatibility with bzr 2.4. |
34 | + (Jelmer Vernooij, #717937) |
35 | + |
36 | + * Revisions attached to tags that are not in the tips ancestry are now fetched. |
37 | + (Jelmer Vernooij, #309682) |
38 | + |
39 | + API COMPLETENESS |
40 | + |
41 | + * Implement LocalGitControlDir.clone_on_transport. (Jelmer Vernooij, #721899) |
42 | + |
43 | + COMPATIBILITY |
44 | + |
45 | + * Drop support for Bazaar < 2.3. (Jelmer Vernooij) |
46 | + |
47 | +0.5.4 2011-02-10 |
48 | + |
49 | + BUG FIXES |
50 | + |
51 | + * Fix test suite compatibility with Bazaar 2.2. (Max Bowsher, #707434) |
52 | + |
53 | + * Fix compatibility with older versions of python-tdb. |
54 | + (Jelmer Vernooij, #707735) |
55 | + |
56 | + * Fix 'bzr git-import' from remote repositories. (Jelmer Vernooij, #706990) |
57 | + |
58 | + * Cope with tags when doing local fetches. (Jelmer Vernooij, #675637) |
59 | + |
60 | +0.5.3 2011-01-21 |
61 | |
62 | BUG FIXES |
63 | |
64 | * Add in an empty git repository now works. (Jelmer Vernooij, #603823) |
65 | |
66 | * Support opening of repositories over HTTP where the HTTP server |
67 | - doesn't allow directory access. (Jelmer Vernooij) |
68 | + doesn't allow directory access. (Jelmer Vernooij, #617078) |
69 | |
70 | * Support non-ascii characters in tag names. (Jelmer Vernooij, #616995) |
71 | |
72 | + * Mark as compatible with bzr 2.3, 2.4. (Jelmer Vernooij) |
73 | + |
74 | + * Cope with unknown refs. (Jelmer Vernooij, #666443) |
75 | + |
76 | + * Don't peel tags automatically when pushing back. |
77 | + (Jelmer Vernooij, #675231) |
78 | + |
79 | + * Fix `bzr-receive-pack` and `bzr-upload-pack`. (Jelmer Vernooij, #681193) |
80 | + |
81 | FEATURES |
82 | |
83 | - * Remove all remaining dependencies on C git. (Jelmer Vernooij) |
84 | + * Remove all remaining dependencies on C git. (Jelmer Vernooij, #348238) |
85 | + |
86 | + * Add some basic documentation in 'bzr help git'. (Jelmer Vernooij, #605394) |
87 | + |
88 | + * Add --signoff option to 'bzr git-apply'. (Jelmer Vernooij) |
89 | + |
90 | + * Add --force option to 'bzr git-apply'. (Jelmer Vernooij) |
91 | |
92 | 0.5.2 2010-07-30 |
93 | |
94 | |
95 | === modified file 'README' |
96 | --- README 2009-05-09 14:22:48 +0000 |
97 | +++ README 2011-03-23 20:12:35 +0000 |
98 | @@ -1,9 +1,9 @@ |
99 | bzr-git, a plugin for bzr that adds git support. |
100 | |
101 | -This was originally written as a proof of concept at Europython 2006 by |
102 | -Robert Collins, using stgit's convenience methods for accessing gits head |
103 | -and parsing git output. Later, it was adapted to use James Westby's Python |
104 | -Git module (which had by then be renamed to "Dulwich") and extended to support |
105 | +This was originally written as a proof of concept at Europython 2006 by |
106 | +Robert Collins, using stgit's convenience methods for accessing gits head |
107 | +and parsing git output. Later, it was adapted to use James Westby's Python |
108 | +Git module (which had by then be renamed to "Dulwich") and extended to support |
109 | push and pull by Jelmer Vernooij. |
110 | |
111 | Please see INSTALL for installation instructions, and TODO for future plans. |
112 | |
113 | === modified file 'TODO' |
114 | --- TODO 2009-05-09 14:22:48 +0000 |
115 | +++ TODO 2011-03-23 20:12:35 +0000 |
116 | @@ -1,2 +1,2 @@ |
117 | -- Push into git |
118 | -- Tests |
119 | +- "Roundtripping" push into git |
120 | +- More tests |
121 | |
122 | === modified file '__init__.py' |
123 | --- __init__.py 2010-08-12 20:25:43 +0000 |
124 | +++ __init__.py 2011-03-23 20:12:35 +0000 |
125 | @@ -43,16 +43,27 @@ |
126 | |
127 | |
128 | from bzrlib import ( |
129 | - bzrdir, |
130 | errors as bzr_errors, |
131 | +<<<<<<< TREE |
132 | osutils, |
133 | transport, |
134 | ) |
135 | +======= |
136 | + ) |
137 | + |
138 | +from bzrlib.controldir import ( |
139 | + ControlDirFormat, |
140 | + Prober, |
141 | + format_registry, |
142 | + network_format_registry as controldir_network_format_registry, |
143 | + ) |
144 | + |
145 | +>>>>>>> MERGE-SOURCE |
146 | from bzrlib.foreign import ( |
147 | foreign_vcs_registry, |
148 | ) |
149 | -from bzrlib.lockable_files import ( |
150 | - TransportLock, |
151 | +from bzrlib.help_topics import ( |
152 | + topic_registry, |
153 | ) |
154 | from bzrlib.transport import ( |
155 | register_lazy_transport, |
156 | @@ -61,9 +72,6 @@ |
157 | from bzrlib.commands import ( |
158 | plugin_cmds, |
159 | ) |
160 | -from bzrlib.version_info_formats.format_rio import ( |
161 | - RioVersionInfoBuilder, |
162 | - ) |
163 | from bzrlib.send import ( |
164 | format_registry as send_format_registry, |
165 | ) |
166 | @@ -96,43 +104,36 @@ |
167 | import_dulwich() |
168 | _versions_checked = True |
169 | |
170 | - |
171 | -class GitBzrDirFormat(bzrdir.BzrDirFormat): |
172 | - |
173 | - _lock_class = TransportLock |
174 | - |
175 | - colocated_branches = True |
176 | - |
177 | - def __eq__(self, other): |
178 | - return type(self) == type(other) |
179 | - |
180 | - def is_supported(self): |
181 | - return True |
182 | - |
183 | - def network_name(self): |
184 | - return "git" |
185 | - |
186 | - |
187 | -class LocalGitBzrDirFormat(GitBzrDirFormat): |
188 | - """The .git directory control format.""" |
189 | - |
190 | - @classmethod |
191 | - def _known_formats(self): |
192 | - return set([LocalGitBzrDirFormat()]) |
193 | - |
194 | - def open(self, transport, _found=None): |
195 | - """Open this directory. |
196 | - |
197 | - """ |
198 | - lazy_check_versions() |
199 | - from bzrlib.plugins.git.transportgit import TransportRepo |
200 | - gitrepo = TransportRepo(transport) |
201 | - from bzrlib.plugins.git.dir import LocalGitDir, GitLockableFiles, GitLock |
202 | - lockfiles = GitLockableFiles(transport, GitLock()) |
203 | - return LocalGitDir(transport, lockfiles, gitrepo, self) |
204 | - |
205 | - @classmethod |
206 | - def probe_transport(klass, transport): |
207 | +<<<<<<< TREE |
208 | +======= |
209 | +format_registry.register_lazy('git', |
210 | + "bzrlib.plugins.git.dir", "LocalGitControlDirFormat", |
211 | + help='GIT repository.', native=False, experimental=False, |
212 | + ) |
213 | + |
214 | +format_registry.register_lazy('git-bare', |
215 | + "bzrlib.plugins.git.dir", "BareLocalGitControlDirFormat", |
216 | + help='Bare GIT repository (no working tree).', native=False, |
217 | + experimental=False, |
218 | + ) |
219 | + |
220 | +from bzrlib.revisionspec import revspec_registry |
221 | +revspec_registry.register_lazy("git:", "bzrlib.plugins.git.revspec", |
222 | + "RevisionSpec_git") |
223 | + |
224 | +from bzrlib.revisionspec import dwim_revspecs, RevisionSpec_dwim |
225 | +if getattr(RevisionSpec_dwim, "append_possible_lazy_revspec", None): |
226 | + RevisionSpec_dwim.append_possible_lazy_revspec( |
227 | + "bzrlib.plugins.git.revspec", "RevisionSpec_git") |
228 | +else: # bzr < 2.4 |
229 | + from bzrlib.plugins.git.revspec import RevisionSpec_git |
230 | + dwim_revspecs.append(RevisionSpec_git) |
231 | + |
232 | +>>>>>>> MERGE-SOURCE |
233 | + |
234 | +class LocalGitProber(Prober): |
235 | + |
236 | + def probe_transport(self, transport): |
237 | try: |
238 | if not transport.has_any(['info/refs', '.git/branches', |
239 | 'branches']): |
240 | @@ -144,73 +145,46 @@ |
241 | raise bzr_errors.NotBranchError(path=transport.base) |
242 | lazy_check_versions() |
243 | import dulwich |
244 | - format = klass() |
245 | + from bzrlib.plugins.git.transportgit import TransportRepo |
246 | try: |
247 | - format.open(transport) |
248 | - return format |
249 | + gitrepo = TransportRepo(transport) |
250 | except dulwich.errors.NotGitRepository, e: |
251 | raise bzr_errors.NotBranchError(path=transport.base) |
252 | - raise bzr_errors.NotBranchError(path=transport.base) |
253 | - |
254 | - def get_format_description(self): |
255 | - return "Local Git Repository" |
256 | - |
257 | - def get_format_string(self): |
258 | - return "Local Git Repository" |
259 | - |
260 | - def initialize_on_transport(self, transport): |
261 | - from bzrlib.transport.local import LocalTransport |
262 | - |
263 | - if not isinstance(transport, LocalTransport): |
264 | - raise NotImplementedError(self.initialize, |
265 | - "Can't create Git Repositories/branches on " |
266 | - "non-local transports") |
267 | - lazy_check_versions() |
268 | - from dulwich.repo import Repo |
269 | - Repo.init(transport.local_abspath(".").encode(osutils._fs_enc)) |
270 | - return self.open(transport) |
271 | - |
272 | - def is_supported(self): |
273 | - return True |
274 | - |
275 | - |
276 | -class RemoteGitBzrDirFormat(GitBzrDirFormat): |
277 | - """The .git directory control format.""" |
278 | - |
279 | - @classmethod |
280 | - def _known_formats(self): |
281 | - return set([RemoteGitBzrDirFormat()]) |
282 | - |
283 | - def open(self, transport, _found=None): |
284 | - """Open this directory. |
285 | - |
286 | - """ |
287 | - # we dont grok readonly - git isn't integrated with transport. |
288 | - url = transport.base |
289 | - if url.startswith('readonly+'): |
290 | - url = url[len('readonly+'):] |
291 | - if (not url.startswith("git://") and not url.startswith("git+")): |
292 | - raise bzr_errors.NotBranchError(transport.base) |
293 | - from bzrlib.plugins.git.remote import RemoteGitDir, GitSmartTransport |
294 | - if not isinstance(transport, GitSmartTransport): |
295 | - raise bzr_errors.NotBranchError(transport.base) |
296 | - from bzrlib.plugins.git.dir import GitLockableFiles, GitLock |
297 | - lockfiles = GitLockableFiles(transport, GitLock()) |
298 | - return RemoteGitDir(transport, lockfiles, self) |
299 | - |
300 | - @classmethod |
301 | - def probe_transport(klass, transport): |
302 | - """Our format is present if the transport ends in '.not/'.""" |
303 | + else: |
304 | + from bzrlib.plugins.git.dir import ( |
305 | + BareLocalGitControlDirFormat, |
306 | + LocalGitControlDirFormat, |
307 | + ) |
308 | + if gitrepo.bare: |
309 | + return BareLocalGitControlDirFormat() |
310 | + else: |
311 | + return LocalGitControlDirFormat() |
312 | + |
313 | + @classmethod |
314 | + def known_formats(cls): |
315 | + from bzrlib.plugins.git.dir import ( |
316 | + BareLocalGitControlDirFormat, |
317 | + LocalGitControlDirFormat, |
318 | + ) |
319 | + return set([BareLocalGitControlDirFormat(), LocalGitControlDirFormat()]) |
320 | + |
321 | + |
322 | +class RemoteGitProber(Prober): |
323 | + |
324 | + def probe_transport(self, transport): |
325 | url = transport.base |
326 | if url.startswith('readonly+'): |
327 | url = url[len('readonly+'):] |
328 | if (not url.startswith("git://") and not url.startswith("git+")): |
329 | raise bzr_errors.NotBranchError(transport.base) |
330 | # little ugly, but works |
331 | - format = klass() |
332 | - from bzrlib.plugins.git.remote import GitSmartTransport |
333 | + from bzrlib.plugins.git.remote import ( |
334 | + GitSmartTransport, |
335 | + RemoteGitControlDirFormat, |
336 | + ) |
337 | if not isinstance(transport, GitSmartTransport): |
338 | raise bzr_errors.NotBranchError(transport.base) |
339 | +<<<<<<< TREE |
340 | return format |
341 | |
342 | def get_format_description(self): |
343 | @@ -223,18 +197,76 @@ |
344 | raise bzr_errors.UninitializableFormat(self) |
345 | |
346 | |
347 | +======= |
348 | + return RemoteGitControlDirFormat() |
349 | + |
350 | + @classmethod |
351 | + def known_formats(cls): |
352 | + from bzrlib.plugins.git.remote import RemoteGitControlDirFormat |
353 | + return set([RemoteGitControlDirFormat()]) |
354 | + |
355 | + |
356 | +if not getattr(Prober, "known_formats", None): # bzr < 2.4 |
357 | + from bzrlib.plugins.git.dir import ( |
358 | + LocalGitControlDirFormat, BareLocalGitControlDirFormat, |
359 | + ) |
360 | + from bzrlib.plugins.git.remote import RemoteGitControlDirFormat |
361 | + ControlDirFormat.register_format(LocalGitControlDirFormat()) |
362 | + ControlDirFormat.register_format(BareLocalGitControlDirFormat()) |
363 | + ControlDirFormat.register_format(RemoteGitControlDirFormat()) |
364 | +ControlDirFormat.register_prober(LocalGitProber) |
365 | +ControlDirFormat.register_prober(RemoteGitProber) |
366 | + |
367 | +register_transport_proto('git://', |
368 | + help="Access using the Git smart server protocol.") |
369 | +register_transport_proto('git+ssh://', |
370 | + help="Access using the Git smart server protocol over SSH.") |
371 | + |
372 | +register_lazy_transport("git://", 'bzrlib.plugins.git.remote', |
373 | + 'TCPGitSmartTransport') |
374 | +register_lazy_transport("git+ssh://", 'bzrlib.plugins.git.remote', |
375 | + 'SSHGitSmartTransport') |
376 | + |
377 | +foreign_vcs_registry.register_lazy("git", |
378 | + "bzrlib.plugins.git.mapping", "foreign_git", "Stupid content tracker") |
379 | + |
380 | +plugin_cmds.register_lazy("cmd_git_import", [], "bzrlib.plugins.git.commands") |
381 | +plugin_cmds.register_lazy("cmd_git_object", ["git-objects", "git-cat"], |
382 | + "bzrlib.plugins.git.commands") |
383 | +plugin_cmds.register_lazy("cmd_git_refs", [], "bzrlib.plugins.git.commands") |
384 | +plugin_cmds.register_lazy("cmd_git_apply", [], "bzrlib.plugins.git.commands") |
385 | + |
386 | +>>>>>>> MERGE-SOURCE |
387 | def update_stanza(rev, stanza): |
388 | mapping = getattr(rev, "mapping", None) |
389 | if mapping is not None and mapping.revid_prefix.startswith("git-"): |
390 | stanza.add("git-commit", rev.foreign_revid) |
391 | |
392 | +<<<<<<< TREE |
393 | |
394 | from bzrlib.revisionspec import revspec_registry |
395 | try: |
396 | from bzrlib.revisionspec import dwim_revspecs |
397 | except ImportError: |
398 | dwim_revspecs = None |
399 | +======= |
400 | +try: |
401 | + from bzrlib.hooks import install_lazy_named_hook |
402 | +except ImportError: # Compatibility with bzr < 2.4 |
403 | + from bzrlib.version_info_formats.format_rio import ( |
404 | + RioVersionInfoBuilder, |
405 | + ) |
406 | + RioVersionInfoBuilder.hooks.install_named_hook('revision', update_stanza, |
407 | + "git commits") |
408 | +else: |
409 | + install_lazy_named_hook("bzrlib.version_info_formats.format_rio", |
410 | + "RioVersionInfoBuilder.hooks", "revision", update_stanza, |
411 | + "git commits") |
412 | + |
413 | + |
414 | +>>>>>>> MERGE-SOURCE |
415 | from bzrlib.transport import transport_server_registry |
416 | +<<<<<<< TREE |
417 | from bzrlib.repository import network_format_registry as repository_network_format_registry |
418 | from bzrlib.bzrdir import network_format_registry as bzrdir_network_format_registry |
419 | |
420 | @@ -340,14 +372,72 @@ |
421 | unregister(send_format_registry, 'git') |
422 | __registered = False |
423 | |
424 | - |
425 | -try: |
426 | - from bzrlib.diff import format_registry as diff_format_registry |
427 | -except ImportError: |
428 | - pass |
429 | -else: |
430 | - diff_format_registry.register_lazy('git', 'bzrlib.plugins.git.send', |
431 | - 'GitDiffTree', 'Git am-style diff format') |
432 | +======= |
433 | +transport_server_registry.register_lazy('git', |
434 | + 'bzrlib.plugins.git.server', |
435 | + 'serve_git', |
436 | + 'Git Smart server protocol over TCP. (default port: 9418)') |
437 | + |
438 | + |
439 | +from bzrlib.repository import ( |
440 | + format_registry as repository_format_registry, |
441 | + network_format_registry as repository_network_format_registry, |
442 | + ) |
443 | +repository_network_format_registry.register_lazy('git', |
444 | + 'bzrlib.plugins.git.repository', 'GitRepositoryFormat') |
445 | + |
446 | +try: |
447 | + register_extra_lazy_repository_format = getattr(repository_format_registry, |
448 | + "register_extra_lazy") |
449 | +except AttributeError: # bzr < 2.4 |
450 | + pass |
451 | +else: |
452 | + register_extra_lazy_repository_format('bzrlib.plugins.git.repository', |
453 | + 'GitRepositoryFormat') |
454 | + |
455 | +from bzrlib.branch import ( |
456 | + network_format_registry as branch_network_format_registry, |
457 | + ) |
458 | +branch_network_format_registry.register_lazy('git', |
459 | + 'bzrlib.plugins.git.branch', 'GitBranchFormat') |
460 | + |
461 | +try: |
462 | + from bzrlib.branch import ( |
463 | + format_registry as branch_format_registry, |
464 | + ) |
465 | +except ImportError: # bzr < 2.4 |
466 | + pass |
467 | +else: |
468 | + branch_format_registry.register_extra_lazy( |
469 | + 'bzrlib.plugins.git.branch', |
470 | + 'GitBranchFormat', |
471 | + ) |
472 | + |
473 | +try: |
474 | + from bzrlib.workingtree import ( |
475 | + format_registry as workingtree_format_registry, |
476 | + ) |
477 | +except ImportError: # bzr < 2.4 |
478 | + pass |
479 | +else: |
480 | + workingtree_format_registry.register_extra_lazy( |
481 | + 'bzrlib.plugins.git.workingtree', |
482 | + 'GitWorkingTreeFormat', |
483 | + ) |
484 | + |
485 | +controldir_network_format_registry.register_lazy('git', |
486 | + "bzrlib.plugins.git.dir", "GitControlDirFormat") |
487 | + |
488 | +send_format_registry.register_lazy('git', 'bzrlib.plugins.git.send', |
489 | + 'send_git', 'Git am-style diff format') |
490 | +>>>>>>> MERGE-SOURCE |
491 | + |
492 | +topic_registry.register_lazy('git', 'bzrlib.plugins.git.help', 'help_git', |
493 | + 'Using Bazaar with Git') |
494 | + |
495 | +from bzrlib.diff import format_registry as diff_format_registry |
496 | +diff_format_registry.register_lazy('git', 'bzrlib.plugins.git.send', |
497 | + 'GitDiffTree', 'Git am-style diff format') |
498 | |
499 | def test_suite(): |
500 | from bzrlib.plugins.git import tests |
501 | |
502 | === modified file 'branch.py' |
503 | --- branch.py 2010-08-08 16:01:26 +0000 |
504 | +++ branch.py 2011-03-23 20:12:35 +0000 |
505 | @@ -17,17 +17,20 @@ |
506 | |
507 | """An adapter between a Git Branch and a Bazaar Branch""" |
508 | |
509 | +from collections import defaultdict |
510 | + |
511 | from dulwich.objects import ( |
512 | Commit, |
513 | Tag, |
514 | ) |
515 | +from dulwich.protocol import ZERO_SHA |
516 | |
517 | from bzrlib import ( |
518 | branch, |
519 | bzrdir, |
520 | config, |
521 | errors, |
522 | - repository, |
523 | + repository as _mod_repository, |
524 | revision, |
525 | tag, |
526 | transport, |
527 | @@ -51,9 +54,13 @@ |
528 | NoSuchRef, |
529 | ) |
530 | from bzrlib.plugins.git.refs import ( |
531 | + branch_name_to_ref, |
532 | + extract_tags, |
533 | + is_tag, |
534 | ref_to_branch_name, |
535 | - extract_tags, |
536 | + ref_to_tag_name, |
537 | tag_name_to_ref, |
538 | + UnpeelMap, |
539 | ) |
540 | |
541 | from bzrlib.foreign import ForeignBranch |
542 | @@ -76,48 +83,136 @@ |
543 | return self._lookup_revno(self.new_revid) |
544 | |
545 | |
546 | -class LocalGitTagDict(tag.BasicTags): |
547 | - """Dictionary with tags in a local repository.""" |
548 | +class GitTags(tag.BasicTags): |
549 | + """Ref-based tag dictionary.""" |
550 | |
551 | def __init__(self, branch): |
552 | self.branch = branch |
553 | self.repository = branch.repository |
554 | |
555 | + def get_refs(self): |
556 | + raise NotImplementedError(self.get_refs) |
557 | + |
558 | + def _iter_tag_refs(self, refs): |
559 | + raise NotImplementedError(self._iter_tag_refs) |
560 | + |
561 | + def _merge_to_git(self, to_tags, refs, overwrite=False): |
562 | + target_repo = to_tags.repository |
563 | + conflicts = [] |
564 | + for k, v in refs.iteritems(): |
565 | + if not is_tag(k): |
566 | + continue |
567 | + if overwrite or not k in target_repo._git.refs: |
568 | + target_repo._git.refs[k] = v |
569 | + elif target_repo._git.refs[k] == v: |
570 | + pass |
571 | + else: |
572 | + conflicts.append((ref_to_tag_name(k), v, target_repo.refs[k])) |
573 | + return conflicts |
574 | + |
575 | + def _merge_to_non_git(self, to_tags, refs, overwrite=False): |
576 | + unpeeled_map = defaultdict(set) |
577 | + conflicts = [] |
578 | + result = dict(to_tags.get_tag_dict()) |
579 | + for n, peeled, unpeeled, bzr_revid in self._iter_tag_refs(refs): |
580 | + if unpeeled is not None: |
581 | + unpeeled_map[peeled].add(unpeeled) |
582 | + if n not in result or overwrite: |
583 | + result[n] = bzr_revid |
584 | + elif result[n] == bzr_revid: |
585 | + pass |
586 | + else: |
587 | + conflicts.append((n, result[n], bzr_revid)) |
588 | + to_tags._set_tag_dict(result) |
589 | + if len(unpeeled_map) > 0: |
590 | + map_file = UnpeelMap.from_repository(to_tags.branch.repository) |
591 | + map_file.update(unpeeled_map) |
592 | + map_file.save_in_repository(to_tags.branch.repository) |
593 | + return conflicts |
594 | + |
595 | + def merge_to(self, to_tags, overwrite=False, ignore_master=False, |
596 | + source_refs=None): |
597 | + """See Tags.merge_to.""" |
598 | + if source_refs is None: |
599 | + source_refs = self.get_refs() |
600 | + if self == to_tags: |
601 | + return |
602 | + if isinstance(to_tags, GitTags): |
603 | + return self._merge_to_git(to_tags, source_refs, |
604 | + overwrite=overwrite) |
605 | + else: |
606 | + if ignore_master: |
607 | + master = None |
608 | + else: |
609 | + master = to_tags.branch.get_master_branch() |
610 | + conflicts = self._merge_to_non_git(to_tags, source_refs, |
611 | + overwrite=overwrite) |
612 | + if master is not None: |
613 | + conflicts += self.merge_to(master.tags, overwrite=overwrite, |
614 | + source_refs=source_refs, |
615 | + ignore_master=ignore_master) |
616 | + return conflicts |
617 | + |
618 | def get_tag_dict(self): |
619 | ret = {} |
620 | - for k,v in extract_tags(self.repository._git.get_refs()).iteritems(): |
621 | + refs = self.get_refs() |
622 | + for (name, peeled, unpeeled, bzr_revid) in self._iter_tag_refs(refs): |
623 | + ret[name] = bzr_revid |
624 | + return ret |
625 | + |
626 | + |
627 | +class LocalGitTagDict(GitTags): |
628 | + """Dictionary with tags in a local repository.""" |
629 | + |
630 | + def __init__(self, branch): |
631 | + super(LocalGitTagDict, self).__init__(branch) |
632 | + self.refs = self.repository._git.refs |
633 | + |
634 | + def get_refs(self): |
635 | + return self.repository._git.get_refs() |
636 | + |
637 | + def _iter_tag_refs(self, refs): |
638 | + """Iterate over the tag refs. |
639 | + |
640 | + :param refs: Refs dictionary (name -> git sha1) |
641 | + :return: iterator over (name, peeled_sha1, unpeeled_sha1, bzr_revid) |
642 | + """ |
643 | + for k, (peeled, unpeeled) in extract_tags(refs).iteritems(): |
644 | try: |
645 | - obj = self.repository._git[v] |
646 | + obj = self.repository._git[peeled] |
647 | except KeyError: |
648 | - mutter("Tag %s points at unknown object %s, ignoring", v, obj) |
649 | + mutter("Tag %s points at unknown object %s, ignoring", peeled, |
650 | + obj) |
651 | continue |
652 | + # FIXME: this shouldn't really be necessary, the repository |
653 | + # already should have these unpeeled. |
654 | while isinstance(obj, Tag): |
655 | - v = obj.object[1] |
656 | - obj = self.repository._git[v] |
657 | + peeled = obj.object[1] |
658 | + obj = self.repository._git[peeled] |
659 | if not isinstance(obj, Commit): |
660 | mutter("Tag %s points at object %r that is not a commit, " |
661 | "ignoring", k, obj) |
662 | continue |
663 | - ret[k] = self.branch.lookup_foreign_revision_id(v) |
664 | - return ret |
665 | + yield (k, peeled, unpeeled, |
666 | + self.branch.lookup_foreign_revision_id(peeled)) |
667 | |
668 | def _set_tag_dict(self, to_dict): |
669 | - extra = set(self.repository._git.get_refs().keys()) |
670 | + extra = set(self.get_refs().keys()) |
671 | for k, revid in to_dict.iteritems(): |
672 | name = tag_name_to_ref(k) |
673 | if name in extra: |
674 | extra.remove(name) |
675 | self.set_tag(k, revid) |
676 | for name in extra: |
677 | - if name.startswith("refs/tags/"): |
678 | + if is_tag(name): |
679 | del self.repository._git[name] |
680 | |
681 | def set_tag(self, name, revid): |
682 | - self.repository._git.refs[tag_name_to_ref(name)], _ = \ |
683 | - self.branch.mapping.revision_id_bzr_to_foreign(revid) |
684 | - |
685 | - |
686 | -class DictTagDict(LocalGitTagDict): |
687 | + self.refs[tag_name_to_ref(name)], _ = \ |
688 | + self.branch.lookup_bzr_revision_id(revid) |
689 | + |
690 | + |
691 | +class DictTagDict(tag.BasicTags): |
692 | |
693 | def __init__(self, branch, tags): |
694 | super(DictTagDict, self).__init__(branch) |
695 | @@ -138,6 +233,14 @@ |
696 | def supports_tags(self): |
697 | return True |
698 | |
699 | + def supports_leaving_lock(self): |
700 | + return False |
701 | + |
702 | + @property |
703 | + def _matchingbzrdir(self): |
704 | + from bzrlib.plugins.git.dir import LocalGitControlDirFormat |
705 | + return LocalGitControlDirFormat() |
706 | + |
707 | def get_foreign_tests_branch_factory(self): |
708 | from bzrlib.plugins.git.tests.test_branch import ForeignTestsBranchFactory |
709 | return ForeignTestsBranchFactory() |
710 | @@ -149,6 +252,16 @@ |
711 | else: |
712 | return LocalGitTagDict(branch) |
713 | |
714 | + def initialize(self, a_bzrdir, name=None, repository=None): |
715 | + from bzrlib.plugins.git.dir import LocalGitDir |
716 | + if not isinstance(a_bzrdir, LocalGitDir): |
717 | + raise errors.IncompatibleFormat(self, a_bzrdir._format) |
718 | + if repository is None: |
719 | + repository = a_bzrdir.open_repository() |
720 | + ref = branch_name_to_ref(name, "HEAD") |
721 | + repository._git[ref] = ZERO_SHA |
722 | + return LocalGitBranch(a_bzrdir, repository, ref, a_bzrdir._lockfiles) |
723 | + |
724 | |
725 | class GitReadLock(object): |
726 | |
727 | @@ -165,6 +278,10 @@ |
728 | class GitBranch(ForeignBranch): |
729 | """An adapter to git repositories for bzr Branch objects.""" |
730 | |
731 | + @property |
732 | + def control_transport(self): |
733 | + return self.bzrdir.control_transport |
734 | + |
735 | def __init__(self, bzrdir, repository, ref, lockfiles, tagsdict=None): |
736 | self.repository = repository |
737 | self._format = GitBranchFormat() |
738 | @@ -208,8 +325,11 @@ |
739 | self.ref or "HEAD") |
740 | |
741 | def generate_revision_history(self, revid, old_revid=None): |
742 | - # FIXME: Check that old_revid is in the ancestry of revid |
743 | - newhead, self.mapping = self.mapping.revision_id_bzr_to_foreign(revid) |
744 | + if revid == NULL_REVISION: |
745 | + newhead = ZERO_SHA |
746 | + else: |
747 | + # FIXME: Check that old_revid is in the ancestry of revid |
748 | + newhead, self.mapping = self.mapping.revision_id_bzr_to_foreign(revid) |
749 | self._set_head(newhead) |
750 | |
751 | def lock_write(self): |
752 | @@ -257,15 +377,19 @@ |
753 | return self.repository.lookup_foreign_revision_id(foreign_revid, |
754 | self.mapping) |
755 | |
756 | + def lookup_bzr_revision_id(self, revid): |
757 | + return self.repository.lookup_bzr_revision_id( |
758 | + revid, mapping=self.mapping) |
759 | + |
760 | |
761 | class LocalGitBranch(GitBranch): |
762 | """A local Git branch.""" |
763 | |
764 | - def __init__(self, bzrdir, repository, name, lockfiles, tagsdict=None): |
765 | - super(LocalGitBranch, self).__init__(bzrdir, repository, name, |
766 | + def __init__(self, bzrdir, repository, ref, lockfiles, tagsdict=None): |
767 | + super(LocalGitBranch, self).__init__(bzrdir, repository, ref, |
768 | lockfiles, tagsdict) |
769 | refs = repository._git.get_refs() |
770 | - if not (name in refs.keys() or "HEAD" in refs.keys()): |
771 | + if not (ref in refs.keys() or "HEAD" in refs.keys()): |
772 | raise errors.NotBranchError(self.base) |
773 | |
774 | def create_checkout(self, to_location, revision_id=None, lightweight=False, |
775 | @@ -347,6 +471,20 @@ |
776 | return True |
777 | |
778 | |
779 | +def _quick_lookup_revno(local_branch, remote_branch, revid): |
780 | + assert isinstance(revid, str), "was %r" % revid |
781 | + # Try in source branch first, it'll be faster |
782 | + try: |
783 | + return local_branch.revision_id_to_revno(revid) |
784 | + except errors.NoSuchRevision: |
785 | + graph = local_branch.repository.get_graph() |
786 | + try: |
787 | + return graph.find_distance_to_null(revid) |
788 | + except errors.GhostRevisionsHaveNoRevno: |
789 | + # FIXME: Check using graph.find_distance_to_null() ? |
790 | + return remote_branch.revision_id_to_revno(revid) |
791 | + |
792 | + |
793 | class GitBranchPullResult(branch.PullResult): |
794 | |
795 | def __init__(self): |
796 | @@ -367,13 +505,7 @@ |
797 | self._show_tag_conficts(to_file) |
798 | |
799 | def _lookup_revno(self, revid): |
800 | - assert isinstance(revid, str), "was %r" % revid |
801 | - # Try in source branch first, it'll be faster |
802 | - try: |
803 | - return self.source_branch.revision_id_to_revno(revid) |
804 | - except errors.NoSuchRevision: |
805 | - # FIXME: Check using graph.find_distance_to_null() ? |
806 | - return self.target_branch.revision_id_to_revno(revid) |
807 | + return _quick_lookup_revno(self.target_branch, self.source_branch, revid) |
808 | |
809 | def _get_old_revno(self): |
810 | if self._old_revno is not None: |
811 | @@ -399,13 +531,7 @@ |
812 | class GitBranchPushResult(branch.BranchPushResult): |
813 | |
814 | def _lookup_revno(self, revid): |
815 | - assert isinstance(revid, str), "was %r" % revid |
816 | - # Try in source branch first, it'll be faster |
817 | - try: |
818 | - return self.source_branch.revision_id_to_revno(revid) |
819 | - except errors.NoSuchRevision: |
820 | - # FIXME: Check using graph.find_distance_to_null() ? |
821 | - return self.target_branch.revision_id_to_revno(revid) |
822 | + return _quick_lookup_revno(self.source_branch, self.target_branch, revid) |
823 | |
824 | @property |
825 | def old_revno(self): |
826 | @@ -413,6 +539,11 @@ |
827 | |
828 | @property |
829 | def new_revno(self): |
830 | + new_original_revno = getattr(self, "new_original_revno", None) |
831 | + if new_original_revno: |
832 | + return new_original_revno |
833 | + if getattr(self, "new_original_revid", None) is not None: |
834 | + return self._lookup_revno(self.new_original_revid) |
835 | return self._lookup_revno(self.new_revid) |
836 | |
837 | |
838 | @@ -421,12 +552,17 @@ |
839 | |
840 | @staticmethod |
841 | def _get_branch_formats_to_test(): |
842 | - return [] |
843 | + try: |
844 | + default_format = branch.format_registry.get_default() |
845 | + except AttributeError: |
846 | + default_format = branch.BranchFormat._default_format |
847 | + return [ |
848 | + (GitBranchFormat(), GitBranchFormat()), |
849 | + (GitBranchFormat(), default_format)] |
850 | |
851 | @classmethod |
852 | def _get_interrepo(self, source, target): |
853 | - return repository.InterRepository.get(source.repository, |
854 | - target.repository) |
855 | + return _mod_repository.InterRepository.get(source.repository, target.repository) |
856 | |
857 | @classmethod |
858 | def is_compatible(cls, source, target): |
859 | @@ -440,25 +576,24 @@ |
860 | |
861 | Compared to the `update_revisions()` below, this function takes a |
862 | `limit` argument that limits how many git commits will be converted |
863 | - and returns the new git head. |
864 | + and returns the new git head and remote refs. |
865 | """ |
866 | interrepo = self._get_interrepo(self.source, self.target) |
867 | def determine_wants(heads): |
868 | if self.source.ref is not None and not self.source.ref in heads: |
869 | raise NoSuchRef(self.source.ref, heads.keys()) |
870 | - if stop_revision is not None: |
871 | - self._last_revid = stop_revision |
872 | - head, mapping = self.source.repository.lookup_bzr_revision_id( |
873 | - stop_revision) |
874 | - else: |
875 | + |
876 | + if stop_revision is None: |
877 | if self.source.ref is not None: |
878 | head = heads[self.source.ref] |
879 | else: |
880 | head = heads["HEAD"] |
881 | self._last_revid = self.source.lookup_foreign_revision_id(head) |
882 | - if self.target.repository.has_revision(self._last_revid): |
883 | - return [] |
884 | - return [head] |
885 | + else: |
886 | + self._last_revid = stop_revision |
887 | + real = interrepo.get_determine_wants_revids( |
888 | + [self._last_revid], include_tags=True) |
889 | + return real(heads) |
890 | pack_hint, head, refs = interrepo.fetch_objects( |
891 | determine_wants, self.source.mapping, limit=limit) |
892 | if (pack_hint is not None and |
893 | @@ -471,8 +606,8 @@ |
894 | else: |
895 | prev_last_revid = self.target.last_revision() |
896 | self.target.generate_revision_history(self._last_revid, |
897 | - prev_last_revid) |
898 | - return head |
899 | + prev_last_revid, self.source) |
900 | + return head, refs |
901 | |
902 | def update_revisions(self, stop_revision=None, overwrite=False, |
903 | graph=None): |
904 | @@ -510,7 +645,7 @@ |
905 | graph = self.target.repository.get_graph(self.source.repository) |
906 | (result.old_revno, result.old_revid) = \ |
907 | self.target.last_revision_info() |
908 | - result.new_git_head = self._update_revisions( |
909 | + result.new_git_head, remote_refs = self._update_revisions( |
910 | stop_revision, overwrite=overwrite, graph=graph, limit=limit) |
911 | result.tag_conflicts = self.source.tags.merge_to(self.target.tags, |
912 | overwrite) |
913 | @@ -535,7 +670,7 @@ |
914 | result.target_branch = self.target |
915 | graph = self.target.repository.get_graph(self.source.repository) |
916 | result.old_revno, result.old_revid = self.target.last_revision_info() |
917 | - result.new_git_head = self._update_revisions( |
918 | + result.new_git_head, remote_refs = self._update_revisions( |
919 | stop_revision, overwrite=overwrite, graph=graph) |
920 | result.tag_conflicts = self.source.tags.merge_to(self.target.tags, |
921 | overwrite) |
922 | @@ -552,6 +687,7 @@ |
923 | |
924 | @staticmethod |
925 | def _get_branch_formats_to_test(): |
926 | + # FIXME |
927 | return [] |
928 | |
929 | @classmethod |
930 | @@ -561,7 +697,6 @@ |
931 | isinstance(target, RemoteGitBranch)) |
932 | |
933 | def _basic_push(self, overwrite=False, stop_revision=None): |
934 | - from dulwich.protocol import ZERO_SHA |
935 | result = GitBranchPushResult() |
936 | result.source_branch = self.source |
937 | result.target_branch = self.target |
938 | @@ -585,6 +720,7 @@ |
939 | |
940 | @staticmethod |
941 | def _get_branch_formats_to_test(): |
942 | + # FIXME |
943 | return [] |
944 | |
945 | @classmethod |
946 | @@ -600,17 +736,13 @@ |
947 | result.old_revid = self.target.last_revision() |
948 | refs, stop_revision = self.update_refs(stop_revision) |
949 | self.target.generate_revision_history(stop_revision, result.old_revid) |
950 | - self.update_tags(refs) |
951 | + result.tag_conflicts = self.source.tags.merge_to(self.target.tags, |
952 | + source_refs=refs, overwrite=overwrite) |
953 | result.new_revid = self.target.last_revision() |
954 | return result |
955 | |
956 | - def update_tags(self, refs): |
957 | - for name, v in extract_tags(refs).iteritems(): |
958 | - revid = self.target.lookup_foreign_revision_id(v) |
959 | - self.target.tags.set_tag(name, revid) |
960 | - |
961 | def update_refs(self, stop_revision=None): |
962 | - interrepo = repository.InterRepository.get(self.source.repository, |
963 | + interrepo = _mod_repository.InterRepository.get(self.source.repository, |
964 | self.target.repository) |
965 | if stop_revision is None: |
966 | refs = interrepo.fetch(branches=["HEAD"]) |
967 | @@ -630,7 +762,8 @@ |
968 | result.old_revid = self.target.last_revision() |
969 | refs, stop_revision = self.update_refs(stop_revision) |
970 | self.target.generate_revision_history(stop_revision, result.old_revid) |
971 | - self.update_tags(refs) |
972 | + result.tag_conflicts = self.source.tags.merge_to(self.target.tags, |
973 | + overwrite=overwrite, source_refs=refs) |
974 | result.new_revid = self.target.last_revision() |
975 | return result |
976 | |
977 | @@ -640,12 +773,16 @@ |
978 | |
979 | def __init__(self, source, target): |
980 | super(InterToGitBranch, self).__init__(source, target) |
981 | - self.interrepo = repository.InterRepository.get(source.repository, |
982 | + self.interrepo = _mod_repository.InterRepository.get(source.repository, |
983 | target.repository) |
984 | |
985 | @staticmethod |
986 | def _get_branch_formats_to_test(): |
987 | - return [] |
988 | + try: |
989 | + default_format = branch.format_registry.get_default() |
990 | + except AttributeError: |
991 | + default_format = branch.BranchFormat._default_format |
992 | + return [(default_format, GitBranchFormat())] |
993 | |
994 | @classmethod |
995 | def is_compatible(self, source, target): |
996 | @@ -657,56 +794,56 @@ |
997 | |
998 | def _get_new_refs(self, stop_revision=None): |
999 | if stop_revision is None: |
1000 | - stop_revision = self.source.last_revision() |
1001 | + (stop_revno, stop_revision) = self.source.last_revision_info() |
1002 | assert type(stop_revision) is str |
1003 | main_ref = self.target.ref or "refs/heads/master" |
1004 | refs = { main_ref: (None, stop_revision) } |
1005 | for name, revid in self.source.tags.get_tag_dict().iteritems(): |
1006 | if self.source.repository.has_revision(revid): |
1007 | refs[tag_name_to_ref(name)] = (None, revid) |
1008 | - return refs, main_ref |
1009 | + return refs, main_ref, (stop_revno, stop_revision) |
1010 | |
1011 | def pull(self, overwrite=False, stop_revision=None, local=False, |
1012 | - possible_transports=None): |
1013 | - from dulwich.protocol import ZERO_SHA |
1014 | + possible_transports=None, run_hooks=True): |
1015 | result = GitBranchPullResult() |
1016 | result.source_branch = self.source |
1017 | result.target_branch = self.target |
1018 | - new_refs, main_ref = self._get_new_refs(stop_revision) |
1019 | + new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision) |
1020 | def update_refs(old_refs): |
1021 | refs = dict(old_refs) |
1022 | # FIXME: Check for diverged branches |
1023 | refs.update(new_refs) |
1024 | return refs |
1025 | old_refs, new_refs = self.interrepo.fetch_refs(update_refs) |
1026 | - result.old_revid = self.target.lookup_foreign_revision_id( |
1027 | - old_refs.get(main_ref, ZERO_SHA)) |
1028 | - result.new_revid = new_refs[main_ref] |
1029 | + (result.old_revid, old_sha1) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION)) |
1030 | + if result.old_revid is None: |
1031 | + result.old_revid = self.target.lookup_foreign_revision_id(old_sha1) |
1032 | + result.new_revid = new_refs[main_ref][1] |
1033 | return result |
1034 | |
1035 | def push(self, overwrite=False, stop_revision=None, |
1036 | _override_hook_source_branch=None): |
1037 | - from dulwich.protocol import ZERO_SHA |
1038 | result = GitBranchPushResult() |
1039 | result.source_branch = self.source |
1040 | result.target_branch = self.target |
1041 | - new_refs, main_ref = self._get_new_refs(stop_revision) |
1042 | + new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision) |
1043 | def update_refs(old_refs): |
1044 | refs = dict(old_refs) |
1045 | # FIXME: Check for diverged branches |
1046 | refs.update(new_refs) |
1047 | return refs |
1048 | old_refs, new_refs = self.interrepo.fetch_refs(update_refs) |
1049 | - result.old_revid = self.target.lookup_foreign_revision_id( |
1050 | - old_refs.get(main_ref, ZERO_SHA)) |
1051 | - result.new_revid = new_refs[main_ref] |
1052 | + (result.old_revid, old_sha1) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION)) |
1053 | + if result.old_revid is None: |
1054 | + result.old_revid = self.target.lookup_foreign_revision_id(old_sha1) |
1055 | + result.new_revid = new_refs[main_ref][1] |
1056 | return result |
1057 | |
1058 | def lossy_push(self, stop_revision=None): |
1059 | result = GitBranchPushResult() |
1060 | result.source_branch = self.source |
1061 | result.target_branch = self.target |
1062 | - new_refs, main_ref = self._get_new_refs(stop_revision) |
1063 | + new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision) |
1064 | def update_refs(old_refs): |
1065 | refs = dict(old_refs) |
1066 | # FIXME: Check for diverged branches |
1067 | @@ -716,6 +853,7 @@ |
1068 | update_refs) |
1069 | result.old_revid = old_refs.get(self.target.ref, (None, NULL_REVISION))[1] |
1070 | result.new_revid = new_refs[main_ref][1] |
1071 | + (result.new_original_revno, result.new_original_revid) = stop_revinfo |
1072 | return result |
1073 | |
1074 | |
1075 | |
1076 | === modified file 'bzr-receive-pack' |
1077 | --- bzr-receive-pack 2009-09-10 13:13:15 +0000 |
1078 | +++ bzr-receive-pack 2011-03-23 20:12:35 +0000 |
1079 | @@ -1,20 +1,15 @@ |
1080 | #!/usr/bin/env python |
1081 | + |
1082 | import bzrlib |
1083 | from bzrlib.plugin import load_plugins |
1084 | load_plugins() |
1085 | from bzrlib.plugins.git.server import BzrBackend |
1086 | -from dulwich.server import ReceivePackHandler |
1087 | +from dulwich.server import ReceivePackHandler, serve_command |
1088 | import sys, os |
1089 | |
1090 | -if len(sys.argv) != 2 or not os.path.isdir(sys.argv[1]): |
1091 | - print "usage: git-receive-pack <git-dir>" |
1092 | +if len(sys.argv) < 2: |
1093 | + print >>sys.stderr, "usage: %s <git-dir>" % os.path.basename(sys.argv[0]) |
1094 | sys.exit(1) |
1095 | - |
1096 | -backend = BzrBackend(bzrlib.transport.get_transport(sys.argv[1])) |
1097 | - |
1098 | -def write_fn(data): |
1099 | - sys.stdout.write(data) |
1100 | - sys.stdout.flush() |
1101 | - |
1102 | -server = ReceivePackHandler(backend, sys.stdin.read, write_fn) |
1103 | -server.handle() |
1104 | + |
1105 | +backend = BzrBackend(bzrlib.transport.get_transport("/")) |
1106 | +sys.exit(serve_command(ReceivePackHandler, backend=backend)) |
1107 | |
1108 | === modified file 'bzr-upload-pack' |
1109 | --- bzr-upload-pack 2009-09-10 13:13:15 +0000 |
1110 | +++ bzr-upload-pack 2011-03-23 20:12:35 +0000 |
1111 | @@ -1,25 +1,15 @@ |
1112 | #!/usr/bin/env python |
1113 | + |
1114 | import bzrlib |
1115 | from bzrlib.plugin import load_plugins |
1116 | load_plugins () |
1117 | from bzrlib.plugins.git.server import BzrBackend |
1118 | -from dulwich.server import UploadPackHandler |
1119 | -import sys, os, optparse |
1120 | - |
1121 | -parser = optparse.OptionParser(usage="usage: git-upload-pack [--strict] [--timeout=nn] <dir>") |
1122 | -parser.add_option("--strict", action="store_true", dest="strict", default=False) |
1123 | -parser.add_option("--timeout", type="int", dest="timeout", default=-1) |
1124 | -options, args = parser.parse_args() |
1125 | - |
1126 | -if len(args) != 1 or not os.path.isdir(args[0]): |
1127 | - print "usage: " + parser.usage |
1128 | +from dulwich.server import UploadPackHandler, serve_command |
1129 | +import sys, os |
1130 | + |
1131 | +if len(sys.argv) < 2: |
1132 | + print "usage: %s <git-dir>" % os.path.basename(sys.argv[0]) |
1133 | sys.exit(1) |
1134 | |
1135 | -backend = BzrBackend(bzrlib.transport.get_transport(sys.argv[1])) |
1136 | - |
1137 | -def write_fn(data): |
1138 | - sys.stdout.write(data) |
1139 | - sys.stdout.flush() |
1140 | - |
1141 | -server = UploadPackHandler(backend, sys.stdin.read, write_fn) |
1142 | -server.handle() |
1143 | +backend = BzrBackend(bzrlib.transport.get_transport("/")) |
1144 | +sys.exit(serve_command(UploadPackHandler, backend=backend)) |
1145 | |
1146 | === modified file 'cache.py' |
1147 | --- cache.py 2010-06-28 21:30:29 +0000 |
1148 | +++ cache.py 2011-03-23 20:12:35 +0000 |
1149 | @@ -55,6 +55,9 @@ |
1150 | |
1151 | |
1152 | def get_remote_cache_transport(): |
1153 | + """Retrieve the transport to use when accessing (unwritable) remote |
1154 | + repositories. |
1155 | + """ |
1156 | return get_transport(get_cache_dir()) |
1157 | |
1158 | |
1159 | @@ -98,7 +101,9 @@ |
1160 | """Lookup a Git sha in the database. |
1161 | :param sha: Git object sha |
1162 | :return: (type, type_data) with type_data: |
1163 | - revision: revid, tree sha |
1164 | + commit: revid, tree_sha, verifiers |
1165 | + blob: fileid, revid |
1166 | + tree: fileid, revid |
1167 | """ |
1168 | raise NotImplementedError(self.lookup_git_sha) |
1169 | |
1170 | @@ -115,6 +120,11 @@ |
1171 | """ |
1172 | raise NotImplementedError(self.lookup_tree_id) |
1173 | |
1174 | + def lookup_commit(self, revid): |
1175 | + """Retrieve a Git commit SHA by Bazaar revision id. |
1176 | + """ |
1177 | + raise NotImplementedError(self.lookup_commit) |
1178 | + |
1179 | def revids(self): |
1180 | """List the revision ids known.""" |
1181 | raise NotImplementedError(self.revids) |
1182 | @@ -217,6 +227,13 @@ |
1183 | """Base class for objects that can update a bzr-git cache.""" |
1184 | |
1185 | def add_object(self, obj, ie, path): |
1186 | + """Add an object. |
1187 | + |
1188 | + :param obj: Object type ("commit", "blob" or "tree") |
1189 | + :param ie: Inventory entry (for blob/tree) or testament_sha in case |
1190 | + of commit |
1191 | + :param path: Path of the object (optional) |
1192 | + """ |
1193 | raise NotImplementedError(self.add_object) |
1194 | |
1195 | def finish(self): |
1196 | @@ -254,8 +271,8 @@ |
1197 | def add_object(self, obj, ie, path): |
1198 | if obj.type_name == "commit": |
1199 | self._commit = obj |
1200 | - assert ie is None |
1201 | - type_data = (self.revid, self._commit.tree) |
1202 | + assert type(ie) is dict |
1203 | + type_data = (self.revid, self._commit.tree, ie) |
1204 | self.cache.idmap._by_revid[self.revid] = obj.id |
1205 | elif obj.type_name in ("blob", "tree"): |
1206 | if ie is not None: |
1207 | @@ -264,8 +281,7 @@ |
1208 | else: |
1209 | revision = self.revid |
1210 | type_data = (ie.file_id, revision) |
1211 | - self.cache.idmap._by_fileid.setdefault(type_data[1], {})[type_data[0]] =\ |
1212 | - obj.id |
1213 | + self.cache.idmap._by_fileid.setdefault(type_data[1], {})[type_data[0]] = obj.id |
1214 | else: |
1215 | raise AssertionError |
1216 | self.cache.idmap._by_sha[obj.id] = (obj.type_name, type_data) |
1217 | @@ -318,7 +334,8 @@ |
1218 | def add_object(self, obj, ie, path): |
1219 | if obj.type_name == "commit": |
1220 | self._commit = obj |
1221 | - assert ie is None |
1222 | + self._testament3_sha1 = ie["testament3-sha1"] |
1223 | + assert type(ie) is dict |
1224 | elif obj.type_name == "tree": |
1225 | if ie is not None: |
1226 | self._trees.append((obj.id, ie.file_id, self.revid)) |
1227 | @@ -338,8 +355,8 @@ |
1228 | "replace into blobs (sha1, fileid, revid) values (?, ?, ?)", |
1229 | self._blobs) |
1230 | self.db.execute( |
1231 | - "replace into commits (sha1, revid, tree_sha) values (?, ?, ?)", |
1232 | - (self._commit.id, self.revid, self._commit.tree)) |
1233 | + "replace into commits (sha1, revid, tree_sha, testament3_sha1) values (?, ?, ?, ?)", |
1234 | + (self._commit.id, self.revid, self._commit.tree, self._testament3_sha1)) |
1235 | return self._commit |
1236 | |
1237 | |
1238 | @@ -394,10 +411,15 @@ |
1239 | create unique index if not exists trees_sha1 on trees(sha1); |
1240 | create unique index if not exists trees_fileid_revid on trees(fileid, revid); |
1241 | """) |
1242 | + try: |
1243 | + self.db.executescript( |
1244 | + "ALTER TABLE commits ADD testament3_sha1 TEXT;") |
1245 | + except sqlite3.OperationalError: |
1246 | + pass # Column already exists. |
1247 | |
1248 | def __repr__(self): |
1249 | return "%s(%r)" % (self.__class__.__name__, self.path) |
1250 | - |
1251 | + |
1252 | def lookup_commit(self, revid): |
1253 | cursor = self.db.execute("select sha1 from commits where revid = ?", |
1254 | (revid,)) |
1255 | @@ -426,11 +448,13 @@ |
1256 | |
1257 | :param sha: Git object sha |
1258 | :return: (type, type_data) with type_data: |
1259 | - revision: revid, tree sha |
1260 | + commit: revid, tree sha, verifiers |
1261 | + tree: fileid, revid |
1262 | + blob: fileid, revid |
1263 | """ |
1264 | - row = self.db.execute("select revid, tree_sha from commits where sha1 = ?", (sha,)).fetchone() |
1265 | + row = self.db.execute("select revid, tree_sha, testament3_sha1 from commits where sha1 = ?", (sha,)).fetchone() |
1266 | if row is not None: |
1267 | - return ("commit", row) |
1268 | + return ("commit", (row[0], row[1], {"testament3-sha1": row[2]})) |
1269 | row = self.db.execute("select fileid, revid from blobs where sha1 = ?", (sha,)).fetchone() |
1270 | if row is not None: |
1271 | return ("blob", row) |
1272 | @@ -465,9 +489,9 @@ |
1273 | sha = obj.sha().digest() |
1274 | if obj.type_name == "commit": |
1275 | self.db["commit\0" + self.revid] = "\0".join((sha, obj.tree)) |
1276 | - type_data = (self.revid, obj.tree) |
1277 | + assert type(ie) is dict, "was %r" % ie |
1278 | + type_data = (self.revid, obj.tree, ie["testament3-sha1"]) |
1279 | self._commit = obj |
1280 | - assert ie is None |
1281 | elif obj.type_name == "blob": |
1282 | if ie is None: |
1283 | return |
1284 | @@ -489,6 +513,7 @@ |
1285 | |
1286 | TdbBzrGitCache = lambda p: BzrGitCache(TdbGitShaMap(p), None, TdbCacheUpdater) |
1287 | |
1288 | + |
1289 | class TdbGitCacheFormat(BzrGitCacheFormat): |
1290 | """Cache format for tdb-based caches.""" |
1291 | |
1292 | @@ -497,9 +522,10 @@ |
1293 | |
1294 | def open(self, transport): |
1295 | try: |
1296 | - basepath = transport.local_abspath(".") |
1297 | + basepath = transport.local_abspath(".").encode(osutils._fs_enc) |
1298 | except bzrlib.errors.NotLocalUrl: |
1299 | basepath = get_cache_dir() |
1300 | + assert isinstance(basepath, str) |
1301 | try: |
1302 | return TdbBzrGitCache(os.path.join(basepath, "idmap.tdb")) |
1303 | except ImportError: |
1304 | @@ -528,6 +554,7 @@ |
1305 | if path is None: |
1306 | self.db = {} |
1307 | else: |
1308 | + assert isinstance(path, str) |
1309 | if not mapdbs().has_key(path): |
1310 | mapdbs()[path] = tdb.Tdb(path, self.TDB_HASH_SIZE, tdb.DEFAULT, |
1311 | os.O_RDWR|os.O_CREAT) |
1312 | @@ -561,18 +588,26 @@ |
1313 | |
1314 | def lookup_blob_id(self, fileid, revision): |
1315 | return sha_to_hex(self.db["\0".join(("blob", fileid, revision))]) |
1316 | - |
1317 | + |
1318 | def lookup_git_sha(self, sha): |
1319 | """Lookup a Git sha in the database. |
1320 | |
1321 | :param sha: Git object sha |
1322 | :return: (type, type_data) with type_data: |
1323 | - revision: revid, tree sha |
1324 | + commit: revid, tree sha |
1325 | + blob: fileid, revid |
1326 | + tree: fileid, revid |
1327 | """ |
1328 | if len(sha) == 40: |
1329 | sha = hex_to_sha(sha) |
1330 | data = self.db["git\0" + sha].split("\0") |
1331 | - return (data[0], (data[1], data[2])) |
1332 | + if data[0] == "commit": |
1333 | + if len(data) == 3: |
1334 | + return (data[0], (data[1], data[2], {})) |
1335 | + else: |
1336 | + return (data[0], (data[1], data[2], {"testament3-sha1": data[3]})) |
1337 | + else: |
1338 | + return (data[0], tuple(data[1:])) |
1339 | |
1340 | def missing_revisions(self, revids): |
1341 | ret = set() |
1342 | @@ -640,9 +675,9 @@ |
1343 | def add_object(self, obj, ie, path): |
1344 | if obj.type_name == "commit": |
1345 | self._commit = obj |
1346 | - assert ie is None |
1347 | + assert type(ie) is dict |
1348 | self.cache.idmap._add_git_sha(obj.id, "commit", |
1349 | - (self.revid, obj.tree)) |
1350 | + (self.revid, obj.tree, ie)) |
1351 | self.cache.idmap._add_node(("commit", self.revid, "X"), |
1352 | " ".join((obj.id, obj.tree))) |
1353 | self._cache_objs.add((obj, path)) |
1354 | @@ -808,8 +843,11 @@ |
1355 | def _add_git_sha(self, hexsha, type, type_data): |
1356 | if hexsha is not None: |
1357 | self._name.update(hexsha) |
1358 | - self._add_node(("git", hexsha, "X"), |
1359 | - " ".join((type, type_data[0], type_data[1]))) |
1360 | + if type == "commit": |
1361 | + td = (type_data[0], type_data[1], type_data[2]["testament3-sha1"]) |
1362 | + else: |
1363 | + td = type_data |
1364 | + self._add_node(("git", hexsha, "X"), " ".join((type,) + td)) |
1365 | else: |
1366 | # This object is not represented in Git - perhaps an empty |
1367 | # directory? |
1368 | @@ -821,8 +859,11 @@ |
1369 | def lookup_git_sha(self, sha): |
1370 | if len(sha) == 20: |
1371 | sha = sha_to_hex(sha) |
1372 | - data = self._get_entry(("git", sha, "X")).split(" ", 2) |
1373 | - return (data[0], (data[1], data[2])) |
1374 | + data = self._get_entry(("git", sha, "X")).split(" ", 3) |
1375 | + if data[0] == "commit": |
1376 | + return ("commit", (data[1], data[2], {"testament3-sha1": data[3]})) |
1377 | + else: |
1378 | + return (data[0], tuple(data[1:])) |
1379 | |
1380 | def revids(self): |
1381 | """List the revision ids known.""" |
1382 | |
1383 | === modified file 'commands.py' |
1384 | --- commands.py 2010-08-08 16:01:26 +0000 |
1385 | +++ commands.py 2011-03-23 20:12:35 +0000 |
1386 | @@ -37,10 +37,12 @@ |
1387 | takes_args = ["src_location", "dest_location?"] |
1388 | |
1389 | def run(self, src_location, dest_location=None): |
1390 | + from collections import defaultdict |
1391 | import os |
1392 | from bzrlib import ( |
1393 | + controldir, |
1394 | + trace, |
1395 | ui, |
1396 | - urlutils, |
1397 | ) |
1398 | from bzrlib.bzrdir import ( |
1399 | BzrDir, |
1400 | @@ -54,22 +56,29 @@ |
1401 | InterRepository, |
1402 | Repository, |
1403 | ) |
1404 | + from bzrlib.transport import get_transport |
1405 | from bzrlib.plugins.git.branch import ( |
1406 | GitBranch, |
1407 | extract_tags, |
1408 | ) |
1409 | + from bzrlib.plugins.git.refs import ref_to_branch_name |
1410 | from bzrlib.plugins.git.repository import GitRepository |
1411 | |
1412 | + dest_format = controldir.ControlDirFormat.get_default_format() |
1413 | + |
1414 | if dest_location is None: |
1415 | dest_location = os.path.basename(src_location.rstrip("/\\")) |
1416 | |
1417 | + dest_transport = get_transport(dest_location) |
1418 | + |
1419 | source_repo = Repository.open(src_location) |
1420 | if not isinstance(source_repo, GitRepository): |
1421 | raise BzrCommandError("%r is not a git repository" % src_location) |
1422 | try: |
1423 | - target_bzrdir = BzrDir.open(dest_location) |
1424 | + target_bzrdir = BzrDir.open_from_transport(dest_transport) |
1425 | except NotBranchError: |
1426 | - target_bzrdir = BzrDir.create(dest_location) |
1427 | + target_bzrdir = dest_format.initialize_on_transport_ex( |
1428 | + dest_transport, shared_repo=True)[1] |
1429 | try: |
1430 | target_repo = target_bzrdir.find_repository() |
1431 | except NoRepositoryPresent: |
1432 | @@ -81,23 +90,28 @@ |
1433 | interrepo = InterRepository.get(source_repo, target_repo) |
1434 | mapping = source_repo.get_mapping() |
1435 | refs = interrepo.fetch() |
1436 | + unpeeled_tags = defaultdict(set) |
1437 | tags = {} |
1438 | - for k, v in extract_tags(refs).iteritems(): |
1439 | - tags[k] = mapping.revision_id_foreign_to_bzr(v) |
1440 | + for k, (peeled, unpeeled) in extract_tags(refs).iteritems(): |
1441 | + tags[k] = mapping.revision_id_foreign_to_bzr(peeled) |
1442 | + if unpeeled is not None: |
1443 | + unpeeled_tags[peeled].add(unpeeled) |
1444 | + # FIXME: Store unpeeled tag map |
1445 | pb = ui.ui_factory.nested_progress_bar() |
1446 | try: |
1447 | for i, (name, ref) in enumerate(refs.iteritems()): |
1448 | - if name.startswith("refs/tags/"): |
1449 | + try: |
1450 | + ref_to_branch_name(name) |
1451 | + except ValueError: |
1452 | + # Not a branch, ignore |
1453 | continue |
1454 | pb.update("creating branches", i, len(refs)) |
1455 | - head_loc = os.path.join(dest_location, name) |
1456 | + head_transport = dest_transport.clone(name) |
1457 | try: |
1458 | - head_bzrdir = BzrDir.open(head_loc) |
1459 | + head_bzrdir = BzrDir.open_from_transport(head_transport) |
1460 | except NotBranchError: |
1461 | - parent_path = urlutils.dirname(head_loc) |
1462 | - if not os.path.isdir(parent_path): |
1463 | - os.makedirs(parent_path) |
1464 | - head_bzrdir = BzrDir.create(head_loc) |
1465 | + head_bzrdir = dest_format.initialize_on_transport_ex( |
1466 | + head_transport, create_prefix=True)[1] |
1467 | try: |
1468 | head_branch = head_bzrdir.open_branch() |
1469 | except NotBranchError: |
1470 | @@ -111,6 +125,9 @@ |
1471 | source_branch.tags.merge_to(head_branch.tags) |
1472 | finally: |
1473 | pb.finished() |
1474 | + trace.note("Use 'bzr checkout' to create a working tree in " |
1475 | + "the newly created branches.") |
1476 | + |
1477 | |
1478 | |
1479 | class cmd_git_object(Command): |
1480 | @@ -204,27 +221,49 @@ |
1481 | "bzr pull". |
1482 | """ |
1483 | |
1484 | + takes_options = [ |
1485 | + Option('signoff', short_name='s', help='Add a Signed-off-by line.'), |
1486 | + 'force'] |
1487 | takes_args = ["patches*"] |
1488 | |
1489 | - def _apply_patch(self, wt, f): |
1490 | + def _apply_patch(self, wt, f, signoff): |
1491 | + """Apply a patch. |
1492 | + |
1493 | + :param wt: A Bazaar working tree object. |
1494 | + :param f: Patch file to read. |
1495 | + :param signoff: Add Signed-Off-By flag. |
1496 | + """ |
1497 | + from bzrlib.errors import BzrCommandError |
1498 | from dulwich.patch import git_am_patch_split |
1499 | + import subprocess |
1500 | (c, diff, version) = git_am_patch_split(f) |
1501 | - # FIXME: Process diff |
1502 | - wt.commit(committer=c.committer, |
1503 | - message=c.message) |
1504 | + # FIXME: Cope with git-specific bits in patch |
1505 | + p = subprocess.Popen(["patch", "-p1"], stdin=subprocess.PIPE, cwd=wt.basedir) |
1506 | + p.communicate(diff) |
1507 | + exitcode = p.wait() |
1508 | + if exitcode != 0: |
1509 | + raise BzrCommandError("error running patch") |
1510 | + message = c.message |
1511 | + if signoff: |
1512 | + signed_off_by = wt.branch.get_config().username() |
1513 | + message += "Signed-off-by: %s\n" % signed_off_by.encode('utf-8') |
1514 | + wt.commit(authors=[c.author], message=message) |
1515 | |
1516 | - def run(self, patches_list=None): |
1517 | + def run(self, patches_list=None, signoff=False, force=False): |
1518 | + from bzrlib.errors import UncommittedChanges |
1519 | from bzrlib.workingtree import WorkingTree |
1520 | if patches_list is None: |
1521 | patches_list = [] |
1522 | - |
1523 | + |
1524 | tree, _ = WorkingTree.open_containing(".") |
1525 | + if tree.basis_tree().changes_from(tree).has_changed() and not force: |
1526 | + raise UncommittedChanges(tree) |
1527 | tree.lock_write() |
1528 | try: |
1529 | for patch in patches_list: |
1530 | f = open(patch, 'r') |
1531 | try: |
1532 | - self._apply_patch(tree, f) |
1533 | + self._apply_patch(tree, f, signoff=signoff) |
1534 | finally: |
1535 | f.close() |
1536 | finally: |
1537 | |
1538 | === modified file 'commit.py' |
1539 | --- commit.py 2010-08-05 23:44:17 +0000 |
1540 | +++ commit.py 2011-03-23 20:12:35 +0000 |
1541 | @@ -21,9 +21,11 @@ |
1542 | from dulwich.index import ( |
1543 | commit_tree, |
1544 | ) |
1545 | -import os |
1546 | import stat |
1547 | |
1548 | +from bzrlib.errors import ( |
1549 | + RootMissing, |
1550 | + ) |
1551 | from bzrlib.repository import ( |
1552 | CommitBuilder, |
1553 | ) |
1554 | @@ -41,6 +43,9 @@ |
1555 | |
1556 | |
1557 | class GitCommitBuilder(CommitBuilder): |
1558 | + """Commit builder for Git repositories.""" |
1559 | + |
1560 | + supports_record_entry_contents = False |
1561 | |
1562 | def __init__(self, *args, **kwargs): |
1563 | super(GitCommitBuilder, self).__init__(*args, **kwargs) |
1564 | @@ -70,11 +75,14 @@ |
1565 | blob = Blob() |
1566 | blob.data = workingtree.get_file_text(file_id, path) |
1567 | return blob.id |
1568 | + seen_root = False |
1569 | for (file_id, path, changed_content, versioned, parent, name, kind, |
1570 | executable) in iter_changes: |
1571 | if kind[1] in ("directory",): |
1572 | if kind[0] in ("file", "symlink"): |
1573 | self.record_delete(path[0], file_id) |
1574 | + if path[1] == "": |
1575 | + seen_root = True |
1576 | continue |
1577 | if path[1] is None: |
1578 | self.record_delete(path[0], file_id) |
1579 | @@ -87,7 +95,7 @@ |
1580 | sha = link_sha1(path[1], file_id) |
1581 | elif kind[1] == "tree-reference": |
1582 | mode = S_IFGITLINK |
1583 | - sha = "FIXME" |
1584 | + sha = "FIXME" # FIXME |
1585 | else: |
1586 | raise AssertionError("Unknown kind %r" % kind[1]) |
1587 | if executable[1]: |
1588 | @@ -95,10 +103,14 @@ |
1589 | self._any_changes = True |
1590 | self._blobs[path[1].encode("utf-8")] = (mode, sha) |
1591 | file_sha1 = workingtree.get_file_sha1(file_id, path[1]) |
1592 | - yield file_id, path[1], (file_sha1, os.lstat(workingtree.abspath(path[1]))) |
1593 | + _, st = workingtree.get_file_with_stat(file_id, path[1]) |
1594 | + yield file_id, path[1], (file_sha1, st) |
1595 | + if not seen_root and len(self.parents) == 0: |
1596 | + raise RootMissing() |
1597 | # Fill in entries that were not changed |
1598 | basis_tree = workingtree.basis_tree() |
1599 | - assert basis_tree.get_revision_id() == basis_revid |
1600 | + assert basis_tree.get_revision_id() == basis_revid, "expected %r == %r" % ( |
1601 | + basis_tree.get_revision_id(), basis_revid) |
1602 | for path, entry in basis_tree.iter_entries_by_dir(): |
1603 | if entry.kind not in ("file", "symlink"): |
1604 | continue |
1605 | @@ -109,6 +121,7 @@ |
1606 | else: |
1607 | blob.data = basis_tree.get_file_text(entry.file_id) |
1608 | self._blobs[path.encode("utf-8")] = (entry_mode(entry), blob.id) |
1609 | + self.new_inventory = None |
1610 | |
1611 | def finish_inventory(self): |
1612 | # eliminate blobs that were removed |
1613 | @@ -132,6 +145,7 @@ |
1614 | c.encoding = 'utf-8' |
1615 | c.message = message.encode("utf-8") |
1616 | self.store.add_object(c) |
1617 | + assert len(c.id) == 40 |
1618 | self._new_revision_id = self.repository.get_mapping().revision_id_foreign_to_bzr(c.id) |
1619 | self.repository.commit_write_group() |
1620 | return self._new_revision_id |
1621 | |
1622 | === modified file 'dir.py' |
1623 | --- dir.py 2010-08-06 10:51:21 +0000 |
1624 | +++ dir.py 2011-03-23 20:12:35 +0000 |
1625 | @@ -15,26 +15,33 @@ |
1626 | # along with this program; if not, write to the Free Software |
1627 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
1628 | |
1629 | -"""An adapter between a Git control dir and a Bazaar BzrDir.""" |
1630 | +"""An adapter between a Git control dir and a Bazaar ControlDir.""" |
1631 | |
1632 | from bzrlib import ( |
1633 | - bzrdir, |
1634 | errors as bzr_errors, |
1635 | lockable_files, |
1636 | + trace, |
1637 | urlutils, |
1638 | version_info as bzrlib_version, |
1639 | ) |
1640 | +from bzrlib.bzrdir import CreateRepository |
1641 | +from bzrlib.transport import do_catching_redirections |
1642 | |
1643 | LockWarner = getattr(lockable_files, "_LockWarner", None) |
1644 | |
1645 | -from bzrlib.plugins.git import ( |
1646 | - LocalGitBzrDirFormat, |
1647 | +from bzrlib.controldir import ( |
1648 | + ControlDir, |
1649 | + ControlDirFormat, |
1650 | + format_registry, |
1651 | ) |
1652 | |
1653 | |
1654 | class GitLock(object): |
1655 | """A lock that thunks through to Git.""" |
1656 | |
1657 | + def __init__(self): |
1658 | + self.lock_name = "git lock" |
1659 | + |
1660 | def lock_write(self, token=None): |
1661 | pass |
1662 | |
1663 | @@ -69,7 +76,33 @@ |
1664 | self._lock_warner = LockWarner(repr(self)) |
1665 | |
1666 | |
1667 | -class GitDir(bzrdir.BzrDir): |
1668 | +class GitDirConfig(object): |
1669 | + |
1670 | + def get_default_stack_on(self): |
1671 | + return None |
1672 | + |
1673 | + def set_default_stack_on(self, value): |
1674 | + raise bzr_errors.BzrError("Cannot set configuration") |
1675 | + |
1676 | + |
1677 | +class GitControlDirFormat(ControlDirFormat): |
1678 | + |
1679 | + _lock_class = lockable_files.TransportLock |
1680 | + |
1681 | + colocated_branches = True |
1682 | + fixed_components = True |
1683 | + |
1684 | + def __eq__(self, other): |
1685 | + return type(self) == type(other) |
1686 | + |
1687 | + def is_supported(self): |
1688 | + return True |
1689 | + |
1690 | + def network_name(self): |
1691 | + return "git" |
1692 | + |
1693 | + |
1694 | +class GitDir(ControlDir): |
1695 | """An adapter to the '.git' dir used by git.""" |
1696 | |
1697 | def is_supported(self): |
1698 | @@ -78,21 +111,171 @@ |
1699 | def can_convert_format(self): |
1700 | return False |
1701 | |
1702 | + def break_lock(self): |
1703 | + pass |
1704 | + |
1705 | def cloning_metadir(self, stacked=False): |
1706 | - return bzrdir.format_registry.make_bzrdir("default") |
1707 | + return format_registry.make_bzrdir("default") |
1708 | |
1709 | def _branch_name_to_ref(self, name): |
1710 | raise NotImplementedError(self._branch_name_to_ref) |
1711 | |
1712 | - if bzrlib_version >= (2, 2): |
1713 | - def open_branch(self, name=None, unsupported=False, |
1714 | - ignore_fallbacks=None): |
1715 | - return self._open_branch(name=name, |
1716 | - ignore_fallbacks=ignore_fallbacks, unsupported=unsupported) |
1717 | - else: |
1718 | - def open_branch(self, ignore_fallbacks=None, unsupported=False): |
1719 | - return self._open_branch(name=None, |
1720 | - ignore_fallbacks=ignore_fallbacks, unsupported=unsupported) |
1721 | + def get_config(self): |
1722 | + return GitDirConfig() |
1723 | + |
1724 | + def sprout(self, url, revision_id=None, force_new_repo=False, |
1725 | + recurse='down', possible_transports=None, |
1726 | + accelerator_tree=None, hardlink=False, stacked=False, |
1727 | + source_branch=None, create_tree_if_local=True): |
1728 | + from bzrlib.repository import InterRepository |
1729 | + from bzrlib.transport.local import LocalTransport |
1730 | + from bzrlib.transport import get_transport |
1731 | + target_transport = get_transport(url, possible_transports) |
1732 | + target_transport.ensure_base() |
1733 | + cloning_format = self.cloning_metadir() |
1734 | + # Create/update the result branch |
1735 | + result = cloning_format.initialize_on_transport(target_transport) |
1736 | + source_branch = self.open_branch() |
1737 | + source_repository = self.find_repository() |
1738 | + try: |
1739 | + result_repo = result.find_repository() |
1740 | + except bzr_errors.NoRepositoryPresent: |
1741 | + result_repo = result.create_repository() |
1742 | + target_is_empty = True |
1743 | + else: |
1744 | + target_is_empty = None # Unknown |
1745 | + if stacked: |
1746 | + raise bzr_errors.IncompatibleRepositories(source_repository, result_repo) |
1747 | + interrepo = InterRepository.get(source_repository, result_repo) |
1748 | + |
1749 | + if revision_id is not None: |
1750 | + determine_wants = interrepo.get_determine_wants_revids( |
1751 | + [revision_id], include_tags=True) |
1752 | + else: |
1753 | + determine_wants = interrepo.determine_wants_all |
1754 | + interrepo.fetch_objects(determine_wants=determine_wants, |
1755 | + mapping=source_branch.mapping) |
1756 | + result_branch = source_branch.sprout(result, |
1757 | + revision_id=revision_id, repository=result_repo) |
1758 | + if (create_tree_if_local and isinstance(target_transport, LocalTransport) |
1759 | + and (result_repo is None or result_repo.make_working_trees())): |
1760 | + wt = result.create_workingtree(accelerator_tree=accelerator_tree, |
1761 | + hardlink=hardlink, from_branch=result_branch) |
1762 | + wt.lock_write() |
1763 | + try: |
1764 | + if wt.path2id('') is None: |
1765 | + try: |
1766 | + wt.set_root_id(self.open_workingtree.get_root_id()) |
1767 | + except bzr_errors.NoWorkingTree: |
1768 | + pass |
1769 | + finally: |
1770 | + wt.unlock() |
1771 | + return result |
1772 | + |
1773 | + def clone_on_transport(self, transport, revision_id=None, |
1774 | + force_new_repo=False, preserve_stacking=False, stacked_on=None, |
1775 | + create_prefix=False, use_existing_dir=True, no_tree=False): |
1776 | + from dulwich.protocol import ZERO_SHA |
1777 | + """See ControlDir.clone_on_transport.""" |
1778 | + if no_tree: |
1779 | + format = BareLocalGitControlDirFormat() |
1780 | + else: |
1781 | + format = LocalGitControlDirFormat() |
1782 | + (target_repo, target_controldir, stacking, repo_policy) = format.initialize_on_transport_ex(transport, use_existing_dir=use_existing_dir, create_prefix=create_prefix, force_new_repo=force_new_repo) |
1783 | + target_git_repo = target_repo._git |
1784 | + source_repo = self.open_repository() |
1785 | + source_git_repo = source_repo._git |
1786 | + if revision_id is not None: |
1787 | + determine_wants = source_repo.determine_wants_revid_and_tags(revision_id) |
1788 | + else: |
1789 | + determine_wants = target_git_repo.object_store.determine_wants_all |
1790 | + refs = source_git_repo.fetch(target_git_repo, determine_wants) |
1791 | + for name, val in refs.iteritems(): |
1792 | + target_git_repo.refs[name] = val |
1793 | + lockfiles = GitLockableFiles(transport, GitLock()) |
1794 | + return self.__class__(transport, lockfiles, target_git_repo, format) |
1795 | + |
1796 | + def find_repository(self): |
1797 | + """Find the repository that should be used. |
1798 | + |
1799 | + This does not require a branch as we use it to find the repo for |
1800 | + new branches as well as to hook existing branches up to their |
1801 | + repository. |
1802 | + """ |
1803 | + return self.open_repository() |
1804 | + |
1805 | + |
1806 | +class LocalGitControlDirFormat(GitControlDirFormat): |
1807 | + """The .git directory control format.""" |
1808 | + |
1809 | + bare = False |
1810 | + |
1811 | + @classmethod |
1812 | + def _known_formats(self): |
1813 | + return set([LocalGitControlDirFormat()]) |
1814 | + |
1815 | + @property |
1816 | + def repository_format(self): |
1817 | + from bzrlib.plugins.git.repository import GitRepositoryFormat |
1818 | + return GitRepositoryFormat() |
1819 | + |
1820 | + def get_branch_format(self): |
1821 | + from bzrlib.plugins.git.branch import GitBranchFormat |
1822 | + return GitBranchFormat() |
1823 | + |
1824 | + def open(self, transport, _found=None): |
1825 | + """Open this directory. |
1826 | + |
1827 | + """ |
1828 | + from bzrlib.plugins.git.transportgit import TransportRepo |
1829 | + gitrepo = TransportRepo(transport) |
1830 | + lockfiles = GitLockableFiles(transport, GitLock()) |
1831 | + return LocalGitDir(transport, lockfiles, gitrepo, self) |
1832 | + |
1833 | + def get_format_description(self): |
1834 | + return "Local Git Repository" |
1835 | + |
1836 | + def initialize_on_transport(self, transport): |
1837 | + from bzrlib.plugins.git.transportgit import TransportRepo |
1838 | + TransportRepo.init(transport, bare=self.bare) |
1839 | + return self.open(transport) |
1840 | + |
1841 | + def initialize_on_transport_ex(self, transport, use_existing_dir=False, |
1842 | + create_prefix=False, force_new_repo=False, stacked_on=None, |
1843 | + stack_on_pwd=None, repo_format_name=None, make_working_trees=None, |
1844 | + shared_repo=False, vfs_only=False): |
1845 | + def make_directory(transport): |
1846 | + transport.mkdir('.') |
1847 | + return transport |
1848 | + def redirected(transport, e, redirection_notice): |
1849 | + trace.note(redirection_notice) |
1850 | + return transport._redirected_to(e.source, e.target) |
1851 | + try: |
1852 | + transport = do_catching_redirections(make_directory, transport, |
1853 | + redirected) |
1854 | + except bzr_errors.FileExists: |
1855 | + if not use_existing_dir: |
1856 | + raise |
1857 | + except bzr_errors.NoSuchFile: |
1858 | + if not create_prefix: |
1859 | + raise |
1860 | + transport.create_prefix() |
1861 | + controldir = self.initialize_on_transport(transport) |
1862 | + repository = controldir.open_repository() |
1863 | + repository.lock_write() |
1864 | + return (repository, controldir, False, CreateRepository(controldir)) |
1865 | + |
1866 | + def is_supported(self): |
1867 | + return True |
1868 | + |
1869 | + |
1870 | +class BareLocalGitControlDirFormat(LocalGitControlDirFormat): |
1871 | + |
1872 | + bare = True |
1873 | + supports_workingtrees = False |
1874 | + |
1875 | + def get_format_description(self): |
1876 | + return "Local Git Repository (bare)" |
1877 | |
1878 | |
1879 | class LocalGitDir(GitDir): |
1880 | @@ -104,9 +287,18 @@ |
1881 | |
1882 | _gitrepository_class = property(_get_gitrepository_class) |
1883 | |
1884 | + @property |
1885 | + def user_transport(self): |
1886 | + return self.root_transport |
1887 | + |
1888 | + @property |
1889 | + def control_transport(self): |
1890 | + return self.transport |
1891 | + |
1892 | def __init__(self, transport, lockfiles, gitrepo, format): |
1893 | self._format = format |
1894 | self.root_transport = transport |
1895 | + self._mode_check_done = False |
1896 | self._git = gitrepo |
1897 | if gitrepo.bare: |
1898 | self.transport = transport |
1899 | @@ -126,30 +318,30 @@ |
1900 | return ref |
1901 | |
1902 | def is_control_filename(self, filename): |
1903 | - return filename == '.git' or filename.startswith('.git/') |
1904 | + return (filename == '.git' or filename.startswith('.git/')) |
1905 | |
1906 | def get_branch_transport(self, branch_format, name=None): |
1907 | if branch_format is None: |
1908 | return self.transport |
1909 | - if isinstance(branch_format, LocalGitBzrDirFormat): |
1910 | + if isinstance(branch_format, LocalGitControlDirFormat): |
1911 | return self.transport |
1912 | raise bzr_errors.IncompatibleFormat(branch_format, self._format) |
1913 | |
1914 | def get_repository_transport(self, format): |
1915 | if format is None: |
1916 | return self.transport |
1917 | - if isinstance(format, LocalGitBzrDirFormat): |
1918 | + if isinstance(format, LocalGitControlDirFormat): |
1919 | return self.transport |
1920 | raise bzr_errors.IncompatibleFormat(format, self._format) |
1921 | |
1922 | def get_workingtree_transport(self, format): |
1923 | if format is None: |
1924 | return self.transport |
1925 | - if isinstance(format, LocalGitBzrDirFormat): |
1926 | + if isinstance(format, LocalGitControlDirFormat): |
1927 | return self.transport |
1928 | raise bzr_errors.IncompatibleFormat(format, self._format) |
1929 | |
1930 | - def _open_branch(self, name=None, ignore_fallbacks=None, unsupported=False): |
1931 | + def open_branch(self, name=None, unsupported=False, ignore_fallbacks=None): |
1932 | """'create' a branch for this dir.""" |
1933 | repo = self.open_repository() |
1934 | from bzrlib.plugins.git.branch import LocalGitBranch |
1935 | @@ -179,7 +371,7 @@ |
1936 | ret.append(self.open_branch(name=name)) |
1937 | return ret |
1938 | |
1939 | - def open_repository(self, shared=False): |
1940 | + def open_repository(self): |
1941 | """'open' a repository for this dir.""" |
1942 | return self._gitrepository_class(self, self._lockfiles) |
1943 | |
1944 | @@ -203,9 +395,12 @@ |
1945 | raise bzr_errors.NoWorkingTree(loc) |
1946 | |
1947 | def create_repository(self, shared=False): |
1948 | + from bzrlib.plugins.git.repository import GitRepositoryFormat |
1949 | + if shared: |
1950 | + raise bzr_errors.IncompatibleFormat(GitRepositoryFormat(), self._format) |
1951 | return self.open_repository() |
1952 | |
1953 | - def create_branch(self, name=None): |
1954 | + def create_branch(self, name=None, repository=None): |
1955 | refname = self._branch_name_to_ref(name) |
1956 | from dulwich.protocol import ZERO_SHA |
1957 | self._git.refs[refname or "HEAD"] = ZERO_SHA |
1958 | @@ -222,7 +417,7 @@ |
1959 | def create_workingtree(self, revision_id=None, from_branch=None, |
1960 | accelerator_tree=None, hardlink=False): |
1961 | if self._git.bare: |
1962 | - raise bzr_errors.BzrError("Can't create working tree in a bare repo") |
1963 | + raise bzr_errors.UnsupportedOperation(self.create_workingtree, self) |
1964 | from dulwich.index import write_index |
1965 | from dulwich.pack import SHA1Writer |
1966 | f = open(self.transport.local_abspath("index"), 'w+') |
1967 | @@ -232,3 +427,51 @@ |
1968 | finally: |
1969 | f.close() |
1970 | return self.open_workingtree() |
1971 | + |
1972 | + def _find_or_create_repository(self, force_new_repo=None): |
1973 | + return self.create_repository(shared=False) |
1974 | + |
1975 | + def _find_creation_modes(self): |
1976 | + """Determine the appropriate modes for files and directories. |
1977 | + |
1978 | + They're always set to be consistent with the base directory, |
1979 | + assuming that this transport allows setting modes. |
1980 | + """ |
1981 | + # TODO: Do we need or want an option (maybe a config setting) to turn |
1982 | + # this off or override it for particular locations? -- mbp 20080512 |
1983 | + if self._mode_check_done: |
1984 | + return |
1985 | + self._mode_check_done = True |
1986 | + try: |
1987 | + st = self.transport.stat('.') |
1988 | + except bzr_errors.TransportNotPossible: |
1989 | + self._dir_mode = None |
1990 | + self._file_mode = None |
1991 | + else: |
1992 | + # Check the directory mode, but also make sure the created |
1993 | + # directories and files are read-write for this user. This is |
1994 | + # mostly a workaround for filesystems which lie about being able to |
1995 | + # write to a directory (cygwin & win32) |
1996 | + if (st.st_mode & 07777 == 00000): |
1997 | + # FTP allows stat but does not return dir/file modes |
1998 | + self._dir_mode = None |
1999 | + self._file_mode = None |
2000 | + else: |
2001 | + self._dir_mode = (st.st_mode & 07777) | 00700 |
2002 | + # Remove the sticky and execute bits for files |
2003 | + self._file_mode = self._dir_mode & ~07111 |
2004 | + |
2005 | + def _get_file_mode(self): |
2006 | + """Return Unix mode for newly created files, or None. |
2007 | + """ |
2008 | + if not self._mode_check_done: |
2009 | + self._find_creation_modes() |
2010 | + return self._file_mode |
2011 | + |
2012 | + def _get_dir_mode(self): |
2013 | + """Return Unix mode for newly created directories, or None. |
2014 | + """ |
2015 | + if not self._mode_check_done: |
2016 | + self._find_creation_modes() |
2017 | + return self._dir_mode |
2018 | + |
2019 | |
2020 | === modified file 'fetch.py' |
2021 | --- fetch.py 2010-08-08 17:40:33 +0000 |
2022 | +++ fetch.py 2011-03-23 20:12:35 +0000 |
2023 | @@ -22,6 +22,7 @@ |
2024 | ) |
2025 | from dulwich.object_store import ( |
2026 | tree_lookup_path, |
2027 | + ZERO_SHA, |
2028 | ) |
2029 | from itertools import ( |
2030 | imap, |
2031 | @@ -56,6 +57,9 @@ |
2032 | from bzrlib.revisiontree import ( |
2033 | RevisionTree, |
2034 | ) |
2035 | +from bzrlib.testament import ( |
2036 | + StrictTestament3, |
2037 | + ) |
2038 | from bzrlib.tsort import ( |
2039 | topo_sort, |
2040 | ) |
2041 | @@ -74,6 +78,7 @@ |
2042 | LRUTreeCache, |
2043 | _tree_to_objects, |
2044 | ) |
2045 | +from bzrlib.plugins.git.refs import extract_tags |
2046 | from bzrlib.plugins.git.remote import ( |
2047 | RemoteGitRepository, |
2048 | ) |
2049 | @@ -257,7 +262,7 @@ |
2050 | # Remember for next time |
2051 | existing_children = set() |
2052 | child_modes = {} |
2053 | - for child_mode, name, child_hexsha in tree.entries(): |
2054 | + for name, child_mode, child_hexsha in tree.iteritems(): |
2055 | existing_children.add(name) |
2056 | child_path = posixpath.join(path, name) |
2057 | if type(base_tree) is Tree: |
2058 | @@ -272,7 +277,7 @@ |
2059 | if stat.S_ISDIR(child_mode): |
2060 | subinvdelta, grandchildmodes = import_git_tree(texts, mapping, |
2061 | child_path, name, (child_base_hexsha, child_hexsha), base_inv, |
2062 | - file_id, revision_id, parent_invs, lookup_object, |
2063 | + file_id, revision_id, parent_invs, lookup_object, |
2064 | (child_base_mode, child_mode), store_updater, lookup_file_id, |
2065 | allow_submodules=allow_submodules) |
2066 | elif S_ISGITLINK(child_mode): # submodule |
2067 | @@ -302,13 +307,14 @@ |
2068 | |
2069 | |
2070 | def verify_commit_reconstruction(target_git_object_retriever, lookup_object, |
2071 | - o, rev, ret_tree, parent_trees, mapping, unusual_modes): |
2072 | + o, rev, ret_tree, parent_trees, mapping, unusual_modes, verifiers): |
2073 | new_unusual_modes = mapping.export_unusual_file_modes(rev) |
2074 | if new_unusual_modes != unusual_modes: |
2075 | raise AssertionError("unusual modes don't match: %r != %r" % ( |
2076 | unusual_modes, new_unusual_modes)) |
2077 | # Verify that we can reconstruct the commit properly |
2078 | - rec_o = target_git_object_retriever._reconstruct_commit(rev, o.tree, True) |
2079 | + rec_o = target_git_object_retriever._reconstruct_commit(rev, o.tree, True, |
2080 | + verifiers) |
2081 | if rec_o != o: |
2082 | raise AssertionError("Reconstructed commit differs: %r != %r" % ( |
2083 | rec_o, o)) |
2084 | @@ -341,7 +347,7 @@ |
2085 | def import_git_commit(repo, mapping, head, lookup_object, |
2086 | target_git_object_retriever, trees_cache): |
2087 | o = lookup_object(head) |
2088 | - rev = mapping.import_commit(o, |
2089 | + rev, roundtrip_revid, verifiers = mapping.import_commit(o, |
2090 | lambda x: target_git_object_retriever.lookup_git_sha(x)[1][0]) |
2091 | # We have to do this here, since we have to walk the tree and |
2092 | # we need to make sure to import the blobs / trees with the right |
2093 | @@ -356,7 +362,6 @@ |
2094 | base_tree = lookup_object(o.parents[0]).tree |
2095 | base_mode = stat.S_IFDIR |
2096 | store_updater = target_git_object_retriever._get_updater(rev) |
2097 | - store_updater.add_object(o, None, None) |
2098 | fileid_map = mapping.get_fileid_map(lookup_object, o.tree) |
2099 | inv_delta, unusual_modes = import_git_tree(repo.texts, |
2100 | mapping, "", "", (base_tree, o.tree), base_inv, |
2101 | @@ -364,7 +369,6 @@ |
2102 | lookup_object, (base_mode, stat.S_IFDIR), store_updater, |
2103 | fileid_map.lookup_file_id, |
2104 | allow_submodules=getattr(repo._format, "supports_tree_reference", False)) |
2105 | - store_updater.finish() |
2106 | if unusual_modes != {}: |
2107 | for path, mode in unusual_modes.iteritems(): |
2108 | warn_unusual_mode(rev.foreign_revid, path, mode) |
2109 | @@ -376,13 +380,26 @@ |
2110 | base_inv = None |
2111 | rev.inventory_sha1, inv = repo.add_inventory_by_delta(basis_id, |
2112 | inv_delta, rev.revision_id, rev.parent_ids, base_inv) |
2113 | + # Check verifiers |
2114 | + testament = StrictTestament3(rev, inv) |
2115 | + calculated_verifiers = { "testament3-sha1": testament.as_sha1() } |
2116 | + if roundtrip_revid is not None: |
2117 | + original_revid = rev.revision_id |
2118 | + rev.revision_id = roundtrip_revid |
2119 | + if calculated_verifiers != verifiers: |
2120 | + trace.mutter("Testament SHA1 %r for %r did not match %r.", |
2121 | + calculated_verifiers["testament3-sha1"], |
2122 | + rev.revision_id, verifiers["testament3-sha1"]) |
2123 | + rev.revision_id = original_revid |
2124 | + store_updater.add_object(o, calculated_verifiers, None) |
2125 | + store_updater.finish() |
2126 | ret_tree = RevisionTree(repo, inv, rev.revision_id) |
2127 | trees_cache.add(ret_tree) |
2128 | repo.add_revision(rev.revision_id, rev) |
2129 | if "verify" in debug.debug_flags: |
2130 | verify_commit_reconstruction(target_git_object_retriever, |
2131 | lookup_object, o, rev, ret_tree, parent_trees, mapping, |
2132 | - unusual_modes) |
2133 | + unusual_modes, verifiers) |
2134 | |
2135 | |
2136 | def import_git_objects(repo, mapping, object_iter, |
2137 | @@ -414,8 +431,10 @@ |
2138 | except KeyError: |
2139 | continue |
2140 | if isinstance(o, Commit): |
2141 | - rev = mapping.import_commit(o, lambda x: None) |
2142 | - if repo.has_revision(rev.revision_id): |
2143 | + rev, roundtrip_revid, verifiers = mapping.import_commit(o, |
2144 | + lambda x: None) |
2145 | + if (repo.has_revision(rev.revision_id) or |
2146 | + (roundtrip_revid and repo.has_revision(roundtrip_revid))): |
2147 | continue |
2148 | graph.append((o.id, o.parents)) |
2149 | heads.extend([p for p in o.parents if p not in checked]) |
2150 | @@ -479,6 +498,30 @@ |
2151 | """Base InterRepository that copies revisions from a Git into a non-Git |
2152 | repository.""" |
2153 | |
2154 | + def _target_has_shas(self, shas): |
2155 | + revids = [self.source.lookup_foreign_revision_id(sha) for sha in shas] |
2156 | + return self.target.has_revisions(revids) |
2157 | + |
2158 | + def get_determine_wants_heads(self, wants, include_tags=False): |
2159 | + wants = set(wants) |
2160 | + def determine_wants(refs): |
2161 | + potential = set(wants) |
2162 | + if include_tags: |
2163 | + potential.update([v[0] for v in extract_tags(refs).itervalues()]) |
2164 | + return list(potential - self._target_has_shas(potential)) |
2165 | + return determine_wants |
2166 | + |
2167 | + def get_determine_wants_revids(self, revids, include_tags=False): |
2168 | + wants = set() |
2169 | + for revid in set(revids): |
2170 | + git_sha, mapping = self.source.lookup_bzr_revision_id(revid) |
2171 | + wants.add(git_sha) |
2172 | + return self.get_determine_wants_heads(wants, include_tags=include_tags) |
2173 | + |
2174 | + def determine_wants_all(self, refs): |
2175 | + potential = set([sha for (ref, sha) in refs.iteritems() if not ref.endswith("^{}")]) |
2176 | + return list(potential - self._target_has_shas(potential)) |
2177 | + |
2178 | def fetch_objects(self, determine_wants, mapping, pb=None, limit=None): |
2179 | """Fetch objects from a remote server. |
2180 | |
2181 | @@ -497,16 +540,22 @@ |
2182 | if revision_id is not None: |
2183 | interesting_heads = [revision_id] |
2184 | elif fetch_spec is not None: |
2185 | - interesting_heads = fetch_spec.heads |
2186 | + recipe = fetch_spec.get_recipe() |
2187 | + if recipe[0] in ("search", "proxy-search"): |
2188 | + interesting_heads = recipe[1] |
2189 | + else: |
2190 | + raise AssertionError("Unsupported search result type %s" % recipe[0]) |
2191 | else: |
2192 | interesting_heads = None |
2193 | - def determine_wants(refs): |
2194 | - if interesting_heads is None: |
2195 | - ret = [sha for (ref, sha) in refs.iteritems() if not ref.endswith("^{}")] |
2196 | - else: |
2197 | - ret = [self.source.lookup_bzr_revision_id(revid)[0] for revid in interesting_heads if revid not in (None, NULL_REVISION)] |
2198 | - return [rev for rev in ret if not self.target.has_revision(self.source.lookup_foreign_revision_id(rev))] |
2199 | - (pack_hint, _, remote_refs) = self.fetch_objects(determine_wants, mapping, pb) |
2200 | + |
2201 | + if interesting_heads is not None: |
2202 | + determine_wants = self.get_determine_wants_revids(interesting_heads, |
2203 | + include_tags=False) |
2204 | + else: |
2205 | + determine_wants = self.determine_wants_all |
2206 | + |
2207 | + (pack_hint, _, remote_refs) = self.fetch_objects(determine_wants, |
2208 | + mapping, pb) |
2209 | if pack_hint is not None and self.target._format.pack_compresses: |
2210 | self.target.pack(hint=pack_hint) |
2211 | return remote_refs |
2212 | @@ -567,6 +616,7 @@ |
2213 | objects_iter = self.source.fetch_objects( |
2214 | wants_recorder, graph_walker, store.get_raw, |
2215 | progress) |
2216 | + trace.mutter("Importing %d new revisions", len(wants_recorder.wants)) |
2217 | (pack_hint, last_rev) = import_git_objects(self.target, mapping, |
2218 | objects_iter, store, wants_recorder.wants, pb, limit) |
2219 | return (pack_hint, last_rev, wants_recorder.remote_refs) |
2220 | @@ -656,7 +706,12 @@ |
2221 | if revision_id is not None: |
2222 | args = [mapping.revision_id_bzr_to_foreign(revision_id)[0]] |
2223 | elif fetch_spec is not None: |
2224 | - args = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in fetch_spec.heads] |
2225 | + recipe = fetch_spec.get_recipe() |
2226 | + if recipe[0] in ("search", "proxy-search"): |
2227 | + heads = recipe[1] |
2228 | + else: |
2229 | + raise AssertionError("Unsupported search result type %s" % recipe[0]) |
2230 | + args = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in heads] |
2231 | if branches is not None: |
2232 | determine_wants = lambda x: [x[y] for y in branches if not x[y] in r.object_store] |
2233 | elif fetch_spec is None and revision_id is None: |
2234 | |
2235 | === added file 'help.py' |
2236 | --- help.py 1970-01-01 00:00:00 +0000 |
2237 | +++ help.py 2011-03-23 20:12:35 +0000 |
2238 | @@ -0,0 +1,33 @@ |
2239 | +# Copyright (C) 2010 Jelmer Vernooij <jelmer@samba.org> |
2240 | + |
2241 | +# This program is free software; you can redistribute it and/or modify |
2242 | +# it under the terms of the GNU General Public License as published by |
2243 | +# the Free Software Foundation; either version 2 of the License, or |
2244 | +# (at your option) any later version. |
2245 | + |
2246 | +# This program is distributed in the hope that it will be useful, |
2247 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2248 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2249 | +# GNU General Public License for more details. |
2250 | + |
2251 | +# You should have received a copy of the GNU General Public License |
2252 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
2253 | + |
2254 | +"""Help information.""" |
2255 | + |
2256 | +help_git = """Using Bazaar with Git. |
2257 | + |
2258 | +The bzr-git plugin provides support for using Bazaar with local and remote |
2259 | +Git repositories, as just another format. You can clone, pull from and |
2260 | +push to git repositories as you would with any native Bazaar branch. |
2261 | + |
2262 | +The bzr-git plugin also adds two new bzr subcommands: |
2263 | + |
2264 | + * bzr git-objects: Extracts Git objects out of a Bazaar repository |
2265 | + * bzr git-refs: Display Git refs from a Bazaar branch or repository |
2266 | + * bzr svn-import: Imports a local or remote Git repository to a set of Bazaar |
2267 | + branches |
2268 | + |
2269 | +The 'git:' revision specifier can be used to find revisions by short or long |
2270 | +GIT SHA1. |
2271 | +""" |
2272 | |
2273 | === modified file 'info.py' |
2274 | --- info.py 2010-08-05 23:42:42 +0000 |
2275 | +++ info.py 2011-03-23 20:12:35 +0000 |
2276 | @@ -1,15 +1,15 @@ |
2277 | bzr_plugin_name = "git" |
2278 | |
2279 | -dulwich_minimum_version = (0, 6, 1) |
2280 | +dulwich_minimum_version = (0, 6, 2) |
2281 | |
2282 | # versions ending in 'exp' mean experimental mappings |
2283 | # versions ending in 'dev' mean development version |
2284 | # versions ending in 'final' mean release (well tested, etc) |
2285 | -bzr_plugin_version = (0, 5, 3, 'dev', 0) |
2286 | +bzr_plugin_version = (0, 5, 5, 'dev', 0) |
2287 | |
2288 | bzr_commands = ["git-import", "git-object", "git-refs", "git-apply"] |
2289 | |
2290 | -bzr_compatible_versions = [(2, x, 0) for x in [0, 1, 2]] |
2291 | +bzr_compatible_versions = [(2, x, 0) for x in [3, 4]] |
2292 | |
2293 | bzr_minimum_version = bzr_compatible_versions[0] |
2294 | |
2295 | |
2296 | === modified file 'inventory.py' |
2297 | --- inventory.py 2010-08-03 19:47:30 +0000 |
2298 | +++ inventory.py 2011-03-23 20:12:35 +0000 |
2299 | @@ -176,7 +176,7 @@ |
2300 | |
2301 | def _retrieve_children(self): |
2302 | self._children = {} |
2303 | - for mode, name, hexsha in self.object.entries(): |
2304 | + for name, mode, hexsha in self.object.iteritems(): |
2305 | basename = name.decode("utf-8") |
2306 | child_path = osutils.pathjoin(self.path, basename) |
2307 | if self._inventory.mapping.is_control_file(child_path): |
2308 | @@ -241,7 +241,6 @@ |
2309 | path = self.fileid_map.lookup_path(file_id) |
2310 | try: |
2311 | ie = self._get_ie(path) |
2312 | - assert ie.path == path |
2313 | except KeyError: |
2314 | raise errors.NoSuchId(None, file_id) |
2315 | |
2316 | @@ -313,8 +312,9 @@ |
2317 | def path2id(self, path): |
2318 | if type(path) in (list, tuple): |
2319 | path = "/".join(path) |
2320 | - if path in self.index: |
2321 | - file_id = self.fileid_map.lookup_file_id(path) |
2322 | + encoded_path = path.encode("utf-8") |
2323 | + if encoded_path in self.index: |
2324 | + file_id = self.fileid_map.lookup_file_id(encoded_path) |
2325 | else: |
2326 | self._read_contents() |
2327 | file_id = super(GitIndexInventory, self).path2id(path) |
2328 | @@ -357,7 +357,7 @@ |
2329 | self.add_parents(path) |
2330 | self.add(old_ie) |
2331 | else: |
2332 | - ie = self.add_path(path, kind, file_id, |
2333 | + ie = self.add_path(path.decode("utf-8"), kind, file_id, |
2334 | self.add_parents(path)) |
2335 | data = self.store[sha].data |
2336 | if kind == "symlink": |
2337 | @@ -370,6 +370,7 @@ |
2338 | pb.finished() |
2339 | |
2340 | def add_parents(self, path): |
2341 | + assert isinstance(path, str) |
2342 | dirname, _ = osutils.split(path) |
2343 | file_id = super(GitIndexInventory, self).path2id(dirname) |
2344 | if file_id is None: |
2345 | @@ -377,7 +378,7 @@ |
2346 | parent_fid = None |
2347 | else: |
2348 | parent_fid = self.add_parents(dirname) |
2349 | - ie = self.add_path(dirname, 'directory', |
2350 | + ie = self.add_path(dirname.decode("utf-8"), 'directory', |
2351 | self.fileid_map.lookup_file_id(dirname), parent_fid) |
2352 | if self.basis_inv is not None and ie.file_id in self.basis_inv: |
2353 | ie.revision = self.basis_inv[ie.file_id].revision |
2354 | |
2355 | === modified file 'mapping.py' |
2356 | --- mapping.py 2010-08-01 22:29:21 +0000 |
2357 | +++ mapping.py 2011-03-23 20:12:35 +0000 |
2358 | @@ -104,7 +104,7 @@ |
2359 | super(BzrGitMapping, self).__init__(foreign_git) |
2360 | |
2361 | def __eq__(self, other): |
2362 | - return (type(self) == type(other) and |
2363 | + return (type(self) == type(other) and |
2364 | self.revid_prefix == other.revid_prefix) |
2365 | |
2366 | @classmethod |
2367 | @@ -185,7 +185,7 @@ |
2368 | |
2369 | def _extract_git_svn_metadata(self, rev, message): |
2370 | lines = message.split("\n") |
2371 | - if not (lines[-1] == "" and lines[-2].startswith("git-svn-id:")): |
2372 | + if not (lines[-1] == "" and len(lines) >= 2 and lines[-2].startswith("git-svn-id:")): |
2373 | return message |
2374 | git_svn_id = lines[-2].split(": ", 1)[1] |
2375 | rev.properties['git-svn-id'] = git_svn_id |
2376 | @@ -227,12 +227,15 @@ |
2377 | b.set_raw_chunks(serialize_fileid_map(fileid_map)) |
2378 | return b |
2379 | |
2380 | - def export_commit(self, rev, tree_sha, parent_lookup, roundtrip): |
2381 | + def export_commit(self, rev, tree_sha, parent_lookup, roundtrip, |
2382 | + verifiers): |
2383 | """Turn a Bazaar revision in to a Git commit |
2384 | |
2385 | :param tree_sha: Tree sha for the commit |
2386 | :param parent_lookup: Function for looking up the GIT sha equiv of a |
2387 | bzr revision |
2388 | + :param roundtrip: Whether to store roundtripping information. |
2389 | + :param verifiers: Verifiers info |
2390 | :return dulwich.objects.Commit represent the revision: |
2391 | """ |
2392 | from dulwich.objects import Commit |
2393 | @@ -240,6 +243,7 @@ |
2394 | commit.tree = tree_sha |
2395 | if roundtrip: |
2396 | metadata = BzrGitRevisionMetadata() |
2397 | + metadata.verifiers = verifiers |
2398 | else: |
2399 | metadata = None |
2400 | parents = [] |
2401 | @@ -307,8 +311,8 @@ |
2402 | def import_commit(self, commit, lookup_parent_revid): |
2403 | """Convert a git commit to a bzr revision. |
2404 | |
2405 | - :return: a `bzrlib.revision.Revision` object and a |
2406 | - dictionary of path -> file ids |
2407 | + :return: a `bzrlib.revision.Revision` object, foreign revid and a |
2408 | + testament sha1 |
2409 | """ |
2410 | if commit is None: |
2411 | raise AssertionError("Commit object can't be None") |
2412 | @@ -347,12 +351,15 @@ |
2413 | rev.timezone = commit.commit_timezone |
2414 | if rev.git_metadata is not None: |
2415 | md = rev.git_metadata |
2416 | - if md.revision_id: |
2417 | - rev.revision_id = md.revision_id |
2418 | + roundtrip_revid = md.revision_id |
2419 | if md.explicit_parent_ids: |
2420 | rev.parent_ids = md.explicit_parent_ids |
2421 | rev.properties.update(md.properties) |
2422 | - return rev |
2423 | + verifiers = md.verifiers |
2424 | + else: |
2425 | + roundtrip_revid = None |
2426 | + verifiers = {} |
2427 | + return rev, roundtrip_revid, verifiers |
2428 | |
2429 | def get_fileid_map(self, lookup_object, tree_sha): |
2430 | """Obtain a fileid map for a particular tree. |
2431 | @@ -400,9 +407,9 @@ |
2432 | return ret |
2433 | |
2434 | def import_commit(self, commit, lookup_parent_revid): |
2435 | - rev, file_ids = super(BzrGitMappingExperimental, self).import_commit(commit, lookup_parent_revid) |
2436 | + rev, roundtrip_revid, verifiers = super(BzrGitMappingExperimental, self).import_commit(commit, lookup_parent_revid) |
2437 | rev.properties['converted_revision'] = "git %s\n" % commit.id |
2438 | - return rev, file_ids |
2439 | + return rev, roundtrip_revid, verifiers |
2440 | |
2441 | |
2442 | class GitMappingRegistry(VcsMappingRegistry): |
2443 | |
2444 | === modified file 'object_store.py' |
2445 | --- object_store.py 2010-08-03 19:43:59 +0000 |
2446 | +++ object_store.py 2011-03-23 20:12:35 +0000 |
2447 | @@ -18,6 +18,7 @@ |
2448 | |
2449 | from dulwich.objects import ( |
2450 | Blob, |
2451 | + Commit, |
2452 | Tree, |
2453 | sha_to_hex, |
2454 | ) |
2455 | @@ -35,6 +36,9 @@ |
2456 | from bzrlib.revision import ( |
2457 | NULL_REVISION, |
2458 | ) |
2459 | +from bzrlib.testament import( |
2460 | + StrictTestament3, |
2461 | + ) |
2462 | |
2463 | from bzrlib.plugins.git.mapping import ( |
2464 | default_mapping, |
2465 | @@ -104,7 +108,7 @@ |
2466 | self._cache.add(tree.get_revision_id(), tree) |
2467 | |
2468 | |
2469 | -def _find_missing_bzr_revids(get_parent_map, want, have): |
2470 | +def _find_missing_bzr_revids(graph, want, have): |
2471 | """Find the revisions that have to be pushed. |
2472 | |
2473 | :param get_parent_map: Function that returns the parents for a sequence |
2474 | @@ -113,19 +117,9 @@ |
2475 | :param have: Revisions the target already has |
2476 | :return: Set of revisions to fetch |
2477 | """ |
2478 | - pending = want - have |
2479 | - processed = set() |
2480 | todo = set() |
2481 | - while pending: |
2482 | - processed.update(pending) |
2483 | - next_map = get_parent_map(pending) |
2484 | - next_pending = set() |
2485 | - for item in next_map.iteritems(): |
2486 | - if item[0] in have: |
2487 | - continue |
2488 | - todo.add(item[0]) |
2489 | - next_pending.update(p for p in item[1] if p not in processed) |
2490 | - pending = next_pending |
2491 | + for rev in want: |
2492 | + todo.update(graph.find_unique_ancestors(rev, have)) |
2493 | if NULL_REVISION in todo: |
2494 | todo.remove(NULL_REVISION) |
2495 | return todo |
2496 | @@ -220,7 +214,9 @@ |
2497 | new_trees[posixpath.dirname(path[1])] = parent[1] |
2498 | elif kind[1] not in (None, "directory"): |
2499 | raise AssertionError(kind[1]) |
2500 | - if path[0] is not None and parent[0] in tree.inventory and tree.inventory[parent[0]].kind == "directory": |
2501 | + if (path[0] not in (None, "") and |
2502 | + parent[0] in tree.inventory and |
2503 | + tree.inventory[parent[0]].kind == "directory"): |
2504 | # Removal |
2505 | new_trees[posixpath.dirname(path[0])] = parent[0] |
2506 | |
2507 | @@ -336,14 +332,22 @@ |
2508 | self._update_sha_map() |
2509 | return iter(self._cache.idmap.sha1s()) |
2510 | |
2511 | - def _reconstruct_commit(self, rev, tree_sha, roundtrip): |
2512 | + def _reconstruct_commit(self, rev, tree_sha, roundtrip, verifiers): |
2513 | + """Reconstruct a Commit object. |
2514 | + |
2515 | + :param rev: Revision object |
2516 | + :param tree_sha: SHA1 of the root tree object |
2517 | + :param roundtrip: Whether or not to roundtrip bzr metadata |
2518 | + :param verifiers: Verifiers for the commits |
2519 | + :return: Commit object |
2520 | + """ |
2521 | def parent_lookup(revid): |
2522 | try: |
2523 | return self._lookup_revision_sha1(revid) |
2524 | except errors.NoSuchRevision: |
2525 | return None |
2526 | return self.mapping.export_commit(rev, tree_sha, parent_lookup, |
2527 | - roundtrip) |
2528 | + roundtrip, verifiers) |
2529 | |
2530 | def _create_fileid_map_blob(self, inv): |
2531 | # FIXME: This can probably be a lot more efficient, |
2532 | @@ -388,8 +392,13 @@ |
2533 | root_tree[self.mapping.BZR_FILE_IDS_FILE] = ((stat.S_IFREG | 0644), b.id) |
2534 | yield self.mapping.BZR_FILE_IDS_FILE, b, None |
2535 | yield "", root_tree, root_ie |
2536 | + if roundtrip: |
2537 | + testament3 = StrictTestament3(rev, tree.inventory) |
2538 | + verifiers = { "testament3-sha1": testament3.as_sha1() } |
2539 | + else: |
2540 | + verifiers = {} |
2541 | commit_obj = self._reconstruct_commit(rev, root_tree.id, |
2542 | - roundtrip=roundtrip) |
2543 | + roundtrip=roundtrip, verifiers=verifiers) |
2544 | try: |
2545 | foreign_revid, mapping = mapping_registry.parse_revision_id( |
2546 | rev.revision_id) |
2547 | @@ -408,6 +417,9 @@ |
2548 | updater = self._get_updater(rev) |
2549 | for path, obj, ie in self._revision_to_objects(rev, tree, |
2550 | roundtrip=True): |
2551 | + if isinstance(obj, Commit): |
2552 | + testament3 = StrictTestament3(rev, tree.inventory) |
2553 | + ie = { "testament3-sha1": testament3.as_sha1() } |
2554 | updater.add_object(obj, ie, path) |
2555 | commit_obj = updater.finish() |
2556 | return commit_obj.id |
2557 | @@ -525,7 +537,7 @@ |
2558 | ret = {} |
2559 | for sha in shas: |
2560 | if sha == ZERO_SHA: |
2561 | - ret[sha] = ("commit", (NULL_REVISION, None)) |
2562 | + ret[sha] = ("commit", (NULL_REVISION, None, {})) |
2563 | continue |
2564 | try: |
2565 | ret[sha] = self._cache.idmap.lookup_git_sha(sha) |
2566 | @@ -553,14 +565,15 @@ |
2567 | (type, type_data) = self.lookup_git_sha(sha) |
2568 | # convert object to git object |
2569 | if type == "commit": |
2570 | - (revid, tree_sha) = type_data |
2571 | + (revid, tree_sha, verifiers) = type_data |
2572 | try: |
2573 | rev = self.repository.get_revision(revid) |
2574 | except errors.NoSuchRevision: |
2575 | trace.mutter('entry for %s %s in shamap: %r, but not found in ' |
2576 | 'repository', type, sha, type_data) |
2577 | raise KeyError(sha) |
2578 | - commit = self._reconstruct_commit(rev, tree_sha, roundtrip=True) |
2579 | + commit = self._reconstruct_commit(rev, tree_sha, roundtrip=True, |
2580 | + verifiers=verifiers) |
2581 | _check_expected_sha(sha, commit) |
2582 | return commit |
2583 | elif type == "blob": |
2584 | @@ -599,7 +612,7 @@ |
2585 | ret = self.lookup_git_shas(have + want) |
2586 | for commit_sha in have: |
2587 | try: |
2588 | - (type, (revid, tree_sha)) = ret[commit_sha] |
2589 | + (type, (revid, tree_sha, verifiers)) = ret[commit_sha] |
2590 | except KeyError: |
2591 | pass |
2592 | else: |
2593 | @@ -610,22 +623,25 @@ |
2594 | if commit_sha in have: |
2595 | continue |
2596 | try: |
2597 | - (type, (revid, tree_sha)) = ret[commit_sha] |
2598 | + (type, (revid, tree_sha, verifiers)) = ret[commit_sha] |
2599 | except KeyError: |
2600 | pass |
2601 | else: |
2602 | assert type == "commit" |
2603 | pending.add(revid) |
2604 | |
2605 | - todo = _find_missing_bzr_revids(self.repository.get_parent_map, |
2606 | - pending, processed) |
2607 | + graph = self.repository.get_graph() |
2608 | + todo = _find_missing_bzr_revids(graph, pending, processed) |
2609 | trace.mutter('sending revisions %r', todo) |
2610 | ret = [] |
2611 | pb = ui.ui_factory.nested_progress_bar() |
2612 | try: |
2613 | for i, revid in enumerate(todo): |
2614 | pb.update("generating git objects", i, len(todo)) |
2615 | - rev = self.repository.get_revision(revid) |
2616 | + try: |
2617 | + rev = self.repository.get_revision(revid) |
2618 | + except errors.NoSuchRevision: |
2619 | + continue |
2620 | tree = self.tree_cache.revision_tree(revid) |
2621 | for path, obj, ie in self._revision_to_objects(rev, tree, |
2622 | roundtrip=not lossy): |
2623 | |
2624 | === modified file 'push.py' |
2625 | --- push.py 2010-07-24 11:15:11 +0000 |
2626 | +++ push.py 2011-03-23 20:12:35 +0000 |
2627 | @@ -20,9 +20,6 @@ |
2628 | errors, |
2629 | ui, |
2630 | ) |
2631 | -from bzrlib.graph import ( |
2632 | - PendingAncestryResult, |
2633 | - ) |
2634 | from bzrlib.repository import ( |
2635 | InterRepository, |
2636 | ) |
2637 | @@ -44,6 +41,9 @@ |
2638 | from bzrlib.plugins.git.remote import ( |
2639 | RemoteGitRepository, |
2640 | ) |
2641 | +from bzrlib.plugins.git.refs import ( |
2642 | + UnpeelMap, |
2643 | + ) |
2644 | |
2645 | |
2646 | class MissingObjectsIterator(object): |
2647 | @@ -116,7 +116,7 @@ |
2648 | def dfetch_refs(self, update_refs): |
2649 | """Fetch non-roundtripped revisions into the target repository. |
2650 | |
2651 | - :param update_refs: Generate refs to fetch. Receives dictionary |
2652 | + :param update_refs: Generate refs to fetch. Receives dictionary |
2653 | with old names to old git shas. Should return a dictionary |
2654 | of new names to Bazaar revision ids. |
2655 | :return: revision id map, old refs dictionary and new refs dictionary |
2656 | @@ -126,8 +126,8 @@ |
2657 | def fetch_refs(self, update_refs): |
2658 | """Fetch possibly roundtripped revisions into the target repository. |
2659 | |
2660 | - :param update_refs: Generate refs to fetch. Receives dictionary |
2661 | - with old refs (git shas), returns dictionary of new names to |
2662 | + :param update_refs: Generate refs to fetch. Receives dictionary |
2663 | + with old refs (git shas), returns dictionary of new names to |
2664 | git shas. |
2665 | :return: old refs, new refs |
2666 | """ |
2667 | @@ -142,13 +142,14 @@ |
2668 | self.target_store = self.target._git.object_store |
2669 | self.target_refs = self.target._git.refs |
2670 | |
2671 | - def _revision_needs_fetching(self, revid): |
2672 | + def _revision_needs_fetching(self, sha_id, revid): |
2673 | if revid == NULL_REVISION: |
2674 | return False |
2675 | - try: |
2676 | - sha_id = self.source_store._lookup_revision_sha1(revid) |
2677 | - except KeyError: |
2678 | - raise errors.NoSuchRevision(self.source, revid) |
2679 | + if sha_id is None: |
2680 | + try: |
2681 | + sha_id = self.source_store._lookup_revision_sha1(revid) |
2682 | + except KeyError: |
2683 | + return False |
2684 | try: |
2685 | return (sha_id not in self.target_store) |
2686 | except errors.NoSuchRevision: |
2687 | @@ -158,22 +159,43 @@ |
2688 | def missing_revisions(self, stop_revisions): |
2689 | """Find the revisions that are missing from the target repository. |
2690 | |
2691 | - :param stop_revisions: Revisions to check for |
2692 | + :param stop_revisions: Revisions to check for (tuples with |
2693 | + Git SHA1, bzr revid) |
2694 | :return: sequence of missing revisions, in topological order |
2695 | :raise: NoSuchRevision if the stop_revisions are not present in |
2696 | the source |
2697 | """ |
2698 | + revid_sha_map = {} |
2699 | + stop_revids = [] |
2700 | + stop_sha1s = [] |
2701 | + for (sha1, revid) in stop_revisions: |
2702 | + if sha1 is not None and revid is not None: |
2703 | + revid_sha_map[revid] = sha1 |
2704 | + elif sha1 is not None: |
2705 | + stop_sha1s.append(sha1) |
2706 | + else: |
2707 | + assert revid is not None |
2708 | + stop_revids.append(revid) |
2709 | missing = [] |
2710 | graph = self.source.get_graph() |
2711 | pb = ui.ui_factory.nested_progress_bar() |
2712 | try: |
2713 | - for revid, _ in graph.iter_ancestry(stop_revisions): |
2714 | + for revid, _ in graph.iter_ancestry(stop_revids): |
2715 | assert type(revid) is str |
2716 | pb.update("determining revisions to fetch", len(missing)) |
2717 | - if self._revision_needs_fetching(revid): |
2718 | + sha1 = revid_sha_map.get(revid) |
2719 | + if self._revision_needs_fetching(sha1, revid): |
2720 | missing.append(revid) |
2721 | finally: |
2722 | pb.finished() |
2723 | + for sha1 in stop_sha1s: |
2724 | + try: |
2725 | + (kind, (revid, tree_sha, verifiers)) = self.source_store.lookup_git_sha(sha1) |
2726 | + except KeyError: |
2727 | + continue |
2728 | + else: |
2729 | + missing.append(revid) |
2730 | + revid_sha_map[revid] = sha1 |
2731 | return graph.iter_topo_order(missing) |
2732 | |
2733 | def _get_target_bzr_refs(self): |
2734 | @@ -186,9 +208,14 @@ |
2735 | refs = self.target._git.get_refs() |
2736 | for k, v in refs.iteritems(): |
2737 | try: |
2738 | - (kind, (revid, treesha)) = self.source_store.lookup_git_sha(v) |
2739 | + (kind, type_data) = self.source_store.lookup_git_sha(v) |
2740 | except KeyError: |
2741 | revid = None |
2742 | + else: |
2743 | + if kind == "commit": |
2744 | + revid = type_data[0] |
2745 | + else: |
2746 | + revid = None |
2747 | bzr_refs[k] = (v, revid) |
2748 | return bzr_refs |
2749 | |
2750 | @@ -197,10 +224,7 @@ |
2751 | try: |
2752 | old_refs = self._get_target_bzr_refs() |
2753 | new_refs = update_refs(old_refs) |
2754 | - # FIXME: Keep track of already looked up revid<->sha mappings |
2755 | - fetch_spec = PendingAncestryResult( |
2756 | - [revid for sha, revid in new_refs.values()], self.source) |
2757 | - self.fetch(fetch_spec=fetch_spec) |
2758 | + self.fetch(mapped_refs=new_refs.values()) |
2759 | finally: |
2760 | self.source.unlock() |
2761 | return old_refs, new_refs |
2762 | @@ -232,7 +256,7 @@ |
2763 | revidmap = {} |
2764 | self.source.lock_read() |
2765 | try: |
2766 | - todo = list(self.missing_revisions([revid for sha, revid in stop_revisions])) |
2767 | + todo = list(self.missing_revisions(stop_revisions)) |
2768 | pb = ui.ui_factory.nested_progress_bar() |
2769 | try: |
2770 | object_generator = self._get_missing_objects_iterator(pb) |
2771 | @@ -249,13 +273,15 @@ |
2772 | return revidmap, gitidmap |
2773 | |
2774 | def fetch(self, revision_id=None, pb=None, find_ghosts=False, |
2775 | - fetch_spec=None): |
2776 | - if revision_id is not None: |
2777 | - stop_revisions = [revision_id] |
2778 | + fetch_spec=None, mapped_refs=None): |
2779 | + if mapped_refs is not None: |
2780 | + stop_revisions = mapped_refs |
2781 | + elif revision_id is not None: |
2782 | + stop_revisions = [(None, revision_id)] |
2783 | elif fetch_spec is not None: |
2784 | - stop_revisions = fetch_spec.heads |
2785 | + stop_revisions = [(None, revid) for revid in fetch_spec.heads] |
2786 | else: |
2787 | - stop_revisions = self.source.all_revision_ids() |
2788 | + stop_revisions = [(None, revid) for revid in self.source.all_revision_ids()] |
2789 | self.source.lock_read() |
2790 | try: |
2791 | todo = list(self.missing_revisions(stop_revisions)) |
2792 | @@ -285,14 +311,15 @@ |
2793 | |
2794 | def dfetch_refs(self, update_refs): |
2795 | """Import the gist of the ancestry of a particular revision.""" |
2796 | + unpeel_map = UnpeelMap.from_repository(self.source) |
2797 | revidmap = {} |
2798 | def determine_wants(old_refs): |
2799 | ret = {} |
2800 | - self.old_refs = old_refs |
2801 | + self.old_refs = dict([(k, (v, None)) for (k, v) in old_refs.iteritems()]) |
2802 | self.new_refs = update_refs(self.old_refs) |
2803 | for name, (gitid, revid) in self.new_refs.iteritems(): |
2804 | if gitid is None: |
2805 | - ret[name] = self.source_store._lookup_revision_sha1(revid) |
2806 | + ret[name] = unpeel_map.re_unpeel_tag(self.source_store._lookup_revision_sha1(revid), old_refs.get(name)) |
2807 | else: |
2808 | ret[name] = gitid |
2809 | return ret |
2810 | |
2811 | === modified file 'refs.py' |
2812 | --- refs.py 2010-08-12 19:36:37 +0000 |
2813 | +++ refs.py 2011-03-23 20:12:35 +0000 |
2814 | @@ -16,31 +16,43 @@ |
2815 | |
2816 | """Conversion between refs and Bazaar revision pointers.""" |
2817 | |
2818 | +from collections import defaultdict |
2819 | +from cStringIO import StringIO |
2820 | + |
2821 | from dulwich.repo import ( |
2822 | RefsContainer, |
2823 | ) |
2824 | |
2825 | from bzrlib import ( |
2826 | errors, |
2827 | + trace, |
2828 | ) |
2829 | |
2830 | +is_tag = lambda x: x.startswith("refs/tags/") |
2831 | + |
2832 | |
2833 | def extract_tags(refs): |
2834 | """Extract the tags from a refs dictionary. |
2835 | |
2836 | :param refs: Refs to extract the tags from. |
2837 | - :return: Dictionary mapping tag names to SHA1s. |
2838 | + :return: Dictionary mapping tag names to SHA1s of the actual object |
2839 | + and unpeeled object SHA1s. |
2840 | """ |
2841 | ret = {} |
2842 | - for k,v in refs.iteritems(): |
2843 | - if k.startswith("refs/tags/") and not k.endswith("^{}"): |
2844 | - v = refs.get(k+"^{}", v) |
2845 | + for k, v in refs.iteritems(): |
2846 | + if is_tag(k) and not k.endswith("^{}"): |
2847 | + try: |
2848 | + peeled = refs[k+"^{}"] |
2849 | + unpeeled = v |
2850 | + except KeyError: |
2851 | + peeled = v |
2852 | + unpeeled = None |
2853 | try: |
2854 | tagname = ref_to_tag_name(k) |
2855 | except UnicodeDecodeError: |
2856 | pass |
2857 | else: |
2858 | - ret[tagname] = v |
2859 | + ret[tagname] = (peeled, unpeeled) |
2860 | return ret |
2861 | |
2862 | |
2863 | @@ -85,7 +97,7 @@ |
2864 | def ref_to_tag_name(ref): |
2865 | if ref.startswith("refs/tags/"): |
2866 | return ref[len('refs/tags/'):].decode("utf-8") |
2867 | - raise ValueError("unable to map ref %s back to branch name" % ref) |
2868 | + raise ValueError("unable to map ref %s back to tag name" % ref) |
2869 | |
2870 | |
2871 | class BazaarRefsContainer(RefsContainer): |
2872 | @@ -169,3 +181,62 @@ |
2873 | target_branch.generate_revision_history(rev_id) |
2874 | finally: |
2875 | target_branch.unlock() |
2876 | + |
2877 | + |
2878 | +class UnpeelMap(object): |
2879 | + """Unpeel map. |
2880 | + |
2881 | + Keeps track of the unpeeled object id of tags. |
2882 | + """ |
2883 | + |
2884 | + def __init__(self): |
2885 | + self._map = defaultdict(set) |
2886 | + |
2887 | + def update(self, m): |
2888 | + for k, v in m.iteritems(): |
2889 | + self._map[k].update(v) |
2890 | + |
2891 | + def load(self, f): |
2892 | + firstline = f.readline() |
2893 | + if firstline != "unpeel map version 1\n": |
2894 | + raise AssertionError("invalid format for unpeel map: %r" % firstline) |
2895 | + for l in f.readlines(): |
2896 | + (k, v) = l.split(":", 1) |
2897 | + self._map[k.strip()].add(v.strip()) |
2898 | + |
2899 | + def save(self, f): |
2900 | + f.write("unpeel map version 1\n") |
2901 | + for k, vs in self._map.iteritems(): |
2902 | + for v in vs: |
2903 | + f.write("%s: %s\n" % (k, v)) |
2904 | + |
2905 | + def save_in_repository(self, repository): |
2906 | + f = StringIO() |
2907 | + try: |
2908 | + self.save(f) |
2909 | + f.seek(0) |
2910 | + repository.control_transport.put_file("git-unpeel-map", f) |
2911 | + finally: |
2912 | + f.close() |
2913 | + |
2914 | + def re_unpeel_tag(self, new_git_sha, old_git_sha): |
2915 | + """Re-unpeel tags. |
2916 | + |
2917 | + Bazaar can't store unpeeled refs so in order to prevent peeling |
2918 | + existing tags when pushing they are "re-peeled" here. |
2919 | + """ |
2920 | + if old_git_sha is not None and old_git_sha in self._map[new_git_sha]: |
2921 | + trace.mutter("re-unpeeling %r to %r", new_git_sha, old_git_sha) |
2922 | + return old_git_sha |
2923 | + return new_git_sha |
2924 | + |
2925 | + @classmethod |
2926 | + def from_repository(cls, repository): |
2927 | + """Load the unpeel map for a repository. |
2928 | + """ |
2929 | + m = UnpeelMap() |
2930 | + try: |
2931 | + m.load(repository.control_transport.get("git-unpeel-map")) |
2932 | + except errors.NoSuchFile: |
2933 | + pass |
2934 | + return m |
2935 | |
2936 | === modified file 'remote.py' |
2937 | --- remote.py 2010-06-28 19:52:39 +0000 |
2938 | +++ remote.py 2011-03-23 20:12:35 +0000 |
2939 | @@ -1,4 +1,4 @@ |
2940 | -# Copyright (C) 2007-2009 Jelmer Vernooij <jelmer@samba.org> |
2941 | +# Copyright (C) 2007-2010 Jelmer Vernooij <jelmer@samba.org> |
2942 | # |
2943 | # This program is free software; you can redistribute it and/or modify |
2944 | # it under the terms of the GNU General Public License as published by |
2945 | @@ -17,7 +17,6 @@ |
2946 | from bzrlib import ( |
2947 | config, |
2948 | debug, |
2949 | - tag, |
2950 | trace, |
2951 | ui, |
2952 | urlutils, |
2953 | @@ -27,7 +26,9 @@ |
2954 | InvalidRevisionId, |
2955 | NoSuchFile, |
2956 | NoSuchRevision, |
2957 | + NotBranchError, |
2958 | NotLocalUrl, |
2959 | + UninitializableFormat, |
2960 | ) |
2961 | from bzrlib.transport import ( |
2962 | Transport, |
2963 | @@ -40,14 +41,18 @@ |
2964 | |
2965 | from bzrlib.plugins.git.branch import ( |
2966 | GitBranch, |
2967 | + GitTags, |
2968 | + ) |
2969 | +from bzrlib.plugins.git.dir import ( |
2970 | + GitControlDirFormat, |
2971 | + GitDir, |
2972 | + GitLockableFiles, |
2973 | + GitLock, |
2974 | ) |
2975 | from bzrlib.plugins.git.errors import ( |
2976 | GitSmartRemoteNotSupported, |
2977 | NoSuchRef, |
2978 | ) |
2979 | -from bzrlib.plugins.git.dir import ( |
2980 | - GitDir, |
2981 | - ) |
2982 | from bzrlib.plugins.git.mapping import ( |
2983 | mapping_registry, |
2984 | ) |
2985 | @@ -204,14 +209,17 @@ |
2986 | self._lockfiles = lockfiles |
2987 | self._mode_check_done = None |
2988 | |
2989 | + @property |
2990 | + def user_url(self): |
2991 | + return self.control_url |
2992 | + |
2993 | def _branch_name_to_ref(self, name, default=None): |
2994 | return branch_name_to_ref(name, default=default) |
2995 | |
2996 | def open_repository(self): |
2997 | return RemoteGitRepository(self, self._lockfiles) |
2998 | |
2999 | - def _open_branch(self, name=None, ignore_fallbacks=False, |
3000 | - unsupported=False): |
3001 | + def open_branch(self, name=None, unsupported=False, ignore_fallbacks=False): |
3002 | repo = self.open_repository() |
3003 | refname = self._branch_name_to_ref(name) |
3004 | return RemoteGitBranch(self, repo, refname, self._lockfiles) |
3005 | @@ -262,6 +270,37 @@ |
3006 | os.remove(self._data_path) |
3007 | |
3008 | |
3009 | +class RemoteGitControlDirFormat(GitControlDirFormat): |
3010 | + """The .git directory control format.""" |
3011 | + |
3012 | + supports_workingtrees = False |
3013 | + |
3014 | + @classmethod |
3015 | + def _known_formats(self): |
3016 | + return set([RemoteGitControlDirFormat()]) |
3017 | + |
3018 | + def open(self, transport, _found=None): |
3019 | + """Open this directory. |
3020 | + |
3021 | + """ |
3022 | + # we dont grok readonly - git isn't integrated with transport. |
3023 | + url = transport.base |
3024 | + if url.startswith('readonly+'): |
3025 | + url = url[len('readonly+'):] |
3026 | + if (not url.startswith("git://") and not url.startswith("git+")): |
3027 | + raise NotBranchError(transport.base) |
3028 | + if not isinstance(transport, GitSmartTransport): |
3029 | + raise NotBranchError(transport.base) |
3030 | + lockfiles = GitLockableFiles(transport, GitLock()) |
3031 | + return RemoteGitDir(transport, lockfiles, self) |
3032 | + |
3033 | + def get_format_description(self): |
3034 | + return "Remote Git Repository" |
3035 | + |
3036 | + def initialize_on_transport(self, transport): |
3037 | + raise UninitializableFormat(self) |
3038 | + |
3039 | + |
3040 | class RemoteGitRepository(GitRepository): |
3041 | |
3042 | def __init__(self, gitdir, lockfiles): |
3043 | @@ -269,6 +308,10 @@ |
3044 | self._refs = None |
3045 | |
3046 | @property |
3047 | + def user_url(self): |
3048 | + return self.control_url |
3049 | + |
3050 | + @property |
3051 | def inventories(self): |
3052 | raise GitSmartRemoteNotSupported() |
3053 | |
3054 | @@ -322,17 +365,15 @@ |
3055 | return mapping.revision_id_foreign_to_bzr(foreign_revid) |
3056 | |
3057 | |
3058 | -class RemoteGitTagDict(tag.BasicTags): |
3059 | - |
3060 | - def __init__(self, branch): |
3061 | - self.branch = branch |
3062 | - self.repository = branch.repository |
3063 | - |
3064 | - def get_tag_dict(self): |
3065 | - tags = {} |
3066 | - for k, v in extract_tags(self.repository.get_refs()).iteritems(): |
3067 | - tags[k] = self.branch.mapping.revision_id_foreign_to_bzr(v) |
3068 | - return tags |
3069 | +class RemoteGitTagDict(GitTags): |
3070 | + |
3071 | + def get_refs(self): |
3072 | + return self.repository.get_refs() |
3073 | + |
3074 | + def _iter_tag_refs(self, refs): |
3075 | + for k, (peeled, unpeeled) in extract_tags(refs).iteritems(): |
3076 | + yield (k, peeled, unpeeled, |
3077 | + self.branch.mapping.revision_id_foreign_to_bzr(peeled)) |
3078 | |
3079 | def set_tag(self, name, revid): |
3080 | # FIXME: Not supported yet, should do a push of a new ref |
3081 | @@ -346,6 +387,14 @@ |
3082 | super(RemoteGitBranch, self).__init__(bzrdir, repository, name, |
3083 | lockfiles) |
3084 | |
3085 | + @property |
3086 | + def user_url(self): |
3087 | + return self.control_url |
3088 | + |
3089 | + @property |
3090 | + def control_url(self): |
3091 | + return self.base |
3092 | + |
3093 | def revision_history(self): |
3094 | raise GitSmartRemoteNotSupported() |
3095 | |
3096 | |
3097 | === modified file 'repository.py' |
3098 | --- repository.py 2010-06-28 22:30:34 +0000 |
3099 | +++ repository.py 2011-03-23 20:12:35 +0000 |
3100 | @@ -47,6 +47,7 @@ |
3101 | |
3102 | from dulwich.objects import ( |
3103 | Commit, |
3104 | + Tag, |
3105 | ) |
3106 | |
3107 | |
3108 | @@ -56,10 +57,10 @@ |
3109 | _serializer = None |
3110 | _commit_builder_class = GitCommitBuilder |
3111 | vcs = foreign_git |
3112 | + chk_bytes = None |
3113 | |
3114 | def __init__(self, gitdir, lockfiles): |
3115 | - ForeignRepository.__init__(self, GitRepositoryFormat(), gitdir, |
3116 | - lockfiles) |
3117 | + ForeignRepository.__init__(self, GitRepositoryFormat(), gitdir, lockfiles) |
3118 | from bzrlib.plugins.git import fetch, push |
3119 | for optimiser in [fetch.InterRemoteGitNonGitRepository, |
3120 | fetch.InterLocalGitNonGitRepository, |
3121 | @@ -74,7 +75,7 @@ |
3122 | def supports_rich_root(self): |
3123 | return True |
3124 | |
3125 | - def _warn_if_deprecated(self, branch=None): |
3126 | + def _warn_if_deprecated(self, branch=None): # for bzr < 2.4 |
3127 | # This class isn't deprecated |
3128 | pass |
3129 | |
3130 | @@ -82,7 +83,7 @@ |
3131 | return default_mapping |
3132 | |
3133 | def make_working_trees(self): |
3134 | - return True |
3135 | + return not self._git.bare |
3136 | |
3137 | def revision_graph_can_have_wrong_parents(self): |
3138 | return False |
3139 | @@ -105,18 +106,21 @@ |
3140 | self.texts = GitTexts(self) |
3141 | |
3142 | def _iter_revision_ids(self): |
3143 | + mapping = self.get_mapping() |
3144 | for sha in self._git.object_store: |
3145 | o = self._git.object_store[sha] |
3146 | if not isinstance(o, Commit): |
3147 | continue |
3148 | - rev = self.get_mapping().import_commit(o, |
3149 | + rev, roundtrip_revid, verifiers = mapping.import_commit(o, |
3150 | self.lookup_foreign_revision_id) |
3151 | - yield o.id, rev.revision_id |
3152 | + yield o.id, rev.revision_id, roundtrip_revid |
3153 | |
3154 | def all_revision_ids(self): |
3155 | ret = set([]) |
3156 | - for git_sha, revid in self._iter_revision_ids(): |
3157 | + for git_sha, revid, roundtrip_revid in self._iter_revision_ids(): |
3158 | ret.add(revid) |
3159 | + if roundtrip_revid: |
3160 | + ret.add(roundtrip_revid) |
3161 | return ret |
3162 | |
3163 | def get_parent_map(self, revids): |
3164 | @@ -131,7 +135,12 @@ |
3165 | commit = self._git[hexsha] |
3166 | except KeyError: |
3167 | continue |
3168 | - parent_map[revision_id] = [self.lookup_foreign_revision_id(p, mapping) for p in commit.parents] |
3169 | + parents = [ |
3170 | + self.lookup_foreign_revision_id(p, mapping) |
3171 | + for p in commit.parents] |
3172 | + if parents == []: |
3173 | + parents = [revision.NULL_REVISION] |
3174 | + parent_map[revision_id] = tuple(parents) |
3175 | return parent_map |
3176 | |
3177 | def get_ancestry(self, revision_id, topo_sorted=True): |
3178 | @@ -144,6 +153,8 @@ |
3179 | graph = self.get_graph() |
3180 | for rev, parents in graph.iter_ancestry([revision_id]): |
3181 | ancestry.append(rev) |
3182 | + if revision.NULL_REVISION in ancestry: |
3183 | + ancestry.remove(revision.NULL_REVISION) |
3184 | ancestry.reverse() |
3185 | return [None] + ancestry |
3186 | |
3187 | @@ -157,6 +168,7 @@ |
3188 | """Lookup a revision id. |
3189 | |
3190 | """ |
3191 | + assert type(foreign_revid) is str |
3192 | if mapping is None: |
3193 | mapping = self.get_mapping() |
3194 | from dulwich.protocol import ( |
3195 | @@ -165,8 +177,15 @@ |
3196 | if foreign_revid == ZERO_SHA: |
3197 | return revision.NULL_REVISION |
3198 | commit = self._git[foreign_revid] |
3199 | - rev = mapping.import_commit(commit, lambda x: None) |
3200 | - return rev.revision_id |
3201 | + while isinstance(commit, Tag): |
3202 | + commit = self._git[commit.object[1]] |
3203 | + rev, roundtrip_revid, verifiers = mapping.import_commit(commit, |
3204 | + lambda x: None) |
3205 | + # FIXME: check testament before doing this? |
3206 | + if roundtrip_revid: |
3207 | + return roundtrip_revid |
3208 | + else: |
3209 | + return rev.revision_id |
3210 | |
3211 | def has_signature_for_revision_id(self, revision_id): |
3212 | return False |
3213 | @@ -178,29 +197,39 @@ |
3214 | if mapping is None: |
3215 | mapping = self.get_mapping() |
3216 | try: |
3217 | - return self._git.refs[mapping.revid_as_refname(bzr_revid)], mapping |
3218 | + return (self._git.refs[mapping.revid_as_refname(bzr_revid)], mapping) |
3219 | except KeyError: |
3220 | # Update refs from Git commit objects |
3221 | # FIXME: Hitting this a lot will be very inefficient... |
3222 | - for git_sha, revid in self._iter_revision_ids(): |
3223 | - self._git.refs[mapping.revid_as_refname(revid)] = git_sha |
3224 | - if revid == bzr_revid: |
3225 | + for git_sha, revid, roundtrip_revid in self._iter_revision_ids(): |
3226 | + if not roundtrip_revid: |
3227 | + continue |
3228 | + refname = mapping.revid_as_refname(roundtrip_revid) |
3229 | + self._git.refs[refname] = git_sha |
3230 | + if roundtrip_revid == bzr_revid: |
3231 | return git_sha, mapping |
3232 | raise errors.NoSuchRevision(self, bzr_revid) |
3233 | |
3234 | def get_revision(self, revision_id): |
3235 | + if not isinstance(revision_id, str): |
3236 | + raise errors.InvalidRevisionId(revision_id, self) |
3237 | git_commit_id, mapping = self.lookup_bzr_revision_id(revision_id) |
3238 | try: |
3239 | commit = self._git[git_commit_id] |
3240 | except KeyError: |
3241 | raise errors.NoSuchRevision(self, revision_id) |
3242 | - # print "fetched revision:", git_commit_id |
3243 | - revision = mapping.import_commit(commit, |
3244 | - self.lookup_foreign_revision_id) |
3245 | + revision, roundtrip_revid, verifiers = mapping.import_commit( |
3246 | + commit, self.lookup_foreign_revision_id) |
3247 | assert revision is not None |
3248 | + # FIXME: check verifiers ? |
3249 | + if roundtrip_revid: |
3250 | + revision.revision_id = roundtrip_revid |
3251 | return revision |
3252 | |
3253 | def has_revision(self, revision_id): |
3254 | + """See Repository.has_revision.""" |
3255 | + if revision_id == revision.NULL_REVISION: |
3256 | + return True |
3257 | try: |
3258 | git_commit_id, mapping = self.lookup_bzr_revision_id(revision_id) |
3259 | except errors.NoSuchRevision: |
3260 | @@ -208,16 +237,20 @@ |
3261 | return (git_commit_id in self._git) |
3262 | |
3263 | def has_revisions(self, revision_ids): |
3264 | + """See Repository.has_revisions.""" |
3265 | return set(filter(self.has_revision, revision_ids)) |
3266 | |
3267 | def get_revisions(self, revids): |
3268 | + """See Repository.get_revisions.""" |
3269 | return [self.get_revision(r) for r in revids] |
3270 | |
3271 | def revision_trees(self, revids): |
3272 | + """See Repository.revision_trees.""" |
3273 | for revid in revids: |
3274 | yield self.revision_tree(revid) |
3275 | |
3276 | def revision_tree(self, revision_id): |
3277 | + """See Repository.revision_tree.""" |
3278 | revision_id = revision.ensure_null(revision_id) |
3279 | if revision_id == revision.NULL_REVISION: |
3280 | inv = inventory.Inventory(root_id=None) |
3281 | @@ -230,17 +263,16 @@ |
3282 | return self.revision_tree(revision_id).inventory |
3283 | |
3284 | def set_make_working_trees(self, trees): |
3285 | - pass |
3286 | + raise NotImplementedError(self.set_make_working_trees) |
3287 | |
3288 | def fetch_objects(self, determine_wants, graph_walker, resolve_ext_ref, |
3289 | progress=None): |
3290 | return self._git.fetch_objects(determine_wants, graph_walker, progress) |
3291 | |
3292 | - def _get_versioned_file_checker(self, text_key_references=None, |
3293 | - ancestors=None): |
3294 | + def _get_versioned_file_checker(self, text_key_references=None, ancestors=None): |
3295 | return GitVersionedFileChecker(self, |
3296 | text_key_references=text_key_references, ancestors=ancestors) |
3297 | - |
3298 | + |
3299 | |
3300 | class GitVersionedFileChecker(repository._VersionedFileChecker): |
3301 | |
3302 | @@ -255,12 +287,25 @@ |
3303 | |
3304 | supports_tree_reference = False |
3305 | rich_root_data = True |
3306 | + supports_leaving_lock = False |
3307 | + fast_deltas = True |
3308 | + supports_funky_characters = True |
3309 | + supports_external_lookups = False |
3310 | + supports_full_versioned_files = False |
3311 | + |
3312 | + @property |
3313 | + def _matchingbzrdir(self): |
3314 | + from bzrlib.plugins.git.dir import LocalGitControlDirFormat |
3315 | + return LocalGitControlDirFormat() |
3316 | |
3317 | def get_format_description(self): |
3318 | return "Git Repository" |
3319 | |
3320 | - def initialize(self, url, shared=False, _internal=False): |
3321 | - raise errors.UninitializableFormat(self) |
3322 | + def initialize(self, controldir, shared=False, _internal=False): |
3323 | + from bzrlib.plugins.git.dir import GitDir |
3324 | + if not isinstance(controldir, GitDir): |
3325 | + raise errors.UninitializableFormat(self) |
3326 | + return controldir.open_repository() |
3327 | |
3328 | def check_conversion_target(self, target_repo_format): |
3329 | return target_repo_format.rich_root_data |
3330 | |
3331 | === modified file 'roundtrip.py' |
3332 | --- roundtrip.py 2010-05-13 08:40:18 +0000 |
3333 | +++ roundtrip.py 2011-03-23 20:12:35 +0000 |
3334 | @@ -22,16 +22,19 @@ |
3335 | |
3336 | class BzrGitRevisionMetadata(object): |
3337 | """Metadata for a Bazaar revision roundtripped into Git. |
3338 | - |
3339 | + |
3340 | :ivar revision_id: Revision id, as string |
3341 | :ivar properties: Revision properties, as dictionary |
3342 | :ivar explicit_parent_ids: Parent ids (needed if there are ghosts) |
3343 | + :ivar verifiers: Verifier information |
3344 | """ |
3345 | |
3346 | revision_id = None |
3347 | |
3348 | explicit_parent_ids = None |
3349 | |
3350 | + verifiers = {} |
3351 | + |
3352 | def __init__(self): |
3353 | self.properties = {} |
3354 | |
3355 | @@ -49,6 +52,8 @@ |
3356 | ret.revision_id = value.strip() |
3357 | elif key == "parent-ids": |
3358 | ret.explicit_parent_ids = tuple(value.strip().split(" ")) |
3359 | + elif key == "testament3-sha1": |
3360 | + ret.verifiers["testament3-sha1"] = value.strip() |
3361 | elif key.startswith("property-"): |
3362 | ret.properties[key[len("property-"):]] = value[1:].rstrip("\n") |
3363 | else: |
3364 | @@ -69,6 +74,9 @@ |
3365 | lines.append("parent-ids: %s\n" % " ".join(metadata.explicit_parent_ids)) |
3366 | for key in sorted(metadata.properties.keys()): |
3367 | lines.append("property-%s: %s\n" % (key.encode(encoding), metadata.properties[key].encode(encoding))) |
3368 | + if "testament3-sha1" in metadata.verifiers: |
3369 | + lines.append("testament3-sha1: %s\n" % |
3370 | + metadata.verifiers["testament3-sha1"]) |
3371 | return "".join(lines) |
3372 | |
3373 | |
3374 | @@ -88,20 +96,24 @@ |
3375 | if not metadata: |
3376 | return message |
3377 | rt_data = generate_roundtripping_metadata(metadata, encoding) |
3378 | + if not rt_data: |
3379 | + return message |
3380 | assert type(rt_data) == str |
3381 | return message + "\n--BZR--\n" + rt_data |
3382 | |
3383 | |
3384 | def serialize_fileid_map(file_ids): |
3385 | + """Serialize a file id map.""" |
3386 | lines = [] |
3387 | for path in sorted(file_ids.keys()): |
3388 | lines.append("%s\0%s\n" % (path, file_ids[path])) |
3389 | return lines |
3390 | |
3391 | |
3392 | -def deserialize_fileid_map(file): |
3393 | +def deserialize_fileid_map(filetext): |
3394 | + """Deserialize a file id map.""" |
3395 | ret = {} |
3396 | - f = StringIO(file) |
3397 | + f = StringIO(filetext) |
3398 | lines = f.readlines() |
3399 | for l in lines: |
3400 | (path, file_id) = l.rstrip("\n").split("\0") |
3401 | |
3402 | === modified file 'send.py' |
3403 | --- send.py 2010-03-13 02:48:37 +0000 |
3404 | +++ send.py 2011-03-23 20:12:35 +0000 |
3405 | @@ -30,12 +30,7 @@ |
3406 | revision as _mod_revision, |
3407 | ) |
3408 | |
3409 | -try: |
3410 | - from bzrlib.merge_directive import BaseMergeDirective |
3411 | -except ImportError: |
3412 | - from bzrlib.merge_directive import ( |
3413 | - _BaseMergeDirective as BaseMergeDirective, |
3414 | - ) |
3415 | +from bzrlib.merge_directive import BaseMergeDirective |
3416 | |
3417 | from bzrlib.plugins.git import ( |
3418 | version_info as bzr_git_version_info, |
3419 | |
3420 | === modified file 'tests/__init__.py' |
3421 | --- tests/__init__.py 2010-08-03 21:37:59 +0000 |
3422 | +++ tests/__init__.py 2011-03-23 20:12:35 +0000 |
3423 | @@ -27,6 +27,9 @@ |
3424 | from bzrlib.plugins.git import ( |
3425 | import_dulwich, |
3426 | ) |
3427 | +from fastimport import ( |
3428 | + commands, |
3429 | + ) |
3430 | |
3431 | TestCase = tests.TestCase |
3432 | TestCaseInTempDir = tests.TestCaseInTempDir |
3433 | @@ -73,11 +76,8 @@ |
3434 | |
3435 | def _create_blob(self, content): |
3436 | self._counter += 1 |
3437 | - self._write('blob\n') |
3438 | - self._write('mark :%d\n' % (self._counter,)) |
3439 | - self._write('data %d\n' % (len(content),)) |
3440 | - self._write(content) |
3441 | - self._write('\n') |
3442 | + blob = commands.BlobCommand(str(self._counter), content) |
3443 | + self._write(str(blob)+"\n") |
3444 | return self._counter |
3445 | |
3446 | def set_symlink(self, path, content): |
3447 | @@ -136,11 +136,11 @@ |
3448 | commit. |
3449 | """ |
3450 | self._counter += 1 |
3451 | - mark = self._counter |
3452 | + mark = str(self._counter) |
3453 | if timestamp is None: |
3454 | timestamp = int(time.time()) |
3455 | self._write('commit %s\n' % (self._branch,)) |
3456 | - self._write('mark :%d\n' % (mark,)) |
3457 | + self._write('mark :%s\n' % (mark,)) |
3458 | self._write('committer %s %s %s\n' |
3459 | % (committer, timestamp, timezone)) |
3460 | message = message.encode('UTF-8') |
3461 | @@ -148,10 +148,10 @@ |
3462 | self._write(message) |
3463 | self._write('\n') |
3464 | if base is not None: |
3465 | - self._write('from :%d\n' % (base,)) |
3466 | + self._write('from :%s\n' % (base,)) |
3467 | if merge is not None: |
3468 | for m in merge: |
3469 | - self._write('merge :%d\n' % (m,)) |
3470 | + self._write('merge :%s\n' % (m,)) |
3471 | self._writelines(self.commit_info) |
3472 | self._write('\n') |
3473 | self.commit_info = [] |
3474 | @@ -167,7 +167,7 @@ |
3475 | ref = self._branch |
3476 | self._write('reset %s\n' % (ref,)) |
3477 | if mark is not None: |
3478 | - self._write('from :%d\n' % mark) |
3479 | + self._write('from :%s\n' % mark) |
3480 | self._write('\n') |
3481 | |
3482 | def finish(self): |
3483 | @@ -176,15 +176,15 @@ |
3484 | if self.orig_stream is None: |
3485 | from dulwich.repo import Repo |
3486 | r = Repo(".") |
3487 | - from dulwich.fastexport import FastImporter |
3488 | - importer = FastImporter(r) |
3489 | + from dulwich.fastexport import GitImportProcessor |
3490 | + importer = GitImportProcessor(r) |
3491 | return importer.import_stream(self.stream) |
3492 | |
3493 | |
3494 | def test_suite(): |
3495 | - loader = tests.TestLoader() |
3496 | + loader = tests.TestUtil.TestLoader() |
3497 | |
3498 | - suite = tests.TestSuite() |
3499 | + suite = tests.TestUtil.TestSuite() |
3500 | |
3501 | testmod_names = [ |
3502 | 'test_blackbox', |
3503 | @@ -202,6 +202,7 @@ |
3504 | 'test_revspec', |
3505 | 'test_roundtrip', |
3506 | 'test_transportgit', |
3507 | + 'test_versionedfiles', |
3508 | ] |
3509 | testmod_names = ['%s.%s' % (__name__, t) for t in testmod_names] |
3510 | suite.addTests(loader.loadTestsFromModuleNames(testmod_names)) |
3511 | |
3512 | === modified file 'tests/test_blackbox.py' |
3513 | --- tests/test_blackbox.py 2010-05-22 22:55:03 +0000 |
3514 | +++ tests/test_blackbox.py 2011-03-23 20:12:35 +0000 |
3515 | @@ -76,7 +76,7 @@ |
3516 | self.assertEqual(output, "a\n") |
3517 | |
3518 | def test_init(self): |
3519 | - self.run_bzr("init-repo --git repo") |
3520 | + self.run_bzr("init --git repo") |
3521 | |
3522 | def test_info_verbose(self): |
3523 | self.simple_commit() |
3524 | @@ -128,11 +128,12 @@ |
3525 | |
3526 | output, error = self.run_bzr(["tag", "bar"]) |
3527 | |
3528 | - self.assertEquals(error, '') |
3529 | - self.assertEquals(output, 'Created tag bar.\n') |
3530 | + # bzr <= 2.2 emits this message in the output stream |
3531 | + # bzr => 2.3 emits this message in the error stream |
3532 | + self.assertEquals(error + output, 'Created tag bar.\n') |
3533 | |
3534 | def test_init_repo(self): |
3535 | - output, error = self.run_bzr(["init-repo", "--git", "bla.git"]) |
3536 | + output, error = self.run_bzr(["init", "--git", "bla.git"]) |
3537 | self.assertEquals(error, '') |
3538 | - self.assertEquals(output, 'Unshared repository with trees (format: git)\nLocation:\n repository: bla.git\n') |
3539 | + self.assertEquals(output, 'Created a standalone tree (format: git)\n') |
3540 | |
3541 | |
3542 | === modified file 'tests/test_branch.py' |
3543 | --- tests/test_branch.py 2010-08-03 20:14:44 +0000 |
3544 | +++ tests/test_branch.py 2011-03-23 20:12:35 +0000 |
3545 | @@ -45,10 +45,12 @@ |
3546 | ) |
3547 | |
3548 | from bzrlib.plugins.git import ( |
3549 | - LocalGitBzrDirFormat, |
3550 | branch, |
3551 | tests, |
3552 | ) |
3553 | +from bzrlib.plugins.git.dir import ( |
3554 | + LocalGitControlDirFormat, |
3555 | + ) |
3556 | from bzrlib.plugins.git.mapping import ( |
3557 | default_mapping, |
3558 | ) |
3559 | @@ -248,11 +250,24 @@ |
3560 | inter_branch.pull(limit=1) |
3561 | self.assertEquals(revid2, newbranch.last_revision()) |
3562 | |
3563 | + def test_interbranch_pull_with_tags(self): |
3564 | + path, (gitsha1, gitsha2) = self.make_tworev_branch() |
3565 | + gitrepo = GitRepo(path) |
3566 | + gitrepo.refs["refs/tags/sometag"] = gitsha2 |
3567 | + oldrepo = Repository.open(path) |
3568 | + revid1 = oldrepo.get_mapping().revision_id_foreign_to_bzr(gitsha1) |
3569 | + revid2 = oldrepo.get_mapping().revision_id_foreign_to_bzr(gitsha2) |
3570 | + newbranch = self.make_branch('g') |
3571 | + inter_branch = InterBranch.get(Branch.open(path), newbranch) |
3572 | + inter_branch.pull(stop_revision=revid1) |
3573 | + self.assertEquals(revid1, newbranch.last_revision()) |
3574 | + self.assertTrue(newbranch.repository.has_revision(revid2)) |
3575 | + |
3576 | |
3577 | class ForeignTestsBranchFactory(object): |
3578 | |
3579 | def make_empty_branch(self, transport): |
3580 | - d = LocalGitBzrDirFormat().initialize_on_transport(transport) |
3581 | + d = LocalGitControlDirFormat().initialize_on_transport(transport) |
3582 | return d.create_branch() |
3583 | |
3584 | make_branch = make_empty_branch |
3585 | |
3586 | === modified file 'tests/test_builder.py' |
3587 | --- tests/test_builder.py 2010-08-03 20:14:44 +0000 |
3588 | +++ tests/test_builder.py 2011-03-23 20:12:35 +0000 |
3589 | @@ -112,7 +112,7 @@ |
3590 | builder = tests.GitBranchBuilder(stream) |
3591 | |
3592 | builder.set_file(u'f\xb5/bar', 'contents\nbar\n', False) |
3593 | - self.assertEqual(2, builder.commit('Joe Foo <joe@foo.com>', |
3594 | + self.assertEqual('2', builder.commit('Joe Foo <joe@foo.com>', |
3595 | u'committing f\xb5/bar', |
3596 | timestamp=1194586400, |
3597 | timezone='+0100')) |
3598 | @@ -253,6 +253,6 @@ |
3599 | r1 = builder.commit('Joe Foo <joe@foo.com>', u'first', |
3600 | timestamp=1194586400) |
3601 | mapping = builder.finish() |
3602 | - self.assertEqual({1:'44411e8e9202177dd19b6599d7a7991059fa3cb4', |
3603 | - 2: 'b0b62e674f67306fddcf72fa888c3b56df100d64', |
3604 | + self.assertEqual({'1':'44411e8e9202177dd19b6599d7a7991059fa3cb4', |
3605 | + '2': 'b0b62e674f67306fddcf72fa888c3b56df100d64', |
3606 | }, mapping) |
3607 | |
3608 | === modified file 'tests/test_cache.py' |
3609 | --- tests/test_cache.py 2010-06-28 21:30:29 +0000 |
3610 | +++ tests/test_cache.py 2011-03-23 20:12:35 +0000 |
3611 | @@ -25,6 +25,8 @@ |
3612 | import os |
3613 | import stat |
3614 | |
3615 | +from bzrlib import osutils |
3616 | + |
3617 | from bzrlib.inventory import ( |
3618 | InventoryFile, |
3619 | InventoryDirectory, |
3620 | @@ -70,11 +72,16 @@ |
3621 | self.map.start_write_group() |
3622 | updater = self.cache.get_updater(Revision("myrevid")) |
3623 | c = self._get_test_commit() |
3624 | - updater.add_object(c, None, None) |
3625 | + updater.add_object(c, { |
3626 | + "testament3-sha1": "cc9462f7f8263ef5adf8eff2fb936bb36b504cba"}, |
3627 | + None) |
3628 | updater.finish() |
3629 | self.map.commit_write_group() |
3630 | self.assertEquals( |
3631 | - ("commit", ("myrevid", "cc9462f7f8263ef5adfbeff2fb936bb36b504cba")), |
3632 | + ("commit", ("myrevid", |
3633 | + "cc9462f7f8263ef5adfbeff2fb936bb36b504cba", |
3634 | + {"testament3-sha1": "cc9462f7f8263ef5adf8eff2fb936bb36b504cba"}, |
3635 | + )), |
3636 | self.map.lookup_git_sha(c.id)) |
3637 | self.assertEquals(c.id, self.map.lookup_commit("myrevid")) |
3638 | |
3639 | @@ -85,7 +92,7 @@ |
3640 | def test_blob(self): |
3641 | self.map.start_write_group() |
3642 | updater = self.cache.get_updater(Revision("myrevid")) |
3643 | - updater.add_object(self._get_test_commit(), None, None) |
3644 | + updater.add_object(self._get_test_commit(), { "testament3-sha1": "Test" }, None) |
3645 | b = Blob() |
3646 | b.data = "TEH BLOB" |
3647 | ie = InventoryFile("myfileid", "somename", ROOT_ID) |
3648 | @@ -102,7 +109,8 @@ |
3649 | def test_tree(self): |
3650 | self.map.start_write_group() |
3651 | updater = self.cache.get_updater(Revision("myrevid")) |
3652 | - updater.add_object(self._get_test_commit(), None, None) |
3653 | + updater.add_object(self._get_test_commit(), { |
3654 | + "testament3-sha1": "mytestamentsha" }, None) |
3655 | t = Tree() |
3656 | t.add(stat.S_IFREG, "somename", Blob().id) |
3657 | ie = InventoryDirectory("fileid", "myname", ROOT_ID) |
3658 | @@ -123,7 +131,7 @@ |
3659 | self.map.start_write_group() |
3660 | updater = self.cache.get_updater(Revision("myrevid")) |
3661 | c = self._get_test_commit() |
3662 | - updater.add_object(c, None, None) |
3663 | + updater.add_object(c, {"testament3-sha1": "mtestament"}, None) |
3664 | updater.finish() |
3665 | self.map.commit_write_group() |
3666 | self.assertEquals(["myrevid"], list(self.map.revids())) |
3667 | @@ -132,7 +140,7 @@ |
3668 | self.map.start_write_group() |
3669 | updater = self.cache.get_updater(Revision("myrevid")) |
3670 | c = self._get_test_commit() |
3671 | - updater.add_object(c, None, None) |
3672 | + updater.add_object(c, {"testament3-sha1": "testament"}, None) |
3673 | updater.finish() |
3674 | self.map.commit_write_group() |
3675 | self.assertEquals(set(["lala", "bla"]), |
3676 | @@ -160,7 +168,8 @@ |
3677 | def setUp(self): |
3678 | TestCaseInTempDir.setUp(self) |
3679 | try: |
3680 | - self.cache = TdbBzrGitCache(os.path.join(self.test_dir, 'foo.tdb')) |
3681 | + self.cache = TdbBzrGitCache( |
3682 | + os.path.join(self.test_dir, 'foo.tdb').encode(osutils._fs_enc)) |
3683 | except ImportError: |
3684 | raise UnavailableFeature("Missing tdb") |
3685 | self.map = self.cache.idmap |
3686 | |
3687 | === modified file 'tests/test_dir.py' |
3688 | --- tests/test_dir.py 2010-08-03 20:14:44 +0000 |
3689 | +++ tests/test_dir.py 2011-03-23 20:12:35 +0000 |
3690 | @@ -58,14 +58,14 @@ |
3691 | |
3692 | def setUp(self): |
3693 | super(TestGitDirFormat, self).setUp() |
3694 | - self.format = dir.LocalGitBzrDirFormat() |
3695 | + self.format = dir.LocalGitControlDirFormat() |
3696 | |
3697 | def test_get_format_description(self): |
3698 | self.assertEquals("Local Git Repository", |
3699 | self.format.get_format_description()) |
3700 | |
3701 | def test_eq(self): |
3702 | - format2 = dir.LocalGitBzrDirFormat() |
3703 | + format2 = dir.LocalGitControlDirFormat() |
3704 | self.assertEquals(self.format, format2) |
3705 | self.assertEquals(self.format, self.format) |
3706 | bzr_format = bzrdir.format_registry.make_bzrdir("default") |
3707 | |
3708 | === modified file 'tests/test_fetch.py' |
3709 | --- tests/test_fetch.py 2010-06-28 21:30:29 +0000 |
3710 | +++ tests/test_fetch.py 2011-03-23 20:12:35 +0000 |
3711 | @@ -56,7 +56,7 @@ |
3712 | ) |
3713 | |
3714 | |
3715 | -class RepositoryFetchTests: |
3716 | +class RepositoryFetchTests(object): |
3717 | |
3718 | def make_git_repo(self, path): |
3719 | os.mkdir(path) |
3720 | |
3721 | === modified file 'tests/test_mapping.py' |
3722 | --- tests/test_mapping.py 2010-07-30 20:42:27 +0000 |
3723 | +++ tests/test_mapping.py 2011-03-23 20:12:35 +0000 |
3724 | @@ -97,7 +97,10 @@ |
3725 | c.author_timezone = 60 * 3 |
3726 | c.author = "Author" |
3727 | mapping = BzrGitMappingv1() |
3728 | - rev = mapping.import_commit(c, mapping.revision_id_foreign_to_bzr) |
3729 | + rev, roundtrip_revid, verifiers = mapping.import_commit(c, |
3730 | + mapping.revision_id_foreign_to_bzr) |
3731 | + self.assertEquals(None, roundtrip_revid) |
3732 | + self.assertEquals({}, verifiers) |
3733 | self.assertEquals("Some message", rev.message) |
3734 | self.assertEquals("Committer", rev.committer) |
3735 | self.assertEquals("Author", rev.properties['author']) |
3736 | @@ -119,7 +122,10 @@ |
3737 | c.author = u"Authér".encode("iso8859-1") |
3738 | c.encoding = "iso8859-1" |
3739 | mapping = BzrGitMappingv1() |
3740 | - rev = mapping.import_commit(c, mapping.revision_id_foreign_to_bzr) |
3741 | + rev, roundtrip_revid, verifiers = mapping.import_commit(c, |
3742 | + mapping.revision_id_foreign_to_bzr) |
3743 | + self.assertEquals(None, roundtrip_revid) |
3744 | + self.assertEquals({}, verifiers) |
3745 | self.assertEquals(u"Authér", rev.properties['author']) |
3746 | self.assertEquals("iso8859-1", rev.properties["git-explicit-encoding"]) |
3747 | self.assertTrue("git-implicit-encoding" not in rev.properties) |
3748 | @@ -135,7 +141,10 @@ |
3749 | c.author_timezone = 60 * 3 |
3750 | c.author = u"Authér".encode("latin1") |
3751 | mapping = BzrGitMappingv1() |
3752 | - rev = mapping.import_commit(c, mapping.revision_id_foreign_to_bzr) |
3753 | + rev, roundtrip_revid, verifiers = mapping.import_commit(c, |
3754 | + mapping.revision_id_foreign_to_bzr) |
3755 | + self.assertEquals(None, roundtrip_revid) |
3756 | + self.assertEquals({}, verifiers) |
3757 | self.assertEquals(u"Authér", rev.properties['author']) |
3758 | self.assertEquals("latin1", rev.properties["git-implicit-encoding"]) |
3759 | self.assertTrue("git-explicit-encoding" not in rev.properties) |
3760 | @@ -151,7 +160,10 @@ |
3761 | c.author_timezone = 60 * 3 |
3762 | c.author = u"Authér".encode("utf-8") |
3763 | mapping = BzrGitMappingv1() |
3764 | - rev = mapping.import_commit(c, mapping.revision_id_foreign_to_bzr) |
3765 | + rev, roundtrip_revid, verifiers = mapping.import_commit(c, |
3766 | + mapping.revision_id_foreign_to_bzr) |
3767 | + self.assertEquals(None, roundtrip_revid) |
3768 | + self.assertEquals({}, verifiers) |
3769 | self.assertEquals(u"Authér", rev.properties['author']) |
3770 | self.assertTrue("git-explicit-encoding" not in rev.properties) |
3771 | self.assertTrue("git-implicit-encoding" not in rev.properties) |
3772 | @@ -167,17 +179,22 @@ |
3773 | |
3774 | def assertRoundtripRevision(self, orig_rev): |
3775 | commit = self.mapping.export_commit(orig_rev, "mysha", |
3776 | - self._lookup_parent, True) |
3777 | - rev = self.mapping.import_commit(commit, |
3778 | - self.mapping.revision_id_foreign_to_bzr) |
3779 | + self._lookup_parent, True, "testamentsha") |
3780 | + rev, roundtrip_revid, verifiers = self.mapping.import_commit( |
3781 | + commit, self.mapping.revision_id_foreign_to_bzr) |
3782 | + self.assertEquals(rev.revision_id, |
3783 | + self.mapping.revision_id_foreign_to_bzr(commit.id)) |
3784 | if self.mapping.roundtripping: |
3785 | - self.assertEquals(orig_rev.revision_id, rev.revision_id) |
3786 | + self.assertEquals({"testament3-sha1": "testamentsha"} , verifiers) |
3787 | + self.assertEquals(orig_rev.revision_id, roundtrip_revid) |
3788 | self.assertEquals(orig_rev.properties, rev.properties) |
3789 | self.assertEquals(orig_rev.committer, rev.committer) |
3790 | self.assertEquals(orig_rev.timestamp, rev.timestamp) |
3791 | self.assertEquals(orig_rev.timezone, rev.timezone) |
3792 | self.assertEquals(orig_rev.message, rev.message) |
3793 | self.assertEquals(list(orig_rev.parent_ids), list(rev.parent_ids)) |
3794 | + else: |
3795 | + self.assertEquals({}, verifiers) |
3796 | |
3797 | def test_simple_commit(self): |
3798 | r = Revision(self.mapping.revision_id_foreign_to_bzr("edf99e6c56495c620f20d5dacff9859ff7119261")) |
3799 | @@ -233,10 +250,10 @@ |
3800 | raise NotImplementedError(self.assertRoundtripBlob) |
3801 | |
3802 | def assertRoundtripCommit(self, commit1): |
3803 | - rev = self.mapping.import_commit(commit1, |
3804 | - self.mapping.revision_id_foreign_to_bzr) |
3805 | + rev, roundtrip_revid, verifiers = self.mapping.import_commit( |
3806 | + commit1, self.mapping.revision_id_foreign_to_bzr) |
3807 | commit2 = self.mapping.export_commit(rev, "12341212121212", None, |
3808 | - True) |
3809 | + True, None) |
3810 | self.assertEquals(commit1.committer, commit2.committer) |
3811 | self.assertEquals(commit1.commit_time, commit2.commit_time) |
3812 | self.assertEquals(commit1.commit_timezone, commit2.commit_timezone) |
3813 | |
3814 | === modified file 'tests/test_object_store.py' |
3815 | --- tests/test_object_store.py 2010-07-24 08:40:21 +0000 |
3816 | +++ tests/test_object_store.py 2011-03-23 20:12:35 +0000 |
3817 | @@ -28,17 +28,22 @@ |
3818 | ) |
3819 | from bzrlib.graph import ( |
3820 | DictParentsProvider, |
3821 | + Graph, |
3822 | ) |
3823 | from bzrlib.tests import ( |
3824 | TestCase, |
3825 | TestCaseWithTransport, |
3826 | ) |
3827 | |
3828 | +from bzrlib.plugins.git.cache import ( |
3829 | + DictGitShaMap, |
3830 | + ) |
3831 | from bzrlib.plugins.git.object_store import ( |
3832 | BazaarObjectStore, |
3833 | LRUTreeCache, |
3834 | _check_expected_sha, |
3835 | _find_missing_bzr_revids, |
3836 | + _tree_to_objects, |
3837 | ) |
3838 | |
3839 | |
3840 | @@ -67,7 +72,7 @@ |
3841 | |
3842 | def _find_missing(self, ancestry, want, have): |
3843 | return _find_missing_bzr_revids( |
3844 | - DictParentsProvider(ancestry).get_parent_map, |
3845 | + Graph(DictParentsProvider(ancestry)), |
3846 | set(want), set(have)) |
3847 | |
3848 | def test_simple(self): |
3849 | @@ -172,3 +177,14 @@ |
3850 | bb.finish_series() |
3851 | self.assertTrue(b.id in self.store) |
3852 | |
3853 | + |
3854 | +class TreeToObjectsTests(TestCaseWithTransport): |
3855 | + |
3856 | + def setUp(self): |
3857 | + super(TreeToObjectsTests, self).setUp() |
3858 | + self.idmap = DictGitShaMap() |
3859 | + |
3860 | + def test_no_changes(self): |
3861 | + tree = self.make_branch_and_tree('.') |
3862 | + entries = list(_tree_to_objects(tree, [tree], self.idmap, {})) |
3863 | + self.assertEquals([], entries) |
3864 | |
3865 | === modified file 'tests/test_push.py' |
3866 | --- tests/test_push.py 2010-07-24 09:34:35 +0000 |
3867 | +++ tests/test_push.py 2011-03-23 20:12:35 +0000 |
3868 | @@ -62,5 +62,5 @@ |
3869 | self.assertEquals([], list(self.interrepo.missing_revisions([]))) |
3870 | |
3871 | def test_missing_revisions_unknown_stop_rev(self): |
3872 | - self.assertRaises(errors.NoSuchRevision, |
3873 | - self.interrepo.missing_revisions, ["unknown"]) |
3874 | + self.assertEquals([], |
3875 | + list(self.interrepo.missing_revisions([(None, "unknown")]))) |
3876 | |
3877 | === modified file 'tests/test_refs.py' |
3878 | --- tests/test_refs.py 2010-08-12 19:36:37 +0000 |
3879 | +++ tests/test_refs.py 2011-03-23 20:12:35 +0000 |
3880 | @@ -52,19 +52,19 @@ |
3881 | "HEAD": "ref: foo", "refs/branches/blala": "la"})) |
3882 | |
3883 | def test_tags(self): |
3884 | - self.assertEquals({"mytag": "mysha"}, |
3885 | + self.assertEquals({"mytag": ("mysha", None)}, |
3886 | refs.extract_tags({ |
3887 | "HEAD": "ref: foo", "refs/tags/mytag": "mysha"})) |
3888 | |
3889 | def test_ignores_peels(self): |
3890 | - self.assertEquals({"mytag": "actualsha"}, |
3891 | + self.assertEquals({"mytag": ("actualsha", "mysha")}, |
3892 | refs.extract_tags({ |
3893 | "HEAD": "ref: foo", |
3894 | "refs/tags/mytag": "mysha", |
3895 | "refs/tags/mytag^{}": "actualsha"})) |
3896 | |
3897 | def test_non_ascii_name(self): |
3898 | - self.assertEquals({u'myt\xe2g': "actualsha"}, |
3899 | + self.assertEquals({u'myt\xe2g': ("actualsha", None)}, |
3900 | refs.extract_tags({ |
3901 | "HEAD": "ref: foo", |
3902 | "refs/tags/myt\xc3\xa2g": "actualsha"})) |
3903 | |
3904 | === modified file 'tests/test_repository.py' |
3905 | --- tests/test_repository.py 2010-08-03 20:14:44 +0000 |
3906 | +++ tests/test_repository.py 2011-03-23 20:12:35 +0000 |
3907 | @@ -192,11 +192,12 @@ |
3908 | |
3909 | def test_all_revision_ids(self): |
3910 | commit_id = self._do_commit() |
3911 | - self.assertEquals(set(["git-v1:%s" % commit_id]), |
3912 | + self.assertEquals( |
3913 | + set([default_mapping.revision_id_foreign_to_bzr(commit_id)]), |
3914 | self.git_repo.all_revision_ids()) |
3915 | |
3916 | def test_get_ancestry_null(self): |
3917 | - self.assertEquals([None, revision.NULL_REVISION], self.git_repo.get_ancestry(revision.NULL_REVISION)) |
3918 | + self.assertEquals([None], self.git_repo.get_ancestry(revision.NULL_REVISION)) |
3919 | |
3920 | def assertIsNullInventory(self, inv): |
3921 | self.assertEqual(inv.root, None) |
3922 | @@ -274,4 +275,4 @@ |
3923 | class ForeignTestsRepositoryFactory(object): |
3924 | |
3925 | def make_repository(self, transport): |
3926 | - return dir.LocalGitBzrDirFormat().initialize_on_transport(transport).open_repository() |
3927 | + return dir.LocalGitControlDirFormat().initialize_on_transport(transport).open_repository() |
3928 | |
3929 | === modified file 'tests/test_roundtrip.py' |
3930 | --- tests/test_roundtrip.py 2010-05-13 09:40:48 +0000 |
3931 | +++ tests/test_roundtrip.py 2011-03-23 20:12:35 +0000 |
3932 | @@ -67,6 +67,11 @@ |
3933 | self.assertEquals("property-foo: bar\n", |
3934 | generate_roundtripping_metadata(metadata, "utf-8")) |
3935 | |
3936 | + def test_empty(self): |
3937 | + metadata = BzrGitRevisionMetadata() |
3938 | + self.assertEquals("", |
3939 | + generate_roundtripping_metadata(metadata, "utf-8")) |
3940 | + |
3941 | |
3942 | class ExtractMetadataTests(TestCase): |
3943 | |
3944 | @@ -90,6 +95,11 @@ |
3945 | revision-id: myrevid |
3946 | """, msg) |
3947 | |
3948 | + def test_no_metadata(self): |
3949 | + metadata = BzrGitRevisionMetadata() |
3950 | + msg = inject_bzr_metadata("Foo", metadata, "utf-8") |
3951 | + self.assertEquals("Foo", msg) |
3952 | + |
3953 | |
3954 | class FileIdRoundTripTests(TestCase): |
3955 | |
3956 | |
3957 | === modified file 'tests/test_transportgit.py' |
3958 | --- tests/test_transportgit.py 2010-06-28 23:48:36 +0000 |
3959 | +++ tests/test_transportgit.py 2011-03-23 20:12:35 +0000 |
3960 | @@ -31,6 +31,10 @@ |
3961 | TestCaseWithTransport.setUp(self) |
3962 | self.store = TransportObjectStore.init(self.get_transport()) |
3963 | |
3964 | + def tearDown(self): |
3965 | + PackBasedObjectStoreTests.tearDown(self) |
3966 | + TestCaseWithTransport.tearDown(self) |
3967 | + |
3968 | # FIXME: Unfortunately RefsContainerTests requires on a specific set of refs existing. |
3969 | |
3970 | # class TransportRefContainerTests(RefsContainerTests, TestCaseWithTransport): |
3971 | |
3972 | === added file 'tests/test_versionedfiles.py' |
3973 | --- tests/test_versionedfiles.py 1970-01-01 00:00:00 +0000 |
3974 | +++ tests/test_versionedfiles.py 2011-03-23 20:12:35 +0000 |
3975 | @@ -0,0 +1,50 @@ |
3976 | +# Copyright (C) 2011 Canonical Ltd |
3977 | +# Authors: Jelmer Vernooij <jelmer@canonical.com> |
3978 | +# |
3979 | +# This program is free software; you can redistribute it and/or modify |
3980 | +# it under the terms of the GNU General Public License as published by |
3981 | +# the Free Software Foundation; either version 2 of the License, or |
3982 | +# (at your option) any later version. |
3983 | +# |
3984 | +# This program is distributed in the hope that it will be useful, |
3985 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3986 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3987 | +# GNU General Public License for more details. |
3988 | +# |
3989 | +# You should have received a copy of the GNU General Public License |
3990 | +# along with this program; if not, write to the Free Software |
3991 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
3992 | + |
3993 | +"""Tests for git-like versioned files.""" |
3994 | + |
3995 | +from dulwich.repo import Repo as GitRepo |
3996 | + |
3997 | +from bzrlib.repository import Repository |
3998 | +from bzrlib.tests import TestCaseInTempDir |
3999 | + |
4000 | +from bzrlib.plugins.git import tests |
4001 | + |
4002 | +class GitRevisionsTests(TestCaseInTempDir): |
4003 | + |
4004 | + def setUp(self): |
4005 | + super(GitRevisionsTests, self).setUp() |
4006 | + self.gitrepo = GitRepo.init(self.test_dir) |
4007 | + self.revisions = Repository.open(self.test_dir).revisions |
4008 | + |
4009 | + def _do_commit(self): |
4010 | + builder = tests.GitBranchBuilder() |
4011 | + builder.set_file('a', 'text for a\n', False) |
4012 | + commit_handle = builder.commit('Joe Foo <joe@foo.com>', u'message') |
4013 | + mapping = builder.finish() |
4014 | + return mapping[commit_handle] |
4015 | + |
4016 | + def test_empty(self): |
4017 | + self.assertEquals([], self.revisions.keys()) |
4018 | + |
4019 | + def test_check(self): |
4020 | + # Just a no-op at the moment |
4021 | + self.assertTrue(self.revisions.check()) |
4022 | + |
4023 | + def test_revision(self): |
4024 | + gitsha = self._do_commit() |
4025 | + self.assertEquals([(gitsha,)], self.revisions.keys()) |
4026 | |
4027 | === modified file 'transportgit.py' |
4028 | --- transportgit.py 2010-08-12 10:18:56 +0000 |
4029 | +++ transportgit.py 2011-03-23 20:12:35 +0000 |
4030 | @@ -42,6 +42,7 @@ |
4031 | from dulwich.repo import ( |
4032 | BaseRepo, |
4033 | RefsContainer, |
4034 | + BASE_DIRECTORIES, |
4035 | INDEX_FILENAME, |
4036 | OBJECTDIR, |
4037 | REFSDIR, |
4038 | @@ -312,6 +313,9 @@ |
4039 | except NoSuchFile: |
4040 | return None |
4041 | |
4042 | + def _put_named_file(self, relpath, contents): |
4043 | + self._controltransport.put_bytes(relpath, contents) |
4044 | + |
4045 | def index_path(self): |
4046 | """Return the path to the index file.""" |
4047 | return self._controltransport.local_abspath(INDEX_FILENAME) |
4048 | @@ -330,7 +334,23 @@ |
4049 | return not self.bare |
4050 | |
4051 | def __repr__(self): |
4052 | - return "<TransportRepo for %r>" % self.transport |
4053 | + return "<%s for %r>" % (self.__class__.__name__, self.transport) |
4054 | + |
4055 | + @classmethod |
4056 | + def init(cls, transport, bare=False): |
4057 | + if not bare: |
4058 | + transport.mkdir(".git") |
4059 | + control_transport = transport.clone(".git") |
4060 | + else: |
4061 | + control_transport = transport |
4062 | + for d in BASE_DIRECTORIES: |
4063 | + control_transport.mkdir("/".join(d)) |
4064 | + control_transport.mkdir(OBJECTDIR) |
4065 | + TransportObjectStore.init(control_transport.clone(OBJECTDIR)) |
4066 | + ret = cls(transport) |
4067 | + ret.refs.set_symbolic_ref("HEAD", "refs/heads/master") |
4068 | + ret._init_files(bare) |
4069 | + return ret |
4070 | |
4071 | |
4072 | class TransportObjectStore(PackBasedObjectStore): |
4073 | @@ -344,7 +364,10 @@ |
4074 | super(TransportObjectStore, self).__init__() |
4075 | self.transport = transport |
4076 | self.pack_transport = self.transport.clone(PACKDIR) |
4077 | - |
4078 | + |
4079 | + def __repr__(self): |
4080 | + return "%s(%r)" % (self.__class__.__name__, self.transport) |
4081 | + |
4082 | def _pack_cache_stale(self): |
4083 | return False # FIXME |
4084 | |
4085 | |
4086 | === modified file 'versionedfiles.py' |
4087 | --- versionedfiles.py 2010-07-30 20:43:28 +0000 |
4088 | +++ versionedfiles.py 2011-03-23 20:12:35 +0000 |
4089 | @@ -48,7 +48,7 @@ |
4090 | |
4091 | def iterkeys(self): |
4092 | for sha in self.object_store: |
4093 | - if type(sha) == Commit: |
4094 | + if isinstance(self.object_store[sha], Commit): |
4095 | yield (sha,) |
4096 | |
4097 | def keys(self): |
4098 | |
4099 | === modified file 'workingtree.py' |
4100 | --- workingtree.py 2010-07-31 19:26:31 +0000 |
4101 | +++ workingtree.py 2011-03-23 20:12:35 +0000 |
4102 | @@ -22,6 +22,9 @@ |
4103 | StringIO, |
4104 | ) |
4105 | import errno |
4106 | +from dulwich.index import ( |
4107 | + Index, |
4108 | + ) |
4109 | from dulwich.objects import ( |
4110 | Blob, |
4111 | ) |
4112 | @@ -46,6 +49,9 @@ |
4113 | ) |
4114 | |
4115 | |
4116 | +from bzrlib.plugins.git.dir import ( |
4117 | + LocalGitDir, |
4118 | + ) |
4119 | from bzrlib.plugins.git.inventory import ( |
4120 | GitIndexInventory, |
4121 | ) |
4122 | @@ -108,9 +114,6 @@ |
4123 | finally: |
4124 | self.branch.unlock() |
4125 | |
4126 | - def is_control_filename(self, path): |
4127 | - return os.path.basename(path) == ".git" |
4128 | - |
4129 | def _rewrite_index(self): |
4130 | self.index.clear() |
4131 | for path, entry in self._inventory.iter_entries(): |
4132 | @@ -136,7 +139,7 @@ |
4133 | # old index |
4134 | from posix import stat_result |
4135 | stat_val = stat_result((stat.S_IFLNK, 0, 0, 0, 0, 0, 0, 0, 0, 0)) |
4136 | - blob.set_raw_string(entry.symlink_target) |
4137 | + blob.set_raw_string(self.get_symlink_target(entry.file_id).encode("utf-8")) |
4138 | else: |
4139 | raise AssertionError("unknown kind '%s'" % entry.kind) |
4140 | # Add object to the repository if it didn't exist yet |
4141 | @@ -215,12 +218,22 @@ |
4142 | |
4143 | @property |
4144 | def _matchingbzrdir(self): |
4145 | - from bzrlib.plugins.git import LocalGitBzrDirFormat |
4146 | - return LocalGitBzrDirFormat() |
4147 | + from bzrlib.plugins.git.dir import LocalGitControlDirFormat |
4148 | + return LocalGitControlDirFormat() |
4149 | |
4150 | def get_format_description(self): |
4151 | return "Git Working Tree" |
4152 | |
4153 | + def initialize(self, a_bzrdir, revision_id=None, from_branch=None, |
4154 | + accelerator_tree=None, hardlink=False): |
4155 | + """See WorkingTreeFormat.initialize().""" |
4156 | + if not isinstance(a_bzrdir, LocalGitDir): |
4157 | + raise errors.IncompatibleFormat(self, a_bzrdir) |
4158 | + index = Index(a_bzrdir.root_transport.local_abspath(".git/index")) |
4159 | + index.write() |
4160 | + return GitWorkingTree(a_bzrdir, a_bzrdir.open_repository(), |
4161 | + a_bzrdir.open_branch(), index) |
4162 | + |
4163 | |
4164 | class InterIndexGitTree(tree.InterTree): |
4165 | """InterTree that works between a Git revision tree and an index.""" |
4166 | @@ -245,17 +258,16 @@ |
4167 | self.source._repository._git.object_store.__getitem__, |
4168 | self.source.tree) |
4169 | if self.target.mapping.BZR_FILE_IDS_FILE is not None: |
4170 | - try: |
4171 | - file_id = self.target.path2id( |
4172 | - self.target.mapping.BZR_FILE_IDS_FILE) |
4173 | - except errors.NoSuchId: |
4174 | + file_id = self.target.path2id( |
4175 | + self.target.mapping.BZR_FILE_IDS_FILE) |
4176 | + if file_id is None: |
4177 | target_fileid_map = {} |
4178 | else: |
4179 | - target_fileid_map = self.import_fileid_map(Blob.from_string(self.target.file_text(file_id))) |
4180 | + target_fileid_map = self.target.mapping.import_fileid_map(Blob.from_string(self.target.get_file_text(file_id))) |
4181 | else: |
4182 | target_fileid_map = {} |
4183 | target_fileid_map = GitFileIdMap(target_fileid_map, self.target.mapping) |
4184 | - ret = tree_delta_from_git_changes(changes, self.target.mapping, |
4185 | + ret = tree_delta_from_git_changes(changes, self.target.mapping, |
4186 | (source_fileid_map, target_fileid_map), |
4187 | specific_file=specific_files, require_versioned=require_versioned) |
4188 | if want_unversioned: |