ARTICLE DETAIL

资讯详情

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

Git指令全链路实战:从安装配置到回滚与冲突处理

Git指令全链路实战:从安装配置到回滚与冲突处理 写完代码准备提交推送结果git命令一个接一个报错好不容易推上去了发现提交信息写错了再一看分支还搞乱了。这些场景干开发的兄弟应该都不陌生。git这玩意日常无非就是 clone、add、commit、push、pull 那一套但真到了要改历史、回滚版本、处理冲突的时候不少人就开始发怵了。这篇东西不聊什么高深理论就针对“git相关指令”把从安装配置、日常高频操作到提交改写、回滚恢复、远程协作这套完整链路走一遍每一步都给出可以直接抄的指令和参数顺带把我自己踩过的坑也一并说了。适合刚接触git的新手以及那些指令会用但遇到报错就懵的进阶选手。1. 环境准备从零装好Git别让第一步卡住你1.1 三个平台的安装差异与版本选择不管你是Windows、macOS还是Linux第一步都是把git本体装好。Windows用户最简单的方式是直接下载安装包一路Next。但注意安装过程中有几个选项值得留心一是“Adjusting your PATH environment”务必选“Git from the command line and also from 3rd-party software”否则后面在cmd或PowerShell里敲git会提示找不到命令二是行尾转换那一步默认“Checkout Windows-style, commit Unix-style line endings”就行后面我会解释为什么这个默认值对跨平台协作很重要。macOS上如果你装了Homebrewbrew install git一行搞定没装的话直接去官网下pkg包也行。Linux尤其是Ubuntu/Debian系用sudo apt install gitCentOS用sudo yum install git注意Ubuntu的apt源里git版本可能会偏老不影响日常使用但如果要用到较新的submodule或worktree特性建议加git官方PPA或自行编译。下载安装包时如果速度不理想可以去国内镜像站点下载这是网络加速的正常手段没必要跟下载源较劲。装完后先验证一下git --version能输出版本号就说明装成功了。接下来说一个很多人忽略的点Windows上建议把git bash和系统自带终端都留着。git bash模拟的是Linux环境里面那些ls、grep、sed、awk在Windows原生cmd里是不存在的但你日常开发如果用的是VS Code或IntelliJ系列内置终端直接选git bash作为默认shell体验会顺滑很多。1.2 装完必做的三件事全局配置、换行符、默认编辑器装好git后第一件事不是急着clone而是先做身份声明。git的每个commit都会带上作者信息如果没配好提交时会报错或者留下一串乱码身份。git config --global user.name 你的名字 git config --global user.email 你的邮箱 git config --global core.editor code --wait三行命令分别搞定名字、邮箱和默认编辑器。把默认编辑器设成VS Code是强烈推荐的否则你commit的时候万一要补充提交信息git会默认弹出一个Vim很多新手直接卡在里面不知道怎么保存退出。用code --wait之后git会等待VS Code窗口关闭再继续整个流程就亲切多了。换行符这块必须展开讲。Windows和Unix/Linux对换行的存储方式不一样Windows用CRLF回车换行Linux/macOS用LF换行。如果git不做任何处理同一个文件在不同的操作系统间切换时git diff会显示每一行都有修改因为整个文件都被识别为改动过了。所以git有一个autocrlf机制git config --global core.autocrlf true # Windows用户 git config --global core.autocrlf input # macOS/Linux用户Windows设true的意思是checkout代码时把LF转成CRLFcommit时把CRLF转回LF。这样仓库里永远存的是LF而本地工作区是CRLF跨平台协作就不会出现同一个文件被反复标记为修改的尴尬了。macOS/Linux设input就行因为它们在本地就用LF不需要转换。这一步虽然不显眼但实际项目里因为换行符引发的冲突比代码逻辑冲突还让人头疼我见过不止一个团队在merge时莫名其妙出现几千行冲突最后排查下来就是换行符的锅。1.3 和远程仓库建立信任SSH密钥配置gitee为例接下来要做的是让本地git和远程仓库比如gitee互相认识。HTTP方式每次push都要输账号密码虽然可以用凭据管理器缓存但终究麻烦而且某些内网环境对HTTP长连接限制不少。SSH密钥的方式一劳永逸配一次之后所有推送拉取都不用再输密码。ssh-keygen -t rsa -b 4096 -C 你的邮箱执行后会问你保存路径和密码短语直接一路回车密码短语不设置也行设置了每次用私钥都要输入图省事就留空。生成的文件默认在用户目录的.ssh文件夹下公钥是id_rsa.pub私钥是id_rsa。私钥绝对不能外传公钥随便给。然后查看公钥内容cat ~/.ssh/id_rsa.pub把输出的整段文本复制下来登录gitee在“设置”-“SSH公钥”里粘贴保存。最后测试一下ssh -T gitgitee.com如果提示“Hi, xxx! Youve successfully authenticated”就说明密钥配置成功了。这一步相当于告诉远程服务器以后看到这把公钥就认你是自己人。2. 高频指令实战从第一个commit到正常推送2.1 add、commit、status、log——每天都离不开的四兄弟把一个文件纳入git的版本管理最基础的路径是add到暂存区再commit固化成一个版本。很多新手不理解为什么要分两步其实就是暂存区给了你一个挑选和审视的机会你改动了十个文件但本次提交只打算提交其中三个或者想分两次提交比如一次提交界面改动一次提交逻辑改动这都是日常非常常见的诉求。git status # 查看当前状态红色是未跟踪/已修改绿色是已暂存 git add src/main.py # 把单个文件加入暂存区 git add . # 把当前目录下所有改动加入暂存区 git commit -m feat: 新增登录接口 # 提交并写信息 git log --oneline # 简洁查看提交历史一行一个commit先说git status它输出的信息很关键能看到你当前在哪个分支如On branch master、有没有文件改动、有没有文件还没被git跟踪。在commit之前养成先status的习惯能避免漏提交文件或者误提交垃圾文件。再说git log --oneline这条指令会把每个提交压缩成一行包含一个短哈希和提交信息比如a1b2c3d feat: 新增登录接口。--oneline还有几个变体很有用--graph会画出分支合并的拓扑图--all显示所有分支的提交-5只显示最近5条。提交信息这个事圈内有很多规范业界最有名的是Conventional Commits。说白了就是提交信息前缀约定feat:表示新功能fix:表示修bugdocs:表示文档改动refactor:表示重构chore:表示杂务。用这类前缀不是为了好看而是后续生成changelog、自动过滤提交记录、判断版本号的major/minor/patch升级全都依赖这个格式。我自己的习惯是英文前缀中文描述比如fix: 修复空指针导致的崩溃沟通清楚且不费脑。2.2 推送、拉取与协作push、pull、fetch、clone远程协作绕不开四个指令clone、fetch、pull、push。最容易混淆的是fetch和pull。fetch是把远程的更新拉到本地仓库但不会动你的工作区pull则是fetch之后再merge直接更新工作区代码。理解了这点就明白为什么很多人建议在pull之前先commit或stash保存自己的本地改动否则pull时出现冲突会直接被强制闯入处理起来很被动。git clone gitgitee.com:用户名/仓库名.git # 首次从远程拿到完整项目 git pull origin master # 拉取远程master更新并合并 git fetch origin # 只下载远程更新不合并 git push origin main # 推送本地main分支到远程 git push -u origin main # 首次推送-u建立本地分支和远程分支的追踪关系origin是远程仓库的默认别名clone下来的项目自动就叫这个名。-u的意思是“upstream”设置好追踪关系后以后直接git push和git pull就行git知道该跟哪个远程分支对应。这里有个经验之谈在push之前先pull一次。哪怕你觉得自己改的和别人完全不搭边也pull一下把远程的新提交合并进来再检查有没有冲突最后push。这个过程虽然多一条指令但能大幅减少在远程端因为非快进而被拒绝的尴尬。万一你pull和push之间被打断了远程又被别人推了新代码你push时会看到! [rejected] main - main (fetch first) error: failed to push some refs to ...别慌这是git的自我保护。解决方式是再pull一次合并后再push。2.3 分支操作checkout、branch、merge分支是git最值得炫耀的设计。它本质上是创建了一个可移动的指针指向某个提交所以创建分支的成本极低几乎瞬间完成。git branch dev # 创建dev分支 git checkout dev # 切换到dev分支 git checkout -b feature/login # 创建并切换等价于上面两条 git branch -d dev # 删除dev分支-d只允许删除已合并的分支 git branch -D dev # 强制删除未合并也能删慎用 git merge dev # 把dev分支合并到当前分支日常开发的黄金流程是主分支master/main保持稳定可发布开发时从主分支拉一个功能分支出来在功能分支上随便折腾写完了再合并回去。这里重点说merge。merge会生成一个新的合并提交历史里会多出“Merge branch xxx into xxx”这样一条记录。如果团队成员多、提交频繁历史树会变得很乱。所以一些团队选择用rebase方式拉取和整合这个话题我们在讲历史整理时再展开。分支命名也有讲究我见过的团队常用这些模式feature/用户登录、bugfix/修复订单金额错误、release/1.2.0、hotfix/紧急修复崩溃。命名清晰的分支配合规范的提交信息光看git log就能知道项目演进的全貌这点在大型项目里价值极大。3. 写过代码都该掌握的“后悔药”回滚与改写3.1 还没提交的改动restore与checkout恢复改了一半发现方向错了想撤销重来。如果你还没把改动添加到暂存区直接git restore src/main.py这条指令让你工作区里这个文件恢复到最近一次提交的版本你本地那些未暂存的修改会全部丢失。如果你已经把文件git add进暂存区了需要用git restore --staged src/main.py这条不是撤销代码修改而是把文件从暂存区撤回到未暂存状态保留工作区里的改动。简单记忆法--staged管的是缓存区不加--staged管的是工作区。这个指令是git 2.23以后才引入的替代了原来git checkout -- file的旧写法。老版本的写法也能用但新指令的语义清晰得多不容易混淆。我日常最常犯的错就是把还没提交的改动覆盖了所以习惯是做大幅度回滚前先stash或复制一份保命为原则。3.2 已经提交的改动reset与revert情况升级你已经commit了但提交完就后悔了。这时有两个方向reset是回滚本地历史revert是生成一个反向提交。git reset --soft HEAD~1 # 撤销最近一次commit但保留改动在暂存区 git reset --mixed HEAD~1 # 默认模式撤销commit和暂存保留工作区改动 git reset --hard HEAD~1 # 彻底回滚工作区和暂存区都恢复改动全部丢失HEAD~1表示上一个提交HEAD~2表示上两个也可以直接用commit的哈希值。这里强烈建议不要随便用--hard因为它会直接丢弃工作区改动而且不可恢复。很多新手在向远程push之后还试图用reset --hard回滚然后push被拒绝折腾半天。正确的做法是如果提交已经push到了远程且别人可能已经拉取了绝不reset改用revert。git revert HEADrevert会生成一个新的提交内容是“把上一个提交的改动全部反向撤销”当前分支历史是向前推进的所以对远程是友好兼容的。团队协作中撤销公共历史必须用revert你改的是未来而不是篡改过去。3.3 commit --amend修改最近一次提交这个指令被问到的频率非常高因为场景太常遇了提交完发现漏了一个文件或者提交信息写错字了。在你尚未推送到远程前--amend是非常好用的工具git add src/missing_file.py git commit --amend --no-edit--no-edit表示保留原来的提交信息只追加内容如果你提交信息也要改直接git commit --amend -m 新的提交信息。它的本质是把当前暂存区内容合并进最近一次commit并生成一个新的commit替换旧的那个。留意“替换”二字原commit被丢弃了所以如果原commit已经push到远程再用--amend就会出现历史分叉push时会被拒绝需要强制push才能覆盖这在多人协作时非常危险。稳妥的原则是--amend只对还没推上远程的commit用。日常流程可以练成一个习惯commit之后push之前快速git log --oneline -3看一眼确认提交信息无误、文件齐全再git push。3.4 用rebase整理提交历史合并、改写、调序如果说--amend是修改最近一次提交那git rebase -i交互式rebase就是批量修改一连串提交。典型场景你在功能分支上开发了三五天留下了十几个琐碎的提交比如“改了个参数”“临时调试代码”“再改一下”。合并到主分支之前把这一堆碎提交整理成三个结构清晰的提交整个历史的可读性会有质的飞跃。git rebase -i HEAD~5这条指令会弹出编辑器列出最近5条提交每一行都有关键词可以改pick表示保留squash表示合并到上一个提交reword表示修改提交信息edit表示停下来修改提交内容等。把后面几条前面的pick改成squash保存退出git会逐个合并并让你整理新的提交信息。这里有个实战建议rebase和merge一样会冲突但冲突的顺序不一样。rebase是把你的提交逐个“移植”到新基础上过程中每个提交都可能冲突处理起来比merge更琐碎。所以除非你的提交足够“原子化”每次改动聚焦一个逻辑点、同时包commit否则rebase时很容易被一连串冲突搞到崩溃。我个人的偏好是功能开发期间随便commit无所谓但合并前一定用rebase -i整理整理时如果发现某个提交牵扯太多改动宁可把它拆开也不要硬塞在一起。4. 真实工作流实战从零开始托管一个项目4.1 本地初始化到推送gitee的完整指令序列把一堆散落的本地方件变成一个有远程仓库托管的项目到底要走哪些指令下面给你一套可以直接照抄的命令序列# 1. 进入项目目录 cd my-project # 2. 初始化本地仓库 git init # 3. 查看状态确认哪些文件会被跟踪 git status # 4. 写一个.gitignore先排除不需要的文件node_modules、编译产物等 vim .gitignore # 5. 把所有文件加入暂存区 git add . # 6. 看看这次暂存了哪些文件确认没有杂七杂八的东西 git status # 7. 提交 git commit -m chore: 项目初始化 # 8. 关联远程仓库在gitee上先新建一个空仓库会得到这个地址 git remote add origin gitgitee.com:用户名/my-project.git # 9. 推送-u建立追踪 git push -u origin master第4步的.gitignore往往被别人忽略但极其重要。如果你没写node_modules这种动辄几万文件的目录会被git统统跟踪起来仓库体积爆炸不说每次diff都卡成幻灯片。一份最基本的.gitignore至少应该包含操作系统生成的隐藏文件.DS_Store、Thumbs.db、依赖目录node_modules/、vendor/、编译产物dist/、build/、*.class、IDE配置.idea/、.vscode/。gitee提供了各语言常用的.gitignore模板直接选一份改改最好。第8步如果报remote origin already exists说明之前已经关联过了用git remote remove origin清掉再重新add即可。第9步推送时如果提示要设置user.name和user.email回到1.2节补上就行。4.2 日常迭代的一天功能分支、提交、推送初始化之后日常开发就进入重复节奏了。假设你要开发“忘记密码”这个功能git checkout master # 回到主分支 git pull origin master # 拿到最新代码 git checkout -b feature/forgot-password # 拉一条功能分支之后所有改动都在功能分支上进行master保持干净。开发中的提交可以直接写相对口语化的信息比如“完成验证码发送逻辑”因为反正是要squash整理的。功能完成后git add . git commit -m feat: 完成忘记密码功能 git checkout master git pull origin master # 合并前先同步主分支 git merge feature/forgot-password git push origin master合并后功能分支如果不再需要顺手清掉git branch -d feature/forgot-password这套流程看起来简单但有两个容易被忽视的点。第一合并前务必检查主分支代码是否最新否则merge时可能出现太多无关冲突。第二功能分支的合并信息尽量写清楚相比“合并分支xxx”写“合并功能忘记密码”对后来人友好得多。4.3 遇到的一长串git -c参数是什么东西有些开发工具在命令行里会拼出一长串看起来很吓人的指令比如git -c diff.mnemonicprefixfalse -c core.quotepathfalse --no-optional-locks status这不是乱码而是git把一部分配置项通过命令行临时传入。-c keyvalue的作用等价于临时修改git配置只对当前命令生效不影响全局配置。这里的diff.mnemonicprefixfalse是关闭diff输出里的临时前缀标识默认会显示a/和b/前缀某些工具会自动加上以区分两个对比对象core.quotepathfalse是让中文文件名在git输出时直接显示汉字而不是转义成\xxx的八进制编码--no-optional-locks是告诉git不要获取一些可选的内部锁避免影响并发操作。简单说就是某些GUI工具为了避免配置环境和桌面端行为不一致直接把必要配置写在每条指令后面确保行为稳定。你以后在命令行手工操作时不用这么费劲配置写在配置文件里即可但看到别人给出的指令带了-c明白是暂时的配置注入不是加密也不是魔法。5. 常见问题与排查技巧实录5.1 高频报错速查表git的命令行报错信息普遍不算浮夸但英文对新手还是有点劝退。我把实际项目里遇到频率最高的几类问题整理成了一张表报错信息实际原因解决方案fatal: not a git repository当前目录不是git仓库cd到正确目录或先git initPermission denied (publickey)SSH密钥没配好检查~/.ssh/id_rsa.pub是否已添加到gitee测试ssh -T gitgitee.comfatal: refusing to merge unrelated histories两个仓库没有共同的历史基线常见于本地init后又关联了远程git pull origin master --allow-unrelated-histories先合并再处理冲突error: failed to push some refs to ...远程有新提交本地推送被拒绝先git pull合并再pushPlease make sure you have the correct access rights无权限或地址不对确认仓库地址是SSH格式确认被授权可写Your branch is ahead of origin/master by 2 commits本地有2个提交还没推送正常状态git push即可关于refusing to merge unrelated histories这条我想多提醒一嘴。很多人图省事直接在gitee上新建仓库时初始化了README或license文件然后本地也已经有提交两边各自从零开始合并时必然报这个错。--allow-unrelated-histories一次性把两边强行并起来大部分情况下能成功但如果你本地和远程改动重叠会有很多冲突。最稳妥的流程是远程建空仓库不要勾选任何初始化文件本地init后直接remote add push从根源上避开这个问题。5.2 冲突处理实战把冲突标记当朋友冲突大概是git最劝退新手的场景了但它的机制其实很简单。当两个人改了同一个文件的同一行代码git无法自动判断谁对谁错时就会在merge或pull后停下在冲突文件里写入标记 HEAD 你当前分支的代码 对方分支的代码 feature/xxx这段标记明确告诉你有两版代码到之间是当前分支HEAD的到之间是对方分支的。你只需要打开文件手动决定保留哪边、或者两边整合成新代码然后删掉三行标记保存即可。之后执行git add src/conflict_file.py git commit这里不需要再手动写提交信息git会帮你准备好一个默认的merge提交信息。解决冲突时有个经验原则不要迷信任何一边也不要直接无脑选HEAD。正确做法是先看双方改动的意图和协作者确认后再整合。尤其是两个人都改了同一处逻辑简单合并两边代码的行为很可能引入隐藏bug。如果冲突文件特别多别硬刚先把大方向跟队友对齐然后逐个文件处理每处理完一个add一个最后commit节奏会稳很多。还有个小技巧用支持三路对比的编辑器VS Code就有内置的Source Control合并编辑器它能并排显示当前分支、对方分支和合并结果处理起来比纯文本标记直观得多。5.3 日志、清理和别名提升效率的三个小技巧最后分享三个我自己切实受益的小技巧算不上指令大全但很实用。第一是用git log的进阶写法快速了解某人最近提交了什么git log --author张三 --oneline -10第二个是清理未跟踪的多余文件git clean -nd # 先列出会被清理的文件-n是演习模式 git clean -fd # 确认后真正清理-f强制-d连目录一起清理这个指令慎用因为删掉的文件无法恢复。我通常在分支切换前发现工作区有一堆临时文件先git status --ignored看看有哪些被忽略的文件即将带过去再用git clean -nd看看哪些能清理最后才决定动不动手。第三是配置别名把高频指令缩短git config --global alias.st status git config --global alias.co checkout git config --global alias.lg log --oneline --graph --all --decorate配好之后git lg敲起来舒服得多。不过注意给别人分享指令或让别人看你的操作时尽量用完整指令否则对方可能看不懂你在做什么也影响网上搜索和沟通效率。别名是给自己用的快捷键不是给别人设的迷障。6. 写在最后的经验之谈git的指令体系说大不大说小也不小。很多人学git只记住了add、commit、push三板斧够用但一旦遇到历史改写、回滚、冲突就会手足无措。我自己的体会是git最大的价值不是“存代码”而是“留退路”——它允许你随时回到任何一个历史节点允许你把一段混乱的草稿整理成清晰的作品。所以从第一天起就养成看git status和git log的习惯比记住一百条命令都重要。还有一个小建议遇到不确定后果的指令先查它会修改什么、丢弃什么再用git stash把现场保存起来给自己留条后路。工具是死的习惯是活的把这几条核心指令练成肌肉记忆大多数协作场景你都能从容应对了。
返回列表