Skip to content

git

LibGit2Sharp wrapper module for pyRevit.

Documentation

https://github.com/libgit2/libgit2sharp/wiki

Attributes

mlogger = get_logger(__name__) module-attribute

GIT_LIB = 'LibGit2Sharp' module-attribute

LIBGIT_DLL = framework.get_dll_file(GIT_LIB) module-attribute

Classes

PyRevitGitAuthenticationError

Bases: PyRevitException

Git authentication error.

Attributes

msg property

Return exception message.

RepoInfo(repo)

Bases: object

Repo wrapper for passing around repository information.

Attributes:

Name Type Description
directory str

repo directory

name str

repo name

head_name str

head branch name

last_commit_hash str

hash of head commit

repo str

LibGit2Sharp.Repository object

branch str

current branch name

username str

credentials - username

password str

credentials - password

Source code in pyrevitlib/pyrevit/coreutils/git.py
def __init__(self, repo):
    self.directory = repo.Info.WorkingDirectory
    self.name = op.basename(op.normpath(self.directory))
    self.head_name = repo.Head.FriendlyName
    self.last_commit_hash = repo.Head.Tip.Id.Sha
    self.repo = repo
    self.branch = repo.Head.FriendlyName
    self.username = self.password = None

Attributes

directory = repo.Info.WorkingDirectory instance-attribute
name = op.basename(op.normpath(self.directory)) instance-attribute
head_name = repo.Head.FriendlyName instance-attribute
last_commit_hash = repo.Head.Tip.Id.Sha instance-attribute
repo = repo instance-attribute
branch = repo.Head.FriendlyName instance-attribute
username = None instance-attribute
password = None instance-attribute

Functions

get_repo(repo_dir)

Return repo object for given git repo directory.

Parameters:

Name Type Description Default
repo_dir str

full path of git repo directory

required

Returns:

Type Description
RepoInfo

repo object

Source code in pyrevitlib/pyrevit/coreutils/git.py
def get_repo(repo_dir):
    """Return repo object for given git repo directory.

    Args:
        repo_dir (str): full path of git repo directory

    Returns:
        (RepoInfo): repo object
    """
    repo = libgit.Repository(repo_dir)
    return RepoInfo(repo)

git_pull(repo_info)

Pull the current head of given repo.

Parameters:

Name Type Description Default
repo_info RepoInfo

target repo object

required

Returns:

Type Description
RepoInfo

repo object with updated head

Source code in pyrevitlib/pyrevit/coreutils/git.py
def git_pull(repo_info):
    """Pull the current head of given repo.

    Args:
        repo_info (RepoInfo): target repo object

    Returns:
        (RepoInfo): repo object with updated head
    """
    repo = repo_info.repo
    try:
        libgit.Commands.Pull(repo,
                             _make_pull_signature(),
                             _make_pull_options(repo_info))

        mlogger.debug('Successfully pulled repo: %s', repo_info.directory)
        head_msg = safe_strtype(repo.Head.Tip.Message).replace('\n', '')

        mlogger.debug('New head is: %s > %s', repo.Head.Tip.Id.Sha, head_msg)
        return RepoInfo(repo)

    except Exception as pull_err:
        mlogger.debug('Failed git pull: %s | %s', repo_info.directory, pull_err)
        _process_git_error(pull_err)

git_fetch(repo_info)

Fetch current branch of given repo.

Parameters:

Name Type Description Default
repo_info RepoInfo

target repo object

required

Returns:

Type Description
RepoInfo

repo object with updated head

Source code in pyrevitlib/pyrevit/coreutils/git.py
def git_fetch(repo_info):
    """Fetch current branch of given repo.

    Args:
        repo_info (RepoInfo): target repo object

    Returns:
        (RepoInfo): repo object with updated head
    """
    repo = repo_info.repo
    try:
        libgit.Commands.Fetch(repo,
                              repo.Head.TrackedBranch.RemoteName,
                              [],
                              _make_fetch_options(repo_info),
                              'fetching pyrevit updates')

        mlogger.debug('Successfully pulled repo: %s', repo_info.directory)
        head_msg = safe_strtype(repo.Head.Tip.Message).replace('\n', '')

        mlogger.debug('New head is: %s > %s', repo.Head.Tip.Id.Sha, head_msg)
        return RepoInfo(repo)

    except Exception as fetch_err:
        mlogger.debug('Failed git fetch: %s | %s',
                      repo_info.directory, fetch_err)
        _process_git_error(fetch_err)

git_clone(repo_url, clone_dir, username=None, password=None)

Clone git repository to given location.

Parameters:

Name Type Description Default
repo_url str

repo .git url

required
clone_dir str

destination path

required
username str

credentials - username

None
password str

credentials - password

None
Source code in pyrevitlib/pyrevit/coreutils/git.py
def git_clone(repo_url, clone_dir, username=None, password=None):
    """Clone git repository to given location.

    Args:
        repo_url (str): repo .git url
        clone_dir (str): destination path
        username (str): credentials - username
        password (str): credentials - password
    """
    try:
        libgit.Repository.Clone(repo_url,
                                clone_dir,
                                _make_clone_options(username=username,
                                                    password=password))

        mlogger.debug('Completed git clone: %s @ %s', repo_url, clone_dir)

    except Exception as clone_err:
        mlogger.debug('Error cloning repo: %s to %s | %s',
                      repo_url, clone_dir, clone_err)
        _process_git_error(clone_err)

compare_branch_heads(repo_info)

Compare local and remote branch heads and return ???

Parameters:

Name Type Description Default
repo_info RepoInfo

target repo object

required
Source code in pyrevitlib/pyrevit/coreutils/git.py
def compare_branch_heads(repo_info):
    """Compare local and remote branch heads and return ???

    Args:
        repo_info (RepoInfo): target repo object
    """
    # FIXME: need return type. possibly simplify
    repo = repo_info.repo
    repo_branches = repo.Branches

    mlogger.debug('Repo branches: %s', [b.FriendlyName for b in repo_branches])

    for branch in repo_branches:
        if branch.FriendlyName == repo_info.branch and not branch.IsRemote:
            try:
                if branch.TrackedBranch:
                    mlogger.debug('Comparing heads: %s of %s',
                                  branch.CanonicalName,
                                  branch.TrackedBranch.CanonicalName)

                    hist_div = repo.ObjectDatabase. \
                        CalculateHistoryDivergence(branch.Tip,
                                                   branch.TrackedBranch.Tip)
                    return hist_div
            except Exception as compare_err:
                mlogger.error('Can not compare branch %s in repo: %s | %s',
                              branch,
                              repo,
                              safe_strtype(compare_err).replace('\n', ''))
        else:
            mlogger.debug('Skipping remote branch: %s', branch.CanonicalName)

get_all_new_commits(repo_info)

Fetch and return new commits ahead of current head.

Parameters:

Name Type Description Default
repo_info RepoInfo

target repo object

required

Returns:

Type Description
OrderedDict[str, str]

ordered dict of commit hash:message

Source code in pyrevitlib/pyrevit/coreutils/git.py
def get_all_new_commits(repo_info):
    """Fetch and return new commits ahead of current head.

    Args:
        repo_info (RepoInfo): target repo object

    Returns:
        (OrderedDict[str, str]): ordered dict of commit hash:message
    """
    repo = repo_info.repo
    current_commit = repo_info.last_commit_hash

    ref_commit = repo.Lookup(libgit.ObjectId(current_commit),
                             libgit.ObjectType.Commit)

    # Let's only consider the refs that lead to this commit...
    refs = repo.Refs.ReachableFrom([ref_commit])

    # ...and create a filter that will retrieve all the commits...
    commit_filter = libgit.CommitFilter()
    commit_filter.IncludeReachableFrom = refs
    commit_filter.ExcludeReachableFrom = ref_commit
    commit_filter.SortBy = libgit.CommitSortStrategies.Time

    commits = repo.Commits.QueryBy(commit_filter)
    commitsdict = OrderedDict()
    for commit in commits:
        if commit in repo.Head.Commits \
                or commit in repo.Head.TrackedBranch.Commits:
            commitsdict[commit.Id.ToString()] = commit.MessageShort

    return commitsdict