How to Mock Git Stash and Pop in Python

Mock Git stash, apply, and pop operations using unittest.mock so you can test Git automation without touching a real repository.

Medium Python 3.9+ Aug 9, 2026 Git + Python 14 views 0 copies

Requires third-party packages — install first
pip install GitPython

Python code

28 lines
Python 3.9+
import git
from unittest.mock import Mock, patch

def stash_and_pop(repo):
    """Mock a stash operation and then pop it back."""
    repo.git.stash("save", "WIP: temp changes")
    stashed_output = repo.git.stash("list")
    
    # Simulate the stash was applied, then pop
    repo.git.stash("apply", "stash@{0}")
    repo.git.stash("pop", "stash@{0}")
    
    return stashed_output

if __name__ == "__main__":
    # Create a fake repo with mocked git command
    fake_repo = Mock()
    fake_repo.git.stash.side_effect = ["Saved working directory", "stash@{0}: On main: WIP: temp changes", "Applied", "Dropped"]
    
    # Patch a real Repo object to return the fake
    with patch("git.Repo", return_value=fake_repo):
        repo = git.Repo("/fake/path")
        result = stash_and_pop(repo)
    
    print("Stash operation result:", result)
    
    # Verify the sequence of calls
    print("Calls made:", [str(call) for call in fake_repo.git.stash.call_args_list])

Output

stdout
Stash operation result: stash@{0}: On main: WIP: temp changes
Calls made: [call('save', 'WIP: temp changes'), call('list'), call('apply', 'stash@{0}'), call('pop', 'stash@{0}')]

How it works

The Mock() object replaces a real GitPython Repo instance, and side_effect queues up fake return values for successive stash calls. Patching git.Repo with patch() makes git.Repo('/fake/path') return the mock, so stash_and_pop runs against a controlled fake. The function invokes stash save, stash list, stash apply, and stash pop in sequence; the mock records each call so you can assert the exact Git commands issued. This pattern isolates your Git logic from the filesystem, making tests fast, deterministic, and safe.

Common mistakes

  • Calling `repo.git.stash` without mocking, which raises `GitCommandNotFound` or touches real repos.
  • Forgetting `side_effect` must supply one value per call — too few values raises `StopIteration`.
  • Using `return_value` instead of `side_effect` so every call returns the same string, hiding call order.
  • Patching `git.Repo` globally instead of using a context manager, leaking the mock into other tests.

Variations

  1. Use `patch.object(repo, 'git')` and set `repo.git.stash.side_effect` directly for finer control.
  2. Capture calls with `fake_repo.git.stash.assert_has_calls([...])` to verify exact call order.

Real-world use cases

  • Unit-testing a script that auto-stashes uncommitted work before pulling in a CI job.
  • Validating a tool that applies then pops a stash after generating code changes, without polluting the repo.
  • Simulating stash recovery logic in a Git maintenance script so edge cases are covered offline.

Sponsored

Run locally

This sample needs third-party packages, so it cannot run in the browser IDE. Copy the code above, install the packages shown at the top, then run it in your own Python environment.

More from Git + Python

Related tutorials and quizzes for this topic.