内容参考自 廖雪峰
集中式和分布式
集中式版本控制系统:版本库存放在中央服务器,干活的时候需要从中央服务器获得最新版本到自己电脑,干完了再推送回去。
- 缺点:中央服务器出问题,所有人都不能干活了。而且必须联网工作,局域网带宽够大速度够快,而互联网若网速慢的话提交就很慢
分布式版本控制系统:没有中央服务器,每个人的电脑上都是一个完整的版本库
1、git 初始化设置和入门
在 linux 或 windows 下安装好 git 后,便可进行初始化操作。
1、设置用户名和邮箱
如果在 windows 下,那么打开 git bash
git config --global user.name '你在GitHub注册的用户名'
git config --global user.email '你在GitHub注册的邮箱'
2、生成 ssh 公钥
ssh-keygen -t rsa -C '你在GitHub注册的邮箱'
3、github 配置 ssh 公钥
用户主目录下,到 .ssh 所在目录,找到 id_rsa.pub 文件
- 用 cat 命令查看并复制里面的内容
上 github 点头像中的 setting,然后点击下面的 new SSH key,title 随意,然后复制的公钥粘贴到 key 中
假如你有多台电脑,只要把每台电脑的 key 都添加至 github,就可以在每台电脑上推送文件到 github 了,有了这个 key,别人就不能推导你的账户,而在你 key 列表中的电脑,便可以推到你的账户
4、建仓远程仓库
5、提交的基本流程
git init
git add readme.txt
git commit -m 'git test'
git remote add origin https://github.com/GogoingDzh/Git_test.git
git push -u origin master
2、基本概念
1、工作区
也就是在电脑上能看到的目录
2、版本库 .git
版本库中包含 暂存区stage 和 master 分支,以及指向 master 的一个 HEAD 指针。当我们使用 git init 创建版本库的时候,就会创建唯一的 master 分支。
3、如何把工作区的文件提交到暂存区
例如,创建版本库以后,你修改了 readme 文件,那么使用 git status 查看时,会出现 modified 的字样
此时,使用 git add <文件名>
把修改了的文件添加到暂存区,modified 字样由红变绿,表示文件已添加到暂存区
4、如何将暂存区的所有内容提交到当前分支
在使用 git add
将需要提交的文件放到暂存区以后,使用 git commit -m <message>
一次性提交所有更改,把暂存区的所有内容提交到当前分支。
在提交以后,如果没有再对工作区做修改,那么工作区是干净的。
提交时,需要保证你的修改都被 add 到暂存区了,否则这一次的 commit 就是无效的
5、如何查看工作区和仓库之间的差异
比如 2020年6月19号 20点 我提交了一次修改
第二天早上 6月20号,我作了一些修改,但是我已经记不清19号时作了哪些修改了,那么可以用 git diff <文件名>
来查看之间的差异
- 新增的代码将在前面显示 +,而去掉的代码将显示 -
6、如何撤销修改
比如,我们在 readme 中加入了一行
如果发现及时,可以手动删掉新增的这一行,在 git status 看来,这个文件是没有被修改过的
6.1 修改还没放入暂存区,如何撤销
使用 git checkout -- <文件名>
可以撤销这一次工作区的修改,这样,这个文件就恢复到修改之前的样子
6.2 修改已加入暂存区,如何撤销
如果你已将这个修改加入暂存区,那么正确的办法应该是使用 git reset HEAD <file>
把暂存区的修改撤销,看到了吗,此时这个文件 modified 字样又变为了红色,说明它还没有提交到暂存区
然后就回到了第一个场景中,使用 git checkout -- <文件名>
来将还没有 add 到暂存区的在工作区的修改丢弃掉
6.3 修改已提交,如何撤销
假如提交了版本,那么使用 git log 可以查看由最近到最远的版本提交日志,可以看到,我们先后提交了 A、B、C 三次版本
如果觉得输出了太多信息,可以加上 --pretty=online
参数,可以看到:
- HEAD 指针指向最新的修改
- 前面的一堆数字是使用 SHA1 计算的版本号
现在,我们想撤销 C,回到 B ,那么使用 git reset --hard HEAD^
- 回退两个版本,用两个^;回退n个版本,用 HEAD~n
现在,就退回到 B 版本了,而再使用 git log 发现此时已经没有了 C 版本
如果你想再回到 C版本,那怎么办呢? - 使用
git reflog
查看记录,找到 C版本提交时的版本号 d8c878f
- 使用
git reset --hard 版本号
,可以看到,又回到了 C版本
说白了,就是 HEAD 指针在几个版本之间来回游走的问题。
7、删除文件
通常我们都 rm 删除掉某个文件。此时工作区和版本库将出现不一致,git status 出现 deleted 字样
7.1 确实要删
使用 git rm <文件名>
删除这个文件,并且要 commit 提交来更新版本库
7.2 误删
使用 git checkout -- <文件名>
将其恢复
- 如果文件已经提交到了版本库,是不用担心误删的,可以使用该命令将其恢复。
8、分支管理
在多人协作开发的场景下,分支具有很多的作用。假如你负责一个模块还没搞完,如果你提交了,那么别人都不能正常干活了。
- 现在你创建了属于你自己的分支,别人是看不到的,你在自己创建的分支上干活,干完了再提交,最后合并到原来的分支上即可。
在最开始时,我们的每一次提交,git 都会将其连成一条时间线,这一条时间线我们叫它分支。HEAD 指针指向当前分支,而 master 指向最新的一次提交
8.1 创建和使用分支
8.1.1 创建并使用分支
现在,我们使用 git checkout -b <分支名>
或者 git switch -c <分支名>
创建并切换到一个 dev 分支(git 便会创建一个 dev 指针),这样,上面的图就变为
当然,也可以分步执行
- 创建:
git branch dev
- 切换:
git checkout dev
或者git switch dev
- 查看分支:
git branch
,注意*号指向当前分支
现在,我们做的修改和提交,都是在 dev 分支上所做的,每次提交,dev 分支就沿时间线往前走,而 master 分支不动
8.1.2 合并分支到当前分支
那么,如果我们在 dev 分支上完成了开发,应该先 commit 到版本库,并且切换回 master 分支,此时会发现我们做的修改并没有展示在文件中。现在想将 dev 分支所做的修改合并到当前的 master 分支,应该使用
git merge dev
8.1.3 删除分支
此时,查看文件的内容,应当是和 dev 分支的最新提交是一样的,此时,你可以删除 dev 分支
git branch -d dev
如果分支还没有被合并,要强行删除的话,需要使用 -D
参数
git branch -D dev
8.2 解决多分支分别有了新提交后由于分支合并导致的冲突
8.2.1 解决分支合并冲突
在最开始创建分支时,新分支的指向和 master 分支的指向是一致的,均指向最新的提交。
现在,新分支和 master 分支都有了新的提交,就变成了这个样子
下面模拟出这种场景:
创建并切换 feature1 分支上,修改文件并提交
git switch -c feature1
创建并切换到分支- 文件中添加 add by feature1
git add readme.txt
git commit -m 'add by feature1'
切换到 master 分支,修改文件并提交
git switch master
- 此时文件变回master 分支上的样子,没有 add by feature1,此时文件中添加 1add by master 和 2add by master
- 添加到暂存区并提交
现在,我们成功模拟出了上图的场景。现在我们想合并分支,此时可能会发生冲突。
现在,文件变成了这样。可以看到,Git 会尝试将两个分支所做的提交合并,并用<<<<<<<
,=======
和>>>>>>>
标记出了不同分支的内容
现在,需要手动解决冲突。如果我们想合并,便删去<<<<<<<
,=======
和>>>>>>>
标记,否则就在此文件中作出你想要的修改,最后提交即可
最终文件是这样,因为我们保留了两个分支所做的提交。
整个过程如图所示
如果不适用默认的 Fast forward 模式,merge后就是这样的,在 merge 时不会生成一个新的提交
git merge --no-ff -m "merge with no-ff" feature1
查看分支合并情况的指令是:
git log --graph --pretty=oneline --abbrev-commit
8.3 Bug 分支
有了分支管理策略,我们将其应用到平时的开发中,如果我们收到代号为 101 的 bug 修复任务,那么可以创建一个 issue-101 分支来修复,修复后,再合并分支,将此临时的 bug 分支删掉即可。
但是遇到了问题,你在你的 dev 分支上只开发到了一半,还没有办法提交(要一天),但你必须迅速解决 bug(两小时)。此时应该怎么办?
过于繁琐,贴上链接
8.4 多人协作
从远程仓库克隆时,Git 自动将本地的 master 分支和远程的 master 分支对应起来。所以本地的 master 主分支通常要和远程同步
远程仓库的默认名称是 origin。
git remote -v
可以查看有权限的抓取和推送的 origin 地址
8.4.1 多人协作分支管理策略
团队合作时,一般不在 master 上直接干活,你和组内的人应该分别在各自的分支上干活,时不时往 dev 分支合并,最后版本发布的时候,再将 dev 分支合并到 master 分支上即可
8.4.2 多人协作场景模拟
多人协作时,组员都会往 master 和 dev 分支上推送自己的修改。现在,假如我们的远程仓库有 master 和 dev 两个分支
模拟出 micheal 和 bob 协作开发的场景。通过 git clone 在本机的两个不同目录下克隆。
现在在本地,我们是看不到 dev 分支的,只能看到本地的 master 分支
现在,bob 要在 dev 分支上开发,所以要创建远程 origin 的 dev 分支到本地
git checkout -b dev origin/dev
现在,Bob 就可以在 dev 分支上修改,并且把 dev 分支 push 到远程。git push origin <分支名>
用于将该分支上的所有本地提交推送到远程仓库。
可以看到,远程仓库的 dev 分支下,增加了 bob 所做的修改,而 master 分支还是和之前一样的,以后版本发布时再与 dev 分支合并。
8.4.3 多人协作冲突
假如,Micheal 在 origin/dev 分支下对同样的文件,在相同的地方作了修改,并试图将其推送到远程仓库的 dev 分支,那么就会发生冲突,因为Micheal 并不知道 bob 在这个地方作了修改,也就是说,远程的这个 dev 分支比你本地的 dev 新
。
- 这里一定注意,在 Micheal 的本地工作区上,也是有 dev 分支的,同样与远程的 dev 分支关联
此时需要用 git pull
将最新的提交从远程的 origin/dev 分支上抓取下来,并且试图合并。在此之前如果没有指定本地 dev 分支和远程 origin/dev 分支的链接的话,需要指定,否则会出现 git pull 失败,提示 no tracking information
git branch --set-upstream-to=origin/dev dev
拉下来,如果在同一处做了修改就会合并产生冲突,需要用之前的方法手动解决。如果没有冲突就不管,最后提交
因此:多人在同一个分支上协作开发时,通常都会出现冲突。后面 push 的就要先 pull,在本地合并,如果有冲突就要手动解决后才能 push 成功。
9、标签
比如某个版本想发布,你总不能给一个 又臭又长的 commit ID,这时候取而代之的就是标签 v1.0,和 commit ID 一样,跟某个 commit 绑定在一起。
9.1 创建和使用标签
首先切换到想要打标签的分支上,使用下面命令即可
git tag v1.0 [可选:commitID]
在某次提交上打标签,名字叫 v1.0git tag -a [标签名] -m '说明' [可选:commitID]
git tag
查看所有标签git show [标签名]
查看标签信息- 如果某个 commit 同时出现在两个分支上,那么两个分支上都能看到这个标签
9.2 删除标签
创建的标签都存在本地,不会推送到远程,使用 git tag -d 标签名
删除本地标签
如果需要推送到远程,使用 git push origin 标签名
,而使用 git push origin --tags
可以全部推送
推送后,再想删除,就需要先从本地删除,在从远程删除
git tag -d 标签名
git push origin :refs/tags/标签名