git-workflow
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGit Workflow
Git 工作流
Conventional Commit Format
规范提交格式
<type>(<scope>): <description>
[optional body]
[optional footer(s)]<type>(<scope>): <description>
[可选正文]
[可选页脚]Commit Types
提交类型
| Type | Purpose | Example |
|---|---|---|
| New feature for the user | |
| Bug fix for the user | |
| Code change that neither fixes nor adds | |
| Documentation only changes | |
| Adding or correcting tests | |
| Maintenance tasks, dependencies | |
| Performance improvement | |
| Formatting, whitespace, semicolons | |
| CI configuration and scripts | |
| Reverts a previous commit | |
| 类型 | 用途 | 示例 |
|---|---|---|
| 为用户添加新功能 | |
| 修复用户侧的Bug | |
| 既不修复Bug也不添加新功能的代码变更 | |
| 仅修改文档 | |
| 添加或修正测试用例 | |
| 维护任务、依赖更新 | |
| 性能优化 | |
| 格式调整、空格、分号修改 | |
| CI配置和脚本修改 | |
| 回滚之前的提交 | |
Commit Message Rules
提交消息规则
- Subject line: max 72 characters, imperative mood, no trailing period
- Body: wrap at 80 characters, explain what and why (not how)
- Footer: reference issues with or
Closes #123Refs #456 - Breaking changes: add in the footer or
BREAKING CHANGE:after type!
feat(api)!: change authentication endpoint response shape
The /auth/login endpoint now returns a nested token object instead
of a flat structure. This aligns with our OAuth2 token standard.
BREAKING CHANGE: response.token is now response.auth.access_token
Closes #892- 主题行:最多72个字符,使用祈使语气,末尾无句号
- 正文:每行不超过80个字符,说明做了什么以及为什么(而非怎么做)
- 页脚:用或
Closes #123关联议题Refs #456 - 破坏性变更:在页脚添加,或在类型后加
BREAKING CHANGE:!
feat(api)!: change authentication endpoint response shape
The /auth/login endpoint now returns a nested token object instead
of a flat structure. This aligns with our OAuth2 token standard.
BREAKING CHANGE: response.token is now response.auth.access_token
Closes #892Commit Atomicity
提交原子性
Each commit must represent exactly one logical change.
- Address a single concern (one bug fix, one feature slice, one refactor)
- Leave the codebase in a working state (tests pass, code compiles)
- Be revertable without side effects on unrelated code
- Include related test changes alongside the code change
- Never mix formatting with logic changes
- Never bundle unrelated bug fixes together
bash
undefined每个提交必须恰好代表一个逻辑变更。
- 解决单一关注点(一个Bug修复、一个功能片段、一次重构)
- 提交后代码库处于可工作状态(测试通过、代码可编译)
- 可回滚且不会对无关代码产生副作用
- 代码变更时同步包含相关测试修改
- 永远不要将格式调整与逻辑变更混合
- 永远不要将无关的Bug修复捆绑在一起
bash
undefinedStage specific hunks, specific files, then verify
暂存特定代码块、特定文件,然后验证
git add -p
git add src/auth/login.ts src/auth/login.test.ts
git diff --cached
undefinedgit add -p
git add src/auth/login.ts src/auth/login.test.ts
git diff --cached
undefinedInteractive Rebase
交互式变基
bash
git rebase -i HEAD~5 # Rebase last N commits
git rebase -i main # Rebase onto a branchbash
git rebase -i HEAD~5 # 变基最近N个提交
git rebase -i main # 变基到指定分支Rebase Commands
变基命令
| Command | Short | Effect |
|---|---|---|
| | Keep the commit as-is |
| | Keep the commit but edit the message |
| | Pause to amend the commit (add files, split) |
| | Merge into the previous commit, keep message |
| | Merge into previous commit, discard message |
| | Remove the commit entirely |
Squash WIP commits:
pick a1b2c3d feat(auth): add login endpoint
fixup e4f5g6h wip
fixup i7j8k9l fix testsFixup a commit further back:
bash
git commit --fixup=<target-sha>
git rebase -i --autosquash mainSplit a commit (mark as "edit"):
bash
git reset HEAD~1
git add src/models/user.ts
git commit -m "refactor(models): extract user validation"
git add src/routes/auth.ts
git commit -m "feat(auth): add login route"
git rebase --continue| 命令 | 缩写 | 效果 |
|---|---|---|
| | 保留提交原样 |
| | 保留提交但修改提交消息 |
| | 暂停以修改提交(添加文件、拆分) |
| | 合并到前一个提交,保留提交消息 |
| | 合并到前一个提交,丢弃提交消息 |
| | 完全移除提交 |
合并WIP提交:
pick a1b2c3d feat(auth): add login endpoint
fixup e4f5g6h wip
fixup i7j8k9l fix tests修复更早的提交:
bash
git commit --fixup=<target-sha>
git rebase -i --autosquash main拆分提交(标记为"edit"):
bash
git reset HEAD~1
git add src/models/user.ts
git commit -m "refactor(models): extract user validation"
git add src/routes/auth.ts
git commit -m "feat(auth): add login route"
git rebase --continueMerge vs Rebase
合并 vs 变基
| Use Rebase | Use Merge |
|---|---|
| Updating feature branch from main | Integrating feature branch into main ( |
| Cleaning up local commits before pushing | Shared branches others have based work on |
| Maintaining linear history on personal branch | Preserving context of parallel development |
- Never rebase commits pushed to a shared branch
- Always rebase local work before pushing
- Use to avoid unnecessary merge commits
git pull --rebase
bash
git checkout feature/login && git rebase main # Update feature
git checkout main && git merge --no-ff feature/login # Merge feature| 使用变基的场景 | 使用合并的场景 |
|---|---|
| 从主分支更新功能分支 | 将功能分支合并到主分支( |
| 推送前清理本地提交 | 其他开发者已基于此分支开展工作的共享分支 |
| 在个人分支上维护线性提交历史 | 保留并行开发的上下文 |
- 永远不要对推送到共享分支的提交执行变基
- 推送前务必对本地工作执行变基
- 使用避免不必要的合并提交
git pull --rebase
bash
git checkout feature/login && git rebase main # 更新功能分支
git checkout main && git merge --no-ff feature/login # 合并功能分支Conflict Resolution
冲突解决
- Identify: -- look for "both modified" entries
git status - Understand both sides:
bash
git show :1:path/to/file # base version git show :2:path/to/file # ours (current branch) git show :3:path/to/file # theirs (incoming branch) - Resolve each conflict block, then mark resolved:
bash
git add path/to/file git rebase --continue # or git merge --continue
| Strategy | When to Use |
|---|---|
| Accept ours | Their change is outdated or wrong |
| Accept theirs | Our change was superseded |
| Manual merge | Both changes are needed, combine them |
| Re-implement | Both sides diverged too far, rewrite the block |
Abort with: or
git merge --abortgit rebase --abort- 识别冲突: -- 查找"both modified"条目
git status - 理解双方变更:
bash
git show :1:path/to/file # 基础版本 git show :2:path/to/file # 我方(当前分支) git show :3:path/to/file # 对方(待合并分支) - 解决每个冲突块,然后标记为已解决:
bash
git add path/to/file git rebase --continue # 或 git merge --continue
| 策略 | 使用场景 |
|---|---|
| 接受我方变更 | 对方变更已过时或不正确 |
| 接受对方变更 | 我方变更已被替代 |
| 手动合并 | 双方变更都需要,需合并两者 |
| 重新实现 | 双方差异过大,重写代码块 |
终止操作: 或
git merge --abortgit rebase --abort.gitignore Patterns
.gitignore 规则
gitignore
undefinedgitignore
undefinedNode.js # Python # Java / Kotlin
Node.js # Python # Java / Kotlin
node_modules/ pycache/ *.class
dist/ *.py[cod] target/
*.log .venv/ build/
coverage/ *.egg-info/ .gradle/
node_modules/ pycache/ *.class
dist/ *.py[cod] target/
*.log .venv/ build/
coverage/ *.egg-info/ .gradle/
IDE and OS # Secrets (always ignore)
IDE 和 系统文件 # 敏感信息(务必忽略)
.idea/ *.pem
.vscode/ .key
.DS_Store .env
Thumbs.db !.env.example
| Pattern | Matches |
|-----------------|--------------------------------------|
| `*.log` | All .log files in any directory |
| `/build` | build directory in repo root only |
| `build/` | build directory anywhere |
| `**/logs` | logs directory at any depth |
| `!important` | Negate a previous ignore rule |
| `doc/**/*.txt` | txt files anywhere under doc/ |.idea/ *.pem
.vscode/ .key
.DS_Store .env
Thumbs.db !.env.example
| 规则 | 匹配对象 |
|-----------------|--------------------------------------|
| `*.log` | 所有目录下的.log文件 |
| `/build` | 仅仓库根目录下的build目录 |
| `build/` | 任意位置的build目录 |
| `**/logs` | 任意层级下的logs目录 |
| `!important` | 取消之前的忽略规则 |
| `doc/**/*.txt` | doc目录下任意位置的txt文件 |Git Hooks
Git 钩子
| Hook | Trigger | Common Use |
|---|---|---|
| Before commit is created | Lint, format, run fast tests |
| After message is written | Validate conventional commit format |
| Before push to remote | Run full test suite |
| Before editor opens | Add branch name or ticket number |
bash
#!/usr/bin/env bash| 钩子 | 触发时机 | 常见用途 |
|---|---|---|
| 提交创建前 | 代码检查、格式化、运行快速测试 |
| 提交消息编写后 | 验证提交消息是否符合规范格式 |
| 推送到远程仓库前 | 运行完整测试套件 |
| 编辑器打开前 | 添加分支名或工单编号 |
bash
#!/usr/bin/env bash.git/hooks/commit-msg
.git/hooks/commit-msg
commit_msg=$(cat "$1")
pattern='^(feat|fix|refactor|docs|test|chore|perf|style|ci|revert)((.+))?(!)?: .{1,72}'
if ! echo "$commit_msg" | grep -qE "$pattern"; then
echo "ERROR: Commit message does not follow conventional format."
exit 1
fi
undefinedcommit_msg=$(cat "$1")
pattern='^(feat|fix|refactor|docs|test|chore|perf|style|ci|revert)((.+))?(!)?: .{1,72}'
if ! echo "$commit_msg" | grep -qE "$pattern"; then
echo "ERROR: Commit message does not follow conventional format."
exit 1
fi
undefinedStashing
暂存操作
bash
git stash push -m "wip: login form validation" # Save with message
git stash list # List all stashes
git stash apply # Apply most recent (keep in list)
git stash pop # Apply and remove most recent
git stash apply stash@{2} # Apply specific stash
git stash drop stash@{0} # Drop specific stash
git stash clear # Clear all stashes
git stash push -u -m "include new files" # Include untracked files
git stash branch feature/from-stash stash@{0} # Create branch from stashbash
git stash push -m "wip: login form validation" # 带消息保存暂存
git stash list # 列出所有暂存
git stash apply # 应用最近的暂存(保留在列表中)
git stash pop # 应用并移除最近的暂存
git stash apply stash@{2} # 应用指定暂存
git stash drop stash@{0} # 删除指定暂存
git stash clear # 清空所有暂存
git stash push -u -m "include new files" # 包含未跟踪文件
git stash branch feature/from-stash stash@{0} # 从暂存创建分支Cherry-Picking
樱桃拣选(Cherry-Pick)
bash
git cherry-pick -x abc123 # Always use -x to record source
git cherry-pick abc123..def456 # Cherry-pick a range
git cherry-pick --no-commit abc123 # Stage only, do not commit
git cherry-pick --abort # Abort a conflicted cherry-pickThe flag appends for traceability.
-x(cherry picked from commit ...)bash
git cherry-pick -x abc123 # 始终使用-x记录来源
git cherry-pick abc123..def456 # 拣选一系列提交
git cherry-pick --no-commit abc123 # 仅暂存,不提交
git cherry-pick --abort # 终止冲突中的拣选操作-x(cherry picked from commit ...)Git Bisect
Git 二分查找
bash
git bisect start
git bisect bad # Mark current state as bad
git bisect good v2.1.0 # Mark a known good commitbash
git bisect start
git bisect bad # 将当前状态标记为有问题
git bisect good v2.1.0 # 标记已知正常的提交Test each checkout, then: git bisect good / git bisect bad
测试每个检出版本,然后执行:git bisect good / git bisect bad
git bisect start HEAD v2.1.0 # Automate with a test script
git bisect run npm test
git bisect reset # When done, return to original state
undefinedgit bisect start HEAD v2.1.0 # 使用测试脚本自动化执行
git bisect run npm test
git bisect reset # 完成后返回原状态
undefinedAmending Commits Safely
安全地修改提交
bash
git commit --amend -m "fix(auth): correct token expiry check"
git add forgotten-file.ts && git commit --amend --no-edit
git commit --amend --author="Name <email@example.com>"bash
git commit --amend -m "fix(auth): correct token expiry check"
git add forgotten-file.ts && git commit --amend --no-edit
git commit --amend --author="Name <email@example.com>"Safety Checklist
安全检查清单
- The commit has NOT been pushed to a shared branch
- No one else has based work on this commit
- Never amend commits on main, master, develop, or release branches
- Use instead of
--force-with-leasewhen pushing amended commits--force
- 该提交尚未推送到共享分支
- 没有其他人基于此提交开展工作
- 永远不要修改main、master、develop或release分支上的提交
- 推送修改后的提交时,使用而非
--force-with-lease--force
Daily Workflow
日常工作流
bash
git checkout main && git pull --rebase
git checkout -b feature/ticket-123-description
git add -p && git commit -m "feat(module): add feature description"
git fetch origin && git rebase origin/main
git rebase -i origin/main # Clean up before review
git push -u origin feature/ticket-123-descriptionbash
git checkout main && git pull --rebase
git checkout -b feature/ticket-123-description
git add -p && git commit -m "feat(module): add feature description"
git fetch origin && git rebase origin/main
git rebase -i origin/main # 代码审查前清理提交
git push -u origin feature/ticket-123-description