Module 2: Understanding Git's Three Areas: Working, Staging, and Repository
Master the fundamental workflow of Git by understanding the three core areas. Learn how test files move through Working Directory, Staging Area, and Repository. Practice selective staging to commit only relevant test changes, and understand how to inspect and manipulate files at each stage.
Selective Staging: Committing Only What Matters
Why This Matters
The Real-World Problem
You’ve just finished a productive testing session. You’ve:
- Fixed three failing integration tests
- Added debug logging to troubleshoot a flaky test (but it’s messy)
- Started experimenting with a new test framework configuration
- Updated test data files for multiple test suites
Now you need to commit your work. Do you commit everything at once? That would create a massive, confusing commit mixing bug fixes, experiments, and temporary debug code. Your team reviewer will struggle to understand what actually changed and why.
This is where selective staging becomes critical.
When You’ll Use This Skill
Daily scenarios for test engineers:
- Separating concerns: Commit your passing test fixes separately from experimental work
- Excluding temporary changes: Keep debug print statements and test data dumps out of commits
- Creating reviewable commits: Group related test changes so reviewers can understand each commit’s purpose
- Atomic test updates: Commit test code and corresponding test data together, but separately from unrelated changes
- Maintaining clean history: Build a commit history that tells the story of your test suite’s evolution
Common Pain Points Addressed
“I accidentally committed debug code to main”
- Learn to review what you’re staging before committing
- Use techniques to exclude temporary debugging changes
“My commits are too large and confusing”
- Break work into logical, reviewable chunks
- Create commits that represent single, complete ideas
“I changed multiple tests but only want to commit one”
- Stage specific files while leaving others untouched
- Even stage specific lines within a file
“I staged the wrong file and already committed”
- Understand how to inspect and manipulate the staging area
- Learn recovery techniques for staging mistakes (before pushing)
“Code reviews take forever because my commits are unclear”
- Structure commits to make reviewer’s job easier
- Each commit becomes a clear, testable unit of change
Learning Objectives
By the end of this lesson, you will be able to:
Core Competencies
-
Navigate Git’s three-tree architecture with confidence, understanding how test files transition between working directory, staging area, and repository
-
Selectively stage changes using multiple techniques:
- Stage entire files with
git add
- Stage portions of files with
git add -p
- Stage changes interactively with
git add -i
- Stage entire files with
-
Create atomic commits that group related test changes logically:
- One bug fix per commit
- Test code and test data committed together
- Feature tests separated from refactoring
-
Inspect and verify changes before committing:
- Use
git status
to understand current state - Use
git diff
to compare working directory, staging area, and repository - Preview exactly what will be committed
- Use
-
Correct staging mistakes safely:
- Unstage files without losing changes
- Modify staged content before committing
- Recover from common staging errors
Practical Outcomes
You’ll complete hands-on exercises where you:
- Fix multiple test files but commit them separately based on purpose
- Stage bug fixes while excluding debug logging
- Use interactive staging to commit only specific test assertions
- Build a clean, professional commit history that tells a clear story
- Prepare commits that will pass code review on the first try
The Result: You’ll develop the discipline and skills to create meaningful, reviewable commits—a hallmark of professional test engineers who make their teams more effective.
Core Content
Core Content: Selective Staging - Committing Only What Matters
Core Concepts Explained
Understanding the Staging Area
The staging area (also called the “index”) is Git’s middle ground between your working directory and the repository. It allows you to selectively choose which changes to include in your next commit, giving you precise control over your version history.
Why Selective Staging Matters in Test Automation:
- Separate test code changes from configuration updates
- Commit related test modifications together
- Keep debugging code out of your commits
- Maintain clean, logical commit history
Step-by-Step Workflow
-
Check Current Status
- View modified files
- See what’s staged vs. unstaged
-
Stage Specific Changes
- Add entire files
- Stage parts of files (hunks)
- Stage specific lines
-
Review Before Committing
- Verify staged changes
- Ensure only relevant modifications are included
-
Commit Staged Changes
- Write meaningful commit messages
- Leave other changes for future commits
Git Commands for Selective Staging
# View all changes
git status
# View detailed changes
git diff # Unstaged changes
git diff --staged # Staged changes
# Stage specific files
git add path/to/file.py
# Stage all files of a type
git add tests/*.py
# Stage interactively (choose what to stage)
git add -p filename # Patch mode
# Unstage files
git restore --staged filename
Practical Code Examples
Example 1: Staging Specific Test Files
Let’s say you’ve made changes to multiple test files and a configuration file, but only want to commit the login tests:
# Check what files have changed
git status
# Output might show:
# Modified: tests/test_login.py
# Modified: tests/test_checkout.py
# Modified: config/test_config.py
# Modified: README.md
# Stage only the login test
git add tests/test_login.py
# Verify what's staged
git status
# Output:
# Changes to be committed:
# modified: tests/test_login.py
#
# Changes not staged for commit:
# modified: tests/test_checkout.py
# modified: config/test_config.py
# modified: README.md
# Commit only the staged changes
git commit -m "Add password visibility toggle test for login page"
Example 2: Interactive Staging with Patch Mode
Suppose you’ve added both a new test and debug print statements in the same file:
# tests/test_form_submission.py
from selenium import webdriver
from selenium.webdriver.common.by import By
import pytest
class TestContactForm:
def test_form_submission_success(self):
"""Test successful form submission on practiceautomatedtesting.com"""
driver = webdriver.Chrome()
driver.get("https://practiceautomatedtesting.com/contact")
# Fill form fields
driver.find_element(By.ID, "name").send_keys("John Doe")
driver.find_element(By.ID, "email").send_keys("john@example.com")
driver.find_element(By.ID, "message").send_keys("Test message")
print("DEBUG: Form filled") # Debug statement - don't commit
# Submit form
driver.find_element(By.ID, "submit").click()
# Verify success message
success_msg = driver.find_element(By.CLASS_NAME, "success")
assert "Thank you" in success_msg.text
driver.quit()
def test_form_validation_empty_email(self):
"""Test form validation for empty email field"""
driver = webdriver.Chrome()
driver.get("https://practiceautomatedtesting.com/contact")
# Fill only name and message
driver.find_element(By.ID, "name").send_keys("John Doe")
driver.find_element(By.ID, "message").send_keys("Test message")
print("DEBUG: Checking validation") # Debug statement - don't commit
# Try to submit
driver.find_element(By.ID, "submit").click()
# Verify error appears
error_msg = driver.find_element(By.CLASS_NAME, "error")
assert "Email is required" in error_msg.text
driver.quit()
Using patch mode to exclude debug statements:
# Start interactive staging
git add -p tests/test_form_submission.py
# Git will show hunks (chunks of changes) one at a time:
# Stage this hunk [y,n,q,a,d,s,e,?]?
#
# Options:
# y - stage this hunk
# n - do not stage this hunk
# q - quit; do not stage this hunk or any remaining ones
# a - stage this hunk and all later hunks in the file
# s - split the current hunk into smaller hunks
# e - manually edit the hunk
# ? - print help
# When you see the hunk with print statements, press 'n'
# For the actual test code, press 'y'
Example 3: Staging Parts of Page Object Model Updates
# pages/login_page.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class LoginPage:
# URLs
URL = "https://practiceautomatedtesting.com/login"
# Locators
USERNAME_INPUT = (By.ID, "username")
PASSWORD_INPUT = (By.ID, "password")
LOGIN_BUTTON = (By.ID, "login-button")
ERROR_MESSAGE = (By.CLASS_NAME, "error-message")
SUCCESS_MESSAGE = (By.CLASS_NAME, "success-message") # New locator
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def navigate(self):
"""Navigate to login page"""
self.driver.get(self.URL)
print(f"DEBUG: Navigated to {self.URL}") # Debug - don't commit
def enter_username(self, username):
"""Enter username in the login form"""
element = self.wait.until(
EC.presence_of_element_located(self.USERNAME_INPUT)
)
element.clear()
element.send_keys(username)
def enter_password(self, password):
"""Enter password in the login form"""
element = self.driver.find_element(*self.PASSWORD_INPUT)
element.clear()
element.send_keys(password)
def click_login(self):
"""Click the login button"""
button = self.driver.find_element(*self.LOGIN_BUTTON)
button.click()
print("DEBUG: Login button clicked") # Debug - don't commit
def get_error_message(self):
"""Get error message text"""
element = self.wait.until(
EC.visibility_of_element_located(self.ERROR_MESSAGE)
)
return element.text
def is_login_successful(self): # New method
"""Check if login was successful"""
try:
self.wait.until(
EC.visibility_of_element_located(self.SUCCESS_MESSAGE)
)
return True
except:
return False
Selective staging workflow:
# View the changes in detail
git diff pages/login_page.py
# Use patch mode to selectively stage
git add -p pages/login_page.py
# When the new SUCCESS_MESSAGE locator appears: press 'y'
# When the new is_login_successful() method appears: press 'y'
# When debug print statements appear: press 'n'
# Verify what's staged
git diff --staged pages/login_page.py
# Commit only the production-ready code
git commit -m "Add success message verification to LoginPage POM"
Example 4: Using .gitignore and Selective Staging Together
# .gitignore file (commit this!)
# Ignore test outputs and temporary files
screenshots/
reports/
*.log
*.pyc
__pycache__/
.pytest_cache/
venv/
# config/local_settings.py (don't commit local overrides)
config/local_settings.py
Workflow:
# Modified multiple files including config
git status
# Stage only test files, not config
git add tests/
# Add the .gitignore if it's new
git add .gitignore
# Verify
git status
# Shows:
# Changes to be committed:
# new file: .gitignore
# modified: tests/test_login.py
#
# Untracked files:
# config/local_settings.py (ignored, won't be committed)
git commit -m "Add login test suite and update gitignore"
Common Mistakes Section
Mistake 1: Using git add .
Too Liberally
Problem:
# Stages EVERYTHING, including files you don't want
git add .
Why it’s wrong: You might accidentally commit:
- Debug code
- Local configuration files
- Temporary test files
- Credentials or API keys
Solution:
# Be explicit about what you stage
git add tests/test_login.py tests/test_checkout.py
# Or use gitignore for files that should never be committed
echo "local_config.py" >> .gitignore
Mistake 2: Not Reviewing Staged Changes
Problem:
git add tests/
git commit -m "Updated tests" # What exactly changed?
Why it’s wrong: You don’t know what you’re committing.
Solution:
# Always review before committing
git add tests/
git diff --staged # Review all staged changes
git status # See which files are staged
# Then commit with confidence
git commit -m "Add validation tests for email field format"
Mistake 3: Mixing Unrelated Changes in One Commit
Problem:
# In one commit: fixed bug + added new feature + updated config
git add tests/ config/ pages/
git commit -m "Various updates"
Why it’s wrong: Makes history unclear, harder to review, difficult to revert.
Solution:
# Commit 1: Bug fix
git add tests/test_login.py
git commit -m "Fix: Correct timeout in login wait condition"
# Commit 2: New feature
git add tests/test_two_factor.py pages/two_factor_page.py
git commit -m "Add two-factor authentication test suite"
# Commit 3: Config update
git add config/test_config.py
git commit -m "Update test config for staging environment"
Mistake 4: Forgetting to Unstage Files
Problem:
git add config/secrets.py # Oops! This has API keys
# How do I undo this?
Solution:
# Unstage the file before committing
git restore --staged config/secrets.py
# Or use the older syntax
git reset HEAD config/secrets.py
# Verify it's unstaged
git status
Debugging Tip: Use git status
Frequently
# Check status at each step
git status # What's changed?
git add specific_file.py
git status # What's staged?
git diff --staged # What exactly am I committing?
git commit -m "message"
git status # Clean working directory?
Pro Tip: Create Logical Commits
Good commit structure:
- One logical change per commit
- Related files committed together
- Clear, descriptive commit messages
- Easy to review and revert if needed
Example workflow:
# You've updated login tests, added screenshot capability, and fixed a typo
# Make 3 separate commits:
git add tests/test_login.py
git commit -m "Add remember-me checkbox test for login"
git add utils/screenshot_helper.py tests/conftest.py
git commit -m "Add screenshot capture on test failure"
git add README.md
git commit -m "Fix typo in installation instructions"
This approach creates a clean, maintainable Git history that your team will thank you for!
Hands-On Practice
Hands-On Exercise
Task: Clean Up a Messy Repository
You’ve been working on a test automation project and your working directory is cluttered with various files. Your task is to selectively stage and commit only the files that belong together, while ignoring temporary and generated files.
Scenario Setup
Create a new Git repository with the following files:
mkdir selective-staging-practice
cd selective-staging-practice
git init
# Create test files
mkdir -p tests/api tests/ui
echo "def test_login(): pass" > tests/api/test_auth.py
echo "def test_dashboard(): pass" > tests/ui/test_dashboard.py
# Create configuration files
echo "BASE_URL=http://localhost:8080" > .env
echo "test-report.html" > .gitignore
# Create documentation
echo "# Test Suite Documentation" > README.md
# Create temporary/generated files
echo "<html>Test Report</html>" > test-report.html
echo "compiled_code" > tests/__pycache__/cache.pyc
touch .DS_Store
Instructions
Step 1: Check Repository Status
git status
Expected outcome: You should see multiple untracked files.
Step 2: Stage Only Test Files
Use selective staging to add only the Python test files:
git add tests/*.py tests/**/*.py
# or
git add tests/api/test_auth.py tests/ui/test_dashboard.py
Check your staging area:
git status
Step 3: Commit Test Files
git commit -m "Add initial API and UI test files"
Step 4: Stage Configuration Files
Now stage only the configuration-related files:
git add .gitignore README.md
git status
Step 5: Review Staged Changes
Before committing, review what you’re about to commit:
git diff --staged
Step 6: Commit Configuration
git commit -m "Add project documentation and gitignore"
Step 7: Verify Clean Separation
Check your commit history:
git log --oneline
git show HEAD~1 # View first commit
git show HEAD # View second commit
Step 8: Handle Remaining Files
Check what’s left:
git status
Expected outcome: You should still see .env
, test-report.html
, .DS_Store
, and cache files as untracked. These should NOT be committed.
Update your .gitignore
:
echo ".env" >> .gitignore
echo "*.pyc" >> .gitignore
echo "__pycache__/" >> .gitignore
echo ".DS_Store" >> .gitignore
Stage and commit the updated .gitignore
:
git add .gitignore
git commit -m "Update gitignore for environment and temp files"
Challenge Exercise
Make modifications to multiple files and practice selective staging:
# Modify multiple files
echo "def test_logout(): pass" >> tests/api/test_auth.py
echo "def test_profile(): pass" >> tests/ui/test_dashboard.py
echo "Updated documentation" >> README.md
# Use interactive staging to commit only test changes
git add -p tests/
# Review and commit
git commit -m "Add logout and profile tests"
# Then stage and commit documentation separately
git add README.md
git commit -m "Update documentation"
Expected Final Outcome
Your repository should have:
- 4 commits with clear, focused purposes
- Clean working directory (or only untracked files that should be ignored)
- Logical separation between test code, configuration, and documentation
- Proper
.gitignore
preventing sensitive/temporary files from being tracked
Verify with:
git log --oneline --graph
Key Takeaways
-
Selective staging enables focused commits – By staging only related changes together, each commit tells a clear story and serves a single purpose, making code reviews and debugging much easier.
-
Use pattern matching and interactive mode strategically –
git add
with wildcards (e.g.,*.py
), directory paths, or-p
flag for interactive staging gives you fine-grained control over what goes into each commit. -
Always review before committing – Use
git status
andgit diff --staged
to verify exactly what you’re about to commit, preventing accidental inclusion of sensitive data, debug code, or unrelated changes. -
Proper
.gitignore
is your first line of defense – Setting up.gitignore
early prevents you from accidentally staging environment files, build artifacts, or IDE-specific files that don’t belong in version control. -
Small, logical commits are easier to manage – When you need to revert changes, cherry-pick features, or understand project history, atomic commits that change one thing at a time are invaluable.
Next Steps
What to Practice
-
Daily workflow integration – Make selective staging your default approach. Before each commit, consciously decide what belongs together.
-
Interactive staging mastery – Practice using
git add -p
on files with multiple unrelated changes to split them into separate commits. -
Refine your
.gitignore
– Build a comprehensive.gitignore
for your test automation stack (Python/Java/JavaScript specific files, test reports, screenshots, logs). -
Review staging regularly – Make
git diff --staged
a habit before every commit to catch mistakes early.
Related Topics to Explore
- Partial file commits with
git add -p
– Learn to stage only specific hunks within a file - Unstaging mistakes – Master
git reset HEAD <file>
andgit restore --staged <file>
- Branch strategies – Combine selective staging with feature branches for cleaner PRs
- Commit message conventions – Pair your focused commits with conventional commit formats
- Pre-commit hooks – Automate checks to prevent committing unwanted files or code patterns
- Git stash workflows – Temporarily save unrelated changes while you commit focused work
- Amending and interactive rebase – Clean up commit history when you didn’t stage selectively enough
Pro Tip: Create a pre-commit checklist for your team: “Have you reviewed git diff --staged
? Are all changes in this commit related? Is the commit message descriptive?” This builds a culture of quality commits.