Git 实战指南:从安装配置到团队协作的完整命令速查
2026/9/19 4:15:09 网站建设 项目流程

只要写代码,就绕不开 Git。这句话我说了无数回,每次带新人的第一件事,就是先发一份 Git 命令速查表过去,省得在 add、commit、push 这三个词之间来回折腾。其实 Git 本身的概念并不复杂,它就是一个文件版本管理工具,但命令体系确实比较庞大,新手经常被各种参数吓退,老手偶尔也会被一些偏门报错卡住。

这篇文章我打算把自己这几年用 Git 的常用命令、踩过的坑、以及文档里不会明说的经验一次性整理出来。内容从 Windows、macOS、Linux 的安装配置,到本地仓库的日常操作、分支管理、远程协作、撤销与误删恢复,最后还会送上一张常见报错速查表。不管你是刚入行的学生,还是从 SVN 转过来的老开发,甚至只是需要管理自己脚本文件的人,只要电脑里装过 Git,这套内容你都能直接对照着用。我们不聊那些一年用不到一次的花活,就讲工作日里每天都在用的东西。

1. 环境准备与安装配置

1.1 各平台安装教程:Windows、macOS、Linux 全覆盖

先说 Windows。绝大多数人用的是 Git for Windows,它自带了 Git Bash、Git CMD 和 Git GUI 三个工具。安装时去官网下载 exe,一路 Next 能装完,但有几个选项需要手动确认。第一是默认编辑器,新版安装器会让你选,如果选 Vim,后面每次写提交信息都会卡在 Vim 奇奇怪怪的交互里,建议直接选 Visual Studio Code 或者 Notepad++。第二是 PATH 环境变量那一步,务必选择“Git from the command line and also from 3rd-party software”,这样 VS Code、IDEA、小乌龟这些第三方软件才能直接在终端里找到 git 命令。如果官网下载速度太慢,可以找国内开源镜像站下载 Git for Windows,文件名一般是类似Git-x.x.x-64-bit.exe这种格式,认准官方版本号即可。

macOS 上最简单的办法是先尝试直接执行git --version,系统会弹出安装命令行开发者工具的提示,装完自带 Git。想自己控制版本的话,用 Homebrew 安装更干净,命令就一条:

brew install git

Linux 上根据发行版选包管理器,Debian/Ubuntu 用apt install git,CentOS/RHEL 用yum install gitdnf install git。我是建议 Linux 用户优先走系统包管理,除非你要用的是最新特性,否则系统源里的 Git 版本完全够用。装完之后统一执行git --version,能看到版本号就说明安装成功。

1.2 环境变量与基础配置:解决“git 无法识别”和首次设置

Windows 上经常遇到一种情况:安装完成后打开 cmd 或 PowerShell,输入git --version却提示“无法将‘git’项识别为 cmdlet、函数、脚本文件或可运行程序的名称”。这基本就是环境变量没配置好。打开系统环境变量设置,找到 Path,把 Git 的Gits\cmd目录加进去,默认路径通常是C:\Program Files\Git\cmd,手动添加后重新打开终端即可生效。这里提醒一句,改完环境变量一定要记得关闭并重开终端,不然系统读不到新的变量值。

Git 装好后第一件事是配置用户名和邮箱,这一步不能偷懒,因为每次提交都会把这两条信息记录到历史里。配置文件分三级:--system针对整台机器,--global针对当前用户,--local只对当前仓库生效,优先级从高到低是 local > global > system。日常开发配置全局用户名和邮箱就够了:

git config --global user.name "你的名字" git config --global user.email "you@example.com"

配置完可以用git config --list检查所有生效的配置项。还有两个配置我强烈建议顺手加上。一个是 Windows 下处理换行符,Git 默认会有 CRLF 的警告,执行git config --global core.autocrlf true可以让 Git 帮你把换行符自动转换,团队协作时能少一票莫名其妙的 diff 问题。另一个是git config --global core.quotepath false,这个配置解决的是中文文件名和中文路径在git status里显示成八进制乱码的问题,很多人在国内仓库里被这玩意儿坑过,配置之后显示就正常了。

2. 本地仓库的日常操作命令

2.1 仓库初始化与文件生命周期:理解 Git 的三层状态

Git 的文件管理核心是三棵树的概念:工作区、暂存区(Index)、本地仓库。打个比方,工作区是你的工位,暂存区是打包台,本地仓库是货架。文件先放在工位上,你挑出要发走的放在打包台,最后统一搬到货架上存放。日常命令基本就在这三个区域之间搬运文件。

初始化仓库有两种方式。第一种是从零开始,在项目根目录执行git init,这条命令会在目录下生成一个.git文件夹,里面装着所有版本历史。第二种是从远程拿项目,git clone会把远端仓库完整复制到本地。很多新手容易把git init当成所有项目的起点,但如果你的项目已经在一个远程仓库里了,直接用git clone就行,不要先 clone 再git init,那会破坏仓库结构。

日常编辑文件的流程是这样的:先用git status查看当前状态,它会明确告诉你哪些文件被修改了、哪些还没被跟踪。文件从工作区挪到暂存区用git add <file>,全部添加用git add .,只添加部分文件或部分改动可以用git add -p,这个交互式命令会询问每一块改动是否暂存,适合代码里混了调试日志时精准提交。暂存完毕用git commit -m "提交信息"生成一次提交。这里想多说一句提交信息,理想情况下应该写清楚“为什么改”,而不是“改了啥”,比如“修复登录接口空指针异常”就比“update”好得多。

2.2 查看历史与差异对比:git log 和 git diff 的常用组合

提交记录查少了会找不到代码是谁改的,查多了又费眼,关键是掌握几个常用参数。git log --oneline把每次提交压成一行显示,干净利落;git log --graph --all会把分支的合并图谱画出来,适合观察整体结构;git log -5只看最近 5 条。我个人每天最常用的命令是git log --oneline --graph --all,在公司代码里查分支合并情况,一眼就能看出哪个功能是从哪个分支合进来的。

差异对比要看场景分四种命令:git diff比较工作区与暂存区的差异,也就是你改了但还没 add 的内容;git diff --cached比较暂存区与本地仓库的差异,也就是已经 add 但还没 commit 的内容;git diff <commit1> <commit2>比较两个提交之间的差异;git show <commit_id>查看某一次提交具体改了什么文件、哪些行增删。带着这个对照关系去用,基本不用再背参数了。

2.3 .gitignore 配置:让 Git 自动忽略不该提交的文件

几乎每个仓库里都有一些不该进版本库的文件,比如 IDE 配置、依赖目录、日志文件、本地环境变量。这些文件的共同点是:每个人都不同、自动生成、更新频繁。Git 提供了.gitignore文件来过滤它们。

.gitignore的规则不复杂,但有几个关键点容易踩坑。以/开头的路径表示仓库根目录,/结尾的表示目录,*匹配零个或多个字符,!表示取反。给一个实际例子:

node_modules/ target/ dist/ *.log .env .idea/ .vscode/

每一行表示一种忽略规则。如果某些文件已经被跟踪了,再往.gitignore里加规则是无效的,需要先git rm -r --cached <file>把它们从暂存区移除,再提交一次,之后的改动才会被忽略。新手最容易在这里困惑:明明加了规则,git status 还在显示某个文件,八成就是因为它之前已经被 add 过了。

3. 分支管理:并行开发的“平行宇宙”

3.1 分支的创建、切换与合并

分支是 Git 最让人上头的设计,本质上就是一个指向某次提交的可移动指针。创建分支用git branch <branch_name>,创建并切换用git checkout -b <branch_name>。从 Git 2.23 开始官方更推荐用git switch系列命令:git switch -c创建并切换,git switch切换已有分支,语义更清晰,不容易和还原文件的checkout混淆。

合并分支用git merge <branch_name>。合并有两种常见形态:fast-forward 和 三方合并。如果当前分支的 HEAD 是被合并分支的上游,Git 会直接把指针往前移,不产生新的合并提交;如果两个分支在分开后都有新提交,Git 就必须做三方合并,生成一个 merge commit。很多团队为保证主干历史清晰,会习惯用git merge --no-ff <branch_name>强制保留一个合并节点,这样主分支上的功能合并点一目了然,回滚时也方便定位。

3.2 解决冲突的正确姿势

冲突是合并时最让人头疼的事,但处理过一次之后就会发现其实逻辑很机械。Git 在冲突发生时会在冲突文件里插入<<<<<<<=======>>>>>>>三行标记,分别表示当前分支的内容、分隔线、被合并分支的内容。你需要做的就是打开文件,手动逐段选择保留哪边,删掉这三行标记,然后git add <file>git commit

我的经验是:遇到冲突千万别在终端里硬凭记忆改,尤其当冲突文件比较大时,用 VS Code 或者 IDEA 自带的冲突解决工具效率会高很多,这两个工具都提供“接受当前/接受传入/同时保留”的按钮,可视化操作不容易出错。改完之后重点检查一遍整个文件,确认没有遗留的<<<<<<<标记,再提交。提交信息里不用专门写“解决冲突”,默认的 merge 提交信息就行。

3.3 stash 临时保存工作现场

开发中最尴尬的事情莫过于:当前分支代码改了一半,还没写完不能提交,这时候突然要切到另一个分支改个紧急 bug。硬切分支会被 Git 拦住,因为工作区和目标分支有冲突。这时候git stash就派上用场了,它能把当前工作区的修改保存到一个临时堆栈里,让工作区恢复干净。

git stash git stash list git stash pop

git stash默认只保存已跟踪文件的修改,如果你在工作区新建了一个还没git add的文件,需要加-u参数(即git stash -u)才会一并保存。恢复时用git stash pop会把最新一次 stash 弹出并应用;如果你同时在多个 stash 之间切换,用git stash apply stash@{1}可以恢复指定的那次。如果 stash 了多个任务,建议压栈时带个备注,git stash push -m "登录模块的临时修改",不然时间一长自己都分不清哪条是哪条。

4. 远程仓库协作

4.1 SSH 配置与免密推送:以 Gitee 为例

使用 HTTPS 协议拉代码虽然简单,但每次 push 都要输账号密码,相当影响心情。SSH 协议通过密钥对免密认证,而且更安全,是很多开发者的首选。生成密钥的方法很简单,在 Git Bash 里执行:

ssh-keygen -t rsa -b 4096 -C "you@example.com"

一路回车,生成的密钥默认在~/.ssh/id_rsa(私钥)和~/.ssh/id_rsa.pub(公钥)。然后把公钥内容复制到代码托管平台的设置页面,Gitee 的地址是“设置 -> SSH 公钥”,粘贴保存即可。验证是否配置成功用:

ssh -T git@gitee.com

如果显示成功欢迎信息,说明密钥已经生效。我遇到过不少情况是公钥复制不全,少几个字符导致认证失败;还有人是把私钥内容贴上去了,这里尤其要注意,粘贴的一定是.pub结尾的公钥文件内容。

4.2 clone、pull、fetch、push 的区别与使用场景

很多初学者分不清这四个命令,其实它们承担完全不同的职责。git clone是把远程仓库整个拿下来,只在新机器或新目录上执行一次。git fetch会把远程的新提交拉取到本地,但不会自动合并到你的工作分支,适合想先看看别人改了什么再决定怎么处理的情况。git pull等于fetch + merge,一条命令直接把远程更新合并进当前分支。git push则是把本地提交推送到远程。

这里有一个值得反复强调的习惯:在多人协作的分支上做git push之前,先执行一次git pullgit pull --rebase,把远程的更新合并到本地再推上去。直接推很容易被服务器拒绝,报出failed to push some refs,那基本就是远程有本地还没有的提交。如果本地和远程各自都有新提交,用git pull --rebase可以把本地的提交“垫”到远程提交之后,提交历史是一条直线,比自动产生的 merge 提交更清爽。

4.3 团队协作的完整工作流与 PR/MR 流程

现在稍微正规一点的团队都会走 pull request 或 merge request 流程。完整的命令序列大概是这样:先 clone 主仓库,为每个功能创建独立分支,在分支上完成开发并推送到远程,然后在代码托管平台上发起合并请求,等有权限的人评审通过后合并。本地操作对应的完整命令链是:

git clone git@gitee.com:yourname/project.git git checkout -b feature-login # 写代码... git add . git commit -m "feat: 完成登录功能" git push -u origin feature-login

-u参数(全称--set-upstream)的作用是让本地分支和远程分支建立跟踪关系,第一次推送之后就可以直接git push而不用再写远程分支名。整个工作流的精髓在于:主干分支保持稳定,所有变更都经过分支和评审,这样即使某个功能写崩了也不会直接影响线上代码。

5. 撤销操作与历史改写

5.1 撤回暂存区和工作区的修改:git restore 的正确用法

撤销操作是最容易把仓库搞乱的地方,因为 Git 有好几种“反悔”方式,应用场景完全不同。最简单的情况是你改了一个文件但还没git add,想恢复到上次提交的状态,用git restore <file>或旧式写法git checkout -- <file>。如果已经git add进入了暂存区,想撤销暂存,用git restore --staged <file>,这样文件会变回“已修改未暂存”的状态,但工作区内容不会变。再多走一步,想把文件彻底恢复到上次提交的样子,先git restore --staged <file>git restore <file>,两步组合拳打好就行。

这里需要特别提醒:git checkout -- <file>git restore <file>都是不可恢复的操作,如果这个文件有重要的、未提交的修改,一旦覆盖就找不回来了。执行前最好先git diff看一眼确实要丢弃这些改动。

5.2 commit --amend 修改最后一次提交

提交完之后发现漏了个文件、提交信息写错、或者想多带一个文件进去,这种情况几乎每周都会遇到。git commit --amend就是为它准备的。最简单用法:

git add . git commit --amend -m "新的提交信息"

如果只是想把新文件并进上一次提交,不改提交信息,用git commit --amend --no-edit。这个命令的本质是生成一个新的提交对象来替换原来的提交。用起来很顺手,但有一个大忌:只能 amend 还没推送到远程的提交。如果已经git push了,再 amend 会导致本地与远程历史不一致,之后推送会被强制要求git push --force,而 force push 会把远程历史覆盖掉,团队里其他人可能因此直接乱套。所以我的习惯是:没推送随便改,推送了宁可新开一条提交也不要 amend。

5.3 用 revert 安全回滚已推送的提交

已推送提交出了问题,想要安全回滚,首选不是reset,而是git revertgit revert <commit_id>会生成一个新的提交,内容正好是那次提交的逆操作,相当于“用新提交撤销旧提交”。这种方式的好处是不改变已有历史,远程仓库的历史一直是线性增长的,团队其他成员 pull 下来不会有任何冲突。

对比一下git resetgit revert的区别:reset是把 HEAD 指针回退到过去的某个提交,适用于本地还没有推送、想彻底抹掉一段历史的情况;revert是在当前历史后面追加一个反向提交,适用于线上分支、公共分支,因为历史不会被改写。说人话就是:自己家里怎么折腾都行,公共区域还是用revert这种留痕的方式更稳妥。

5.4 reflog 找回误删的分支和提交

git reflog是真正意义上的后悔药。它记录了 HEAD 指针每一次移动的历史,包括 reset、revert、merge、branch 删除等操作。就算你误删了一个分支,只要你知道分支最后指向的提交 ID,就能用git checkout -b <branch_name> <commit_id>把它原封不动找回来。操作步骤是:

git reflog

输出里能看到一串操作记录,比如HEAD@{2}: checkout: moving from feature to main,找到对应的提交 ID,然后基于它重建分支即可。我自己的经历是曾在一个项目里误删了特性分支,以为一个星期的代码全没了,后来在 reflog 里翻出来恢复,那一刻真的长出一口气。这个命令在日常工作中用得不多,但一旦用上就是救命级的操作。

6. 进阶技巧与工作流

6.1 worktree 一库多目录并行开发

git worktree是我近两年用得越来越顺手的功能。它允许一个仓库同时 checkout 出多个工作目录,每个目录可以停留在不同的分支上。最典型的场景:你正在 feature 分支改一个比较复杂的功能,代码一半没写完,线上突然出现一个紧急 bug 必须马上修复。常规做法是 stash 或 commit,切回主干分支修复,修完再切回来,来回折腾很痛苦。用 worktree 就不一样:

git worktree add ../hotfix -b hotfix-urgent

这个命令会在上层目录新建一个hotfix目录,并自动 checkout 一个新的hotfix-urgent分支。你可以在这个新目录里修复 bug、提交、推送,完全不影响原目录里没写完的功能。等 bug 修好,回到原目录继续干活就行。常用操作还包括git worktree list查看所有关联目录,git worktree remove <path>删除某个工作目录。我用完的感受就是:早该装了,省掉大量 stash 和切换分支的中断成本。

6.2 cherry-pick 摘取指定提交

有些时候你并不想把一个分支整个合并过来,只需要其中某一个提交。比如某个修复 bug 的提交在 develop 分支上,但线上用的还是 release 分支,只想把那个 fix 拿过来。git cherry-pick <commit_id>就能精准摘取:

git switch release git cherry-pick a1b2c3d

a1b2c3d是 fix 提交的 ID。cherry-pick 会把这次提交的改动应用到你当前的分支上,并生成一个新的提交。它和 merge 的关系可以这么理解:merge 是整条分支嫁接到一起,cherry-pick 是单独取一两个提交搬到别的分支。如果过程中有冲突,解决方式跟 merge 冲突一样,处理完记得git cherry-pick --continue收尾,不要中途退出,否则 Git 会一直处于一个未完成的 cherry-pick 状态。

6.3 submodule 子模块:把仓库嵌进仓库

当一个项目需要引用另一个独立开发的仓库时,可以用git submodule。典型场景:你的主系统依赖一个内部公共库,这个公共库有自己的仓库和版本演进,你不想把它的代码直接复制进来,而是希望锁定到某个版本,主仓库和子模块各自独立演进。

git submodule add https://github.com/example/common-lib.git libs/common git clone <主仓库地址> git submodule update --init --recursive

第一行命令添加子模块,之后别人 clone 主仓库时,子模块目录是空的,需要执行第三条命令初始化并拉取。这里最常见的坑是:子模块的 URL 写在.gitmodules文件里,如果这个文件用了团队的内部地址,外面的人 clone 时会因为没权限而失败。在实际工作中,除非确有强依赖,否则我不太建议轻易使用 submodule,因为它让仓库复杂度上了一整个台阶。简单的依赖关系用包管理器它不香吗?只有当共享代码必须跟随独立版本发布时,submodule 才是正确答案。

7. 常见问题与排查技巧实录

7.1 常见报错速查表

把工作里高频出现的 Git 报错整理成一张表,每个错误都是我或者同事实际踩过的,照着排查能省下大量搜索时间。

报错信息出现原因解决办法
fatal: not a git repository (or any of the parent directories): .git当前目录不是 Git 仓库,或者命令执行错了位置cd进仓库目录,必要时用git init初始化仓库
无法将“git”项识别为 cmdlet、函数、脚本文件或可运行程序的名称Windows 环境下 Git 没有安装或没配环境变量检查是否安装了 Git,然后在系统环境变量 Path 中加入Git\cmd
fatal: refusing to merge unrelated histories两个分支(仓库)没有共同的历史基点确认没问题后,用git merge --allow-unrelated-histories <branch>
failed to push some refs to ...远程分支有本地没有的提交git pull --rebase再重新推送
Permission denied (publickey)SSH 公钥未配置、配置错误或密钥不匹配检查ssh -T git@gitee.com,确认公钥已完整添加
warning: LF will be replaced by CRLF换行符不统一引起的警告执行git config --global core.autocrlf true
Login failed. Check API token or GitLab versionIDE 插件连不上 GitLab,通常是 token 过期或版本兼容问题在 IDE 设置里重新生成并填入 Access Token,并确认 GitLab 版本
中文文件名显示成\346\265\213...core.quotepath 默认转义了非 ASCII 字符执行git config --global core.quotepath false
error: Your local changes would be overwritten by checkout当前工作区有未提交修改,且目标分支也改过这些文件git stash保存现场,切换分支后再git stash pop

7.2 一个完整的日常协作战术演练

把上面的命令串起来,模拟一个典型的开发日。早上到公司,先拉取最新代码:git pull。然后基于最新主干创建功能分支:git switch -c feature-optimize-query。开始写代码,写了一部分后想看一眼自己改了什么:git diff。确认无误后提交:git add . && git commit -m "perf: 优化查询接口的数据库访问"

下午发现功能分支写了一半,线上出了紧急 bug。用git stash -u保存当前未完成改动,切回主干创建修复分支,修完提交推送到远程,发起合并请求。线上问题处理完后,切回功能分支,git stash pop恢复现场。下班前把功能分支推送到远程:git push -u origin feature-optimize-query,然后在代码托管平台发起 MR 让同事评审。整套流程熟练之后,其实每天会输入的命令就那十几条,不用背,敲多了自然就形成了肌肉记忆。

7.3 少有人提的实用经验:别名、quotepath 和 optional locks

有一部分高频命令我建议配置别名,能显著提升日常操作效率。Git 的别名配置非常简单:

git config --global alias.st status git config --global alias.co checkout git config --global alias.lg "log --oneline --graph --all"

配完之后git st等于git statusgit lg可以快速浏览分支图谱。这些别名存的位置在用户目录下的~/.gitconfig文件里,想删想改直接编辑文本文件也行。

关于 IDE 集成的-c core.quotepath=false参数,它的作用和前面配置 core.quotepath 一样,只是 IDE 启动 Git 命令时通过-c key=value的方式临时注入配置,不需要全局修改。--no-optional-locks则是告诉 Git 在运行只读命令(比如 status、diff)时不要创建可选锁文件,避免多个 Git 进程并发时互相影响。这个参数在脚本里调用 Git 命令时尤其有用,很多 CI 工具调用 Git 都会默认加上它。

另外提醒一句,如果你负责部署项目,务必确认 Web 服务器不会把仓库的.git目录暴露到可访问路径下。这虽然不是 Git 命令本身的问题,但一旦.git目录能被外部下载,整个代码历史和配置信息就全部泄露了,这是部署配置层面的硬性教训。

7.4 安全操作提醒

git push --force是能直接改写远程历史的命令,风险极高,不建议在公共分支上使用。如果确实需要 force push,先确认没有其他同事正在使用该分支,操作后主动通知团队。更温和的做法是用git push --force-with-lease,它会在推送前检查远程分支是否与你上次拉取时一致,如果期间有别人推了新提交,就会自动拒绝,安全性高得多。

Git 命令的报错信息有时候看起来吓人,但绝大多数都只是语法或状态问题。我的经验是:先看第一行提示,它通常直接告诉你错误类型;再确认当前在哪个分支、工作区状态如何,用git status打开局面;最后才搜索或者查文档。盲目执行网络上搜来的命令,尤其是和管理历史相关的命令,往往是搞坏仓库的根源。

最后分享一个我自己的习惯吧。每天收工前,我都会执行一遍git status确认工作区是干净的,再看一眼git log --oneline -5确认今天的提交都在。这个习惯坚持了很多年,让我几乎很少遇到“代码到底提交到哪去了”的情况。Git 这门工具,命令确实多,但每天真正高频用到的就那一小部分,把这部分练扎实,再配合 reflog 和 worktree 这类进阶兜底能力,日常开发已经绰绰有余。希望这篇整理能替你省下一点摸石头过河的时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询