1 # vim: set fileencoding=utf-8 :
4 Test L{gbp.git.GitRepository}
6 This testcase creates several repositores:
8 - A repository at L{repo_dir} called I{repo}
9 - A bare repository at L{bare_dir} called I{bare}
10 - A clone of I{repo} below L{clone_dir} called I{clone}
11 - A mirror of I{repo} below L{mirror_clone_dir} called I{mirror}
18 gbp.log.setup(color=False, verbose=True)
20 repo_dir, bare_dir, clone_dir, mirror_clone_dir = list(map(
21 lambda x, tmpdir=context.new_tmpdir(__name__): tmpdir.join(x),
22 ['repo', 'bare', 'clone', 'mirror_clone']))
29 - L{gbp.git.GitRepository.create}
32 - L{gbp.git.GitRepository.path}
33 - L{gbp.git.GitRepository.git_dir}
35 >>> import os, gbp.git
36 >>> repo = gbp.git.GitRepository.create(repo_dir)
37 >>> repo.path == repo_dir
39 >>> repo.git_dir == os.path.join(repo_dir, '.git')
41 >>> type(repo) == gbp.git.GitRepository
48 Empty repos have no branch
51 - L{gbp.git.GitRepository.get_branch}
52 - L{gbp.git.GitRepository.is_empty}
55 >>> repo = gbp.git.GitRepository(repo_dir)
68 - L{gbp.git.GitRepository.add_files}
69 - L{gbp.git.GitRepository.commit_all}
70 - L{gbp.git.GitRepository.is_clean}
71 - L{gbp.git.GitRepository.status}
74 - L{gbp.git.GitRepository.head}
76 >>> import gbp.git, shutil, os
77 >>> repo = gbp.git.GitRepository(repo_dir)
78 >>> shutil.copy(os.path.join(repo.path, ".git/HEAD"), \
79 os.path.join(repo.path, "testfile"))
80 >>> repo.is_clean()[0]
82 >>> repo.is_clean(ignore_untracked=True)[0]
84 >>> repo.add_files('testfile', force=True, untracked=False)
85 >>> list(repo.status().items())
86 [('??', ['testfile'])]
87 >>> repo.add_files(repo.path, force=True)
88 >>> list(repo.status().items())
89 [('A ', ['testfile'])]
90 >>> repo.commit_all(msg="foo")
91 >>> repo.is_clean()[0]
99 def test_branch_master():
101 First branch is called I{master}
104 - L{gbp.git.GitRepository.get_branch}
105 >>> import gbp.git, shutil
106 >>> repo = gbp.git.GitRepository(repo_dir)
107 >>> repo.get_branch()
115 Remove untracked files from the working tree
118 - L{gbp.git.GitRepository.clean}
120 >>> import gbp.git, shutil, os
121 >>> repo = gbp.git.GitRepository(repo_dir)
122 >>> shutil.copy(os.path.join(repo.path, ".git/HEAD"), \
123 os.path.join(repo.path, "testclean"))
124 >>> repo.clean(dry_run=True)
125 >>> repo.is_clean()[0]
127 >>> repo.clean(directories=True, force=True)
128 >>> repo.is_clean()[0]
132 def test_create_branch():
134 Create a branch name I{foo}
137 - L{gbp.git.GitRepository.create_branch}
138 - L{gbp.git.GitRepository.branch_contains}
140 >>> import gbp.git, shutil
141 >>> repo = gbp.git.GitRepository(repo_dir)
142 >>> repo.create_branch("foo")
143 >>> repo.branch_contains("foo", 'HEAD')
145 >>> repo.branch_contains("doesnotexist", 'HEAD', remote=True)
149 def test_delete_branch():
151 Create a branch named I{foo2} and delete it
154 - L{gbp.git.GitRepository.create_branch}
155 - L{gbp.git.GitRepository.delete_branch}
157 >>> import gbp.git, shutil
158 >>> repo = gbp.git.GitRepository(repo_dir)
159 >>> repo.create_branch("bar")
160 >>> repo.delete_branch("bar")
161 >>> repo.delete_branch("master")
162 Traceback (most recent call last):
164 GitRepositoryError: Can't delete the branch you're on
167 def test_set_branch():
169 Switch to branch named I{foo}
172 - L{gbp.git.GitRepository.set_branch}
173 - L{gbp.git.GitRepository.get_branch}
174 - L{gbp.git.GitRepository.branch}
177 >>> repo = gbp.git.GitRepository(repo_dir)
178 >>> repo.set_branch("foo")
179 >>> repo.get_branch() == "foo"
181 >>> repo.branch == "foo"
186 def test_rename_branch():
188 Create branch named I{baz}, rename it to I{bax} and finally delete it
191 - L{gbp.git.GitRepository.create_branch}
192 - L{gbp.git.GitRepository.rename_branch}
193 - L{gbp.git.GitRepository.delete_branch}
196 >>> repo = gbp.git.GitRepository(repo_dir)
197 >>> repo.create_branch("baz")
198 >>> repo.rename_branch("baz", "bax")
199 >>> repo.delete_branch("bax")
203 def test_set_upstream_branch():
205 Set upstream branch master -> origin/master
207 >>> import os, shutil
209 >>> repo = gbp.git.GitRepository(repo_dir)
210 >>> os.makedirs(os.path.join(repo.git_dir, 'refs/remotes/origin'))
211 >>> shutil.copy(os.path.join(repo.git_dir, 'refs/heads/master'), \
212 os.path.join(repo.git_dir, 'refs/remotes/origin/'))
213 >>> repo.add_remote_repo('origin', 'git://git.example.com/git/origin')
214 >>> repo.set_upstream_branch('master', 'origin/master')
215 >>> repo.get_upstream_branch('master')
217 >>> repo.set_upstream_branch('bla', 'origin/master')
218 Traceback (most recent call last):
219 GitRepositoryError: Branch bla doesn't exist!
220 >>> repo.set_upstream_branch('foo', 'origin/bla')
221 Traceback (most recent call last):
222 GitRepositoryError: Branch origin/bla doesn't exist!
225 def test_get_upstream_branch():
227 Get info about upstream branches set in test_set_upstream_branch
230 >>> repo = gbp.git.GitRepository(repo_dir)
231 >>> repo.get_upstream_branch('master')
233 >>> repo.get_upstream_branch('foo')
235 >>> repo.get_upstream_branch('bla')
236 Traceback (most recent call last):
237 GitRepositoryError: Branch bla doesn't exist!
242 Create a tag named I{tag} and check it's existance
245 - L{gbp.git.GitRepository.create_tag}
246 - L{gbp.git.GitRepository.verify_tag}
247 - L{gbp.git.GitRepository.has_tag}
248 - L{gbp.git.GitRepository.get_tags}
251 >>> repo = gbp.git.GitRepository(repo_dir)
252 >>> repo.create_tag("tag")
253 >>> repo.has_tag("tag")
255 >>> repo.has_tag("unknown")
257 >>> repo.create_tag("tag2", msg="foo")
258 >>> repo.has_tag("tag2")
260 >>> repo.verify_tag("tag2")
273 - L{gbp.git.GitRepository.describe}
276 >>> repo = gbp.git.GitRepository(repo_dir)
277 >>> sha = repo.rev_parse('HEAD')
278 >>> repo.describe('HEAD')
280 >>> repo.describe('HEAD', longfmt=True) == 'tag2-0-g%s' % sha[:7]
282 >>> repo.describe('HEAD', pattern='foo*')
283 Traceback (most recent call last):
285 GitRepositoryError: Can't describe HEAD. Git error: fatal: No names found, cannot describe anything.
286 >>> repo.describe('HEAD', pattern='foo*', always=True) == sha[:7]
288 >>> repo.describe('HEAD', always=True, abbrev=16)
290 >>> repo.describe('HEAD', pattern='foo*', always=True, abbrev=16) == sha[:16]
292 >>> tag = repo.describe('HEAD', longfmt=True, abbrev=16) == 'tag2-0-g%s' % sha[:16]
293 >>> repo.delete_tag('tag2')
294 >>> repo.describe('HEAD', tags=True)
296 >>> repo.describe('HEAD', tags=True, exact_match=True)
298 >>> repo.create_tag('tag2', msg='foo')
306 - L{gbp.git.GitRepository.find_tag}
309 >>> repo = gbp.git.GitRepository(repo_dir)
310 >>> repo.find_tag('HEAD')
312 >>> repo.find_tag('HEAD', pattern='foo*')
313 Traceback (most recent call last):
315 GitRepositoryError: Can't describe HEAD. Git error: fatal: No names found, cannot describe anything.
323 - L{gbp.git.GitRepository.move_tag}
324 - L{gbp.git.GitRepository.has_tag}
327 >>> repo = gbp.git.GitRepository(repo_dir)
328 >>> repo.move_tag("tag", "moved")
329 >>> repo.has_tag("tag")
331 >>> repo.has_tag("moved")
335 def test_delete_tag():
340 - L{gbp.git.GitRepository.delete_tag}
341 - L{gbp.git.GitRepository.has_tag}
344 >>> repo = gbp.git.GitRepository(repo_dir)
345 >>> repo.has_tag("moved")
347 >>> repo.delete_tag("moved")
348 >>> repo.has_tag("moved")
352 def test_get_obj_type():
354 Find commit SHA1 related to tags
357 - L{gbp.git.GitRepository.create_tag}
358 - L{gbp.git.GitRepository.get_obj_type}
359 - L{gbp.git.GitRepository.delete_tag}
362 >>> repo = gbp.git.GitRepository(repo_dir)
363 >>> repo.create_tag("tag3", "tag msg")
364 >>> repo.get_obj_type("tag3")
366 >>> repo.get_obj_type("HEAD")
368 >>> repo.get_obj_type("HEAD:testfile")
370 >>> repo.delete_tag("tag3")
373 def test_list_files():
375 List files in the index
378 - L{gbp.git.GitRepository.list_files}
379 - L{gbp.git.GitRepository.add_files}
380 - L{gbp.git.GitRepository.commit_staged}
381 - L{gbp.git.GitRepository.commit_files}
382 - L{gbp.git.GitRepository.force_head}
384 >>> import gbp.git, os, shutil
385 >>> repo = gbp.git.GitRepository(repo_dir)
386 >>> src = os.path.join(repo.path, ".git/HEAD")
387 >>> dst = os.path.join(repo.path, "testfile")
388 >>> repo.list_files()
390 >>> repo.list_files(['modified'])
392 >>> repo.list_files(['modified', 'deleted'])
394 >>> repo.list_files(['modified', 'deleted', 'cached'])
396 >>> shutil.copy(src, dst)
397 >>> repo.list_files(['modified'])
399 >>> repo.add_files(dst)
400 >>> repo.commit_staged(msg="foo")
401 >>> repo.list_files(['modified'])
403 >>> repo.list_files(['foo'])
404 Traceback (most recent call last):
406 GitRepositoryError: Unknown type 'foo'
407 >>> repo.force_head('HEAD^', hard=True)
408 >>> repo.list_files(['modified'])
410 >>> shutil.copy(src, dst)
411 >>> repo.list_files(['modified'])
413 >>> repo.commit_files(dst, msg="foo")
414 >>> repo.list_files(['modified'])
418 def test_get_commits():
423 - L{gbp.git.GitRepository.get_commits}
426 >>> repo = gbp.git.GitRepository(repo_dir)
427 >>> commits = repo.get_commits()
428 >>> type(commits) == list and len(commits) == 2
430 >>> len(repo.get_commits(num=1)) == 1
432 >>> commits2 = repo.get_commits(since='HEAD~1')
433 >>> len(commits2) == 1
435 >>> commits2[0] == commits[0]
437 >>> commits2 = repo.get_commits(until='HEAD~1')
438 >>> len(commits2) == 1
440 >>> commits2[0] == commits[-1]
442 >>> repo.get_commits(paths=['foo', 'bar'])
444 >>> repo.get_commits(paths=['testfile']) == commits
449 def test_get_commit_info():
451 Test inspecting commits
454 - L{gbp.git.GitRepository.get_commit_info}
457 >>> from datetime import datetime
458 >>> repo = gbp.git.GitRepository(repo_dir)
459 >>> info = repo.get_commit_info('HEAD')
466 >>> '@' in info['author'].email
468 >>> '@' in info['committer'].email
470 >>> now = datetime.now()
471 >>> (now - datetime.fromtimestamp(int(info['author'].date.split()[0]))).seconds < 10
473 >>> (now - datetime.fromtimestamp(int(info['committer'].date.split()[0]))).seconds < 10
475 >>> info['patchname']
478 defaultdict(<type 'list'>, {'M': ['testfile']})
479 >>> repo.get_subject('HEAD')
488 - L{gbp.git.GitRepository.diff}
491 >>> repo = gbp.git.GitRepository(repo_dir)
492 >>> len(repo.diff('HEAD~1', 'HEAD')) > 3
494 >>> len(repo.diff('HEAD~1', 'HEAD', 'testfile')) > 3
496 >>> len(repo.diff('HEAD~1', 'HEAD', 'testfile', text=True)) > 3
498 >>> len(repo.diff('HEAD~1', 'HEAD', 'filenotexist')) == 0
502 def test_diff_status():
505 - L{gbp.git.GitRepository.diff_status}
508 >>> repo = gbp.git.GitRepository(repo_dir)
509 >>> repo.diff_status("HEAD", "HEAD")
510 defaultdict(<type 'list'>, {})
511 >>> repo.diff_status("HEAD~1", "HEAD")
512 defaultdict(<type 'list'>, {'M': ['testfile']})
515 def test_mirror_clone():
520 - L{gbp.git.GitRepository.clone}
521 - L{gbp.git.GitRepository.is_empty}
522 - L{gbp.git.GitRepository.set_branch}
523 - L{gbp.git.GitRepository.has_branch}
524 - L{gbp.git.GitRepository.branch}
527 >>> repo = gbp.git.GitRepository(repo_dir)
528 >>> repo.set_branch('master')
529 >>> mirror = gbp.git.GitRepository.clone(mirror_clone_dir, repo.path, mirror=True)
530 >>> mirror.is_empty()
534 >>> mirror.has_branch('foo')
536 >>> mirror.has_branch('bar')
538 >>> mirror.set_branch('foo')
541 >>> mirror.force_head('foo^')
549 - L{gbp.git.GitRepository.clone}
550 - L{gbp.git.GitRepository.is_empty}
551 - L{gbp.git.GitRepository.set_branch}
552 - L{gbp.git.GitRepository.branch}
553 - L{gbp.git.GitRepository.get_merge_branch}
554 - L{gbp.git.GitRepository.get_remote_branches}
555 - L{gbp.git.GitRepository.get_local_branches}
556 - L{gbp.git.GitRepository.get_remote_repos}
557 - L{gbp.git.GitRepository.has_remote_repo}
560 >>> repo = gbp.git.GitRepository(repo_dir)
561 >>> repo.set_branch('master')
562 >>> clone = gbp.git.GitRepository.clone(clone_dir, repo.path)
567 >>> clone.get_remote_branches()
568 ['origin/HEAD', 'origin/foo', 'origin/master']
569 >>> clone.get_local_branches()
571 >>> clone.get_merge_branch('master')
573 >>> clone.create_branch('foo', 'origin/foo')
574 >>> clone.get_merge_branch('foo')
576 >>> clone.create_branch('bar')
577 >>> clone.get_merge_branch('bar') # None if no merge branch exists
578 >>> clone.get_local_branches()
579 ['bar', 'foo', 'master']
580 >>> remotes = clone.get_remote_repos()
581 >>> {'origin': [repo_dir, repo_dir]} == remotes
583 >>> clone.has_remote_repo('origin')
585 >>> clone.has_branch('origin/master', remote=True)
587 >>> clone.has_remote_repo('godiug')
591 def test_get_remotes():
596 - L{gbp.git.GitRepository.get_remotes}
599 >>> import gbp.git.repository
600 >>> repo = gbp.git.repository.GitRepository(os.path.join(clone_dir, 'repo'))
601 >>> remotes = repo.get_remotes()
604 >>> origin = remotes['origin']
607 >>> origin.fetch_url == repo_dir
609 >>> origin.push_urls == [repo_dir]
618 - L{gbp.git.GitRepository.merge}
619 - L{gbp.git.GitRepository.set_branch}
622 >>> repo = gbp.git.GitRepository(repo_dir)
623 >>> repo.set_branch('master')
624 >>> repo.merge('foo')
629 Pull from a remote repository
632 - L{gbp.git.GitRepository.set_branch}
633 - L{gbp.git.GitRepository.pull}
635 >>> import gbp.git, os
636 >>> d = os.path.join(clone_dir, 'repo')
637 >>> clone = gbp.git.GitRepository(d)
638 >>> clone.set_branch('master')
640 >>> clone.pull(all_remotes=True)
641 >>> clone.pull('origin', all_remotes=True)
646 Fetch from a remote repository
649 - L{gbp.git.GitRepository.fetch}
650 - L{gbp.git.GitRepository.push}
651 - L{gbp.git.GitRepository.push_tag}
652 - L{gbp.git.GitRepository.add_remote_repo}
653 - L{gbp.git.GitRepository.remove_remote_repo}
655 >>> import gbp.git, os
656 >>> d = os.path.join(clone_dir, 'repo')
657 >>> clone = gbp.git.GitRepository(d)
660 >>> clone.push('origin')
661 >>> clone.push('origin', 'master')
662 >>> clone.push('origin', 'master', force=True)
663 >>> clone.create_tag('tag3')
664 >>> clone.push_tag('origin', 'tag3')
665 >>> clone.create_tag('tag4')
666 >>> clone.push('origin', 'master', tags=True)
667 >>> clone.add_remote_repo('foo', repo_dir)
668 >>> clone.fetch('foo')
669 >>> clone.fetch('foo', tags=True)
670 >>> clone.fetch('foo', refspec='refs/heads/master')
671 >>> clone.fetch(all_remotes=True)
672 >>> clone.remove_remote_repo('foo')
675 def test_create_bare():
677 Create a bare repository
680 - L{gbp.git.GitRepository.create}
681 - L{gbp.git.GitRepository.is_empty}
684 >>> bare = gbp.git.GitRepository.create(bare_dir, bare=True, description="msg")
685 >>> bare.path == bare_dir
687 >>> bare.git_dir == bare_dir
689 >>> type(bare) == gbp.git.GitRepository
697 def test_nonexistant():
699 Check that accessing a non existant repository fails.
702 - L{gbp.git.GitRepository.__init__}
705 >>> bare = gbp.git.GitRepository("/does/not/exist")
706 Traceback (most recent call last):
708 GitRepositoryError: No Git repository at '/does/not/exist' (or any parent dir)
711 def test_create_noperm():
713 Check that creating a repository at a path that isn't writeable fails
716 - L{gbp.git.GitRepository.create}
719 >>> gbp.git.GitRepository.create("/does/not/exist")
720 Traceback (most recent call last):
722 GitRepositoryError: Cannot create Git repository at '/does/not/exist': Permission denied
730 - L{gbp.git.GitRepository.checkout}
731 - L{gbp.git.GitRepository.get_branch}
732 - L{gbp.git.GitRepository.set_branch}
733 - L{gbp.git.GitRepository.rev_parse}
736 - L{gbp.git.GitRepository.branch}
737 - L{gbp.git.GitRepository.tags}
740 >>> repo = gbp.git.GitRepository(repo_dir)
741 >>> repo.checkout('master')
744 >>> repo.rev_parse('doesnotexist')
745 Traceback (most recent call last):
747 GitRepositoryError: revision 'doesnotexist' not found
748 >>> sha1 = repo.rev_parse('master', short=10)
751 >>> sha1 = repo.rev_parse('master')
754 >>> repo.checkout(sha1)
756 >>> repo.get_branch()
757 Traceback (most recent call last):
759 GitRepositoryError: Currently not on a branch
760 >>> tag = repo.tags[0]
761 >>> repo.checkout(tag)
767 Test garbace collection
770 - L{gbp.git.GitRepository.collect_garbage}
773 >>> repo = gbp.git.GitRepository(repo_dir)
774 >>> repo.collect_garbage()
779 Test grepping through commit messages
782 - L{gbp.git.GitRepository.grep_log}
785 >>> repo = gbp.git.GitRepository(repo_dir)
786 >>> repo.set_branch('master')
787 >>> len(repo.grep_log('foo')) == 2
789 >>> len(repo.grep_log('foo', 'master')) == 2
791 >>> repo.grep_log('blafasel')
793 >>> repo.grep_log('foo', 'doesnotexist')
794 Traceback (most recent call last):
796 GitRepositoryError: Error grepping log for foo: fatal: bad revision 'doesnotexist'
801 Test if branch is fast forwardable
804 - L{gbp.git.GitRepository.is_fast_forward}
807 >>> repo = gbp.git.GitRepository(repo_dir)
808 >>> repo.is_fast_forward('master', 'foo')
810 >>> repo.create_branch('ff', 'HEAD^')
811 >>> repo.is_fast_forward('ff', 'master')
813 >>> repo.is_fast_forward('master', 'ff')
817 def test_update_ref():
819 Test updating a reference
822 - L{gbp.git.GitRepository.update_ref}
824 >>> import gbp.git, os
825 >>> repo = gbp.git.GitRepository(repo_dir)
826 >>> repo.update_ref('new_ref', 'master', msg='update')
827 >>> os.path.exists(os.path.join(repo.git_dir, 'new_ref'))
832 def test_make_tree():
837 - L{gbp.git.GitRepository.write_file}
838 - L{gbp.git.GitRepository.list_tree}
839 - L{gbp.git.GitRepository.make_tree}
842 >>> repo = gbp.git.GitRepository(repo_dir)
843 >>> sha1 = repo.write_file('testfile')
845 '19af7398c894bc5e86e17259317e4db519e9241f'
846 >>> head = repo.list_tree('HEAD')
848 [['100644', 'blob', '19af7398c894bc5e86e17259317e4db519e9241f', 'testfile']]
849 >>> head.append(['100644', 'blob', '19af7398c894bc5e86e17259317e4db519e9241f', 'testfile2'])
850 >>> newtree = repo.make_tree(head)
852 '745951810c9e22fcc6de9b23f05efd6ab5512123'
853 >>> repo.list_tree(newtree, recurse=False, paths='testfile')
854 [['100644', 'blob', '19af7398c894bc5e86e17259317e4db519e9241f', 'testfile']]
858 def test_update_submodules():
860 Updating submodules if we don't have any is a noop
863 - L{gbp.git.GitRepository.has_submodules}
864 - L{gbp.git.GitRepository.update_submodules}
867 >>> repo = gbp.git.GitRepository(repo_dir)
868 >>> repo.has_submodules()
870 >>> repo.update_submodules()
873 def test_get_merge_base():
875 Find the common ancestor of two objects
878 - L{gbp.git.GitRepository.get_merge_base}
881 >>> repo = gbp.git.GitRepository(repo_dir)
882 >>> sha1 = repo.get_merge_base('master', 'foo')
885 >>> repo.get_merge_base('master', 'doesnotexist')
886 Traceback (most recent call last):
888 GitRepositoryError: Failed to get common ancestor: fatal: Not a valid object name doesnotexist
894 - L{gbp.git.GitRepository.status}
896 >>> import gbp.git, os, shutil
897 >>> repo = gbp.git.GitRepository(repo_dir)
898 >>> fname = os.path.join(repo.path, "test_status")
899 >>> shutil.copy(os.path.join(repo.path, ".git/HEAD"), fname)
900 >>> list(repo.status().items())
901 [('??', ['test_status'])]
902 >>> list(repo.status().items())
904 >>> list(repo.status().items())
905 [('??', ['test_status'])]
906 >>> repo.add_files(repo.path, force=True)
907 >>> repo.commit_all(msg='added %s' % fname)
908 >>> _ = repo._git_inout('mv', [fname, fname + 'new'])
909 >>> list(repo.status().items())
910 [('R ', ['test_status\x00test_statusnew'])]
913 def test_cmd_has_feature():
916 - L{gbp.git.GitRepository._cmd_has_feature}
919 >>> repo = gbp.git.GitRepository(repo_dir)
920 >>> repo._cmd_has_feature("commit", "a")
922 >>> repo._cmd_has_feature("commit", "reuse-message")
924 >>> repo._cmd_has_feature("merge", "n")
926 >>> repo._cmd_has_feature("merge", "stat")
928 >>> repo._cmd_has_feature("format-patch", "cc")
930 >>> repo._cmd_has_feature("merge", "foobaroption")
932 >>> repo._cmd_has_feature("foobarcmd", "foobaroption")
933 Traceback (most recent call last):
935 GitRepositoryError: Invalid git command 'foobarcmd': No manual entry for gitfoobarcmd
936 >>> repo._cmd_has_feature("show", "standard-notes")
938 >>> repo._cmd_has_feature("show", "no-standard-notes")
946 >>> context.teardown()
949 # vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·: