最简单明了的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 checkout时,HEAD会指向一个commit而不是分支,这时你做的新 commit 不属于任何分支,一旦 git switch 切走,这些 commit 就可能被垃圾回收而丢失。

git reset 的 --soft、--mixed、--hard 三种模式有什么区别?

--soft:相当于“只撤销commit,保留add,修改还保留在暂存区域”,可以直接重新commit。
--mixed:相当于“撤销commit,撤销add”,修改还在工作区,可以add到暂存区,再commit。
--hard:相当于撤销一切,工作区的修改也会被撤销。

git reset 和 git revert 的区别是什么?

git reset:把HEAD指针往回移动到指定的commit,后面的commit会“消失”,本质就是改写历史。不适合已经push到远程仓库的commit,否则自己的本地历史和别人的会冲突。
git revert:生成一条新的commit,这条commit正好是指定commit的反向。也就是生成一个指定commit的撤销commit,原来的 commit 依然保留在历史中,不会改写历史。是安全的,适合在共享分支上回滚已经发布的改动。
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 :撤销工作区修改(恢复到暂存区或 HEAD 的版本)。
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 就可以啦,现在需要把这个修复单独搬到 main 上,而不是把整个 feat/a 都合并过去。

如何删除本地分支和远程分支?

删除本地分支:
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 :逐行显示一个文件每一行最后是谁、在哪个 commit、什么时候修改的。用于定位一行具体代码的"作者"和"出处"。
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就会生效了。

posted @ 2026-05-29 21:01  吟月229  阅读(30)  评论(0)    收藏  举报