예제 #1
0
def currentVersion() -> str:
    versions = getVersions()
    g = Git(path)
    for i in versions:
        if i in g.branch():
            return i

    if "* master" == g.branch():
        return "master"

    return g.branch()
예제 #2
0
def branches():
    g = Git(PROJECT_DIR)
    send('重新获取远程分支中.....')
    g.fetch(REMOTE_NAME)
    send('获取成功')
    branch_names = g.branch('-a').split('\n')
    return jsonify(branch_names)
예제 #3
0
def branches():
    g = Git(PROJECT_DIR)
    send('重新获取远程分支中.....')
    g.fetch(REMOTE_NAME)
    send('获取成功')
    branch_names = g.branch('-a').split('\n')
    return jsonify(branch_names)
예제 #4
0
파일: helpers.py 프로젝트: jsBoot/jsboot.js
    def __init__(self):
        # Prevent working as root
        uname = puke.utils.env.get("PUKE_OS", puke.system.uname).lower()
        id = puke.utils.env.get("PUKE_LOGIN", puke.system.login)
        if id == "root":
            puke.display.error("Root detected! Panic!")
            puke.log.critical(
                "Running puke as root without a PUKE_LOGIN is frown upon")

        # Load chained config files
        r = Config(
            {}, "~/.pukerc", ["package.json", "package-%s-%s.json" % (id, uname)])

        self.man = r
        r = r.content
        # Map to older format for lazyness reasons :)
        clean = re.sub('[.]git$', '', r.repository["url"])

        r.package = {
            "name": r.name,
            "version": r.version
        }

        r.rights = {
            "license": '<a href="%s">%s</a>' % (r.licenses[0]["url"], r.licenses[0]["type"]),
            "copyright": 'All rights reserved <a href="http://www.webitup.fr">copyright %s</a>' % r.author,
            "author": r.author
        }

        r.git = {
            "root": '%s/blob/master/src' % clean
        }
        r.paths = r.directories
        r.config = r.config

        # Git in the yanks
        try:
            g = Git()
            r.git.root = r.git.root.replace(
                '/master/', '/%s/' % g.branch())
            r.git.revision = '#' + g.nb() + '-' + g.hash()
        except:
            r.git.revision = '#no-git-information'
            puke.display.warning(
                "FAILED fetching git information - locations won't be accurate")

        for (key, path) in r.paths.items():
            puke.fs.mkdir(path)

        self.config = r

        # Bower wrapping
        try:
            self.bower = Bower(self.config.bower)
        except Exception as e:
            puke.sh.npm.install()
            self.bower = Bower(self.config.bower)
예제 #5
0
def findGitBranch(commit_id=None):
    SHA = commit_id
    repo = Git()
    branchs = repo.branch(contains=SHA).split()
    # print(repo.branch(contains=SHA).split())

    pattern = re.compile('^(release|prerelease)\/.*$')
    match = filter(pattern.search, branchs)
    return list(match)[0]
 def _get_branches(self):
     c_git = Git(str(self._conf.get("path_to_repo")))
     branches = set()
     args = ["--contains", self.hash]
     if self._conf.get("include_remotes"):
         args = ["-r"] + args
     if self._conf.get("include_refs"):
         args = ["-a"] + args
     for branch in set(c_git.branch(*args).split("\n")):
         branches.add(branch.strip().replace("* ", ""))
     return branches
예제 #7
0
    def branches(self) -> Set[str]:
        """
        Return the set of branches that contain the commit.

        :return: set(str) branches
        """
        git = Git(self._path)
        branches = set()
        for branch in set(git.branch('--contains', self.hash).split('\n')):
            branches.add(branch.strip().replace('* ', ''))
        return branches
예제 #8
0
    def update(self):
        """
        Updates the opsoro software thru git
        """
        if self.git is None:
            return False
        # Create file to let deamon know it has to update before starting the server
        # file = open(self.dir + 'update.var', 'w+')

        backup_dir = '/home/pi/OPSORO/backup/'

        print('Updating...')
        if os.path.exists(backup_dir):
            # remove previous backup
            try:
                shutil.rmtree(backup_dir)
            except Exception as e:
                print_error('Remove backup failed: ' + str(e))
                pass

        try:
            shutil.copytree(self.dir, backup_dir)
        except Exception as e:
            print_error('Backup copy failed: ' + str(e))
            pass

        # Link git & update
        try:
            g = Git(self.dir)
            g.fetch('--all')
            g.reset('--hard', 'origin/' + g.branch().split()[-1])
            # g.pull()
        except Exception as e:
            print_error('Git failed to update: ' + str(e))
            pass

        # Set script executable for deamon
        try:
            st = os.stat(os.path.join(self.dir, 'run'))
            os.chmod(os.path.join(self.dir, 'run'),
                     st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
        except Exception as e:
            print_error('Exec state set failed: ' + str(e))
            pass

        # Clear update var file
        # os.remove(os.path.join(self.dir, 'update.var'))

        # restart service
        command = ['/usr/sbin/service', 'opsoro', 'restart']
        #shell=FALSE for sudo to work.
        subprocess.call(command, shell=False)
예제 #9
0
    def get_version_from_branch(repository: git.Git) -> Optional[str]:
        """Extract version from release branch name

        Parameters
        ----------
        repository

        Returns
        -------
        version
        """
        current_remote_branch = repository.branch("--remote", "--contains")
        if "releases/" in current_remote_branch:
            return current_remote_branch.split("/")[-1]
        return None
예제 #10
0
파일: cloner.py 프로젝트: ofiriluz/octopus
def get_branches(path):
    os.chdir(path)

    ## end new
    branches = []
    try:
        g = Git()
        branches = g.branch()

        print('branches object {}'.format(branches.replace('g', '')))
        names = branches.split(' ')
        print(names)

    except Exception as e:
        print(e)
예제 #11
0
def branch(branch_name):
    # branch_name = 'remotes/ssh/' + branch_name
    g = Git(PROJECT_DIR)
    send('重新获取远程分支中.....')
    g.fetch(REMOTE_NAME)
    send('获取成功')
    branch_names = g.branch('-a').split('\n')
    branch_names = [_.strip() for _ in branch_names]
    if branch_name not in branch_names:
        return '你的这个branch啊,鹅母鸡鸭'
    try:
        send('重置分支')
        g.reset('--hard')
        send('切换分支中')
        g.checkout(branch_name)
        send('切换分支完成')
    except Exception as e:
        return str(e)
    return branch_name
예제 #12
0
def branch(branch_name):
    # branch_name = 'remotes/ssh/' + branch_name
    g = Git(PROJECT_DIR)
    send('重新获取远程分支中.....')
    g.fetch(REMOTE_NAME)
    send('获取成功')
    branch_names = g.branch('-a').split('\n')
    branch_names = [_.strip() for _ in branch_names]
    if branch_name not in branch_names:
        return '你的这个branch啊,鹅母鸡鸭'
    try:
        send('重置分支')
        g.reset('--hard')
        send('切换分支中')
        g.checkout(branch_name)
        send('切换分支完成')
    except Exception as e:
        return str(e)
    return branch_name
예제 #13
0
파일: core.py 프로젝트: opicacek/gitflow
class GitFlow(object):
    """
    Creates a :class:`GitFlow` instance.

    :param working_dir:
        The directory where the Git repo is located.  If not specified, the
        current working directory is used.

    When a :class:`GitFlow` class is instantiated, it auto-discovers all
    subclasses of :class:`gitflow.branches.BranchManager`, so there is no
    explicit registration required.
    """

    def _discover_branch_managers(self):
        managers = {}
        for cls in itersubclasses(BranchManager):
            # TODO: Initialize managers with the gitflow branch prefixes
            managers[cls.identifier] = cls(self)
        return managers

    def __init__(self, working_dir='.'):
        # Allow Repos to be passed in instead of strings
        self.repo = None
        if isinstance(working_dir, Repo):
            self.working_dir = working_dir.working_dir
        else:
            self.working_dir = working_dir

        self.git = Git(self.working_dir)
        try:
            self.repo = Repo(self.working_dir)
        except InvalidGitRepositoryError:
            pass

        self.managers = self._discover_branch_managers()
        self.defaults = {
            'gitflow.branch.master': 'master',
            'gitflow.branch.develop': 'develop',
            'gitflow.prefix.versiontag': '',
            'gitflow.origin': 'origin',
            'gitflow.release.versionmatcher': '[0-9]+([.][0-9]+){2}',
            }
        for identifier, manager in self.managers.items():
            self.defaults['gitflow.prefix.%s' % identifier] = manager.DEFAULT_PREFIX


    def _init_config(self, master=None, develop=None, prefixes={}, names={},
                     force_defaults=False):
        for setting, default in self.defaults.items():
            if force_defaults:
                value = default
            elif setting == 'gitflow.branch.master':
                value = master
            elif setting == 'gitflow.branch.develop':
                value = develop
            elif setting.startswith('gitflow.prefix.'):
                name = setting[len('gitflow.prefix.'):]
                value = prefixes.get(name, None)
            else:
                name = setting[len('gitflow.'):]
                value = names.get(name, None)
            if value is None:
                value = self.get(setting, default)
            self.set(setting, value)

    def _init_initial_commit(self):
        master = self.master_name()
        if master in self.repo.branches:
            # local `master` branch exists
            return
        elif self.origin_name(master) in self.repo.refs:
            # the origin branch counterpart exists
            origin = self.repo.refs[self.origin_name(master)]
            branch = self.repo.create_head(master, origin)
            branch.set_tracking_branch(origin)
        elif self.repo.heads:
            raise NotImplementedError('Local and remote branches exist, '
                                     'but neither %s nor %s'
                                     % (master, self.origin_name(master) ))
        else:
            # Create 'master' branch
            info('Creating branch %r' % master)
            c = self.repo.index.commit('Initial commit', head=False)
            self.repo.create_head(master, c)

    def _init_develop_branch(self):
        # assert master already exists
        assert self.master_name() in self.repo.refs
        develop = self.develop_name()
        if develop in self.repo.branches:
            # local `develop` branch exists, but do not switch there
            return
        if self.origin_name(develop) in self.repo.refs:
            # the origin branch counterpart exists
            origin = self.repo.refs[self.origin_name(develop)]
            branch = self.repo.create_head(develop, origin)
            branch.set_tracking_branch(origin)
        else:
            # Create 'develop' branch
            info('Creating branch %r' % develop)
            branch = self.repo.create_head(develop, self.master())
        # switch to develop branch if its newly created
        info('Switching to branch %s' % branch)
        branch.checkout()


    def _enforce_git_repo(self):
        """
        Ensure a (maybe empty) repository exists we can work on.

        This is to be used by the `init` sub-command.
        """
        if self.repo is None:
            self.git.init(self.working_dir)
            self.repo = Repo(self.working_dir)


    def _enforce_services(self):
        err = False
        for option in ('gitflow.pt.token',
                       'reviewboard.url',
                       'reviewboard.server'):
            try:
                value = self.get(option)
                if option == 'reviewboard.url' and not value.endswith('/'):
                    err = True
                    print """
Git config key 'reviewboard.url' must contain a trailing slash.
Update your configuration by executing

    $ git config [--global] reviewboard.url %s
""" % (value + '/')
            except Exception:
                try:
                    if option == 'gitflow.pt.token':
                        value = self.get('workflow.token')
                        self.set('gitflow.pt.token', value)
                        continue
                except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
                    pass
                err = True
                print """
Git config '%s' missing, please fill it in by executing

    $ git config [--global] %s <value>
""" % (option, option)

        if err:
            raise SystemExit('Operation canceled.')


    def init(self, master=None, develop=None, prefixes={}, names={},
             force_defaults=False):
        self._enforce_git_repo()
        self._init_config(master, develop, prefixes, names, force_defaults)
        self._init_initial_commit()
        self._init_develop_branch()
        return self

    def is_initialized(self):
        return (self.repo and
                self.is_set('gitflow.branch.master') and
                self.is_set('gitflow.branch.develop') and
                self.is_set('gitflow.prefix.feature') and
                self.is_set('gitflow.prefix.release') and
                self.is_set('gitflow.prefix.hotfix') and
                self.is_set('gitflow.prefix.support') and
                self.is_set('gitflow.prefix.versiontag') and
                self.is_set('gitflow.release.versionmatcher'))

    def _parse_setting(self, setting):
        groups = setting.split('.', 2)
        if len(groups) == 2:
            section, option = groups
        elif len(groups) == 3:
            section, subsection, option = groups
            section = '%s "%s"' % (section, subsection)
        else:
            raise ValueError('Invalid setting name: %s' % setting)
        return (section, option)

    @requires_repo
    def get(self, setting, default=_NONE):
        section, option = self._parse_setting(setting)
        try:
            return self.repo.config_reader().get_value(section, option)
        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
            if default is not _NONE:
                return default
            raise

    def get_prefix(self, identifier):
        return self._safe_get('gitflow.prefix.%s' % (identifier,))

    @requires_repo
    def set(self, setting, value):
        section, option = self._parse_setting(setting)
        self.repo.config_writer().set_value(section, option, value)

    def is_set(self, setting):
        return self.get(setting, None) is not None


    @requires_repo
    def _safe_get(self, setting_name):
        try:
            return self.get(setting_name)
        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
            raise NotInitialized('This repo has not yet been initialized.')

    def master_name(self):
        return self._safe_get('gitflow.branch.master')

    def develop_name(self):
        return self._safe_get('gitflow.branch.develop')

    def origin_name(self, name=None):
        origin = self.get('gitflow.origin', self.defaults['gitflow.origin'])
        if name is not None:
            return origin + '/' + name
        else:
            return origin

    @requires_repo
    def require_remote(self, name):
        try:
            remote = self.repo.remotes[name]
        except IndexError:
            raise NoSuchRemoteError("Remote '{0}' was not found.".format(name))

        # Monkey patch remote.fetch to fix a false negative assertion in GitPython.
        class RemoteWrapper(object):
            def __init__(self, remote):
                self._remote = remote

            def __str__(self):
                return self._remote.__str__()

            def __repr__(self):
                return self._remote.__repr__()

            def __eq__(self, other):
                return self._remote.__eq__(other)

            def __ne__(self, other):
                return self._remote.__ne__(other)

            def __hash__(self):
                return self._remote.__hash__()

            # For some reason __getattribute__ is making infinite recursion,
            # so let's use __getattr__.
            def __getattr__(self, name):
                return self._remote.__getattribute__(name)

            # This is the main monkey patching part.
            def fetch(self, refspec=None, progress=None, **kwargs):
                # Let's try 3 times...
                err = None
                for x in range(3):
                    try:
                        return self._remote.fetch(refspec=refspec,
                                progress=progress, **kwargs)
                    except AssertionError as e:
                        err = e
                        # If we are in '_get_fetch_info_from_stderr',
                        # it's the broken assertion and we skip it.
                        func_name = traceback.extract_stack()[-1][2]
                        if func_name != '_get_fetch_info_from_stderr':
                            raise e
                        err = None
                # If we somehow get the same exception 3 times, just raise anyway.
                raise err

        return RemoteWrapper(remote)

    @requires_repo
    def require_origin_branch(self, branch_name):
        origin_name = self.origin_name(branch_name)
        refs = [r
                for r in self.repo.refs
                if isinstance(r, RemoteReference) and r.name == origin_name]
        if len(refs) != 0:
            assert len(refs) == 1
            return refs[0]
        raise NoSuchBranchError(
                "Remote branch '{0}' was not found".format(origin_name))

    def origin(self):
        return self.require_remote(self.origin_name())

    @requires_repo
    def develop(self):
        return self.repo.branches[self.develop_name()]

    @requires_repo
    def master(self):
        return self.repo.branches[self.master_name()]

    @requires_repo
    def branch_names(self, remote=False):
        if remote:
            return [r.name
                    for r in self.repo.refs
                    if isinstance(r, RemoteReference)]
        else:
            return [r.name for r in self.repo.branches]

    @requires_repo
    def nameprefix_or_current(self, identifier, prefix):
        """
        :param identifier:
            The identifier for the type of branch to create.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param prefix: If the empty, see if the current branch is of
            type `identifier`. If so, returns the current branches
            short name, otherwise raises :exc:`NoSuchBranchError`.

        If exactly one branch of type `identifier` starts with
        the given name `prefix`, returns that branches short name.
        Raises :exc:`NoSuchBranchError` in case no branch exists with
        the given prefix, or :exc:`PrefixNotUniqueError` in case
        multiple matches are found.
        """
        repo = self.repo
        manager = self.managers[identifier]
        if not prefix:
            if repo.active_branch.name.startswith(manager.prefix):
                return manager.shorten(repo.active_branch.name)
            else:
                raise NoSuchBranchError('The current branch is no %s branch. '
                    'Please specify one explicitly.' % identifier)
        branch = None
        try:
            branch = manager.by_name_prefix(prefix).name
        except NoSuchBranchError as ex:
            try:
                remote_branch = manager.by_name_prefix(prefix, remote=True).name
                branch = str(remote_branch)[len(self.origin_name())+1:]
                self.git.branch(branch, remote_branch)
            except NoSuchBranchError:
                raise ex
        return manager.shorten(branch)

    @requires_repo
    def name_or_current(self, identifier, name, must_exist=True):
        """
        :param identifier:
            The identifier for the type of branch to create.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param  name:
           If the `name` is empty, see if the current branch is of
           type `identifier`. If so, returns the current branches
           short name, otherwise raises :exc:`NoSuchBranchError`.

        :param must_exist: If `True` (the default), raises
            :exc:`NoSuchBranchError` in case no branch exists with the
            given `name`.

        Otherwise return the `name` unchanged.
        """
        repo = self.repo
        manager = self.managers[identifier]
        if not name:
            if repo.active_branch.name.startswith(manager.prefix):
                return manager.shorten(repo.active_branch.name)
            else:
                raise NoSuchBranchError('The current branch is no %s branch. '
                    'Please specify one explicitly.' % identifier)
        elif must_exist and not manager.full_name(name) in (b.name for b in manager.list()):
            raise NoSuchBranchError('There is no %s branch named %s.'
                                    % (identifier, name))
        return name


    @requires_repo
    def status(self):
        result = []
        for b in self.repo.branches:
            tup = self.branch_info(b.name)
            result.append(tup)
        return result

    @requires_repo
    def branch_info(self, name):
        active_branch = self.repo.active_branch
        b = self.repo.heads[name]
        return (name, b.commit.hexsha, b == active_branch)

    @requires_repo
    def is_dirty(self):
        """
        Returns whether or not the current working directory contains
        uncommitted changes.
        """
        return self.repo.is_dirty()

    @requires_repo
    def has_staged_commits(self):
        """
        Returns whether or not the current repo contains local changes
        checked into the index but not committed.
        """
        return len(self.repo.index.diff(self.repo.head.commit)) > 0


    @requires_repo
    def require_no_merge_conflict(self):
        """
        Raises :exc:`MergeConflict` if the current working directory
        contains a merge conflict.
        """
        try:
            git.Reference(self.repo, 'MERGE_HEAD', check_path=False).commit
            # reference exists, so there is a merge conflict
            raise MergeConflict()
        except ValueError:
            # no such reference, so there is no merge conflict
            pass


    def is_merged_into(self, commit, target_branch):
        """
        Checks whether `commit` is successfully merged into branch
        `target_branch`.

        :param commit:
            The commit or branch that ought to be merged. This may be
            a full branch-name, a commit-hexsha or any of branch-,
            head-, reference- or commit-object.

        :param target_branch:
            The branch which should contain the commit. This may be a
            full branch-name, or any of branch-, head- or
            reference-object.
        """
        try:
            commit = self.repo.rev_parse(str(commit))
        except git.BadObject:
            raise BadObjectError(commit)
        if isinstance(target_branch, git.RemoteReference):
            target_branch = 'remotes/' + target_branch.name
        elif isinstance(target_branch, git.SymbolicReference):
            target_branch = target_branch.name
        # :todo: implement this more efficiently
        return target_branch in [
            b.lstrip('* ')
            for b in self.git.branch('-a', '--contains', commit).splitlines()]


    def must_be_uptodate(self, branch):
        remote_branch = self.origin_name(branch)
        if remote_branch in self.branch_names(remote=True):
            self.require_branches_equal(branch, remote_branch)

    @requires_repo
    def _compare_branches(self, branch1, branch2):
        """
        Tests whether branches and their 'origin' counterparts have
        diverged and need merging first. It returns error codes to
        provide more detail, like so:

        0    Branch heads point to the same commit
        1    First given branch needs fast-forwarding
        2    Second given branch needs fast-forwarding
        3    Branch needs a real merge
        4    There is no merge base, i.e. the branches have no common ancestors
        """
        try:
            commit1 = self.repo.rev_parse(branch1)
            commit2 = self.repo.rev_parse(branch2)
        except git.BadObject, e:
            raise NoSuchBranchError('Branch {0} not found'.format(e.args[0]))
        if commit1 == commit2:
            return 0
        try:
            base = self.repo.git.merge_base(commit1, commit2)
        except GitCommandError:
            return 4
        if base == commit1:
            return 1
        elif base == commit2:
            return 2
        else:
            return 3
예제 #14
0
 def _get_branches(self):
     git = Git(str(self.project_path))
     branches = set()
     for branch in set(git.branch('--contains', self.hash).split('\n')):
         branches.add(branch.strip().replace('* ', ''))
     return branches
예제 #15
0
def main():
    # Determine what json-ld file to compare
    which = 'locations'
    if len(sys.argv) == 2:
        which = sys.argv[1]

    # Initialize git client and store current branch name
    git = Git()
    currentBranch = None
    for branch in git.branch().split('\n'):
        if branch[0] == '*':
            currentBranch = branch[2:]
            break

    print(f'Comparing {which} in {currentBranch} to master')

    # Get current working copy of the JSON-LD file
    newFile = openJsonFile(which)

    try:
        # Checkout master and get the current version
        git.checkout('master')
        masterFile = openJsonFile(which)

        # Compare the two objects
        for item in newFile['@graph']:
            if not 'skos:notation' in item:
                print('Item found without a skos:notation!', item)

        newLocDict = {
            item['skos:notation']: item
            for item in newFile['@graph']
        }
        masterLocDict = {
            item['skos:notation']: item
            for item in masterFile['@graph']
        }
        newKeys = newLocDict.keys() - masterLocDict.keys()
        deletedKeys = masterLocDict.keys() - newLocDict.keys()
        alteredKeys = list(
            filter(lambda x: x[1], [
                (key,
                 DeepDiff(
                     masterLocDict[key], newLocDict[key], ignore_order=True))
                for key in set(newLocDict.keys()) & set(masterLocDict.keys())
            ]))

        # Output comparison results
        print('Keys Added: {}'.format(len(newKeys)))
        print('Keys Deleted: {}'.format(len(deletedKeys)))
        print('Keys Altered: {}'.format(len(alteredKeys)))
        displayAlterations(
            alteredKeys)  # Provide details on altered mapping objects

    except Exception as e:
        template = "An exception of type {0} occurred. Arguments:\n{1!r}"
        message = template.format(type(e).__name__, e.args)
        print(message)

    # Reset to current working branch
    git.checkout(currentBranch)
예제 #16
0
class GitFlow(object):
    """
    Creates a :class:`GitFlow` instance.

    :param working_dir:
        The directory where the Git repo is located.  If not specified, the
        current working directory is used.

    When a :class:`GitFlow` class is instantiated, it auto-discovers all
    subclasses of :class:`gitflow.branches.BranchManager`, so there is no
    explicit registration required.
    """
    def _discover_branch_managers(self):
        managers = {}
        for cls in itersubclasses(BranchManager):
            # TODO: Initialize managers with the gitflow branch prefixes
            managers[cls.identifier] = cls(self)
        return managers

    def __init__(self, working_dir='.'):
        # Allow Repos to be passed in instead of strings
        self.repo = None
        if isinstance(working_dir, Repo):
            self.working_dir = working_dir.working_dir
        else:
            self.working_dir = working_dir

        self.git = Git(self.working_dir)
        try:
            self.repo = Repo(self.working_dir)
        except InvalidGitRepositoryError:
            pass

        self.managers = self._discover_branch_managers()
        self.defaults = {
            'gitflow.branch.master': 'master',
            'gitflow.branch.develop': 'develop',
            'gitflow.prefix.versiontag': '',
            'gitflow.origin': 'origin',
        }
        for identifier, manager in self.managers.items():
            self.defaults['gitflow.prefix.%s' %
                          identifier] = manager.DEFAULT_PREFIX

    def _init_config(self,
                     master=None,
                     develop=None,
                     prefixes={},
                     names={},
                     force_defaults=False):
        for setting, default in self.defaults.items():
            if force_defaults:
                value = default
            elif setting == 'gitflow.branch.master':
                value = master
            elif setting == 'gitflow.branch.develop':
                value = develop
            elif setting.startswith('gitflow.prefix.'):
                name = setting[len('gitflow.prefix.'):]
                value = prefixes.get(name, None)
            else:
                name = setting[len('gitflow.'):]
                value = names.get(name, None)
            if value is None:
                value = self.get(setting, default)
            self.set(setting, value)

    def _init_initial_commit(self):
        master = self.master_name()
        if master in self.repo.branches:
            # local `master` branch exists
            return
        elif self.origin_name(master) in self.repo.refs:
            # the origin branch counterpart exists
            origin = self.repo.refs[self.origin_name(master)]
            branch = self.repo.create_head(master, origin)
            branch.set_tracking_branch(origin)
        elif self.repo.heads:
            raise NotImplementedError('Local and remote branches exist, '
                                      'but neither %s nor %s' %
                                      (master, self.origin_name(master)))
        else:
            # Create 'master' branch
            info('Creating branch %r' % master)
            c = self.repo.index.commit('Initial commit', head=False)
            self.repo.create_head(master, c)

    def _init_develop_branch(self):
        # assert master already exists
        assert self.master_name() in self.repo.refs
        develop = self.develop_name()
        if develop in self.repo.branches:
            # local `develop` branch exists, but do not switch there
            return
        if self.origin_name(develop) in self.repo.refs:
            # the origin branch counterpart exists
            origin = self.repo.refs[self.origin_name(develop)]
            branch = self.repo.create_head(develop, origin)
            branch.set_tracking_branch(origin)
        else:
            # Create 'develop' branch
            info('Creating branch %r' % develop)
            branch = self.repo.create_head(develop, self.master())
        # switch to develop branch if its newly created
        info('Switching to branch %s' % branch)
        branch.checkout()

    def _enforce_git_repo(self):
        """
        Ensure a (maybe empty) repository exists we can work on.

        This is to be used by the `init` sub-command.
        """
        if self.repo is None:
            self.git.init(self.working_dir)
            self.repo = Repo(self.working_dir)

    def init(self,
             master=None,
             develop=None,
             prefixes={},
             names={},
             force_defaults=False):
        self._enforce_git_repo()
        self._init_config(master, develop, prefixes, names, force_defaults)
        self._init_initial_commit()
        self._init_develop_branch()
        return self

    def is_initialized(self):
        return (self.repo and self.is_set('gitflow.branch.master')
                and self.is_set('gitflow.branch.develop')
                and self.is_set('gitflow.prefix.feature')
                and self.is_set('gitflow.prefix.release')
                and self.is_set('gitflow.prefix.hotfix')
                and self.is_set('gitflow.prefix.support')
                and self.is_set('gitflow.prefix.versiontag'))

    def _parse_setting(self, setting):
        groups = setting.split('.', 2)
        if len(groups) == 2:
            section, option = groups
        elif len(groups) == 3:
            section, subsection, option = groups
            section = '%s "%s"' % (section, subsection)
        else:
            raise ValueError('Invalid setting name: %s' % setting)
        return (section, option)

    @requires_repo
    def get(self, setting, default=_NONE):
        section, option = self._parse_setting(setting)
        try:
            return self.repo.config_reader().get_value(section, option)
        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
            if default is not _NONE:
                return default
            raise

    def get_prefix(self, identifier):
        return self._safe_get('gitflow.prefix.%s' % (identifier, ))

    @requires_repo
    def set(self, setting, value):
        section, option = self._parse_setting(setting)
        self.repo.config_writer().set_value(section, option, value)

    def is_set(self, setting):
        return self.get(setting, None) is not None

    @requires_repo
    def _safe_get(self, setting_name):
        try:
            return self.get(setting_name)
        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
            raise NotInitialized('This repo has not yet been initialized.')

    def master_name(self):
        return self._safe_get('gitflow.branch.master')

    def develop_name(self):
        return self._safe_get('gitflow.branch.develop')

    def origin_name(self, name=None):
        origin = self.get('gitflow.origin', self.defaults['gitflow.origin'])
        if name is not None:
            return origin + '/' + name
        else:
            return origin

    @requires_repo
    def require_remote(self, name):
        try:
            return self.repo.remotes[name]
        except IndexError:
            raise NoSuchRemoteError(name)

    def origin(self):
        return self.require_remote(self.origin_name())

    @requires_repo
    def develop(self):
        return self.repo.branches[self.develop_name()]

    @requires_repo
    def master(self):
        return self.repo.branches[self.master_name()]

    @requires_repo
    def branch_names(self, remote=False):
        if remote:
            return [
                r.name for r in self.repo.refs
                if isinstance(r, RemoteReference)
            ]
        else:
            return [r.name for r in self.repo.branches]

    @requires_repo
    def nameprefix_or_current(self, identifier, prefix):
        """
        :param identifier:
            The identifier for the type of branch to create.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param prefix: If the empty, see if the current branch is of
            type `identifier`. If so, returns the current branches
            short name, otherwise raises :exc:`NoSuchBranchError`.

        If exactly one branch of type `identifier` starts with
        the given name `prefix`, returns that branches short name.
        Raises :exc:`NoSuchBranchError` in case no branch exists with
        the given prefix, or :exc:`PrefixNotUniqueError` in case
        multiple matches are found.
        """
        repo = self.repo
        manager = self.managers[identifier]
        if not prefix:
            if repo.active_branch.name.startswith(manager.prefix):
                return manager.shorten(repo.active_branch.name)
            else:
                raise NoSuchBranchError('The current branch is no %s branch. '
                                        'Please specify one explicitly.' %
                                        identifier)
        return manager.shorten(manager.by_name_prefix(prefix).name)

    @requires_repo
    def name_or_current(self, identifier, name, must_exist=True):
        """
        :param identifier:
            The identifier for the type of branch to create.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param  name: 
           If the `name` is empty, see if the current branch is of
           type `identifier`. If so, returns the current branches
           short name, otherwise raises :exc:`NoSuchBranchError`.

        :param must_exist: If `True` (the default), raises
            :exc:`NoSuchBranchError` in case no branch exists with the
            given `name`.

        Otherwise return the `name` unchanged.
        """
        repo = self.repo
        manager = self.managers[identifier]
        if not name:
            if repo.active_branch.name.startswith(manager.prefix):
                return manager.shorten(repo.active_branch.name)
            else:
                raise NoSuchBranchError('The current branch is no %s branch. '
                                        'Please specify one explicitly.' %
                                        identifier)
        elif must_exist and not manager.full_name(name) in (
                b.name for b in manager.list()):
            raise NoSuchBranchError('There is no %s branch named %s.' %
                                    (identifier, name))
        return name

    @requires_repo
    def status(self):
        result = []
        for b in self.repo.branches:
            tup = self.branch_info(b.name)
            result.append(tup)
        return result

    @requires_repo
    def branch_info(self, name):
        active_branch = self.repo.active_branch
        b = self.repo.heads[name]
        return (name, b.commit.hexsha, b == active_branch)

    @requires_repo
    def is_dirty(self):
        """
        Returns whether or not the current working directory contains
        uncommitted changes.
        """
        return self.repo.is_dirty()

    @requires_repo
    def has_staged_commits(self):
        """
        Returns whether or not the current repo contains local changes
        checked into the index but not committed.
        """
        return len(self.repo.index.diff(self.repo.head.commit)) > 0

    @requires_repo
    def require_no_merge_conflict(self):
        """
        Raises :exc:`MergeConflict` if the current working directory
        contains a merge conflict.
        """
        try:
            git.Reference(self.repo, 'MERGE_HEAD', check_path=False).commit
            # reference exists, so there is a merge conflict
            raise MergeConflict()
        except ValueError:
            # no such reference, so there is no merge conflict
            pass

    def is_merged_into(self, commit, target_branch):
        """
        Checks whether `commit` is successfully merged into branch
        `target_branch`.

        :param commit:
            The commit or branch that ought to be merged. This may be
            a full branch-name, a commit-hexsha or any of branch-,
            head-, reference- or commit-object.

        :param target_branch:
            The branch which should contain the commit. This may be a
            full branch-name, or any of branch-, head- or
            reference-object.
        """
        try:
            commit = self.repo.rev_parse(str(commit))
        except git.BadObject:
            raise BadObjectError(commit)
        if isinstance(target_branch, git.RemoteReference):
            target_branch = 'remotes/' + target_branch.name
        elif isinstance(target_branch, git.SymbolicReference):
            target_branch = target_branch.name
        # :todo: implement this more efficiently
        return target_branch in [
            b.lstrip('* ')
            for b in self.git.branch('-a', '--contains', commit).splitlines()
        ]

    def must_be_uptodate(self, branch, fetch):
        remote_branch = self.origin_name(branch)
        if remote_branch in self.branch_names(remote=True):
            if fetch:
                self.origin().fetch(branch)
            self.require_branches_equal(branch, remote_branch)

    @requires_repo
    def _compare_branches(self, branch1, branch2):
        """
        Tests whether branches and their 'origin' counterparts have
        diverged and need merging first. It returns error codes to
        provide more detail, like so:

        0    Branch heads point to the same commit
        1    First given branch needs fast-forwarding
        2    Second given branch needs fast-forwarding
        3    Branch needs a real merge
        4    There is no merge base, i.e. the branches have no common ancestors
        """
        try:
            commit1 = self.repo.rev_parse(branch1)
            commit2 = self.repo.rev_parse(branch2)
        except git.BadObject, e:
            raise NoSuchBranchError(e.args[0])
        if commit1 == commit2:
            return 0
        try:
            base = self.repo.git.merge_base(commit1, commit2)
        except GitCommandError:
            return 4
        if base == commit1:
            return 1
        elif base == commit2:
            return 2
        else:
            return 3
예제 #17
0
파일: commit.py 프로젝트: marco-c/pydriller
 def _get_branches(self):
     c_git = Git(str(self._conf.get('path_to_repo')))
     branches = set()
     for branch in set(c_git.branch('--contains', self.hash).split('\n')):
         branches.add(branch.strip().replace('* ', ''))
     return branches
예제 #18
0
    repos = [
        repo for repo in get_public_repos(args.username, fork)
        if fork or not repo['fork']
    ]

    # TODO: Add failsafe mechanism to print partial emails
    for repo in repos:
        printv('Cloning {}...'.format(repo['name']))

        repo_authors = {}
        Git().clone(repo['ssh_url'] if fix else repo['clone_url'])

        g = Git(repo['repo_path'])

        # Hacky way of getting remote branches
        branches = g.branch('-r').split('\n')

        # Remove references e.g. "origin/HEAD -> origin/master"
        branches = filter(filter_branch, branches)

        # Strip empty spaces
        branches = [branch.strip() for branch in branches]

        if fix:
            chdir(repo['repo_path'])
            printv(getcwd())

        # Get emails from each branch
        for branch in branches:
            system('git branch --track "{btrim}" "{b}" >/dev/null 2>/dev/null'.
                   format(btrim=branch.replace('origin/', ''), b=branch))
예제 #19
0
파일: core.py 프로젝트: sananthula/gitflow
class GitFlow(object):
    """
    Creates a :class:`GitFlow` instance.

    :param working_dir:
        The directory where the Git repo is located.  If not specified, the
        current working directory is used.

    When a :class:`GitFlow` class is instantiated, it auto-discovers all
    subclasses of :class:`gitflow.branches.BranchManager`, so there is no
    explicit registration required.
    """
    def _discover_branch_managers(self):
        managers = {}
        for cls in itersubclasses(BranchManager):
            # TODO: Initialize managers with the gitflow branch prefixes
            managers[cls.identifier] = cls(self)
        return managers

    def __init__(self, working_dir='.'):
        # Allow Repos to be passed in instead of strings
        self.repo = None
        if isinstance(working_dir, Repo):
            self.working_dir = working_dir.working_dir
        else:
            self.working_dir = working_dir

        self.git = Git(self.working_dir)
        try:
            self.repo = Repo(self.working_dir)
        except InvalidGitRepositoryError:
            pass

        self.managers = self._discover_branch_managers()
        self.defaults = {
            'gitflow.branch.master': 'master',
            'gitflow.branch.develop': 'develop',
            'gitflow.prefix.versiontag': '',
            'gitflow.origin': 'origin',
        }
        for identifier, manager in self.managers.items():
            self.defaults['gitflow.prefix.%s' %
                          identifier] = manager.DEFAULT_PREFIX

    def _init_config(self,
                     master=None,
                     develop=None,
                     prefixes={},
                     names={},
                     force_defaults=False):
        for setting, default in self.defaults.items():
            if force_defaults:
                value = default
            elif setting == 'gitflow.branch.master':
                value = master
            elif setting == 'gitflow.branch.develop':
                value = develop
            elif setting.startswith('gitflow.prefix.'):
                name = setting[len('gitflow.prefix.'):]
                value = prefixes.get(name, None)
            else:
                name = setting[len('gitflow.'):]
                value = names.get(name, None)
            if value is None:
                value = self.get(setting, default)
            self.set(setting, value)

    def _init_initial_commit(self):
        master = self.master_name()
        if master in self.repo.branches:
            # local `master` branch exists
            return
        elif self.origin_name(master) in self.repo.refs:
            # the origin branch counterpart exists
            origin = self.repo.refs[self.origin_name(master)]
            branch = self.repo.create_head(master, origin)
            branch.set_tracking_branch(origin)
        elif self.repo.heads:
            raise NotImplementedError(
                'Local and remote branches exist, but neither %s nor %s' %
                (master, self.origin_name(master)))
        else:
            # Create 'master' branch
            info('Creating branch %r' % master)
            c = self.repo.index.commit('Initial commit', head=False)
            self.repo.create_head(master, c)

    def _init_develop_branch(self):
        # assert master already exists
        assert self.master_name() in self.repo.refs
        develop = self.develop_name()
        if develop in self.repo.branches:
            # local `develop` branch exists, but do not switch there
            return
        if self.origin_name(develop) in self.repo.refs:
            # the origin branch counterpart exists
            origin = self.repo.refs[self.origin_name(develop)]
            branch = self.repo.create_head(develop, origin)
            branch.set_tracking_branch(origin)
        else:
            # Create 'develop' branch
            info('Creating branch %r' % develop)
            branch = self.repo.create_head(develop, self.master())
        # switch to develop branch if its newly created
        info('Switching to branch %s' % branch)
        branch.checkout()

    def _enforce_git_repo(self):
        """
        Ensure a (maybe empty) repository exists we can work on.

        This is to be used by the `init` sub-command.
        """
        if self.repo is None:
            self.git.init(self.working_dir)
            self.repo = Repo(self.working_dir)

    def init(self,
             master=None,
             develop=None,
             prefixes={},
             names={},
             force_defaults=False):
        self._enforce_git_repo()
        self._init_config(master, develop, prefixes, names, force_defaults)
        self._init_initial_commit()
        self._init_develop_branch()
        return self

    def is_initialized(self):
        return (self.repo and self.is_set('gitflow.branch.master')
                and self.is_set('gitflow.branch.develop')
                and self.is_set('gitflow.prefix.feature')
                and self.is_set('gitflow.prefix.release')
                and self.is_set('gitflow.prefix.hotfix')
                and self.is_set('gitflow.prefix.support')
                and self.is_set('gitflow.prefix.versiontag'))

    def _parse_setting(self, setting):
        groups = setting.split('.', 2)
        if len(groups) == 2:
            section, option = groups
        elif len(groups) == 3:
            section, subsection, option = groups
            section = '%s "%s"' % (section, subsection)
        else:
            raise ValueError('Invalid setting name: %s' % setting)
        return (section, option)

    @requires_repo
    def get(self, setting, default=_NONE):
        section, option = self._parse_setting(setting)
        try:
            return self.repo.config_reader().get_value(section, option)
        except (NoSectionError, NoOptionError):
            if default is not _NONE:
                return default
            raise

    def get_prefix(self, identifier):
        return self._safe_get('gitflow.prefix.%s' % (identifier, ))

    @requires_repo
    def set(self, setting, value):
        section, option = self._parse_setting(setting)
        writer = self.repo.config_writer()
        writer.set_value(section, option, value)
        writer.release()
        del writer

    def is_set(self, setting):
        return self.get(setting, None) is not None

    @requires_repo
    def _safe_get(self, setting_name):
        try:
            return self.get(setting_name)
        except (NoSectionError, NoOptionError):
            raise NotInitialized('This repo has not yet been initialized.')

    def master_name(self):
        return self._safe_get('gitflow.branch.master')

    def develop_name(self):
        return self._safe_get('gitflow.branch.develop')

    def origin_name(self, name=None):
        origin = self.get('gitflow.origin', self.defaults['gitflow.origin'])
        if name is not None:
            return origin + '/' + name
        else:
            return origin

    @requires_repo
    def require_remote(self, name):
        try:
            return self.repo.remotes[name]
        except IndexError:
            raise NoSuchRemoteError(name)

    def origin(self):
        return self.require_remote(self.origin_name())

    @requires_repo
    def develop(self):
        return self.repo.branches[self.develop_name()]

    @requires_repo
    def master(self):
        return self.repo.branches[self.master_name()]

    @requires_repo
    def branch_names(self, remote=False):
        if remote:
            return [
                r.name for r in self.repo.refs
                if isinstance(r, RemoteReference)
            ]
        else:
            return [r.name for r in self.repo.branches]

    @requires_repo
    def nameprefix_or_current(self, identifier, prefix):
        """
        :param identifier:
            The identifier for the type of branch to work on.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param prefix: If the empty, see if the current branch is of
            type `identifier`. If so, returns the current branches
            short name, otherwise raises :exc:`NoSuchBranchError`.

        :returns:
            If exactly one branch of type `identifier` starts with the
            given name `prefix`, returns that branches short name.
            Raises :exc:`NoSuchBranchError` in case no branch exists
            with the given prefix, or :exc:`PrefixNotUniqueError` in
            case multiple matches are found.
        """
        repo = self.repo
        manager = self.managers[identifier]
        if not prefix:
            if repo.active_branch.name.startswith(manager.prefix):
                return manager.shorten(repo.active_branch.name)
            else:
                raise NoSuchBranchError('The current branch is no %s branch. '
                                        'Please specify one explicitly.' %
                                        identifier)
        return manager.shorten(manager.by_name_prefix(prefix).name)

    @requires_repo
    def name_or_current(self, identifier, name, must_exist=True):
        """
        :param identifier:
            The identifier for the type of branch to work on.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param  name:
           If the `name` is empty, see if the current branch is of
           type `identifier`. If so, returns the current branches
           short name, otherwise raises :exc:`NoSuchBranchError`.

        :param must_exist: If `True` (the default), raises
            :exc:`NoSuchBranchError` in case no branch exists with the
            given `name`.

        Otherwise return the `name` unchanged.
        """
        repo = self.repo
        manager = self.managers[identifier]
        if not name:
            if repo.active_branch.name.startswith(manager.prefix):
                return manager.shorten(repo.active_branch.name)
            else:
                raise NoSuchBranchError('The current branch is no %s branch. '
                                        'Please specify one explicitly.' %
                                        identifier)
        elif must_exist and not manager.full_name(name) in (
                b.name for b in manager.list()):
            raise NoSuchBranchError('There is no %s branch named %s.' %
                                    (identifier, name))
        return name

    @requires_repo
    def status(self):
        result = []
        for b in self.repo.branches:
            tup = self.branch_info(b.name)
            result.append(tup)
        return result

    @requires_repo
    def branch_info(self, name):
        active_branch = self.repo.active_branch
        b = self.repo.heads[name]
        return (name, b.commit.hexsha, b == active_branch)

    @requires_repo
    def is_dirty(self):
        """
        Returns whether or not the current working directory contains
        uncommitted changes.
        """
        return self.repo.is_dirty()

    @requires_repo
    def has_staged_commits(self):
        """
        Returns whether or not the current repo contains local changes
        checked into the index but not committed.
        """
        return len(self.repo.index.diff(self.repo.head.commit)) > 0

    @requires_repo
    def require_no_merge_conflict(self):
        """
        Raises :exc:`MergeConflict` if the current working directory
        contains a merge conflict.
        """
        try:
            git.Reference(self.repo, 'MERGE_HEAD', check_path=False).commit
            # reference exists, so there is a merge conflict
            raise MergeConflict()
        except ValueError:
            # no such reference, so there is no merge conflict
            pass

    def is_merged_into(self, commit, target_branch):
        """
        Checks whether `commit` is successfully merged into branch
        `target_branch`.

        :param commit:
            The commit or branch that ought to be merged. This may be
            a full branch-name, a commit-hexsha or any of branch-,
            head-, reference- or commit-object.

        :param target_branch:
            The branch which should contain the commit. This may be a
            full branch-name, or any of branch-, head- or
            reference-object.
        """
        try:
            commit = self.repo.rev_parse(str(commit))
        except (git.BadObject, git.BadName):
            raise BadObjectError(commit)
        if isinstance(target_branch, git.RemoteReference):
            target_branch = 'remotes/' + target_branch.name
        elif isinstance(target_branch, git.SymbolicReference):
            target_branch = target_branch.name
        # :todo: implement this more efficiently
        return target_branch in [
            b.lstrip('* ')
            for b in self.git.branch('-a', '--contains', commit).splitlines()
        ]

    def must_be_uptodate(self, branch, fetch):
        remote_branch = self.origin_name(branch)
        if remote_branch in self.branch_names(remote=True):
            if fetch:
                self.origin().fetch(branch)
            self.require_branches_equal(branch, remote_branch)

    @requires_repo
    def _compare_branches(self, branch1, branch2):
        """
        Tests whether branches and their 'origin' counterparts have
        diverged and need merging first. It returns error codes to
        provide more detail, like so:

        0    Branch heads point to the same commit
        1    First given branch needs fast-forwarding
        2    Second given branch needs fast-forwarding
        3    Branch needs a real merge
        4    There is no merge base, i.e. the branches have no common ancestors
        """
        try:
            commit1 = self.repo.rev_parse(branch1)
            commit2 = self.repo.rev_parse(branch2)
        except (git.BadObject, git.BadName) as e:
            raise NoSuchBranchError(e.args[0])
        if commit1 == commit2:
            return 0
        try:
            # merge_base() returns a list of Commit objects
            # this list will have at max one Commit
            # or it will be empty if no common merge base exists
            base = self.repo.merge_base(commit1, commit2)[0]
        except (GitCommandError, IndexError):
            return 4
        if base == commit1:
            return 1
        elif base == commit2:
            return 2
        else:
            return 3

    @requires_repo
    def require_branches_equal(self, branch1, branch2):
        status = self._compare_branches(branch1, branch2)
        if status == 0:
            # branches are equal
            return
        else:
            warn("Branches '%s' and '%s' have diverged." % (branch1, branch2))
            if status == 1:
                raise SystemExit("And branch '%s' may be fast-forwarded." %
                                 branch1)
            elif status == 2:
                # Warn here, since there is no harm in being ahead
                warn("And local branch '%s' is ahead of '%s'." %
                     (branch1, branch2))
            else:
                raise SystemExit("Branches need merging first.")

    @requires_repo
    def start_transaction(self, message=None):
        if message:
            info(message)

    @requires_initialized
    def tag(self, tagname, commit, message=None, sign=False, signingkey=None):
        kwargs = {}
        if sign:
            kwargs['s'] = True
        if signingkey:
            kwargs['u'] = signingkey
        self.repo.create_tag(tagname,
                             commit,
                             message=message or None,
                             **kwargs)

    #
    # ====== sub commands =====
    #

    @requires_repo
    def list(self, identifier, arg0_name, verbose, use_tagname):
        """
        List the all branches of the given type. If there are not
        branches of this type, raises :exc:`Usage` with an
        explanation on how to start a branch of this type.

        :param identifier:
            The identifier for the type of branch to work on.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param arg0_name:
            Name of the first argument for the command line to be put
            into the explanation on how to start a branch of this
            type. This typically is `name` or `version`.

        :param verbose:
            If True, give more information about the state of the
            branch: Whether it's ahead or behind it's default base,
            may be rebased, etc.

        :param use_tagname:
            If True, try to describe the state based on the next tag.
        """
        repo = self.repo
        manager = self.managers[identifier]
        branches = manager.list()
        if not branches:
            raise Usage(
                'No %s branches exist.' % identifier,
                'You can start a new %s branch with the command:' % identifier,
                '    git flow %s start <%s> [<base>]' %
                (identifier, arg0_name))

        # determine the longest branch name
        width = max(len(b.name) for b in branches) - len(manager.prefix) + 1

        basebranch_sha = repo.branches[manager.default_base()].commit.hexsha

        for branch in branches:
            if repo.active_branch == branch:
                prefix = '* '
            else:
                prefix = '  '

            name = manager.shorten(branch.name)
            extra_info = ''

            if verbose:
                name = name.ljust(width)
                branch_sha = branch.commit.hexsha
                base_sha = repo.git.merge_base(basebranch_sha, branch_sha)
                if branch_sha == basebranch_sha:
                    extra_info = '(no commits yet)'
                elif use_tagname:
                    try:
                        extra_info = self.git.name_rev('--tags', '--name-only',
                                                       '--no-undefined',
                                                       base_sha)
                        extra_info = '(based on %s)' % extra_info
                    except GitCommandError:
                        pass
                if not extra_info:
                    if base_sha == branch_sha:
                        extra_info = '(is behind %s, may ff)' % manager.default_base(
                        )
                    elif base_sha == basebranch_sha:
                        extra_info = '(based on latest %s)' % manager.default_base(
                        )
                    else:
                        extra_info = '(may be rebased)'

            info(prefix + name + extra_info)

    @requires_initialized
    def create(self, identifier, name, base, fetch):
        """
        Creates a branch of the given type, with the given short name.

        :param identifier:
            The identifier for the type of branch to create.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param name:
            The friendly (short) name to create.

        :param base:
            The alternative base to branch off from.  If not given, the default
            base for the given branch type is used.

        :returns:
            The newly created :class:`git.refs.Head` branch.
        """
        return self.managers[identifier].create(name, base, fetch=fetch)

    @requires_initialized
    def finish(self, identifier, name, fetch, rebase, keep, force_delete,
               tagging_info):
        """
        Finishes a branch of the given type, with the given short name.

        :param identifier:
            The identifier for the type of branch to finish.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param name:
            The friendly (short) name to finish.
        """
        mgr = self.managers[identifier]
        branch = mgr.by_name_prefix(name)
        try:
            self.require_no_merge_conflict()
        except MergeConflict as e:
            raise Usage(
                e, "You can then complete the finish by running it again:",
                "    git flow %s finish %s" % (identifier, name))
        return mgr.finish(mgr.shorten(branch.name),
                          fetch=fetch,
                          rebase=rebase,
                          keep=keep,
                          force_delete=force_delete,
                          tagging_info=tagging_info)

    @requires_initialized
    def checkout(self, identifier, name):
        """
        Checkout a branch of the given type, with the given short name.

        :param identifier:
            The identifier for the type of branch to checkout.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param name:
            The friendly (short) name to checkout.

        :returns:
            The checked out :class:`git.refs.Head` branch.
        """
        mgr = self.managers[identifier]
        branch = mgr.by_name_prefix(name)
        return branch.checkout()

    @requires_initialized
    def diff(self, identifier, name):
        """
        Print the diff of changes since this branch branched off.

        :param identifier:
            The identifier for the type of branch to work on.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param name:
            The friendly (short) name to work on.
        """
        mgr = self.managers[identifier]
        full_name = mgr.full_name(name)
        base = self.git.merge_base(mgr.default_base(), full_name)
        print(self.git.diff('%s..%s' % (base, full_name)))

    @requires_initialized
    def rebase(self, identifier, name, interactive):
        """
        Rebase a branch of the given type, with the given short name,
        on top of it's default base.

        :param identifier:
            The identifier for the type of branch to rebase.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param name:
            The friendly (short) name to rebase.

        :param interactive:
            If True, do an interactive rebase.
        """
        warn("Will try to rebase %s branch '%s' ..." % (identifier, name))
        mgr = self.managers[identifier]
        mgr.full_name(name)
        # :todo: require_clean_working_tree
        self.checkout(identifier, name)
        args = []
        if interactive:
            args.append('-i')
        args.append(mgr.default_base())
        self.git.rebase(*args)

    @requires_initialized
    def publish(self, identifier, name):
        """
        Publish a branch of the given type, with the given short name,
        to `origin` (or whatever is configured as `remote` for gitflow.)

        :param identifier:
            The identifier for the type of branch to publish.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param name:
            The friendly (short) name to publish.

        :returns:
            The full name of the published branch.
        """
        repo = self.repo
        mgr = self.managers[identifier]

        # sanity checks
        # :todo: require_clean_working_tree
        full_name = mgr.full_name(name)
        remote_name = self.origin_name(full_name)
        if full_name not in repo.branches:
            raise NoSuchBranchError(full_name)
        if remote_name in repo.refs:
            raise BranchExistsError(remote_name)
        # :todo: check if full_name already has a tracking branch
        # :todo: check if full_name already has the same tracking branch

        # create remote branch
        origin = self.origin()
        info = origin.push('%s:refs/heads/%s' % (full_name, full_name))[0]
        origin.fetch()
        # configure remote tracking
        repo.branches[full_name].set_tracking_branch(info.remote_ref)
        return full_name

    @requires_initialized
    def pull(self, identifier, remote, name):
        """
        Pull a branch of the given type, with the given short name,
        from the given remote peer.

        :param identifier:
            The identifier for the type of branch to pull.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param remote:
            The remote to pull from. This must have been configured by
            `git remote add ...`.

        :param name:
            The friendly (short) name to pull.
        """
        def avoid_accidental_cross_branch_action(branch_name):
            current_branch = repo.active_branch
            if branch_name != current_branch.name:
                warn("Trying to pull from '%s' while currently on branch '%s'."
                     % (branch_name, current_branch))
                raise SystemExit(
                    "To avoid unintended merges, git-flow aborted.")

        repo = self.repo
        mgr = self.managers[identifier]
        full_name = mgr.full_name(name)
        # To avoid accidentally merging different feature branches
        # into each other, die if the current feature branch differs
        # from the requested $NAME argument.
        if repo.active_branch.name.startswith(self.get_prefix(identifier)):
            # We are on a local `identifier` branch already, so `full_name`
            # must be equal to the current branch.
            avoid_accidental_cross_branch_action(full_name)
        # :todo: require_clean_working_tree
        if full_name in self.repo.branches:
            # Again, avoid accidental merges
            avoid_accidental_cross_branch_action(full_name)
            # We already have a local branch called like this, so
            # simply pull the remote changes in
            self.require_remote(remote).pull(full_name)
            # :fixme: why is the branch not checked out here?
            info("Pulled %s's changes into %s." % (remote, full_name))
        else:
            # Setup the non-tracking local branch clone for the first time
            self.require_remote(remote).fetch(full_name + ':' + full_name)
            repo.heads[full_name].checkout()
            info("Created local branch %s based on %s's %s." %
                 (full_name, remote, full_name))

    @requires_initialized
    def track(self, identifier, name):
        """
        Track a branch of the given type, with the given short name,
        from `origin` (or whatever is configured as `remote` for
        gitflow.)

        :param identifier:
            The identifier for the type of branch to track.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param name:
            The friendly (short) name to track.

        :param base:
            The alternative base to branch off from.  If not given, the default
            base for the given branch type is used.

        :returns:
            The newly created :class:`git.refs.Head` branch.
        """
        repo = self.repo
        mgr = self.managers[identifier]
        # sanity checks
        # :todo: require_clean_working_tree
        full_name = mgr.full_name(name)
        if full_name in repo.branches:
            raise BranchExistsError(full_name)
        self.origin().fetch(full_name)
        remote_branch = self.origin().refs[full_name]
        branch = repo.create_head(full_name, remote_branch)
        branch.set_tracking_branch(remote_branch)
        return branch.checkout()
예제 #20
0
class GitFlow(object):
    """
    Creates a :class:`GitFlow` instance.

    :param working_dir:
        The directory where the Git repo is located.  If not specified, the
        current working directory is used.

    When a :class:`GitFlow` class is instantiated, it auto-discovers all
    subclasses of :class:`gitflow.branches.BranchManager`, so there is no
    explicit registration required.
    """

    def _discover_branch_managers(self):
        managers = {}
        for cls in itersubclasses(BranchManager):
            # TODO: Initialize managers with the gitflow branch prefixes
            managers[cls.identifier] = cls(self)
        return managers

    def __init__(self, working_dir='.'):
        # Allow Repos to be passed in instead of strings
        self.repo = None
        if isinstance(working_dir, Repo):
            self.working_dir = working_dir.working_dir
        else:
            self.working_dir = working_dir

        self.git = Git(self.working_dir)
        try:
            self.repo = Repo(self.working_dir)
        except InvalidGitRepositoryError:
            pass

        self.managers = self._discover_branch_managers()
        self.defaults = {
            'gitflow.branch.master': 'master',
            'gitflow.branch.develop': 'develop',
            'gitflow.prefix.versiontag': '',
            'gitflow.origin': 'origin',
            'gitflow.release.versionmatcher': '[0-9]+([.][0-9]+){2}',
            'gitflow.circleci.enabled': 'y',
            'gitflow.pagination': '10',
            }
        for identifier, manager in self.managers.items():
            self.defaults['gitflow.prefix.%s' % identifier] = manager.DEFAULT_PREFIX


    def _init_config(self, master=None, develop=None, prefixes={}, names={},
                     force_defaults=False):
        for setting, default in self.defaults.items():
            if force_defaults:
                value = default
            elif setting == 'gitflow.branch.master':
                value = master
            elif setting == 'gitflow.branch.develop':
                value = develop
            elif setting.startswith('gitflow.prefix.'):
                name = setting[len('gitflow.prefix.'):]
                value = prefixes.get(name, None)
            else:
                name = setting[len('gitflow.'):]
                value = names.get(name, None)
            if value is None:
                value = self.get(setting, default)
            self.set(setting, value)

    def _init_initial_commit(self):
        master = self.master_name()
        if master in self.repo.branches:
            # local `master` branch exists
            return
        elif self.origin_name(master) in self.repo.refs:
            # the origin branch counterpart exists
            origin = self.repo.refs[self.origin_name(master)]
            branch = self.repo.create_head(master, origin)
            branch.set_tracking_branch(origin)
        elif self.repo.heads:
            raise NotImplementedError('Local and remote branches exist, '
                                     'but neither %s nor %s'
                                     % (master, self.origin_name(master) ))
        else:
            # Create 'master' branch
            info('Creating branch %r' % master)
            c = self.repo.index.commit('Initial commit', head=False)
            self.repo.create_head(master, c)

    def _init_develop_branch(self):
        # assert master already exists
        assert self.master_name() in self.repo.refs
        develop = self.develop_name()
        if develop in self.repo.branches:
            # local `develop` branch exists, but do not switch there
            return
        if self.origin_name(develop) in self.repo.refs:
            # the origin branch counterpart exists
            origin = self.repo.refs[self.origin_name(develop)]
            branch = self.repo.create_head(develop, origin)
            branch.set_tracking_branch(origin)
        else:
            # Create 'develop' branch
            info('Creating branch %r' % develop)
            branch = self.repo.create_head(develop, self.master())
        # switch to develop branch if its newly created
        info('Switching to branch %s' % branch)
        branch.checkout()

    def _ensure_remote_develop_branch(self):
        # push the develop branch if it does not exist in origin
        remote_develop = self.origin_name(self.develop_name())
        if not remote_develop in self.branch_names(remote=True):
            develop_name = self.develop_name()
            sys.stdout.write('Branch {0} not found in origin, pushing it now ... '.format(develop_name))
            self.origin().push([develop_name])
            print('OK')

    def _enforce_git_repo(self):
        """
        Ensure a (maybe empty) repository exists we can work on.

        This is to be used by the `init` sub-command.
        """
        if self.repo is None:
            self.git.init(self.working_dir)
            self.repo = Repo(self.working_dir)


    def _enforce_services(self):
        err = False
        for option in ('gitflow.pt.token',
                       'reviewboard.url',
                       'reviewboard.server'):
            try:
                value = self.get(option)
                if option == 'reviewboard.url' and not value.endswith('/'):
                    err = True
                    print """
Git config key 'reviewboard.url' must contain a trailing slash.
Update your configuration by executing

    $ git config [--global] reviewboard.url %s
""" % (value + '/')
            except Exception:
                try:
                    if option == 'gitflow.pt.token':
                        value = self.get('workflow.token')
                        self.set('gitflow.pt.token', value)
                        continue
                except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
                    pass
                err = True
                print """
Git config '%s' missing, please fill it in by executing

    $ git config [--global] %s <value>
""" % (option, option)

        if err:
            raise SystemExit('Operation canceled.')


    def init(self, master=None, develop=None, prefixes={}, names={},
             force_defaults=False):
        self._enforce_git_repo()
        self._init_config(master, develop, prefixes, names, force_defaults)
        self._init_initial_commit()
        self._init_develop_branch()
        self._ensure_remote_develop_branch()
        return self

    def is_initialized(self):
        return (self.repo and
                self.is_set('gitflow.branch.master') and
                self.is_set('gitflow.branch.develop') and
                self.is_set('gitflow.prefix.feature') and
                self.is_set('gitflow.prefix.release') and
                self.is_set('gitflow.prefix.hotfix') and
                self.is_set('gitflow.prefix.support') and
                self.is_set('gitflow.prefix.versiontag') and
                self.is_set('gitflow.release.versionmatcher') and
                self.is_set('gitflow.circleci.enabled') and
                self.is_set('gitflow.pagination'))

    def _parse_setting(self, setting):
        groups = setting.split('.', 2)
        if len(groups) == 2:
            section, option = groups
        elif len(groups) == 3:
            section, subsection, option = groups
            section = '%s "%s"' % (section, subsection)
        else:
            raise ValueError('Invalid setting name: %s' % setting)
        return (section, option)

    @requires_repo
    def get(self, setting, default=_NONE):
        section, option = self._parse_setting(setting)
        try:
            value = self.repo.config_reader().get_value(section, option)
            # git config value cannot contain '-', so it is encoded as
            # MAGIC_STRING and we have to replace it with '-' when we read a
            # value.
            if isinstance(value, basestring):
                value = value.replace(MAGIC_STRING, '-')
            return value
        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
            if default is not _NONE:
                return default
            raise

    def get_prefix(self, identifier):
        return self._safe_get('gitflow.prefix.%s' % (identifier,))

    @requires_repo
    def set(self, setting, value):
        # git config value cannot contain '-', so we have to encode it.
        if isinstance(value, basestring):
            value = value.replace('-', MAGIC_STRING)
        section, option = self._parse_setting(setting)
        self.repo.config_writer().set_value(section, option, value)

    def is_set(self, setting):
        return self.get(setting, None) is not None

    @requires_repo
    def delete(self, setting):
        section, option = self._parse_setting(setting)
        self.repo.config_writer().remove(section, option)

    @requires_repo
    def _safe_get(self, setting_name):
        try:
            return self.get(setting_name)
        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
            raise NotInitialized('This repo has not yet been initialized.')

    def master_name(self):
        return self._safe_get('gitflow.branch.master')

    def develop_name(self):
        return self._safe_get('gitflow.branch.develop')

    def client_name(self):
        return 'client'

    def client_exists(self):
        client = self.client_name()
        return client in self.branch_names() or \
               self.origin_name(client) in self.branch_names(remote=True)

    def origin_name(self, name=None):
        origin = self.get('gitflow.origin', self.defaults['gitflow.origin'])
        if name is not None:
            return origin + '/' + name
        else:
            return origin

    @requires_repo
    def current_branch(self):
        return self.repo.head.ref.name

    @requires_initialized
    def is_circleci_enabled(self):
        return self.get('gitflow.circleci.enabled').lower() != 'n'

    @requires_repo
    def require_remote(self, name):
        try:
            remote = self.repo.remotes[name]
        except IndexError:
            raise NoSuchRemoteError("Remote '{0}' was not found.".format(name))

        # Monkey patch remote.fetch to fix a false negative assertion in GitPython.
        class RemoteWrapper(object):
            def __init__(self, remote):
                self._remote = remote

            def __str__(self):
                return self._remote.__str__()

            def __repr__(self):
                return self._remote.__repr__()

            def __eq__(self, other):
                return self._remote.__eq__(other)

            def __ne__(self, other):
                return self._remote.__ne__(other)

            def __hash__(self):
                return self._remote.__hash__()

            # For some reason __getattribute__ is making infinite recursion,
            # so let's use __getattr__.
            def __getattr__(self, name):
                return self._remote.__getattribute__(name)

            # This is the main monkey patching part.
            def fetch(self, refspec=None, progress=None, **kwargs):
                # Let's try 3 times...
                err = None
                for x in range(3):
                    try:
                        return self._remote.fetch(refspec=refspec,
                                progress=progress, **kwargs)
                    except AssertionError as e:
                        err = e
                        # If we are in '_get_fetch_info_from_stderr',
                        # it's the broken assertion and we skip it.
                        func_name = traceback.extract_stack()[-1][2]
                        if func_name != '_get_fetch_info_from_stderr':
                            raise e
                        err = None
                # If we somehow get the same exception 3 times, just raise anyway.
                raise err

        return RemoteWrapper(remote)

    @requires_repo
    def require_origin_branch(self, branch_name):
        origin_name = self.origin_name(branch_name)
        refs = [r
                for r in self.repo.refs
                if isinstance(r, RemoteReference) and r.name == origin_name]
        if len(refs) != 0:
            assert len(refs) == 1
            return refs[0]
        raise NoSuchBranchError(
                "Remote branch '{0}' was not found".format(origin_name))

    def origin(self):
        return self.require_remote(self.origin_name())

    @requires_repo
    def develop(self):
        return self.repo.branches[self.develop_name()]

    @requires_repo
    def master(self):
        return self.repo.branches[self.master_name()]

    @requires_repo
    def branch_names(self, remote=False):
        if remote:
            return [r.name
                    for r in self.repo.refs
                    if isinstance(r, RemoteReference)]
        else:
            return [r.name for r in self.repo.branches]

    @requires_repo
    def nameprefix_or_current(self, identifier, prefix):
        """
        :param identifier:
            The identifier for the type of branch to create.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param prefix: If the empty, see if the current branch is of
            type `identifier`. If so, returns the current branches
            short name, otherwise raises :exc:`NoSuchBranchError`.

        If exactly one branch of type `identifier` starts with
        the given name `prefix`, returns that branches short name.
        Raises :exc:`NoSuchBranchError` in case no branch exists with
        the given prefix, or :exc:`PrefixNotUniqueError` in case
        multiple matches are found.
        """
        repo = self.repo
        manager = self.managers[identifier]
        if not prefix:
            if repo.active_branch.name.startswith(manager.prefix):
                return manager.shorten(repo.active_branch.name)
            else:
                raise NoSuchBranchError('The current branch is no %s branch. '
                    'Please specify one explicitly.' % identifier)
        branch = None
        try:
            branch = manager.by_name_prefix(prefix).name
        except NoSuchBranchError as ex:
            try:
                remote_branch = manager.by_name_prefix(prefix, remote=True).name
                branch = str(remote_branch)[len(self.origin_name())+1:]
                self.git.branch(branch, remote_branch)
            except NoSuchBranchError:
                raise ex
        return manager.shorten(branch)

    @requires_repo
    def name_or_current(self, identifier, name, must_exist=True):
        """
        :param identifier:
            The identifier for the type of branch to create.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param  name:
           If the `name` is empty, see if the current branch is of
           type `identifier`. If so, returns the current branches
           short name, otherwise raises :exc:`NoSuchBranchError`.

        :param must_exist: If `True` (the default), raises
            :exc:`NoSuchBranchError` in case no branch exists with the
            given `name`.

        Otherwise return the `name` unchanged.
        """
        repo = self.repo
        manager = self.managers[identifier]
        if not name:
            if repo.active_branch.name.startswith(manager.prefix):
                return manager.shorten(repo.active_branch.name)
            else:
                raise NoSuchBranchError('The current branch is no %s branch. '
                    'Please specify one explicitly.' % identifier)
        elif must_exist and not manager.full_name(name) in (b.name for b in manager.list()):
            raise NoSuchBranchError('There is no %s branch named %s.'
                                    % (identifier, name))
        return name


    @requires_repo
    def status(self):
        result = []
        for b in self.repo.branches:
            tup = self.branch_info(b.name)
            result.append(tup)
        return result

    @requires_repo
    def branch_info(self, name):
        active_branch = self.repo.active_branch
        b = self.repo.heads[name]
        return (name, b.commit.hexsha, b == active_branch)

    @requires_repo
    def is_dirty(self):
        """
        Returns whether or not the current working directory contains
        uncommitted changes.
        """
        return self.repo.is_dirty()

    @requires_repo
    def has_staged_commits(self):
        """
        Returns whether or not the current repo contains local changes
        checked into the index but not committed.
        """
        return len(self.repo.index.diff(self.repo.head.commit)) > 0


    @requires_repo
    def require_no_merge_conflict(self):
        """
        Raises :exc:`MergeConflict` if the current working directory
        contains a merge conflict.
        """
        try:
            git.Reference(self.repo, 'MERGE_HEAD', check_path=False).commit
            # reference exists, so there is a merge conflict
            raise MergeConflict()
        except ValueError:
            # no such reference, so there is no merge conflict
            pass


    def is_merged_into(self, commit, target_branch):
        """
        Checks whether `commit` is successfully merged into branch
        `target_branch`.

        :param commit:
            The commit or branch that ought to be merged. This may be
            a full branch-name, a commit-hexsha or any of branch-,
            head-, reference- or commit-object.

        :param target_branch:
            The branch which should contain the commit. This may be a
            full branch-name, or any of branch-, head- or
            reference-object.
        """
        try:
            commit = self.repo.rev_parse(str(commit))
        except git.BadObject:
            raise BadObjectError(commit)
        if isinstance(target_branch, git.RemoteReference):
            target_branch = 'remotes/' + target_branch.name
        elif isinstance(target_branch, git.SymbolicReference):
            target_branch = target_branch.name
        # :todo: implement this more efficiently
        return target_branch in [
            b.lstrip('* ')
            for b in self.git.branch('-a', '--contains', commit).splitlines()]


    def must_be_uptodate(self, branch):
        remote_branch = self.origin_name(branch)
        if remote_branch in self.branch_names(remote=True):
            self.require_branches_equal(branch, remote_branch)

    @requires_repo
    def _compare_branches(self, branch1, branch2):
        """
        Tests whether branches and their 'origin' counterparts have
        diverged and need merging first. It returns error codes to
        provide more detail, like so:

        0    Branch heads point to the same commit
        1    First given branch needs fast-forwarding
        2    Second given branch needs fast-forwarding
        3    Branch needs a real merge
        4    There is no merge base, i.e. the branches have no common ancestors
        """
        try:
            commit1 = self.repo.rev_parse(branch1)
            commit2 = self.repo.rev_parse(branch2)
        except git.BadObject, e:
            raise NoSuchBranchError('Branch {0} not found'.format(e.args[0]))
        if commit1 == commit2:
            return 0
        try:
            base = self.repo.git.merge_base(commit1, commit2)
        except GitCommandError:
            return 4
        if base == commit1:
            return 1
        elif base == commit2:
            return 2
        else:
            return 3
예제 #21
0
def main():
    #make sure the directory is empty
    if not os.listdir(gitdirectory):
        repo = git.Repo.clone_from(giturl, gitdirectory, branch='master')

    else:
        repo = git.Repo(gitdirectory)
        repo2 = git.Repo(localdirectory)
    originalrepo = Git(gitdirectory)
    commits = list(repo.iter_commits("master", max_count=1000000))
    tree = repo.head.commit.tree

    print(originalrepo.branch())

    stay = True
    current_original_commit = 0

    print(
        "quit to quit, current to check current commit sha, < for past one, > for future one, number for type commit number you want, \"complete\" to go back to a commit and update commits until up to newest and newest feature \"100commit\""
    )
    iterate = 0
    iterate100 = 0
    iteratevalue = 0
    while stay:
        if iterate == 0 and iterate100 == 0:
            userinput = input("input: ")
        if userinput == "quit":
            stay = False
        elif userinput == "current":
            print(repo.head.commit)
            print(originalrepo.committed_date)
            print("commits in the past = " + str(current_original_commit))
            continue
        elif userinput == ">":
            if current_original_commit > 0:
                current_original_commit -= 1
            else:
                print(
                    "you tried to go out of range, this is the newest commit")
                continue
        elif userinput == "<":
            if current_original_commit < len(commits) - 1:
                current_original_commit += 1
            else:
                print(
                    "you tried to go out of range, this is the oldest commit")
                continue
        elif userinput.isdigit():
            if int(userinput) < len(commits) and int(userinput) >= 0:
                current_original_commit = int(userinput)
            else:
                print("you tried to go out of range, max range is: " +
                      str(len(commits)))
                continue
        elif userinput == "complete" or iterate == 1:
            if iterate == 0:
                firstcommitnumber = int(
                    input(
                        "How far back would you like to go in commits? Input: "
                    ))
                if firstcommitnumber > len(commits):
                    print(
                        "sorry, you have gone out of the scope of the project. There are "
                        + str(len(commits)) + " total commits")
                else:
                    start_time = time.time()
                    current_original_commit = firstcommitnumber - 1
                    iterate = 1
            if iterate == 1:
                if current_original_commit > 0:
                    current_original_commit -= 1
                else:
                    time_elapsed = time.time() - start_time
                    print(
                        "You have reached the final newest commit (shown below) in "
                        + str(time_elapsed))
                    iterate = 0
        elif userinput == "100commit" or iterate100 == 1:
            if iterate100 == 0:
                firstcommitnumber = int(
                    input(
                        "How far back would you like to go in commits? Input: "
                    ))
                if firstcommitnumber > len(commits):
                    print(
                        "sorry, you have gone out of the scope of the project. There are "
                        + str(len(commits)) + " total commits")
                else:
                    start_time = time.time()
                    current_original_commit = firstcommitnumber - 1
                    iterate100 = 1
            if iterate100 == 1:
                if current_original_commit > 0:
                    if iteratevalue < 100:
                        current_original_commit -= 1
                        iteratevalue += 1
                    else:
                        confirmation = input(
                            "type \"confirm\" to run the next 50 commits: ")
                        if confirmation == "confirm":
                            iteratevalue = 0
                            current_original_commit -= 1
                else:
                    time_elapsed = time.time() - start_time
                    print(
                        "You have reached the final newest commit (shown below) in "
                        + str(time_elapsed))
                    iterate100 = 0

        else:
            print("sorry, not recognised try again")
            continue
        originalrepo.checkout(commits[current_original_commit])

        dcmp = dircmp(localdirectory, gitdirectory)
        #print("files and folders added:")
        add_diff_files(dcmp)
        #print("files and folders removed:")
        delete_diff_files(dcmp)
        #print("files and folders replaced:")
        merge_diff_files(dcmp)
        #print("DIFFERENCES" + str(dcmp.left_only) + str(dcmp.right_only) +str(dcmp.diff_files))
        print("changes complete, starting commit number: " +
              str(current_original_commit) +
              " commit(s) from the newest commit. hash: " +
              str(commits[current_original_commit]))
        #try:
        repo2 = Repo(localdirectory)
        #repo2.git.push(force=True)
        #repo2.index.add('.')
        #repo2.git.add(update=True)
        repo2.git.add("-A")
        repo2.index.commit(str(current_original_commit))
        #repo2.git.commit('-m', 'test commit', author='*****@*****.**')
        origin = repo2.remote(name='origin')
        origin.push()
        print("commit successful, pushing")
예제 #22
0
class _Preferences(object):
    def __init__(self):
        """
        Preferences class to store and retrieve settings.
        """
        self.data = {}
        self.load_prefs()
        self.dir = os.path.abspath(
            os.path.join(os.path.dirname(__file__), '..', '..')) + '/'

        self.git = None
        self.repo = None
        try:
            self.git = Git(self.dir)
            self.repo = Repo(self.dir)
        except:
            pass

    def get_current_branch(self):
        """
        Retrieves the current git branch of the repository.

        :return:    current git branch.
        :rtype:     string
        """
        if self.git is None:
            return False
        try:
            return self.git.branch().split()[-1]
        except:
            print_error(
                "Failed to get current branch, is there a git repo setup?")
            return ""

    def get_remote_branches(self):
        """
        Retrieve all git branches of the repository.

        :return:    branches of the repository.
        :rtype:     list
        """
        if self.git is None:
            return False
        branches = []

        try:
            # Get all remote branches (not only local)
            returnvalue = self.git.ls_remote('--heads').split()

            # Strip data
            for i in range(len(returnvalue)):
                if i % 2 != 0:
                    # Get only branch name (last value)
                    branches.append(returnvalue[i].split("/")[-1])
        except:
            print_warning(
                "Failed to get remote branches, is there a git repo setup and do you have internet?"
            )
            pass

        return branches

    def check_if_update(self):
        """
        Checks git repository for available changes.

        :return:    True if update is available, False if the command failed or no update available.
        :rtype:     bool
        """
        if self.git is None:
            return False
        try:
            # Update local git data
            self.git.fetch()
        except:
            print_warning(
                "Failed to fetch, is there a git repo setup and do you have internet?"
            )
            return False
        # Retrieve git remote <-> local difference status
        status = self.git.status()
        # easy check to see if local is behind
        if status.find('behind') > 0:
            return True
        return False

    def update(self):
        """
        Creates a update file flag and restarts the service.
        """
        if self.git is None:
            return False
        # Create file to let deamon know it has to update before starting the server
        file = open(self.dir + 'update.var', 'w+')

        # restart service
        command = ['/usr/sbin/service', 'opsoro', 'restart']
        #shell=FALSE for sudo to work.
        subprocess.call(command, shell=False)

        python = sys.executable
        os.execl(python, python, *sys.argv)

        # # Reboot system used for user development server run
        # os.system('/sbin/shutdown -r now')

    def load_prefs(self):
        """
        Load preferences into data.
        """
        try:
            with open(get_path("config/preferences.yaml")) as f:
                self.data = yaml.load(f, Loader=Loader)
        except IOError:
            self.data = {}
            print_warning("Could not open config/preferences.yaml")

        print_info("Preferences loaded")

    def get(self, section, item, default):
        """
        Retrieve preference value.

        :param string section:  category in which the item is defined.
        :param string item:     item to retrieve.
        :param default:         default value to return if the value is not available.

        :return:    preference value
        """
        return self.data.get(section, {}).get(item, default)

    def set(self, section, item, value):
        """
        Set preference value.

        :param string section:  category in which the item is defined.
        :param string item:     item to set.
        :param value:           value to set.
        """
        if value is None:
            return
        try:
            self.data[section][item] = value
        except KeyError:
            self.data[section] = {item: value}

    def save_prefs(self):
        """
        Saves preferences to yaml file.
        """
        try:
            with open(get_path("config/preferences.yaml"), "w") as f:
                f.write(
                    yaml.dump(self.data,
                              default_flow_style=False,
                              Dumper=Dumper))
        except IOError:
            print_warning("Could not save config/preferences.yaml")

    def apply_prefs(self,
                    update_audio=False,
                    update_wireless=False,
                    restart_wireless=False,
                    update_dns=False):
        """
        Apply preferences to the system.

        :param bool update_audio:       True if audio settings have changed and needs to update.
        :param bool update_wireless:    True if wireless settings have changed and the wireless interface needs to update.
        :param bool restart_wireless:   True if wireless settings have changed and the wireless interface needs to restart.
        :param bool update_dns:         True if DNS settings have changed and needs to update.
        """
        def change_conf_setting(txt, name, val):
            pattern = "^%s([ \t]*)=([ \t]*).*$" % name
            new = "%s=%s" % (name, val)
            txt, subs = re.subn(pattern, new, txt, flags=re.MULTILINE)
            if subs == 0:
                if txt[-1:] != "\n":
                    txt = txt + "\n"
                txt = txt + new
            return txt

        FNULL = open(os.devnull, "w")

        if update_audio:
            vol = self.get("audio", "master_volume", 66)
            vol = constrain(vol, 0, 100)

            subprocess.Popen(["amixer", "sset", "'Master'",
                              "%d%%" % vol],
                             stdout=FNULL,
                             stderr=subprocess.STDOUT)

        if update_wireless:
            with open("/etc/hostapd/hostapd.conf", "r+") as f:
                lines = f.read()

                ssid = self.get("wireless", "ssid", "OPSORO-bot")
                password = self.get("wireless", "password", "opsoro123")
                channel = self.get("wireless", "channel", 6)
                channel = constrain(int(channel), 1, 11)

                lines = change_conf_setting(lines, "ssid", ssid)
                lines = change_conf_setting(lines, "wpa_passphrase", password)
                lines = change_conf_setting(lines, "channel", channel)

                f.seek(0)
                f.write(lines)
                f.truncate()

            if restart_wireless:
                subprocess.Popen(["service", "hostapd", "restart"],
                                 stdout=FNULL,
                                 stderr=subprocess.STDOUT)

        if update_dns:
            with open("/etc/dnsmasq.d/dnsmasq.opsoro.conf", "r+") as f:
                lines = f.read()

                # redirect = self.get("wireless", "ssid", "OPSORO-bot")
                # password = self.get("wireless", "password", "opsoro123")
                # channel = self.get("wireless", "channel", 6)
                # channel = constrain(int(channel), 1, 11)

                address = "/play.opsoro.be/192.168.42.1"
                dhcp = "interface:wlan0,192.168.42.100,192.168.42.200,infinite"

                lines = change_conf_setting(lines, "address", address)
                lines = change_conf_setting(lines, "dhcp-range", dhcp)

                f.seek(0)
                f.write(lines)
                f.truncate()
                subprocess.Popen(["service", "dnsmasq", "restart"],
                                 stdout=FNULL,
                                 stderr=subprocess.STDOUT)
예제 #23
0
파일: core.py 프로젝트: chassing/gitflow
class GitFlow(object):
    """
    Creates a :class:`GitFlow` instance.

    :param working_dir:
        The directory where the Git repo is located.  If not specified, the
        current working directory is used.

    When a :class:`GitFlow` class is instantiated, it auto-discovers all
    subclasses of :class:`gitflow.branches.BranchManager`, so there is no
    explicit registration required.
    """

    def _discover_branch_managers(self):
        managers = {}
        for cls in itersubclasses(BranchManager):
            # TODO: Initialize managers with the gitflow branch prefixes
            managers[cls.identifier] = cls(self)
        return managers

    def __init__(self, working_dir='.'):
        # Allow Repos to be passed in instead of strings
        self.repo = None
        if isinstance(working_dir, Repo):
            self.working_dir = working_dir.working_dir
        else:
            self.working_dir = working_dir

        self.git = Git(self.working_dir)
        try:
            self.repo = Repo(self.working_dir)
        except InvalidGitRepositoryError:
            pass

        self.managers = self._discover_branch_managers()
        self.defaults = {
            'gitflow.branch.master': 'master',
            'gitflow.branch.develop': 'develop',
            'gitflow.prefix.versiontag': '',
            'gitflow.origin': 'origin',
        }
        for identifier, manager in self.managers.items():
            self.defaults['gitflow.prefix.%s' % identifier] = manager.DEFAULT_PREFIX

    def _init_config(self, master=None, develop=None, prefixes={}, names={},
                     force_defaults=False):
        for setting, default in self.defaults.items():
            if force_defaults:
                value = default
            elif setting == 'gitflow.branch.master':
                value = master
            elif setting == 'gitflow.branch.develop':
                value = develop
            elif setting.startswith('gitflow.prefix.'):
                name = setting[len('gitflow.prefix.'):]
                value = prefixes.get(name, None)
            else:
                name = setting[len('gitflow.'):]
                value = names.get(name, None)
            if value is None:
                value = self.get(setting, default)
            self.set(setting, value)

    def _init_initial_commit(self):
        master = self.master_name()
        if master in self.repo.branches:
            # local `master` branch exists
            return
        elif self.origin_name(master) in self.repo.refs:
            # the origin branch counterpart exists
            origin = self.repo.refs[self.origin_name(master)]
            branch = self.repo.create_head(master, origin)
            branch.set_tracking_branch(origin)
        elif self.repo.heads:
            raise NotImplementedError(
                'Local and remote branches exist, but neither %s nor %s' % (
                    master, self.origin_name(master)
                ))
        else:
            # Create 'master' branch
            info('Creating branch %r' % master)
            c = self.repo.index.commit('Initial commit', head=False)
            self.repo.create_head(master, c)

    def _init_develop_branch(self):
        # assert master already exists
        assert self.master_name() in self.repo.refs
        develop = self.develop_name()
        if develop in self.repo.branches:
            # local `develop` branch exists, but do not switch there
            return
        if self.origin_name(develop) in self.repo.refs:
            # the origin branch counterpart exists
            origin = self.repo.refs[self.origin_name(develop)]
            branch = self.repo.create_head(develop, origin)
            branch.set_tracking_branch(origin)
        else:
            # Create 'develop' branch
            info('Creating branch %r' % develop)
            branch = self.repo.create_head(develop, self.master())
        # switch to develop branch if its newly created
        info('Switching to branch %s' % branch)
        branch.checkout()

    def _enforce_git_repo(self):
        """
        Ensure a (maybe empty) repository exists we can work on.

        This is to be used by the `init` sub-command.
        """
        if self.repo is None:
            self.git.init(self.working_dir)
            self.repo = Repo(self.working_dir)

    def init(self, master=None, develop=None, prefixes={}, names={},
             force_defaults=False):
        self._enforce_git_repo()
        self._init_config(master, develop, prefixes, names, force_defaults)
        self._init_initial_commit()
        self._init_develop_branch()
        return self

    def is_initialized(self):
        return (self.repo and
                self.is_set('gitflow.branch.master') and
                self.is_set('gitflow.branch.develop') and
                self.is_set('gitflow.prefix.feature') and
                self.is_set('gitflow.prefix.release') and
                self.is_set('gitflow.prefix.hotfix') and
                self.is_set('gitflow.prefix.support') and
                self.is_set('gitflow.prefix.versiontag'))

    def _parse_setting(self, setting):
        groups = setting.split('.', 2)
        if len(groups) == 2:
            section, option = groups
        elif len(groups) == 3:
            section, subsection, option = groups
            section = '%s "%s"' % (section, subsection)
        else:
            raise ValueError('Invalid setting name: %s' % setting)
        return (section, option)

    @requires_repo
    def get(self, setting, default=_NONE):
        section, option = self._parse_setting(setting)
        try:
            return self.repo.config_reader().get_value(section, option)
        except (NoSectionError, NoOptionError):
            if default is not _NONE:
                return default
            raise

    def get_prefix(self, identifier):
        return self._safe_get('gitflow.prefix.%s' % (identifier,))

    @requires_repo
    def set(self, setting, value):
        section, option = self._parse_setting(setting)
        writer = self.repo.config_writer()
        writer.set_value(section, option, value)
        writer.release()
        del writer

    def is_set(self, setting):
        return self.get(setting, None) is not None

    @requires_repo
    def _safe_get(self, setting_name):
        try:
            return self.get(setting_name)
        except (NoSectionError, NoOptionError):
            raise NotInitialized('This repo has not yet been initialized.')

    def master_name(self):
        return self._safe_get('gitflow.branch.master')

    def develop_name(self):
        return self._safe_get('gitflow.branch.develop')

    def origin_name(self, name=None):
        origin = self.get('gitflow.origin', self.defaults['gitflow.origin'])
        if name is not None:
            return origin + '/' + name
        else:
            return origin

    @requires_repo
    def require_remote(self, name):
        try:
            return self.repo.remotes[name]
        except IndexError:
            raise NoSuchRemoteError(name)

    def origin(self):
        return self.require_remote(self.origin_name())

    @requires_repo
    def develop(self):
        return self.repo.branches[self.develop_name()]

    @requires_repo
    def master(self):
        return self.repo.branches[self.master_name()]

    @requires_repo
    def branch_names(self, remote=False):
        if remote:
            return [r.name
                    for r in self.repo.refs
                    if isinstance(r, RemoteReference)]
        else:
            return [r.name for r in self.repo.branches]

    @requires_repo
    def nameprefix_or_current(self, identifier, prefix):
        """
        :param identifier:
            The identifier for the type of branch to work on.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param prefix: If the empty, see if the current branch is of
            type `identifier`. If so, returns the current branches
            short name, otherwise raises :exc:`NoSuchBranchError`.

        :returns:
            If exactly one branch of type `identifier` starts with the
            given name `prefix`, returns that branches short name.
            Raises :exc:`NoSuchBranchError` in case no branch exists
            with the given prefix, or :exc:`PrefixNotUniqueError` in
            case multiple matches are found.
        """
        repo = self.repo
        manager = self.managers[identifier]
        if not prefix:
            if repo.active_branch.name.startswith(manager.prefix):
                return manager.shorten(repo.active_branch.name)
            else:
                raise NoSuchBranchError(
                    'The current branch is no %s branch. '
                    'Please specify one explicitly.' % identifier)
        return manager.shorten(manager.by_name_prefix(prefix).name)

    @requires_repo
    def name_or_current(self, identifier, name, must_exist=True):
        """
        :param identifier:
            The identifier for the type of branch to work on.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param  name:
           If the `name` is empty, see if the current branch is of
           type `identifier`. If so, returns the current branches
           short name, otherwise raises :exc:`NoSuchBranchError`.

        :param must_exist: If `True` (the default), raises
            :exc:`NoSuchBranchError` in case no branch exists with the
            given `name`.

        Otherwise return the `name` unchanged.
        """
        repo = self.repo
        manager = self.managers[identifier]
        if not name:
            if repo.active_branch.name.startswith(manager.prefix):
                return manager.shorten(repo.active_branch.name)
            else:
                raise NoSuchBranchError(
                    'The current branch is no %s branch. '
                    'Please specify one explicitly.' % identifier)
        elif must_exist and not manager.full_name(name) in (b.name for b in manager.list()):
            raise NoSuchBranchError('There is no %s branch named %s.'
                                    % (identifier, name))
        return name

    @requires_repo
    def status(self):
        result = []
        for b in self.repo.branches:
            tup = self.branch_info(b.name)
            result.append(tup)
        return result

    @requires_repo
    def branch_info(self, name):
        active_branch = self.repo.active_branch
        b = self.repo.heads[name]
        return (name, b.commit.hexsha, b == active_branch)

    @requires_repo
    def is_dirty(self):
        """
        Returns whether or not the current working directory contains
        uncommitted changes.
        """
        return self.repo.is_dirty()

    @requires_repo
    def has_staged_commits(self):
        """
        Returns whether or not the current repo contains local changes
        checked into the index but not committed.
        """
        return len(self.repo.index.diff(self.repo.head.commit)) > 0

    @requires_repo
    def require_no_merge_conflict(self):
        """
        Raises :exc:`MergeConflict` if the current working directory
        contains a merge conflict.
        """
        try:
            git.Reference(self.repo, 'MERGE_HEAD', check_path=False).commit
            # reference exists, so there is a merge conflict
            raise MergeConflict()
        except ValueError:
            # no such reference, so there is no merge conflict
            pass

    def is_merged_into(self, commit, target_branch):
        """
        Checks whether `commit` is successfully merged into branch
        `target_branch`.

        :param commit:
            The commit or branch that ought to be merged. This may be
            a full branch-name, a commit-hexsha or any of branch-,
            head-, reference- or commit-object.

        :param target_branch:
            The branch which should contain the commit. This may be a
            full branch-name, or any of branch-, head- or
            reference-object.
        """
        try:
            commit = self.repo.rev_parse(str(commit))
        except (git.BadObject, git.BadName):
            raise BadObjectError(commit)
        if isinstance(target_branch, git.RemoteReference):
            target_branch = 'remotes/' + target_branch.name
        elif isinstance(target_branch, git.SymbolicReference):
            target_branch = target_branch.name
        # :todo: implement this more efficiently
        return target_branch in [
            b.lstrip('* ')
            for b in self.git.branch('-a', '--contains', commit).splitlines()]

    def must_be_uptodate(self, branch, fetch):
        remote_branch = self.origin_name(branch)
        if remote_branch in self.branch_names(remote=True):
            if fetch:
                self.origin().fetch(branch)
            self.require_branches_equal(branch, remote_branch)

    @requires_repo
    def _compare_branches(self, branch1, branch2):
        """
        Tests whether branches and their 'origin' counterparts have
        diverged and need merging first. It returns error codes to
        provide more detail, like so:

        0    Branch heads point to the same commit
        1    First given branch needs fast-forwarding
        2    Second given branch needs fast-forwarding
        3    Branch needs a real merge
        4    There is no merge base, i.e. the branches have no common ancestors
        """
        try:
            commit1 = self.repo.rev_parse(branch1)
            commit2 = self.repo.rev_parse(branch2)
        except (git.BadObject, git.BadName) as e:
            raise NoSuchBranchError(e.args[0])
        if commit1 == commit2:
            return 0
        try:
            # merge_base() returns a list of Commit objects
            # this list will have at max one Commit
            # or it will be empty if no common merge base exists
            base = self.repo.merge_base(commit1, commit2)[0]
        except (GitCommandError, IndexError):
            return 4
        if base == commit1:
            return 1
        elif base == commit2:
            return 2
        else:
            return 3

    @requires_repo
    def require_branches_equal(self, branch1, branch2):
        status = self._compare_branches(branch1, branch2)
        if status == 0:
            # branches are equal
            return
        else:
            warn("Branches '%s' and '%s' have diverged." % (branch1, branch2))
            if status == 1:
                raise SystemExit("And branch '%s' may be fast-forwarded." % branch1)
            elif status == 2:
                # Warn here, since there is no harm in being ahead
                warn("And local branch '%s' is ahead of '%s'." % (branch1, branch2))
            else:
                raise SystemExit("Branches need merging first.")

    @requires_repo
    def start_transaction(self, message=None):
        if message:
            info(message)

    @requires_initialized
    def tag(self, tagname, commit, message=None, sign=False, signingkey=None):
        kwargs = {}
        if sign:
            kwargs['s'] = True
        if signingkey:
            kwargs['u'] = signingkey
        self.repo.create_tag(tagname, commit, message=message or None, **kwargs)

    #
    # ====== sub commands =====
    #

    @requires_repo
    def list(self, identifier, arg0_name, verbose, use_tagname):
        """
        List the all branches of the given type. If there are not
        branches of this type, raises :exc:`Usage` with an
        explanation on how to start a branch of this type.

        :param identifier:
            The identifier for the type of branch to work on.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param arg0_name:
            Name of the first argument for the command line to be put
            into the explanation on how to start a branch of this
            type. This typically is `name` or `version`.

        :param verbose:
            If True, give more information about the state of the
            branch: Whether it's ahead or behind it's default base,
            may be rebased, etc.

        :param use_tagname:
            If True, try to describe the state based on the next tag.
        """
        repo = self.repo
        manager = self.managers[identifier]
        branches = manager.list()
        if not branches:
            raise Usage(
                'No %s branches exist.' % identifier,
                'You can start a new %s branch with the command:' % identifier,
                '    git flow %s start <%s> [<base>]' % (identifier, arg0_name)
            )

        # determine the longest branch name
        width = max(len(b.name) for b in branches) - len(manager.prefix) + 1

        basebranch_sha = repo.branches[manager.default_base()].commit.hexsha

        for branch in branches:
            if repo.active_branch == branch:
                prefix = '* '
            else:
                prefix = '  '

            name = manager.shorten(branch.name)
            extra_info = ''

            if verbose:
                name = name.ljust(width)
                branch_sha = branch.commit.hexsha
                base_sha = repo.git.merge_base(basebranch_sha, branch_sha)
                if branch_sha == basebranch_sha:
                    extra_info = '(no commits yet)'
                elif use_tagname:
                    try:
                        extra_info = self.git.name_rev('--tags', '--name-only',
                                                       '--no-undefined', base_sha)
                        extra_info = '(based on %s)' % extra_info
                    except GitCommandError:
                        pass
                if not extra_info:
                    if base_sha == branch_sha:
                        extra_info = '(is behind %s, may ff)' % manager.default_base()
                    elif base_sha == basebranch_sha:
                        extra_info = '(based on latest %s)' % manager.default_base()
                    else:
                        extra_info = '(may be rebased)'

            info(prefix + name + extra_info)

    @requires_initialized
    def create(self, identifier, name, base, fetch):
        """
        Creates a branch of the given type, with the given short name.

        :param identifier:
            The identifier for the type of branch to create.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param name:
            The friendly (short) name to create.

        :param base:
            The alternative base to branch off from.  If not given, the default
            base for the given branch type is used.

        :returns:
            The newly created :class:`git.refs.Head` branch.
        """
        return self.managers[identifier].create(name, base, fetch=fetch)

    @requires_initialized
    def finish(self, identifier, name, fetch, rebase, keep, force_delete,
               tagging_info):
        """
        Finishes a branch of the given type, with the given short name.

        :param identifier:
            The identifier for the type of branch to finish.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param name:
            The friendly (short) name to finish.
        """
        mgr = self.managers[identifier]
        branch = mgr.by_name_prefix(name)
        try:
            self.require_no_merge_conflict()
        except MergeConflict as e:
            raise Usage(e,
                        "You can then complete the finish by running it again:",
                        "    git flow %s finish %s" % (identifier, name)
                        )
        return mgr.finish(mgr.shorten(branch.name), fetch=fetch, rebase=rebase,
                          keep=keep, force_delete=force_delete,
                          tagging_info=tagging_info)

    @requires_initialized
    def checkout(self, identifier, name):
        """
        Checkout a branch of the given type, with the given short name.

        :param identifier:
            The identifier for the type of branch to checkout.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param name:
            The friendly (short) name to checkout.

        :returns:
            The checked out :class:`git.refs.Head` branch.
        """
        mgr = self.managers[identifier]
        branch = mgr.by_name_prefix(name)
        return branch.checkout()

    @requires_initialized
    def diff(self, identifier, name):
        """
        Print the diff of changes since this branch branched off.

        :param identifier:
            The identifier for the type of branch to work on.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param name:
            The friendly (short) name to work on.
        """
        mgr = self.managers[identifier]
        full_name = mgr.full_name(name)
        base = self.git.merge_base(mgr.default_base(), full_name)
        print(self.git.diff('%s..%s' % (base, full_name)))

    @requires_initialized
    def rebase(self, identifier, name, interactive):
        """
        Rebase a branch of the given type, with the given short name,
        on top of it's default base.

        :param identifier:
            The identifier for the type of branch to rebase.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param name:
            The friendly (short) name to rebase.

        :param interactive:
            If True, do an interactive rebase.
        """
        warn("Will try to rebase %s branch '%s' ..." % (identifier, name))
        mgr = self.managers[identifier]
        mgr.full_name(name)
        # :todo: require_clean_working_tree
        self.checkout(identifier, name)
        args = []
        if interactive:
            args.append('-i')
        args.append(mgr.default_base())
        self.git.rebase(*args)

    @requires_initialized
    def publish(self, identifier, name):
        """
        Publish a branch of the given type, with the given short name,
        to `origin` (or whatever is configured as `remote` for gitflow.)

        :param identifier:
            The identifier for the type of branch to publish.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param name:
            The friendly (short) name to publish.

        :returns:
            The full name of the published branch.
        """
        repo = self.repo
        mgr = self.managers[identifier]

        # sanity checks
        # :todo: require_clean_working_tree
        full_name = mgr.full_name(name)
        remote_name = self.origin_name(full_name)
        if full_name not in repo.branches:
            raise NoSuchBranchError(full_name)
        if remote_name in repo.refs:
            raise BranchExistsError(remote_name)
        # :todo: check if full_name already has a tracking branch
        # :todo: check if full_name already has the same tracking branch

        # create remote branch
        origin = self.origin()
        info = origin.push('%s:refs/heads/%s' % (full_name, full_name))[0]
        origin.fetch()
        # configure remote tracking
        repo.branches[full_name].set_tracking_branch(info.remote_ref)
        return full_name

    @requires_initialized
    def pull(self, identifier, remote, name):
        """
        Pull a branch of the given type, with the given short name,
        from the given remote peer.

        :param identifier:
            The identifier for the type of branch to pull.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param remote:
            The remote to pull from. This must have been configured by
            `git remote add ...`.

        :param name:
            The friendly (short) name to pull.
        """

        def avoid_accidental_cross_branch_action(branch_name):
            current_branch = repo.active_branch
            if branch_name != current_branch.name:
                warn("Trying to pull from '%s' while currently on branch '%s'."
                     % (branch_name, current_branch))
                raise SystemExit("To avoid unintended merges, git-flow aborted.")

        repo = self.repo
        mgr = self.managers[identifier]
        full_name = mgr.full_name(name)
        # To avoid accidentally merging different feature branches
        # into each other, die if the current feature branch differs
        # from the requested $NAME argument.
        if repo.active_branch.name.startswith(self.get_prefix(identifier)):
            # We are on a local `identifier` branch already, so `full_name`
            # must be equal to the current branch.
            avoid_accidental_cross_branch_action(full_name)
        # :todo: require_clean_working_tree
        if full_name in self.repo.branches:
            # Again, avoid accidental merges
            avoid_accidental_cross_branch_action(full_name)
            # We already have a local branch called like this, so
            # simply pull the remote changes in
            self.require_remote(remote).pull(full_name)
            # :fixme: why is the branch not checked out here?
            info("Pulled %s's changes into %s." % (remote, full_name))
        else:
            # Setup the non-tracking local branch clone for the first time
            self.require_remote(remote).fetch(full_name + ':' + full_name)
            repo.heads[full_name].checkout()
            info("Created local branch %s based on %s's %s."
                 % (full_name, remote, full_name))

    @requires_initialized
    def track(self, identifier, name):
        """
        Track a branch of the given type, with the given short name,
        from `origin` (or whatever is configured as `remote` for
        gitflow.)

        :param identifier:
            The identifier for the type of branch to track.
            A :class:`BranchManager <git.branches.BranchManager>` for the given
            identifier must exist in the :attr:`self.managers`.

        :param name:
            The friendly (short) name to track.

        :param base:
            The alternative base to branch off from.  If not given, the default
            base for the given branch type is used.

        :returns:
            The newly created :class:`git.refs.Head` branch.
        """
        repo = self.repo
        mgr = self.managers[identifier]
        # sanity checks
        # :todo: require_clean_working_tree
        full_name = mgr.full_name(name)
        if full_name in repo.branches:
            raise BranchExistsError(full_name)
        self.origin().fetch(full_name)
        remote_branch = self.origin().refs[full_name]
        branch = repo.create_head(full_name, remote_branch)
        branch.set_tracking_branch(remote_branch)
        return branch.checkout()
예제 #24
0
    '--list-of-pos',
    help=
    'A list of dvc-cc indizes that you want include in the display. You can also use slicing for example: 12:15:2 to use 12, 14.',
    nargs="+",
    type=str)
parser.add_argument(
    '-e',
    '--print-error',
    help=
    'If this parameter is set, it will print the error message, why a file or folder could not be found.',
    action='store_true')
args = parser.parse_args()

repo = DVCRepo()
g = Git()
starting_branch = g.branch().split('*')[1].split('\n')[0][1:]

# Set the password only once!
remote_name = repo.config['core']['remote']
remote_settings = repo.config['remote'][remote_name]
if 'ask_password' in remote_settings and remote_settings['ask_password']:
    remote_settings['password'] = getpass.getpass('Password for ' +
                                                  remote_settings['url'] +
                                                  ': ')
    remote_settings['ask_password'] = False

path_to_output_clean = args.path_to_output.replace('./', '_').replace(
    '/', '_').replace('\\\\', '_')
outputdir = create_output_dir(repo.root_dir, path_to_output_clean)
print('##################', outputdir)
if outputdir is None:
예제 #25
0
파일: cli.py 프로젝트: vlobzakov/docs
def build_command(config_file, strict, site_dir, branches, default_branch,
                  latest, logging_level):
    """Build the MkDocs documentation"""

    #    cli.configure_logging(level=logging.INFO)
    global release_branches

    logging.basicConfig(
        stream=sys.stdout,
        level=get_logging_level(logging_level),
        format=
        '%(asctime)s %(levelname)s [%(threadName)s] [%(filename)s:%(lineno)d] %(message)s'
    )

    g = Git()
    repo = Repo()

    branches = branches or g.branch('-r').splitlines()
    all_branch_names = list(
        map(lambda branch: branch.split("origin/")[1], branches))

    active_branch = repo.active_branch.name
    print("Active branch %s", active_branch)
    print("Default branch %s", default_branch)
    print("Latest branch %s", latest)

    start_stashes_count = len(
        re.findall("stash@{[0-9]{1,3}}:", repo.git.stash("list")))
    repo.git.stash("save")

    if active_branch != latest:
        print("Checkout Default %s", active_branch)
        g.checkout(default_branch)

    default_config = _load_config(config_file, strict, site_dir)

    versions = default_config.get("extra").get("versions")

    formatedCSVersions = {}
    jelasticVersions = []

    for version in versions:
        formatedCSVersions[unicode(version)] = versions[version]

    if formatedCSVersions is not None:
        release_branches = formatedCSVersions.keys()
        jelasticVersions = formatedCSVersions.values()

    if release_branches is not None:
        release_branches = sorted(release_branches,
                                  key=functools.cmp_to_key(version_compare))
        jelasticVersions = sorted(jelasticVersions,
                                  key=functools.cmp_to_key(version_compare))

        default_version = next(iter(release_branches),
                               None)  # release_branches[-1]

        print("Default version %s", default_version)
        print("Building %s to /", default_version)

        _build(default_config, default_version, release_branches,
               jelasticVersions)

        for branch in release_branches:
            if branch in all_branch_names:  #branch != default_version and
                g.checkout(branch)
                g.pull()

                if not os.path.exists("mkdocs.yml"):
                    log.warning(
                        "Unable to build %s, as no mkdocs.yml was found",
                        branch)
                    print("Unable to build %s, as no mkdocs.yml was found",
                          branch)
                    continue

                site_dir = "{0}".format(branch)
                log.info("Building %s to /%s", branch, "site/" + site_dir)
                print("Building %s to /%s", branch, "site/" + site_dir)
                _build(_load_config(config_file, strict, site_dir), branch,
                       release_branches, jelasticVersions, "site/" + site_dir)

        # print("Selected Branches %s", default_config.get("versions").get("releases"))

    print("Checkout branch %s", active_branch)
    g.checkout("master")

    end_stashes_count = len(
        re.findall("stash@{[0-9]{1,3}}:", repo.git.stash("list")))

    if end_stashes_count > start_stashes_count:
        repo.git.stash("pop")
        print("pop latest stash")
예제 #26
0
 def _get_branches(self, git: Git, commit_hash: str) -> set():
     branches = set()
     for branch in set(git.branch('--contains', commit_hash).split('\n')):
         branches.add(branch.strip().replace('* ', ''))
     return branches
예제 #27
0
def main():
    parser = ArgumentParser(description=DESCRIPTION)
    parser.add_argument(
        'path_to_output',
        help='The path to the output/metric file or folder that you want get.',
        type=str)
    parser.add_argument(
        '-p',
        '--list-of-pos',
        help=
        'A list of dvc-cc indizes that you want include in the display. You can also use slicing for example: 12:15:2 to use 12, 14.',
        nargs="+",
        type=str)
    parser.add_argument(
        '-e',
        '--print-error',
        help=
        'If this parameter is set, it will print the error message, why a file or folder could not be found.',
        action='store_true')
    args = parser.parse_args()

    repo = DVCRepo()
    g = Git()
    starting_branch = g.branch().split('*')[1].split('\n')[0][1:]

    # Set the password only once!
    remote_name = repo.config['core']['remote']
    remote_settings = repo.config['remote'][remote_name]
    if 'ask_password' in remote_settings and remote_settings['ask_password']:
        remote_settings['password'] = getpass.getpass('Password for ' +
                                                      remote_settings['url'] +
                                                      ': ')
        remote_settings['ask_password'] = False

    path_to_output_clean = args.path_to_output.replace('./', '_').replace(
        '/', '_').replace('\\\\', '_')
    outputdir = create_output_dir(repo.root_dir, path_to_output_clean)
    print('##################', outputdir)
    if outputdir is None:
        exit(1)

    list_of_allowed_dvccc_ids = None

    if args.list_of_pos is not None:

        list_of_allowed_dvccc_ids = []
        for pos in args.list_of_pos:
            try:
                if pos.find(':') > -1:
                    pos = np.array(pos.split(':'), dtype=int)
                    list_of_allowed_dvccc_ids.extend(np.arange(*pos))
                else:
                    pos = int(pos)
                    if pos >= 0:
                        list_of_allowed_dvccc_ids.append(pos)
                    else:
                        raise ValueError(
                            'ERROR: The parameters ' + str(pos) +
                            ' from --list-of-pos must be positive.')
            except:
                raise ValueError(
                    'ERROR: The parameters ' + str(pos) +
                    ' from --list-of-pos must be an integer or a slicings. i.e.1: 12 14    i.e.2: 12:15:2'
                )

        list_of_allowed_dvccc_ids = np.array(list_of_allowed_dvccc_ids)

    all_branches = [
        b.split('/')[-1] for b in g.branch('-a').split()
        if b.startswith('rcc_')
    ]
    all_branches = np.unique(all_branches)

    for b in all_branches:
        if list_of_allowed_dvccc_ids is None or int(
                b.split('_')[1]) in list_of_allowed_dvccc_ids:
            print('dvc get : ', '.', args.path_to_output,
                  str(Path(outputdir + '/' + b)), b)
            try:
                if b.endswith('.dvc'):
                    path_to_output = str(Path(outputdir + '/' + b[:-4]))
                    print(path_to_output)
                    os.mkdir(path_to_output)
                    repo.get('.',
                             args.path_to_output,
                             out=str(
                                 Path(path_to_output + '/' +
                                      path_to_output_clean)),
                             rev=b)
                else:
                    path_to_output = str(Path(outputdir + '/' + b))
                    print(path_to_output)
                    os.mkdir(path_to_output)
                    repo.get('.',
                             args.path_to_output,
                             out=str(
                                 Path(path_to_output + '/' +
                                      path_to_output_clean)),
                             rev=b)
                for p in os.listdir('/tmp'):
                    if p.endswith('dvc-erepo'):
                        shutil.rmtree('/tmp/' + p)
            except Exception as ex:
                print('File was not found.')
                os.rmdir(path_to_output)
                if args.print_error:
                    print(ex)
    print()
    print('Found ' + str(len(os.listdir(str(Path(outputdir))))) +
          ' files or folders.')
    if len(os.listdir(str(Path(outputdir)))) == 0:
        print('Folder was removed.')
        os.rmdir(str(Path(outputdir)))
    print(
        'If files are missing, please use "dvc-cc git sync" to get new result branches and repeat this command.'
    )
예제 #28
0
파일: main.py 프로젝트: KarakoA/dvc-cc
def main():
    parser = ArgumentParser(description=DESCRIPTION)
    parser.add_argument(
        '-f',
        '--regex-name-of-file',
        type=str,
        default=None,
        help='A regex of the name of the files that you want to find.')
    parser.add_argument(
        '-ef',
        '--exclude-regex-name-of-file',
        type=str,
        default=None,
        help='A regex of the name of the file that are excluded.')
    parser.add_argument(
        '-b',
        '--regex-name-of-branch',
        type=str,
        default=None,
        help='A regex of the name of the branches to be included in the search.'
    )
    parser.add_argument(
        '-eb',
        '--exclude-regex-name-of-branch',
        type=str,
        default=None,
        help='A regex of the name of the branch that are excluded.')
    parser.add_argument(
        '-pos',
        '--list-of-pos',
        help=
        'A list of dvc-cc indizes that you want include in the display. You can also use slicing for example: 12:15:2 to use 12, 14.',
        nargs="+",
        type=str)
    parser.add_argument('-p',
                        '--path-to-output',
                        type=str,
                        default=None,
                        help='The path where you want save the files.')
    parser.add_argument(
        '-o',
        '--original-name',
        dest='original_name',
        action='store_true',
        default=False,
        help=
        'In default, the branch name is added to the file or folder name. If this parameter is '
        'set,  it will use the original name of the file or folder. If the file exists multiple'
        'times and this parameter is set, then it will use indices at the end of the file or folder names.'
    )
    parser.add_argument('--debug',
                        dest='debug',
                        action='store_true',
                        default=False,
                        help='Print all files that are copied.')
    parser.add_argument(
        '-d',
        '--download-stages',
        dest='download_stages',
        action='store_true',
        default=False,
        help='Download a stage if the file is not in the local cache.')
    parser.add_argument(
        '-fd',
        '--forbid-dir',
        dest='forbid_dir',
        action='store_true',
        default=False,
        help='If this parameter is set, then it will ignore output folders.')
    parser.add_argument(
        '-ns',
        '--no-save',
        dest='no_save',
        action='store_true',
        default=False,
        help=
        'If true, it will not create a folder or link the file. This parameter is helpfull if it is used with --debug to test your regular expressions.'
    )
    parser.add_argument(
        '-nw',
        '--no-print-of-warnings',
        dest='no_warning',
        action='store_true',
        default=False,
        help=
        'If true, it will not print warning if a file is not created or not in the local cache.'
    )
    args = parser.parse_args()

    repo = DVCRepo()
    g = Git()
    starting_branch = g.branch().split('*')[1].split('\n')[0][1:]

    # Set the password only once!
    if args.download_stages:
        remote_name = repo.config['core']['remote']
        remote_settings = repo.config['remote'][remote_name]
        if 'ask_password' in remote_settings and remote_settings[
                'ask_password']:
            remote_settings['password'] = getpass.getpass(
                'Password for ' + remote_settings['url'] + ': ')
            remote_settings['ask_password'] = False

    if not args.no_save:
        path_to_output = create_output_dir(repo.root_dir, args.path_to_output)
        if path_to_output is None:
            exit(1)
    else:
        path_to_output = 'NONE'

    list_of_allowed_dvccc_ids = None

    if args.list_of_pos is not None:

        list_of_allowed_dvccc_ids = []
        for pos in args.list_of_pos:
            try:
                if pos.find(':') > -1:
                    pos = np.array(pos.split(':'), dtype=int)
                    list_of_allowed_dvccc_ids.extend(np.arange(*pos))
                else:
                    pos = int(pos)
                    if pos >= 0:
                        list_of_allowed_dvccc_ids.append(pos)
                    else:
                        raise ValueError(
                            'ERROR: The parameters ' + str(pos) +
                            ' from --list-of-pos must be positive.')
            except:
                raise ValueError(
                    'ERROR: The parameters ' + str(pos) +
                    ' from --list-of-pos must be an integer or a slicings. i.e.1: 12 14    i.e.2: 12:15:2'
                )

        list_of_allowed_dvccc_ids = np.array(list_of_allowed_dvccc_ids)

    try:
        file_counter = 0
        saved_files = {}
        for branch in repo.brancher(all_branches=True):
            outs = []
            branch_names = []
            if branch.lower() != 'working tree':

                # check if this is a result branch:
                is_dvccc_result_branch = branch.startswith('rcc_')

                # search for all output files in the current branch
                is_branch_of_interest1 = args.regex_name_of_branch is None or re.match(
                    args.regex_name_of_branch, branch)
                is_branch_of_interest2 = args.exclude_regex_name_of_branch is None or not re.match(
                    args.exclude_regex_name_of_branch, branch)

                is_allowed_dvccc_id = True
                if list_of_allowed_dvccc_ids is not None and is_dvccc_result_branch:
                    if not int(
                            branch.split('_')[1]) in list_of_allowed_dvccc_ids:
                        is_allowed_dvccc_id = False

                if is_branch_of_interest1 and is_branch_of_interest2 and is_dvccc_result_branch and is_allowed_dvccc_id:
                    print(branch)
                    g.checkout(branch)
                    #TODO: This would be nice, but its too sloow!
                    try:
                        repo.checkout()
                    except:
                        print('Some files are missing.')

                    print('\tIt is a branch of interest!')
                    #TODO: repo.stages is very slow!
                    for stage in repo.stages:
                        for out in stage.outs:
                            valid_msg = check_out_if_its_valid(
                                out, args.regex_name_of_file,
                                args.exclude_regex_name_of_file,
                                not args.forbid_dir)
                            print('\t\t\t', out, valid_msg)
                            if valid_msg == 'not_in_local_cache' and args.download_stages:
                                g.pull()
                                try:
                                    repo.pull(stage.relpath)
                                except:
                                    print('Some files are missing.')
                                time.sleep(1)
                                valid_msg = check_out_if_its_valid(
                                    out, args.regex_name_of_file,
                                    args.exclude_regex_name_of_file,
                                    not args.forbid_dir)
                                print(valid_msg)
                            if valid_msg == 'valid':
                                outs.append(out)
                                branch_names.append(branch)
                            elif valid_msg == 'not_created' and args.no_warning == False:
                                print(
                                    'Warning: A output file of interest has not yet been created. '
                                    + '(file: ' + str(out) + '; branch: ' +
                                    branch + ')')
                            elif valid_msg == 'not_in_local_cache' and args.no_warning == False:
                                print(
                                    'Warning: A output file of interest is not on the local cache. '
                                    + '(file: ' + out.cache_path +
                                    '; branch: ' + branch +
                                    ')\n You can use this script with -d and it will download the missing stage.'
                                )

                # create a link for each output file of interest in the current branch
                for out, branch_name in zip(outs, branch_names):
                    # create the output file name
                    if not args.original_name:
                        out_filename = branch_name + '_' + str(out).replace(
                            '/', '_').replace('\\\\', '_')
                    else:
                        out_filename = str(out).replace('/', '_').replace(
                            '\\\\', '_')
                    out_filepath = os.path.join(repo.root_dir, path_to_output,
                                                out_filename)

                    file_was_already_saved = False
                    renamer_index = 2
                    file_can_be_saved = False
                    tmp_out_filepath = out_filepath

                    while not file_can_be_saved and not file_was_already_saved:
                        if tmp_out_filepath not in saved_files:
                            file_can_be_saved = True
                            out_filepath = tmp_out_filepath
                            saved_files[out_filepath] = out.checksum
                        elif saved_files[tmp_out_filepath] == out.checksum:
                            file_was_already_saved = True
                        else:
                            tmp_out_filepath = out_filepath + '_' + str(
                                renamer_index)
                            renamer_index += 1
                    if file_can_be_saved:
                        if args.debug:
                            print(out.cache_path, ' -> ', out_filepath)
                        if args.no_save is False:
                            if out.isfile():
                                os.link(out.cache_path, out_filepath)
                            elif out.isdir():
                                os.mkdir(out_filepath)
                                for cache in out.dir_cache:
                                    dirfile_cache_path = repo.cache.local.get(
                                        cache['md5'])
                                    dirfile_outpath = os.path.join(
                                        out_filepath, cache['relpath'])
                                    os.makedirs(
                                        os.path.dirname(dirfile_outpath),
                                        exist_ok=True)
                                    os.link(dirfile_cache_path,
                                            dirfile_outpath)

                        file_counter += 1

        print(
            str(file_counter) + ' files are linked to ' + path_to_output + '.')

    # return always to the starting branch!
    finally:
        g.checkout(starting_branch)
        try:
            repo.checkout()
        except:
            print('Some files are missing.')