Initial commit with translated description
This commit is contained in:
533
SKILL.md
Normal file
533
SKILL.md
Normal file
@@ -0,0 +1,533 @@
|
||||
---
|
||||
name: git-workflows
|
||||
description: "超越add/commit/push的高级git操作。在rebase、bisect调试bug、使用worktrees进行并行开发、使用reflog恢复、管理子树/子模块、解决合并冲突、跨分支cherry-pick或使用monorepos时使用。"
|
||||
metadata: {"clawdbot":{"emoji":"🌿","requires":{"bins":["git"]},"os":["linux","darwin","win32"]}}
|
||||
---
|
||||
|
||||
# Git Workflows
|
||||
|
||||
Advanced git operations for real-world development. Covers interactive rebase, bisect, worktree, reflog recovery, subtrees, submodules, sparse checkout, conflict resolution, and monorepo patterns.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Cleaning up commit history before merging (interactive rebase)
|
||||
- Finding which commit introduced a bug (bisect)
|
||||
- Working on multiple branches simultaneously (worktree)
|
||||
- Recovering lost commits or undoing mistakes (reflog)
|
||||
- Managing shared code across repos (subtree/submodule)
|
||||
- Resolving complex merge conflicts
|
||||
- Cherry-picking commits across branches or forks
|
||||
- Working with large monorepos (sparse checkout)
|
||||
|
||||
## Interactive Rebase
|
||||
|
||||
### Squash, reorder, edit commits
|
||||
|
||||
```bash
|
||||
# Rebase last 5 commits interactively
|
||||
git rebase -i HEAD~5
|
||||
|
||||
# Rebase onto main (all commits since diverging)
|
||||
git rebase -i main
|
||||
```
|
||||
|
||||
The editor opens with a pick list:
|
||||
|
||||
```
|
||||
pick a1b2c3d Add user model
|
||||
pick e4f5g6h Fix typo in user model
|
||||
pick i7j8k9l Add user controller
|
||||
pick m0n1o2p Add user routes
|
||||
pick q3r4s5t Fix import in controller
|
||||
```
|
||||
|
||||
Commands available:
|
||||
```
|
||||
pick = use commit as-is
|
||||
reword = use commit but edit the message
|
||||
edit = stop after this commit to amend it
|
||||
squash = merge into previous commit (keep both messages)
|
||||
fixup = merge into previous commit (discard this message)
|
||||
drop = remove the commit entirely
|
||||
```
|
||||
|
||||
### Common patterns
|
||||
|
||||
```bash
|
||||
# Squash fix commits into their parent
|
||||
# Change "pick" to "fixup" for the fix commits:
|
||||
pick a1b2c3d Add user model
|
||||
fixup e4f5g6h Fix typo in user model
|
||||
pick i7j8k9l Add user controller
|
||||
fixup q3r4s5t Fix import in controller
|
||||
pick m0n1o2p Add user routes
|
||||
|
||||
# Reorder commits (just move lines)
|
||||
pick i7j8k9l Add user controller
|
||||
pick m0n1o2p Add user routes
|
||||
pick a1b2c3d Add user model
|
||||
|
||||
# Split a commit into two
|
||||
# Mark as "edit", then when it stops:
|
||||
git reset HEAD~
|
||||
git add src/model.ts
|
||||
git commit -m "Add user model"
|
||||
git add src/controller.ts
|
||||
git commit -m "Add user controller"
|
||||
git rebase --continue
|
||||
```
|
||||
|
||||
### Autosquash (commit messages that auto-arrange)
|
||||
|
||||
```bash
|
||||
# When committing a fix, reference the commit to squash into
|
||||
git commit --fixup=a1b2c3d -m "Fix typo"
|
||||
# or
|
||||
git commit --squash=a1b2c3d -m "Additional changes"
|
||||
|
||||
# Later, rebase with autosquash
|
||||
git rebase -i --autosquash main
|
||||
# fixup/squash commits are automatically placed after their targets
|
||||
```
|
||||
|
||||
### Abort or continue
|
||||
|
||||
```bash
|
||||
git rebase --abort # Cancel and restore original state
|
||||
git rebase --continue # Continue after resolving conflicts or editing
|
||||
git rebase --skip # Skip the current commit and continue
|
||||
```
|
||||
|
||||
## Bisect (Find the Bug)
|
||||
|
||||
### Binary search through commits
|
||||
|
||||
```bash
|
||||
# Start bisect
|
||||
git bisect start
|
||||
|
||||
# Mark current commit as bad (has the bug)
|
||||
git bisect bad
|
||||
|
||||
# Mark a known-good commit (before the bug existed)
|
||||
git bisect good v1.2.0
|
||||
# or: git bisect good abc123
|
||||
|
||||
# Git checks out a middle commit. Test it, then:
|
||||
git bisect good # if this commit doesn't have the bug
|
||||
git bisect bad # if this commit has the bug
|
||||
|
||||
# Repeat until git identifies the exact commit
|
||||
# "abc123 is the first bad commit"
|
||||
|
||||
# Done — return to original branch
|
||||
git bisect reset
|
||||
```
|
||||
|
||||
### Automated bisect (with a test script)
|
||||
|
||||
```bash
|
||||
# Fully automatic: git runs the script on each commit
|
||||
# Script must exit 0 for good, 1 for bad
|
||||
git bisect start HEAD v1.2.0
|
||||
git bisect run ./test-for-bug.sh
|
||||
|
||||
# Example test script
|
||||
cat > /tmp/test-for-bug.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
# Return 0 if bug is NOT present, 1 if it IS
|
||||
npm test -- --grep "login should redirect" 2>/dev/null
|
||||
EOF
|
||||
chmod +x /tmp/test-for-bug.sh
|
||||
git bisect run /tmp/test-for-bug.sh
|
||||
```
|
||||
|
||||
### Bisect with build failures
|
||||
|
||||
```bash
|
||||
# If a commit doesn't compile, skip it
|
||||
git bisect skip
|
||||
|
||||
# Skip a range of known-broken commits
|
||||
git bisect skip v1.3.0..v1.3.5
|
||||
```
|
||||
|
||||
## Worktree (Parallel Branches)
|
||||
|
||||
### Work on multiple branches simultaneously
|
||||
|
||||
```bash
|
||||
# Add a worktree for a different branch
|
||||
git worktree add ../myproject-hotfix hotfix/urgent-fix
|
||||
# Creates a new directory with that branch checked out
|
||||
|
||||
# Add a worktree with a new branch
|
||||
git worktree add ../myproject-feature -b feature/new-thing
|
||||
|
||||
# List worktrees
|
||||
git worktree list
|
||||
|
||||
# Remove a worktree when done
|
||||
git worktree remove ../myproject-hotfix
|
||||
|
||||
# Prune stale worktree references
|
||||
git worktree prune
|
||||
```
|
||||
|
||||
### Use cases
|
||||
|
||||
```bash
|
||||
# Review a PR while keeping your current work untouched
|
||||
git worktree add ../review-pr-123 origin/pr-123
|
||||
|
||||
# Run tests on main while developing on feature branch
|
||||
git worktree add ../main-tests main
|
||||
cd ../main-tests && npm test
|
||||
|
||||
# Compare behavior between branches side by side
|
||||
git worktree add ../compare-old release/v1.0
|
||||
git worktree add ../compare-new release/v2.0
|
||||
```
|
||||
|
||||
## Reflog (Recovery)
|
||||
|
||||
### See everything git remembers
|
||||
|
||||
```bash
|
||||
# Show reflog (all HEAD movements)
|
||||
git reflog
|
||||
# Output:
|
||||
# abc123 HEAD@{0}: commit: Add feature
|
||||
# def456 HEAD@{1}: rebase: moving to main
|
||||
# ghi789 HEAD@{2}: checkout: moving from feature to main
|
||||
|
||||
# Show reflog for a specific branch
|
||||
git reflog show feature/my-branch
|
||||
|
||||
# Show with timestamps
|
||||
git reflog --date=relative
|
||||
```
|
||||
|
||||
### Recover from mistakes
|
||||
|
||||
```bash
|
||||
# Undo a bad rebase (find the commit before rebase in reflog)
|
||||
git reflog
|
||||
# Find: "ghi789 HEAD@{5}: checkout: moving from feature to main" (pre-rebase)
|
||||
git reset --hard ghi789
|
||||
|
||||
# Recover a deleted branch
|
||||
git reflog
|
||||
# Find the last commit on that branch
|
||||
git branch recovered-branch abc123
|
||||
|
||||
# Recover after reset --hard
|
||||
git reflog
|
||||
git reset --hard HEAD@{2} # Go back 2 reflog entries
|
||||
|
||||
# Recover a dropped stash
|
||||
git fsck --unreachable | grep commit
|
||||
# or
|
||||
git stash list # if it's still there
|
||||
git log --walk-reflogs --all -- stash # find dropped stash commits
|
||||
```
|
||||
|
||||
## Cherry-Pick
|
||||
|
||||
### Copy specific commits to another branch
|
||||
|
||||
```bash
|
||||
# Pick a single commit
|
||||
git cherry-pick abc123
|
||||
|
||||
# Pick multiple commits
|
||||
git cherry-pick abc123 def456 ghi789
|
||||
|
||||
# Pick a range (exclusive start, inclusive end)
|
||||
git cherry-pick abc123..ghi789
|
||||
|
||||
# Pick without committing (stage changes only)
|
||||
git cherry-pick --no-commit abc123
|
||||
|
||||
# Cherry-pick from another remote/fork
|
||||
git remote add upstream https://github.com/other/repo.git
|
||||
git fetch upstream
|
||||
git cherry-pick upstream/main~3 # 3rd commit from upstream's main
|
||||
```
|
||||
|
||||
### Handle conflicts during cherry-pick
|
||||
|
||||
```bash
|
||||
# If conflicts arise:
|
||||
# 1. Resolve conflicts in the files
|
||||
# 2. Stage resolved files
|
||||
git add resolved-file.ts
|
||||
# 3. Continue
|
||||
git cherry-pick --continue
|
||||
|
||||
# Or abort
|
||||
git cherry-pick --abort
|
||||
```
|
||||
|
||||
## Subtree and Submodule
|
||||
|
||||
### Subtree (simpler — copies code into your repo)
|
||||
|
||||
```bash
|
||||
# Add a subtree
|
||||
git subtree add --prefix=lib/shared https://github.com/org/shared-lib.git main --squash
|
||||
|
||||
# Pull updates from upstream
|
||||
git subtree pull --prefix=lib/shared https://github.com/org/shared-lib.git main --squash
|
||||
|
||||
# Push local changes back to upstream
|
||||
git subtree push --prefix=lib/shared https://github.com/org/shared-lib.git main
|
||||
|
||||
# Split subtree into its own branch (for extraction)
|
||||
git subtree split --prefix=lib/shared -b shared-lib-standalone
|
||||
```
|
||||
|
||||
### Submodule (pointer to another repo at a specific commit)
|
||||
|
||||
```bash
|
||||
# Add a submodule
|
||||
git submodule add https://github.com/org/shared-lib.git lib/shared
|
||||
|
||||
# Clone a repo with submodules
|
||||
git clone --recurse-submodules https://github.com/org/main-repo.git
|
||||
|
||||
# Initialize submodules after clone (if forgot --recurse)
|
||||
git submodule update --init --recursive
|
||||
|
||||
# Update submodules to latest
|
||||
git submodule update --remote
|
||||
|
||||
# Remove a submodule
|
||||
git rm lib/shared
|
||||
rm -rf .git/modules/lib/shared
|
||||
# Remove entry from .gitmodules if it persists
|
||||
```
|
||||
|
||||
### When to use which
|
||||
|
||||
```
|
||||
Subtree: Simpler, no special commands for cloners, code lives in your repo.
|
||||
Use when: shared library, vendor code, infrequent upstream changes.
|
||||
|
||||
Submodule: Pointer to exact commit, smaller repo, clear separation.
|
||||
Use when: large dependency, independent release cycle, many contributors.
|
||||
```
|
||||
|
||||
## Sparse Checkout (Monorepo)
|
||||
|
||||
### Check out only the directories you need
|
||||
|
||||
```bash
|
||||
# Enable sparse checkout
|
||||
git sparse-checkout init --cone
|
||||
|
||||
# Select directories
|
||||
git sparse-checkout set packages/my-app packages/shared-lib
|
||||
|
||||
# Add another directory
|
||||
git sparse-checkout add packages/another-lib
|
||||
|
||||
# List what's checked out
|
||||
git sparse-checkout list
|
||||
|
||||
# Disable (check out everything again)
|
||||
git sparse-checkout disable
|
||||
```
|
||||
|
||||
### Clone with sparse checkout (large monorepos)
|
||||
|
||||
```bash
|
||||
# Partial clone + sparse checkout (fastest for huge repos)
|
||||
git clone --filter=blob:none --sparse https://github.com/org/monorepo.git
|
||||
cd monorepo
|
||||
git sparse-checkout set packages/my-service
|
||||
|
||||
# No-checkout clone (just metadata)
|
||||
git clone --no-checkout https://github.com/org/monorepo.git
|
||||
cd monorepo
|
||||
git sparse-checkout set packages/my-service
|
||||
git checkout main
|
||||
```
|
||||
|
||||
## Conflict Resolution
|
||||
|
||||
### Understand the conflict markers
|
||||
|
||||
```
|
||||
<<<<<<< HEAD (or "ours")
|
||||
Your changes on the current branch
|
||||
=======
|
||||
Their changes from the incoming branch
|
||||
>>>>>>> feature-branch (or "theirs")
|
||||
```
|
||||
|
||||
### Resolution strategies
|
||||
|
||||
```bash
|
||||
# Accept all of ours (current branch wins)
|
||||
git checkout --ours path/to/file.ts
|
||||
git add path/to/file.ts
|
||||
|
||||
# Accept all of theirs (incoming branch wins)
|
||||
git checkout --theirs path/to/file.ts
|
||||
git add path/to/file.ts
|
||||
|
||||
# Accept ours for ALL files
|
||||
git checkout --ours .
|
||||
git add .
|
||||
|
||||
# Use a merge tool
|
||||
git mergetool
|
||||
|
||||
# See the three-way diff (base, ours, theirs)
|
||||
git diff --cc path/to/file.ts
|
||||
|
||||
# Show common ancestor version
|
||||
git show :1:path/to/file.ts # base (common ancestor)
|
||||
git show :2:path/to/file.ts # ours
|
||||
git show :3:path/to/file.ts # theirs
|
||||
```
|
||||
|
||||
### Rebase conflict workflow
|
||||
|
||||
```bash
|
||||
# During rebase, conflicts appear one commit at a time
|
||||
# 1. Fix the conflict in the file
|
||||
# 2. Stage the fix
|
||||
git add fixed-file.ts
|
||||
# 3. Continue to next commit
|
||||
git rebase --continue
|
||||
# 4. Repeat until done
|
||||
|
||||
# If a commit is now empty after resolution
|
||||
git rebase --skip
|
||||
```
|
||||
|
||||
### Rerere (reuse recorded resolutions)
|
||||
|
||||
```bash
|
||||
# Enable rerere globally
|
||||
git config --global rerere.enabled true
|
||||
|
||||
# Git remembers how you resolved conflicts
|
||||
# Next time the same conflict appears, it auto-resolves
|
||||
|
||||
# See recorded resolutions
|
||||
ls .git/rr-cache/
|
||||
|
||||
# Forget a bad resolution
|
||||
git rerere forget path/to/file.ts
|
||||
```
|
||||
|
||||
## Stash Patterns
|
||||
|
||||
```bash
|
||||
# Stash with a message
|
||||
git stash push -m "WIP: refactoring auth flow"
|
||||
|
||||
# Stash specific files
|
||||
git stash push -m "partial stash" -- src/auth.ts src/login.ts
|
||||
|
||||
# Stash including untracked files
|
||||
git stash push -u -m "with untracked"
|
||||
|
||||
# List stashes
|
||||
git stash list
|
||||
|
||||
# Apply most recent stash (keep in stash list)
|
||||
git stash apply
|
||||
|
||||
# Apply and remove from stash list
|
||||
git stash pop
|
||||
|
||||
# Apply a specific stash
|
||||
git stash apply stash@{2}
|
||||
|
||||
# Show what's in a stash
|
||||
git stash show -p stash@{0}
|
||||
|
||||
# Create a branch from a stash
|
||||
git stash branch new-feature stash@{0}
|
||||
|
||||
# Drop a specific stash
|
||||
git stash drop stash@{1}
|
||||
|
||||
# Clear all stashes
|
||||
git stash clear
|
||||
```
|
||||
|
||||
## Blame and Log Archaeology
|
||||
|
||||
```bash
|
||||
# Who changed each line (with date)
|
||||
git blame src/auth.ts
|
||||
|
||||
# Blame a specific line range
|
||||
git blame -L 50,70 src/auth.ts
|
||||
|
||||
# Ignore whitespace changes in blame
|
||||
git blame -w src/auth.ts
|
||||
|
||||
# Find when a line was deleted (search all history)
|
||||
git log -S "function oldName" --oneline
|
||||
|
||||
# Find when a regex pattern was added/removed
|
||||
git log -G "TODO.*hack" --oneline
|
||||
|
||||
# Follow a file through renames
|
||||
git log --follow --oneline -- src/new-name.ts
|
||||
|
||||
# Show the commit that last touched each line, ignoring moves
|
||||
git blame -M src/auth.ts
|
||||
|
||||
# Show log with file changes
|
||||
git log --stat --oneline -20
|
||||
|
||||
# Show all commits affecting a specific file
|
||||
git log --oneline -- src/auth.ts
|
||||
|
||||
# Show diff of a specific commit
|
||||
git show abc123
|
||||
```
|
||||
|
||||
## Tags and Releases
|
||||
|
||||
```bash
|
||||
# Create annotated tag (preferred for releases)
|
||||
git tag -a v1.2.0 -m "Release 1.2.0: Added auth module"
|
||||
|
||||
# Create lightweight tag
|
||||
git tag v1.2.0
|
||||
|
||||
# Tag a past commit
|
||||
git tag -a v1.1.0 abc123 -m "Retroactive tag for release 1.1.0"
|
||||
|
||||
# List tags
|
||||
git tag -l
|
||||
git tag -l "v1.*"
|
||||
|
||||
# Push tags
|
||||
git push origin v1.2.0 # Single tag
|
||||
git push origin --tags # All tags
|
||||
|
||||
# Delete a tag
|
||||
git tag -d v1.2.0 # Local
|
||||
git push origin --delete v1.2.0 # Remote
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
- `git rebase -i` is the single most useful advanced git command. Learn it first.
|
||||
- Never rebase commits that have been pushed to a shared branch. Rebase your local/feature work only.
|
||||
- `git reflog` is your safety net. If you lose commits, they're almost always recoverable within 90 days.
|
||||
- `git bisect run` with an automated test is faster than manual binary search and eliminates human error.
|
||||
- Worktrees are cheaper than multiple clones — they share `.git` storage.
|
||||
- Prefer `git subtree` over `git submodule` unless you have a specific reason. Subtrees are simpler for collaborators.
|
||||
- Enable `rerere` globally. It remembers conflict resolutions so you never solve the same conflict twice.
|
||||
- `git stash push -m "description"` is much better than bare `git stash`. You'll thank yourself when you have 5 stashes.
|
||||
- `git log -S "string"` (pickaxe) is the fastest way to find when a function or variable was added or removed.
|
||||
6
_meta.json
Normal file
6
_meta.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"ownerId": "kn7f6g2r31qsb1ts8cf5x7rpk180fn9j",
|
||||
"slug": "git-workflows",
|
||||
"version": "1.0.0",
|
||||
"publishedAt": 1770158556308
|
||||
}
|
||||
Reference in New Issue
Block a user