1小时轻松掌握Git
还在为文件版本混乱而头疼?Git—— 这款分布式版本控制系统能帮你解决烦恼。本文从 Git 的核心概念讲起,类比 Word 文档版本管理的痛点,揭示 Git 如何通过工作区、暂存区和版本库精准追踪文件变化。涵盖安装指南(Linux-centos/ubuntu)、基本操作(仓库创建、配置、提交、回退)、分支管理(创建、切换、合并、冲突解决)及远程协作(克隆、推送、拉取)等核心内容,助你告别混乱的文件
Git
1. Git是什么?
Git 是一个分布式版本控制系统。 它的主要作用是跟踪和管理文件(尤其是代码文件)的变化。
通过一个比喻来理解
假设你在用 Word 写一篇很重要的论文:
-
初稿:你写完了第一部分,保存为
论文_v1.docx。 -
修改:你做了大量修改,但又怕改坏了,于是另存为
论文_v2.docx。 -
导师反馈:导师给了意见,你在新文件上修改,又存为
论文_导师修改版.docx。 -
自己再改:你自己又有新想法,于是又复制一份
论文_最终版.docx,然后继续改… -
混乱:很快,你的文件夹里全是
最终版_v2_真的最终了_最新.docx这样的文件,你根本分不清哪个是哪个,也不知道每个文件具体改了什么地方。
而 Git 就是为了解决这种混乱而生的!
使用 Git,你只需要一个项目文件夹。Git 会在背后为你记录每一次的修改(谁、在什么时候、为什么、修改了什么内容)。你可以随时回到任何一个历史版本,可以比较不同版本之间的差异,也可以创建不同的分支来尝试新功能而不影响主线。
2. 安装Git
2.1 Linux-centos
sudo yum -y install git
查看 Git 安装的版本
git --version
2.2 Linux-ubuntu
sudo apt-get -y install git
查看 Git 安装的版本
git --version
3. Git基本操作
3.1 创建 Git 本地仓库
本地仓库: 本质就是你的项目文件夹,但是这个文件夹被 Git 接管了,里面隐藏着一个 .git 目录,这里存储着 Git 的管理信息。
git init
3.2 配置 Git 仓库
当初始化 Git 后⾸先要做的事情是设置你的 ⽤⼾名称 和 e-mail 地址。
3.2.1 配置命令
git config [--global] user.name "Your Name"
git config [--global] user.email "email@example.com"
其中 --global 是⼀个可选项。如果使⽤了该选项,表⽰这台机器上所有的 Git 仓库都会使⽤这个配置。如果你希望在不同仓库中使⽤不同的 name 或 e-mail ,可以省略 --global 选项,但要注意的是,执⾏命令时必须要在仓库⾥。
3.2.2 查看配置命令
git config -l
3.2.3 删除对应的配置命令
git config [--global] --unset user.name
git config [--global] --unset user.email
3.3 认识工作区、暂存区、版本库
3.3.1 概念
-
工作区: 就是你当前初始化的 git 仓库(目录),你在这里新建、编辑、删除文件。所有你对代码的改动,首先都发生在这里。
- 注意: 工作区中的文件还没有被 git 管理。
-
暂存区: 英⽂叫 stage 或 index。⼀般存放在 .git ⽬录下的 index ⽂件(.git/index)中,我们把暂存区有时也叫作索引(index)。
- 为什么需要暂存区? 这是 Git 设计非常精妙的地方。它让你可以精确控制哪些修改需要被提交。比如你同时修改了
A.txt和B.txt,但这两个修改属于不同的功能,你可以只将A.txt的改动加入暂存区,先提交A.txt的功能,而B.txt的改动留待下次再提交。这保证了每次提交的原子性和清晰性。
- 为什么需要暂存区? 这是 Git 设计非常精妙的地方。它让你可以精确控制哪些修改需要被提交。比如你同时修改了
-
版本库: 就是隐藏在你项目目录下的
.git文件夹,这个版本库⾥⾯的所有⽂件都可以被 Git 管理起来,每个⽂件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以还原。

- 在创建 Git 版本库时,Git 会为我们⾃动创建⼀个唯⼀的 master 分⽀,以及指向 master 的⼀个指针叫
HEAD。
3.3.2 工作区 -> 暂存区
1️⃣:将 filename 文件的修改(新增、修改、删除)添加到暂存区。
git add filename
2️⃣:将工作区所有的修改(新增、修改、删除)都添加到暂存区。
git add .
3.3.3 暂存区 -> 版本库
git commit -m "Submit Description information"
3.4 查看提交历史
当你使用 git commit 将更改保存到版本库后,每次提交都会形成一条历史记录。git log 就是用来展示这个历史记录的列表,它按照时间从近到远排列(最新的提交在最上面)。
git log 后分解一下没部分的信息:
-
commit id:提交的唯一标识符 (git reset时需要指定版本的操作中会用到此ID)。
-
Author:显示这次提交的作者姓名和邮箱。这来自于你安装 Git 时设置的
user.name和user.email。 -
Date:提交的时间。
-
git commit 时的描述信息。
1️⃣ :正常打印
git log
2️⃣ :简化输出
git log --oneline
3️⃣:图形化显示分支和合并
git log --oneline --graph
4️⃣:不仅显示提交信息,还显示每次提交所引入的具体代码差异
git log -p
5️⃣:只显示最近 n 条提交
git log -n
6️⃣:显示指定时间之后/之前的提交
git log --since = "yyyy-mm-dd"
git log --until = "yyyy-mm-dd"
3.5 显示工作区和暂存区的当前状态
在你执行 git add 或 git commit 之前,你应该先运行 git status 来确认一下当前的情况,避免误操作。
git status
3.6 版本回退
3.6.1 git reset
git reset [--soft | --mixed | --hard] [HEAD]
-
--mixed为默认选项,使⽤时可以不⽤带该参数。重置暂存区,但不触碰工作区。你之前的修改会保留在工作目录,但变回了未add的状态。 -
--soft不触碰工作区和暂存区。你之前的修改仍然处于add后的状态 。 -
--hard工作区和暂存区都彻底回退。 -
HEAD 说明:
-
可直接写成 commit id,表⽰指定退回的版本
-
HEAD 表⽰当前版本
-
HEAD^ 上⼀个版本
-
HEAD^^ 上上⼀个版本
-
以此类推…
-
可以使⽤ 〜数字表⽰:
-
HEAD~0 表⽰当前版本
-
HEAD~1 上⼀个版本
-
HEAD~2 上上⼀个版本
-
以此类推…
-
| 工作区 | 暂存区 | 版本库 | 对应的操作 | |
|---|---|---|---|---|
| 当前状态 | hello git hello world |
hello git hello world |
hello git hello world |
|
| –soft | hello git hello world |
hello git hello world |
hello git | git commit |
| –mixed | hello git hello world |
hello git | hello git | git add git commit |
| –hard | hello git | hello git | hello git | 连工作区的内容也要重新开始写 |
测试回退功能,我们先做⼀些准备⼯作:更新3个版本的 ReadMe,并分别进⾏3次提交,如下所⽰:
# 第⼀次修改提交
ccc@ccc:~/gitcode$ cat ReadMe
hello version1
hyb@139-159-150-152:~/gitcode$ git add ReadMe
hyb@139-159-150-152:~/gitcode$ git commit -m "add version1"
[master cff9d1e] add version1
1 file changed, 1 insertion(+)
# 第⼆次修改提交
ccc@ccc:~/gitcode$ cat ReadMe
hello version1
hello version2
hyb@139-159-150-152:~/gitcode$ git add ReadMe
hyb@139-159-150-152:~/gitcode$ git commit -m "add version2"
1 file changed, 1 insertion(+)
# 第三次修改提交
ccc@ccc:~/gitcode$ cat ReadMe
hello version1
hello version2
hello version3
hyb@139-159-150-152:~/gitcode$ git add ReadMe
hyb@139-159-150-152:~/gitcode$ git commit -m "add version3"
[master d95c13f] add version3
1 file changed, 1 insertion(+)
# 查看历史提交记录
hyb@139-159-150-152:~/gitcode$ git log --pretty=oneline
d95c13ffc878a55a25a3d04e22abfc7d2e3e1383 (HEAD -> master) add version3
14c12c32464d6ead7159f5c24e786ce450c899dd add version2
cff9d1e019333318156f8c7d356a78c9e49a6e7b add version1
现在,如果我们在提交完 version3 后, 发现 version 3 编写错误,想回退到 version2,重新基于 version 2 开编写。由于我们在这⾥希望的是将⼯作区的内容也回退到 version 2 版本,所以需要⽤到 --hard 参数,⽰例如下:
ccc@ccc:~/gitcode$ git log --pretty=oneline
d95c13ffc878a55a25a3d04e22abfc7d2e3e1383 (HEAD -> master) add version3
14c12c32464d6ead7159f5c24e786ce450c899dd add version2
cff9d1e019333318156f8c7d356a78c9e49a6e7b add version1
...
ccc@ccc:~/gitcode$ git reset --hard 14c12c32464d6ead7159f5c24e786ce4
HEAD is now at 14c12c3 add version2
ccc@ccc:~/gitcode$ cat ReadMe
hello version1
hello version2
3.6.2 git reflog
但如果我现在后悔了,再想回到 version3 怎么办?别慌,Git 几乎记录了你的所有操作。使用 git reflog 命令,你会看到所有 HEAD 移动的记录。
ccc@ccc:~/gitcode$ git reflog
14c12c3 (HEAD -> master) HEAD@{0}: reset: moving to 14c12c32464d6ead7159f5c24e78
d95c13f HEAD@{1}: commit: add version3
14c12c3 (HEAD -> master) HEAD@{2}: commit: add version2
cff9d1e HEAD@{3}: commit: add version1
# 回退到v3
ccc@ccc:~/gitcode$ git reset --hard d95c13f
HEAD is now at d95c13f add version3
# 查看⼯作区
ccc@ccc:~/gitcode$ cat ReadMe
hello version1
hello version2
hello version3
3.7 撤销修改
3.7.1 情况一:对于工作区的代码,还没有add
git checkout -- filename
3.7.2 情况二:已经add,但是没有commit
1️⃣
git reset HEAD filename
git checkout -- filename
2️⃣
git reset --hard HEAD
3.7.3 情况三:已经add,并且也commit
注意:前提是没有 git push。
git reset --hard HEAD^
| 工作区 | 暂存区 | 版本库 | 解决方式 |
|---|---|---|---|
| xxx code | git checkout – filename | ||
| xxx code | xxx code | git reset --hard HEAD | |
| xxx code | xxx code | xxx code | git rese --hard HEAD^ |
解决后:
| 工作区 | 暂存区 | 版本库 |
|---|---|---|
核心规则:
-
HEAD是一个指针,指向你当前所在的提交。 -
当你
commit后,HEAD会带着分支指针一起移动到新的提交。所以你想回退,就需要用HEAD^、HEAD~n来指向之前的提交。 -
当你没有
commit时,HEAD纹丝不动,依然指向上一次提交。所以你的所有撤销操作都是对照HEAD来进行。
简单的口诀:只要 git commit 了 就一定要 HEAD^ 如果没有 commit 就是 HEAD。
4. 分支管理
4.1 理解HEAD
本质是一个指针,可以指向其他分支,被指向的分支就是当前正在工作的分支所在的提交。
可以把 HEAD 想象成游戏中的存档点。附加状态就像你在主线任务上不断创建新存档;创建分支就像你从当前的游戏的存档点去探索一些新的内容,但是此时你的主线任务还是旧的,所以需要将你在新分支探索的新内容添加到主线任务中。
.git
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── fsmonitor-watchman.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── pre-merge-commit.sample
│ ├── prepare-commit-msg.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ ├── pre-receive.sample
│ └── update.sample
├── index # 暂存区
├── info
│ └── exclude
├── logs
│ ├── HEAD
│ └── refs
│ └── heads
│ └── master
│ #(每一次对文件内容的新修改,在通过 git add 添加到暂存区后,Git 都会为其生成一个数据对象(blob object)。而执行 git commit时, │Git 会生成一个提交对象(commit object)来封装这次提交。
├── objects
│ ├── 03
│ │ └── 850187d483fa3a8828f695ea2d1b9d7f9f22d3
│ ├── 05
│ │ ├── 7e450bd946bb6f72576b9beacc3538f5ac5a4f
│ ├── info
│ └── pack
├── ORIG_HEAD
└── refs
├── heads
│ └── master
└── tags
# 通过 cat 查看 HEAD
cat .git/HEAD
ref: refs/heads/master
4.2 查看分支
查看本地分支:
git branch
查看本地和远程分支:
git branch -a
4.3 创建分支
git branch branch-name

4.4 切换分支
git checkout branch-name

在 dev 分支上进行 commit 操作

4.5 合并分支
前提: 需要切换回 master 分支。
git checkout master
git merge dev #dev分支中的内容合并到master分支上

当前属于快速合并模式 ,直接切换,没有遇到合并冲突。
4.6 删除分支
git branch -d branch-name
注意:你不能在
dev分支上删除它自己,必须先切换到其他分支才能执行删除dev分支的操作。
4.7 创建并切换分支
git checkout -b brance-name
4.8 合并冲突
手动合并冲突时,可以选择保留dev分支的内容,也可以选择保留master分支的内容,或者都保留。
手动合并冲突后,需要 git add 和 git commit。

4.9 禁用快速合并模式
将 dev 分支的更改合并到当前分支,并且强制创建一个新的提交记录来明确标记这次合并行为
git merge --no-ff -m "merge dev" dev
- fast-forward (快进合并):如果主分支(例如
master)自新分支(dev)创建以来没有新的提交,Git 默认会简单地移动指针来完成合并,不会创建新的提交。这使得历史记录呈一条直线,但丢失了曾经存在过一个分支并进行合并的信息。
假设你的提交历史一开始是这样的:
A---B---C dev
/
...---D---E master (HEAD)
如果不加 --no-ff (默认快速合并):
如果 master 分支在创建 dev 后没有新提交,合并后历史会变成一条直线:
...---D---E---A---B---C master (HEAD), dev
使用 --no-ff 后:
A---B---C dev
/ \
...---D---E---------M master (HEAD)
这个 M 提交就是 -m "merge dev" 所创建的那个合并提交。
4.10 一种特殊情况修改bug分支
假如我们现在正在 dev 分⽀上进⾏开发,开发到⼀半,突然发现 master 分⽀上⾯有 bug,需要解决。在Git中,每个 bug 都可以通过⼀个新的临时分⽀来修复,修复后,合并分⽀,然后将临时分⽀删除。可现在 dev2 的代码在⼯作区中开发了⼀半,还⽆法提交,怎么办?
ccc@ccc:~/gitcode$ git branch # 查看分支情况
* dev
master
ccc@ccc:~/gitcode$ cat ReadMe # 查看 ReadMe 文件
hello world
i am coing ... # 在 dev 分支上对 ReadMe 文件新增的内容
...
ccc@ccc:~/gitcode$ git checkout master # dev 分支切换到 master 分支
ccc@ccc:~/gitcode$ git branch # 查看分支情况
dev
* master
ccc@ccc:~/gitcode$ cat ReadMe # 查看 ReadMe 文件
hello world
i am coding ... # 这里发现由于 dev 分支开发新内容没有提交,导致 master分支中的 ReadMe 文件也存在 i am coding ...
...
# 我们的期望是 master 分支没有在 dev 分支的工作区新增的但是没有提交的内容
# 可以通过 git stash 将当前 dev 分支中工作区的内容进行存储,被存储的内容额可以在将来某个时间恢复出来
ccc@ccc:~/gitcode$ git checkout dev # master 分支切换到 dev 分支
ccc@ccc:~/gitcode$ git branch # 查看分支情况
* dev
master
ccc@ccc:~/gitcode$ git stash # 将 dev 分支中工作区的内容进行存储
ccc@ccc:~/gitcode$ cat ReadMe # 查看 ReadMe 文件
hello world
...
ccc@ccc:~/gitcode$ git checkout master # dev 分支切换到 master 分支
ccc@ccc:~/gitcode$ git branch # 查看分支情况
dev
* master
ccc@ccc:~/gitcode$ cat ReadMe # 查看 ReadMe 文件
hello world
...
# 现在就可以放心的新建 fix_bug 分支来处理 master 的 bug
ccc@ccc:~/gitcode$ git checkout -b fix_bug # 新建 fix_bug 分支并切换
ccc@ccc:~/gitcode$ git branch # 查看分支情况
dev
master
* fix_bug
# 比如将 ReadMe 文件中的 hello world 改为 hello world world
ccc@ccc:~/gitcode$ cat ReadMe # 查看 ReadMe 文件
hello world world
...
ccc@ccc:~/gitcode$ git add ./ReadMe
ccc@ccc:~/gitcode$ git commit -m "fix master bug"
ccc@ccc:~/gitcode$ git checkout master # 将 fix_bug 分支切换回 master 分支
ccc@ccc:~/gitcode$ git branch # 查看分支情况
dev
* master
fix_bug
ccc@ccc:~/gitcode$ git merge --no-ff -m "fix bug" fix_bug # 使用非快速合并模式合并 fix_bug 分支
ccc@ccc:~/gitcode$ cat ReadMe # 查看 ReadMe 文件
hello world world
...
ccc@ccc:~/gitcode$ git branch -d fix_bug #删除 fix_bug 分支
ccc@ccc:~/gitcode$ git branch # 查看分支情况
dev
* master
ccc@ccc:~/gitcode$ git checkout dev # 将 master 分支切换到 dev 分支继续开发没有提交的内容
ccc@ccc:~/gitcode$ git stash pop # 恢复之前 dev 存储工作区的内容
# 你可以多次stash,恢复的时候,先⽤ git stash list 查看,然后恢复指定的stash,⽤命令 git stash apply stash@{0}
ccc@ccc:~/gitcode$ cat ReadMe # 查看 ReadMe 文件
hello world
i am coding
...
ccc@ccc:~/gitcode$ git add ./ReadMe
ccc@ccc:~/gitcode$ git commit -m "i am coding done!"
# 但是此时,修复 bug 的内容,并没有在 dev 分支上显示
# 此时,我们可以切换回 master 分支中合并 dev 分支,但是当复杂性高的时候,难免会出错。
# 所以,建议是将 master的内容合并到 dev 分支上,等测试/调试完毕后再将 dev 分支的内容合并回 master 分支
ccc@ccc:~/gitcode$ git merge --no-ff -m "master merge dev - test" master # master 分支合并到 dev 分支上
# 处理冲突
ccc@ccc:~/gitcode$ git checkout master # dev 切换到 master 分支,已经在 dev 中处理完毕冲突,直接将 dev 与 master 合并
ccc@ccc:~/gitcode$ git merge --no-ff -m "dev merge master - test end" dev # dev 合并至 master
ccc@ccc:~/gitcode$ git branch -d dev # 删除 dev 分支
ccc@ccc:~/gitcode$ git branch # 查看当前分支情况
* master
4.11 强制删除分支
git branch -D branch-name
5. 远程操作
5.1 远程仓库
你可以把远程仓库想象成一个部署在中央服务器上的项目中心库(比如 GitHub, Gitee)。而你自己电脑上的 Git 仓库,则是这个中心库的一个完整副本。
为什么要使用远程仓库?
-
协作:这是最主要的目的。它让多个开发者可以共享代码,并在同一个项目上并行工作。
-
备份:将代码推送到远程仓库,相当于为你的项目做了一个可靠的云端备份,防止本地电脑损坏导致代码丢失。
5.2 克隆远程仓库
git clone url
5.3 向远程仓库推送
向远程仓库推送的本质就是:用你本地仓库的最新代码,去更新远程仓库中相对较旧的代码。
git push <远程主机名> <本地分支名>:<远程分支名>
git push origin master:master
# 如果当前本地分支名和远程分支名一致 可以省略
git push origin master
5.4 拉取远程仓库
拉取远程仓库的本质是:用远程仓库的最新代码,来更新你本地相对较旧的代码。
git pull <远程主机名> <远程分⽀名>:<本地分⽀名>
#如果当前本地分支名和远程分支名一致 可以省略
git pull <远程主机名> <远程分⽀名>
git pull origin master
注意:git pull 是拉取 + 合并。
5.5 .gitignore
在⽇常开发中,我们有些⽂件不想或者不应该提交到远端。在 Git ⼯作区的根⽬录下创建⼀个特殊的 .gitignore ⽂件,然后把要忽略的⽂件名填进去,Git 就会⾃动忽略这些⽂件了。
# 可以直接写文件名或者文件后缀
*.so
*.ini
# 排除所有的.so文件,但是b.so可以提交
!b.so
强制被忽略的文件进行 add 操作。
git add -f a.so
5.6 创建本地分支并切换,同时与远程分支建立连接
git checkout -b dev origin/dev
-
已建立连接后的日常推送:
git push -
已建立连接后的日常拉取:
git pull -
查看已建立的连接:
git branch -vv
5.7 创建本地分支后并切换,之后与远程分支建立连接
git checkout -b dev
git branch --set-upstream-to=origin/dev dev
5.8 远程分支删除后,本地 git branch -a 依然能看到远程被删除的分支的解决办法
git remote prune origin
# 再查看就是正常的了
git branch -a
更多推荐



所有评论(0)