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