☰
Git代码提交实操指南:从工作区到远端仓库的完整流程与冲突处理
2026/10/18 23:03:49 网站建设 项目流程

还在用“git 提交代码”把自己绕晕?每次 pull、commit、push 的顺序都靠肌肉记忆,遇到冲突就头皮发麻?别急,这篇文章把我自己从 “git 菜鸟” 到 “命令行操作如喝水” 的完整经验整理出来了。不管你是刚入行的新人,还是被同事的“乱提交”折磨的队友,这篇都能给你一套闭眼可用的标准操作流程。

很多初学者甚至部分“熟练工”都觉得 git 难,不是因为命令本身复杂,而是脑子里没有一套完整的流程。今天我就把这套流程拆开了揉碎了讲,从最基础的原理到最容易翻车的冲突解决,再到几个我踩坑踩出来的小技巧,一次性说清楚。

1. 内容整体设计与思路拆解

1.1 先搞清楚我们要解决什么问题

git 传代码,本质上就是解决一个问题:如何安全、有序、不丢代码地把本地修改送到远端仓库。听起来简单,但实际场景里,你很可能遇到过这些情况:

  • 提交完之后发现漏了一个文件,结果又补了一次 commit,历史记录里全是“fix typo”、“update”。
  • push 的时候报错,提示远端有更新,你怕冲突,直接强推,把同事的代码覆盖了,第二天被拉去开会。
  • 拉取代码的时候发现冲突,不知道怎么处理,一顿乱改,不光没解决冲突,还把不该删的代码删了。

这些问题,我一个一个都遇到过。后来我总结出一个核心理念:git 操作不是靠背命令,而是靠理解状态。你只要时刻清楚代码在本地仓库、暂存区、远端仓库这三个位置中的哪一步,思路就清晰了。

1.2 为什么强烈推荐“先拉再推”的标准动作

对于团队协作而言,顺序其实非常有讲究。最简单的原则就是“先更新、后提交、再推送”。很多人图省事,习惯性直接 commit 然后 push,结果远端已经被别人更新过了,你的 push 就会被拒绝。被迫 merge 的时候又会引发一堆冲突。

所以我个人的习惯是,只要准备提交,开头必然先git pull --rebase,再 commit,再 push。这一下就能避免绝大部分无谓的合并记录。当然,rebase和merge的选择本身就有很多讲究,后面我专门用一节来讲,这里先不展开,大家记住“先拉再推”这个总原则就行。

1.3 核心逻辑:工作区、暂存区、本地仓库的关系

我一直在想用什么类比来解释 git 的状态最合适,后来发现“存钱”这个例子最容易懂:

  • 工作区(Working Directory)—— 你手里的现金,随便花,随便改,只要没存进银行,都算你自己的事。
  • 暂存区(Index / Stage)—— 你填好的存款单,钱已经点了,但还没交付到柜台,也许你还会想再改改。
  • 本地仓库(Local Repository)—— 你的银行账户,一旦 commit 进去,这笔钱就有了交易流水,不可随意抹除。
  • 远端仓库(Remote Repository)—— 你存进总行的账户,其他同事(其他分行)才看得到、拿得到。

理解了这四个概念,git 传代码的流程就清晰了:在工作区里改代码,把要交的东西挑拣进暂存区,在本地仓库生成一个不可回退的记录,最后推送到远端让别人看见。是不是一下子就通透了?

2. 核心细节解析与实操要点

2.1 初始化与配置:别等出错了才想起配置身份

很多时候我们拿到一台新电脑或者新项目,第一件事就是git init。但你会发现,即使你本地初始化成功,第一次 commit 也会报错,提示你配置 user.name 和 user.email。这是 git 的基本身份校验机制,可别想着能跳过,它会直接卡住你提交操作。

我建议新手在安装完 git 后,立刻执行这两条命令,做全局配置:

git config --global user.name "你的名字" git config --global user.email "你的邮箱"

这里有个小细节值得注意:--global是全局配置,会影响这台电脑上所有仓库。如果你只希望在一个仓库里用特定身份(比如公司项目和私人项目分开),就在项目目录下执行不带--global的版本。配置完了可以用git config --list检查一下,省得后面 push 的时候远端记录里挂着一个奇怪名字。

2.2 精准暂存:add 的三种境界

git add是操作暂存区的命令,但很多新手只知道git add .(添加所有变更)。不是不行,而是不够好。

直接添加全部文件,很容易把调试用的临时文件、本地配置文件、甚至依赖包不小心加进来。这时候.gitignore文件的作用就出来了。我每次新建项目,必写.gitignore,把node_modules/、dist/、.env、*.log这些通通排除在版本管理之外。

在暂存阶段,我常用的命令有:

git add src/ # 添加某个目录 git add app.py # 添加某个文件 git add -p # 交互式逐块暂存,非常精致

git add -p是最容易被忽略的一个神级命令。它会把你的改动按“块”拆开,让你挑选哪些改动需要被暂存,哪些暂时不提交。比如你在一个文件里同时改了一个新功能和一行调试日志,用-p就能只把功能代码提交上去,把调试日志留在工作区。这个命令的熟练程度,基本可以算是 git 操作的一个分水岭。

2.3 提交的艺术:写清楚 commit message 才算真正完成

很多人都把 commit 当成“存个档”,message 随便写个 “update” 甚至直接留空。说实话,这对自己是省事了,对同事和以后的自己,就是灾难。你想想,三个月后你看到一行git log满屏都是“update”,你能记得当时改了啥吗?

我建议遵循业界常用的规范格式,大致长这样:

<type>(<scope>): <subject>

常见的 type 包括feat(新功能)、fix(修 bug)、docs(文档)、style(格式)、refactor(重构)、test(测试)等。scope 表示影响范围,可以省略。举个例子:

git commit -m "fix(login): 修复密码重置后无法登录的问题"

这样一行信息,任何人都能一眼看懂这次提交做了什么。如果你觉得一行不够描述清楚,可以不加-m,直接执行git commit,git 会打开默认编辑器让你写多行详细描述。在团队里,一份清晰的提交历史,就是最好的项目文档。

我还特别想提醒一点:不要提交你还没有写完的代码。半成品代码提交上去,如果同事 pull 下来跑不起来,你的 commit 就成了“项目毒药”。宁可放在工作区或者单独的分支上,也别打断团队主线。

2.4 分支管理:别在主分支上裸奔

分支这块,很多新手和老手的理解程度完全不同。新手完全不用分支,一个 master 走天下,push 全靠莽。真正经历过多人协作、代码被覆盖、频繁冲突的人,才会明白分支就是 git 给你搭的“安全围栏”。

我推荐一个稳定好用的“三贱客”分支模型:

  • main(或master):主分支,长期存在且保持稳定,只能通过合并分支进入,不能直接在上面提交。
  • develop:开发分支,大家日常的工作从这里拉出去分支,再合并回来。
  • feature/xxx:功能分支,比如feature/login-modal、feature/payment-api。你开发新功能就开一个,开发完合并后就删掉。

每次新开发前,我都习惯在最新的develop基础上新建分支:

git checkout develop git pull origin develop git checkout -b feature/my-feature

这样做的绝对好处是,你在自己的开发流里为所欲为,没人能干扰你。就算代码写崩了,删掉这个分支重开一个就行,完全不影响主干,心理负担大大降低。

3. 实操过程与核心环节实现

3.1 最经典的完整提交流程演示

下面这套流程是我在团队里推行的标准动作,几乎适用于所有日常工作场景。我们来模拟一次完整的开发提交流程。

假设我在feature/user-center分支上开发了一个用户中心的功能,现在要提交代码:

# 1. 先看一眼当前仓库的状态,心里有个底 git status # 2. 切换到自己的分支(如果你是新建分支,这步会带上 -b) git checkout feature/user-center # 3. 把远端最新的代码拉到本地,保持基线在同一水平线 git pull origin feature/user-center # 4. 将改动的文件加入暂存区 git add src/pages/user/ src/utils/userHelper.js # 5. 确认已经加入暂存区的文件情况 git status # 6. 提交到本地仓库(凝视你的 commit message 三秒,再确认) git commit -m "feat(user): 新增用户中心个人信息展示与编辑功能" # 7. 推送前再拉一次远端代码,防止有新的提交 git pull origin feature/user-center # 8. 推送本地提交到远端 git push origin feature/user-center

流程跑完,你的代码就安全抵达远端了。注意我在第 7 步又做了一次 pull,这其实是双保险策略。很多人可能认为第 3 步已经拉过了,没必要再拉一次,但实际操作中,有时候你第 3 步拉的版本和第 7 步之间的时间窗口里,同事可能刚好 push 了内容。多拉一次总比后面处理 rebase 冲突要划算。

我从第 3 步的pull多用的是git pull --rebase。上面说了--rebase和默认 merge 的差异很大,这里我来补一段完整解释。

3.2 深度解析:pull 到底该用 merge 还是 rebase

这是很多 git 教程里最含糊的一个点。git pull默认等价于git fetch+git merge。也就是说,它会把远端的新提交直接合并到你的本地分支,生成一个“合并提交”。这样的好处是记录真实,缺点也很明显——提交历史会被搞得多叉分叉,时间线变得像一团毛线。

而git pull --rebase等价于git fetch+git rebase。它会把你本地尚未推送的提交“摘下来”,接到远端最新的提交之后,就像你的代码总是在最新代码之后修改的一样,整个历史是一条干净直线。

我个人的标准是:在你的个人功能分支上,用 rebase;在公共的 develop/main 分支上,用 merge。这样既保持了个人分支的整洁,又避免在公共分支上重写提交历史导致其他同事的“操作历史”被更改。

如果 pull 的过程中出现冲突,rebase 的处理方式也稍微复杂一点,不过别担心,下面会专门讲。

3.3 实战演示:完整演示 rebase 下的冲突处理

假设现在我在feature/user-center分支,同事往develop分支提交了一个修改,而且恰好改的是同一个文件。我的操作如下:

# 在 feature 分支执行 git pull origin develop --rebase

执行之后,git 会提示冲突:

CONFLICT (content): Merge conflict in src/utils/userHelper.js

这时候不要慌。执行git status,你会看到这个文件被标记为both modified。打开文件,你大概率会看到这样的内容:

<<<<<<< HEAD const AGE_LIMIT = 18; ======= const AGE_LIMIT = 21; >>>>>>> develop (commit message)

上面是当前分支的代码,下面是远端进来的代码。这里要做的是:根据需求手动判断保留哪个值,或者综合改成合理的代码,然后把<<<<<<<、=======、>>>>>>>这些标记全部删掉。修改完后执行:

# 标记冲突已解决 git add src/utils/userHelper.js # 继续执行 rebase 流程 git rebase --continue

git 会弹出 commit message 编辑界面,默认沿用之前提交的信息,直接保存退出即可。如果你在 rebase 过程中想中断放弃,可以使用git rebase --abort回到 rebase 前的状态。我有一段时间对冲突合并分外的谨慎,每次 rebase 之前都会复制一份当前分支到 tmp 分支,相当于一个后悔药。

3.4 推送被拒绝怎么办:强推有风险,操作需谨慎

push 被拒是另一种高频问题,一般表现为:

! [rejected] feature -> feature (fetch first) error: failed to push some refs

原因很简单,远端有本地没有的提交。这时候很多人会偷懒,直接git push -f(强推)。我必须强烈地说:在共享分支上,永远不要强推。强推会覆盖远端记录,同事如果没及时拉取,他们的本地提交就相当于“消失”了,干掉的不是代码,是信任。

如果分支只有自己用(比如某个纯私有功能分支),强推问题不大,可以放心。但多人共享分支,正确做法就是上面 3.3 里的那套操作——先git pull --rebase origin 分支名,处理冲突,然后普通git push就能成功了。

如果你发现自己不小心已经把强推干出去了,并且影响到了同事,赶紧跟同事同步情况,让他们在了解冲突的前提下重新处理本地分支。这不是技术问题,是沟通问题。

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

4.1 push 到一半网络中断、目标分支选错之类的问题

有一种情况,commit 成功了,push 到一半却断网了,导致远端其实没有完整收到你的提交。此时仓库状态比较暧昧,常见的措辞是failed to push some refs。排查方式很直接:

# 查看本地和远端分支的差异 git status # 看到 ahead 状态,就知道本地领先远端了 # 然后直接重新 push git push origin branch-name

git 的 push 操作设计成幂等的,只要上次不是覆盖性操作(force),重新执行一下就好。别直接删本地分支,也别急着大动作。

另外,还有一种常见的乌龙:在main分支上直接改了代码,想 push 到远端,发现拒绝。我的建议是,如果真的误改了 main 分支并且还没提交,就直接git stash暂存起来,切走,在新分支 pop 出来继续干。

git stash git checkout feature/another-branch git stash pop

这一套下来,代码安然无恙,你也脱离了危险的 main 分支。

4.2 想撤销刚才的 commit:reset 和 revert 怎么选

撤销提交也是高频操作。很多新手的习惯是直接git reset --hard,这个是“时光倒流”的感觉,直接把所有提交干掉,危险度很高。硬重置会让你丢失所有未推送的本地修改和记录,而且无法恢复(除非你用了 reflog),我不建议在任何场景随意使用硬重置。

如果要撤销还没推送的 commit,但想保留改动,可以用软重置:

git reset --soft HEAD~1

这会把 commit 撤销,但保留你的代码改动在暂存区。相当于后悔了,把东西退回到“已暂存”状态。

git reset --mixed HEAD~1

更常用一点,撤销 commit 并把改动退回工作区,需要重新 add,这也是默认参数。

如果 commit 已经 push 到远端了,这时候别用 reset,因为会跟远端历史冲突。正确的姿势是新增一个反向提交:

git revert HEAD

git revert 会创建一个新提交,内容是把上一个提交的改动翻转过来。这样既保留了原有历史,又能让代码回到之前状态,是协作场景里最安全的后悔药。

4.3 .gitignore 不生效:文件已经被跟踪了

这个问题极高频,而且踩坑的人特别多。你在.gitignore里写了node_modules/,但 git status 还是能看到它。原因很可能是这个目录之前已经被纳入了版本控制,.gitignore只对“未跟踪”的文件生效。

解决方法是先把缓存里的文件移除跟踪:

git rm -r --cached node_modules/

这条命令会保留磁盘文件,只从 git 的跟踪列表里移除。之后 add 一下.gitignore和这个变更,再 commit 一次,你会发现那些大文件终于不再出现在 git status 里了。

4.4 误删了分支怎么办: reflog 的救命用途

这是几乎每个老手都会遇到的一次惊魂时刻。比如git branch -D feature/tmp,然后发现这个分支上还有没合并的辛辛苦苦写的代码。一瞬间汗毛都竖起来了。

别慌,git 有一张“所有分支操作”的日志表,叫 reflog。它是本地仓库的“时光机”。执行:

git reflog

你会看到一串操作记录,里面记录了每个分支 HEAD 曾经指向的 commit 哈希。找到你想恢复的那个 commit 哈希(形如a1b2c3d),然后:

git checkout -b feature/tmp-recovered a1b2c3d

这个分支就复活了,代码完好无损。我有个朋友真的靠这个方法,在删错分支后救回了差不多两周的工作量。所以,遇事不决先查 reflog,这是本地仓库最后的底线。

4.5 常见报错信息对照速查表

报错信息特征核心原因推荐操作
unable to read askpass response身份验证/凭据失效检查 SSH key 或 token,重新认证
Please commit or stash...切换分支时本地有未提交改动先 commit 或 stash 再切换分支
You have unmerged paths合并/rebase 冲突未解决完逐个处理冲突并 add,再继续 merge/rebase
src refspec xxx does not match any本地分支名拼写错误git branch查看实际分支名再 push
failed to push some refs远端有新提交,推送被拒先 pull 再 push,禁止无脑强推
Not a git repository在非 git 目录执行了 git 命令检查当前目录是否为仓库根目录

这张表标注了常见报错的特征、原因和解法,建议收藏。我每次带新人的时候,都要求他们把这张表贴在自己的笔记软件里,遇到问题先对号入座。

5. 进阶技巧与团队协作心得

5.1 用别名简化高频命令

操作熟练之后,人就懒了。Git 命令其实有很多可以自定义的“快捷方式”,叫别名(alias)。我对高频操作做了几个别名,用了好多年,效率极其显著:

git config --global alias.st status git config --global alias.cm commit git config --global alias.co checkout git config --global alias.br branch git config --global alias.lg "log --oneline --graph --all --decorate"

设置好之后,这些别名永久生效。git lg看提交历史,一屏就能看清所有分支的分叉走向,非常好用,我用完之后再也没用回原版 log。

5.2 Commit 提交之后发现少了个文件

很多人用完git commit后发现漏了个文件,就赶紧又补一个 commit,搞出两条记录。其实 git 有个“后悔药”选项,可以把漏掉的内容追加到上一个 commit 里:

git add 漏掉的文件 git commit --amend --no-edit

--amend表示修改上次提交,--no-edit表示保留原始 commit message,不会弹出编辑器。这个操作会把两次改动合并成一个 commit,历史看起来依然干净。

但是有一件事要牢牢记住:--amend等同于重写上次提交,如果上次提交已经 push 到远端,那么本地和远端历史就不一致了。这种时候,只有你自己在用的分支才推荐这么操作。公共分支上千万别用,会引起一堆同步问题。

5.3 如何写一封“有信息量”的 Pull Request 描述

很多团队用 PR/MR 来合并代码。我看到过太多 PR 描述就是标题一句话,点进去完全不知道改了什么、为什么要改、影响范围是什么、怎么测试。这等于把一个“代码变更”硬生生变成了“猜谜游戏”。

我自己的习惯是写清楚四件事:

  • 背景:为什么有这次改动?业务上或者技术上的原因是什么?
  • 改动内容:核心是哪些文件,哪个模块,大致思路是什么?
  • 测试方式:我验证了什么?有没有跑单测?手动验证了什么路径?
  • 风险点:哪些地方可能会影响现有功能?需要重点 review 哪里?

这个习惯刚开始有点繁琐,但坚持下来之后,我发现团队里 review 代码的效率高了不止一个档次,因为双方不需要反复沟通来猜测意图。

5.4 分支命名与 commit 规范,说一百遍都不嫌多

关于团队协作中最大痛点,我的体会是:不是代码写得烂,而是提交记录烂。一行“fix bug”看不出任何信息量,等于没有提交信息。所以如果你是在一个团队里工作,且你们还没有一套 git 规范,我建议你主动牵头拟一份,大致的骨架是:

  • 分支命名:feature/开头表示功能,fix/开头表示修复,docs/表示文档,后面跟简要描述,如feature/login-page-v2。
  • commit message:用上文提到的 Conventional Commits 格式,type 必写,scope 可写可不写,subject 用祈使句,不要超过 50 个字符左右。
  • 合并方式:公共分支用 merge --no-ff(保留合并记录),个人分支随意。

有了规范以后,哪怕团队人很多,git log翻出来依然是条理清晰的历史书,这是团队“技术整洁度”的最直接体现。

6. 实操心得:从“会用”到“用好”

很多人问我,到底要怎么才能学好 git?我的答案是:忘掉命令大全,去理解 git 的底层模型。你不需要背下所有命令的参数,只需要知道你的“代码文件”目前处于四层状态中的哪一层,然后选择把这一层推进到下一层的命令就行了。

另外,从心态上给大家一个建议:git 操作没有“一锤子买卖”。所有危险操作,几乎都有后悔药。reset有 reflog 护体,rebase有abort兜底,stash有pop恢复。所以不要因为怕出错就不敢操作,越不敢操作,技术就越生疏。哪怕真的一不小心删了分支,只要没跑git gc(清理垃圾对象),基本都还能找回来。

最后分享一个压箱底的习惯:提交前花 30 秒审查一下git diff。很多人不看直接 add 全部,结果把调试代码、console.log 全部发上去了,后面处理起来非常尴尬。对这个场景,我用的就是最原始的:

git diff

看一眼改动是否符合预期,再提交。这个习惯不仅帮你过滤低级失误,还会慢慢提高你对代码变更的敏感度。真到了团队合作里,别人看到你的 commit 永远干干净净,这个信任感是无价的。

git 传代码这件事,说到底并不难,难的是把每一步养成规范习惯。按照这篇文章的方法,从下一次 commit 开始,试着把 message 写清楚、把分支理干净、把流程走全。坚持一个月,你会回来感谢自己。

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

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

立即咨询