一文带你解决Git那些事~ git 指令 实用指南

        Git是目前世界上最先进的分布式版本控制系统。虽然日常中最多的在上面找项目代码...... Git有非常完善版本控制流程,适合团体和个人的开发,提升开发效率,企业开发必备!日常使用的时候可能接触更多的是 github、或者国内的gitee等网站,这些都是部署在不同服务器的Git服务。因为 Git 定义是一个系统,所以不同公司的 Git 使用都是符合标准的,但是因为存在数据安全的问题,公司一般会在自己的服务器上部署Git服务。当然还有更多更多的介绍信息和复杂功能,本文主要介绍经常使用的代码工程版本控制的常见使用

码字不易,希望有用~~~

目录

一、核心关系图

二、分支开发与版本控制

2.1 分支开发 之 创建切换分支:

2.2 版本回退

2.3 分支开发 之 合并分支:

2.4 分支开发 之 冲突解决

三、远程仓库

3.1 免密设置

3.2 推送拉取

3.3 push 、 pull 冲突问题

四、写在最后


一、核心关系图

结合上图来看:

Workspace: 工作区

                你的当前工作区的文件内容

Index / Stage:暂存区

                当你使用 add 指令提交之后,尚未commit之前

Repository:本地仓库

                commit 之后,你的工程会暂存到本地仓库(存在commit提交历史记录,还可以根据记录,回退到曾经提交的某个版本

Remote:远程仓库

                这个就是你在网页上看到的仓库里面的内容了(因为远程都是在本地commit之后再提交,commit存在记录,推导知 远程仓库的版本也可以回退)

        Git初印象:每次提交,先是提交commit到本地库,再推送push到远程仓库;也可以从远程复制clone(就是你最常用的 git clone)到本地仓库,也可以直接拉取pull到你的工作区。而且每次涉及提交的都会有git记录,利用这些记录可以实现版本回退..... 你可以把每个提交记录的节点,看成一个链表(数据结构警告!),你可以切换到任意一个节点,你的工作区会切换为那一节点对应的文件状态!!! 这么一看是不是“版本控制”的味道就有了呢。其实更准确点,刚刚说的工作流不仅仅是链表,应该是树。因为git还支持对当前的开发节点,创建一个分支,你再这个分支上的任何操作,不会影响原先节点或者从同一节点分出来的其他分支,如下图所示,一般主线的默认是master,例如有个需求需要改,你创建了dev分支和test分支,这个时候你再dev分支上不管进行了怎样的改动(包括添加、删除、修改),只要切回其他分支,工作区立刻切换到当前分支的情况!加上上面说的,你可以找到历史提交记录,从版本3直接回滚到版本2,工作区也会对应回到版本2对应的状态。这下完美了,相比自己进行 备份1.0 备份2.0 备份3.0,如果熟练掌握 Git,并在对应节点变换时进行说明注释,版本控制多香~~~

        这里再补充一点,不要单纯认为 Git 就是网络上存代码的地方。如果你合理利用Git,根据上面的工作流,我们不需要关联远程仓库;利用分支开发版本回退 只是不断的在本地Repository 和 workspace 之间游走,实现本地的版本控制管理。

刚刚说到 版本应该是一个树形,一些 IDE 和 软件 集成了可视化观察完整版本树的图形界面,当然你也可以用指令简要的查看下树形结构,方便对开发流的直观理解。

git log --oneline --graph --decorate --all

二、分支开发与版本控制

2.1 分支开发 之 创建切换分支:

        如果本地已经安装过git了,或者找个 git安装教程安装后尝试

        下面的脚本可以直接拷贝到terminal执行,主要完成如下任务:新建一个分支为master,在master的基础上分别新建两个分支b1、b2。因为b1、b2是在master基础上,故其分支除了自己特有的文件外,还有继承于master分支的 file1.txt。并且 b1、b2拥有自己特有的文件,且分支间不共享,随着分支的切换,工作区的内容会随之变化。可以从下面的图中看出

# 创建文件夹 初始化git
mkdir test_git
cd test_git
git init

# 创建并切换到 master 分支 并写一个 file1.txt 提交
git checkout -b master
echo "hello" >> file1.txt
git add .
git commit -m "master第一次提交"

# 创建并切换到 b1 分支 并写一个 file2.txt 提交
git checkout -b b1
echo "world" >> file2.txt
git add .
git commit -m "b1第一次提交"

# 此时在b1分支上,切换会master,在此节点上 创建并切换到 b2 分支 并写一个 file3.txt 提交
git checkout master
git checkout -b b2
echo "git" >> file3.txt
git add .
git commit -m "b2第一次提交"

        顺便可以生成版本树,的确可以看出b1、b2分支确实是从master分支分出来的,HEAD指向了现在所处的分支b1,注意此时master分支并未只是历史版本了,他还是一个分支,可以继续延伸,我们可以试着在master上提交点东西再来看看版本树。

git checkout master
vim file1.txt
# 随便写点什么吧
git add .
git commit -m "master 修改了file1"
git log --oneline --graph --decorate --all 

        这样就清楚了,master分支还是在做自己的事情,b1、b2在 master 454f686 引出来之后,也是在做自己的事情。这就比较符合日常开发过程,每个人都从某一版本创建一个分支,在分支上独立开发自己的需求,这里应该可以推断出来,刚刚在master上修改了file1.txt,但是b1、b2中的file1.txt是没有变化的,因为我只拷贝了你的历史版本,并没有实时更新,这也和 每个分支独立开发是呼应的,不然又乱套了,写了一晚上代码,结果另一个开发更新了master,自己写的没了.................... 但是,毕竟我拉分支出来是为了完成需求,改完了应该是需要将其合并到主干master上,总得把改完的需求合到主干上吧! 这里就存在一些问题了:

  • 如果b1、b2同时改了同一个代码呢?
  • 如果b1先提交了呢?
  • 提交的话是合到哪个版本节点呢(肯定是希望合到master最新)?

这就涉及到 合并分支 和 多人协作问题了,在此之前先了解下版本回退。

2.2 版本回退

git branch 
# 查看分支是否在b1、或者b2上 此处需要将分支切换到 b1或b2
git checkout b1

# 查看当前分支操作记录
git log

# 发现在 第一条中 完成了 b1 第一次提交,对应的如果再往前回退一个版本,不就是没有完成第一次提交了嘛? 试试看
git reset --hard 454f6860a83c54d353341bbfb4d543a1ca7f3120

git branch
ls
# 发现个有趣的事情....
# b1 分支回退到了 第一次提交 之前的状态,这里没有file2.txt,只有继承 master 来的file1.txt,回滚过程符合预期!!!
# 这个时候有些 好奇宝宝 又会疑惑,能不能回去呢? 当然可以! 我们回到刚刚提交完的那个版本
git reset --hard 8417b9dd329271bb8829cce6563d76a2fca46d51
ls
# 又回来了...
# 注意这里面有很多记录,并非是当前分支的,需要选取

2.3 分支开发 之 合并分支:

        前面阐述过分支开发完全独立于原先分支,实际工作上,在分支开发为了不影响原先分支,并不是为了做备份呐! 开发完了是需要将功能合并到主体代码中,这才是分支的意义嘛。这里就涉及到了 分支合并的问题了!

        分支合并主要依靠 两条指令 git rebase git merge ,有一个知乎帖子做了很多详细的实验分析,并给出下面的结论:

  1. 下游分支更新上游分支内容的时候使用 rebase
  2. 上游分支合并下游分支内容的时候使用 merge
  3. 更新当前分支的内容时一定要使用 --rebase 参数

        例如现有上游分支 master,基于 master 分支拉出来一个开发分支 dev,在 dev 上开发了一段时间后要把 master 分支提交的新内容更新到 dev 分支,此时切换到 dev 分支,使用 git rebase master。 等 dev 分支开发完成了之后,要合并到上游分支 master 上的时候,切换到 master 分支,使用 git merge dev   (看不明白的话先继续往下看吧,最后回过来再理解下)

        看不懂也没关系。。个人认为其实两种的功能相同,都可以进行分支的合并,注意是分支的合并,而不仅仅是将分支合到master,b1和b2之间有需求也是可以合并的!

        两个指令似乎是解释起来是反的,git rebase xxx 倾向于将 xxx作为当前分支的底,也就是将当前分支移到xxx前面git merge xxx倾向于将xxx合并到当前分支,将xxx内容合并到当前分支,xxx分支没有变动。例如此处分别采用 rebase 将 b1 合并到 master;使用 merge 将 b2 合并到master:

# 切换分支到 b1
git checkout b1
git rebase master

git checkout master
git merge b2

        在 b1 分支上进行 git rebase master。变成了b1 在前,master为底。具体表现为 master 分支的修改在 B1 分支上重演了一遍。 直接作为基底,线性开发,没有分支。 参考下面第一张图,查看分支内容发现,master上对file1 修改的内容,已经同步到 b1分支上,符合 master 分支的修改在 B1 分支上重演了一遍”,master是没有变化的,事实上分支b1独有的file2 确实没有合到master。 但是道理都懂了,你可以选择在这里将 b1 作为master的base,不就把b1 的内容变更重演到了master上了嘛。或者你一开始就这样做,也是会合并成功。这里不放结果了,可以自己试试,如果已经你说刚刚已经合并了b2了,不方便试,刚好可以用上前面说的版本回退,回退到merge之前就好了嘛~

在 master 分支上进行 git merge b2。将b2合到了master上,b2分支上的修改在master上重演一遍。 会产生额外的分支,导致版本树混乱。参考上面的分析,因为是直接将b2内容合并到master,从下图2确实看到master上存在了b2特有的file3.txt。

        关于版本树混乱的问题,从下面两个图看,注意纵轴可看成时间。而且第二张图merge 是在rebase基础上做的,为了对应清楚来看,参考节点的编号。fd5fbc3 是rebase之后b1的编号; 91bc4c5是master 修改file1 内容时候的记录;按照版本回退原则,因为 91bc4c5 在 fd5fbc3 之后,所以 fd5fbc3 对应的第一次提交file2事件没有合并到master里面, 91bc4c5 对应的修改file1事件是同步到了 b1里面;这里的现象与从前面的提到的分支对应工作区文件是对应的! 如果这里你按照刚刚说的,在master分支上再来一次 git rebase b1,你会发现两个节点重合,因为这里确实是一模一样的工作区了!

        对应第二个图,发现b1的 fd5fbc3 依旧存在(不要管颜色),你还会发现 b2 的 989e923节点 和 master的 91bc4c5节点 合并到了一起,对应了一个新的节点b0081eb,注释是 Merge branch 'b2' ,这也就是合并分支那个事件。 说它混乱,是因为此时 b2还在,只是把内容合到了master,相比于 b1 那条,是不是混乱很多,这个问题在大量分支的时候就变得非常复杂!

        总体来说,尽管merge指令看起来更像合并,很多帖子还是推荐使用rebase指令!但是我个人其实更倾向于使用 上述大段标红文字部分,在将master变更到当前分支的时候,使用rebase,在master合并开发分支的时候用merge,这样可以将merge的分支作为一个分支开发的版本保留也是一个不错的需要嘛~~~

2.4 分支开发 之 冲突解决

        目前为止,合并分支很顺利很顺利,因为每个人都是独立开发自己文件,没有出现冲突的问题!

        设想一下,b1和b2 同时有需求待开发,他们对应的从master引出了自己的分支b1、b2 。并分别在自己分支中创建了file2,file2(到现在和之前案例都一样)。但是同时又都需要修改了file1,b1 在file1中添加了一行代码,b2在file1中添加了一万行代码。 b1优先完成开发任务,就把自己分支往master上合,b2编写了很久很久(扩展来想,这个中间可能还有其他人多次提交),等到b2往master合代码的时候,不出意外,会出错误,我们来模拟一下这个过程:

# 创建文件夹 初始化git
cd ..
mkdir test_git2
cd test_git2
git init

# 创建并切换到 master 分支 并写一个 file1.txt 提交
git checkout -b master
echo "hello" >> file1.txt
git add .
git commit -m "master第一次提交"

# 创建并切换到 b1 分支 并写一个 file2.txt 提交
git checkout -b b1
echo "world" >> file2.txt
# 追加到 file1.txt 文件后
sed -i '$a 一行代码' file1.txt
git add .
git commit -m "b1第一次提交"

# 此时在b1分支上,切换会master,在此节点上 创建并切换到 b2 分支 并写一个 file3.txt 提交
git checkout master
git checkout -b b2
echo "git" >> file3.txt
# 追加到 file1.txt 文件后
sed -i '$a 一行万代码' file1.txt
git add .
git commit -m "b2第一次提交"

# 到这里b1 b2 已经完成各自的编码需求,我们知道,他们都改了file1
git checkout b1
git rebase master
git checkout master
git rebase b1

# 查看一下当前分支、路径下文件、file1 内容
git branch
ls
cat file1.txt
# 不出意外 确定了 b1 的内容已经合到master了

# b2 开始合
git checkout b2
git rebase master
...... 下面不用执行了,到这里报错了,符合我们预期! 
git checkout master
git rebase b1

        提示告诉我们,在file1.txt 中存在合并冲突,简单理解下:前面说到 rebase 表现为 master 分支的修改在 B1 分支上重演了一遍,像master独有的file2,直接重演一遍到b2,不存在冲突。 但是的问题是,master上已经出现了对file1的修改,而我们b2也存在对file1的修改,究竟该使用哪个作为最终的修改结果?Git无法为你选择,因为此时不知道 1行代码和1万行代码 实际业务需求,也许两个都需要保留呢?所以Git发生了冲突,并提示你需要手动去解决冲突,然后再执行 add,再 git rebase --continue。此时我们打开 出现冲突的 file1,  vim file1.txt

发现Git已经把冲突的地方都给你写上了,比如我们需要两个都保存,则删除提示信息。

这个时候提交的事情放一放,看下现在在哪个分支 git branch

        提示告诉说,不在分支上,现在在 rebasing b2 这个阶段,我们暂且认为是 临时分支吧,试着add和 continue

git add .
git rebase --continue

# 再将b2 合到 master
git checkout master
git rebase b2

发现所有的文件都合并了,file1的冲突也解决了,由于使用rebase,master主线非常干净!

        注意:本节所有的合并都是将冲突等问题在分支上解决,也就是拉取最新的master到当前分支,在当前分支完成合并,再合到原master上,保证了master分支和版本树的干净!

当然也可以直接合并,再删除掉多余分支,类似于

# b2 开始合
git checkout master
git rebase b1

git rebase b2
# 解决冲突
git add .
git rebase --continue
# 删除分支
git branch -b XXXX

三、远程仓库

        先去远程网页上创建一个仓库,空仓库就行!建立完成后可以按照网页的提示,将本地仓库与远程仓库关联,主要步骤如下:

git init
touch README.md
git add README.md
git commit -m "first commit"
git remote add origin XXXXXXXXXXXXXXXXXXXXXXXXXX
git push -u origin master

3.1 免密设置

        为什么GitHub需要SSH Key呢?因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。当然,GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。

        如果不这么做的话,你每次提交,git 就提示你输入用户密码,这也是防止随便一个人就往你远程仓库里提交东西,岂不是乱了套了......

本地生成的SSH KEY一般储存于: C:\Users\用户名\.ssh\id_rsa.pub

3.2 推送拉取

        现在再来看这个工作流,可以发现之前一直在 本地Repository 和 workspace 之间,使用 add commit / checkout进行切换,类似的可以把本地库视为远程库的一个分支,进行对应的 clone / push 的管理。可是一般都是对应push和pull啊?因为提交的时候一般本地分支就是在master,一般提上去的分支也是和本地对应,那么你pull下来,虽然直接到的workspace,但是pull下来的就是master分支,编辑的时候就是在对应分支上开发,可以认为在同分支下checkout自动执行。从工作流中看出来,每次push 到远程仓库的时候,并不是直接从工作区上传,而是将本地仓库的分支推送到远程同名分支。

PULL 注意事项:

        当你本地版本的base落后与远程版本时(远程仓库在你开发的时候,有其他人提交),此时你pull希望将base更到最新,Git会要求你将本地工作区提交干净(不允许存在未提交文件),因为你的分支落后远程分支。而当你本地分支base是最新的远程分支,此时pull下来就没有问题,因为不存在任何文件冲突(远程的你本地都有了,不会覆盖本地什么内容的)!

        同样的,因为你的分支落后于远程仓库,当其他人在这个期间和你同时修改了相同文件时,你就需要按照提示手动解决存在冲突的文件,随后进行add、commit、push。这个过程和本地分支merge解决冲突类似!

        建议经常性的pull最新仓库内容到本地,不至于本地落后远程过多版本,造成单次pull产生大量冲突!

PUSH注意事项:

  git push origin master

        上面命令表示,将本地的master分支推送到origin主机的master分支。如果master不存在,则会被新建。

        提交的是你本地仓库与远程仓库同名的分支,并非是工作区文件!

        master 一般表示本地的分支名,测试发现 如果 改为本地其他分支,则线上也会对应生成这个分支。Git远程因此也会创建相同的远程分支,远程上的分支维护,又和本地的分支维护类似。但是远程上一般称呼 PR(Pull Request)或 MR(Merge Request)。一般在网页端,进行提审核,并由仓库的owner审核,冲突解决,最终将内容合并到远程仓库的主线!

        实际企业开发中,远程的Master分支至关重要的,因为该工程将会直接部署到线上生产的服务器,也是当前工程最官方的版本!例如我在实习的时候,一般创建需求的时候,关联代码库,开发流程生成后,会自动在远程仓库上给你创建一个分支,你需要将对应的分支clone到本地,进行开发,如果多人协作,本地又具备了多个分支。最终开发完成后,只是将你的代码提到了最开始给你派发的那个远程分支,需要通知管理员审核你的代码,通过后才能合到远程主线master!流程非常繁琐和严谨,但是正是流程保障到结果保障,保证了线上代码的安全性!

3.3 push 、 pull 冲突问题

        例如 A、B同时开发一个工程,A pull 下来了工程,进行了一定的修改,此时push上去没有问题;

        但是如果 A pull 下来修改后没有及时提交,在这段时间,B pull 下来了,修改了部分文件,提交上去了;

        这个时候 A push 的时候,就存在问题,因为远程的节点比A当前新,A需要pull下来,pull 是把 远程分支拉到对应本地分支,要求对应分支的工作区已经提交commit干净了。commit完成后,完成 pull 拉取远程代码下来,没有共同修改的地方直接存下来,有冲突的地方需要手动解决冲突,完成后 add、 commit、 push、 就OK了!

四、写在最后

        希望大家熟练掌握Git使用,从平时用起,养成良好版本控制和多人协作的习惯!

        推一个小游戏,完成第一章就好了。可以直观的感受版本树的变化,但是无法准确的看到文件变化和测试版本冲突,建议还是跟着我写的走一遍,彻底理解清楚~~~

https://learngitbranching.js.org/locale=zh_CN&ADUIN=1565634332&ADSESSION=1635854015&ADTAG=CLIENT.QQ.5651_.0&ADPUBNO=27156

        个人能力和理解有限,文中出现错误在所难免,发现问题可以随时反馈给我,万分感谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值