ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Git常用命令场景化详解:从配置提交到分支与安全

Git常用命令场景化详解:从配置提交到分支与安全 这两年不管是在大厂带新人还是自己写开源项目我发现一个扎心的规律很多人不是不会用 Git而是只会那几个固定命令一遇到分支合错了提交信息写错远程连不上不小心把密钥提交上去了就彻底卡住然后开始在各种群里发求助截图。其实 Git 的核心命令就那么几十个真正决定你效率的不是背多少命令而是搞清楚每个命令在什么场景下用、背后改了什么、风险在哪。这篇就结合我实际踩过的坑把 git 常用命令按场景重新捋一遍希望能帮你从会用升级到用得明白。1. 装好 Git 后先做这几件事全局配置与默认行为先聊安装。不同平台的安装方式不太一样Windows 建议直接去官网下载 Git for Windows装完自带 Git BashmacOS 可以brew install gitLinux 按发行版走 apt/yum/dnf 就行。装完第一件事不是急着 clone而是把全局配置写好否则每次提交都会报Please tell me who you are。1.1 全局身份配置与默认分支名身份配置是提交的署名它会写进每一次 commit 记录里后期做代码追溯、版本审计全靠它。命令很简单git config --global user.name 你的名字 git config --global user.email 你的邮箱这里有个细节很多人不知道邮箱不一定非要和 GitHub/Gitee 注册邮箱一致但建议保持一致。因为平台上绿色方块贡献图的统计逻辑就是按邮箱匹配提交者的邮箱不一致你会发现自己在 GitHub 上的贡献图一片空白哪怕你天天提交。默认分支名也是一个容易忽略的点。老版本 Git 默认分支是master新版本已经改成main。如果你不想每次都碰到不同的默认分支名可以统一一下git config --global init.defaultBranch main这个设置影响的是git init时创建的默认分支对 clone 下来的仓库没有影响所以改不改看你个人习惯。我个人建议统一成main少一点历史包袱。1.2 换行符问题CRLF 与 LF 的坑Windows 和 Linux/macOS 对换行符的处理不一样Windows 用CRLF回车换行Linux/macOS 用LF换行。如果你在 Windows 上写代码又和 Mac 同事协作最典型的现象是你只是改了一行提交时却显示整个文件都变了因为每一行的行尾符都被自动替换了。解决办法在安装 Git for Windows 时就有一个选项Checkout Windows-style, commit Unix-style line endings对应配置就是git config --global core.autocrlf true这个配置的意思是从仓库检出代码到工作区时把 LF 自动转成 CRLF提交时把 CRLF 转回 LF 存进仓库。这样仓库里永远存的是 LFWindows 本地用 CRLF跨平台不会互相污染。如果你已经在协作过程中了更稳妥的做法是直接在仓库里放一个.gitattributes文件强制指定文件类型和换行符规则比如* textauto *.sh text eollf *.bat text eolcrlf.gitattributes的优先级高于个人全局配置团队所有人进来就会被统一约束。这个文件值得每个仓库都放一份能省掉大量无意义的 diff 噪音。1.3 编辑器与 Git Bash 的操作习惯默认编辑器建议改成你自己顺手的不然git commit没带-m参数时会直接把你丢进 Vim。我见过太多新人在 Vim 里不知道怎么保存退出直接关终端导致提交失败。改用 VS Code 的话git config --global core.editor code --wait还有个实用配置是免交互提交时直接跳过编辑器git config --global core.editor true这个技巧让git commit在没有-m时不打开编辑器适合写临时提交的场景。还有人在 Git Bash 里不习惯复制粘贴Windows 的 Git Bash 默认选中即复制、按鼠标中键或 ShiftInsert 粘贴刚开始会有点别扭用习惯了效率反而高。安装配置阶段最容易出问题的还有一个git open /dev/null or dup failed: no such file or directory这个报错一般出现在 Git Bash 的环境异常上常见原因是系统临时目录被清理工具误删或者环境变量缺失重启 Git Bash 或重新安装 Git 基本能解决。2. 日常提交链路add、commit、.gitignore 与误提大文件日常开发最核心的三个命令就是git add、git commit、git push。看似简单实际用的时候到处是细节。2.1 git add 的三种范围与真实用法基础用法不用多说关键是别只会git add .。全量添加虽然省事但会把临时文件、编译产物全部拉进来也导致提交粒度不清晰后面想回滚某个功能时非常痛苦。我习惯的做法是:git add src/xxx/yyy.js tests/xxx/yyy.test.js按文件精确添加一个提交只解决一个问题。想偷懒时可以用git add -A把所有改动加入索引或者git add -u只添加已跟踪文件的修改和删除不添加新增未跟踪文件。还有个交互式命令git add -p可以逐个 hunk 确认适合把一个大文件改动拆成几个逻辑提交。这个命令初用会有点繁琐但配合git commit打出来的提交历史会非常干净。2.2 commit 的正确写法与 amend 使用提交信息是写给人看的不是写给机器看的。规范的格式一般是第一行摘要、空一行、正文说明。git commit -m feat: 增加用户注册接口 -m 包含手机号校验和短信验证码发送逻辑多个-m在 Git 里会自动连成多段提交信息比写一个带换行符的长字符串方便很多。提交之后发现漏了一个文件或者提交信息写错了别慌用git commit --amend。它会把暂存区的内容追加到上一次提交里同时允许修改提交信息。这个命令的适用场景是提交还在本地、没有推送或者推送到了自己的临时分支、还没有人基于它开发。我在实际项目里经常用它来补漏比如加一个debug.log忘了传或者在提交信息里把单号写错了。但要注意amend本质是替换上一次提交会生成一个新的 commit 对象。如果上一次提交已经推送到公共分支且被其他人拉走了此时amend再强推会造成别人本地历史错乱这是协作大忌。2.3 .gitignore 看似不起作用的原因git 的过滤文件没有作用是我被问过最多的问题之一。绝大多数情况不是.gitignore写错了而是那个文件已经被 Git 跟踪了。.gitignore只对未跟踪的文件生效。一旦某个文件被git add提交过之后就永远处于已跟踪状态你再怎么往.gitignore里加规则都没用。解决方案是把它从 Git 索引里移除但保留本地文件git rm --cached filename git commit -m 停止跟踪 filename之后再把规则写进.gitignore后续改动就不会被纳入了。我见过不少人因此在生产环境提交了配置文件然后天天手忙脚乱地改回。切记先加.gitignore再git add顺序不能反。2.4 提交大文件被 Git 拒绝怎么办当你执行git push遇到 remote: error: File is larger than 100.00 MB 或 GH001: Large files detected 时说明仓库里混入了不该提交的大文件。GitHub 单文件限制是 100MBGitee 会更高一点但同样有限制。如果大文件还没提交成功直接在本地删除并确保没进索引就行。如果已经提交进了历史记录那只是从工作区删除文件还不够因为历史提交里还留着它。处理方案有两种简单做法用git filter-repo或git filter-branch把大文件从历史中彻底抹掉然后强推所有分支。项目确实需要存放二进制大文件时用 Git LFSGit 只存文件指针真实内容放到 LFS 服务器。git filter-repo 安装和使用示例pip install git-filter-repo git filter-repo --path path/to/large.zip --invert-paths注意改写历史后所有人的本地仓库都需要重新 clone这是个伤筋动骨的操作最好在项目组内确认后再做。日常提交链路里还有个小建议每个 commit 保持单一职责不要攒几十个文件再一股脑提交。这样查 bug 时git log和git blame会给你非常精准的定位而不是在一个庞大提交里大海捞针。3. 分支切换与合并fetch、pull、merge、rebase 别混着用说到 Git 就绕不开分支问题。分支合并pick 和 fetch 有什么区别idea 中 git 如何合并分支这类问题背后其实是很多人没搞懂 Git 的本地仓库、远程跟踪分支和远端分支这三层概念。3.1 fetch 与 pull 的本质区别git fetch是把远程仓库的最新状态下载到本地但它只更新远程跟踪分支比如origin/main不会动你的工作区也不会自动合并。也就是说fetch 之后你会看到远程有更新但本地代码完全没变。git pull等价于git fetch加git merge一步到位把远程改动合到当前分支。听起来 pull 更方便但实际协作中我喜欢先用 fetch 看一眼再决定因为当本地和远程都有各自的新提交时直接 pull 很可能触发一次你毫无心理准备的合并或冲突。如果你就想让本地分支严格对齐远程用git fetch origin git reset --hard origin/main注意这个操作会丢弃本地所有未提交的改动和新增提交务必谨慎。适合的场景是本地已经改乱了我不想留了以远程为准。3.2 merge 的三种主要场景git merge最常规的用法是把一个分支合并到当前分支。比如当前在main要把feature/login合进来git checkout main git pull git merge feature/login当两个分支从同一个提交点分叉后再各自产生了新提交merge 会自动创建一个新的合并提交。如果只有一条分支领先、另一条没动Git 会走 fast-forward 模式直接把当前分支指针快速移动到目标分支不生成合并提交。不想让 Git 走 fast-forward可以加--no-ff强制创建合并提交git merge --no-ff feature/login这个习惯在团队项目里很流行因为合并提交能非常清晰地记录某个功能是在哪个节点合进来的回购历史一拉就懂。反过来说如果分支很多、合并频繁--no-ff也会让历史显得冗长单人或小项目按需选择即可。3.3 master 上的代码怎么移到 dev 的三种操作法这个问题其实是问如何把当前分支的改动/提交复制到另一个分支要分情况回答。情况一改动还没提交。用git stash暂存当前工作区切到目标分支后git stash popgit stash git checkout dev git stash pop情况二已经提交了想把某个特定提交复制过去。用git cherry-pickgit cherry-pick commit_hash这个命令会把指定提交的改动应用到当前分支上相当于移植。我用它最多的地方是把热修复从临时分支搬到主分支而不需要把整个分支合并过去。情况三想把整个master分支上的所有新提交合并到dev。那直接切换后 merge 就行git checkout dev git merge master看清楚需求属于哪种别动不动就重建分支拷贝文件既低效又容易漏东西。3.4 worktree 与 branch 的分工差异许多人把git worktree单纯理解成另一个文件夹里的项目副本其实它是设计用来解决一个痛点的同一个仓库在多个分支上并行开发时不停地 stash 和 checkout 切换很痛苦尤其是任务来回穿插的时候。git branch只是逻辑指针一个工作目录同一时刻只能检出一个分支。git worktree则允许同一个仓库同时有多个工作目录每个目录对应不同分支。用法git worktree add ../proj-dev dev git worktree add ../proj-hotfix hotfix上面的命令会在proj-dev目录里单独检出一个dev分支的工作区两个目录互不干扰但共享同一个 Git 仓库对象库。适合的场景是线上 bug 要立刻修手上的新功能又不想动。日常不需要多个工作区就老老实实用一个 checkout 加 stash需要频繁切换上下文或者想隔离构建目录worktree 是比再 clone 一份仓库更省磁盘、更方便的选择。3.5 合并冲突怎么看、怎么解决真的遇到冲突时千万别慌。Git 指的是把冲突标记清晰地放在文件里标记和之间的是两个人各自的代码。我用 vscode 对比冲突有点繁琐实际操作反而依赖下面的命令git status它会明确告诉你哪个文件 both modified。打开文件后搜索一个个处理。解决完删掉冲突标记然后git add filename git commit至于 merge 和 rebase 的选择如果你开发的功能分支已经合到主分支了我一般只用 merge简单可回溯。rebase 更适合在个人分支上整理历史让提交看起来变成一条直线但它有个前提不要 rebase 已推送且被其他人拉取的分支否则会重新生成 commit hash别人再 pull 时会出现一堆重复提交。4. 历史改写commit --amend、reset、revert、rebase -i 的安全边界Git 最强大的能力之一就是修改历史但也是最容易出事故的能力。我见过有人因为一次 reset 把一天的代码清空也见过有人 revert 之后一脸懵提交没了但代码却还在。这里把几个高频历史操作讲透。4.1 commit --amend 的边界前面已经说过amend就是把上一次提交连同暂存区的新改动打包成一个新提交。如果你只修改提交信息不改内容也走这个命令git commit --amend -m 新的提交信息记住一条铁律不要对已经推送到远程且被多人拉取过的提交执行amend。如果确实改了远程合并请求里的旧 commit 记录还在推进会导致两边历史不一致协商成本极高。4.2 reset 三种模式到底会丢什么git reset是把当前分支的 HEAD 指针往回移动的神奇命令它有三个模式我每次讲这个都会配一张对比表这里直接用文字描述--soft指针回退但索引和工作区不变。也就是提交记录回退了但所有改动已暂存随时可以重新提交。这是用完amend后发现心里有点虚时的补救方式。--mixed默认指针回退索引也回退但工作区文件保留。改动会回到已修改未暂存状态是回退提交后继续改代码的常用方式。--hard指针、索引、工作区全部回退。工作区里那些在这个提交之后产生的改动也会被丢弃相当于时光倒流。执行前必须确认。git reset --hard HEAD~1 # 回退一次提交并丢弃改动 git reset --soft HEAD~1 # 回退一次提交但保留改动在暂存区危险之处在于reset 之后你原来的提交并没有被立刻清除而是进入 reflog 里30 天内还可以找回来。所以哪天手误执行了--hard先别砸键盘用git reflog找到原来的 commit hash再 reset 回去即可。4.3 revert 与 reset一个建新碑一个毁旧碑git revert和git reset表面看都是撤销机制完全不同。revert会生成一个新的提交这个提交的作用是反着应用目标提交的改动。比如 commit A 加了 10 行代码revert A 就是删掉这 10 行并留一条完整的撤销记录。git revert commit_hash重点来了revert 不会删除历史它只是追加了一段补丁。所以 revert 适用于已经推到远程、或者其他人已经因为你那个提交做了进一步开发的场景。它保留了所有历史轨迹适合正式项目里的回溯操作。reset 则是直接把 HEAD 指回老的提交中间那段历史就消失了实际还在 reflog 里。reset 更适合本地整理比如连续提交了三笔你发现全是一团乱麻想重新来过。什么时候选哪个一句话只要提交已经 push 到共享分支优先 revert只要提交还在本地没上过天随便 reset。4.4 rebase -i整理提交历史的瑞士军刀git rebase -i交互式变基是我个人最推荐的历史整理工具没有之一。它的能力列表很长但日常用到的主要是这几个操作pick保留该提交可以改变顺序。reword修改提交信息。edit停下来允许拆分提交或修改内容。squash把当前提交和上一个提交合并成一个常用于把一堆wip、fix typo之类的琐碎提交压缩成一个完整功能提交。一个典型的操作把最近 3 个提交压缩为一个。git rebase -i HEAD~3编辑器里会列出最近 3 条提交把后面两条的pick改成squash保存退出后按提示写一个新的提交信息。整个过程都在本地对远端没有影响直到你主动 push。rebase 的危险是它会改写提交 hash和 amend 同理已经推到共享分支的绝对不要 rebase。我的经验是rebase 只用于个人分支或合并前清理自己的提交历史合并进公共分支后一个字都不要动。5. 远程协作clone 失败、SSH 认证失败与免密登录远程仓库相关的命令和问题占了日常求助的很大一部分。尤其ssh 认证失败、clone failed to connect to 127.0.0.1 port 7890: connection refused这类报错我帮别人排查的次数多到数不清。5.1 clone 失败最常用的三个排查方向connection refused类型的报错本质是 Git 尝试连接远程服务器时TCP 层就被拒绝了。常见原因有三类本地网络访问不了 GitHub/Gitee 的服务器属于网络层面的问题跟 Git 本身没关系。本地有代理类软件或系统代理设置异常导致 Git 把请求指向了某个本地端口而那个端口的代理服务没有正常启动。这种情况下建议检查系统代理设置环境变量或者执行git config --global --unset http.proxy、git config --global --unset https.proxy把 Git 的代理配置清掉。目标地址主机名解析异常或者公司内网限制直接访问外网。可以先ping github.com看通不通也可以ssh -T gitgithub.com测试 SSH 通道是否正常。排查顺序我一般这样走先看报错带不带port带端口基本就与代理有关再看能不能ping通域名最后curl -I https://github.com看 HTTP 通不通。一步一步缩小范围别一开始就重装 Git。5.2 SSH 认证失败从生成密钥到配置全流程SSH 认证失败最常见的特征是Permission denied (publickey)。很多人以为把本地生成的公钥贴到 GitHub/Gitee 就完事了实际还有几个环节生成密钥ssh-keygen -t ed25519 -C 你的邮箱或备注生成的文件默认在~/.ssh/id_ed25519.pub内容是公钥id_ed25519是私钥私钥绝不能外传。然后把公钥内容复制到代码平台的设置页里。GitHub 是 Settings - SSH and GPG keysGitee 是安全设置 - SSH 公钥。但到这里还没完你还要确保本地 SSH 客户端认这个私钥。测试命令ssh -T gitgithub.com ssh -T gitgitee.com如果提示Hi 用户名! Youve successfully authenticated说明认证已通过。如果显示Permission denied优先检查ls -l ~/.ssh/id_ed25519.pub ~/.ssh/id_ed25519私钥文件权限太宽松ssh 会拒绝使用。Windows 下要注意文件权限Linux/macOS 下改chmod 600 ~/.ssh/id_ed25519另一个坑是用了非默认路径的私钥比如我把密钥放在~/.ssh/company/id_ed25519就得配置~/.ssh/configHost github.com HostName github.com User git IdentityFile ~/.ssh/company/id_ed25519之后ssh -T gitgithub.com就能识别了。5.3 免密与账密缓存问题SSH 方式配置好以后本身就是免密的不需要输密码。如果你还是用 HTTPS 方式 clone那么用户名密码或 token 会被 Git 提示输入可能还会要求反复输入这时可以开启凭证缓存git config --global credential.helper store这样首次输入后凭证会明文保存在~/.git-credentials里之后不再提示。macOS 可以用osxkeychainWindows 上 Git for Windows 自带manager用系统安全地存储凭据比明文要安全些。如果发现缓存里存了错误的账号需要清除git config --global --unset credential.helper或者在 Windows 的凭据管理器里删除对应条目Git 就会重新让你输入账号密码。这个操作在多人共用一台机器、或者切换公司账号和个人账号时特别重要。5.4 git remote添加、查看与切换git remote管理的是远程仓库别名。一个本地仓库可以同时关联多个远程比如公司内网一份、GitHub 公开一份。常用命令git remote -v git remote add origin gitgithub.com:user/repo.git git remote set-url origin gitgitee.com:user/repo.git git remote remove origin修改关联地址时不要删掉再重新 add直接set-url更稳妥。还有git remote show origin可以查看远程分支与本地分支的跟踪关系排查为什么 push 不到远程时很好用。IDE 里比如 IntelliJ IDEA 或全家桶自带的 Git 插件本质就是这些命令的图形化封装。遇到IDEA 创建新项目拉取 Git的需求我建议还是先在命令行把 clone 和 remote 搞定再回到 IDE 里打开项目这样出问题时你至少知道自己每一步做了什么。6. 敏感信息与仓库安全从 .git 目录泄露说起git 目录泄露如何下载这个热词让我有点担心。网上确实存在利用网站部署时遗漏了.git目录通过访问/.git/config或/.git/HEAD来下载源码的攻击手法。这里我不打算演示怎么攻击但作为开发人员你更需要知道的是为什么你的.git目录会暴露、暴露后会造成什么后果以及如何从源头上避免提交敏感文件。6.1 部署时为什么会把 .git 留在服务器上很多静态站点、CMS 或小型项目部署时图省事直接把整个项目文件夹打包上传或者用 rsync 同步整个目录到服务器.git目录就会跟着上去。.git目录本身包含了所有历史提交、对象文件、配置信息一旦被人通过 HTTP 直连访问到等于把整个仓库的完整历史暴露了包括你曾经提交过又删除的数据库密码、密钥、内网地址。正确的部署姿势是只同步构建产物比如dist、build目录或者在服务器端把.git目录加入 Web 服务器的拒绝访问列表。Nginx 可以加一条location ~ /\.git { deny all; }Apache 则可以在.htaccess里写RedirectMatch 404 /\.git6.2 历史中的敏感信息清理filter-repo 与重新生成密钥比部署更常见的场景是你稀里糊涂把.env文件或密钥写进过一次提交后来虽然删了文件但提交记录里依然永久保留着这个内容。这种信息不是靠删掉文件再提交一次能抹掉的因为它躺在历史对象里。清理思路分三步第一步把当前状态里的敏感文件彻底移除跟踪并加进.gitignore。git rm --cached .env echo .env .gitignore git commit -m 移除环境变量文件第二步用 git filter-repo 把所有历史提交中的该文件剔除git filter-repo --path .env --invert-paths这个命令会遍历所有提交把.env的存在彻底从历史中删除。执行后提交 hash 全部变化所有协作者需要重新 clone。第三步最硬核的一条密码、密钥、Token 这类信息即使从历史中删除了也可能已经被抓取并缓存了。安全界的主流建议是当它泄露永远不要相信它直接作废旧密钥、重新生成新的否则清理历史只是心理安慰。6.3 用 .gitignore 和扫描工具守住底线把防线前置比事后擦屁股高效太多。我给每个新仓库都初始化一份基础.gitignore至少包含这几类.env .env.* *.pem *.key *.p12 node_modules/ dist/ build/ target/ .idea/ .vscode/ *.log还有一个小习惯在提交前用git diff --cached --name-only扫一眼看看暂存区到底有哪些文件避免git add .顺手把不该传的传上去。更严格一点可以在 CI 里加一个密钥扫描工具在推送前自动拦截。GitHub 本身也有 secret scanning会把扫描到的密钥信息通知平台方并建议你主动失效。真正被泄露后的第一反应也不应该是我删掉重新提交一下而是立刻判断泄露信息的严重程度如果你是把自己的云厂商密钥传上去了第一时间去控制台禁用或轮换密钥这比任何 Git 操作都紧急。我自己的习惯是每次新建仓库先把.gitignore、.gitattributes、全局配置全部就位再动手写代码每次提交前用git diff自己审查一遍每次 push 前想一下这个分支有没有被别人在用这个提交是不是真的要推到公共分支。这些看起来慢其实是在给未来的自己省事。Git 命令本身不复杂复杂的是你在什么时机用哪个命令而这一点只能靠大量实际操作来积累肌肉记忆。平时多练练 stash、rebase -i、reset 和 revert 的排列组合等真正线上出问题时你才能不慌。
返回列表