1# Copyright (C) 2006-2012, 2016 Canonical Ltd
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17
18"""Tests for the info command of bzr."""
19
20import shutil
21import sys
22
23from breezy import (
24    branch,
25    controldir,
26    errors,
27    info,
28    osutils,
29    tests,
30    upgrade,
31    urlutils,
32    )
33from breezy.bzr import (
34    bzrdir,
35    )
36from breezy.transport import memory
37
38
39class TestInfo(tests.TestCaseWithTransport):
40
41    def setUp(self):
42        super(TestInfo, self).setUp()
43        self._repo_strings = "2a"
44
45    def test_info_non_existing(self):
46        self.vfs_transport_factory = memory.MemoryServer
47        location = self.get_url()
48        out, err = self.run_bzr('info ' + location, retcode=3)
49        self.assertEqual(out, '')
50        self.assertEqual(err, 'brz: ERROR: Not a branch: "%s".\n' % location)
51
52    def test_info_empty_controldir(self):
53        self.make_controldir('ctrl')
54        out, err = self.run_bzr('info ctrl')
55        self.assertEqual(out,
56                         'Empty control directory (format: 2a)\n'
57                         'Location:\n'
58                         '  control directory: ctrl\n')
59        self.assertEqual(err, '')
60
61    def test_info_empty_controldir_verbose(self):
62        self.make_controldir('ctrl')
63        out, err = self.run_bzr('info -v ctrl')
64        self.assertEqualDiff(out,
65                             'Empty control directory (format: 2a)\n'
66                             'Location:\n'
67                             '  control directory: ctrl\n\n'
68                             'Format:\n'
69                             '       control: Meta directory format 1\n\n'
70                             'Control directory:\n'
71                             '         0 branches\n')
72        self.assertEqual(err, '')
73
74    def test_info_dangling_branch_reference(self):
75        br = self.make_branch('target')
76        br.create_checkout('from', lightweight=True)
77        shutil.rmtree('target')
78        out, err = self.run_bzr('info from')
79        self.assertEqual(out,
80                         'Dangling branch reference (format: 2a)\n'
81                         'Location:\n'
82                         '   control directory: from\n'
83                         '  checkout of branch: target\n')
84        self.assertEqual(err, '')
85
86    def test_info_colocated(self):
87        br = self.make_branch_and_tree('target', format='development-colo')
88        target = br.controldir.create_branch(name='dichtbij')
89        br.controldir.set_branch_reference(target)
90        out, err = self.run_bzr('info target')
91        self.assertEqual(out,
92                         'Standalone tree (format: development-colo)\n'
93                         'Location:\n'
94                         '            light checkout root: target\n'
95                         '  checkout of co-located branch: dichtbij\n')
96        self.assertEqual(err, '')
97
98    def test_info_standalone(self):
99        transport = self.get_transport()
100
101        # Create initial standalone branch
102        tree1 = self.make_branch_and_tree('standalone', 'knit')
103        self.build_tree(['standalone/a'])
104        tree1.add('a')
105        branch1 = tree1.branch
106
107        out, err = self.run_bzr('info standalone')
108        self.assertEqualDiff(
109            """Standalone tree (format: knit)
110Location:
111  branch root: standalone
112""", out)
113        self.assertEqual('', err)
114
115        # Standalone branch - verbose mode
116        out, err = self.run_bzr('info standalone -v')
117        self.assertEqualDiff(
118            """Standalone tree (format: knit)
119Location:
120  branch root: standalone
121
122Format:
123       control: Meta directory format 1
124  working tree: Working tree format 3
125        branch: Branch format 5
126    repository: Knit repository format 1
127
128Control directory:
129         1 branches
130
131In the working tree:
132         0 unchanged
133         0 modified
134         1 added
135         0 removed
136         0 renamed
137         0 copied
138         0 unknown
139         0 ignored
140         0 versioned subdirectories
141
142Branch history:
143         0 revisions
144
145Repository:
146         0 revisions
147""", out)
148        self.assertEqual('', err)
149
150        # Standalone branch - really verbose mode
151        out, err = self.run_bzr('info standalone -vv')
152        self.assertEqualDiff(
153            """Standalone tree (format: knit)
154Location:
155  branch root: standalone
156
157Format:
158       control: Meta directory format 1
159  working tree: Working tree format 3
160        branch: Branch format 5
161    repository: Knit repository format 1
162
163Control directory:
164         1 branches
165
166In the working tree:
167         0 unchanged
168         0 modified
169         1 added
170         0 removed
171         0 renamed
172         0 copied
173         0 unknown
174         0 ignored
175         0 versioned subdirectories
176
177Branch history:
178         0 revisions
179         0 committers
180
181Repository:
182         0 revisions
183""", out)
184        self.assertEqual('', err)
185        tree1.commit('commit one')
186        rev = branch1.repository.get_revision(branch1.last_revision())
187        datestring_first = osutils.format_date(rev.timestamp, rev.timezone)
188
189        # Branch standalone with push location
190        branch2 = branch1.controldir.sprout('branch').open_branch()
191        branch2.set_push_location(branch1.controldir.root_transport.base)
192
193        out, err = self.run_bzr('info branch')
194        self.assertEqualDiff(
195            """Standalone tree (format: knit)
196Location:
197  branch root: branch
198
199Related branches:
200    push branch: standalone
201  parent branch: standalone
202""", out)
203        self.assertEqual('', err)
204
205        out, err = self.run_bzr('info branch --verbose')
206        self.assertEqualDiff(
207            """Standalone tree (format: knit)
208Location:
209  branch root: branch
210
211Related branches:
212    push branch: standalone
213  parent branch: standalone
214
215Format:
216       control: Meta directory format 1
217  working tree: Working tree format 3
218        branch: Branch format 5
219    repository: Knit repository format 1
220
221Control directory:
222         1 branches
223
224In the working tree:
225         1 unchanged
226         0 modified
227         0 added
228         0 removed
229         0 renamed
230         0 copied
231         0 unknown
232         0 ignored
233         0 versioned subdirectories
234
235Branch history:
236         1 revision
237         0 days old
238   first revision: %s
239  latest revision: %s
240
241Repository:
242         1 revision
243""" % (datestring_first, datestring_first,
244       ), out)
245        self.assertEqual('', err)
246
247        # Branch and bind to standalone, needs upgrade to metadir
248        # (creates backup as unknown)
249        branch1.controldir.sprout('bound')
250        knit1_format = controldir.format_registry.make_controldir('knit')
251        upgrade.upgrade('bound', knit1_format)
252        branch3 = controldir.ControlDir.open('bound').open_branch()
253        branch3.bind(branch1)
254        bound_tree = branch3.controldir.open_workingtree()
255        out, err = self.run_bzr('info -v bound')
256        self.assertEqualDiff(
257            """Checkout (format: knit)
258Location:
259       checkout root: bound
260  checkout of branch: standalone
261
262Related branches:
263  parent branch: standalone
264
265Format:
266       control: Meta directory format 1
267  working tree: %s
268        branch: %s
269    repository: %s
270
271Control directory:
272         1 branches
273
274In the working tree:
275         1 unchanged
276         0 modified
277         0 added
278         0 removed
279         0 renamed
280         0 copied
281         0 unknown
282         0 ignored
283         0 versioned subdirectories
284
285Branch history:
286         1 revision
287         0 days old
288   first revision: %s
289  latest revision: %s
290
291Repository:
292         1 revision
293""" % (bound_tree._format.get_format_description(),
294                branch3._format.get_format_description(),
295                branch3.repository._format.get_format_description(),
296                datestring_first, datestring_first,
297       ), out)
298        self.assertEqual('', err)
299
300        # Checkout standalone (same as above, but does not have parent set)
301        branch4 = controldir.ControlDir.create_branch_convenience('checkout',
302                                                                  format=knit1_format)
303        branch4.bind(branch1)
304        branch4.controldir.open_workingtree().update()
305        out, err = self.run_bzr('info checkout --verbose')
306        self.assertEqualDiff(
307            """Checkout (format: knit)
308Location:
309       checkout root: checkout
310  checkout of branch: standalone
311
312Format:
313       control: Meta directory format 1
314  working tree: Working tree format 3
315        branch: Branch format 5
316    repository: %s
317
318Control directory:
319         1 branches
320
321In the working tree:
322         1 unchanged
323         0 modified
324         0 added
325         0 removed
326         0 renamed
327         0 copied
328         0 unknown
329         0 ignored
330         0 versioned subdirectories
331
332Branch history:
333         1 revision
334         0 days old
335   first revision: %s
336  latest revision: %s
337
338Repository:
339         1 revision
340""" % (branch4.repository._format.get_format_description(),
341                datestring_first, datestring_first,
342       ), out)
343        self.assertEqual('', err)
344
345        # Lightweight checkout (same as above, different branch and repository)
346        tree5 = branch1.create_checkout('lightcheckout', lightweight=True)
347        branch5 = tree5.branch
348        out, err = self.run_bzr('info -v lightcheckout')
349        if "metaweave" in controldir.format_registry:
350            format_description = "knit or metaweave"
351        else:
352            format_description = "knit"
353        self.assertEqualDiff(
354            """Lightweight checkout (format: %s)
355Location:
356  light checkout root: lightcheckout
357   checkout of branch: standalone
358
359Format:
360       control: Meta directory format 1
361  working tree: Working tree format 3
362        branch: Branch format 5
363    repository: Knit repository format 1
364
365Control directory:
366         1 branches
367
368In the working tree:
369         1 unchanged
370         0 modified
371         0 added
372         0 removed
373         0 renamed
374         0 copied
375         0 unknown
376         0 ignored
377         0 versioned subdirectories
378
379Branch history:
380         1 revision
381         0 days old
382   first revision: %s
383  latest revision: %s
384
385Repository:
386         1 revision
387""" % (format_description, datestring_first, datestring_first,), out)
388        self.assertEqual('', err)
389
390        # Update initial standalone branch
391        self.build_tree(['standalone/b'])
392        tree1.add('b')
393        tree1.commit('commit two')
394        rev = branch1.repository.get_revision(branch1.last_revision())
395        datestring_last = osutils.format_date(rev.timestamp, rev.timezone)
396
397        # Out of date branched standalone branch will not be detected
398        out, err = self.run_bzr('info -v branch')
399        self.assertEqualDiff(
400            """Standalone tree (format: knit)
401Location:
402  branch root: branch
403
404Related branches:
405    push branch: standalone
406  parent branch: standalone
407
408Format:
409       control: Meta directory format 1
410  working tree: Working tree format 3
411        branch: Branch format 5
412    repository: Knit repository format 1
413
414Control directory:
415         1 branches
416
417In the working tree:
418         1 unchanged
419         0 modified
420         0 added
421         0 removed
422         0 renamed
423         0 copied
424         0 unknown
425         0 ignored
426         0 versioned subdirectories
427
428Branch history:
429         1 revision
430         0 days old
431   first revision: %s
432  latest revision: %s
433
434Repository:
435         1 revision
436""" % (datestring_first, datestring_first,
437       ), out)
438        self.assertEqual('', err)
439
440        # Out of date bound branch
441        out, err = self.run_bzr('info -v bound')
442        self.assertEqualDiff(
443            """Checkout (format: knit)
444Location:
445       checkout root: bound
446  checkout of branch: standalone
447
448Related branches:
449  parent branch: standalone
450
451Format:
452       control: Meta directory format 1
453  working tree: Working tree format 3
454        branch: Branch format 5
455    repository: %s
456
457Control directory:
458         1 branches
459
460Branch is out of date: missing 1 revision.
461
462In the working tree:
463         1 unchanged
464         0 modified
465         0 added
466         0 removed
467         0 renamed
468         0 copied
469         0 unknown
470         0 ignored
471         0 versioned subdirectories
472
473Branch history:
474         1 revision
475         0 days old
476   first revision: %s
477  latest revision: %s
478
479Repository:
480         1 revision
481""" % (branch3.repository._format.get_format_description(),
482                datestring_first, datestring_first,
483       ), out)
484        self.assertEqual('', err)
485
486        # Out of date checkout
487        out, err = self.run_bzr('info -v checkout')
488        self.assertEqualDiff(
489            """Checkout (format: knit)
490Location:
491       checkout root: checkout
492  checkout of branch: standalone
493
494Format:
495       control: Meta directory format 1
496  working tree: Working tree format 3
497        branch: Branch format 5
498    repository: %s
499
500Control directory:
501         1 branches
502
503Branch is out of date: missing 1 revision.
504
505In the working tree:
506         1 unchanged
507         0 modified
508         0 added
509         0 removed
510         0 renamed
511         0 copied
512         0 unknown
513         0 ignored
514         0 versioned subdirectories
515
516Branch history:
517         1 revision
518         0 days old
519   first revision: %s
520  latest revision: %s
521
522Repository:
523         1 revision
524""" % (branch4.repository._format.get_format_description(),
525                datestring_first, datestring_first,
526       ), out)
527        self.assertEqual('', err)
528
529        # Out of date lightweight checkout
530        out, err = self.run_bzr('info lightcheckout --verbose')
531        self.assertEqualDiff(
532            """Lightweight checkout (format: %s)
533Location:
534  light checkout root: lightcheckout
535   checkout of branch: standalone
536
537Format:
538       control: Meta directory format 1
539  working tree: Working tree format 3
540        branch: Branch format 5
541    repository: Knit repository format 1
542
543Control directory:
544         1 branches
545
546Working tree is out of date: missing 1 revision.
547
548In the working tree:
549         1 unchanged
550         0 modified
551         0 added
552         0 removed
553         0 renamed
554         0 copied
555         0 unknown
556         0 ignored
557         0 versioned subdirectories
558
559Branch history:
560         2 revisions
561         0 days old
562   first revision: %s
563  latest revision: %s
564
565Repository:
566         2 revisions
567""" % (format_description, datestring_first, datestring_last,), out)
568        self.assertEqual('', err)
569
570    def test_info_standalone_no_tree(self):
571        # create standalone branch without a working tree
572        format = controldir.format_registry.make_controldir('default')
573        branch = self.make_branch('branch')
574        repo = branch.repository
575        out, err = self.run_bzr('info branch -v')
576        self.assertEqualDiff(
577            """Standalone branch (format: %s)
578Location:
579  branch root: branch
580
581Format:
582       control: Meta directory format 1
583        branch: %s
584    repository: %s
585
586Control directory:
587         1 branches
588
589Branch history:
590         0 revisions
591
592Repository:
593         0 revisions
594""" % (info.describe_format(repo.controldir, repo, branch, None),
595                format.get_branch_format().get_format_description(),
596                format.repository_format.get_format_description(),
597       ), out)
598        self.assertEqual('', err)
599
600    def test_info_shared_repository(self):
601        format = controldir.format_registry.make_controldir('knit')
602        transport = self.get_transport()
603
604        # Create shared repository
605        repo = self.make_repository('repo', shared=True, format=format)
606        repo.set_make_working_trees(False)
607        out, err = self.run_bzr('info -v repo')
608        self.assertEqualDiff(
609            """Shared repository (format: dirstate or dirstate-tags or knit)
610Location:
611  shared repository: %s
612
613Format:
614       control: Meta directory format 1
615    repository: %s
616
617Control directory:
618         0 branches
619
620Repository:
621         0 revisions
622""" % ('repo', format.repository_format.get_format_description(),
623       ), out)
624        self.assertEqual('', err)
625
626        # Create branch inside shared repository
627        repo.controldir.root_transport.mkdir('branch')
628        branch1 = controldir.ControlDir.create_branch_convenience(
629            'repo/branch', format=format)
630        out, err = self.run_bzr('info -v repo/branch')
631        self.assertEqualDiff(
632            """Repository branch (format: dirstate or knit)
633Location:
634  shared repository: repo
635  repository branch: repo/branch
636
637Format:
638       control: Meta directory format 1
639        branch: %s
640    repository: %s
641
642Control directory:
643         1 branches
644
645Branch history:
646         0 revisions
647
648Repository:
649         0 revisions
650""" % (format.get_branch_format().get_format_description(),
651                format.repository_format.get_format_description(),
652       ), out)
653        self.assertEqual('', err)
654
655        # Create lightweight checkout
656        transport.mkdir('tree')
657        transport.mkdir('tree/lightcheckout')
658        tree2 = branch1.create_checkout('tree/lightcheckout',
659                                        lightweight=True)
660        branch2 = tree2.branch
661        self.assertCheckoutStatusOutput('-v tree/lightcheckout', tree2,
662                                        shared_repo=repo, repo_branch=branch1, verbose=True)
663
664        # Create normal checkout
665        tree3 = branch1.create_checkout('tree/checkout')
666        self.assertCheckoutStatusOutput('tree/checkout --verbose', tree3,
667                                        verbose=True,
668                                        light_checkout=False, repo_branch=branch1)
669        # Update lightweight checkout
670        self.build_tree(['tree/lightcheckout/a'])
671        tree2.add('a')
672        tree2.commit('commit one')
673        rev = repo.get_revision(branch2.last_revision())
674        datestring_first = osutils.format_date(rev.timestamp, rev.timezone)
675        out, err = self.run_bzr('info tree/lightcheckout --verbose')
676        self.assertEqualDiff(
677            """Lightweight checkout (format: %s)
678Location:
679  light checkout root: tree/lightcheckout
680   checkout of branch: repo/branch
681    shared repository: repo
682
683Format:
684       control: Meta directory format 1
685  working tree: Working tree format 6
686        branch: %s
687    repository: %s
688
689Control directory:
690         1 branches
691
692In the working tree:
693         1 unchanged
694         0 modified
695         0 added
696         0 removed
697         0 renamed
698         0 copied
699         0 unknown
700         0 ignored
701         0 versioned subdirectories
702
703Branch history:
704         1 revision
705         0 days old
706   first revision: %s
707  latest revision: %s
708
709Repository:
710         1 revision
711""" % (self._repo_strings, format.get_branch_format().get_format_description(),
712                format.repository_format.get_format_description(),
713                datestring_first, datestring_first,
714       ), out)
715        self.assertEqual('', err)
716
717        # Out of date checkout
718        out, err = self.run_bzr('info -v tree/checkout')
719        self.assertEqualDiff(
720            """Checkout (format: unnamed)
721Location:
722       checkout root: tree/checkout
723  checkout of branch: repo/branch
724
725Format:
726       control: Meta directory format 1
727  working tree: Working tree format 6
728        branch: %s
729    repository: %s
730
731Control directory:
732         1 branches
733
734Branch is out of date: missing 1 revision.
735
736In the working tree:
737         0 unchanged
738         0 modified
739         0 added
740         0 removed
741         0 renamed
742         0 copied
743         0 unknown
744         0 ignored
745         0 versioned subdirectories
746
747Branch history:
748         0 revisions
749
750Repository:
751         0 revisions
752""" % (format.get_branch_format().get_format_description(),
753                format.repository_format.get_format_description(),
754       ), out)
755        self.assertEqual('', err)
756
757        # Update checkout
758        tree3.update()
759        self.build_tree(['tree/checkout/b'])
760        tree3.add('b')
761        out, err = self.run_bzr('info tree/checkout --verbose')
762        self.assertEqualDiff(
763            """Checkout (format: unnamed)
764Location:
765       checkout root: tree/checkout
766  checkout of branch: repo/branch
767
768Format:
769       control: Meta directory format 1
770  working tree: Working tree format 6
771        branch: %s
772    repository: %s
773
774Control directory:
775         1 branches
776
777In the working tree:
778         1 unchanged
779         0 modified
780         1 added
781         0 removed
782         0 renamed
783         0 copied
784         0 unknown
785         0 ignored
786         0 versioned subdirectories
787
788Branch history:
789         1 revision
790         0 days old
791   first revision: %s
792  latest revision: %s
793
794Repository:
795         1 revision
796""" % (format.get_branch_format().get_format_description(),
797                format.repository_format.get_format_description(),
798                datestring_first, datestring_first,
799       ), out)
800        self.assertEqual('', err)
801        tree3.commit('commit two')
802
803        # Out of date lightweight checkout
804        rev = repo.get_revision(branch1.last_revision())
805        datestring_last = osutils.format_date(rev.timestamp, rev.timezone)
806        out, err = self.run_bzr('info tree/lightcheckout --verbose')
807        self.assertEqualDiff(
808            """Lightweight checkout (format: %s)
809Location:
810  light checkout root: tree/lightcheckout
811   checkout of branch: repo/branch
812    shared repository: repo
813
814Format:
815       control: Meta directory format 1
816  working tree: Working tree format 6
817        branch: %s
818    repository: %s
819
820Control directory:
821         1 branches
822
823Working tree is out of date: missing 1 revision.
824
825In the working tree:
826         1 unchanged
827         0 modified
828         0 added
829         0 removed
830         0 renamed
831         0 copied
832         0 unknown
833         0 ignored
834         0 versioned subdirectories
835
836Branch history:
837         2 revisions
838         0 days old
839   first revision: %s
840  latest revision: %s
841
842Repository:
843         2 revisions
844""" % (self._repo_strings, format.get_branch_format().get_format_description(),
845                format.repository_format.get_format_description(),
846                datestring_first, datestring_last,
847       ), out)
848        self.assertEqual('', err)
849
850        # Show info about shared branch
851        out, err = self.run_bzr('info repo/branch --verbose')
852        self.assertEqualDiff(
853            """Repository branch (format: dirstate or knit)
854Location:
855  shared repository: repo
856  repository branch: repo/branch
857
858Format:
859       control: Meta directory format 1
860        branch: %s
861    repository: %s
862
863Control directory:
864         1 branches
865
866Branch history:
867         2 revisions
868         0 days old
869   first revision: %s
870  latest revision: %s
871
872Repository:
873         2 revisions
874""" % (format.get_branch_format().get_format_description(),
875                format.repository_format.get_format_description(),
876                datestring_first, datestring_last,
877       ), out)
878        self.assertEqual('', err)
879
880        # Show info about repository with revisions
881        out, err = self.run_bzr('info -v repo')
882        self.assertEqualDiff(
883            """Shared repository (format: dirstate or dirstate-tags or knit)
884Location:
885  shared repository: repo
886
887Format:
888       control: Meta directory format 1
889    repository: %s
890
891Control directory:
892         0 branches
893
894Repository:
895         2 revisions
896""" % (format.repository_format.get_format_description(),
897       ), out)
898        self.assertEqual('', err)
899
900    def test_info_shared_repository_with_trees(self):
901        format = controldir.format_registry.make_controldir('knit')
902        transport = self.get_transport()
903
904        # Create shared repository with working trees
905        repo = self.make_repository('repo', shared=True, format=format)
906        repo.set_make_working_trees(True)
907        out, err = self.run_bzr('info -v repo')
908        self.assertEqualDiff(
909            """Shared repository with trees (format: dirstate or dirstate-tags or knit)
910Location:
911  shared repository: repo
912
913Format:
914       control: Meta directory format 1
915    repository: %s
916
917Control directory:
918         0 branches
919
920Create working tree for new branches inside the repository.
921
922Repository:
923         0 revisions
924""" % (format.repository_format.get_format_description(),
925       ), out)
926        self.assertEqual('', err)
927
928        # Create two branches
929        repo.controldir.root_transport.mkdir('branch1')
930        branch1 = controldir.ControlDir.create_branch_convenience('repo/branch1',
931                                                                  format=format)
932        branch2 = branch1.controldir.sprout('repo/branch2').open_branch()
933
934        # Empty first branch
935        out, err = self.run_bzr('info repo/branch1 --verbose')
936        self.assertEqualDiff(
937            """Repository tree (format: knit)
938Location:
939  shared repository: repo
940  repository branch: repo/branch1
941
942Format:
943       control: Meta directory format 1
944  working tree: Working tree format 3
945        branch: %s
946    repository: %s
947
948Control directory:
949         1 branches
950
951In the working tree:
952         0 unchanged
953         0 modified
954         0 added
955         0 removed
956         0 renamed
957         0 copied
958         0 unknown
959         0 ignored
960         0 versioned subdirectories
961
962Branch history:
963         0 revisions
964
965Repository:
966         0 revisions
967""" % (format.get_branch_format().get_format_description(),
968                format.repository_format.get_format_description(),
969       ), out)
970        self.assertEqual('', err)
971
972        # Update first branch
973        self.build_tree(['repo/branch1/a'])
974        tree1 = branch1.controldir.open_workingtree()
975        tree1.add('a')
976        tree1.commit('commit one')
977        rev = repo.get_revision(branch1.last_revision())
978        datestring_first = osutils.format_date(rev.timestamp, rev.timezone)
979        out, err = self.run_bzr('info -v repo/branch1')
980        self.assertEqualDiff(
981            """Repository tree (format: knit)
982Location:
983  shared repository: repo
984  repository branch: repo/branch1
985
986Format:
987       control: Meta directory format 1
988  working tree: Working tree format 3
989        branch: %s
990    repository: %s
991
992Control directory:
993         1 branches
994
995In the working tree:
996         1 unchanged
997         0 modified
998         0 added
999         0 removed
1000         0 renamed
1001         0 copied
1002         0 unknown
1003         0 ignored
1004         0 versioned subdirectories
1005
1006Branch history:
1007         1 revision
1008         0 days old
1009   first revision: %s
1010  latest revision: %s
1011
1012Repository:
1013         1 revision
1014""" % (format.get_branch_format().get_format_description(),
1015                format.repository_format.get_format_description(),
1016                datestring_first, datestring_first,
1017       ), out)
1018        self.assertEqual('', err)
1019
1020        # Out of date second branch
1021        out, err = self.run_bzr('info repo/branch2 --verbose')
1022        self.assertEqualDiff(
1023            """Repository tree (format: knit)
1024Location:
1025  shared repository: repo
1026  repository branch: repo/branch2
1027
1028Related branches:
1029  parent branch: repo/branch1
1030
1031Format:
1032       control: Meta directory format 1
1033  working tree: Working tree format 3
1034        branch: %s
1035    repository: %s
1036
1037Control directory:
1038         1 branches
1039
1040In the working tree:
1041         0 unchanged
1042         0 modified
1043         0 added
1044         0 removed
1045         0 renamed
1046         0 copied
1047         0 unknown
1048         0 ignored
1049         0 versioned subdirectories
1050
1051Branch history:
1052         0 revisions
1053
1054Repository:
1055         1 revision
1056""" % (format.get_branch_format().get_format_description(),
1057                format.repository_format.get_format_description(),
1058       ), out)
1059        self.assertEqual('', err)
1060
1061        # Update second branch
1062        tree2 = branch2.controldir.open_workingtree()
1063        tree2.pull(branch1)
1064        out, err = self.run_bzr('info -v repo/branch2')
1065        self.assertEqualDiff(
1066            """Repository tree (format: knit)
1067Location:
1068  shared repository: repo
1069  repository branch: repo/branch2
1070
1071Related branches:
1072  parent branch: repo/branch1
1073
1074Format:
1075       control: Meta directory format 1
1076  working tree: Working tree format 3
1077        branch: %s
1078    repository: %s
1079
1080Control directory:
1081         1 branches
1082
1083In the working tree:
1084         1 unchanged
1085         0 modified
1086         0 added
1087         0 removed
1088         0 renamed
1089         0 copied
1090         0 unknown
1091         0 ignored
1092         0 versioned subdirectories
1093
1094Branch history:
1095         1 revision
1096         0 days old
1097   first revision: %s
1098  latest revision: %s
1099
1100Repository:
1101         1 revision
1102""" % (format.get_branch_format().get_format_description(),
1103                format.repository_format.get_format_description(),
1104                datestring_first, datestring_first,
1105       ), out)
1106        self.assertEqual('', err)
1107
1108        # Show info about repository with revisions
1109        out, err = self.run_bzr('info -v repo')
1110        self.assertEqualDiff(
1111            """Shared repository with trees (format: dirstate or dirstate-tags or knit)
1112Location:
1113  shared repository: repo
1114
1115Format:
1116       control: Meta directory format 1
1117    repository: %s
1118
1119Control directory:
1120         0 branches
1121
1122Create working tree for new branches inside the repository.
1123
1124Repository:
1125         1 revision
1126""" % (format.repository_format.get_format_description(),
1127       ),
1128            out)
1129        self.assertEqual('', err)
1130
1131    def test_info_shared_repository_with_tree_in_root(self):
1132        format = controldir.format_registry.make_controldir('knit')
1133        transport = self.get_transport()
1134
1135        # Create shared repository with working trees
1136        repo = self.make_repository('repo', shared=True, format=format)
1137        repo.set_make_working_trees(True)
1138        out, err = self.run_bzr('info -v repo')
1139        self.assertEqualDiff(
1140            """Shared repository with trees (format: dirstate or dirstate-tags or knit)
1141Location:
1142  shared repository: repo
1143
1144Format:
1145       control: Meta directory format 1
1146    repository: %s
1147
1148Control directory:
1149         0 branches
1150
1151Create working tree for new branches inside the repository.
1152
1153Repository:
1154         0 revisions
1155""" % (format.repository_format.get_format_description(),
1156       ), out)
1157        self.assertEqual('', err)
1158
1159        # Create branch in root of repository
1160        control = repo.controldir
1161        branch = control.create_branch()
1162        control.create_workingtree()
1163        out, err = self.run_bzr('info -v repo')
1164        self.assertEqualDiff(
1165            """Repository tree (format: knit)
1166Location:
1167  shared repository: repo
1168  repository branch: repo
1169
1170Format:
1171       control: Meta directory format 1
1172  working tree: Working tree format 3
1173        branch: %s
1174    repository: %s
1175
1176Control directory:
1177         1 branches
1178
1179In the working tree:
1180         0 unchanged
1181         0 modified
1182         0 added
1183         0 removed
1184         0 renamed
1185         0 copied
1186         0 unknown
1187         0 ignored
1188         0 versioned subdirectories
1189
1190Branch history:
1191         0 revisions
1192
1193Repository:
1194         0 revisions
1195""" % (format.get_branch_format().get_format_description(),
1196                format.repository_format.get_format_description(),
1197       ), out)
1198        self.assertEqual('', err)
1199
1200    def test_info_repository_hook(self):
1201        format = controldir.format_registry.make_controldir('knit')
1202
1203        def repo_info(repo, stats, outf):
1204            outf.write(u"more info\n")
1205        info.hooks.install_named_hook('repository', repo_info, None)
1206        # Create shared repository with working trees
1207        repo = self.make_repository('repo', shared=True, format=format)
1208        out, err = self.run_bzr('info -v repo')
1209        self.assertEqualDiff(
1210            """Shared repository with trees (format: dirstate or dirstate-tags or knit)
1211Location:
1212  shared repository: repo
1213
1214Format:
1215       control: Meta directory format 1
1216    repository: %s
1217
1218Control directory:
1219         0 branches
1220
1221Create working tree for new branches inside the repository.
1222
1223Repository:
1224         0 revisions
1225more info
1226""" % (format.repository_format.get_format_description(),
1227       ), out)
1228        self.assertEqual('', err)
1229
1230    def test_info_unshared_repository_with_colocated_branches(self):
1231        format = controldir.format_registry.make_controldir('development-colo')
1232        transport = self.get_transport()
1233
1234        # Create unshared repository
1235        repo = self.make_repository('repo', shared=False, format=format)
1236        repo.set_make_working_trees(True)
1237        repo.controldir.create_branch(name='foo')
1238        out, err = self.run_bzr('info repo')
1239        self.assertEqualDiff(
1240            """Unshared repository with trees and colocated branches (format: development-colo)
1241Location:
1242  repository: repo
1243""", out)
1244        self.assertEqual('', err)
1245
1246    def assertCheckoutStatusOutput(self,
1247                                   command_string, lco_tree, shared_repo=None,
1248                                   repo_branch=None,
1249                                   tree_locked=False,
1250                                   branch_locked=False, repo_locked=False,
1251                                   verbose=False,
1252                                   light_checkout=True,
1253                                   checkout_root=None):
1254        """Check the output of info in a checkout.
1255
1256        This is not quite a mirror of the info code: rather than using the
1257        tree being examined to predict output, it uses a bunch of flags which
1258        allow us, the test writers, to document what *should* be present in
1259        the output. Removing this separation would remove the value of the
1260        tests.
1261
1262        :param path: the path to the light checkout.
1263        :param lco_tree: the tree object for the light checkout.
1264        :param shared_repo: A shared repository is in use, expect that in
1265            the output.
1266        :param repo_branch: A branch in a shared repository for non light
1267            checkouts.
1268        :param tree_locked: If true, expect the tree to be locked.
1269        :param branch_locked: If true, expect the branch to be locked.
1270        :param repo_locked: If true, expect the repository to be locked.
1271            Note that the lco_tree.branch.repository is inspected, and if is not
1272            actually locked then this parameter is overridden. This is because
1273            pack repositories do not have any public API for obtaining an
1274            exclusive repository wide lock.
1275        :param verbose: verbosity level: 2 or higher to show committers
1276        """
1277        def friendly_location(url):
1278            path = urlutils.unescape_for_display(url, 'ascii')
1279            try:
1280                return osutils.relpath(osutils.getcwd(), path)
1281            except errors.PathNotChild:
1282                return path
1283
1284        if tree_locked:
1285            # We expect this to fail because of locking errors.
1286            # (A write-locked file cannot be read-locked
1287            # in the different process -- either on win32 or on linux).
1288            # This should be removed when the locking errors are fixed.
1289            self.expectFailure('OS locks are exclusive '
1290                               'for different processes (Bug #174055)',
1291                               self.run_bzr_subprocess,
1292                               'info ' + command_string)
1293        out, err = self.run_bzr('info %s' % command_string)
1294        description = {
1295            (True, True): 'Lightweight checkout',
1296            (True, False): 'Repository checkout',
1297            (False, True): 'Lightweight checkout',
1298            (False, False): 'Checkout',
1299            }[(shared_repo is not None, light_checkout)]
1300        format = {True: self._repo_strings,
1301                  False: 'unnamed'}[light_checkout]
1302        if repo_locked:
1303            repo_locked = lco_tree.branch.repository.get_physical_lock_status()
1304        if repo_locked or branch_locked or tree_locked:
1305            def locked_message(a_bool):
1306                if a_bool:
1307                    return 'locked'
1308                else:
1309                    return 'unlocked'
1310            expected_lock_output = (
1311                "\n"
1312                "Lock status:\n"
1313                "  working tree: %s\n"
1314                "        branch: %s\n"
1315                "    repository: %s\n" % (
1316                    locked_message(tree_locked),
1317                    locked_message(branch_locked),
1318                    locked_message(repo_locked)))
1319        else:
1320            expected_lock_output = ''
1321        tree_data = ''
1322        extra_space = ''
1323        if light_checkout:
1324            tree_data = ("  light checkout root: %s\n" %
1325                         friendly_location(lco_tree.controldir.root_transport.base))
1326            extra_space = ' '
1327        if lco_tree.branch.get_bound_location() is not None:
1328            tree_data += ("%s       checkout root: %s\n" % (extra_space,
1329                                                            friendly_location(lco_tree.branch.controldir.root_transport.base)))
1330        if shared_repo is not None:
1331            branch_data = (
1332                "   checkout of branch: %s\n"
1333                "    shared repository: %s\n" %
1334                (friendly_location(repo_branch.controldir.root_transport.base),
1335                 friendly_location(shared_repo.controldir.root_transport.base)))
1336        elif repo_branch is not None:
1337            branch_data = (
1338                "%s  checkout of branch: %s\n" %
1339                (extra_space,
1340                 friendly_location(repo_branch.controldir.root_transport.base)))
1341        else:
1342            branch_data = ("   checkout of branch: %s\n" %
1343                           lco_tree.branch.controldir.root_transport.base)
1344
1345        if verbose >= 2:
1346            verbose_info = '         0 committers\n'
1347        else:
1348            verbose_info = ''
1349
1350        self.assertEqualDiff(
1351            """%s (format: %s)
1352Location:
1353%s%s
1354Format:
1355       control: Meta directory format 1
1356  working tree: %s
1357        branch: %s
1358    repository: %s
1359%s
1360Control directory:
1361         1 branches
1362
1363In the working tree:
1364         0 unchanged
1365         0 modified
1366         0 added
1367         0 removed
1368         0 renamed
1369         0 copied
1370         0 unknown
1371         0 ignored
1372         0 versioned subdirectories
1373
1374Branch history:
1375         0 revisions
1376%s
1377Repository:
1378         0 revisions
1379""" % (description,
1380                format,
1381                tree_data,
1382                branch_data,
1383                lco_tree._format.get_format_description(),
1384                lco_tree.branch._format.get_format_description(),
1385                lco_tree.branch.repository._format.get_format_description(),
1386                expected_lock_output,
1387                verbose_info,
1388       ), out)
1389        self.assertEqual('', err)
1390
1391    def test_info_locking(self):
1392        transport = self.get_transport()
1393        # Create shared repository with a branch
1394        repo = self.make_repository('repo', shared=True,
1395                                    format=bzrdir.BzrDirMetaFormat1())
1396        repo.set_make_working_trees(False)
1397        repo.controldir.root_transport.mkdir('branch')
1398        repo_branch = controldir.ControlDir.create_branch_convenience(
1399            'repo/branch', format=bzrdir.BzrDirMetaFormat1())
1400        # Do a heavy checkout
1401        transport.mkdir('tree')
1402        transport.mkdir('tree/checkout')
1403        co_branch = controldir.ControlDir.create_branch_convenience(
1404            'tree/checkout', format=bzrdir.BzrDirMetaFormat1())
1405        co_branch.bind(repo_branch)
1406        # Do a light checkout of the heavy one
1407        transport.mkdir('tree/lightcheckout')
1408        lco_dir = bzrdir.BzrDirMetaFormat1().initialize('tree/lightcheckout')
1409        lco_dir.set_branch_reference(co_branch)
1410        lco_dir.create_workingtree()
1411        lco_tree = lco_dir.open_workingtree()
1412
1413        # Test all permutations of locking the working tree, branch and repository
1414        # W B R
1415
1416        # U U U
1417        self.assertCheckoutStatusOutput('-v tree/lightcheckout', lco_tree,
1418                                        repo_branch=repo_branch,
1419                                        verbose=True, light_checkout=True)
1420        # U U L
1421        with lco_tree.branch.repository.lock_write():
1422            self.assertCheckoutStatusOutput('-v tree/lightcheckout',
1423                                            lco_tree, repo_branch=repo_branch,
1424                                            repo_locked=True, verbose=True, light_checkout=True)
1425        # U L L
1426        with lco_tree.branch.lock_write():
1427            self.assertCheckoutStatusOutput('-v tree/lightcheckout',
1428                                            lco_tree,
1429                                            branch_locked=True,
1430                                            repo_locked=True,
1431                                            repo_branch=repo_branch,
1432                                            verbose=True)
1433        # L L L
1434        with lco_tree.lock_write():
1435            self.assertCheckoutStatusOutput('-v tree/lightcheckout',
1436                                            lco_tree, repo_branch=repo_branch,
1437                                            tree_locked=True,
1438                                            branch_locked=True,
1439                                            repo_locked=True,
1440                                            verbose=True)
1441        # L L U
1442        with lco_tree.lock_write(), lco_tree.branch.repository.unlock():
1443            self.assertCheckoutStatusOutput('-v tree/lightcheckout',
1444                                            lco_tree, repo_branch=repo_branch,
1445                                            tree_locked=True,
1446                                            branch_locked=True,
1447                                            verbose=True)
1448        # L U U
1449        with lco_tree.lock_write(), lco_tree.branch.unlock():
1450            self.assertCheckoutStatusOutput('-v tree/lightcheckout',
1451                                            lco_tree, repo_branch=repo_branch,
1452                                            tree_locked=True,
1453                                            verbose=True)
1454        # L U L
1455        with lco_tree.lock_write(), lco_tree.branch.unlock(), \
1456                lco_tree.branch.repository.lock_write():
1457            self.assertCheckoutStatusOutput('-v tree/lightcheckout',
1458                                            lco_tree, repo_branch=repo_branch,
1459                                            tree_locked=True,
1460                                            repo_locked=True,
1461                                            verbose=True)
1462        # U L U
1463        with lco_tree.branch.lock_write(), lco_tree.branch.repository.unlock():
1464            self.assertCheckoutStatusOutput('-v tree/lightcheckout',
1465                                            lco_tree, repo_branch=repo_branch,
1466                                            branch_locked=True,
1467                                            verbose=True)
1468
1469        if sys.platform == 'win32':
1470            self.knownFailure('Win32 cannot run "brz info"'
1471                              ' when the tree is locked.')
1472
1473    def test_info_stacked(self):
1474        # We have a mainline
1475        trunk_tree = self.make_branch_and_tree('mainline',
1476                                               format='1.6')
1477        trunk_tree.commit('mainline')
1478        # and a branch from it which is stacked
1479        new_dir = trunk_tree.controldir.sprout('newbranch', stacked=True)
1480        out, err = self.run_bzr('info newbranch')
1481        self.assertEqual(
1482            """Standalone tree (format: 1.6)
1483Location:
1484  branch root: newbranch
1485
1486Related branches:
1487  parent branch: mainline
1488     stacked on: mainline
1489""", out)
1490        self.assertEqual("", err)
1491
1492    def test_info_revinfo_optional(self):
1493        tree = self.make_branch_and_tree('.')
1494
1495        def last_revision_info(self):
1496            raise errors.UnsupportedOperation(last_revision_info, self)
1497        self.overrideAttr(
1498            branch.Branch, "last_revision_info", last_revision_info)
1499        out, err = self.run_bzr('info -v .')
1500        self.assertEqual(
1501            """Standalone tree (format: 2a)
1502Location:
1503  branch root: .
1504
1505Format:
1506       control: Meta directory format 1
1507  working tree: Working tree format 6
1508        branch: Branch format 7
1509    repository: Repository format 2a - rich roots, group compression and chk inventories
1510
1511Control directory:
1512         1 branches
1513
1514In the working tree:
1515         0 unchanged
1516         0 modified
1517         0 added
1518         0 removed
1519         0 renamed
1520         0 copied
1521         0 unknown
1522         0 ignored
1523         0 versioned subdirectories
1524""", out)
1525        self.assertEqual("", err)
1526
1527    def test_info_shows_colocated_branches(self):
1528        bzrdir = self.make_branch('.', format='development-colo').controldir
1529        bzrdir.create_branch(name="colo1")
1530        bzrdir.create_branch(name="colo2")
1531        bzrdir.create_branch(name="colo3")
1532        out, err = self.run_bzr('info -v .')
1533        self.assertEqualDiff(
1534            """Standalone branch (format: development-colo)
1535Location:
1536  branch root: .
1537
1538Format:
1539       control: Meta directory format 1 with support for colocated branches
1540        branch: Branch format 7
1541    repository: Repository format 2a - rich roots, group compression and chk inventories
1542
1543Control directory:
1544         4 branches
1545
1546Branch history:
1547         0 revisions
1548
1549Repository:
1550         0 revisions
1551""", out)
1552        self.assertEqual("", err)
1553