运维知识
悠悠
2026年1月5日

从零开始掌握Git:我用了三年总结的实战干货,让你少走弯路

说起Git,估计很多人都有过这样的经历:刚开始接触的时候觉得简单,不就是几个命令嘛,push、pull、commit,有什么难的?结果真正用起来发现处处是坑,代码冲突、分支混乱、历史记录乱七八糟...

我记得刚工作那会儿,因为对Git理解不够深入,曾经把整个项目的提交历史搞得一团糟,最后只能重新建仓库。那次之后我就下定决心要把Git彻底搞明白。

今天就把我这几年使用Git的经验和踩过的坑全部分享出来,相信看完之后你对Git的认识会有一个质的提升。

先搞清楚Git到底是个啥

很多人一上来就开始学命令,但其实理解Git的工作原理更重要。Git本质上就是一个分布式版本控制系统,它会追踪文件的每一次变化,并且可以让多个人协同工作。

想象一下你在写一份重要的文档,每次修改前你都会复制一份备份,比如"方案v1.doc"、"方案v2.doc"、"方案最终版.doc"、"方案真的最终版.doc"... 这种做法很熟悉吧?Git做的事情本质上就是这样,只不过它更智能,能够追踪每个字符的变化。

image-20260105220140362

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: 构建工具、依赖更新等

分支管理,这是重点

image-20260105220510032

分支是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

image-20260105220535643

我现在基本上都用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分支。

分支合并

合并分支有几种方式,每种都有自己的使用场景:

  1. Fast-forward合并(默认)
git checkout main
git merge feature/user-login
  1. No-fast-forward合并(保留分支历史)
git merge --no-ff feature/user-login
  1. 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:别人的修改

解决冲突

解决冲突的步骤:

  1. 找到所有冲突文件:git status
  2. 打开冲突文件,手动编辑
  3. 删除冲突标记(<<<<<<<, =======, >>>>>>>
  4. 保留你想要的内容
  5. 添加到暂存区:git add filename
  6. 提交: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提供了几种方式:

  1. 回退到上一个提交
git reset --hard HEAD~1
  1. 回退到指定提交
git reset --hard commit-hash
  1. 只回退代码,保留提交历史
git revert commit-hash

resetrevert的区别很重要:

  • 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分支工作流

  1. 开始新功能开发
git checkout develop
git pull origin develop
git checkout -b feature/user-avatar-upload
  1. 开发过程中定期同步
# 每天开始工作前
git checkout develop
git pull origin develop
git checkout feature/user-avatar-upload
git rebase develop  # 保持分支基于最新的develop
  1. 功能完成后
git add .
git commit -m "feat: 实现用户头像上传功能"
git push origin feature/user-avatar-upload
# 然后在GitLab/GitHub上创建Merge Request
  1. 代码审查通过后合并
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
}

公众号:运维躬行录

个人博客:躬行笔记

文章目录

博主介绍

热爱技术的云计算运维工程师,Python全栈工程师,分享开发经验与生活感悟。
欢迎关注我的微信公众号@运维躬行录,领取海量学习资料

微信二维码