最简单明了的Git教程
Git的安装
Linux 和 Windows 系统的安装大家可以到 Git 官网上查看安装方法,上面讲的非常详细。
https://git-scm.com/downloads
安装成功后,再使用 git --version 就可以查看版本号了。
Git有三种状态
已修改(modified):表示修改了文件,但还没保存到数据库中。
已暂存(staged):表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
已提交(commit):表示数据已经安全的保存在本地数据库中。
Git的四个工作区域
工作区域(Workspace):本地磁盘上你能直接看到和修改的项目目录。
暂存区域(Index/Stage):git add 之后文件的快照会被暂存在 .git/index 里。
本地仓库(Repository):git commit 之后,暂存区的内容被固化成一条 commit,记录到本地的 .git 目录。
远程仓库(Remote Repository):git push 把本地 commit 同步过去。
工作流程
最基础的工作流程是,首先执行git pull获取远程仓库的最新代码,进行代码的编写。
完成相应功能的开发后执行 git add . /git filename将工作区代码的修改添加到暂存区,再执行 git commit -m 完成xx功能 将暂存区代码提交到本地仓库并添加相应的注释,最后执行git push命令推送到远程仓库。
撤回git commit操作
当执行了执行git commit -m 完成xx功能命令想要撤回,可以使用git reset --soft HEAD^把本地仓库回退到当前版本的上一个版本,也就是暂存区的文件还没有提交的时候,代码的改动会保留在暂存区和工作区。
还可以使用git reset --mixed HEAD^ ,这样不止回退了刚刚的git commit操作,还回退了刚刚的git add操作也就是说代码的改动只会留在工作区,并且--mixed参数是git reset命令的默认选项,所以也可以写为git reset HEAD^。还有一个参数--hard:重置HEAD到指定的提交,暂存区和工作目录都重置。HEAD 是一个特殊的指针,正常情况下指向"当前分支的最新 commit"。
撤回git push操作
当执行了 git push 命令想要撤回,可以使用 git reset HEAD^将本地仓库回退到当前版本的上一个版本,代码的修改会保留在工作区,然后使用 git push origin xxx --force 将本地仓库当前版本的代码强制推送到代码库。origin是远程仓库的名字,--force是强制的参数。
git Rebase和Merge的区别
Rebase:将一个分支上的提交逐步的应用到另一个分支上面,使得提交历史变得更加线性。简而言之,rebase可以将提交按照时间顺序线性排列。(把当前分支的提交重新接到目标分支后面,使历史变成线性。)
Merge:将指定分支的更改合并到当前分支,将两个分支的代码提交历史合并为一个新的提交。在执行Merge时,Git会创建一个新的提交(commit),将两份分支的代码提交历史连接在一起。这样,两个分支的修改都会包含在一个合并提交中。合并后的历史会保留每个分支的提交记录。(直接合并两个分支,保留原始分叉历史。)
如何使用 git 命令合并两个分支,发生冲突如何解决(多个分支对同一部分代码进行了不同修改,会产生冲突,Git不知道到底要保留哪个。)
1.查看冲突:当发生冲突时,Git 会提示您文件中的冲突部分。使用git status查询所有冲突文件的状态。
2.解决冲突:打开冲突的文件,手动修改成你最终想保留的代码。
3.标记为已解决:修改完成后,执行git add.将已解决的文件添加到暂存区域。
4.继续提交这些已解决的文件,用git commit命令。
git pull 和 git fetch 有什么区别?
git fecth:把远程仓库的最新commit拉到本地的远程跟踪分支,不会改动当前工作区和本地分支,相当于只下载。拉下来后你可以先 git log origin/main 看看远程都改了什么,然后再决定要不要合并。
git pull:等价于git fecth+git merge,先拉取远程更新,然后立刻合并到当前分支。
什么是 HEAD?什么是游离 HEAD(detached HEAD)?
HEAD:是一个指针,正常情况下指向“当前分支下的最新commit”,更准确的来说它会指向一个分支引用,这个分支会指向最新的commit。
游离HEAD:当你执行git checkout
git reset 的 --soft、--mixed、--hard 三种模式有什么区别?
--soft:相当于“只撤销commit,保留add,修改还保留在暂存区域”,可以直接重新commit。
--mixed:相当于“撤销commit,撤销add”,修改还在工作区,可以add到暂存区,再commit。
--hard:相当于撤销一切,工作区的修改也会被撤销。
git reset 和 git revert 的区别是什么?
git reset
git revert
git reset 是回退提交并修改历史;git revert 是新增一个反向提交来撤销修改,不会改变原有提交历史。一般本地未 push 的提交用 reset,已经 push 到远程的提交用 revert。共享分支永远优先用 revert。
git commit --amend 是做什么的?什么时候用?
需要注意的是,amend 实际上是生成了一个新的 commit(hash 会变),替换掉原来的那个。所以如果原 commit 已经 push 到远程并被别人拉过,不要 amend,否则会让别人的本地历史对不上。
最常见的两种用法如下:
1.修改提交说明:刚刚提交git commit -m "fix bug",发现commit message写错了,会把最近一次的commit说明改掉,git commit --amend -m "fix login bug"。
2.补充漏提交的文件:git commit -m "完成登录功能",发现少提交了一个文件,直接git add.,然后git commit --amend。
git checkout、git switch、git restore 有什么关系?
在 Git 2.23 之前,git checkout 一个命令同时承担了两个完全不同的职责:切换分支和恢复文件。后来引入了两个新命令git switch、git restore把职责拆开了。
git switch:专门用于切换分支。
比如git switch main:切换到main分支。
git switch -c feat/xxx:创建并切换到新分支。
git restore:专门用于恢复文件。
git restore
git checkout 仍然可用,但现代项目推荐按职责用 switch 和 restore,语义更清晰。
git stash 是做什么用的?常用命令有哪些?
git stash 临时保存当前工作区的修改,你可以切换到其他的分支或做别的事情,等会再回来继续。
典型场景:你在feat/a分支改了一半的代码还没有commit,这时main分支有个bug需要紧急修复,你可以git stash把当前修改暂存起来,git switch main切换到main分支修复bug后,再git stash pop把之前的改动取回来继续干。
常用命令:
git stash:暂存当前修改到stash栈。
git stash pop:从stash栈中取出最近一次暂存的修改继续干。(取出后从 stash 栈删除)
git stash list:查看所有 stash。
git stash apply:应用某个 stash 但不删除它(可以反复 apply)
git stash drop:丢弃某个 stash
git stash clear:清空所有 stash
git pull 和 git pull --rebase 的区别是什么?
git pull = fetch + merge,如果你本地和远程都有新 commit,会产生一个额外的 merge commit,让提交历史出现分叉。
git pull --rebase = fetch + rebase,会把你本地的 commit "搬"到远程分支最新 commit 的后面,保持线性历史,看起来更干净。
注意:rebase 会改写本地 commit 的 hash,所以不要对已经 push 到远程、被别人拉过的 commit 做 rebase,否则会给协作者带来巨大的历史同步问题。
什么是 Fast-Forward 合并?和普通 merge 有什么区别?
Fast-Forward(快进)合并:也就是目标分支 main 从 feat/a 分出去之后没有新的 commit,Git 只需要把 main 的指针直接往前挪到 feat/a 的位置即可,不会产生新的 merge commit,提交历史保持线性。
Non-Fast-Forward 合并:两个分支有分叉(各自都有新 commit)时,Git 会创建一个新的"merge commit"把两条历史连起来。
git cherry-pick 是做什么的?什么场景用?
git cherry-pick的作用是将某个commit的改动应用到当前分支。会在当前分支生成一个内容相同的新 commit(hash 不一样)。
典型场景:
目标分支main上有一个bug,但是你已经在feat/a分支上已经修好了,只需要git switch main,然后git
cherry-pick
如何删除本地分支和远程分支?
删除本地分支:
git branch -d feat/xxx:安全删除,只有已经合并过的分支才允许删。
git branch -D feat/xxx:强制删除(大写D),未合并的分支也能删除。
删除远程分支:
git push origin --delete feat/xxx(推荐写法)
清理已经在远程被删除但本地还缓存的跟踪分支:
git fetch --prune 或 git remote prune origin
prune的英文意思是裁剪。
常见的 Git 工作流有哪些?
Git Flow:有 master主分支、develop开发分支、feature/功能分支、release/预发布分支、hotfix/* 多种长期和临时分支,规则严谨,适合版本化发布的产品(比如客户端 App、大型企业产品)。缺点是流程太重,对快速迭代不友好。
GitHub Flow:只有 main 一个长期分支 + 多个短生命周期的 feature 分支,全部通过 Pull Request 合并。简单、灵活,适合持续部署的 Web 应用。流程是创建feature分支、开发、Pull Request、merge main。
Fork、Clone、Pull Request 在 GitHub 协作中分别是什么?
这是开源协作的标准流程,三个概念一定要分清:
Fork:在 GitHub 上把别人的仓库复制一份到自己的账号下,是服务器端操作。Fork 出的仓库和原仓库保持关联(方便发起pull request),但你可以自由往自己 fork 的仓库推代码,不影响原作者。
Clone:把一个远程仓库下载到本地,是本地操作。只有 clone 下来的代码你才能在本地编辑、测试、编译。
Pull Request(PR):在自己 fork 的仓库改完代码推上去后,发起一个"请求",请求原仓库的维护者把你的改动合并进来。原作者可以 review 代码、讨论、最终 merge 或者拒绝。
标准开源贡献流程:
Fork(在 GitHub 上)
↓
Clone(到本地)
↓
改代码 + commit
↓
Push 到自己的 Fork
↓
发 Pull Request
↓
维护者 Review & Merge
git log 常用参数有哪些?
git log:显示当前分支的版本历史
git log --oneline:一行显示一个 commit,简洁直观
git log --graph:图形化显示分支 / 合并关系
git log -p [file]:显示指定文件相关的每一次diff
git log --stat:显示每个 commit 修改了哪些文件、增删行数;也可以说是查看文件修改统计。
git log --
git log --author="name":按作者过滤;查看某人提交的commit
git log --since="1 week ago" --until="yesterday":按时间过滤
stat的意思是统计数字,统计信息。
日常最常用的一个组合:
git log --oneline --graph --decorate --all
| 参数 | 含义 |
|---|---|
| --oneline | 简洁显示 |
| --graph | 显示分支图 |
| --decorate | 显示 HEAD、tag、branch |
| --all | 显示所有分支 |
git tag有什么用?轻量标签和附注标签有什么区别?
git tag 用于给某个 commit 打一个固定的名字(通常是版本号,比如 v1.0.0),方便以后找到这个"关键节点",比如发布版本、里程碑。这样能快速定位“当时发布版本对应的 commit”
轻量标签(lightweight tag):git tag v1.0,只是一个指向 commit 的指针(只是一个 commit 别名,类似v1.0 -> commit abc123),没有任何额外信息(作者时间描述等)。
附注标签(annotated tag):git tag -a v1.0 -m "release 1.0",会作为独立的对象存储在 Git 里,包含打标签者、日期、说明、甚至 GPG 签名。
发布版本时应该用附注标签,因为它带有完整的元数据,可追溯性更好;轻量标签更像是一次性的临时书签。
推送标签到远程:
git push origin v1.0:推送单个
git push origin --tags:一次推送所有本地标签
删除标签:
本地:git tag -d v1.0
远程:git push origin --delete v1.0
注意:
tag 永远固定指向:创建时那个 commit,不会变。
git blame 和 git bisect 分别是做什么的?
这两个命令都是排查"这个问题是谁引入的"的利器,但思路完全不同:
git blame
git bisect:二分查找定位引入 bug 的 commit。当你知道某个 commit 是好的、某个 commit 是坏的,但不知道中间是哪个 commit 引入了 bug 时,git bisect 可以帮你自动二分:
git bisect start # 告诉git我要开始排查了
git bisect bad # 标记当前 HEAD 是坏的
git bisect good <old-commit> # 标记一个早期好的 commit
# Git 自动跳到中间(checkout)某个 commit,你测试一下,看看 bug 有没有
git bisect good # 或 git bisect bad
# ... 几轮之后 Git 会告诉你"就是这个 commit 引入了 bug"
git bisect reset # 结束排查,回到原来的 HEAD
500 个 commit 之间出现 bug,普通的查找一个个试;而 bisect:log₂(500) ≈ 9,9 次左右就能找到。
git blame 用于查看文件每一行最后一次是谁修改的;git bisect 用于通过二分查找快速定位引入 bug 的 commit,适合在大量 commit 中高效排查问题。
.gitignore 里加了文件为什么不生效?
.gitignore 是Git的一个配置文件,告诉 Git:哪些文件不要纳入版本控制。
典型的场景:application.yml
很多项目:数据库密码,不想提交。但:第一次已经 commit 了,于是:.gitignore 不生效,这是最常见的坑。
这是一个非常高频的坑。核心原因是:.gitignore 只对"还没被 Git 追踪的文件"生效。
如果一个文件已经被 git add + commit 过了,之后再把它加到 .gitignore 是没用的——Git 依然会继续追踪它的改动,因为它已经在版本控制中了。
例如:
git add application.yml
git commit
这时候:Git 已经开始追踪 application.yml
后面如果
application.yml加入.gitignore
Git 会认为:
“这个文件我早就认识了”,所以继续跟踪,.gitignore 不会生效。
解决方法:
git rm --cached application.yml #注意:只取消追踪,不会删本地文件。从暂存区域移除追踪,不删除本地文件。-rm是remove的意思移除,--cached是代表暂存区域
然后:
git commit -m "stop tracking application.yml" #-m是message说明
之后再将application.yml加入.gitignore就会生效了。

浙公网安备 33010602011771号