Sitemap

Learn Git Advanced Workflow

10 min readNov 23, 2025

--

Press enter or click to view image in full size
Photo by Yancy Min on Unsplash

Here is the scenario: you are a full-stack developer working on a veterinary clinic management application. You’re in the middle of implementing a new feature for appointment scheduling, but an urgent bug fix is needed in production. This scenario walks you throughgit add --patch, conventional commits with headers, bodies, and footers, and git worktree via a realistic workflow.

Part 1: Understanding the Starting Point

Repository Structure

clinic-app/
├── src/
│ ├── components/
│ │ ├── AppointmentForm.tsx
│ │ ├── AppointmentList.tsx
│ │ └── PatientCard.tsx
│ ├── services/
│ │ ├── appointmentService.ts
│ │ └── authService.ts
│ └── App.tsx
├── tests/
│ └── appointmentService.test.ts
├── package.json
└── README.md

You’re on the feature/appointment-scheduler branch where you've made changes across multiple files:

  1. AppointmentForm.tsx: Added new form validation and accidentally left debug logging code.
  2. appointmentService.ts: Implemented appointment creation API and improved error handling.
  3. README.md: Updated documentation.

Suddenly, your manager reports a critical bug in production: the authentication service is returning incorrect user roles, causing patients to see restricted areas.

Doing the task, you will learn how to:

  • Use git add --patch to selectively stage only the appointment form validation (not the debug code).
  • Create a proper commit with a conventional commit format.
  • Use git worktree to create an isolated environment for the hotfix without stashing your work.
  • Continue work on the feature branch while the hotfix is being prepared.

Part 2: Setting Up the Scenario

Ensure you have Git 2.25+ (for modern worktree features):

git --version

Set up the Scenario

  1. Create the repository:
git init clinic-app
cd clinic-app
# Create initial commit
touch README.md package.json
git add .
git commit -m "chore: init"
# Create main and develop branches
git checkout -b develop
git checkout main
  1. Create the feature branch:
git checkout -b feature/appointment-scheduler
  1. Create the file structure:
mkdir -p src/{components,services} tests
touch src/components/{AppointmentForm.tsx,AppointmentList.tsx,PatientCard.tsx}
touch src/services/{appointmentService.ts,authService.ts}
touch src/App.tsx
touch tests/appointmentService.test.ts
touch README.md

Part 3: Goal 1, Git Add Patch (git add -p)

What is git add --patch?

git add --patch (or git add -p) allows you to interactively stage hunks (chunks of code) instead of entire files. This is powerful for:

  • Separating concerns in a single file
  • Staging only the good code and leaving debug code unstaged
  • Creating more atomic, focused commits

The Scenario Modification

Edit src/components/AppointmentForm.tsx:

import React, { useState } from 'react';export const AppointmentForm: React.FC = () => {
const [date, setDate] = useState('');
const [time, setTime] = useState('');
const [reason, setReason] = useState('');
// Good code: proper validation
const validateForm = (): boolean => {
if (!date || !time || !reason) {
console.error('Form validation failed: missing required fields');
return false;
}
if (new Date(date) < new Date()) {
console.error('Form validation failed: date is in the past');
return false;
}
return true;
};
// DEBUG CODE (left accidentally)
const debugInfo = () => {
console.log('DEBUG: AppointmentForm component mounted');
console.log('DEBUG: Current state -', { date, time, reason });
console.log('DEBUG: validateForm function called');
};
const handleSubmit = (e: React.FormEvent) => {
debugInfo(); // Should not be here!
if (validateForm()) {
console.log('Submitting appointment:', { date, time, reason });
// Call API
}
};
return (
<form onSubmit={handleSubmit}>
<input type="date" value={date} onChange={(e) => setDate(e.target.value)} />
<input type="time" value={time} onChange={(e) => setTime(e.target.value)} />
<textarea value={reason} onChange={(e) => setReason(e.target.value)} />
<button type="submit">Book Appointment</button>
</form>
);
};

Edit src/services/appointmentService.ts:

export interface Appointment {
id: string;
patientId: string;
date: string;
time: string;
reason: string;
}
export const appointmentService = {
async createAppointment(data: Omit<Appointment, 'id'>): Promise<Appointment> {
try {
const response = await fetch('/api/appointments', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
// Improved error handling
if (!response.ok) {
const error = await response.json();
throw new Error(`Appointment creation failed: ${error.message}`);
}
return response.json();
} catch (error) {
// Better error propagation
if (error instanceof Error) {
console.error('Appointment service error:', error.message);
throw error;
}
throw new Error('Unknown error in appointment service');
}
},
async getAppointments(patientId: string): Promise<Appointment[]> {
const response = await fetch(`/api/appointments?patientId=${patientId}`);
return response.json();
},
};

Edit README.md:

# Clinic Management Application

## New Features

### Appointment Scheduler
- Book appointments with date and time validation
- View all patient appointments
- Automatic reminders before appointments

### Installation

```bash
npm install
npm start
```

Now use git add -p

  1. Check the current status:
git status

You’ll see all three files as untracked or modified.

  1. Start interactive patch mode:
git add -p

2. Walk through the hunks:

When you run git add -p src/components/AppointmentForm.tsx, Git will show you each hunk and ask:

Stage this hunk [y,n,q,a,d,s,e,?]?

Available options:

  • y - stage this hunk
  • n - don't stage this hunk
  • s - split this hunk into smaller hunks
  • e - manually edit this hunk
  • q - quit
  • ? - show help
  1. Your approach:
# First file
git add -p src/components/AppointmentForm.tsx
# When you see the validation function: press 'y'
# When you see the debug code: press 'n'
# Second file (all good changes)
git add -p src/services/appointmentService.ts
# Press 'y' for all hunks
# Third file
git add -p README.md
# Press 'y' for all hunks
  1. Verify your staging area:
git diff --cached

This shows only what you staged (good code, no debug).

git diff

This shows unstaged changes (debug code).

Part 4: Goals 2, Conventional Commits (Header, Body, Footer)

What are Conventional Commits?

Conventional commits follow a structured format that makes your commit history readable, automatable, and suitable for semantic versioning.

Format:

<type>(<scope>): <description>

<body>

<footer>

Commit Type Reference

| Type     | Purpose                          | Example                          |
|----------|----------------------------------|----------------------------------|
| `feat` | New feature | `feat(appointments): add booking` |
| `fix` | Bug fix | `fix(auth): correct role check` |
| `refactor` | Code refactor (no feature change) | `refactor(api): extract helper` |
| `docs` | Documentation changes | `docs: update README` |
| `test` | Add or update tests | `test(auth): add role tests` |
| `chore` | Maintenance tasks | `chore: update dependencies` |

Your First Conventional Commit

After using git add -p to stage the good code, create a commit:

git commit

This opens your editor (nano or vim). Then type:

feat(appointments): implement form validation and improved error handling- Add comprehensive form validation with date/time checks
- Validate that appointment dates are in the future
- Implement improved error handling in appointment service
- Add specific error messages for troubleshooting
The form now properly validates all required fields before submission.
The service layer provides better error propagation and logging,
making debugging easier for developers and users.
Closes #234
Related to #231

Breaking this down:

Get Faizul Fuadi’s stories in your inbox

Join Medium for free to get updates from this writer.

Header: feat(appointments): implement form validation and improved error handling

  • Type: feat (new feature)
  • Scope: appointments (what part of the system)
  • Description: Clear, concise description (present tense, lowercase)

Body:

  • Explains the why and what, not just code changes
  • Use bullet points for clarity
  • Wrap at 72 characters for readability
  • Leave a blank line between the header and the body

Footer:

  • Closes #234 - Automatically closes GitHub issue #234
  • Related to #231 - References related issues
  • Other options: Fixes #X, Resolves #X, BREAKING CHANGE: ...

Common Footer Keywords

| Keyword | Purpose |
|---------|---------|
| `Closes` | Closes the referenced issue |
| `Fixes` | Fixes the bug in referenced issue |
| `Resolves` | Resolves the referenced issue |
| `Related to` | References a related issue (doesn't close it) |
| `BREAKING CHANGE:` | Indicates breaking API/behavior changes |
| `Co-authored-by:` | Credits multiple authors |

View Your Commit

git log --oneline
git log -1 --format=fuller

Part 5: Goals 3, Git Worktree

What is git worktree?

git worktree allows you to have multiple working directories from the same repository, each checked out to a different branch. This solves the problem where you need to work on multiple branches simultaneously without:

  • Stashing/unstashing work
  • Losing your IDE state
  • Switching contexts constantly

Real-World Scenario

Your manager needs the hotfix NOW. Instead of interrupting your appointment scheduler work, you’ll:

  1. Create a worktree for the hotfix branch
  2. Work on the hotfix in a separate directory
  3. Continue working on your feature branch in the original directory

Step 1: Create a Hotfix Worktree

First, ensure your main/master branch exists and is up to date:

git checkout main
git pull # or update manually

Now create a worktree:

git worktree add ../clinic-app-hotfix fix/auth-role-bug --track -b fix/auth-role-bug

Breaking this down:

  • git worktree add - Create a new worktree
  • ../clinic-app-hotfix - Path to the new working directory (outside main repo)
  • fix/auth-role-bug - Branch name for the hotfix
  • --track - Track the remote branch
  • -b - Create the branch if it doesn't exist

Your directory structure now looks like:

parent-directory/
├── clinic-app/ # Your original worktree (feature work)
│ └── src/
│ ├── components/
│ ├── services/
│ └── ...
└── clinic-app-hotfix/ # New worktree (hotfix work)
└── src/
├── components/
├── services/
└── ...

Step 2: Work in the Hotfix Worktree

Navigate to the hotfix directory:

cd ../clinic-app-hotfix

Now create the bug (so you can fix it):

Edit src/services/authService.ts:

export interface User {
id: string;
name: string;
role: 'admin' | 'vet' | 'patient';
}
export const authService = {
// BUG: roles are inverted!
getRoleLevel(role: string): number {
switch (role) {
case 'patient':
return 3; // BUG: patient should not be level 3
case 'vet':
return 2;
case 'admin':
return 1; // BUG: admin should be highest
default:
return 0;
}
},
hasPermission(userRole: string, requiredRole: string): boolean {
// Patients with level 3 shouldn't access everything!
return this.getRoleLevel(userRole) >= this.getRoleLevel(requiredRole);
},
getCurrentUser(): User | null {
// Simulated: in real app, get from localStorage/session
return {
id: '123',
name: 'John Vet',
role: 'vet',
};
},
};

Add and commit the fix using patch mode and conventional commits:

git add -p

Stage the bugfix, then commit:

git commit
fix(auth): correct role permission hierarchyThe role level mapping was inverted, causing patients to have
administrative permissions and blocking admin access to certain features.
Fixed the role levels so that:
- patient: level 1 (lowest permissions)
- vet: level 2 (standard permissions)
- admin: level 3 (highest permissions)
Fixes #999
BREAKING CHANGE: Role hierarchy has been corrected. Code relying on
the old (buggy) hierarchy will need to be updated."

Step 3: Return to Your Feature Branch

Navigate back to your original repository:

cd ../clinic-app

Your feature branch work is exactly as you left it! Continue working:

git log --oneline
# You'll still see your appointments feature commits

Step 4: Manage Your Worktrees

List all worktrees:

git worktree list

Output:

/path/to/clinic-app        abc1234 [feature/appointment-scheduler]
/path/to/clinic-app-hotfix def5678 [fix/auth-role-bug]

Step 5: Cleanup After Hotfix

Once the hotfix is merged to main and deployed:

# From the hotfix worktree directory
cd ../clinic-app-hotfix
# Switch to main and merge
git checkout main
git pull
git merge fix/auth-role-bug
# Remove the worktree
cd ../clinic-app
git worktree remove ../clinic-app-hotfix

Or use the shorthand:

git worktree remove fix/auth-role-bug

List worktrees again to confirm:

git worktree list

Now only the main repo remains:

/path/to/clinic-app  [feature/appointment-scheduler]

Part 6: Advanced Patterns

Pattern 1: Using Patch Mode with --edit

If hunks don’t split the way you want, use -e to manually edit:

git add -p --edit

Your editor opens with the full diff. Remove lines you don’t want to stage.

Pattern 2: Conventional Commits with Breaking Changes

git commit
refactor(auth)!: redesign permission system- Replace role hierarchy with permission-based system
- All components now use new authService API
BREAKING CHANGE: The getRoleLevel() method has been removed.
Use hasPermission() instead. Migrate code before upgrading."

The ! in the header (refactor(auth)!) and BREAKING CHANGE: In footer, both indicate breaking changes.

Pattern 3: Multiple Worktrees for Parallel Development

# Feature A
git worktree add ../clinic-app-feature-a feature/appointment-scheduler

# Feature B
git worktree add ../clinic-app-feature-b feature/payment-integration

# Hotfix
git worktree add ../clinic-app-hotfix fix/auth-bug main
# Your directory structure
# ├── clinic-app/
# ├── clinic-app-feature-a/
# ├── clinic-app-feature-b/
# └── clinic-app-hotfix/

Now you can work on three things simultaneously without context switching!

Pattern 4: Temporary Worktree for Merge Testing

# Test what happens if you merge develop into feature
git worktree add ../clinic-app-test --detach HEAD
cd ../clinic-app-test
git merge develop

# Test... does it compile? Do tests pass?
cd ../clinic-app
git worktree remove ../clinic-app-test
# Your real branches are untouched

Part 7: Exercises, Practice makes perfect

Exercise 1: Selective Staging

  1. Edit src/components/PatientCard.tsx with two separate concerns:
  • Add new patient status field (good)
  • Add console.log debugging (not good)

2. Use git add -p to stage only the status field change

3. Write a conventional commit for the status field feature

Exercise 2: Complete Commit Workflow

Create a feature that fixes error handling and closes an issue:

git add -p
git commit
feat(api): improve error messagesDisplay user friendly error messages when API calls fail.- Add error boundary component
- Map server errors to user messages
- Add logging for debugging
Closes #456
Related to #450"

Exercise 3: Worktree Workflow

  1. Create a worktree for a documentation branch:
git worktree add ../clinic-app-docs docs/api-guide

2. In the main repo, continue feature work

3. In the docs worktree, add API documentation

4. Return to main repo and list both worktrees

5. Remove the docs worktree

Part 8: Troubleshooting

Issue: Can’t stage hunk (too complex)

Use s to split hunks:

Stage this hunk [y,n,q,a,d,s,e,?]? s

If split doesn’t work, use e to edit manually.

Issue: Worktree directory already exists

git worktree add ../path  # Error: directory exists

Either remove the directory or use a different path.

Issue: Can’t remove worktree (still in use)

# Navigate AWAY from the worktree first
cd ../clinic-app

git worktree remove ../clinic-app-hotfix

Issue: Worktree is locked

# If Git says worktree is locked
git worktree prune

# Or force remove
git worktree remove --force ../clinic-app-hotfix

Key Takeaways

| Concept | When to Use | Benefit |
|---------|------------|---------|
| `git add -p` | Multiple units of work in one file | Atomic, focused commits |
| Conventional commits | All commits | Readable history, semantic versioning |
| `git worktree` | Multiple branches needed simultaneously | No context switching, faster workflow |

References

--

--