Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Fix branch error handling#468

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Open
tony wants to merge3 commits intomaster
base:master
Choose a base branch
Loading
fromfix-branch-error-handling
Open

Conversation

@tony
Copy link
Member

@tonytony commentedSep 4, 2025
edited by sourcery-aibot
Loading

Implements proactive branch error detection and helpful user guidance for vcspull sync operations. When users encounter branch-related sync issues, vcspull now provides clear, actionable instructions instead of cryptic git error messages.

Problem

Users experienced confusing failures when runningvcspull sync on repositories with branch issues:

❯ vcspull sync zodfatal: ambiguous argument'docs/docs': unknown revision or path notin the working tree.fatal: invalid upstream'docs/docs'

The root cause was that libvcs handles certain git errors internally without re-raising them, so vcspull never had a chance to provide helpful guidance.

Solution

Pre-Validation Approach

Added proactive branch state checking that detects problematic conditions before attempting sync operations:

  • check_branch_state(): Validates git repository branch configuration
  • handle_branch_error(): Shows clear, actionable error messages
  • Pre-sync validation: Warns users before operations that will fail
  • Fallback handling: Still catches CommandError exceptions as backup

User Experience Improvement

Before:

fatal: invalid upstream 'docs/docs'

After:

⚠️  Error syncing 'zod': Branch issue detectedThe repository appears to be on a local branch with no remote tracking.To fix this, add a 'rev' field to your vcspull configuration:  zod:    repo: git@github.com:colinhacks/zod.git    rev: main  # or specify your desired branchAlternatively, switch to a branch that exists remotely:  cd /home/user/repos/zod  git checkout main  # or another remote branchAvailable remote branches: main, develop, v3, v4

Technical Implementation

Core Components

  1. Branch State Detection (check_branch_state)

    • Detects branches without upstream tracking
    • Validates remote branch existence
    • Handles git command failures gracefully
  2. Error Message Handler (handle_branch_error)

    • Shows repository-specific guidance
    • Lists available remote branches
    • Provides both configuration and manual fix options
  3. Integration (update_repo)

    • Pre-validation before sync attempts
    • Fallback error handling for edge cases
    • Continues sync with user warning

Error Detection Logic

The function detects problematic branch states by:

  1. Git Repository Check: Verifies we're in a valid git repository
  2. Current Branch: Gets the currently checked out branch name
  3. Upstream Tracking: Checks if branch has upstream configuration
  4. Remote Existence: Validates the branch exists on remote (origin)

Any branch without proper upstream tracking is flagged as potentially problematic, as it can cause libvcs to construct invalid remote references.

Testing

Comprehensive Test Coverage

  • test_check_branch_state(): Tests detection of problematic branches
  • test_check_branch_state_good_branch(): Verifies good branches aren't flagged
  • test_handle_branch_error(): Tests error message formatting and branch listing
  • test_handle_branch_error_no_git(): Tests graceful handling when git unavailable
  • test_update_repo_branch_error_precheck(): Tests pre-validation integration
  • test_update_repo_branch_error_fallback(): Tests CommandError fallback handling

Real-World Validation

Tested with actual repository experiencing the issue:

❯ uv run vcspull sync zod⚠️  Error syncing'zod': Branch issue detected[...helpful guidance shown...]

Quality Assurance

  • ✅ All 64 tests pass
  • ✅ Code passes ruff linting and formatting
  • ✅ Code passes mypy type checking
  • ✅ Follows project conventions (NumPy docstrings, proper imports, etc.)

Configuration Example

Users can now easily fix branch issues by adding arev field:

~/study/typescript/:zod:repo:git@github.com:colinhacks/zod.gitrev:main# Specify desired branch

Backwards Compatibility

  • No breaking changes to existing functionality
  • Only adds helpful guidance for error cases
  • Gracefully handles edge cases (git unavailable, etc.)
  • Maintains existing sync behavior

Files Changed

  • src/vcspull/cli/sync.py: Added branch detection and error handling
  • tests/test_sync.py: Added comprehensive test coverage

Benefits

  1. Better User Experience: Clear guidance instead of cryptic errors
  2. Proactive Detection: Issues caught before failed operations
  3. Educational: Users learn proper vcspull configuration
  4. Robust: Handles edge cases and git command failures
  5. Maintainable: Well-tested with comprehensive coverage

Summary by Sourcery

Improve vcspull sync error handling by proactively detecting branch configuration issues and providing actionable user guidance, integrated into the sync workflow with thorough test coverage.

New Features:

  • Introduce proactive branch state checking to detect missing upstream tracking or absent remote branches before sync operations
  • Add a user-friendly error handler that displays clear guidance and available remote branches when branch issues are detected
  • Integrate pre-sync validation and fallback error handling for branch-related git errors in the update_repo workflow

Enhancements:

  • Define a reusable branch error message template for consistent user guidance

Tests:

  • Extend test suite with comprehensive tests for branch state detection, error handling output, and sync integration scenarios

…ser guidancewhy: When vcspull encounters branch-related errors (like local branches with noremote tracking), it fails with cryptic git error messages that don't help usersunderstand how to fix their configuration.what:- Add BRANCH_ERROR_MSG constant with clear, actionable error template- Add handle_branch_error() function to detect available remote branches- Enhance update_repo() to catch CommandError exceptions for branch issues- Show helpful instructions for adding 'rev' field to configuration- Display available remote branches when git is accessible- Gracefully handle cases where git commands failrefs: Addresses issue where 'vcspull sync zod' failed with "invalid upstream" errors
why: Need to ensure branch error detection and user guidance works correctlyacross different scenarios and edge cases.what:- Add test_handle_branch_error() to verify error message content and branch detection- Add test_handle_branch_error_no_git() to test graceful handling when git fails- Add test_update_repo_branch_error() for integration testing of error flow- Mock subprocess.run and GitSync.update_repo for controlled testing- Test error message formatting and available branch display- Verify CommandError exceptions are properly caught and re-raisedrefs: Tests for branch error handling feature implementation
…t all upstream issueswhy: The initial implementation only flagged branches that didn't exist on remote,but missed the more common case where branches exist remotely but lack upstreamtracking, which still causes libvcs to construct invalid remote references.what:- Update check_branch_state() to flag ANY branch without upstream tracking- This catches both cases: branches that don't exist remotely AND branches  that exist remotely but have no upstream configured- Add comprehensive tests for both problematic and good branch states- Fix linting issues and improve code formatting- Test validates the fix works with real repository (zod) showing proper  error detection and user guidancerefs: Fixes the root cause where vcspull sync would fail silently on brancheswith upstream tracking issues
@sourcery-ai
Copy link

sourcery-aibot commentedSep 4, 2025
edited
Loading

Reviewer's Guide

Adds proactive branch error detection and user-friendly guidance for branch-related failures in vcspull sync operations, with pre-sync validation, fallback handling, and comprehensive tests.

Class diagram for new branch error handling functions

classDiagram    class SyncHandler {        +check_branch_state(repo_dict: dict) bool        +handle_branch_error(repo_dict: dict) void        +update_repo(repo_dict: dict, ...) Repo    }    SyncHandler <|.. check_branch_state    SyncHandler <|.. handle_branch_error    SyncHandler <|.. update_repo
Loading

File-Level Changes

ChangeDetailsFiles
Introduce proactive branch state validation and error handling
  • Define check_branch_state to detect problematic branch configurations
  • Implement handle_branch_error with clear user guidance and branch listing
  • Add BRANCH_ERROR_MSG template for consistent formatting
  • Integrate pre-sync branch check into update_repo
  • Add fallback handling for CommandError with branch-specific checks
src/vcspull/cli/sync.py
Add comprehensive tests for branch detection and error messaging
  • Create tests for check_branch_state in both problematic and good branch scenarios
  • Write tests for handle_branch_error output with and without git availability
  • Test pre-validation branch error handling in update_repo
  • Test fallback error handling in update_repo raising CommandError
tests/test_sync.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!

Customizing Your Experience

Access yourdashboard 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

@codecov
Copy link

codecovbot commentedSep 4, 2025

Codecov Report

❌ Patch coverage is76.36364% with13 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.58%. Comparing base (3c366f4) to head (32178c8).

Files with missing linesPatch %Lines
src/vcspull/cli/sync.py76.36%7 Missing and 6 partials⚠️
Additional details and impacted files
@@            Coverage Diff             @@##           master     #468      +/-   ##==========================================- Coverage   78.93%   78.58%   -0.35%==========================================  Files           8        8                Lines         413      467      +54       Branches       85       95      +10     ==========================================+ Hits          326      367      +41- Misses         52       59       +7- Partials       35       41       +6

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

🚀 New features to boost your workflow:
  • ❄️Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@sourcery-aisourcery-aibot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes and they look great!

Blocking issues:

  • Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'. (link)
Prompt for AI Agents
Please address the comments from this code review:##Individual Comments###Comment 1<location>`src/vcspull/cli/sync.py:225` </location><code_context>+       # Even if the remote branch exists, the lack of upstream tracking+       # can cause libvcs to construct invalid remote references+       # Check if there's a remote branch with the same name+       remote_ref = f"origin/{branch_name}"+       remote_branch = subprocess.run(+           ["git", "-C", str(repo_path), "rev-parse", "--verify", remote_ref],+           capture_output=True,</code_context><issue_to_address>Assumes 'origin' as remote; may not be correct for all repos.This approach may fail in repositories where the remote is not named 'origin'. Allow configuring the remote name or detect it automatically.Suggested implementation:```python# Even if the remote branch exists, the lack of upstream tracking# can cause libvcs to construct invalid remote references# Check if there's a remote branch with the same name# Detect remote name automatically            remote_name_proc= subprocess.run(                ["git","-C",str(repo_path),"remote"],capture_output=True,text=True,timeout=5,check=False,            )            remote_name="origin"if remote_name_proc.returncode==0:                remotes= remote_name_proc.stdout.strip().splitlines()if remotes:                    remote_name= remotes[0]# Use the first remote found            remote_ref=f"{remote_name}/{branch_name}"            remote_branch= subprocess.run(                ["git","-C",str(repo_path),"rev-parse","--verify", remote_ref],capture_output=True,```If you want to allow the remote name to be configured (e.g., via a function argumentor environment variable), you should:1. Add a parameter to the relevant function to accept`remote_name`, defaulting toNone.2. If`remote_name`is provided, use it; otherwise, detectas above.3. Optionally, checkfor an environment variable (e.g.,`VCS_REMOTE_NAME`) before falling back to detection.You may need to propagate this parameter through function callsor configuration.</issue_to_address>### Comment 2<location>`src/vcspull/cli/sync.py:246`</location><code_context>+returnFalse# Git command failed or not available+++def handle_branch_error(repo_dict: dict[str, t.Any])->None:+"""Handle branch-related errors by showing helpful instructions.+</code_context><issue_to_address>Consider logging errors instead of only printing to stderr.Using the logger will improve error traceability and make integration with monitoring tools easier.Suggested implementation:```pythonimport loggingimport subprocessimport sysimport typing as tfrom copy import deepcopylogger = logging.getLogger(__name__)``````pythondef handle_branch_error(repo_dict: dict[str, t.Any]) -> None:"""Handle branch-related errors by showing helpful instructions.    logger.error("Branch-related error detected for repository:%s\n""Please check your branch configuration and ensure upstream tracking is set correctly.",        repo_dict.get("name","<unknown>")    )```</issue_to_address>### Comment 3<location>`tests/test_sync.py:322`</location><code_context>+def test_check_branch_state(tmp_path: pathlib.Path)->None:</code_context><issue_to_address>Consider adding testsfor detachedHEADand missing repo path edge cases.Tests currently cover typical branch states butnot detachedHEADor missing'path'in repo_dict. Including these cases will improve coverageand error handling.</issue_to_address>### Comment 4<location>`tests/test_sync.py:411`</location><code_context>+def test_handle_branch_error(</code_context><issue_to_address>Test only checks first5 branches; consider parametrizingfor different branch counts.Parametrize the test to cover scenarioswith fewer, exactly,and more than5 branches to improve coverage.Suggested implementation:```pythonimport pytest@pytest.mark.parametrize("branches",    [        ["main","dev"],# fewer than 5        ["main","dev","feature1","feature2","feature3"],# exactly 5        ["main","dev","feature1","feature2","feature3","hotfix"],# more than 5    ],)def test_handle_branch_error(    branches,    tmp_path: pathlib.Path,    capsys: pytest.CaptureFixture[str])->None:"""Test that handle_branch_error shows helpful error message for different branch counts."""repo_dict= {"name":"test-repo","url":"git@github.com:user/repo.git","path": tmp_path/"test-repo",    }# Create a mock git directory for testing# Simulate branches in the repo# You may need to adjust the following lines to fit your repo setup logicrepo_path= repo_dict["path"]    repo_path.mkdir(parents=True,exist_ok=True)    (repo_path/".git").mkdir()# Simulate branch list# If your code uses subprocess to get branches, you may need to patch subprocess.run# For now, we just show how to use the branches variable in the test# Example: pass branches to the function or mock the function that lists branches# ... rest of your test logic here, using the branches variable ...```You will need to adjust the test logic to use the`branches` variable, depending on how your code fetches branch names (e.g., patching subprocessor passing directly). If you use subprocess to get branches, consider patching it toreturn the`branches`listfor each test case.</issue_to_address>## Security Issues### Issue 1<location>`src/vcspull/cli/sync.py:262`</location><issue_to_address>**security (python.lang.security.audit.dangerous-subprocess-use-audit):** Detected subprocess function'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure itisnot controllable by an external resource. You may consider using'shlex.escape()'.*Source: opengrep*</issue_to_address>

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.

sourcery-ai[bot] reacted with thumbs up emojisourcery-ai[bot] reacted with thumbs down emoji
Comment on lines +225 to +226
remote_ref=f"origin/{branch_name}"
remote_branch=subprocess.run(

Choose a reason for hiding this comment

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

suggestion: Assumes 'origin' as remote; may not be correct for all repos.

This approach may fail in repositories where the remote is not named 'origin'. Allow configuring the remote name or detect it automatically.

Suggested implementation:

# Even if the remote branch exists, the lack of upstream tracking# can cause libvcs to construct invalid remote references# Check if there's a remote branch with the same name# Detect remote name automaticallyremote_name_proc=subprocess.run(                ["git","-C",str(repo_path),"remote"],capture_output=True,text=True,timeout=5,check=False,            )remote_name="origin"ifremote_name_proc.returncode==0:remotes=remote_name_proc.stdout.strip().splitlines()ifremotes:remote_name=remotes[0]# Use the first remote foundremote_ref=f"{remote_name}/{branch_name}"remote_branch=subprocess.run(                ["git","-C",str(repo_path),"rev-parse","--verify",remote_ref],capture_output=True,

If you want to allow the remote name to be configured (e.g., via a function argument or environment variable), you should:

  1. Add a parameter to the relevant function to acceptremote_name, defaulting to None.
  2. Ifremote_name is provided, use it; otherwise, detect as above.
  3. Optionally, check for an environment variable (e.g.,VCS_REMOTE_NAME) before falling back to detection.

You may need to propagate this parameter through function calls or configuration.

sourcery-ai[bot] reacted with thumbs up emojisourcery-ai[bot] reacted with thumbs down emoji
returnFalse# Git command failed or not available


defhandle_branch_error(repo_dict:dict[str,t.Any])->None:

Choose a reason for hiding this comment

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

suggestion: Consider logging errors instead of only printing to stderr.

Using the logger will improve error traceability and make integration with monitoring tools easier.

Suggested implementation:

importloggingimportsubprocessimportsysimporttypingastfromcopyimportdeepcopylogger=logging.getLogger(__name__)
defhandle_branch_error(repo_dict:dict[str,t.Any])->None:    """Handle branch-related errors by showing helpful instructions.    logger.error(        "Branch-related error detected for repository: %s\n"        "Please check your branch configuration and ensure upstream tracking is set correctly.",        repo_dict.get("name", "<unknown>")    )

sourcery-ai[bot] reacted with thumbs up emojisourcery-ai[bot] reacted with thumbs down emoji
Comment on lines +322 to +331
deftest_check_branch_state(tmp_path:pathlib.Path)->None:
"""Test that check_branch_state detects problematic branches."""
repo_dict= {
"name":"test-repo",
"url":"git@github.com:user/repo.git",
"path":tmp_path/"test-repo",
}

# Create a mock git directory for testing
(tmp_path/"test-repo"/".git").mkdir(parents=True,exist_ok=True)

Choose a reason for hiding this comment

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

suggestion (testing): Consider adding tests for detached HEAD and missing repo path edge cases.

Tests currently cover typical branch states but not detached HEAD or missing 'path' in repo_dict. Including these cases will improve coverage and error handling.

sourcery-ai[bot] reacted with thumbs up emojisourcery-ai[bot] reacted with thumbs down emoji
Comment on lines +411 to +420
deftest_handle_branch_error(
tmp_path:pathlib.Path,capsys:pytest.CaptureFixture[str]
)->None:
"""Test that handle_branch_error shows helpful error message."""
repo_dict= {
"name":"test-repo",
"url":"git@github.com:user/repo.git",
"path":tmp_path/"test-repo",
}

Choose a reason for hiding this comment

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

suggestion (testing): Test only checks first 5 branches; consider parametrizing for different branch counts.

Parametrize the test to cover scenarios with fewer, exactly, and more than 5 branches to improve coverage.

Suggested implementation:

importpytest@pytest.mark.parametrize("branches",    [        ["main","dev"],# fewer than 5        ["main","dev","feature1","feature2","feature3"],# exactly 5        ["main","dev","feature1","feature2","feature3","hotfix"],# more than 5    ],)deftest_handle_branch_error(branches,tmp_path:pathlib.Path,capsys:pytest.CaptureFixture[str])->None:"""Test that handle_branch_error shows helpful error message for different branch counts."""repo_dict= {"name":"test-repo","url":"git@github.com:user/repo.git","path":tmp_path/"test-repo",    }# Create a mock git directory for testing# Simulate branches in the repo# You may need to adjust the following lines to fit your repo setup logicrepo_path=repo_dict["path"]repo_path.mkdir(parents=True,exist_ok=True)    (repo_path/".git").mkdir()# Simulate branch list# If your code uses subprocess to get branches, you may need to patch subprocess.run# For now, we just show how to use the branches variable in the test# Example: pass branches to the function or mock the function that lists branches# ... rest of your test logic here, using the branches variable ...

You will need to adjust the test logic to use thebranches variable, depending on how your code fetches branch names (e.g., patching subprocess or passing directly). If you use subprocess to get branches, consider patching it to return thebranches list for each test case.

sourcery-ai[bot] reacted with thumbs up emojisourcery-ai[bot] reacted with thumbs down emoji
Comment on lines +262 to +264
result=subprocess.run(
cmd,capture_output=True,text=True,timeout=5,check=False
)

Choose a reason for hiding this comment

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

security (python.lang.security.audit.dangerous-subprocess-use-audit): Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

Source: opengrep

sourcery-ai[bot] reacted with thumbs up emojisourcery-ai[bot] reacted with thumbs down emoji
returnFalse

try:
# Check if we're in a git repository

Choose a reason for hiding this comment

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

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

sourcery-ai[bot] reacted with thumbs up emojisourcery-ai[bot] reacted with thumbs down emoji
Comment on lines +266 to +270
branches= [
line.strip().replace("origin/","")
forlineinresult.stdout.splitlines()
if"origin/"inlineand"->"notinline
][:5]# Show first 5 branches

Choose a reason for hiding this comment

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

issue (code-quality): Use named expression to simplify assignment and conditional (use-named-expression)

sourcery-ai[bot] reacted with thumbs up emojisourcery-ai[bot] reacted with thumbs down emoji
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

1 more reviewer

@sourcery-aisourcery-ai[bot]sourcery-ai[bot] requested changes

Reviewers whose approvals may not affect merge requirements

At least 1 approving review is required to merge this pull request.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

2 participants

@tony

[8]ページ先頭

©2009-2025 Movatter.jp