Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More: git init #487

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open

More: git init #487

wants to merge 6 commits into from

Conversation

tony
Copy link
Member

@tony tony commented Feb 22, 2025

Changes

Cmd: Git.init improvements

This PR enhances the Git.init() method with improved parameter validation, comprehensive documentation, and extensive test coverage. Key improvements include:

  1. Enhanced Parameter Support:

    • Added support for ref_format parameter (files/reftable)
    • Added default parameter for default permissions
    • Added make_parents parameter to control directory creation
    • Expanded shared parameter to support all git-supported values
  2. Improved Type Handling:

    • Enhanced template parameter to accept both str and pathlib.Path
    • Added comprehensive validation for all parameters
    • Added proper type hints and validation for shared repository modes
  3. Documentation Improvements:

    • Added detailed parameter descriptions
    • Included comprehensive examples for each parameter
    • Added proper return type and raises documentation
    • Enhanced docstring with real-world usage examples
  4. Validation & Error Handling:

    • Added validation for template directories
    • Added branch name validation
    • Added shared repository mode validation
    • Added proper error messages for invalid inputs

Examples

# Basic repository initialization
git = Git(path="/path/to/repo")
git.init()

# Create with custom branch name
git.init(branch="main")

# Create bare repository
git.init(bare=True)

# Create shared repository with group permissions
git.init(shared="group")

# Create with custom permissions
git.init(shared="0660")

# Create with separate git directory
git.init(separate_git_dir="/path/to/git/dir")

# Create with template
git.init(template="/path/to/template")

# Create with SHA-256 object format (git >= 2.29.0)
git.init(object_format="sha256")

Testing

The PR includes comprehensive test coverage. To run the tests:

# Run all tests
pytest tests/cmd/test_git.py

# Run specific test cases
pytest tests/cmd/test_git.py::test_git_init_basic
pytest tests/cmd/test_git.py::test_git_init_shared
pytest tests/cmd/test_git.py::test_git_init_validation_errors

Validation Tests

The PR includes tests for various validation scenarios:

# Invalid template
git.init(template=123)  # Raises TypeError

# Non-existent template
git.init(template="/nonexistent")  # Raises ValueError

# Invalid object format
git.init(object_format="invalid")  # Raises ValueError

# Invalid branch name
git.init(branch="main branch")  # Raises ValueError

# Invalid shared value
git.init(shared="invalid")  # Raises ValueError

Breaking Changes

None. All changes are backward compatible while adding new functionality.

Dependencies

  • No new dependencies required
  • Some features (like ref_format="reftable") require git >= 2.37.0
  • SHA-256 object format requires git >= 2.29.0

Summary by Sourcery

Enhances the Git.init() method with improved parameter validation, comprehensive documentation, and extensive test coverage. It introduces new features such as support for ref_format, default, and make_parents parameters, and enhances existing parameters like template and shared with improved type handling and validation.

New Features:

  • Adds support for the ref_format parameter to specify the reference storage format.
  • Adds support for the default parameter to use default permissions for directories and files.
  • Adds support for the make_parents parameter to create parent directories if they don't exist.

Enhancements:

  • Enhances the template parameter to accept both str and pathlib.Path types.
  • Expands the shared parameter to support all git-supported values, including boolean, string literals, and octal number strings.
  • Improves parameter validation for template, object_format, branch, and shared parameters.
  • Adds type hints and validation for shared repository modes.

Tests:

  • Adds comprehensive test coverage for the git init command, including tests for various parameters and validation scenarios.

Copy link

sourcery-ai bot commented Feb 22, 2025

Reviewer's Guide by Sourcery

This PR enhances the Git.init() method with improved parameter validation, comprehensive documentation, and extensive test coverage. It introduces new parameters like ref_format, default, and make_parents, expands the functionality of the shared parameter, and improves type handling for the template parameter. The changes also include comprehensive validation and error handling, along with detailed documentation and examples.

No diagrams generated as the changes look simple and do not need a visual representation.

File-Level Changes

Change Details Files
Enhanced the git init method with improved parameter validation, comprehensive documentation, and extensive test coverage.
  • Added support for ref_format parameter
  • Added default parameter for default permissions
  • Added make_parents parameter to control directory creation
  • Expanded shared parameter to support all git-supported values
  • Enhanced template parameter to accept both str and pathlib.Path
  • Added comprehensive validation for all parameters
  • Added proper type hints and validation for shared repository modes
  • Added detailed parameter descriptions
  • Included comprehensive examples for each parameter
  • Added proper return type and raises documentation
  • Enhanced docstring with real-world usage examples
  • Added validation for template directories
  • Added branch name validation
  • Added shared repository mode validation
  • Added proper error messages for invalid inputs
tests/cmd/test_git.py
src/libvcs/cmd/git.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!
  • Generate a plan of action for an issue: Comment @sourcery-ai plan on
    an issue to generate a plan of action for it.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

codecov bot commented Feb 22, 2025

Codecov Report

Attention: Patch coverage is 95.29412% with 8 lines in your changes missing coverage. Please review.

Project coverage is 56.34%. Comparing base (e3f8bee) to head (03acb4c).

Files with missing lines Patch % Lines
src/libvcs/cmd/git.py 90.47% 2 Missing and 2 partials ⚠️
tests/cmd/test_git.py 96.87% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #487      +/-   ##
==========================================
+ Coverage   54.09%   56.34%   +2.25%     
==========================================
  Files          40       40              
  Lines        3627     3789     +162     
  Branches      793      807      +14     
==========================================
+ Hits         1962     2135     +173     
+ Misses       1314     1301      -13     
- Partials      351      353       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @tony - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Consider adding a helper function to reduce the duplication in the validation logic for the shared parameter.
  • The initial_branch parameter is an alias for branch, but it's not clear from the code that they are interchangeable - consider explicitly setting branch = initial_branch.
Here's what I looked at during the review
  • 🟡 General issues: 2 issues found
  • 🟢 Security: all looks good
  • 🟡 Testing: 2 issues found
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +1221 to +1230
if shared is not None:
valid_shared_values = {
"false",
"true",
"umask",
"group",
"all",
"world",
"everybody",
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Boolean shared value check appends flag even for false.

The updated code checks if shared is an instance of bool and then unconditionally appends "--shared". This means that if shared is False (explicitly turning off sharing), the flag will still be added. To maintain behavior consistent with earlier logic—where the flag was only added for True—it would be better to explicitly check that shared is True (e.g. using if shared is True instead).

"or a valid octal number between 0000 and 0777"
)
raise ValueError(msg)
local_flags.append(f"--shared={shared}")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Consistency in handling non-boolean shared values.

After converting the non-boolean shared value to lower case (stored in shared_str) for validation, the flag is appended using the original shared variable rather than the normalized one. Consider using the lowercased value in constructing the flag to ensure consistent representation of the flag value.

Suggested change
local_flags.append(f"--shared={shared}")
local_flags.append(f"--shared={shared_str}")

result = repo.init(bare=True)
assert "Initialized empty Git repository" in result
# Bare repos have files directly in the directory
assert (tmp_path / "HEAD").exists()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Missing assertion for the --bare flag's effect on config.

While the existence of HEAD is a good indicator, it's not definitive proof of a bare repository. A stronger assertion would be to check the core.bare config setting within the .git directory. This ensures the repository is truly configured as bare.

Suggested change
assert (tmp_path / "HEAD").exists()
assert (tmp_path / "HEAD").exists()
config_path = tmp_path / "config"
assert config_path.exists(), "Config file does not exist in bare repository"
config_text = config_path.read_text()
assert "bare = true" in config_text, "Repository core.bare flag not set to true"

Comment on lines +115 to +126
# Note: sha256 test is commented out as it might not be supported in all
# git versions
# repo_dir = tmp_path / "sha256"
# repo_dir.mkdir()
# repo = git.Git(path=repo_dir)
# result = repo.init(object_format="sha256")
# assert "Initialized empty Git repository" in result
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Handle potential unsupported sha256.

Instead of commenting out the sha256 test, use pytest.mark.skipif to conditionally skip the test if the current Git version doesn't support sha256. This provides a cleaner way to handle unsupported features and keeps the test suite more comprehensive.

Suggested implementation:

import pathlib
import git
import pytest
import subprocess

def supports_sha256() -> bool:
    try:
        output = subprocess.check_output(["git", "--version"], universal_newlines=True)
        version_str = output.strip().split()[2]
        major, minor, *_ = map(int, version_str.split('.'))
        # SHA256 support is assumed for Git versions 2.29 and above.
        return (major, minor) >= (2, 29)
    except Exception:
        return False
def test_git_init_object_format(tmp_path: pathlib.Path) -> None:
    """Test git init with different object formats."""
    repo = git.Git(path=tmp_path)

    # Test with sha1 (default)
    result = repo.init(object_format="sha1")
    assert "Initialized empty Git repository" in result

    assert repo.path == tmp_path
@pytest.mark.skipif(not supports_sha256(), reason="sha256 not supported by current git version")
def test_git_init_object_format_sha256(tmp_path: pathlib.Path) -> None:
    """Test git init with sha256 object format."""
    repo_dir = tmp_path / "sha256"
    repo_dir.mkdir()
    repo = git.Git(path=repo_dir)
    result = repo.init(object_format="sha256")
    assert "Initialized empty Git repository" in result

Make sure that the new helper function supports_sha256() and the import for pytest and subprocess do not duplicate existing imports in your project. Adjust the version threshold if your requirements are different.

@@ -1026,80 +1027,243 @@ def pull(
def init(
self,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider extracting parameter validation logic into dedicated helper functions to reduce nesting and improve readability.

Consider extracting parameter validation logic into dedicated helper functions to simplify the nesting. For example, you can extract the template and shared validations. This will make the init method shorter and easier to read without changing its behavior.

Example for extracting template validation:

def _validate_template(template: str | pathlib.Path) -> str:
    if not isinstance(template, (str, pathlib.Path)):
        raise TypeError("template must be a string or Path")
    template_path = pathlib.Path(template)
    if not template_path.is_dir():
        raise ValueError(f"template directory does not exist: {template}")
    return f"--template={template}"

Example for extracting shared validation:

def _validate_shared(shared: bool | t.Literal["false", "true", "umask", "group", "all", "world", "everybody"] | str | None) -> str | None:
    if shared is None:
        return None
    valid_shared_values = {"false", "true", "umask", "group", "all", "world", "everybody"}
    if isinstance(shared, bool):
        return "--shared" if shared else None
    shared_str = str(shared).lower()
    if not (
        shared_str in valid_shared_values or (
            shared_str.isdigit() and len(shared_str) <= 4 and
            all(c in string.octdigits for c in shared_str) and int(shared_str, 8) <= 0o777
        )
    ):
        raise ValueError(
            f"Invalid shared value. Must be one of {valid_shared_values} or a valid octal number between 0000 and 0777"
        )
    return f"--shared={shared}"

Then, in the init method, you can simplify the code:

if template is not None:
    local_flags.append(_validate_template(template))

# ... other flags ...

if shared is not None:
    shared_flag = _validate_shared(shared)
    if shared_flag:
         local_flags.append(shared_flag)

This refactoring reduces nesting and centralizes validation logic, making it easier to maintain and test.

Comment on lines +1222 to +1230
valid_shared_values = {
"false",
"true",
"umask",
"group",
"all",
"world",
"everybody",
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): We've found these issues:

Suggested change
valid_shared_values = {
"false",
"true",
"umask",
"group",
"all",
"world",
"everybody",
}
valid_shared_values = {
"false",
"true",
"umask",
"group",
"all",
"world",
"everybody",
}


Explanation
The quality score for this function is below the quality threshold of 25%.
This score is a combination of the method length, cognitive complexity and working memory.

How can you solve this?

It might be worth refactoring this function to make it shorter and more readable.

  • Reduce the function length by extracting pieces of functionality out into
    their own functions. This is the most important thing you can do - ideally a
    function should be less than 10 lines.
  • Reduce nesting, perhaps by introducing guard clauses to return early.
  • Ensure that variables are tightly scoped, so that code using related concepts
    sits together within the function rather than being scattered.

tony added 6 commits February 22, 2025 17:21
- Add support for all git-init options (template, separate_git_dir, object_format, etc.)
- Add comprehensive tests for each option
- Fix path handling for separate_git_dir
- Fix string formatting for bytes paths
- Update docstrings with examples for all options
- Enhance Git.init docstrings with detailed parameter descriptions
- Add comprehensive examples including SHA-256 object format
- Add return value and exception documentation
- Improve type hints for shared parameter with Literal types
- Add extensive validation tests for all parameters
- Add validation for template parameter type and existence
- Add validation for object_format parameter values
- Improve type formatting for shared parameter
- Complete docstring example output
- Add ref-format parameter support for git init
- Add make_parents parameter to control directory creation
- Improve type hints and validation for template and shared parameters
- Add comprehensive tests for all shared values and octal permissions
- Add validation for octal number range in shared parameter
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant