ARTICLE DETAIL

资讯详情

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

Git从入门到实践:安装配置、分支管理与高频问题排查全攻略

Git从入门到实践:安装配置、分支管理与高频问题排查全攻略 1. 安装与首次配置开工前的三件事说实话Git 刚接触时很多人第一反应是把命令大全背下来。但实际写了两年代码你会发现真正天天敲的 Git 命令就那么几十条高频操作翻来覆去都是那几个套路。比起背命令更重要的是把环境配好、把流程理顺这样后面每一步都不会莫名其妙出问题。我会按实际工作中“从零到能顺利提交代码”的顺序来讲装好 Git、配好身份信息、搞定免密登录。这三件事做完你才算真正有了一个能干活的环境。1.1 安装 Git别在这一步纠结太久不同系统装法不一样但都属于“一次性操作”装完基本就不用再碰。Windows 用户直接去 Git 官网下 Windows 版安装包也就是 Git for Windows一路 Next 基本没问题。但有两点我建议你手动改一下PATH 选项安装到“Adjusting your PATH environment”那一步时选“Git from the command line and also from 3rd-party software”。这样你在 cmd、PowerShell 里都能直接用git而不是只能在 Git Bash 里用。默认编辑器安装时如果让你选 Git 默认编辑器默认是 Vim。新手在 Vim 里输入提交信息经常会卡住不知道怎么保存退出建议直接选 Visual Studio Code 或者 Notepad省掉很多不必要的麻烦。macOS 上最简单的是用 Homebrew 安装brew install gitLinux 看发行版Debian/Ubuntu 用 aptCentOS/RHEL 用 yum 或 dnf# Debian/Ubuntu sudo apt install git # CentOS/RHEL sudo yum install git不建议从源码编译安装除非你有特殊需求。系统自带的 Git 版本一般够用不过版本太老的话像git switch这种 2.23 之后才有的命令会不支持这点心里有数就行。装完先验证一下git --version能输出版本号安装这步就过了。1.2 开箱必设的全局配置身份信息与换行符Git 刚装完是不能直接提交代码的因为你提交的每一笔 commit 都会带有作者信息。没有配置的话Git 要么报错要么生成一个毫无意义的默认名字。全局配置只需要设置一次之后这台机器上所有仓库都会生效git config --global user.name 你的名字 git config --global user.email 你的邮箱这里有个容易被忽略的细节邮箱最好和你代码托管平台的账号邮箱保持一致。因为 GitLab、Gitee 这类平台一般通过邮箱把提交记录关联到你的账户头像和组织信息邮箱不一致的话提交记录上显示的是个灰色匿名用户影响不大但看着别扭。另外三条配置我是强烈建议直接设上git config --global core.autocrlf input git config --global core.quotepath false git config --global push.default simplecore.autocrlf解决的是换行符问题。Windows 用回车换行CRLFLinux/macOS 用换行LF如果不处理同一个文件在 Windows 上打开看起来就像“整个文件都被修改了”非常恶心。具体三种取值我后面在问题排查部分专门讲这里先记住Mac/Linux 用inputWindows 用户如果是纯 Windows 环境可以用true如果是跨平台协作建议靠仓库里的.gitattributes统一管理比个人配置可靠得多。core.quotepath false是为了让中文文件名和中文路径正常显示。Git 默认会把非 ASCII 字符转义成类似\346\265\213...这种八进制形式看着像乱码配置完就舒服了。这个在 Windows 上尤其推荐。push.default simple是 Git 2.0 之后默认值但很多老项目或老教程还在用matchingsimple 的含义是git push只推送当前分支到同名远程分支更安全直观建议显式设置一下。验证全局配置可以执行git config --global --list顺便说一句如果前面安装时选了 Vim 作为编辑器也可以补一条配置改掉git config --global core.editor code --wait1.3 SSH 免密登录不用每次输密码才是真舒服日常开发中最常用的远程仓库交互方式是 HTTPS 和 SSH。HTTPS 每次 push 都要输用户名密码现在很多平台已经不支持账号密码直接 push得用 Personal Access Token 代替密码更麻烦。所以我的建议是直接用 SSH配好之后永久免密。生成密钥的命令ssh-keygen -t ed25519 -C 你的邮箱-t ed25519指定密钥类型现在的 Git 平台基本都支持密钥短、安全性高、性能好。除非公司内网 GitLab 比较老不支持 ed25519才需要退回去用ssh-keygen -t rsa -b 4096 -C 你的邮箱执行ssh-keygen时会问保存路径默认是~/.ssh/id_ed25519如果这个文件已经存在说明你之前生成过密钥直接回车会覆盖掉要小心。我建议直接回车用默认路径除非你已经有一对密钥并且还在用。生成完查看公钥内容cat ~/.ssh/id_ed25519.pub把输出的一整串内容复制下来到你代码托管平台的“SSH Keys”设置页面添加。平台不需要知道你私钥的内容私钥留在本地就够了。测试连接是否成功ssh -T gitgitee.com如果用的是 GitLab就把域名换成对应的比如ssh -T gitgitlab.example.com。第一次连接会询问是否信任主机指纹输入yes回车。如果配置成功平台会返回欢迎信息比如“Hi xxx! Youve successfully authenticated”。如果你需要在多个平台比如公司 GitLab 加个人 Gitee使用不同的密钥那就得用到~/.ssh/config文件每个平台配一个别名Host gitee HostName gitee.com User git IdentityFile ~/.ssh/id_ed25519_gitee Host gitlab-work HostName gitlab.company.com User git IdentityFile ~/.ssh/id_ed25519_work之后拉代码的时候remote 地址就可以写成gitgitee:用户名/仓库名.git这种形式SSH 会根据别名自动选择对应的密钥不再互相干扰。2. 日常提交链路clone、add、commit、push 全套姿势装好环境之后真正的日常高频操作就开始了。这一套动作你每天都会重复很多次从远端拉代码、查看改动、暂存、提交、推送。看起来简单但细节非常多比如git add .和git add -A到底有什么区别、git pull和git pull --rebase差在哪这里一起讲清楚。2.1 git clone 的几种常见姿势拿到一个新仓库第一件事通常是克隆到本地git clone gitgitee.com:team/project.git默认会克隆仓库的默认分支同时把远端地址命名为origin本地自动建立跟踪关系。大部分场景这一条就够了。但实际工作中经常遇到“仓库太大”的情况。前端项目光 node_modules 几万个文件后端仓库塞了一堆历史二进制资源全量克隆可能要几分钟。如果只是临时看看某个分支的代码可以用浅克隆git clone --depth 1 -b release-1.0 gitgitee.com:team/project.git--depth 1表示只拉最近一次提交的历史-b指定分支。这样克隆速度会快很多。后遗症是这是一个“浅仓库”没有完整历史如果后续想拉全量历史执行git fetch --unshallow还有一种情况仓库启用了 Git LFS 但本机没装或者网络不太好克隆的时候容易卡在下载大文件那一步。这种时候可以先跳过 LFS 内容GIT_LFS_SKIP_SMUDGE1 git clone gitgitee.com:team/project.git这条命令会先克隆仓库结构LFS 大文件只拉指针不拉内容后面需要的时候再单独补。具体在第五章 LFS 部分展开。2.2 状态与差异检查提交前必看的两个命令我每次开工的第一件事就是git status -sb而不是大多数人习惯的git status。-s是精简输出-b会顺便显示当前分支和远程跟踪状态git status -sb输出类似这样## main...origin/main M src/App.js ?? notes.md含义很直白M表示已修改但未暂存??表示未跟踪的新文件。一眼扫过去就知道仓库当前什么状态比git status那一大段文字高效得多。看具体改了什么用git diff。这里有三层关系要注意git diff工作区和暂存区对比看的是你改了但还没git add的内容git diff --cached暂存区和最近一次提交对比看的是你已经git add的内容git diff HEAD工作区全部改动和最近一次提交的对比等于前两个之和我个人的习惯是提交前先git diff看看未暂存的改动确认没问题后git add再git diff --cached复查一遍暂存的内容最后才git commit。这个习惯看着麻烦但真的能少犯很多“把调试代码提交上去”的低级错误。想看改动涉及哪些文件、每个文件多少行用git diff --stat2.3 git add 与 git commit提交的进阶玩法很多新手喜欢一上来就git add .然后git commit -m update这样做没问题但有几个坑值得说一下。git add -A和git add .在大多数场景下效果一样但如果你在子目录里执行git add .它只会暂存当前目录及以下的变化父目录的删除不会包含。用git add -A更保险它是全仓库范围操作。另一个很实用的命令是git add -p交互式分块暂存。比如你改了三个文件其中两个是功能代码一个是调试日志输出现在只想提交功能代码就可以git add -pGit 会把每个文件的改动拆成若干块逐个问你“是否暂存这块”y 暂存、n 不暂存、s 拆分更细、e 手动编辑。这是把一次大改动拆成多个语义化提交的神器代码评审的时候特别好用。提交信息我建议遵循“标题 正文”的结构git commit -m feat: 增加用户注册接口 -m - 新增注册页面 - 校验手机号格式 - 处理重复注册异常单行-m是标题第二个-m是正文Git 会把它们拼成一个多行提交信息比用空格拼在一行里清晰得多。还有一个高频操作提交之后发现漏了一个文件或者提交信息写错了这时候不要急着“再提一个提交”用git commit --amend修改上一次提交git add 漏掉的文件 git commit --amend这条命令会用当前暂存区内容替换上一次提交。注意只能修改还没有推送到远端的提交。如果已经 push 了amend 之后本地历史和远端历史不一致再 push 会被拒绝只能强推而强推会让其他拉过这个分支的同事陷入混乱。所以规则很简单没 push 随便 amendpush 过了就老老实实新提一个提交。2.4 push 与 pull推送被拒怎么办首次推送一个新分支记得带上-ugit push -u origin feature/login-u等价于--set-upstream作用是建立本地分支和远程分支的跟踪关系。建好之后后续直接git push和git pull就能自动匹配远程分支不用每次写全。pull 和 push 之间最容易翻车的场景是你本地提交之后远端正巧被别人推了新提交执行git push时收到类似提示! [rejected] main - main (fetch first) error: failed to push some refs这个提示的意思是远端有你本地没有的提交直接 push 会覆盖远端历史Git 拒绝执行。正确姿势是先拉取再推送git pull --rebase git push这里我要特别强调--rebase。默认情况下git pull等于git fetch加git merge会在本地生成一个额外的 merge 提交历史变成乱七八糟的分叉。加上--rebase之后Git 会把你本地未推送的提交“摘下来”放到远端最新提交的后面历史保持线性。多人协作的项目里线性历史真的能省掉一堆 review 时的认知负担。如果你发现自己团队的每个人都这么干可以直接全局设置默认行为git config --global pull.rebase true这样以后直接git pull就自动 rebase 了。不过如果你的工作流就是希望保留 merge 节点做版本记录那别设这个按团队规范来。3. 分支管理与合并多人协作的命脉分支是 Git 最核心的设计之一。一个人开发可以不用分支但三四个人往同一个仓库提交分支管理要点如果没有掌握分分钟把 main 搞乱。这一章把分支的创建、切换、合并、删除和冲突解决完整过一遍。3.1 分支操作速记创建、切换、重命名、删除分支操作本身不复杂复杂的是概念。先给一份速查清单# 查看本地分支 git branch # 查看本地远程全部分支 git branch -a # 创建并切换分支 git checkout -b feature/login # 或者新版 Git 推荐 git switch -c feature/login # 切换已有分支 git checkout feature/login git switch feature/login # 重命名分支 git branch -m old-name new-name # 删除本地已合并分支 git branch -d feature/login # 强制删除本地分支 git branch -D feature/login # 删除远程分支 git push origin --delete feature/logingit switch是 2.23 版本引入的专门用于切换分支的命令语义比checkout更清晰因为checkout还承担着恢复文件的重任。建议新项目统一用switch。分支名建议包含类型前缀比如feature/login、bugfix/issue-123、hotfix/order-pay。这样在分支列表里一眼能看出用途和归属。项目大一点之后分支命名规范真的很重要不然三个月前的分支根本不知道能不能删。3.2 merge 与 rebase什么时候用哪个分支合并有两种主要方式merge 和 rebase。很多人搞不清两者区别其实核心差异就一点merge 保留分叉历史rebase 重写提交历史。举个例子你从 main 拉了feature/login分支开发了两天期间 main 上多了两个提交。此时git merge main在 feature 分支上执行会产生一个 merge 提交把 main 的新提交并进来历史长这样* merge commit |\ | * main 新提交2 | * main 新提交1 * | feature 提交2 * | feature 提交1git rebase main会把 feature 上的每个提交“摘下来”逐个应用到 main 最新提交之后历史变成一条直线* feature 提交2 * feature 提交1 * main 新提交2 * main 新提交1看起来 rebase 更干净但它有个副作用提交的 hash 会变。如果你把 feature 分支 push 到了远端并且其他同事也在基于它开发这时候你 rebase 并强推别人的本地历史就全乱了。我的习惯是个人开发的分支随便 rebase目的是让自己的提交历史干净、能顺滑地跟上 main 的节奏合并到公共分支时用git merge --no-ff强制生成一个 merge 提交标记“这组功能合入主干”的时间点方便回溯和打 tag。公共分支本身绝不去 rebase。3.3 冲突解决别怕有套路无论 merge 还是 rebase只要两个人改了同一个文件的同一块区域就会冲突。冲突本身不可怕处理起来是有固定套路的。先看冲突长什么样。打开冲突文件你会看到类似这样的标记 HEAD 当前分支的代码 另一条分支的代码 feature/login HEAD到之间是当前分支HEAD的内容到之间是正在合并进来的分支内容。你需要决定保留哪边或者两边都保留然后把这些标记行全部删掉。我的处理流程是# 1. 列出所有冲突文件不遗漏 git diff --name-only --diff-filterU # 2. 逐个打开文件解决冲突 # 3. 每个文件解决完标记为已解决 git add 文件路径 # 4. 全部解决后merge 场景提交 git commit # 4. 或者 rebase 场景继续 git rebase --continue如果你用的是 VS Code冲突区域会显示“Accept Current Change / Accept Incoming Change / Accept Both”按钮比较直观。IDEA 的合并工具也做得不错可以把左右两边和最终结果同时展示出来。有一个坑需要提醒rebase 过程中如果你改了几个提交冲突可能不是一次解决完。Git 会一个提交一个提交地重放每遇到冲突就停下来让你处理处理完git rebase --continue它再继续下一个。如果搞到一半发现局面不可收拾不要硬刚直接git rebase --abort这条命令会放弃整个 rebase 操作分支回到 rebase 之前的状态重新来过。3.4 远程分支同步fetch 与 prune 的妙用多人协作时别人的分支你不会永远看到。本地记录的远程分支列表可能和远端真实状态已经脱节了。git fetch只更新本地的远程跟踪分支比如origin/feature/login不会动你的工作区。我经常用这个操作了解远端最新提交git fetch origin git log HEAD..origin/main第二行会显示出远端 main 有而本地没有的提交看完再决定是 merge 还是 rebase。这比直接git pull更安全因为 pull 会直接改变工作区有时候本地有未提交改动就会被拒绝。当别人删除了远程分支你本地的git branch -r还会看到一堆已经不存在的origin/xxx。清理方式git fetch --prune或者更明确git remote prune origin执行完之后那些远端已经删除的分支引用会从本地消失。如果你发现自己本地远程分支列表越堆越多多半是好久没 prun 了。4. 撤销与回滚四种后悔药怎么选Git 最强大的地方之一就是“几乎什么都能撤销”。但撤销方式太多反而让很多人迷惑reset、revert、restore、checkout、reflog到底什么时候用哪个这一章把它们分成两类讲清楚。4.1 reset 三种模式soft、mixed、hard 怎么选git reset的核心动作是“把 HEAD 指针移动到某个提交”同时决定要不要同步重置暂存区和工作区。它有三种模式模式HEAD 位置暂存区工作区典型场景--soft移动保留保留提交后发现漏文件想重新提交--mixed默认移动重置保留提交后发现不想提交这块内容--hard移动重置重置彻底丢弃本地改动举个实际例子。你刚提交了一个 commit但发现里面混进了一个调试用的临时文件想拆出来。此时不要git reset --hard那会把整个提交都删掉。正确姿势是git reset --soft HEAD~1HEAD~1表示上一个提交。执行后HEAD 回退到上一个提交但所有改动都保留在暂存区。接下来你可以git reset HEAD 临时文件把它移出暂存区再重新git commit就完成了“拆分提交”的操作。--mixed是默认模式比如你提交了一个大杂烩想把所有改动恢复到未暂存状态重新整理git reset HEAD~1改动保留在工作区但已经不在暂存区。git status会显示它们是被修改但没有git add的文件。--hard是最危险的操作。它会把你指定提交之后的所有本地改动全部丢弃git reset --hard origin/main这条命令在“本地已经改得面目全非干脆同步远端”的场景很好用但执行前务必确认三个东西有没有未提交的改动、有没有需要留档的文件、当前分支是不是你真正想重置的分支。我见过太多人把--hard当成万能清理工具结果把辛辛苦苦写了两天的代码一键抹掉。如果真的不幸用了--hard又后悔别急第四小节有救。4.2 revert已推送提交的正确回滚姿势git reset是把历史“剪刀剪掉”git revert则是“在历史里补一笔反向修改”。假设你往 main 推了一个功能提交abc1234上线后发现有问题需要回滚。这时候不能git reset --hard abc1234^再强推因为 main 是公共分支强行改写历史会导致所有同事的本地仓库都出问题。应该用git revert abc1234这条命令会创建一个新的提交内容正好把abc1234的改动反过来。原提交还在但实际代码效果等于那个功能被撤销了。优点是对历史友好跟普通提交一样推送不影响任何人。revert 多个提交时要小心。比如你想撤销最近三个提交如果你按时间从旧到新逐个 revert后面的提交可能包含前面已经 revert 的内容冲突会莫名其妙。正确顺序是从最新的往回git revert HEAD~2 HEAD~1 HEAD或者用区间写法git revert --no-commit HEAD~2..HEAD git commit--no-commit表示只把反向修改应用到工作区和暂存区不自动生成提交你可以把所有反向修改合并成一个提交再手动提交。这样历史里只多一个“回滚某三个提交”的提交观感上更干净。revert 同样可能遇到冲突处理方式跟普通 merge 冲突一模一样改文件、git add、git revert --continue。4.3 reflog找回丢失提交的最后救命稻草Git 其实把每一步操作都记在了本地日志里包括那些“被丢失”的提交。这个日志就是git refloggit reflog输出类似abc1234 HEAD{0}: commit: 修复登录 bug def5678 HEAD{1}: reset: moving to HEAD~1 ghi9012 HEAD{2}: commit: 新增登录页面每行代表“HEAD 曾经处于某位置”并记录了当时是通过什么操作到达这里的。哪怕你git reset --hard把提交删了只要 reflog 里还有记录就能找回。操作方式git reflog git reset --hard ghi9012把 HEAD 恢复到那个提交一切就像没发生过。但要注意reflog 是本地操作日志默认保留 90 天而且只在本地。仓库被重新 clone、机器更换、90 天前丢的提交reflog 也救不回来。所以 reflog 是“临时后悔药”不是“永久保险柜”。重要代码推远端才是真正的保险。5. 保持仓库干净stash、.gitignore 与 LFS一个代码仓库用久了最常见的三个痛点分别是切换分支时本地改动无处安放、某些文件永远不该被提交、大体积二进制文件拖垮整个仓库。这一章对应三个解决方案git stash、.gitignore、Git LFS。这些都属于平时不显眼、但关键时刻能救命的操作。5.1 git stash临时把改动藏起来典型场景你在feature/login分支开发到一半改动还没写完整不能提交但线上出了紧急 bug需要立刻切到main修。如果直接切分支Git 会因为当前工作区和目标分支有冲突而拒绝这时候就要用到 stash。git stash push -m 登录页开发中 -u-m给这一份暂存改动起个名字方便后面找-u表示把未跟踪的新文件也一起藏起来。默认情况下 stash 只处理已经跟踪的文件新文件如果不加-u会留在原地切换分支后就很尴尬。之后切分支、改 bug、提交、切回来恢复之前藏的改动git stash list git stash poppop会把最近一份 stash 恢复并删除记录。如果你只想恢复但保留备份用git stash apply。多个 stash 共存时用编号指定git stash list git stash apply stash{1}不需要的 stash 及时清理git stash drop stash{1} git stash clearclear会清空所有 stash操作前务必确认没有后悔可能。还有个小技巧stash 并不严格绑定分支你在feature/login分支 stash 的东西在main分支也能pop。如果两个分支的代码差距很大pop 时会冲突。所以我的习惯是 stash 时写清楚说明pop 之前先git stash show stash{0}看看里面是什么避免张冠李戴。5.2 .gitignore 为什么总是不生效“.gitignore 里写了规则但文件还是显示在 git status 里”这是出现频率极高的问题。原因一句话就能解释.gitignore 只对未跟踪untracked文件生效已经被 Git 跟踪的文件忽略规则管不着。比如你一开始就把config.ini提交进了仓库后来才在.gitignore里加了config.ini这个文件依然会被跟踪。每次修改它Git 依然提示有改动。要让 ignore 规则对它生效必须先把文件从 Git 索引里移除但保留工作区文件git rm --cached config.ini执行后config.ini变成未跟踪状态同时本地文件还在再去修改 .gitignore 提交一次这个文件的跟踪关系就断掉了。比较粗暴但好用的全量操作git rm -r --cached .这会把所有已跟踪文件从索引移除然后重新git add .再提交。效果是让新的 .gitignore 规则对整个仓库重新生效。提交历史里会看到一次“大量文件变更”实际上内容没动只是索引关系重建。适合在一次性整理忽略规则的时候用。再检查一下你自己的规则写法。.gitignore常见语法# 忽略 node_modules 目录 node_modules/ # 忽略所有 .log 文件任意目录 *.log # 忽略 build 目录下的所有内容任意层级 **/build/ # 忽略 .env 文件但保留 .env.example .env !.env.example注意/结尾表示目录**可匹配任意层级!开头是“取反”规则。常见写错的地方是把目录和文件混淆比如规则写了build/但实际目录是dist/自然不生效。另外改完.gitignore记得提交不然其他同事拉下来还是旧的。附一份比较通用的基础模板# 依赖 node_modules/ vendor/ # 构建产物 dist/ build/ out/ # 日志 *.log logs/ # 环境变量 .env .env.local !.env.example # 编辑器 .idea/ .vscode/* !.vscode/settings.json .DS_Store5.3 Git LFS大文件该单独管理Git 对文本文件非常友好但对于二进制大文件设计稿、模型、安装包Git 会把每次历史版本都完整存下来仓库体积迅速膨胀clone 和 fetch 都变得无比痛苦。Git LFSLarge File Storage解决这个问题的方式是仓库里只存一个几十字节的“指针文件”真正的文件内容存到 LFS 服务端需要时再拉取。启用 LFS 分三步# 1. 本机安装 LFS 过滤器全局只需一次 git lfs install # 2. 指定哪些文件走 LFS 管理 git lfs track *.psd *.zip *.exe # 3. 把 LFS 生成的 .gitattributes 提交上去 git add .gitattributes git commit -m chore: 配置 LFS 管理二进制文件之后这些类型文件的 add、commit、push 操作和普通文件完全一样只是底层逻辑变了。.gitattributes必须提交这一点怎么强调都不过分。因为其他同事 clone 这个仓库时需要靠它知道哪些文件应该走 LFS 过滤器。如果.gitattributes没提交别人会把大文件当作普通 Git 对象推送仓库照样膨胀。clone 一个启用 LFS 的大仓库时如果卡在下载大文件阶段常见解法是这样的。先用环境变量跳过 LFS 拉取只拿仓库结构和指针GIT_LFS_SKIP_SMUDGE1 git clone gitgitee.com:team/project.git需要某个目录的 LFS 文件时再单独拉git lfs pull -I assets/design/*如果你发现自己的 Git 配置里莫名设置了lfs.fetchexclude导致某些路径的 LFS 文件始终不拉取可以查询和解除git config --list | grep lfs git config --global --unset lfs.fetchexcludelfs.fetchexclude本来是“拉取时排除某些目录”的优化手段但它一旦误配坑起人来特别隐蔽文件在远端明明存在本地就是拉不下来而且没有任何明显报错。遇到“clone 成功但某目录大文件缺失”的情况第一反应就查这个配置。6. 高频疑难杂症排查实录理论讲完来点实际的。这一部分我把工作中遇到频率最高的几个 Git 问题整理出来每一个都是我或身边同事真实踩过的坑。文章没写到的组合情况也可以按这些思路去排查。6.1 SSH 认证失败从“Permission denied”到定位问题报错长这样gitgitee.com: Permission denied (publickey). fatal: Could not read from remote repository.排查顺序固定下来就好确认你在正确的目录下生成过密钥公钥已经粘贴到平台。用cat ~/.ssh/id_ed25519.pub查看和平台上对比是否一致。确认 SSH 实际使用了哪个私钥。执行ssh -vT gitgitee.com-v会输出详细日志里面能看到 SSH 尝试了哪些密钥文件。有一回同事配置好了公钥但它有两对密钥SSH 默认先尝试~/.ssh/id_rsa旧密钥而不是他在平台上新加的那对就一直是 Permission denied。解决办法要么删掉旧密钥要么用~/.ssh/config指定别名和密钥就是第一章里那个方案。确认 ssh-agent 状态。Windows 下如果密钥路径比较特殊可能需要eval $(ssh-agent -s) ssh-add ~/.ssh/id_ed25519很多 Linux 发行版默认会加载~/.ssh/id_ed25519和~/.ssh/id_rsa但如果你用了自定义文件名ssh-agent 不会自动加载需要手动ssh-add。如果平台支持临时用 HTTPS 地址也能绕过 SSH 问题git remote set-url origin https://gitee.com/用户名/仓库名.git不过这只是应急问题根源还是要从密钥链路里找。6.2 Windows 下的换行符与中文乱码换行符问题的经典表现是在 Windows 上提交了一个文件同事在 macOS 上打开 diff发现整个文件的所有行都被标记为修改但其实内容一个字没变。原因就是 CRLF 和 LF 的差异。core.autocrlf三个取值的解释取值提交时CRLF-LF?检出时LF-CRLF?适用场景true是是Windows 单平台input是否Mac/Linuxfalse否否仓库本身已统一更稳妥的方案是直接在仓库根目录提交一个.gitattributes让换行符规则跟着仓库走不依赖每个开发者的个人配置* textauto *.sh text eollf *.bat text eolcrlf *.jpg binarytextauto表示 Git 自行判断文本文件并统一换行符eollf强制 shell 脚本用 LF.bat脚本强制 CRLFWindows 批处理对换行较敏感图片等二进制文件标记为binary不参与任何换行转换。中文文件名在命令行或 IDE 里显示成\346\265\213这种转义形式是因为 Git 默认对非 ASCII 路径做转义。设一下就行git config --global core.quotepath false另外很多人在 IDEA 里见过这样的命令参数git -c diff.mnemonicprefixfalse -c core.quotepathfalse --no-optional-locks ...这不是什么魔法解释一下就明白了-c临时设置配置项只在当前命令生效diff.mnemonicprefixfalse让 diff 输出使用常见的a/文件、b/文件前缀而不是i/、w/core.quotepathfalse就是刚才说的中文路径不转义--no-optional-locks告知 Git 在 status/diff 这类只读操作中不要获取可选文件锁避免 IDE 频繁刷新和命令行操作抢锁6.3 Git 目录泄露与部署安全很多团队直接在服务器上git pull部署Web 根目录就是仓库目录。这时如果服务器没有拦截对.git目录的访问别人访问https://你的域名/.git/config就能把仓库配置甚至源码下载下来。我就见过有生产环境把.git整个暴露源码被拖干净的案例。防护也简单。如果 Web 服务是 Nginx在站点配置里加location ~ /\.git { deny all; }Apache 则在配置里加DirectoryMatch ^/.*/\.git/ Require all denied /DirectoryMatch更好的做法是不要让 Web 根目录直接等于 Git 仓库目录把代码拉取到其他路径部署时用 rsync 或 CI 产物同步到 Web 目录服务器上根本不保留.git。这样即使犯了配置错误也没有目录可以泄露。安全问题的原则是“从根源消除”而不是靠一层拦截。顺带提醒一句.gitignore里忽略的.env、配置文件等敏感文件只保证不会进入 Git 提交不保证不会出现在服务器上。生产环境配置应该走独立的密钥管理系统别指望 Git 管好一切。6.4 常见问题速查表把日常工作中最常遇到的 Git 问题和对应解法整理成一份速查表建议收藏现象常见原因解决命令push 被拒提示 fetch first远端有新提交git pull --rebase后重新 pushPermission denied (publickey)SSH 公钥未配置或私钥错误检查平台公钥ssh -T测试.gitignore不生效文件已被 Git 跟踪git rm --cached解除跟踪clone 卡在大文件下载LFS 体积大GIT_LFS_SKIP_SMUDGE1跳过 LFS提交信息写错未推送时git commit --amend误提交了大文件或敏感文件已推送到远端从历史移除需要 filter-branch/filter-repo立刻改密钥切换分支时本地改动冲突工作区有未提交更改git stash push -u或提交中文路径显示转义core.quotepath默认git config --global core.quotepath false整个文件 diff 全是换行CRLF/LF 不一致配置.gitattributes想彻底丢弃本地改动确认不保留git reset --hard先确认误删分支或提交本地操作失误git reflog找回拉取 LFS 文件缺失lfs.fetchexclude误配置git config --global --unset lfs.fetchexclude本地分支落后且本地有未提交改动想同步远端先 stash/commit再git pull --rebase想查看某文件某次提交改了什么不知道 commitgit log --oneline -- 文件路径收尾我的一点个人经验最后分享一个我个人的工作习惯不是什么高深技巧但真的很管用每天开工和收工各执行一次git status -sb和git pull --rebase。开工时为了确认昨天的工作状态收工时为了确保没有遗漏未提交的改动。另外我给自己配了几个常用别名敲起来快很多git config --global alias.st status -sb git config --global alias.lg log --graph --oneline --decorate -10 git config --global alias.last log -1 HEAD --stat git config --global alias.unstage reset HEAD --配置完成之后git st就是精简状态git lg就是彩色提交历史git unstage 文件就能把误 add 的文件移出暂存区。命令记不全没关系靠平时多敲形成肌肉记忆比你死记硬背一整本 Git 手册要靠谱得多。
返回列表