Merge lp:~jelmer/bzr-git/sprout into lp:~launchpad-pqm/bzr-git/devel

Proposed by Jelmer Vernooij on 2011-03-23
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
Reviewer Review Type Date Requested Status
Launchpad PQM Bot 2011-03-23 Pending
Review via email: mp+54595@code.launchpad.net

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:

Subscribers

People subscribed via source and target branches