图解工作区、版本库中的暂存区和版本库之间的关系
安装git
yum install -y git
git的身份配置
[root@git ~]# git config --global user.name 'zqf'
[root@git ~]# git config --global user.email '826849158@qq.com'
[root@git ~]# git config --list
user.name=zqf
user.email=826849158@qq.com
上述的配置实际上是写到了这里:
[root@git ~]# cat .gitconfig
[user]
name = zqf
email = 826849158@qq.com
git常用的操作
初始化一个git仓库
[root@git ~]# mkdir git_test
[root@git ~]# cd git_test
[root@git git_test]# git init #初始化一个git仓库
初始化空的 Git 版本库于 /root/git_test/.git/
[root@git git_test]# ll -a
总用量 0
drwxr-xr-x 3 root root 18 4月 7 08:39 .
dr-xr-x---. 4 root root 158 4月 7 08:39 ..
drwxr-xr-x 7 root root 119 4月 7 08:39 .git
[root@git git_test]# cd .git/
[root@git .git]# ll
总用量 12
drwxr-xr-x 2 root root 6 4月 7 08:39 branches #分支目录
-rw-r--r-- 1 root root 92 4月 7 08:39 config #定义项目特有的配置选项
-rw-r--r-- 1 root root 73 4月 7 08:39 description #仅供 git web程序使用
-rw-r--r-- 1 root root 23 4月 7 08:39 HEAD #指针
drwxr-xr-x 2 root root 242 4月 7 08:39 hooks #钩子
drwxr-xr-x 2 root root 21 4月 7 08:39 info #包含一个全局排除文件
drwxr-xr-x 4 root root 30 4月 7 08:39 objects #存放所有数据内容
drwxr-xr-x 4 root root 31 4月 7 08:39 refs #存放指向数据(分支)的提交对象的指针还有一个index,创建了文件提交到暂存区才会有的,用于保存暂存区的内容的
git提交到本地仓库
[root@git git_test]# echo 11111111 > test.txt
[root@git git_test]# git status #查看当前的状态
# 位于分支 master
#
# 初始提交
#
# 未跟踪的文件:
# (使用 "git add <file>..." 以包含要提交的内容)
#
# test.txt
提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)
[root@git git_test]# git add . #提交到暂存区
[root@git git_test]# git commit -m '新建立一个test.txt文件' #从暂存区提交到本地仓库
[master(根提交) db04ddf] 新建立一个test.txt文件
1 file changed, 1 insertion(+)
create mode 100644 test.txt
git的删除操作
- 删除暂存区的内容: (只是删除暂存区的内容)
[root@git git_test]# echo 22222 >> test.txt
[root@git git_test]# git add .
[root@git git_test]# git rm --cached test.txt
rm 'test.txt'
[root@git git_test]# cat test.txt
11111111
22222
[root@git git_test]# git status
# 位于分支 master
# 要提交的变更:
# (使用 "git reset HEAD <file>..." 撤出暂存区)
#
# 删除: test.txt
#
# 未跟踪的文件:
# (使用 "git add <file>..." 以包含要提交的内容)
#
# test.txt
[root@git git_test]# git commit -m "remove test.txt from repository"
[master d20d489] remove test.txt from repository
1 file changed, 1 deletion(-)
delete mode 100644 test.txt
[root@git git_test]# git status
# 位于分支 master
# 未跟踪的文件:
# (使用 "git add <file>..." 以包含要提交的内容)
#
# test.txt
提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)
注意:git rm --cached test.txt只是从暂存区删除了某一个文件,想要删除所有使用如下命令:
git rm --cached `git ls-files`
- 同时删除工作目录和暂存区的内容,前提是没有提交到本地仓库
[root@git git_test]# echo 3333 >> test.txt
[root@git git_test]# git add .
[root@git git_test]# git rm -rf test.txt
rm 'test.txt'
[root@git git_test]# ls #可以看出工作区的文件也没了
[root@git git_test]# git status
# 位于分支 master
无文件要提交,干净的工作区
- 在当前版本删除一个文件的流程(已经提交到了本地仓库)
[root@git git_test]# echo 444444 > test.txt
[root@git git_test]# git add .
[root@git git_test]# git commit -m 'echo 444444 > test.txt'
[master 16c79a1] echo 444444 > test.txt
1 file changed, 1 insertion(+)
create mode 100644 test.txt
[root@git git_test]# git rm test.txt #先在工作目录删除文件
rm 'test.txt'
[root@git git_test]# git add . #提交到暂存区
[root@git git_test]# git commit -m 'del test.txt' #提交到本地仓库
[master ee313e1] del test.txt
1 file changed, 1 deletion(-)
delete mode 100644 test.txt
对于提交到本地仓库的git文件改名操作
[root@git git_test]# echo 55555 > test.txt
[root@git git_test]# git add .
[root@git git_test]# git commit -m 'echo 55555 > test.txt'
[master 544b7a8] echo 55555 > test.txt
1 file changed, 1 insertion(+)
create mode 100644 test.txt
[root@git git_test]# git mv test.txt test.sh
[root@git git_test]# git add .
[root@git git_test]# git commit -m '将test.txt名称改为test.sh'
[master 8b35794] 将test.txt名称改为test.sh
1 file changed, 0 insertions(+), 0 deletions(-)
rename test.txt => test.sh (100%)
对比各个区域的文件
比对当前工作目录和暂存区的不同
git diff
比对暂存区和本地仓库的不同
git diff --cached
查看历史的git commit快照操作
[root@git git_test]# git log
commit 8b3579437d7d4bbb610b0009d6e3a14ac2337018
Author: zqf <826849158@qq.com>
Date: Mon Apr 7 08:56:37 2025 -0400
将test.txt名称改为test.sh
commit 544b7a87f52d0d0e83af92dfdf8a0f79979cbbd6
Author: zqf <826849158@qq.com>
Date: Mon Apr 7 08:56:02 2025 -0400
echo 55555 > test.txt
commit ee313e19613665c16120d7c794194b72ac18ba5b
Author: zqf <826849158@qq.com>
Date: Mon Apr 7 08:53:39 2025 -0400
del test.txt
commit 16c79a180df589c29c34b676edfdb1a6abfc5c13
Author: zqf <826849158@qq.com>
Date: Mon Apr 7 08:53:09 2025 -0400
echo 444444 > test.txt
commit d20d489fb04993783f95c5dcaf5063beed5ab885
Author: zqf <826849158@qq.com>
Date: Mon Apr 7 08:48:18 2025 -0400
remove test.txt from repository
commit db04ddf68afce6c2ec4f25e0a80fbe3378c1da96
Author: zqf <826849158@qq.com>
Date: Mon Apr 7 08:44:15 2025 -0400
新建立一个test.txt文件
[root@git git_test]# git log --oneline ##简单一行的显示
8b35794 将test.txt名称改为test.sh
544b7a8 echo 55555 > test.txt
ee313e1 del test.txt
16c79a1 echo 444444 > test.txt
d20d489 remove test.txt from repository
db04ddf 新建立一个test.txt文件
[root@git git_test]# git reflog #查看所有的历史提交(无论是在哪个快照版本下)
8b35794 HEAD@{0}: commit: 将test.txt名称改为test.sh
544b7a8 HEAD@{1}: commit: echo 55555 > test.txt
ee313e1 HEAD@{2}: commit: del test.txt
16c79a1 HEAD@{3}: commit: echo 444444 > test.txt
d20d489 HEAD@{4}: commit: remove test.txt from repository
db04ddf HEAD@{5}: commit (initial): 新建立一个test.txt文件
显示现在的指针指向到了哪个版本
[root@git git_test]# git log --oneline --decorate
8b35794 (HEAD, master) 将test.txt名称改为test.sh
544b7a8 echo 55555 > test.txt
ee313e1 del test.txt
16c79a1 echo 444444 > test.txt
d20d489 remove test.txt from repository
db04ddf 新建立一个test.txt文件
显示每个版本具体的变化内容
git log -p
git log --oneline -p 简单的显示
git log --oneline -1 -p 显示最后一个版本的变化信息
git的各个区域覆盖操作
- 从暂存区覆盖到工作目录
[root@git git_test]# cat test.sh
55555
#这个时候的test.sh中的内容已经提交到了本地仓库,我又写了一个6666到test.sh文件里。
[root@git git_test]# echo 6666 >> test.sh
[root@git git_test]# git checkout -- test.txt
error: pathspec 'test.txt' did not match any file(s) known to git.
[root@git git_test]# git checkout -- test.sh
[root@git git_test]# cat test.sh #发现内容6666已经被覆盖了
55555
- 本地仓库覆盖暂存区
[root@git git_test]# cat test.sh
55555
#这个时候的test.sh中的内容已经提交到了本地仓库,我又写了一个6666到test.sh文件里。
[root@git git_test]# echo 6666 >> test.sh
[root@git git_test]# git add .
[root@git git_test]# git reset HEAD test.sh
重置后撤出暂存区的变更:
M test.sh
本地仓库回滚版本到工作目录
#当前工作目录的内容:
[root@git git_test]# cat test.sh
55555
6666
[root@git git_test]# git log --oneline
8b35794 将test.txt名称改为test.sh
544b7a8 echo 55555 > test.txt
ee313e1 del test.txt
16c79a1 echo 444444 > test.txt
d20d489 remove test.txt from repository
db04ddf 新建立一个test.txt文件
#直接回滚到《新建立一个test.txt文件》这个时刻
[root@git git_test]# git reset --hard db04ddf
HEAD 现在位于 db04ddf 新建立一个test.txt文件
[root@git git_test]# cat test.txt
11111111
git的tag
查看标签:
git tag
创建标签
git tag 标签名字 -m '消息内容'
推送标签
git push origin 标签名字
删除标签
git tag -d 标签名字
推送
git push origin
给指定的commit打标签
git tag -a v0.1.0 -m 49e0cd22f6bd9510fe65084e023d9c4316XXXXXX
# 查看相应标签的版本信息
git show 版本号
解释:打标签不必要在HEAD之上,也可在之前的版本上打,这需要你知道某个提交对象的校验和,通过git log命令获取。
git的分支
在实际的项目开发中,我们要尽量保证master的分支是非常稳定的,仅用于发布新的版本,平时不要直接修改master里面的数据,工作的时候可以新建不同的分支,等到工作完成之后再合并到master分支上面。
查看当前的分支
git branch
新建一个分支
git branch +分支名称
切换分支
git checkout +目标分支
合并分支
在主分支上执行:
git merge +要合并到master上的分支
删除其他分支
git branch -d +目标分支
切换并创建新的分支,会和主分支的内容保持一致
git checkout -b +目标分支
git的冲突合并
比如同一个文件的某一行,主分支和普通分支不同,比如主分支的某一行为:11111,已经提交了,而普通分支的这一行为:22222,也已经提交,这个时候合并会失败,因为这一行冲突了!!
这个时候只能手动解决,进入当前的文件,手动删除或保留(master上的1111还是普通分支的2222)那一行,之后保存再在master分支上 重新提交暂存区,提交本地仓库即可
git的远程操作
git clone
在本地仓库克隆一个远程仓库
git clone +仓库地址
克隆之后本地仓库和远程仓库的内容一致
git remote
# 列出所有远程仓库
git remote
# 使用-v选项,可以参看远程仓库的网址
git remote -v
# 可以查看该仓库的详细信息
git remote show 仓库名
# 添加远程仓库
git remote add origin 仓库地址 ##origin只是一个仓库名称,可以随意起
添加远程之后,本地目录不会和远程仓库保持一致,使用git pull 拉取之后才会保持一致
# 删除远程仓库
git remote rm origin #origin是仓库的名称
# 修改远程仓库名称
git remote rename <原仓库名> <新仓库名>
git fetch
# 取回所有分支(branch)的更新到本地
git fetch <远程仓库名>
# 取回某的特定分支的更新
git fetch <远程仓库名> <分支名>
# 取回origin主机的master分支的更新
git fetch origin master
# 所取回的更新,在本地主机上要用”远程仓库名/分支名”的形式读取。比如origin仓库的master,就要用origin/master读取。可以使用git merge命令或者git rebase命令,在本地分支上合并远程分支
git merge origin/master
git rebase origin/master
git pull
# 取回origin仓库的next分支,与本地的master分支合并
git pull origin next:master
# 如果远程分支是与当前分支合并,则冒号后面的部分可以省略。
git pull origin next
# 上面的命令实质上等同于先做git fetch,再做git merge。
git fetch origin
git merge origin/next
# 合并需要采用rebase模式
git pull --rebase <远程仓库名> <远程仓库名>:<本地分支名>
git push
推送代码到远程仓库
git push -u origin 仓库分支
-u 代表仓库的名称 不一定就是origin
# 省略了本地分支,以下等同,删除origin仓库的master分支
git push origin :master
git push origin --delete master
# 不管是否存在对应的远程分支,将本地的所有分支都推送到远程仓库
git push --all origin
# 强制推送
git push --force origin
# git push不会推送标签(tag),除非使用–tags选项
git push origin --tags
Git fetch和pull区别
git fetch:相当于是从远程获取最新版本到本地,不会自动merge.
git pull:相当于是从远程获取最新版本并merge到本地.
git fetch示例:
首先从远程的origin的master主分支下载最新的版本到origin/master分支上
然后比较本地的master分支和origin/master分支的差别
最后进行合并
Git fetch origin master
git log -p master..origin/master
git merge origin/master
上述过程其实可以用以下更清晰的方式来进行:git pull
git pull origin master
上述命令其实相当于git fetch和git merge。在实际使用中,git fetch更安全一些,因为在merge前,我们可以查看更新情况,然后再决定是否合并。
Git reset和revert区别
git reset
reset用于回退版本,可以遗弃不再使用的提交,执行遗弃时,需要根据影响的范围而指定不同的参数,可以指定是否复原索引或工作树内容
当没有指定ID的时候,默认使用HEAD,如果指定ID,那么就是基于指向ID去变动暂存区或工作区的内容
没有指定ID, 暂存区的内容会被当前ID版本号的内容覆盖,工作区不变
git reset
指定ID,暂存区的内容会被指定ID版本号的内容覆盖,工作区不变
git reset <ID>
常见参数如下:
--mixed(默认):默认的时候,只有暂存区变化
--hard参数:如果使用 --hard 参数,那么工作区也会变化
--soft:如果使用 --soft 参数,那么暂存区和工作区都不会变化
git revert
在当前提交后面,新增一次提交,抵消掉上一次提交导致的所有变化,不会改变过去的历史,主要是用于安全地取消过去发布的提交
跟git reset用法基本一致,git revert 撤销某次操作,此次操作之前和之后的 commit和history都会保留,并且把这次撤销,作为一次最新的提交,如下:
git revert <commit_id>
如果撤销前一个版本,可以通过如下命令:
git revert HEAD
撤销前前一次,如下:
git revert HEAD^
区别
撤销(revert)被设计为撤销公开的提交(比如已经push)的安全方式,git reset被设计为重设本地更改
因为两个命令的目的不同,它们的实现也不一样:重设完全地移除了一堆更改,而撤销保留了原来的更改,用一个新的提交来实现撤销
两者主要区别如下:
git revert是用一次新的commit来回滚之前的commit,git reset是直接删除指定的commit
git reset 是把HEAD向后移动了一下,而git revert是HEAD继续前进,只是新的commit的内容和要revert的内容正好相反,能够抵消要被revert的内容
在回滚这一操作上看,效果差不多。但是在日后继续 merge 以前的老版本时有区别
git revert是用一次逆向的commit“中和”之前的提交,因此日后合并老的branch时,之前提交合并的代码仍然存在,导致不能够重新合并
但是git reset是之间把某些commit在某个branch上删除,因而和老的branch再次merge时,这些被回滚的commit应该还会被引入
如果回退分支的代码以后还需要的情况则使用git revert, 如果分支是提错了没用的并且不想让别人发现这些错误代码,则使用git reset
Git merge和reabse的相同点和不同点
merge是合并的意思,rebase是复位基底的意思,相同点都是用来合并分支的。
- 不同点:
merge操作会生成一个新的节点,之前的提交分开显示。而rebase操作不会生成新的节点,是将两个分支融合成一个线性的提交。
解决冲突时。merge操作遇到冲突的时候,当前merge不能继续进行下去。手动修改冲突内容后,add 修改,commit就可以了。而rebase操作的话,会中断rebase,同时会提示去解决冲突。解决冲突后,将修改add后执行git rebase –continue继续操作,或者git rebase –skip忽略冲突。 - git pull和git pull --rebase区别:git pull做了两个操作分别是”获取”和”合并”。所以加了rebase就是以rebase的方式进行合并分支,默认为merge。
- 总结:选择 merge 还是 rebase?
merge 是一个合并操作,会将两个分支的修改合并在一起,默认操作的情况下会提交合并中修改的内容
merge 的提交历史忠实地记录了实际发生过什么,关注点在真实的提交历史上面
rebase 并没有进行合并操作,只是提取了当前分支的修改,将其复制在了目标分支的最新提交后面
rebase 的提交历史反映了项目过程中发生了什么,关注点在开发过程上面
merge 与 rebase 都是非常强大的分支整合命令,没有优劣之分,使用哪一个应由项目和团队的开发需求决定
merge 和 rebase 还有很多强大的选项,可以使用 git help 查看