question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Inspecting the index using GitPython

See original GitHub issue

describe your issue

I am trying to write a new hook that checks that a .meta file exists for every file under the Assets folder (Unity specific check), I have working code that uses GitPython==3.1.24 to inspect the Git index. When I run this code directly it works as expected, however when I run it via pre-commit (as a hook or try repo) the index check fails (meta_file_in_index).

It looks like although it’s finding the correct git repo, the index is empty, I’ve tried several things to figure out what is happening but I’ve not had any luck. Any advice would be apprecaited

Hook Code

import os
import sys

from git import Repo, IndexFile, DiffIndex


def filter_assets(path: str):
    lower_case = path.lower()
    return lower_case.startswith("assets")


def path_to_meta_file(path: str):
    return path + ".meta"

def meta_file_exists(meta_file: str):
    return os.path.isfile(meta_file)

def meta_file_in_index(meta_file: str, index: IndexFile):
    return any(x for x in index.entries.keys() if x[0] == meta_file)

def meta_file_in_diff(meta_file: str, diff: DiffIndex):
    return any(x for x in diff if x.a_path == meta_file)


def run():
    files = [path for path in sys.argv[1:] if filter_assets(path)]
    repo = Repo(os.getcwd())
    index = repo.index
    modified = index.diff(None)
    errors = []
    for file in files:
        meta_file = path_to_meta_file(file)
        if not meta_file_exists(meta_file):
            errors.append(f"{file} does not have a matching meta file {meta_file}")
            continue

        if not meta_file_in_index(meta_file, index):
            errors.append(f"{meta_file} is not in the git index")
            continue

        if meta_file_in_diff(meta_file, modified):
            errors.append(f"{meta_file} has been changed but is not staged")

    if len(errors) > 0:
        print("At least one file has an issue with it's meta file:")
        print("  * " + "\n  * ".join(errors))
        sys.exit(1)


if __name__ == "__main__":
    run()

pre-commit --version

pre-commit 2.13.0

.pre-commit-config.yaml

---
- id: meta_check
  name: Check meta files are in the Git index
  entry: meta_check
  exclude: ".meta$"
  language: python

~/.cache/pre-commit/pre-commit.log (if present)

No response

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:9 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
robin-mosscommented, Jan 7, 2022

@guykisel sadly this is being hosted internally at my company, a new version of GitPython has been released that fixed the issue 🎉 and based on your comment I’ve added the 4th check too.

The new code is below (post pylint, pydocstyle, mypy, isort, etc):

"""
Unity meta file checker.

checks is the meta file exists, is in the git index and is committed, vice versa it also checks that a meta file as a
associated asset file/folder.
"""
import os
import sys
from typing import Any

from git.diff import DiffIndex
from git.index.base import IndexFile
from git.repo.base import Repo


def filter_assets(path: str) -> bool:
    """
    Check if the path an asset and not a meta file.

    :param path: Path to check.
    """
    lower_case = path.lower()
    return lower_case.startswith("assets") and not lower_case.endswith("meta")


def filter_meta(path: str) -> bool:
    """
    Check if the path to a meta file.

    :param path: Path to check.
    """
    lower_case = path.lower()
    return lower_case.startswith("assets") and lower_case.endswith("meta")


def path_to_meta_file(path: str) -> str:
    """
    Covert the given asset path to a meta file path.

    :param path: Path to change.
    """
    return path + ".meta"


def meta_file_exists(meta_file: str) -> bool:
    """
    Check the meta file exist on the filesystem.

    :param meta_file: Meta file to check for.
    """
    return os.path.isfile(meta_file)


def meta_file_in_index(meta_file: str, index: IndexFile) -> bool:
    """
    Is the meta file in the Git index.

    :param meta_file: Meta file to check for.
    :param index: Git index to check.
    """
    return any(x for x in index.entries.keys() if x[0] == meta_file)


def meta_file_in_diff(meta_file: str, diff: DiffIndex[Any]) -> bool:
    """
    Check if there has been an un committed change to the meta file.

    :param meta_file: Meta file to check for.
    :param diff: Git diff to check.
    """
    return any(x for x in diff if x.a_path == meta_file)


def file_or_folder_exists(path: str) -> bool:
    """
    Check a file or folder exist at the given path.

    :param path: Path to check.
    """
    return os.path.exists(path)


def run() -> None:
    """Run the Unity meta check."""
    asset_files = [path for path in sys.argv[1:] if filter_assets(path)]
    meta_files = [path for path in sys.argv[1:] if filter_meta(path)]
    repo = Repo(os.getcwd())
    index = repo.index
    modified = index.diff(None)
    errors = []
    for file in asset_files:
        meta_file = path_to_meta_file(file)
        if not meta_file_exists(meta_file):
            errors.append(f"{file} does not have a matching meta file {meta_file}")
            continue

        if not meta_file_in_index(meta_file, index):
            errors.append(f"{meta_file} is not in the git index")
            continue

        if meta_file_in_diff(meta_file, modified):
            errors.append(f"{meta_file} has been changed but is not staged")

    for file in meta_files:
        asset_file = file.removesuffix(".meta")
        if not file_or_folder_exists(asset_file):
            errors.append(f"{file} exists but no asset file exists for it")

    if len(errors) > 0:
        print("  * " + "\n  * ".join(errors))
        sys.exit(1)


if __name__ == "__main__":
    run()
1reaction
asottilecommented, Jan 4, 2022

nothing, it should be identical to you running yourthing filename filename filename – in fact you could stick that in .git/hooks/pre-commit if you want to be in exactly the same state as git would call you

Read more comments on GitHub >

github_iconTop Results From Across the Web

GitPython Tutorial — GitPython 3.1.29 documentation
Create new indices from other trees or as result of a merge. Write that result to a new index file for later inspection....
Read more >
Checking if an object is in a repo in gitpython - Stack Overflow
EricP's answer has a bug. Here's a fixed version: def fileInRepo(repo, filePath): ''' repo is a gitPython Repo object filePath is the full...
Read more >
Getting Started with GitPython - AZZAMSA
A gentle introduction with a brief code example to jump into GitPython.
Read more >
GitPython - The Blue Book
GitPython is a python library used to interact with git repositories, ... Inspect it with the repo.head.reference.log() , which contains a list of ......
Read more >
GitPython Documentation - Read the Docs
GitPython is a python library used to interact with git repositories, ... Write that result to a new index file for later inspection....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found