跳转至

Git:日常使用与安全回退

Git 管理文件历史和分支;GitLab 则是在服务器上托管 Git 仓库,并增加成员权限、Merge Request 和 CI/CD。日常使用 Git 时,最重要的是弄清改动目前位于哪一层,以及它是否已经推送给别人。

工作目录              暂存区                  本地仓库                远程仓库
修改文件 ── git add ──→ 准备提交 ── git commit ──→ 本地历史 ── git push ──→ GitLab

四个位置与三个检查命令

位置 保存什么 常用命令
工作目录 正在编辑、尚未暂存的文件 git statusgit diff
暂存区(stage) 下一次 commit 准备包含的内容 git addgit diff --staged
本地仓库 已经 commit、可能尚未上传的历史 git loggit commit
远程仓库 GitLab 等服务器上的共享历史 git fetchgit pullgit push

不知道当前状态时先执行:

git status
git diff
git log --oneline --graph --decorate -10

git status 看文件和分支状态,git diff 看未暂存变化,git diff --staged 看下一次提交实际包含什么。提交前检查差异,可以避免把密码、日志或无关修改一起提交。

基础配置与克隆

首次使用设置提交身份:

git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main
git config --list

从 GitLab 获取仓库:

git clone git@gitlab.example.com:group/project.git
cd project
git remote -v
git status

SSH 适合长期协作。首次连接服务器时应核对主机指纹,不要为了省事关闭 SSH 主机密钥校验。使用 HTTPS 时建议使用 Personal Access Token,不要把账户密码或令牌直接写入命令和脚本。

将已有目录推到 GitLab

先在 GitLab 建立空项目,再在本地目录执行:

cd existing-project
git init
git branch -M main

# 先检查并完善 .gitignore
git status
git add .
git diff --staged
git commit -m "chore: initialize repository"

git remote add origin git@gitlab.example.com:group/project.git
git push -u origin main

.gitignore 应排除密钥、环境变量文件、日志、依赖缓存、构建产物和 IDE 临时文件。文件一旦进入提交历史,后来加入 .gitignore 不会自动把它从历史中移除。

常见规则示例:

.env
*.key
*.log
.DS_Store
.idea/
.vscode/
node_modules/
dist/
target/
__pycache__/
.venv/

日常开发流程

推荐每个需求或修复创建独立分支,不直接修改 main

git switch main
git pull --ff-only origin main
git switch -c feature/add-login

# 编辑后检查并提交
git status
git diff
git add src/login.py
git diff --staged
git commit -m "feat: add login validation"
git push -u origin feature/add-login

推送后在 GitLab 创建 Merge Request,等待测试和审核通过后再合入 main

为什么使用 git pull --ff-only

普通 git pull 等于先 fetch 再 merge,可能产生没有预期到的合并提交。--ff-only 只允许安全的快进;本地与远程发生分叉时会停止,让你先查看差异再决定 merge 或 rebase。

add 与 commit

暂存指定文件

git add docs/readme.md
git add src/app.py tests/test_app.py
git diff --staged
git commit -m "fix: handle empty request"

比起直接 git add .,指定文件更容易控制提交范围。

按修改块选择暂存

git add -p
git diff --staged
git commit -m "fix: correct SSH port"

当一个文件里混有两类改动时,git add -p 可以只选择本次提交需要的代码块。

提交信息

一次提交只完成一个清晰目标,常见格式:

feat: add user login
fix: correct Runner tag
docs: explain Git rollback
refactor: simplify configuration loader
test: add permission tests
chore: update ignored files

不要把大范围格式化、功能开发和故障修复混在一个提交里,否则审核、定位和回退都会变困难。

分支管理

git branch                       # 查看本地分支
git branch -a                    # 查看本地和远程分支
git switch main                 # 切换分支
git switch -c feature/xxx       # 创建并切换
git branch -d feature/xxx       # 删除已经合并的本地分支
git push origin --delete feature/xxx  # 删除远程分支

常用命名:

类型 示例 用途
功能 feature/add-login 新功能
修复 fix/ssh-timeout 普通问题修复
紧急修复 hotfix/payment-error 线上紧急故障
文档 docs/git-handbook 文档变更

fetch、pull 与 push

命令 做什么 是否改变当前文件
git fetch origin 下载远程分支和提交信息
git pull --ff-only 下载并将当前分支安全快进
git push 上传本地提交 不改变本地文件

想先查看远程发生了什么:

git fetch origin
git log --oneline HEAD..origin/main
git diff HEAD..origin/main

远程 push 被拒绝时,不要立即强推。先 fetch,确认远程是否有同事的新提交,再选择合并或变基。

merge 与 rebase

merge

merge 保留两条分支的发展过程,适合共享分支或 GitLab Merge Request:

git switch main
git pull --ff-only
git merge feature/add-login

团队项目通常通过 GitLab MR 完成合并,以保留审核和 CI 记录。

rebase

rebase 将当前分支的提交重新放到新基线上,历史更线性,但会改变提交 ID:

git switch feature/add-login
git fetch origin
git rebase origin/main

只对自己控制的功能分支执行 rebase。已被多人使用的共享分支不要改写历史。个人分支 rebase 后若必须更新远程:

git push --force-with-lease

使用 --force-with-lease,不要使用裸 --force;前者会在远程出现未知新提交时拒绝覆盖。

解决冲突

冲突表示 Git 无法自动判断两份修改如何组合。先确认当前正在 merge 还是 rebase:

git status

打开冲突文件,处理这些标记:

<<<<<<< HEAD
当前分支内容
=======
另一个分支内容
>>>>>>> other-branch

处理后继续:

git add path/to/conflicted-file

# merge 冲突
git commit

# rebase 冲突
git rebase --continue

如果发现合并方向不对,可以恢复到开始前:

git merge --abort
git rebase --abort

冲突标记处理完不代表逻辑正确,还要运行测试或至少检查相关配置。

回退:先判断是否已经推送

尚未提交

目的 命令 结果
取消暂存但保留修改 git restore --staged file 文件回到工作目录
丢弃某文件未提交修改 git restore file 文件恢复到最近提交,修改通常无法找回
查看未跟踪文件 git status 先确认目标,不要直接批量清理

已提交但尚未推送

修改最近一次提交说明或补文件:

git add forgotten-file
git commit --amend

撤销最近提交但保留修改:

git reset --soft HEAD~1

git reset --hard 会同时丢弃提交和工作目录改动,风险很高;执行前应确认精确目标和是否有需要保留的文件。

已经推送或合并

使用 revert 创建一个反向提交,不改写共享历史:

git log --oneline
git revert <commit-id>
git push

已经共享的提交不要通过 reset --hard 加强制推送来删除,否则同事的分支、MR 和 CI 记录都会混乱。

找回误删提交

git reflog
git switch -c rescue/recovered-work <commit-id>

reflog 记录本地 HEAD 的移动,可用于找回最近误删的提交,但它不是长期备份,应尽快创建救援分支。

临时保存工作

需要临时切换任务时:

git stash push -m "wip: investigate runner issue"
git switch main

# 返回原分支后
git switch feature/xxx
git stash list
git stash pop

stash 适合短期保存,不应替代正常提交。长期堆积的 stash 缺少清晰上下文,也无法方便地与同事共享。

cherry-pick

只把另一个分支中的某次提交带过来:

git cherry-pick <commit-id>

适合将一个独立且已经验证的修复补到发布分支。执行前确认该提交没有依赖其他未包含的改动。

标签与发布

使用附注标签标记正式版本:

git tag -a v1.4.0 -m "release v1.4.0"
git push origin v1.4.0
git tag -n

发布记录应关联 Git tag、提交 SHA、容器镜像完整标签、变更说明和部署时间。生产镜像优先使用不可变的 commit SHA 或版本标签,不要只记录 latest

查看历史与定位问题

git log --oneline --graph --all --decorate
git show <commit-id>
git diff <old-commit>..<new-commit>
git blame path/to/file

git blame 用于查找某行最后一次变更的提交入口,不应简单用于“找责任人”;应结合 commit、MR 和当时需求理解修改原因。

每天可照着做的流程

开始工作:main 拉取最新 → 创建 feature/fix 分支
开发过程:小步修改 → status/diff → 小步提交
团队协作:push 功能分支 → 创建 MR → CI 和审核
完成合并:更新本地 main → 删除已完成分支
正式发布:创建 tag → 构建不可变镜像 → 记录部署版本
发生错误:未共享再考虑 reset;已共享优先 revert

常见问题

现象 优先处理
push 提示远程有新提交 git fetch origin,检查差异后 merge 或 rebase,禁止直接强推
pull 后出现意外 merge commit main 分支改用 git pull --ff-only
提交时漏了文件 未推送时 git commit --amend
已推送的提交需要撤销 git revert <commit-id>
提交了密码或密钥 立即轮换凭据;只删除当前文件不够,还要处理历史与访问风险
分支落后 main 很久 git fetch origin && git rebase origin/main,处理冲突后重新测试
不知道自己改了什么 git statusgit diffgit diff --staged