English
Git Tag as a Fixed Refactoring Baseline: How to Get the Complete Diff at Any Time?

In large-scale feature refactoring, we often encounter a pain point:The refactoring isn't finished yet, but I want to be able to see the complete changes "from the start of the refactoring until now" at any time., rather than by the various WIP commits in between,reset Disrupt the baseline, causing git diff Only fragmented increments can be seen.
This article documents a simple and reliable solution:use git tag Fixed reconstruction starting point.
Problem Background
git diff It is a relative comparison; its baseline determines what you see:
| Command | Comparison benchmark | What you see |
|---|---|---|
git diff | Working Directory vs Staging Area | Only unstaged increments |
git diff HEAD | Working directory vs last commit | Uncommitted changes; once committed, the baseline changes. |
git diff <base> | Working tree vs specified commit | All changes relative to base |
During the refactoring process, if we:
First made a WIP commit to save progress.
again
git reset --soft HEAD~1Undo the commit and continue making changes
At this time git diff will become empty(Because the changes are all in the staging area), and you only want "the complete diff of the entire refactor," but no matter what you do, you just can't get it.
Root cause: The reference point has not been fixed.git diff The baseline drifts continuously with commit and reset.
Solution:git tag Fixed datum
Step 1: Create a tag before starting the refactoring
Ensure the workspace is clean, then create a tag on the current commit:
bash
git status # Confirm the workspace is clean
git tag refactor-base # Tag the current HEAD💡 If there are already uncommitted changes in the workspace, it is recommended to clean them up before creating a tag; otherwise, the tag only marks the commit and does not include workspace changes.
Step 2: Refactor with confidence
Afterwards, regardless:
How many times to commit?
reset --soft/reset --mixedHow many timesHow many files were changed?
None of them affect the baseline.
Step 3: View the complete diff at any time
bash
git diff refactor-base > git.diff.all.txtmeaning:refactor-base That commit vs. the current working tree.
output: from the moment of the tag to now All changes, the intermediate process has no effect at all.
Step 4: Include untracked new files
git diff By default, untracked files are not shown (?? state). If you need to include:
bash
git add -A # Add all to the staging area
git diff refactor-base > git.diff.all.txtOr you just want to mark the intent but don't actually want to stage it:
bash
git add -N .
git diff refactor-base > git.diff.all.txtStep 5: Clean up after refactoring is complete
bash
git tag -d refactor-baseUnexpected situation: What should you do if you forget to tag?
If you forgot to tag before starting the refactor, but Changes are still in the working directory/staging area, never committed, and there are remedies.
Option A: Use HEAD As the baseline (simplest)
As long as there has been at least one commit in the repository,HEAD It is inherently the "state before modification":
bash
git diff HEAD > git.diff.all.txtgit diff HEAD will output All uncommitted changes(working tree + staging area), equivalent to the complete diff "from the last commit to now".
If you're worried that the baseline will be lost after a later commit, just add a tag now:
bash
git tag refactor-base HEAD # Baseline = the commit before you started making changes
git diff refactor-base # Equivalent to git diff HEADAfterward, even if you commit ten times and reset five times,git diff refactor-base Still give it to you Complete changes from the refactoring starting point to now.
Option B: Commit the current changes first, then soft reset.
If you want to make the baseline clearer, you can first commit the current changes, and then use git reset --soft Restore:
bash
# 1. Commit all current changes (take a snapshot of the "refactoring starting point")
git add -A
git commit -m "base: snapshot before refactoring"
# 2. Create a tag to fix the baseline.
git tag refactor-base
# 3. Soft undo this commit, move the changes back to the staging area, and continue refactoring.
git reset --soft HEAD~1⚠️ Note: at this time refactor-base points to the state after your changes, suitable for "tracking subsequent changes from now on," rather than "a complete diff including previous changes." To include previous changes, use Option A.
Case 3: Never committed before; the repository is brand new.
If this is a brand-new repository with not even a single commit, then There is no “before” to compare with.——All files are newly added.
bash
git add -A
git diff --cached # View all contents in the staging area (relative to an empty repository)It is recommended to make a commit first to lock in the current state; only then is there a baseline to discuss:
bash
git add -A
git commit -m "chore: initial snapshot"
git tag refactor-baseSummary
| Your status | How do you find the benchmark? | Command |
|---|---|---|
| There have been commits, but the changes have not been committed. | HEAD It is the benchmark. | git diff HEAD |
| There have been commits, but I forgot which one is the starting point. | git reflog Find | git tag refactor-base <commit> |
| Never committed; the repository already has history. | Current HEAD | git diff HEAD |
| Never committed, brand new repository | No baseline | Commit once first |
As long as there has been at least one commit in the repository,HEAD Natural is your benchmark,git diff HEAD you can get all uncommitted changes—no tag needed.
The value of tag lies in"across multiple commits can still lock onto the same baseline." If you haven't committed at all,HEAD That's enough.
Complete workflow example
bash
# 1. Fix the baseline before starting refactoring
git status
git tag refactor-base
# 2. During refactoring, change and commit freely.
git add -A && git commit -m "WIP: refactor part 1"
# ... Continue revising ...
git reset --soft HEAD~1
# ... Continue revising ...
# 3. View the full diff at any time.
git diff refactor-base > git.diff.all.txt
# 4. Refactoring complete
git add -A
git commit -m "feat: complete refactoring description"
# 5. Clean up benchmark markers
git tag -d refactor-baseSolution comparison
| method | 基准 | Result | Applicable scenarios |
|---|---|---|---|
git diff | Working directory vs staging area | Only increments; the baseline drifts erratically. | Daily small changes |
git diff HEAD | Working tree vs last commit | After commit, the baseline changes. | Single submission pre-check |
git diff refactor-base | Workspace vs fixed tag | Always complete✅ | Large-scale refactoring |
git stash -u | Temporary save | Need git diff stash@{0}^ stash@{0} | Brief absence |
| WIP commit | Save progress | Baseline becomes WIP commit | Need a save point |
Common misconceptions
| Misconception | Truth |
|---|---|
git diff View all changes by default | Wrong, only look at working directory vs staging area. |
git reset --soft HEAD~1 after git diff Can see everything | Wrong, the changes are in the staging area,git diff is empty, need to use git diff HEAD |
After WIP commit git diff can see the entire refactoring | Wrong, the baseline has become a WIP commit, so only the incremental changes are visible. |
git stash Include new files by default | Wrong, it needs to be added.-u only includes untracked files |
Core conclusion
git diff It is a relative comparison, so a base must be fixed before refactoring begins.
use git tag refactor-base As an anchor, afterward no matter how many times you commit or reset,git diff refactor-base Always give you the "complete changes from the refactoring starting point to the present."
This solution is especially suitable for:
Large-scale feature refactoring: need to be able to submit the complete diff to AI (such as DeepSeek) at any time to generate a commit message.
Commit in stages, but want to keep track of the overall changes at any time.
Before code review, I want to see the complete changeset at once.
Tip: Give the tag a meaningful name
bash
git tag refactor/license-agent-v2
git diff refactor/license-agent-v2 > git.diff.all.txtOr use a branch instead of a tag (same effect):
bash
git branch refactor-base
git diff refactor-base > git.diff.all.txtRemember it in one sentence.: tag is an anchor for a fixed baseline; tag it once, and the diff will always be complete thereafter.