从零开始掌握Git:我用了三年总结的实战干货,让你少走弯路
说起Git,估计很多人都有过这样的经历:刚开始接触的时候觉得简单,不就是几个命令嘛,push、pull、commit,有什么难的?结果真正用起来发现处处是坑,代码冲突、分支混乱、历史记录乱七八糟...
我记得刚工作那会儿,因为对Git理解不够深入,曾经把整个项目的提交历史搞得一团糟,最后只能重新建仓库。那次之后我就下定决心要把Git彻底搞明白。
今天就把我这几年使用Git的经验和踩过的坑全部分享出来,相信看完之后你对Git的认识会有一个质的提升。
先搞清楚Git到底是个啥
很多人一上来就开始学命令,但其实理解Git的工作原理更重要。Git本质上就是一个分布式版本控制系统,它会追踪文件的每一次变化,并且可以让多个人协同工作。
想象一下你在写一份重要的文档,每次修改前你都会复制一份备份,比如"方案v1.doc"、"方案v2.doc"、"方案最终版.doc"、"方案真的最终版.doc"... 这种做法很熟悉吧?Git做的事情本质上就是这样,只不过它更智能,能够追踪每个字符的变化。

Git有几个重要的概念需要理解:
- 工作区:就是你能看到的文件
- 暂存区:临时存放待提交的修改
- 仓库:最终保存版本历史的地方
这就像是一个生产流水线,你在工作区修改文件,然后把修改放到暂存区等待质检,最后合格的修改才会被送到仓库永久保存。
基础操作,但很重要
配置用户信息
刚安装Git后第一件事就是配置用户信息,这个步骤很多人会忽略,但非常重要:
git config --global user.name "你的名字"
git config --global user.email "你的邮箱"这里有个小技巧,如果你在公司和个人项目中使用不同的邮箱,可以在具体项目中单独设置:
git config user.email "work@company.com"初始化仓库
有两种方式开始一个Git项目:
方式一:从头开始
mkdir my-project
cd my-project
git init方式二:克隆已有项目
git clone https://github.com/user/repo.git我个人更推荐方式二,因为克隆下来的仓库已经配置好了远程仓库地址。
文件状态管理
Git中文件有几种状态,理解这些状态对后续操作很重要:
# 查看文件状态
git status
# 查看具体修改内容
git diff
# 添加文件到暂存区
git add filename
# 或者添加所有修改
git add .
# 提交到仓库
git commit -m "提交说明"这里说一个我的习惯,我很少用git add .,而是喜欢一个文件一个文件地添加,这样可以确保每次提交的内容都是我确实想要提交的。
git add src/main.js
git add src/utils.js
git commit -m "优化主要逻辑和工具函数"提交信息的艺术
提交信息可能是很多人最容易忽视的部分,但好的提交信息对项目维护太重要了。我见过太多这样的提交信息:
- "修复bug"
- "更新"
- "test"
- "临时提交"
这种信息等于没有信息。我现在遵循这样的规范:
# 格式:类型: 简短描述
git commit -m "feat: 添加用户登录功能"
git commit -m "fix: 修复购物车计算错误"
git commit -m "docs: 更新API文档"
git commit -m "refactor: 重构数据库连接逻辑"类型包括:
- feat: 新功能
- fix: 修复bug
- docs: 文档更新
- style: 代码格式调整
- refactor: 重构
- test: 测试相关
- chore: 构建工具、依赖更新等
分支管理,这是重点

分支是Git最强大的功能之一,但也是最容易出问题的地方。
基本分支操作
# 查看所有分支
git branch -a
# 创建新分支
git branch feature/user-login
# 切换分支
git checkout feature/user-login
# 创建并切换到新分支(推荐)
git checkout -b feature/user-login
# 新版本Git可以用switch
git switch -c feature/user-login
我现在基本上都用git checkout -b,一步到位。
分支策略
在实际项目中,分支策略非常重要。我们公司现在用的是Git Flow的简化版本:
main:主分支,只放稳定代码develop:开发分支,日常开发的基础feature/*:功能分支,开发新功能时使用hotfix/*:紧急修复分支
举个例子,假如我要开发一个用户注册功能:
# 从develop分支创建功能分支
git checkout develop
git pull origin develop
git checkout -b feature/user-register
# 开发完成后
git add .
git commit -m "feat: 实现用户注册功能"
git push origin feature/user-register然后在GitHub或GitLab上创建Pull Request,代码审查通过后再合并到develop分支。
分支合并
合并分支有几种方式,每种都有自己的使用场景:
- Fast-forward合并(默认)
git checkout main
git merge feature/user-login- No-fast-forward合并(保留分支历史)
git merge --no-ff feature/user-login- Squash合并(压缩提交)
git merge --squash feature/user-login我个人比较喜欢用--no-ff,因为能清楚看到功能分支的开发历史,对追溯问题很有帮助。
远程仓库操作
基本远程操作
# 查看远程仓库
git remote -v
# 添加远程仓库
git remote add origin https://github.com/user/repo.git
# 推送到远程
git push origin main
# 从远程拉取
git pull origin main
# 获取远程更新但不合并
git fetch origin这里有个坑需要注意,git pull实际上等于git fetch + git merge,如果你不想自动合并,建议分开执行:
git fetch origin
git diff HEAD origin/main # 查看差异
git merge origin/main # 确认后再合并处理推送冲突
经常会遇到这种情况,你想推送代码时提示:
! [rejected] main -> main (fetch first)这时候不要直接git push --force!正确的做法是:
git fetch origin
git rebase origin/main
# 解决冲突后
git push origin main或者如果你更喜欢merge:
git pull origin main
# 解决冲突后
git push origin main冲突解决,没那么可怕
代码冲突是多人协作时必然会遇到的问题,刚开始可能会觉得很复杂,其实掌握了套路就不难。
识别冲突
当Git无法自动合并时,会在文件中插入冲突标记:
<<<<<<< HEAD
你的修改
=======
别人的修改
>>>>>>> branch-name这些标记把冲突的内容分成了两部分:
<<<<<<< HEAD到=======:你的修改=======到>>>>>>> branch-name:别人的修改
解决冲突
解决冲突的步骤:
- 找到所有冲突文件:
git status - 打开冲突文件,手动编辑
- 删除冲突标记(
<<<<<<<,=======,>>>>>>>) - 保留你想要的内容
- 添加到暂存区:
git add filename - 提交:
git commit
举个实际例子,假设在配置文件中发生冲突:
// 冲突前
<<<<<<< HEAD
const API_URL = 'https://api.prod.com';
=======
const API_URL = 'https://api.test.com';
>>>>>>> feature/new-api解决后:
// 解决后
const API_URL = process.env.NODE_ENV === 'production'
? 'https://api.prod.com'
: 'https://api.test.com';这样既保留了两个版本的内容,又避免了环境问题。
使用工具解决冲突
命令行解决冲突有时候不够直观,可以配置图形化工具:
# 配置merge工具(以VSCode为例)
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
# 使用工具解决冲突
git mergetool我个人更喜欢直接在编辑器中解决,VSCode对Git冲突的支持就很好。
版本回退与历史管理
查看提交历史
# 查看提交历史
git log
# 简洁格式
git log --oneline
# 图形化显示分支
git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'
# 查看某个文件的历史
git log -p filename最后那个命令有点长,但显示效果很好,我把它设置成了别名:
git config --global alias.lg "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'"之后就可以用git lg来查看美观的历史记录了。
版本回退
有时候需要回退到之前的版本,Git提供了几种方式:
- 回退到上一个提交
git reset --hard HEAD~1- 回退到指定提交
git reset --hard commit-hash- 只回退代码,保留提交历史
git revert commit-hashreset和revert的区别很重要:
reset:直接回退,历史记录会丢失revert:创建一个新提交来撤销之前的修改,历史记录保留
在团队项目中,如果代码已经推送到远程,建议使用revert,避免影响其他人的代码。
修改提交信息
# 修改最后一次提交信息
git commit --amend -m "新的提交信息"
# 修改最后一次提交内容(添加遗漏的文件)
git add forgotten-file.txt
git commit --amend --no-edit如果要修改更早的提交,需要用到rebase,这个稍微复杂一些:
git rebase -i HEAD~3 # 修改最近3次提交实用技巧和最佳实践
使用.gitignore
.gitignore文件用来告诉Git忽略某些文件或目录,这些通常是:
- 编译产物(如
dist/,build/) - 依赖包(如
node_modules/) - 配置文件(如
.env) - 系统文件(如
.DS_Store)
# 依赖包
node_modules/
vendor/
# 构建产物
dist/
build/
*.min.js
# 配置文件
.env
.env.local
config/database.yml
# 日志文件
*.log
logs/
# 系统文件
.DS_Store
Thumbs.db
# IDE文件
.vscode/
.idea/
*.swp有个网站https://gitignore.io很好用,可以根据你的技术栈自动生成.gitignore文件。
Git别名配置
设置一些常用的别名可以大大提高效率:
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD'
git config --global alias.visual '!gitk'我最常用的几个别名:
git config --global alias.ac '!git add -A && git commit -m'
git config --global alias.acp '!git add -A && git commit -m "$1" && git push'这样就可以用git ac "提交信息"来快速添加并提交所有文件。
暂存工作进度
有时候工作做到一半需要切换分支处理其他事情,但代码还没到可以提交的状态,这时候可以用stash:
# 暂存当前工作
git stash
# 暂存时添加说明
git stash save "正在开发登录功能"
# 查看所有暂存
git stash list
# 恢复最近的暂存
git stash pop
# 恢复指定暂存
git stash apply stash@{1}
# 删除暂存
git stash drop stash@{0}我经常用这个功能,特别是当PM突然说有个紧急bug需要修复的时候。
标签管理
标签通常用来标记重要的版本:
# 创建轻量标签
git tag v1.0.0
# 创建带注释的标签
git tag -a v1.0.0 -m "发布版本1.0.0"
# 查看所有标签
git tag
# 查看标签信息
git show v1.0.0
# 推送标签到远程
git push origin v1.0.0
# 推送所有标签
git push origin --tags
# 删除标签
git tag -d v1.0.0
git push origin :refs/tags/v1.0.0子模块使用
当项目依赖其他Git仓库时,可以使用子模块:
# 添加子模块
git submodule add https://github.com/user/library.git libs/library
# 克隆包含子模块的项目
git clone --recurse-submodules https://github.com/user/main-project.git
# 初始化子模块
git submodule init
git submodule update
# 或者一步到位
git submodule update --init --recursive不过说实话,子模块用起来比较麻烦,现在更多项目选择用包管理器(如npm、composer)来管理依赖。
团队协作中的Git工作流
在团队开发中,Git工作流的规范性非常重要。我们团队总结了一套比较实用的流程:
Feature分支工作流
- 开始新功能开发
git checkout develop
git pull origin develop
git checkout -b feature/user-avatar-upload- 开发过程中定期同步
# 每天开始工作前
git checkout develop
git pull origin develop
git checkout feature/user-avatar-upload
git rebase develop # 保持分支基于最新的develop- 功能完成后
git add .
git commit -m "feat: 实现用户头像上传功能"
git push origin feature/user-avatar-upload
# 然后在GitLab/GitHub上创建Merge Request- 代码审查通过后合并
git checkout develop
git pull origin develop
git merge --no-ff feature/user-avatar-upload
git push origin develop
git branch -d feature/user-avatar-upload # 删除本地分支
git push origin --delete feature/user-avatar-upload # 删除远程分支紧急修复流程
当生产环境出现紧急bug时:
git checkout main
git pull origin main
git checkout -b hotfix/login-error-fix
# 修复bug
git add .
git commit -m "fix: 修复登录页面验证错误"
git push origin hotfix/login-error-fix
# 创建PR,审查通过后合并到main和develop常见问题和解决方案
忘记切换分支就开始开发了
这种情况经常发生,在main分支上开发了半天才发现:
# 暂存当前修改
git stash
# 创建新分支
git checkout -b feature/correct-branch
# 恢复修改
git stash pop推送时提示需要合并
# 错误信息:Updates were rejected because the remote contains work
git pull origin main
# 解决冲突后
git push origin main不小心删除了文件
# 恢复单个文件
git checkout HEAD -- filename
# 恢复所有文件到最后一次提交状态
git reset --hard HEAD提交到了错误的分支
# 撤销最后一次提交,但保留修改
git reset --soft HEAD~1
# 切换到正确分支
git checkout correct-branch
# 重新提交
git add .
git commit -m "正确的提交信息"性能优化和高级技巧
大文件处理
Git不适合存储大文件,但有时候无法避免:
# 使用Git LFS处理大文件
git lfs track "*.psd"
git lfs track "*.zip"
git add .gitattributes仓库清理
长期使用的仓库可能会变得很大:
# 清理不必要的文件
git gc --prune=now --aggressive
# 查看仓库大小
git count-objects -vH别名和脚本
我在~/.bashrc中定义了一些有用的函数:
# 快速切换到项目并拉取最新代码
function gcd() {
cd ~/projects/$1 && git pull origin $(git branch --show-current)
}
# 快速创建功能分支
function gnb() {
git checkout develop
git pull origin develop
git checkout -b feature/$1
}公众号:运维躬行录
个人博客:躬行笔记