Beispiel #1
0
    def create(klass, path, description=None, bare=False):
        """
        Create a repository at path

        @param path: where to create the repository
        @type path: C{str}
        @param bare: whether to create a bare repository
        @type bare: C{bool}
        @return: git repository object
        @rtype: L{GitRepository}
        """
        args = GitArgs()
        abspath = os.path.abspath(path)

        args.add_true(bare, '--bare')
        git_dir = '' if bare else '.git'

        try:
            if not os.path.exists(abspath):
                os.makedirs(abspath)
            try:
                GitCommand("init", args.args, cwd=abspath)()
            except CommandExecFailed as excobj:
                raise GitRepositoryError("Error running git init: %s" % excobj)

            if description:
                with file(os.path.join(abspath, git_dir, "description"), 'w') as f:
                    description += '\n' if description[-1] != '\n' else ''
                    f.write(description)
            return klass(abspath)
        except OSError as err:
            raise GitRepositoryError("Cannot create Git repository at '%s': %s"
                                     % (abspath, err[1]))
        return None
Beispiel #2
0
    def is_clean(self, ignore_untracked=False):
        """
        Does the repository contain any uncommitted modifications?

        @param ignore_untracked: whether to ignore untracked files when
            checking the repository status
        @type ignore_untracked: C{bool}
        @return: C{True} if the repository is clean, C{False} otherwise
            and Git's status message
        @rtype: C{tuple}
        """
        if self.bare:
            return (True, '')

        clean_msg = 'nothing to commit'

        args = GitArgs()
        args.add_true(ignore_untracked, '-uno')

        out, ret = self._git_getoutput('status',
                                       args.args,
                                       extra_env={'LC_ALL': 'C'})
        if ret:
            raise GbpError("Can't get repository status")
        ret = False
        for line in out:
            if line.startswith('#'):
                continue
            if line.startswith(clean_msg):
                ret = True
            break
        return (ret, "".join(out))
Beispiel #3
0
    def list_tree(self, treeish, recurse=False):
        """
        Get a trees content. It returns a list of objects that match the
        'ls-tree' output: [ mode, type, sha1, path ].

        @param treeish: the treeish object to list
        @type treeish: C{str}
        @param recurse: whether to list the tree recursively
        @type recurse: C{bool}
        @return: the tree
        @rtype: C{list} of objects. See above.
        """
        args = GitArgs('-z')
        args.add_true(recurse, '-r')
        args.add(treeish)

        out, err, ret =  self._git_inout('ls-tree', args.args, capture_stderr=True)
        if ret:
            raise GitRepositoryError("Failed to ls-tree '%s': '%s'" % (treeish, err))

        tree = []
        for line in out.split('\0'):
            if line:
                tree.append(line.split(None, 3))
        return tree
Beispiel #4
0
    def push(self, repo=None, src=None, dst=None, ff_only=True):
        """
        Push changes to the remote repo

        @param repo: repository to push to
        @type repo: C{str}
        @param src: the source ref to push
        @type src: C{str}
        @param dst: the name of the destination ref to push to
        @type dst: C{str}
        @param ff_only: only push if it's a fast forward update
        @type ff_only: C{bool}
        """
        args = GitArgs()
        args.add_cond(repo, repo)

        # Allow for src == '' to delete dst on the remote
        if src != None:
            refspec = src
            if dst:
                refspec += ':%s' % dst
            if not ff_only:
                refspec = '+%s' % refspec
            args.add(refspec)
        self._git_command("push", args.args)
Beispiel #5
0
 def commit_all(self, msg, author_info=None, edit=False):
     """
     Commit all changes to the repository
     @param msg: commit message
     @type msg: C{str}
     @param author_info: authorship information
     @type author_info: L{GitModifier}
     """
     args = GitArgs('-a')
     args.add_true(edit,  '--edit')
     self._commit(msg=msg, args=args.args, author_info=author_info)
Beispiel #6
0
    def create_branch(self, branch, rev=None):
        """
        Create a new branch

        @param branch: the branch's name
        @param rev: where to start the branch from

        If rev is None the branch starts form the current HEAD.
        """
        args = GitArgs(branch)
        args.add_true(rev, rev)
        self._git_command("branch", args.args)
Beispiel #7
0
    def commit_staged(self, msg, author_info=None, edit=False):
        """
        Commit currently staged files to the repository

        @param msg: commit message
        @type msg: C{str}
        @param author_info: authorship information
        @type author_info: L{GitModifier}
        @param edit: whether to spawn an editor to edit the commit info
        @type edit: C{bool}
        """
        args = GitArgs()
        args.add_true(edit,  '--edit')
        self._commit(msg=msg, args=args.args, author_info=author_info)
Beispiel #8
0
    def delete_branch(self, branch, remote=False):
        """
        Delete branch I{branch}

        @param branch: name of the branch to delete
        @type branch: C{str}
        @param remote: delete a remote branch
        @param remote: C{bool}
        """
        args = GitArgs('-D')
        args.add_true(remote, '-r')
        args.add(branch)

        if self.branch != branch:
            self._git_command("branch", args.args)
        else:
            raise GitRepositoryError("Can't delete the branch you're on")
Beispiel #9
0
    def rev_parse(self, name, short=0):
        """
        Find the SHA1 of a given name

        @param name: the name to look for
        @type name: C{str}
        @param short:  try to abbreviate SHA1 to given length
        @type short: C{int}
        @return: the name's sha1
        @rtype: C{str}
        """
        args = GitArgs("--quiet", "--verify")
        args.add_cond(short, '--short=%d' % short)
        args.add(name)
        sha, ret = self._git_getoutput('rev-parse', args.args)
        if ret:
            raise GitRepositoryError("revision '%s' not found" % name)
        return sha[0].strip()
Beispiel #10
0
    def get_merge_base(self, commit1, commit2):
        """
        Get the common ancestor between two commits

        @param commit1: commit SHA1 or name of a branch or tag
        @type commit1: C{str}
        @param commit2: commit SHA1 or name of a branch or tag
        @type commit2: C{str}
        @return: SHA1 of the common ancestor
        @rtype: C{str}
        """
        args = GitArgs()
        args.add(commit1)
        args.add(commit2)
        sha1, stderr, ret = self._git_inout('merge-base', args.args, capture_stderr=True)
        if not ret:
            return sha1.strip()
        else:
            raise GitRepositoryError("Failed to get common ancestor: %s" % stderr.strip())
Beispiel #11
0
    def get_upstream_branch(self, local_branch):
        """
        Get upstream branch for the local branch

        @param local_branch: name fo the local branch
        @type local_branch: C{str}
        @return: upstream (remote/branch) or  '' if no upstream found
        @rtype: C{str}

        """
        args = GitArgs('--format=%(upstream:short)')
        if self.has_branch(local_branch, remote=False):
            args.add('refs/heads/%s' % local_branch)
        else:
            raise GitRepositoryError("Branch %s doesn't exist!" % local_branch)

        out = self._git_getoutput('for-each-ref', args.args)[0]

        return out[0].strip()
Beispiel #12
0
    def write_file(self, filename, filters=True):
        """
        Hash a single file and write it into the object database

        @param filename: the filename to the content of the file to hash
        @type filename: C{str}
        @param filters: whether to run filters
        @type filters: C{bool}
        @return: the hash of the file
        @rtype: C{str}
        """
        args = GitArgs('-w', '-t', 'blob')
        args.add_false(filters, '--no-filters')
        args.add(filename)

        sha1, stderr, ret = self._git_inout('hash-object',
                                            args.args,
                                            capture_stderr=True)
        if not ret:
            return sha1.strip()
        else:
            raise GbpError("Failed to hash %s: %s" % (filename, stderr))
Beispiel #13
0
    def format_patches(self, start, end, output_dir, signature=True, thread=None):
        """
        Output the commits between start and end as patches in output_dir
        """
        options = GitArgs('-N', '-k',
                          '-o', output_dir)
        options.add_cond(not signature, '--no-signature')
        options.add('%s...%s' % (start, end))
        options.add_cond(thread, '--thread=%s' % thread, '--no-thread')

        output, ret = self._git_getoutput('format-patch', options.args)
        return [ line.strip() for line in output ]
Beispiel #14
0
    def merge(self, commit, verbose=False, edit=False):
        """
        Merge changes from the named commit into the current branch

        @param commit: the commit to merge from (usually a branch name or tag)
        @type commit: C{str}
        @param verbose: whether to print a summary after the merge
        @type verbose: C{bool}
        @param edit: wheter to invoke an editor to edit the merge message
        @type edit: C{bool}
        """
        args = GitArgs()
        args.add_cond(verbose, '--summary', '--no-summary')
        args.add_cond(edit, '--edit', '--no-edit')
        args.add(commit)
        self._git_command("merge", args.args)
Beispiel #15
0
    def fetch(self, repo=None, tags=False, depth=0):
        """
        Download objects and refs from another repository.

        @param repo: repository to fetch from
        @type repo: C{str}
        @param tags: whether to fetch all tag objects
        @type tags: C{bool}
        @param depth: deepen the history of (shallow) repository to depth I{depth}
        @type depth: C{int}
        """
        args = GitArgs('--quiet')
        args.add_true(tags, '--tags')
        args.add_cond(depth, '--depth=%s' % depth)
        args.add_cond(repo, repo)

        self._git_command("fetch", args.args)
Beispiel #16
0
    def add_remote_repo(self, name, url, tags=True, fetch=False):
        """
        Add a tracked remote repository

        @param name: the name to use for the remote
        @type name: C{str}
        @param url: the url to add
        @type url: C{str}
        @param tags: whether to fetch tags
        @type tags: C{bool}
        @param fetch: whether to fetch immediately from the remote side
        @type fetch: C{bool}
        """
        args = GitArgs('add')
        args.add_false(tags, '--no-tags')
        args.add_true(fetch, '--fetch')
        args.add(name, url)
        self._git_command("remote", args.args)
Beispiel #17
0
    def branch_contains(self, branch, commit, remote=False):
        """
        Check if branch I{branch} contains commit I{commit}

        @param branch: the branch the commit should be on
        @type branch: C{str}
        @param commit: the C{str} commit to check
        @type commit: C{str}
        @param remote: whether to check remote instead of local branches
        @type remote: C{bool}
        """
        args = GitArgs()
        args.add_true(remote, '-r')
        args.add('--contains')
        args.add(commit)

        out, ret =  self._git_getoutput('branch', args.args)
        for line in out:
            # remove prefix '*' for current branch before comparing
            line = line.replace('*', '')
            if line.strip() == branch:
                return True
        return False
Beispiel #18
0
    def get_commits(self, since=None, until=None, paths=None, num=0,
                    first_parent=False, options=None):
        """
        Get commits from since to until touching paths

        @param since: commit to start from
        @type since: C{str}
        @param until: last commit to get
        @type until: C{str}
        @param paths: only list commits touching paths
        @type paths: C{list} of C{str}
        @param num: maximum number of commits to fetch
        @type num: C{int}
        @param options: list of additional options passed to git log
        @type  options: C{list} of C{str}ings
        @param first_parent: only follow first parent when seeing a
                             merge commit
        @type first_parent: C{bool}
        """
        args = GitArgs('--pretty=format:%H')
        args.add_true(num, '-%d' % num)
        args.add_true(first_parent, '--first-parent')
        args.add_true(since and until, '%s..%s' % (since, until))
        args.add_cond(options, options)
        args.add("--")
        if isinstance(paths, basestring):
            paths = [ paths ]
        args.add_cond(paths, paths)

        commits, ret = self._git_getoutput('log', args.args)
        if ret:
            where = " on %s" % paths if paths else ""
            raise GitRepositoryError("Error getting commits %s..%s%s" %
                        (since, until, where))
        return [ commit.strip() for commit in commits ]
Beispiel #19
0
    def clone(klass, path, remote, depth=0, recursive=False, mirror=False,
              bare=False, auto_name=True):
        """
        Clone a git repository at I{remote} to I{path}.

        @param path: where to clone the repository to
        @type path: C{str}
        @param remote: URL to clone
        @type remote: C{str}
        @param depth: create a shallow clone of depth I{depth}
        @type depth: C{int}
        @param recursive: whether to clone submodules
        @type recursive: C{bool}
        @param mirror: whether to pass --mirror to git-clone
        @type mirror: C{bool}
        @param bare: whether to create a bare repository
        @type bare: C{bool}
        @param auto_name: If I{True} create a directory below I{path} based on
            the I{remote}s name. Otherwise create the repo directly at I{path}.
        @type auto_name: C{bool}
        @return: git repository object
        @rtype: L{GitRepository}
        """
        abspath = os.path.abspath(path)
        if auto_name:
            name = None
        else:
            abspath, name = abspath.rsplit('/', 1)

        args = GitArgs('--quiet')
        args.add_true(depth,  '--depth', depth)
        args.add_true(recursive, '--recursive')
        args.add_true(mirror, '--mirror')
        args.add_true(bare, '--bare')
        args.add(remote)
        args.add_true(name, name)
        try:
            if not os.path.exists(abspath):
                os.makedirs(abspath)

            try:
                GitCommand("clone", args.args, cwd=abspath)()
            except CommandExecFailed as excobj:
                raise GitRepositoryError("Error running git clone: %s" %
                                         excobj)

            if not name:
                name = remote.rstrip('/').rsplit('/',1)[1]
                if (mirror or bare):
                    if not name.endswith('.git'):
                        name = "%s.git" % name
                elif name.endswith('.git'):
                    name = name[:-4]
            return klass(os.path.join(abspath, name))
        except OSError as err:
            raise GitRepositoryError("Cannot clone Git repository "
                                     "'%s' to '%s': %s"
                                     % (remote, abspath, err[1]))
        return None