Git
本文缩写(英文全称与中文含义)
| 缩写 | 英文全称/名称 | 中文含义 |
|---|---|---|
| CI | Continuous Integration | 持续集成 |
| CLI | Command-Line Interface | 命令行接口 |
| PR | Pull Request | 代码合并请求 |
| URL | Uniform Resource Locator | 统一资源定位符,通常指访问地址 |
Links#
https://git-scm.com/docs
https://git-scm.com/book/en/v2
https://cli.github.com/manual/gh_pr_create
https://cli.github.com/manual/gh_release_create
https://docs.github.com/en/pull-requests
https://docs.github.com/en/repositories/releasing-projects-on-github1. Important Points#
Git command mental model:
working tree:
local files
index / stage:
files prepared for next commit
local repository:
local commits
remote repository:
origin / upstream on GitHub / GitLabsafety rules:
git status before destructive commands
prefer git revert for commits already pushed
prefer --force-with-lease over --force
do not reset shared branch unless team agrees2. Basic#
本页命令是按场景独立选用的示例,不应从头到尾连续执行。main、feature/order-api、提交哈希和文件路径都要替换为实际值。
HEAD 通常指当前检出的分支最新提交;HEAD~1 是沿第一父提交回退一步。origin/main 是本地保存的远端分支状态,只有 fetch 等操作后才会更新;它与本地 main 是两个不同引用。
在分支整合或回退前,先确认当前分支、上游关系与未提交改动:
git branch --show-current
git branch -vv
git statusgit branch -vv 中的 ahead/behind 是与本地缓存的上游状态比较;需要最新远端状态时先执行 git fetch origin。
init repository#
git initclone repository#
git clone git@github.com:example/order-api.gitcheck status#
git statusview diff#
比较工作目录与暂存区,显示已跟踪文件中尚未暂存的修改;新建且未跟踪的文件内容不会显示在这里。
git diffview staged diff#
比较暂存区与 HEAD,显示下一次 commit 将包含的修改。修改文件并 add 后再次编辑,后续编辑还需再次 add 才会进入提交。
git diff --cachedadd one file#
git add src/orders/service.tsadd all changes#
将当前目录及其子目录中的新增、修改和删除加入暂存区;在子目录运行时,不会覆盖仓库其他目录。add 不创建提交,也不上传远端。
git add .add all repository changes from any directory#
git add -A :/use this when:
current shell is inside a subdirectory
you want to stage changes across the whole repository
note:
:/ means repository root pathspec
git add . only stages changes under the current directorycommit#
把当前暂存区生成一个本地提交;未暂存的编辑不会包含其中,也不会自动 push。
git commit -m "feat: add order create api"amend last commit#
替换当前分支的最新提交,同时纳入当前暂存内容并修改提交说明;即使只改说明,commit ID 也会变化。仅用于尚未分享的提交;保留原说明用 --no-edit,相关忽略文件场景见第 13 节。
git commit --amend -m "feat: add order create endpoint"show commit history#
git log --oneline --decorate --graph --allshow one commit#
git show a1b2c3d3. Remote#
list remotes#
git remote -vadd remote#
git remote add origin git@github.com:example/order-api.gitchange remote URL#
git remote set-url origin git@github.com:example/new-order-api.gitfetch remote#
下载 origin 的提交并按远端配置更新 origin/main 等远端跟踪引用;通常不会修改本地 main、当前分支、暂存区或工作目录。需要整合到当前分支时,再执行 merge 或 rebase。
git fetch originfetch all remotes#
抓取各个已配置远端(配置为跳过的除外),并清理远端已删除分支对应的远端跟踪引用;不会删除本地开发分支,也不会自动 merge。这里的 prune 默认不等于删除本地 tag。fetch 说明
git fetch --all --prunepull current branch#
从当前分支配置的上游抓取并整合更新;通常是同名远端分支,但以 git branch -vv 显示的配置为准。没有上游时需要先设置或显式指定远端与分支。
git pullpull remote branch into current branch#
从 origin 抓取远端 main,再把它整合到当前检出的分支。 例如当前是 feature/order-api,更新的是这个 feature 分支;命令不会切换到本地 main,也不会向远端提交修改。
git pull origin mainpull 包含 fetch 和后续整合两步;上面省略了整合方式,实际行为受 Git 版本及 pull.rebase、pull.ff 等配置影响,分叉时也可能拒绝执行。为表达明确意图,下面三种方式选择一种:pull 官方说明
| 目的 | 命令 | 对当前分支的影响 |
|---|---|---|
| 使用 merge 整合远端 main | git pull --no-rebase --ff origin main |
可快进时移动分支指针;双方都有新提交时创建 merge commit |
| 使用 rebase 整合远端 main | git pull --rebase origin main |
将当前分支需要重放的提交放到抓取的 main 之后,重放后的 commit ID 改变 |
| 只接受快进 | git pull --ff-only origin main |
当前分支可沿已有历史直接前进时更新;发生分叉时失败,不自动 merge/rebase |
merge 与 rebase 是两种不同的整合方式,rebase 不会随后再自动执行一次 merge。merge 模式用 --ff 明确允许快进或分叉合并,避免仓库的 pull.ff=only 配置改变示例意图。
若想先检查远端新增内容,再决定如何整合,可拆成两步:
git fetch origin
git log --oneline HEAD..origin/main
git merge origin/main
# 或用 git rebase origin/main 代替上面的 merge,不必两者都执行。HEAD..origin/main 列出远端 main 中当前分支尚未包含的提交。pull 前先提交或 stash 本地编辑;发生冲突后,按第 11 节解决或中止对应的 merge/rebase。
push current branch#
把本地已提交内容发送到配置的远端分支;未提交的文件不会上传。目标由上游、push.default 等配置决定,不能仅凭当前分支名猜测;默认 simple 模式通常推送同名上游分支。
git pushpush and set upstream#
将本地 feature/order-api 推送到 origin 的同名分支,并设置它的上游关系,便于之后在该分支运行 pull/push。此命令按显式分支名推送,不会因为当前检出其他分支就改推其他分支。
git push -u origin feature/order-api4. Branch#
list local branches#
git branchlist all branches#
git branch -acreate branch#
从当前 HEAD 创建分支指针;不会切换分支,也不会把未提交改动保存为提交。
git branch feature/order-apiswitch branch#
切换当前分支并更新工作目录。可兼容的未提交改动可能随你一起切换;若会覆盖改动,Git 会拒绝,先 commit 或 stash。
git switch feature/order-apicreate and switch branch#
以当前 HEAD 为起点创建并切换分支。若想基于最新远端 main 开发,先 fetch,再使用 git switch -c feature/order-api origin/main;如只想把 main 当起点而不设成上游,可加 --no-track。
git switch -c feature/order-apicreate local branch from remote branch#
先执行 git fetch origin 获取最新引用,再从 origin/feature/order-api 创建并切换到本地分支;常规配置下会建立与该远端分支的上游跟踪关系,可用 git branch -vv 确认。
git switch -c feature/order-api origin/feature/order-apifetch remote branch directly into a local branch#
这里执行的是 fetch,不是 pull。冒号左侧是远端来源分支,右侧是要创建或更新的本地分支;命令不切换分支、不把内容 merge 到当前分支。
git fetch origin feature/order-api:feature/order-api若目标本地分支已经存在,通常要求更新可快进;Git 默认拒绝直接 fetch 到当前工作树已检出的分支。日常同步当前分支应使用前面的 pull,或先 fetch 再 merge/rebase。
rename current branch#
git branch -m feature/order-api-v2delete local branch#
只删除本地分支,不删除远端分支。-d 会检查分支是否已合并到其上游(未设上游时检查 HEAD);不能删除被任一 worktree 检出的分支。
git branch -d feature/order-apiforce delete local branch#
跳过未合并检查,可能丢失该分支独有提交的正常入口;不会删除远端分支。先检查 git log --oneline HEAD..feature/order-api,确认不再需要这些提交。
git branch -D feature/order-apidelete remote branch#
删除服务器 origin 上的指定分支,不删除同名本地分支;这是对共享远端的修改。
git push origin --delete feature/order-api5. Merge / Rebase#
这些操作的目标是当前检出的分支。引用 origin/main 前先 fetch;merge/rebase 本身不访问远端。merge 保留已有提交,必要时增加合并提交;rebase 重放当前分支提交,通常改变其 ID,避免自行重写他人正在使用的历史。
merge branch into current branch#
将本地 feature/order-api 的历史整合到当前分支;例如先 git switch main 再运行下面的命令,就是把 feature 合入本地 main。被合入的 feature 分支指针不因此移动。
git merge feature/order-apimerge main into current branch#
将本地缓存的远端 main 状态合入当前分支;例如当前是 feature,就是把 main 的更新带入 feature。可快进时默认不产生 merge commit;需要保留显式合并节点时用 git merge --no-ff origin/main。
git merge origin/mainabort merge#
在尚未完成的合并中尝试恢复合并前状态;不是撤销一个已经提交的 merge。合并前若有未提交编辑,可能无法完整恢复,因此应先 commit 或 stash。
git merge --abortrebase current branch on main#
以 origin/main 为新基点,重放当前分支相对上游需要保留的提交;当前是 feature 时,被重写的是 feature,不是 main。rebase 后不需要再 merge 一次 main。
git rebase origin/maincontinue rebase after resolving conflicts#
修改冲突文件、检查内容并执行 git add 后继续;rebase 可能在后续提交再次停下,需要逐次解决,不用手动为每一步另建普通 commit。
git rebase --continueabort rebase#
中止尚未完成的 rebase,回到开始前的分支状态;不能用它撤销已经完成的 rebase,完成后可通过 reflog 寻找原提交。
git rebase --abortcherry-pick commit#
将指定提交的改动应用到当前分支,通常生成一个新提交;不会切换到原分支或合入其全部历史。冲突解决并 add 后运行 git cherry-pick --continue,放弃则用 git cherry-pick --abort。
git cherry-pick a1b2c3d6. Undo / Rollback#
撤销前先区分要改哪一层:工作目录、暂存区、当前分支历史。以下命令分别处理不同情况,不要连续执行。reset 官方说明
| 操作 | 当前分支指针 | 暂存区 | 工作目录 |
|---|---|---|---|
restore --staged FILE |
不变 | 文件恢复到 HEAD 版本 | 不变,编辑仍保留 |
restore FILE |
不变 | 不变 | 文件恢复到暂存区版本 |
reset --soft COMMIT |
移到目标提交 | 不变 | 不变 |
reset --mixed COMMIT(默认) |
移到目标提交 | 恢复到目标提交 | 不变 |
reset --hard COMMIT |
移到目标提交 | 恢复到目标提交 | 覆盖为目标版本,可能丢失本地改动 |
revert COMMIT |
通常新增撤销提交 | 完成后与新提交一致 | 应用反向改动 |
unstage file#
取消该文件的暂存,保留工作目录里的编辑;不会撤销已有 commit。
git restore --staged src/orders/service.tsdiscard file changes#
用暂存区版本覆盖工作目录文件,丢弃该文件尚未暂存的改动。若它已 add,暂存内容仍在;不要将此命令理解为总是恢复到最新 commit。
git restore src/orders/service.tsrestore file from main#
从 origin/main 取指定文件覆盖当前工作目录,不切换分支,也不 merge 整个 main;暂存区不变。先 fetch 保证来源新鲜,检查 git diff 后 add/commit,才会把该文件版本写入当前分支历史。
git restore --source origin/main src/orders/service.tsrevert one commit#
在当前分支创建反向提交来抵消目标提交的改动,保留原历史,适合已分享的提交。冲突解决并 add 后用 git revert --continue,放弃则用 git revert --abort。
git revert a1b2c3drevert merge commit#
-m 1 表示以合并提交的第一个父提交作为主线,撤销 merge 相对该父提交带来的改动,不是 main 分支编号。先用 git show --no-patch --pretty=raw a1b2c3d 检查父提交顺序;撤销 merge 后直接再次 merge 原分支,通常不会重新引入已撤销的旧改动,需要结合 revert-of-revert 等方案处理。
git revert -m 1 a1b2c3dcancel a revert commit#
If git revert already created a new commit, the safest way to cancel it is usually to revert the revert.
git log --oneline
git revert REPLACE_REVERT_COMMIT_HASHExample:
git revert abc1234This creates another commit that restores the content removed by the previous revert. Use this when the revert commit has already been pushed or shared.
If the revert commit is only local and not pushed yet, you can remove the last commit instead.
下面的 HEAD~1 仅适用于要删除的 revert 正好是最新提交;如果它后面还有其他提交,不可照抄,否则移除的是另一个提交。
Keep the revert changes staged:
git reset --soft HEAD~1Discard the revert commit and its working tree changes:
git reset --hard HEAD~1decision:
already pushed:
git revert <revert_commit_hash>
not pushed, want to keep changes staged:
git reset --soft HEAD~1
not pushed, want to delete the revert completely:
git reset --hard HEAD~1
danger:
reset --hard discards local working tree changes
check git status before using itreset last commit but keep changes staged#
将当前分支回退一个提交,暂存区与工作目录保持原样。原最新提交相对其父提交的改动通常会成为已暂存改动,可整理后重新 commit;已有未暂存编辑仍保持未暂存。
git reset --soft HEAD~1reset last commit and keep changes unstaged#
默认是 --mixed:当前分支回退一个提交,暂存区回到该提交,工作目录保持原样。原最新提交的改动变为未暂存;其中新增文件可能变为未跟踪文件。
git reset HEAD~1reset working tree to remote branch#
把当前分支本身重置到 origin/main,同时覆盖暂存区和工作目录,不只是同步文件。即使当前分支叫 feature,也会让 feature 指向 main 的提交;它不会切换分支或修改服务器上的 main。先 fetch,并确认本地独有提交与编辑已保留。
git reset --hard origin/maindanger:
reset --hard discards local working tree changes
use only after checking git statusfind old commit with reflog#
查看本地 HEAD 的移动记录,可寻找 reset/rebase 前的提交。reflog 不等于远端历史或永久备份,记录会过期;它通常无法恢复从未提交或 stash 的编辑。
git reflogrestore branch to reflog point#
HEAD@{3} 是本地 HEAD reflog 中往前数第 3 条状态,不表示 3 个父提交。先通过 git reflog 找到目标,最好复制实际 commit ID,避免后续操作改变序号。下面的 hard reset 会覆盖当前分支和本地编辑。
git reset --hard HEAD@{3}7. Stash#
stash 保存临时未提交改动供稍后恢复,是本地存储,不会随 push 上传。默认 stash push 保存已跟踪文件的暂存与未暂存改动,通常将这些文件恢复到 HEAD;包含未跟踪文件用 -u,连被忽略文件也包含用 -a,后者可能收走大型本地产物。stash 官方说明
stash current changes#
git stash push -m "wip order api"list stashes#
git stash listapply latest stash#
把最新 stash 应用到当前工作目录,保留 stash 条目;也可以指定 stash@{n}。不自动切换回原分支;需要尝试恢复原暂存状态时加 --index。
git stash applyapply and drop latest stash#
应用成功后删除该 stash;若发生冲突,stash 通常保留。先解决并检查结果,再决定是否 drop,避免重复 pop 同一份改动。
git stash popdrop one stash#
删除指定 stash 条目,不会应用其中的改动;先用 git stash show -p 'stash@{0}' 确认内容。条目删除后编号会变化。
git stash drop stash@{0}clear all stashes#
清空本地所有 stash,不会恢复其中的编辑;先确认没有需要保留的工作。
git stash clear8. Tag#
tag 标记一个固定提交,分支继续开发时它不会随之移动。轻量 tag 只是引用;附注 tag 还保存说明、创建者等信息。下面未指定提交的创建命令都标记当前 HEAD;各个 v1.2.0 创建示例是替代写法,不应依次执行。
创建 tag 只影响本地;普通分支 push 通常不会推送轻量 tag,需显式推送。删除本地 tag 与远端 tag 也是两个独立操作。正式发布后避免随意移动同名 tag。
list tags#
git tagcreate lightweight tag#
git tag v1.2.0create annotated tag#
git tag -a v1.2.0 -m "release v1.2.0"create tag on specific commit#
git tag -a v1.2.0 a1b2c3d -m "release v1.2.0"push one tag#
git push origin v1.2.0push all tags#
推送所有本地 tag,可能包含尚未准备发布的标记;只发布一个版本时使用上一条指定 tag 的命令。
git push origin --tagsdelete local tag#
git tag -d v1.2.0delete remote tag#
git push origin --delete v1.2.09. GitHub PR#
git 管理提交和分支,gh 通过 GitHub API 管理 PR、Release 等平台对象。先确认登录账号和当前仓库;创建 PR 不等于合并代码。
login GitHub CLI#
gh auth logincreate PR#
创建从 --head feature/order-api 合入 --base main 的 PR;方向是 feature → main。先提交并 push 要审核的分支;创建 PR 不会将未提交文件包含进去,也不会把 main 拉进本地 feature。
gh pr create --base main --head feature/order-api --title "Add order API" --body "Implement order create and query endpoints."create draft PR#
方向与普通 PR 相同,--draft 将它标记为草稿;准备好后运行 gh pr ready 转为可审核状态。
gh pr create --base main --head feature/order-api --draft --title "WIP order API" --body "Draft for early review."list PRs#
gh pr listview PR#
gh pr view 123 --webcheckout PR locally#
抓取并切换到 PR 对应的本地分支,方便测试;会改变当前检出状态,先处理未提交编辑。
gh pr checkout 123merge PR#
在 GitHub 上把 PR 的改动压成一个提交合入该 PR 的 base 分支;目标不是本地当前分支。--delete-branch 请求合并后删除本地与远端来源分支。操作受仓库检查、保护规则或合并队列约束;合并后本地 main 仍需 fetch/pull 才能同步。gh pr merge 说明
gh pr merge 123 --squash --delete-branch10. GitHub Release#
Release 是关联 tag 的 GitHub 发布对象,可附说明和产物;创建它不等于把本地未推送提交上传。以下示例先在第 8 节创建并 push 目标 tag,再用 --verify-tag 确认远端已有它。否则默认命令可能在 tag 不存在时,从远端默认分支自动创建 tag,发布到意外的提交。gh release create 说明
create release from tag#
gh release create v1.2.0 --verify-tag --title "v1.2.0" --notes "Release v1.2.0"create release and generate notes#
与上一条二选一,自动生成发布说明;若需要先审核,将 --draft 加入创建命令以保存草稿。
gh release create v1.2.0 --verify-tag --generate-notesupload release asset#
将本地文件上传为已有 Release 的附件,不会把它加入 Git 提交;先确认产物版本与该 tag 一致。仓库启用不可变 Release 时,发布后不能再修改附件,应在发布前准备好。
gh release upload v1.2.0 dist/order-api.tar.gzlist releases#
gh release listdelete release#
删除 GitHub 上的发布对象及附件,默认保留关联 tag;不会回退代码或撤销已部署应用。不可变 Release 等仓库规则可能限制删除。
gh release delete v1.2.011. Conflict#
先用 git status 确认当前进行的是 merge、rebase、cherry-pick 还是 revert,再采用对应的继续或中止命令。不要只删除冲突标记就认为内容正确,应核对合并结果并运行相关检查。
show conflicted files#
git diff --name-only --diff-filter=Umark resolved file#
手工编辑得到预期结果后,用 add 将结果写入暂存区并标记已解决;add 本身不会自动解决冲突。若解决方式是删除文件,用 git rm 标记。
git add src/orders/service.tsfinish merge after conflict#
git commitfinish rebase after conflict#
git rebase --continue12. Worktree#
worktree 让同一仓库在多个目录同时检出不同分支,各自拥有工作目录和暂存区,但共享提交对象及分支引用;同一个分支默认不能同时在多个 worktree 检出。
create worktree for another branch#
把已有 hotfix/payment-timeout 分支检出到新目录,当前目录仍停留在原分支。若要新建分支,改用 git worktree add -b hotfix/payment-timeout ../order-api-hotfix main,其中 main 是起点。
git worktree add ../order-api-hotfix hotfix/payment-timeoutlist worktrees#
git worktree listremove worktree#
移除额外工作目录及其 worktree 登记,不删除对应分支。默认要求目录干净,先保存其中的改动;不要把它误当成只移除 Git 跟踪。
git worktree remove ../order-api-hotfix13. Ignore / Cached Files#
staged / index meaning#
staged / index:
files already added to the next commit
git commit reads from the index, not directly from the working tree
.gitignore:
prevents new untracked files from being added accidentally
does not automatically remove files already tracked or stagedremove ignored build output from index#
Use this when files are already staged/tracked, but should stay on disk and be ignored by Git.
git rm --cached -r envs/uat/ap-northeast-1/sns/buildsThen verify:
git statusresult:
files are removed from Git index
local files remain on disk
future git add . will not add them if .gitignore matches
common case:
.gitignore contains **/builds/**
zip/json build artifacts were staged before the ignore rule
git rm --cached removes them from the next commitDo not use plain rm unless you also want to delete local files.
已 commit、尚未 push:改为忽略文件#
场景:执行 git add . 并提交后,才发现某个文件或目录不应该进入版本控制。仅把路径加入 .gitignore 不会取消已有跟踪,需要先从暂存区移除,再修改最新提交。
以下操作适用于要修改的就是最新一次提交,且该提交尚未 push 或分享给他人。在仓库根目录执行;先确认暂存区没有其他不想合入最新提交的修改,因为 amend 会一并提交当前暂存内容。
git status
git diff --cached先在根目录的 .gitignore 中添加要忽略的路径,例如:
/path/to/file然后执行(将 path/to/file 替换为实际文件路径):
git rm --cached -- path/to/file
git add .gitignore
git diff --cached
git commit --amend --no-editgit rm --cached:从暂存区移除文件,保留磁盘上的文件。git add .gitignore:把忽略规则一起加入提交,避免以后git add .再次添加该文件。git commit --amend --no-edit:用暂存区内容重新生成最新提交,保留原提交说明;commit ID 会改变。
如果误提交的是整个 Terraform .terraform 目录,根目录的 .gitignore 可添加 **/.terraform/,忽略各级目录中的本地初始化产物,然后执行:
git rm -r --cached -- terraform/env/dev/ap-southeast-1/vpc/.terraform
git add .gitignore
git diff --cached
git commit --amend --no-edit-r 用于递归移除目录。此规则不包含 .terraform.lock.hcl,锁文件通常仍应提交。
最后检查提交、跟踪状态和忽略规则;下面以单文件为例,检查目录时替换成目录路径:
git show --stat HEAD
git ls-files -- path/to/file
git check-ignore -v -- path/to/file
git status预期:git ls-files 不再列出目标路径,git check-ignore -v 显示命中的忽略规则,本地文件仍在。如果文件是最新提交才首次加入的,amend 后它不会出现在该提交的变更中;如果更早的提交已经跟踪它,最新提交会记录删除,旧历史仍保留文件内容。
如果提交已经 push 或分享,通常以新提交记录停止跟踪,不直接 amend 后强推;如果误提交发生在更早的本地提交,amend 最新提交也不会清除更早历史。涉及密钥泄露时还需撤销或轮换凭据,见 Gitleaks。
14. Clean#
clean 处理未跟踪文件,已跟踪文件的编辑由 restore/reset 等命令处理。以下示例默认排除 .gitignore 匹配项;-x 才会连忽略文件一起处理,-X 则只处理忽略文件。
preview untracked files to delete#
git clean -nddelete untracked files#
-f 确认删除,-d 包含目录;只会按当前路径范围清理,不能依靠 Git 恢复从未提交过的文件。先用相同范围的 git clean -nd 预览,必要时指定路径缩小范围。
git clean -fddanger:
git clean -fd deletes untracked files
run git clean -nd first15. Production Checklist#
before merge:
git status clean
tests passed
branch is up to date
PR reviewed
before rollback:
prefer git revert for pushed commits
record commit hash
know whether commit is merge commit
before release:
tag name checked
release notes generated/reviewed
artifacts uploaded if needed
rollback plan exists