diff options
Diffstat (limited to 'linaropy/git/gitrepo.py')
-rw-r--r-- | linaropy/git/gitrepo.py | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/linaropy/git/gitrepo.py b/linaropy/git/gitrepo.py index d8ddc74..c4b51a0 100644 --- a/linaropy/git/gitrepo.py +++ b/linaropy/git/gitrepo.py @@ -2,6 +2,8 @@ import unittest import logging import os +import uuid + from sh import git from sh import ErrorReturnCode from sh import ErrorReturnCode_128 @@ -30,6 +32,22 @@ class GitRepo(object): else: raise TypeError('proj input parameter is not of type Proj') + @staticmethod + def is_valid_branch_name(branch): + """Check if branch is a valid git branch name. + + Validity is determined based on git check-ref-format, but it is a bit + more permissive, in the sense that branch names without any / are + considered valid. + """ + try: + git("check-ref-format", "--allow-onelevel", branch) + return True + except ErrorReturnCode as exc: + # TODO: maybe distinguish between invalid branch names and other + # errors that may occur + return False + def branchexists(self, branch): logging.info("Checking to see if branch %s exists" % branch) with cd(self.repodir): @@ -109,6 +127,26 @@ class GitRepo(object): raise EnvironmentError( "Unable to return to the previous branch: %s" % exc.stderr) + # TODO: This only works for local branches, make it work for remote ones + # too + def delete_branch(self, branch, force=False): + """Delete the given branch. + + By default, the branch is only deleted if it has already been merged. If + you wish to delete an unmerged branch, you will have to pass + force=True. + """ + deleteFlag = "-d" + if force: + deleteFlag = "-D" + + try: + git("branch", deleteFlag, branch) + logging.info("Deleted branch %s in %s" % (branch, self.repodir)) + except ErrorReturnCode as exc: + raise EnvironmentError( + 'Unable to delete branch: %s' % str(exc)) + # TODO: Write a unit test for this. def add(self, filetogitadd): # TODO: Determine if filetogitadd is relative or absolute. @@ -194,6 +232,20 @@ class GitRepo(object): class TestGitRepo(unittest.TestCase): repoprefix = "GitRepoUT" + def __create_dummy_commit(self): + filename = "file" + str(uuid.uuid4()) + open(filename, "a").close() + git("add", filename) + git("commit", "-m", "Branches without commits confuse git") + + def __get_current_branch(self): + # Helper function used in some of the tests (e.g. for delete_branch). We + # use this instead of the equivalent GitRepo::getbranch so we can test + # different GitRepo functionality independently. + branch = str(git("rev-parse", "--abbrev-ref", "HEAD")) + # git rev-parse returns a trailing newline that we must get rid of + return branch[:-1] + # This class is only used to test the GitRepo functions since, as an, # abstract-baseclass, GitRepo doesn't define repodir, and we need an # actual repository to test git based functions. @@ -236,6 +288,11 @@ class TestGitRepo(unittest.TestCase): git("tag", "-a", "linaro-99.9-2099.08-rc1", "-m", "This is a test tag") self.assertTrue(self.dgr.tag_exists("linaro-99.9-2099.08-rc1")) + def test_is_valid_branch_name(self): + self.assertTrue(GitRepo.is_valid_branch_name("mybranch")) + self.assertTrue(GitRepo.is_valid_branch_name("mynamespace/mybranch")) + self.assertFalse(GitRepo.is_valid_branch_name("some random string")) + def test_not_branchexists(self): self.assertFalse(self.dgr.branchexists("foobar")) @@ -252,6 +309,115 @@ class TestGitRepo(unittest.TestCase): # TODO: Test checkoutbranch with various combinations of polluted # directories. + def test_delete_merged_branch(self): + with cd(self.dgr.repodir): + branchToDelete = "delete_me" + # Use a try block to separate failures in setting up the test from + # failures in the test itself. + try: + previousBranch = self.__get_current_branch() + + # Create a new branch and don't commit anything to it + git("checkout", "-b", branchToDelete) + + # Move to the previous branch since we can't remove the branch + # that we're currently on. + git("checkout", previousBranch) + except ErrorReturnCode as exc: + raise EnvironmentError("Failed to setup test: %s" % str(exc)) + + # Delete the branch and check that it doesn't exist anymore. + self.dgr.delete_branch(branchToDelete, False) + + with self.assertRaises(ErrorReturnCode) as context: + git("rev-parse", branchToDelete) + + def test_delete_unmerged_branch(self): + with cd(self.dgr.repodir): + branchToDelete = "delete_me" + # Use a try block to separate failures in setting up the test from + # failures in the test itself. + try: + previousBranch = self.__get_current_branch() + + # Create a new branch and commit to it + git("checkout", "-b", branchToDelete) + self.__create_dummy_commit() + + # Move to the previous branch since we can't remove the branch + # that we're currently on + git("checkout", previousBranch) + except ErrorReturnCode as exc: + raise EnvironmentError("Failed to setup test: %s" % str(exc)) + + # Try to delete the branch - this should fail because it has + # unmerged commits + with self.assertRaises(EnvironmentError) as context: + self.dgr.delete_branch(branchToDelete, False) + + self.assertRegexpMatches(str(context.exception), + "Unable to delete branch:") + + # Make sure the branch still exists (i.e. this doesn't throw an + # exception) + git("rev-parse", branchToDelete) + + def test_force_delete_branch(self): + with cd(self.dgr.repodir): + branchToDelete = "force_delete_me" + # Use a try block to separate failures in setting up the test from + # failures in the test itself. + try: + previousBranch = self.__get_current_branch() + + # Create a new branch and commit to it + git("checkout", "-b", branchToDelete) + self.__create_dummy_commit() + + # Move to the previous branch since we can't remove the branch + # that we're currently on + git("checkout", previousBranch) + except ErrorReturnCode as exc: + raise EnvironmentError("Failed to setup test: %s" % str(exc)) + + # Delete the branch and check that it doesn't exist anymore. + self.dgr.delete_branch(branchToDelete, True) + + with self.assertRaises(ErrorReturnCode) as context: + git("rev-parse", branchToDelete) + + def test_delete_current_branch(self): + with cd(self.dgr.repodir): + try: + currentBranch = self.__get_current_branch() + + # We can't delete the current branch + with self.assertRaises(EnvironmentError) as context: + self.dgr.delete_branch(currentBranch, False) + + self.assertRegexpMatches(str(context.exception), + "Unable to delete branch:") + + # Make sure the branch still exists (i.e. this doesn't throw an + # exception) + git("rev-parse", currentBranch) + except ErrorReturnCode as exc: + raise EnvironmentError("Failed to setup test: %s" % str(exc)) + + def test_delete_nonexistent_branch(self): + with cd(self.dgr.repodir): + nonexistentBranch = "should_not_exist" + + # First, make sure that the branch really doesn't exist + with self.assertRaises(ErrorReturnCode) as context: + git("rev-parse", nonexistentBranch) + + with self.assertRaises(EnvironmentError) as context: + self.dgr.delete_branch(nonexistentBranch, False) + + self.assertRegexpMatches(str(context.exception), + "Unable to delete branch:") + if __name__ == '__main__': # logging.basicConfig(level="INFO") unittest.main() |