diff --git a/clowder/clowder_repo.py b/clowder/clowder_repo.py index ecd9164ee..e21d22acb 100644 --- a/clowder/clowder_repo.py +++ b/clowder/clowder_repo.py @@ -1,7 +1,9 @@ """Clowder repo management""" import emoji, os +from git import Repo from termcolor import colored from clowder.utility.git_utilities import ( + git_branches, git_clone_url_at_path, git_is_detached, git_pull @@ -21,6 +23,10 @@ def __init__(self, root_directory): self.root_directory = root_directory self.clowder_path = os.path.join(self.root_directory, 'clowder') + def branches(self): + """Return current local branches""" + return git_branches(self.clowder_path) + def breed(self, url): """Clone clowder repo from url""" git_clone_url_at_path(url, self.clowder_path, 'refs/heads/master', 'origin') @@ -59,14 +65,43 @@ def symlink_yaml(self, version=None): def sync(self): """Sync clowder repo""" self._validate() + self.print_status() if not git_is_detached(self.clowder_path): - print(' - Pulling latest changes') git_pull(self.clowder_path) self.symlink_yaml() else: print(' - HEAD is detached') print_exiting() + # Disable errors shown by pylint for no specified exception types + # pylint: disable=W0702 + def sync_branch(self, branch): + """Sync clowder repo to specified branch""" + try: + repo = Repo(self.clowder_path) + except: + repo_path_output = colored(self.clowder_path, 'cyan') + print("Failed to create Repo instance for " + repo_path_output) + else: + if git_is_detached(self.clowder_path): + try: + repo.git.checkout(branch) + except: + print("Failed to checkout branch " + branch) + print_exiting() + + if repo.active_branch.name != branch: + try: + repo.git.checkout(branch) + except: + print("Failed to checkout branch " + branch) + print_exiting() + + self._validate() + self.print_status() + git_pull(self.clowder_path) + self.symlink_yaml() + def _validate(self): """Validate status of clowder repo""" if not validate_repo_state(self.clowder_path): diff --git a/clowder/cmd.py b/clowder/cmd.py index a122155b9..752c94d7c 100644 --- a/clowder/cmd.py +++ b/clowder/cmd.py @@ -20,11 +20,14 @@ def __init__(self): self.versions = None self.group_names = '' self.project_names = '' + self.branches = '' # Load current clowder.yml config if it exists - if os.path.isdir(os.path.join(self.root_directory, 'clowder')): + clowder_path = os.path.join(self.root_directory, 'clowder') + if os.path.isdir(clowder_path): self.clowder_repo = ClowderRepo(self.root_directory) self.clowder = ClowderController(self.root_directory) self.versions = self.clowder.get_fixed_version_names() + self.branches = self.clowder_repo.branches() if self.clowder.get_all_group_names() is not None: self.group_names = self.clowder.get_all_group_names() if self.clowder.get_all_project_names() is not None: @@ -134,8 +137,10 @@ def sync(self): """clowder sync command""" if self.clowder_repo is not None: cprint('Sync...\n', 'yellow') - self.clowder_repo.print_status() - self.clowder_repo.sync() + if self.args.branch is None: + self.clowder_repo.sync() + else: + self.clowder_repo.sync_branch(self.args.branch) else: exit_clowder_not_found() @@ -197,7 +202,9 @@ def _configure_subparsers(self, subparsers): group_stash.add_argument('--projects', '-p', choices=self.project_names, nargs='+', help='Projects to stash') # clowder sync - subparsers.add_parser('sync', add_help=False, help='Sync clowder repo') + parser_sync = subparsers.add_parser('sync', add_help=False, help='Sync clowder repo') + parser_sync.add_argument('--branch', '-b', choices=self.branches, + help='Groups to print status for') def exit_unrecognized_command(parser): """Print unrecognized command message and exit""" diff --git a/clowder/utility/git_utilities.py b/clowder/utility/git_utilities.py index de0f2b652..adef87d7e 100644 --- a/clowder/utility/git_utilities.py +++ b/clowder/utility/git_utilities.py @@ -6,6 +6,16 @@ # Disable errors shown by pylint for no specified exception types # pylint: disable=W0702 +def git_branches(repo_path): + """Get list of current branches""" + try: + repo = Repo(repo_path) + except: + repo_path_output = colored(repo_path, 'cyan') + print("Failed to create Repo instance for " + repo_path_output) + else: + return repo.branches + def git_checkout_branch(repo_path, branch, remote): """Checkout branch, and create if it doesn't exist""" try: diff --git a/scripts/test_cats_example.sh b/scripts/test_cats_example.sh index 6cb4e8b39..7c55bb396 100755 --- a/scripts/test_cats_example.sh +++ b/scripts/test_cats_example.sh @@ -100,6 +100,25 @@ test_no_versions() popd &>/dev/null } +test_sync_branch() +{ + print_separator + echo "TEST: Test syncing branch from current branch" + pushd clowder &>/dev/null + git checkout master + popd &>/dev/null + clowder sync -b master || exit 1 + echo "TEST: Test syncing other branch" + clowder sync -b tags || exit 1 + echo "TEST: Test syncing missing branch" + clowder sync -b sync-missing-branch && exit 1 + echo "TEST: Test syncing branch from detached HEAD" + pushd clowder &>/dev/null + git checkout master~2 + popd &>/dev/null + clowder sync -b master || exit 1 +} + # export projects=( 'black-cats/kit' \ # 'black-cats/kishka' \ # 'black-cats/sasha' \ @@ -144,5 +163,6 @@ test_invalid_yaml test_herd_sha test_herd_tag test_herd_missing_groups +test_sync_branch print_help diff --git a/setup.py b/setup.py index 3f00b1ac8..2508273e7 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ setup( name='clowder', description='A tool for managing code', - version='0.7.3', + version='0.7.4', url='http://clowder.cat', author='joe DeCapo', author_email='joe@polka.cat',