summaryrefslogtreecommitdiff
path: root/linaropy/git/gitrepo.py
diff options
context:
space:
mode:
Diffstat (limited to 'linaropy/git/gitrepo.py')
-rw-r--r--linaropy/git/gitrepo.py166
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()