The Problem
Recently, while working on the RENDER project, I had a repository with this structure:
terminal-code/
βββ packages/
β βββ astro-terminal-code/
β βββ package.json
β βββ README.md
β βββ src/
But I needed it to be:
terminal-code/
βββ package.json
βββ README.md
βββ src/
The challenge? I wanted to preserve the entire commit history while restructuring the files across all commits, not just the latest one.
Approach 1: Manual Rebase Strategy
My usual approach for this type of problem involves manually going through each commit:
# 1. Start interactive rebase from root
git rebase -i --root
# 2. Mark each commit as 'edit'
# 3. For each commit, manually:
git reset --soft HEAD~1
# Move files manually
mv packages/astro-terminal-code/* .
rm -rf packages/
git add -A
git commit --amend --no-edit
# 4. Continue to next commit
git rebase --continue
# 5. Repeat until all commits are processed
Pros of Manual Rebase:
- β Full control over each commit
- β Can make different changes per commit if needed
- β Easy to understand whatβs happening
- β Can abort/fix issues at any step
Cons of Manual Rebase:
- β Time-consuming for many commits
- β Prone to human error
- β Interactive editor issues in some environments
- β Repetitive and tedious process
Approach 2: Git Filter-Branch
Then I discovered git filter-branch - a powerful command that can rewrite entire repository history in one go:
# Single command to restructure entire history
FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch \
--tree-filter 'if [ -d packages/astro-terminal-code ]; then
mv packages/astro-terminal-code/* . 2>/dev/null || true;
mv packages/astro-terminal-code/.* . 2>/dev/null || true;
rm -rf packages;
fi' HEAD
Pros of Filter-Branch:
- β One command handles entire history
- β Consistent changes across all commits
- β Fast execution
- β No interactive editor issues
- β Atomic operation - all or nothing
Cons of Filter-Branch:
- β Less granular control
- β Can be dangerous if command is wrong
- β Harder to debug if something goes wrong
- β Rewrites all commit hashes
Real-World Comparison
Let me show you the actual results from my RENDER project restructuring:
Before:
$ git log --oneline
65e77b3 π₯ Update branding: SandikoOS β RENDER
a2219f5 π FOUNDATION: SandikoOS Ecosystem - Phase 1
$ git show --name-only a2219f5
packages/astro-terminal-code/README.md
packages/astro-terminal-code/package.json
packages/astro-terminal-code/src/index.ts
packages/astro-terminal-code/src/processor.ts
After Filter-Branch:
$ git log --oneline
31a26da π₯ Update branding: SandikoOS β RENDER
064115d π FOUNDATION: SandikoOS Ecosystem - Phase 1
$ git show --name-only 064115d
README.md
package.json
src/index.ts
src/processor.ts
Time taken:
- Manual rebase: ~10-15 minutes (with interactive editor struggles)
- Filter-branch: ~5 seconds
When to Use Each Approach
Use Manual Rebase When:
- You need different changes per commit
- You want to review each commit individually
- Youβre learning Git and want to understand the process
- You have complex merge conflicts to resolve
Use Filter-Branch When:
- You need consistent changes across all commits
- You have many commits to process
- You want a fast, automated solution
- Youβre confident in your filter command
Modern Alternative: Git Filter-Repo
For new projects, consider git filter-repo (requires separate installation):
# More modern and safer alternative
git filter-repo --path-rename packages/astro-terminal-code/:
Safety Tips
Regardless of which approach you choose:
- Always backup your repository first
- Test on a clone before applying to main repo
- Use
--force-with-leasewhen pushing rewritten history - Coordinate with team before rewriting shared history
# Safe backup and testing workflow
git clone original-repo test-repo
cd test-repo
# Apply your changes
git push --force-with-lease origin main
Conclusion
Both approaches have their place in a developerβs toolkit. For the RENDER project restructuring, git filter-branch was clearly the winner - it saved me significant time and eliminated the interactive editor issues I was facing.
However, the manual rebase approach taught me a lot about Git internals and gives me confidence to handle complex history rewriting scenarios.
My recommendation: Start with understanding the manual approach, then graduate to git filter-branch for efficiency. Your future self will thank you for learning both! π
This article is part of my journey building the RENDER ecosystem - revolutionizing desktop development with web technologies. Follow along for more Git tips and development insights!
Comments