使用Git与GitHub进行远程协作
Git 是分布式版本控制系统,GitHub 则在 Git 仓库之上提供代码托管、Issue、Pull Request、代码审查和自动化检查等协作能力。在不具备原仓库写权限的情况下,GitHub 上常见的贡献方式是:Fork 原仓库,在个人 Fork 中维护功能分支,再通过 Pull Request 向原仓库提交变更。
本文以一个完整的开源贡献场景为主线,介绍 Fork、Clone、远程仓库配置、上游同步、分支开发、提交整理、Push、Pull Request、代码审查和分支清理。主流程参考 GitHub 官方 Fork 与 Pull Request 文档;rebase 相关内容以 Git 官方《Pro Git》为依据。具体项目的 CONTRIBUTING.md 和维护者要求具有更高优先级。
0. Git 与 GitHub 远程协作命令速查
示例仓库:
1 | 原仓库:alice/task-board |
将示例中的账号、仓库和分支替换为实际名称。
0.1 Fork 项目
在原仓库页面执行:
1 | Fork → Create a new fork |
0.2 SSH 配置(每台计算机执行一次)
Linux、macOS 或 Git Bash:
1 | git config --global user.name "Xiao Ming" |
Windows PowerShell:
1 | git config --global user.name "Xiao Ming" |
将公钥添加到:
1 | GitHub → Settings → SSH and GPG keys → New SSH key |
测试连接:
1 | ssh -T git@github.com |
0.3 首次克隆 Fork 并配置上游仓库
1 | git clone git@github.com:xiaoming/task-board.git |
预期远程仓库:
1 | origin → xiaoming/task-board |
0.4 每次开始新任务
同步 main:
1 | git status --short --branch |
创建任务分支:
1 | git switch -c fix/issue-128-empty-title |
修改、检查、测试并提交:
1 | git status |
首次 Push 前同步上游:
1 | git fetch upstream |
发生 rebase 冲突:
1 | git status |
推送功能分支:
1 | git push -u origin fix/issue-128-empty-title |
使用 GitHub CLI 创建 Pull Request:
1 | gh auth login |
查看 Pull Request:
1 | gh pr view --web |
0.5 Review 后继续修改
追加提交:
1 | git switch fix/issue-128-empty-title |
维护者要求先同步上游时:
1 | git fetch upstream |
--force-with-lease 仅用于自己独占、允许改写历史的功能分支。
0.6 Pull Request 合并后
1 | git switch main |
0.7 常用状态检查
1 | git status --short --branch |
1. 协作场景与目标
假设 GitHub 上存在以下项目和 Issue,名称均为示例:
1 | 原作者:alice |
本次协作需要完成以下工作:
- 将
alice/task-boardFork 到xiaoming账号; - 将个人 Fork 克隆到本地;
- 配置原仓库为上游远程仓库;
- 从最新的上游代码创建功能分支;
- 完成代码修改、测试和提交;
- 将功能分支推送到个人 Fork;
- 向
alice/task-board:main创建 Pull Request; - 根据代码审查结果继续更新;
- Pull Request 合并后同步主分支并清理功能分支。
代码流向如下:
1 | alice/task-board(原仓库) |
2. Git 远程协作模型
2.1 工作区、暂存区与本地仓库
本地修改在形成可共享提交之前,会经过工作区和暂存区:
1 | 工作区 ── git add ──> 暂存区 ── git commit ──> 本地仓库 |
- 工作区:项目文件实际所在的目录;
- 暂存区:下一次提交准备包含的变更集合;
- 本地仓库:保存在
.git目录中的提交历史和分支引用。
git add 只更新暂存区,git commit 只创建本地提交。只有执行 git push,本地提交才会被发送到远程仓库。
2.2 Git 与 GitHub
Git 负责版本管理,可以在离线环境中创建提交、切换分支和查看历史。GitHub 负责托管 Git 仓库,并提供 Pull Request、Issue、Review 和 Actions 等协作功能。
1 | Git:版本控制 |
2.3 Fork 与 Clone
Fork 和 Clone 都会产生一份仓库副本,但发生位置和用途不同:
| 操作 | 发生位置 | 结果 |
|---|---|---|
| Fork | GitHub 服务器 | 个人账号下的远程仓库 |
| Clone | 本地计算机 | 包含项目文件和 Git 历史的本地仓库 |
Fork 不会在本地创建项目目录,Clone 也不会在 GitHub 账号中创建 Fork。本文采用“先 Fork,后 Clone 个人 Fork”的顺序。
2.4 分支与提交
分支是指向某次提交的可移动引用。创建新提交后,当前分支引用会向前移动。
1 | main: A---B---C |
main 通常承担稳定主线的职责。功能开发和问题修复应在独立分支中进行,使 Pull Request 只包含一个明确任务的变更。
2.5 origin 与 upstream
Git 允许为远程仓库设置本地名称。Fork 协作通常采用以下命名约定:
| 远程名 | 指向 | 主要用途 |
|---|---|---|
origin |
个人 Fork:xiaoming/task-board |
推送个人分支 |
upstream |
原仓库:alice/task-board |
获取项目最新提交 |
数据方向可以概括为:
1 | 从 upstream 获取更新 |
origin 与 upstream 只是约定俗成的名称,并非 GitHub 预定义的特殊仓库。
3. Git 环境与 GitHub 认证
3.1 Git 安装与身份配置
确认 Git 已安装:
1 | git --version |
配置提交者姓名和邮箱:
1 | git config --global user.name "Xiao Ming" |
姓名和邮箱会写入提交历史。为了让 GitHub 正确关联提交,应使用 GitHub 账号中已经验证的邮箱,或者 GitHub 提供的 noreply 邮箱。
查看全局配置:
1 | git config --global --list |
如果特定仓库需要使用不同身份,可以在仓库目录中省略 --global:
1 | git config user.name "Work Name" |
3.2 SSH 认证
本文使用 SSH 地址访问 GitHub。先检查本机是否已有 SSH 密钥:
1 | ls -al ~/.ssh |
如果没有可用的 Ed25519 密钥,可以生成新密钥:
1 | ssh-keygen -t ed25519 -C "xiaoming@example.com" |
默认生成:
1 | ~/.ssh/id_ed25519 私钥 |
私钥不应上传到仓库、网盘或通信工具。GitHub 账号只需要配置公钥。
显示并复制公钥:
1 | cat ~/.ssh/id_ed25519.pub |
进入 GitHub 的以下位置添加公钥:
1 | 头像 → Settings → SSH and GPG keys → New SSH key |
完成后测试连接:
1 | ssh -T git@github.com |
首次连接时,应核对终端显示的主机指纹与 GitHub 官方公布的指纹。认证成功后会显示包含 GitHub 用户名和 successfully authenticated 的提示。由于 GitHub 不提供普通 Shell,ssh -T 即使认证成功也可能返回退出码 1,应以认证提示内容判断结果。
3.3 HTTPS 认证
HTTPS 远程地址的形式为:
1 | https://github.com/xiaoming/task-board.git |
HTTPS 可以使用 GitHub CLI、Git Credential Manager 或 Personal Access Token 完成认证。GitHub 的 Git 命令行认证不接受账号登录密码。
SSH 和 HTTPS 任选一种即可。后续示例统一使用 SSH 地址。
4. 贡献规范与仓库状态检查
4.1 项目贡献规范
开始开发前,应阅读原仓库中的以下文件:
1 | README.md |
需要确认的内容包括:
- 依赖安装和本地启动方式;
- 分支命名和提交信息规范;
- 必须执行的测试和格式检查;
- Issue 的认领或讨论要求;
- Pull Request 的目标分支;
- 项目采用的 merge、rebase 或 squash 策略。
同时应阅读目标 Issue 的完整讨论,确认任务尚未由其他贡献者处理,并确保实现方案符合维护者预期。
4.2 仓库根目录
1 | pwd |
git rev-parse --show-toplevel 用于显示当前 Git 仓库的根目录。如果出现 not a git repository,说明终端当前不在 Git 仓库中。
4.3 当前分支
1 | git branch --show-current |
修改、提交、rebase 和 Push 之前均应确认当前分支,避免把功能提交写入 main 或其他无关分支。
4.4 工作区状态
1 | git status |
Changes not staged for commit 表示存在未暂存修改,Changes to be committed 表示暂存区中已有内容,Untracked files 表示存在尚未由 Git 跟踪的文件。
4.5 远程仓库配置
1 | git remote -v |
该命令用于核对 origin 和 upstream 的 Fetch、Push 地址。涉及远程操作时,应先确认目标地址与预期一致。
5. Fork、Clone 与远程仓库配置
5.1 Fork 原仓库
打开原仓库:
1 | https://github.com/alice/task-board |
点击页面右上角的 Fork,选择个人账号 xiaoming 作为 Owner,然后创建 Fork。完成后,个人账号下会出现:
1 | https://github.com/xiaoming/task-board |
此时仓库关系为:
1 | alice/task-board 原仓库 |
如果贡献者已经拥有原仓库写权限,项目可能要求直接在原仓库创建功能分支,此时不一定需要 Fork。本文讨论的是没有原仓库写权限的贡献模式。
5.2 Clone 个人 Fork
在个人 Fork 页面点击 Code,复制 SSH 地址:
1 | git clone git@github.com:xiaoming/task-board.git |
Clone 的目标应是个人 Fork,而不是原仓库:
1 | 个人 Fork:git@github.com:xiaoming/task-board.git |
普通贡献者通常没有原仓库写权限。如果将原仓库克隆为 origin,后续 Push 可能因权限不足而失败。
Clone 完成后检查:
1 | git status |
预期远程配置为:
1 | origin git@github.com:xiaoming/task-board.git (fetch) |
git clone 会自动将被克隆的仓库命名为 origin。
5.3 添加 upstream
将原仓库添加为 upstream:
1 | git remote add upstream git@github.com:alice/task-board.git |
再次检查:
1 | git remote -v |
预期输出:
1 | origin git@github.com:xiaoming/task-board.git (fetch) |
如果出现 remote upstream already exists,应先查看已有地址:
1 | git remote get-url upstream |
地址错误时使用以下命令修改:
1 | git remote set-url upstream git@github.com:alice/task-board.git |
6. 默认分支同步
本地 main 应跟踪原仓库的 upstream/main,个人 Fork 的 origin/main 则保存同步后的远程副本。
6.1 同步流程
确认工作区状态:
1 | git status |
切换到本地 main:
1 | git switch main |
获取原仓库的最新提交:
1 | git fetch upstream |
将本地 main 快进到 upstream/main:
1 | git merge --ff-only upstream/main |
更新个人 Fork:
1 | git push origin main |
同步完成后的关系为:
1 | upstream/main = 本地 main = origin/main |
6.2 upstream 与 origin 的同步职责
在 Fork 工作流中:
1 | upstream/main 原项目的主分支 |
git pull --ff-only origin main 只能从个人 Fork 拉取。如果个人 Fork 已经落后于原仓库,该命令无法获取原项目的最新提交。因此,原仓库 upstream 应作为项目更新来源。
6.3 merge --ff-only 的使用依据
GitHub 官方命令行同步文档使用 git merge upstream/main。本文在该流程中增加 --ff-only,原因是本地 main 被约定为 upstream/main 的干净镜像,不承载个人开发提交。
--ff-only 具有以下行为:
- 本地
main仅落后于上游时执行快进; - 本地
main存在额外提交时停止; - 不创建新的合并提交;
- 不改写已有提交。
这是针对当前分支模型增加的严格安全约束,并非所有项目必须使用的唯一同步命令。
6.4 默认分支与 rebase 的边界
本地 main 不应包含个人开发提交。如果在 main 上执行 git rebase upstream/main,Git 可能将误提交在 main 上的个人提交重新应用到最新上游之后,从而掩盖分支使用错误。
本文采用以下策略:
1 | 同步 main:git merge --ff-only upstream/main |
如果 merge --ff-only 返回 Not possible to fast-forward,说明本地 main 已与上游分叉。此时应先检查提交图:
1 | git log --oneline --graph --decorate --all -20 |
在确认本地额外提交是否需要保留之前,不应执行 reset --hard 或强制 Push。
7. 功能分支创建
在最新的 main 上创建并切换到任务分支:
1 | git switch -c fix/issue-128-empty-title |
确认当前分支:
1 | git branch --show-current |
预期输出:
1 | fix/issue-128-empty-title |
常见分支前缀包括:
| 前缀 | 用途 | 示例 |
|---|---|---|
feature/ |
新功能 | feature/task-filter |
fix/ |
问题修复 | fix/issue-128-empty-title |
docs/ |
文档更新 | docs/setup-guide |
refactor/ |
代码重构 | refactor/task-service |
chore/ |
工程维护 | chore/update-dependencies |
一个功能分支应对应一个明确任务,避免把无关功能、依赖更新和格式化修改放入同一个 Pull Request。
8. 本地开发与提交
假设本次修改涉及:
1 | src/components/TaskForm.js |
8.1 变更检查与测试
开发过程中可以使用以下命令检查状态和差异:
1 | git status |
git status显示修改、新增和暂存状态;git diff显示工作区中尚未暂存的代码差异。
完成修改后,按照项目文档执行测试和格式检查。例如:
1 | npm test |
具体命令应以项目的 README.md、CONTRIBUTING.md 和构建配置为准。
8.2 提交内容边界
提交中不应包含:
1 | .env |
.gitignore 只能阻止尚未跟踪的文件进入仓库。已经被 Git 跟踪的文件不会因为后来加入 .gitignore 而自动停止跟踪。
如果凭据曾经进入提交,应立即吊销并更换凭据。仅删除文件不能消除已经发生的泄露。
8.3 暂存与提交
明确暂存当前任务涉及的文件:
1 | git add src/components/TaskForm.js tests/TaskForm.test.js |
检查即将提交的内容:
1 | git diff --staged |
创建提交:
1 | git commit -m "fix: reject tasks with an empty title" |
验证结果:
1 | git status |
提交信息应准确描述变更目的,例如:
1 | fix: reject tasks with an empty title |
在未检查状态的情况下直接使用 git add .,可能将无关文件或敏感内容一并加入暂存区。显式指定文件能够提供更清晰的提交边界。
9. 功能分支变基
开发期间,upstream/main 可能已经包含其他贡献者的新提交。在项目允许整理个人提交的前提下,可以在首次 Push 前将功能分支变基到最新上游。
尚未共享的个人功能分支是 rebase 风险较低的使用场景。rebase 不是创建 Pull Request 的必要条件,项目对提交历史的要求应以贡献规范为准。
9.1 rebase 原理
假设功能分支基于提交 C 创建,并产生提交 D 和 E;与此同时,上游增加了 F 和 G:
1 | D---E 功能分支 |
执行:
1 | git rebase upstream/main |
Git 会找到共同祖先 C,暂存功能分支独有的修改,将功能分支移动到 G,然后按顺序重新应用 D 和 E:
1 | A---B---C---F---G upstream/main |
D' 和 E' 的父提交已经改变,因此它们是具有新提交哈希的提交。rebase 改写的是当前功能分支,不会修改 upstream/main。
9.2 执行流程
确认当前分支和工作区:
1 | git branch --show-current |
获取上游更新:
1 | git fetch upstream |
确保位于目标功能分支:
1 | git switch fix/issue-128-empty-title |
执行变基:
1 | git rebase upstream/main |
变基完成后应重新执行项目测试,因为功能代码现在基于更新后的上游代码:
1 | npm test |
9.3 冲突处理
Git 会逐个重新应用功能提交。如果某个提交与上游修改了相同位置,rebase 会暂停。
查看冲突状态:
1 | git status |
冲突文件使用以 <<<<<<< 开始、以 ======= 分隔、以 >>>>>>> 结束的标记划分两侧内容。处理流程如下:
- 理解功能提交与上游修改的目的;
- 编辑为最终正确内容;
- 删除冲突标记;
- 执行相关测试;
- 暂存已经解决的文件;
- 继续 rebase。
1 | git add path/to/conflicted-file |
如果后续提交再次产生冲突,应重复上述步骤。
取消本次变基:
1 | git rebase --abort |
该命令会将分支恢复到 rebase 开始前的状态。
git rebase --skip 会跳过当前提交并可能丢失修改。只有确认该提交已经被上游完整替代时才应使用。
9.4 rebase 与 merge
功能分支也可以通过 merge 同步上游:
1 | git merge upstream/main |
两种策略的区别如下:
| 对比项 | rebase | merge |
|---|---|---|
| 历史结构 | 线性排列 | 保留分叉,可能产生合并提交 |
| 原提交哈希 | 改变 | 不变 |
| 是否改写历史 | 是 | 否 |
| 尚未 Push 的个人分支 | 适用 | 适用 |
| 已公开或多人协作的分支 | 谨慎使用 | 通常更稳妥 |
Git 官方并未规定 rebase 或 merge 在所有项目中具有绝对优先级。项目贡献规范和团队协作方式决定最终策略。
9.5 rebase 安全边界
不应随意 rebase 以下分支或提交:
main等公共分支;- 多人共同开发的分支;
- 其他开发者已经基于其继续工作的提交;
- 提交关系尚未确认、仅为了消除 Push 报错的分支。
Git 官方《Pro Git》给出的核心原则是:不要对已经公开且其他人可能基于其继续工作的提交进行 rebase。
10. 功能分支推送
将本地功能分支推送到个人 Fork:
1 | git push -u origin fix/issue-128-empty-title |
参数含义:
1 | origin 目标远程仓库 |
-u 是 --set-upstream 的缩写,但它并不表示将代码推送到名为 upstream 的远程。该选项会使本地功能分支跟踪:
1 | origin/fix/issue-128-empty-title |
后续 Push 可以简化为:
1 | git push |
查看跟踪关系:
1 | git branch -vv |
Push 只会把功能分支上传到个人 Fork,不会修改原仓库的 main。
11. Pull Request 创建
Push 成功后,可以从原仓库或个人 Fork 页面进入 Pull Request 创建界面。GitHub 通常会显示 Compare & pull request;手工创建时需要选择 compare across forks。
11.1 Base 与 Compare 配置
正确配置如下:
| GitHub 选项 | 值 | 含义 |
|---|---|---|
| base repository | alice/task-board |
接收变更的原仓库 |
| base | main |
原仓库目标分支 |
| head fork | xiaoming/task-board |
提供变更的个人 Fork |
| compare | fix/issue-128-empty-title |
个人功能分支 |
合并方向为:
1 | xiaoming/task-board:fix/issue-128-empty-title |
Base 与 Compare 选反会导致请求方向错误。
11.2 提交与文件审查
创建 Pull Request 前应检查 Commits 和 Files changed:
- 提交是否只属于当前任务;
- 文件是否与 Issue 相关;
- 是否包含依赖目录、构建结果或无关格式化;
- 是否包含凭据、私钥或个人配置;
- 测试结果是否完整。
旧任务提交出现在当前 Pull Request 中,通常说明功能分支不是从干净的目标分支创建,不能通过修改描述解决。
11.3 标题与描述
Pull Request 标题应概括最终变更:
1 | fix: reject tasks with an empty title |
描述示例:
1 | ## 问题 |
Fixes #128 可以关联对应 Issue,并在符合 GitHub 关闭规则时随 Pull Request 合并自动关闭 Issue。
尚未完成但需要提前讨论的变更,可以创建 Draft Pull Request,完成后再标记为 Ready for review。
11.4 维护者编辑权限
Allow edits from maintainers 允许原仓库维护者直接修改 Pull Request 分支。如果 Fork 包含 GitHub Actions 工作流,GitHub 可能同时提示开放与 Secrets 相关的权限。
是否启用该选项应根据页面显示的实际权限范围和项目协作要求决定。
12. 代码审查与分支更新
12.1 Review 修改
收到 Review 意见后,应继续在原功能分支修改:
1 | git switch fix/issue-128-empty-title |
完成修改和测试后创建新提交:
1 | git add tests/TaskForm.test.js |
GitHub 会自动更新现有 Pull Request,无需重新 Fork、创建新分支或创建新 Pull Request。
Review 讨论的处理应包含修改内容和验证结果。实现方案发生变化时,应同步更新 Pull Request 描述。
12.2 已公开分支的 merge 更新
Review 期间 upstream/main 可能继续前进。先获取更新并切换到功能分支:
1 | git fetch upstream |
对于已经公开并进入审查的分支,merge 不会改写已有提交:
1 | git merge upstream/main |
解决冲突并完成测试后执行:
1 | git push |
该方式可能产生合并提交,但不需要强制推送。
12.3 已公开分支的 rebase 更新
如果项目明确要求线性历史,并且功能分支没有被其他开发者使用,可以按照项目规范执行:
1 | git rebase upstream/main |
由于提交哈希已经改变,普通 Push 会被拒绝。执行强制更新前,应获取个人 Fork 的最新状态并检查提交关系:
1 | git fetch origin |
确认远程功能分支没有其他人的新增提交后,可以使用:
1 | git push --force-with-lease origin fix/issue-128-empty-title |
--force-with-lease 会在远程分支与本地预期不一致时拒绝覆盖,比 --force 更安全。但它仍然会改写远程历史,可能使旧 Review 评论失去对应代码位置,也可能导致已有审批失效。
多人共同使用的分支不应自行 rebase 后强制推送。
13. Pull Request 合并后的同步与清理
13.1 更新默认分支
Pull Request 合并后,变更已经进入 alice/task-board:main。本地和个人 Fork 需要再次同步:
1 | git switch main |
13.2 删除功能分支
删除本地功能分支:
1 | git branch -d fix/issue-128-empty-title |
删除个人 Fork 上的远程分支:
1 | git push origin --delete fix/issue-128-empty-title |
如果已经在 GitHub Pull Request 页面删除远程分支,则不需要重复执行远程删除命令。
13.3 Squash 合并后的分支删除
使用 Squash and merge 时,功能分支的多个提交会被压缩为一个新提交。原功能提交不再是 main 的直接祖先,因此 git branch -d 可能拒绝删除。
执行强制删除前应确认:
- Pull Request 已经合并;
- 最新
upstream/main已包含目标修改; - 功能分支不存在尚未保存的额外工作。
确认后可以执行:
1 | git branch -D fix/issue-128-empty-title |
-D 会跳过未合并检查,不应在未确认分支内容时使用。
13.4 清理远程跟踪引用
1 | git fetch --prune origin |
--prune 只清理本地保存的、远程已不存在的引用,不会删除仍存在的远程分支。
14. 后续贡献流程
Fork、Clone 和 upstream 配置通常只需执行一次。后续任务从同步默认分支开始:
1 | git status |
标准循环为:
1 | 同步 main |
每个任务都应从最新、干净的 main 创建新分支。复用已经合并的旧功能分支可能使新 Pull Request 混入旧提交。
15. 常见故障排查
15.1 origin 指向原仓库
检查远程地址:
1 | git remote -v |
如果 origin 指向 alice/task-board,将其修改为个人 Fork:
1 | git remote set-url origin git@github.com:xiaoming/task-board.git |
原仓库应配置为:
1 | git remote add upstream git@github.com:alice/task-board.git |
如果 upstream 已存在,应使用 git remote set-url upstream ... 修改,而不是重复添加。
15.2 在 main 上产生功能修改
如果修改尚未提交,可以从当前位置创建功能分支:
1 | git switch -c fix/issue-128-empty-title |
未提交修改通常会随工作区进入新分支。
如果已经在 main 创建提交,应先创建分支保存当前提交,再分析如何恢复 main,不应直接执行 reset --hard。
15.3 Pull Request 方向错误
正确方向为:
1 | 个人 Fork 的功能分支 → 原仓库的目标分支 |
创建时应核对 base repository、base、head fork 和 compare。
15.4 non-fast-forward
Push 被拒绝并显示 non-fast-forward,表示远程分支包含本地没有的提交,或者本地历史已经被 rebase 等操作改写。
先获取并查看关系:
1 | git fetch origin |
在确认远程新增提交的来源和保留方式之前,不应直接强制 Push。
15.5 未提交修改阻止分支切换
检查当前修改:
1 | git status |
完整修改可以提交;临时修改可以使用 stash;确认不需要的修改可以使用 restore。处理方式取决于修改是否仍需保留。
15.6 GitHub 未显示本地提交
git commit 只创建本地提交,还需要执行 git push。可以检查当前分支和跟踪关系:
1 | git branch -vv |
15.7 原仓库未出现已 Push 的修改
Push 到个人 Fork 后,还需要向原仓库创建 Pull Request。只有 Pull Request 被合并,变更才会进入原仓库目标分支。
15.8 Pull Request 包含无关提交
常见原因包括:
- 从旧功能分支创建新分支;
- 在同一分支处理多个任务;
- 本地
main包含个人提交; - Pull Request 的 base 分支错误。
预防方式是先同步 upstream/main,再从干净的本地 main 创建独立任务分支。
15.9 SSH 公钥认证失败
出现 Permission denied (publickey) 时,检查:
1 | ssh -T git@github.com |
需要确认公钥已添加到正确的 GitHub 账号、SSH 使用了预期私钥,并且仓库地址与账号权限一致。
16. 变更撤销与恢复
撤销操作前应先运行 git status,并确认目标变更是否已经提交或 Push。
16.1 取消暂存
取消暂存但保留工作区修改:
1 | git restore --staged path/to/file |
16.2 丢弃工作区修改
1 | git restore path/to/file |
该命令会丢失目标文件中尚未提交的修改,执行前必须确认内容不再需要。
16.3 修改最近一次本地提交
对于尚未 Push 的最近一次提交,可以补充文件或修改提交信息:
1 | git add forgotten-file |
amend 会生成新的提交哈希。提交已经公开后,不应在未评估影响时使用。
16.4 撤销已共享提交
对于已经 Push 或合并的提交,通常使用反向提交:
1 | git revert <commit-id> |
revert 保留原历史,适合公共分支。reset 会移动分支引用,不适合作为共享历史的常规撤销方式。
16.5 临时保存工作区
1 | git stash push -u -m "wip: issue 128" |
恢复暂存内容:
1 | git stash pop |
stash 适合短期切换任务,不应长期代替分支和提交。
16.6 使用 reflog 定位丢失提交
1 | git reflog |
reflog 记录本地分支和 HEAD 最近移动过的位置。误删分支或错误 reset 后,提交可能仍可通过 reflog 找回。发生历史操作错误后,应停止继续改写历史并记录相关提交哈希,再制定恢复方案。
17. 命令流程汇总
17.1 首次建立协作仓库
在 GitHub 页面完成 Fork 后执行:
1 | git clone git@github.com:xiaoming/task-board.git |
17.2 创建任务分支
1 | git status |
17.3 开发与提交
1 | git status |
17.4 项目允许时执行变基
1 | git fetch upstream |
17.5 推送与 Pull Request
1 | git push -u origin fix/issue-128-empty-title |
Pull Request 方向:
1 | xiaoming/task-board:fix/issue-128-empty-title |
17.6 合并后同步与清理
1 | git switch main |
18. 总结
Fork 模式下的远程协作由三份仓库构成:原仓库、个人 Fork 和本地仓库。upstream 用于获取原项目更新,origin 用于接收个人功能分支。
默认分支和功能分支承担不同职责:
1 | main:使用 merge --ff-only 保持为 upstream/main 的干净镜像 |
完整流程包括:Fork 原仓库、Clone 个人 Fork、配置 upstream、同步 main、创建功能分支、开发和提交、推送个人分支、创建 Pull Request、处理 Review,以及合并后的同步与清理。
在执行分支切换、历史整合和远程操作前,应持续检查:
1 | git status |
这些状态信息能够明确当前操作对象,是避免分支污染、错误 Push 和历史误改的基础。
参考资料
[4] GitHub Docs:从 Fork 创建 Pull Request
[5] GitHub Docs:通过 SSH 连接 GitHub
[6] Pro Git:变基
[7] Git 官方文档
[10] Git 官方文档:git restore







