Git学习初体验

前言

  本科只是听过git,但从未使用类似的版本控制工具。本学期选了孟宁老师的高级软件工程,因为课程作业需要,正式开始了git的使用之旅。
本文引用自孟宁老师的文章:https://mp.weixin.qq.com/s/Km5KuXPETvG0wCGHrvj9Vg

本文章主要讨论几个问题:

1.Git简介
2.Git基本操作
3.时光穿梭机
4.分支管理
5.GitHub
6多人协同开发
7.Git Rebase 练习题(实验四)
总结

1.Git简介

1.1什么是GIt?GIt是用来做什么的?

  Git是世界上最先进的分布式版本控制工具,它是用来对项目进行版本控制的。

1.2集中式版本控制系统与分布式版本控制系统的工作方式

  • 集中式版本控制系统 --cvs、svn

   版本库存放于中央服务器上,开始在工作时,首先从中央服务器上获取到项目的最新版本,然后开始工作,完成工作后,把自己的工作内容推送到中央服务器上。但是必须连接网络,并且一个终端创建的分支,在其他终端也会有一样的分支。

  • 分布式版本控制系统 --git

  git中,每个开发者都有一个属于自己的本地仓库,在工作时,首先在自己的本地仓库进行修改与提交,最后将这些修改的元数据,即修改部分信息,而不是像svn的原始数据推送到远程服务器上。git的每个提交保存的是差异记录,所有提交合在一起的到完整记录,有点像是数学并集问题。
在git的使用当中,远程服务器的职能更偏向于用来交换大家的修改与数据存储,即使没有网络,也可以使用本地仓库进行版本管理,只是不方便多方交换各自的数据而已。另外git创建的分支不会影响其他人的使用。

2.Git基本操作

2.1配置用户名和邮箱

  配置用户名和邮箱是为了方便于当前项目的追踪(准确追踪到具体团队成员进行了什么操作)。
配置前已经安装好了macOS版的Xcode。
git config --global user.name = ''
git config --global suer.email 'xx@xx'
`git config --list' 查看配置列表

2.2 git init

  先创建一个文件夹mkdir git_demo,让文件夹你变成版本库,可以被git控制,它需要一个.git文件。
怎么操作呢?
git init,在文件夹路径下初始化,就是对当前项目初始化,使之可以被git管控。

macOS下可以使用command + shift + 。查看隐藏文件

git init如果直接回车的话,是在桌面生成一个.git文件管理整个桌面,
可以使用git init git_demo是在桌面下创建一个文件夹git_demo,并且在文件下生成.git文件管理文件夹。

如上图,确实可以。
我们再来看看.git中有哪些文件


如上这些都是系统自动生成的 。

2.3 git add

  我们先新建一个文件 touch index.html,查看文件夹状态git status


那怎么进行追踪呢?
git add index.html


上图虽然显示未提交,但没有未追踪了,说明已经追踪成功。
可以看到追踪了显示绿色,未追踪的显示红色。

我们这次新建两个文件git touch 1.txt、git touch 2.txt
查看一下

显示了2个未追踪的,没有问题。我们可以分2ci把文件追踪。那如果一次性添加了很多的文件,我们可以一次性把2个文件都追踪吗?答案是可以的
git add .

OK!

2.4 git commit

  commit就是提交。当前项目备份的版本是否能够生成取决于是否commit,一次commit之后就会生成一个版本,包含了进行的操作的记录。
未追踪的文件是不能进行commit的。
输入git commit是可以提交的,不过会弹出编辑框,让你输入此次修改的描述信息。(我写的是test)

或者直接输入git commit -m “ ”,引号之间填写描述信息

现在来修改一下indexhtml文件,查看状态

添加、提交。

新建一个style.css文件,再修改一下index.html文件,你可以看到2种状态。

一般来说,接下来要进行2步走,先git add .,再git commit -m 'xxx'。
现在我们可以使用git commit -am ‘xxx’一步到位。但是这仅限于文件已经被追踪,像刚刚建立的style.css文件,是不可以的

只要commit后,就会生成一个档案号,有了档案号就可以查询所有的历史信息和操作记录

2.5 git log

  上面已经生成了多个档案,下面就可以进行log的追踪。

可以看到每个档案都有作者、日期、描述内容。

git log -p -2可以看到最近2次档案的差异

上图可以看到具体的差异.

git log --oneline每个档案只显示一行重要的内容,包括档案号(哈希值)以及描述内容。

git log --graph查看版本线图

也可以git log --graph --oneline,简化输出

git log --pretty=oneline可以显示完整的哈希值

git log --pretty=format:"%h - %an, %ar : %s"格式化输出
%h是哈希值,%an是作者,%ar是多久进行的操作,%s是描述信息。

git log --author="程鹏"可以查看作者,对于团队合作项目中,多个人用一个仓库,可以清晰的追踪到每个人所做的工作。

2.6 git diff

  追踪文件修改前后的区别
我们在index.html上做些修改,看下状态,发现有被修改的状态。在提交之前,我们可以检查一下做了哪些修改,看看这些修改是否正确,有问题在修改,没问题可以提交。使用git diff index.html

但是在commit后就不能用这个查看区别了。
那我们在add后能不能查看区别呢?
我们先修改2个文件,git add .之后,再使用git diff没有任何反应,不行吗?
试试这个呢?git diff --staged

可以看到哦,所以git diff --staged就对应在add后的查看区别操作。

2.7 文件忽略

  在git中实现文件或者文件夹的忽略,新建一个文件,文件名一定要为.gitignore,在这个文件中输入一些要忽略的文件或者文件夹。
可以用open .打开文件夹,但是隐藏文件一般是看不到的,所以使用code .打开编辑器
新建一个文件夹mkdir node_modules,模拟一个很大的、装有各种插件的文件夹node_modules,项目上传时也不会上传这个文件夹。

这是一个新创建的文件夹,是空的,我们只能获取到空的文件,并不能使用获取到空的文件夹。

现在我们在node_modules中新建一个文件,再次获取。

我们把不想追踪的文件放到.gitignore文件中。
例如:我们当前不想追踪node_modules文件夹下的所有文件,就打开编辑器,在.gitignore文件中写上/node_modules

可以看到被忽略的文件夹变灰了。

测试一下,确实没有追踪到node_modules文件夹了
用*.txt可以忽略txt类文件夹(某一类文件夹)
忽略一个特指的文件,直接在.gitignore写上文件名

我们发现已经commit的文件中有的不该提交,这时我们想忽略,写在.gitignore中是不起作用的。如果想要忽略,我们需要输入git rm -r --cached .,把所有提交的文件都踢出来(可以只踢出单个文件)。

此时被踢出文件都变成未被追踪的状态,现在可以在.gitignore写上文件名字即可。然后提交。

3.时光穿梭机

3.1 一键还原

git checkout -- [filename]恢复到上一次的状态
  当我们经过一系列操作后发现项目出现了bug,我们可以使用git diff进行匹配,查看做出了哪些改动,但是如果修改的信息过多,寻找bug过于耗时。所以可以使用git checkout -- index.html来还原。

如果文件还是未被追踪的状态,则不能使用上面的命令进行还原。即一个文件在被追踪之前的所有操作都无法还原。

3.2 撤销追踪操作与文件还原

  当我们将修改后的文件git add加入缓存区后,如果我们想还原这个文件,输入git checkout -- index.html没有任何改变

那我们该怎么做呢?我们需要把index.html重置到没有添加到缓存的状态。git reset HEAD index.html,在这之后,我们就可以使用git checkout -- index.html来还原文件。

3.3 版本回退

  当我们觉得现在这个版本有问题,我们可以回退到上个版本。git rset --hard HEAD^

上图可以看到当前是处在v5版本的。
现在输入git reset --hard HEAD^

成功切换到了v4版本。
git rset --hard HEAD^ 有几个^就切换到上几个版本,如果版本数过多怎么办?
HEAD是指针,可以指向对应的版本,同样也可以哈希值,生成的版本号(哈希值)是唯一的。
可以使用git reset --hard 0c46f08

目前的问题是,版本回退后,后面的版本都不显示了。
git reflog查看指针指向的日志,会有所有的版本信息。

3.4 回到旧版本

回到旧版本和版本回退的区别?
版本回退是退回到原版本,后面的版本不保留:v1 -> v2 -> v3 -> v4 变为v1 -> v2
而回到旧版本会保留后面的版本:v1 -> v2 -> v3 -> v4 变为 v1 -> v2 -> v3 -> v4 -> v5(v5等于v2,相当于回到v2版本,且保留了后面的v3、v4)
使用git checkout 哈希值 -- 具体的文件,哪个文件需要回到旧版本,如果想全部回退,也可以把具体的文件换成. ,表明把所有的文件都恢复到某个版本。

如上图,恢复之后的文件index.html是修改状态,但是它和perfect版本下的index.html是没有区别的,这时需要给它提交一下,一个新的版本号。(如果是全部文件都回复的话,那么新的版本和回到的某个版本除了版本号和描述内容外,其他都是一摸一样的,如果只是某个文件回到原来版本,则这个文件和原来是一样的)

4.分支管理

4.1 建立、切换、删除分支

  ;因为前面已经进行了很多操作,现在先把.git文件删除rm -rf .git,重新生成一个git文件git init,再add、commit后

当前分支是master,默认为主分支。
git branch罗列当前分支
git branch 未创建的名字创建一个分支,在哪个分支下创建的分支,就以哪个分支为模板来复制。

git checkout 已创建的分支名分支切换

git checkout -b 分支名创建并切换到该分支(要求未创建过的分支名)

当前我们处于v3分支下,我们对文件做了一些改动

现在可以看到有文件已经被改动了,我们一定要把这个文件处理完之后再切换分支,否则会混乱我们的版本。因为我们这个该过的文件或者提交或者忽略,得处理完,如果不处理完就切换到别的分支,再别的分支下也会看到这样的文件被改动信息。

现在就不明白是要提交master的版本还是v3的版本。分支内部问题,内部解决。

重新切回v3并提交。

不同分支下打印的版本不一样,v3分支下包括了复制来的v1和自己提交的v3版本。
对于没有经过任何操作的分支develop,要删除很容易git branch -d develop,要主义的是,不能在develop分支下删除develop。

那用同样的方法能删除v3分支吗?我们试试

因为当前分支修改了,没有合并,没有合并的分支被删除就会报这样的错误。
如果一定要删除,需要强制删除git branch -D v3

4.2 什么是分支

  例如我们想要添加一个新功能,但不确定能否实现,为了不影响我原来的项目,就创建一个分支,去实现这个新功能。不同分支相当于不同的存储空间,之间不会有干扰。每个分支的地位都是相同的,即使master被删除了,也没关系,还可以找回来(因为删除时会有哈希值),重新创建的master和之前是完全一样的。

4.3 合并分支

  重新初始化一个git

新建一个分支develop,利用此分支添加新功能--会员系统

查看日志

现在有2个版本号。现在在develop中已经开发完会员系统了,需要合并。是把develop合并到master上,现在develop上只比master分支上多会员系统的功能,所以合并就相当于把develop分支的内容复制给master。
合并开始:先切换到master上,切换过去之后,可以看到没有实现会员系统的文件了(test.txt)。
输入git merge develop

现在合并完成了

可以看到master和develop的内容完全一样了。
拿有个疑问?既然是完全一样,为什么还要合并呢?
在实际开发的过程中,可能有很多歌分支,有一个当前成功在运行的项目叫master,不同的开发人员在开发不同功能时都有自己的分支,当他们成功时都用自己的肯定不行,最终都是要合并到正在运行的master中的。
再用了一段时间后,发现会员系统text.txt 不好用,需要把它删掉,仍然使用develop去处理。

在develop中删除了会员系统。

现在develop中有了三个版本,其中delete 会员系统就是现在所需要的。

在master下合并后,master中有了自己所需要的版本(删除会员系统的版本)。

4.4 如何解决合并时发生的冲突

  现在有2个分支,对相同位置进行了修改。


都提交了。现在我们需要进行合并。

合并之后我们发现有冲突。输入git status会有详细的问题描述

现在有2中解决办法,
一是输入git merge --abort撤销合并,每个分支回到了合并前的状态。
二是解决冲突,对二部分进行取舍或者二者都留下。
下面模拟第二种解决方案。
合并后,把develop部分内容删除,然后git add .,接着输入git commit。

记录本次合并冲突的解决方案。

小结:多人开发时,冲突在所难免,解决冲突最好的方式是和同事沟通清楚,才能避免冲突、解决冲突。

4.5 查看版本线图

git log --oneline --graph查看当前分支版本线图,如果已经合并,可以不使用all也能看到所有的线图。
git log --oneline --graph --all查看所有分支版本线图
git log --oneline --graph --all -2查看当前最新的2个版本线图

  最左侧分支是最新的版本,不论是在那个分支下都是一样的显示结果。
master下依次是first commit -- master v2 -- merge
develop分支下依次是 first commit -- develop v2 -- develop v3 --merge

4.6 快转机制

  合并时默认是使用快转机制的,不使用快转机制要写git merge develop --no-ff

我们可以看到上图没有使用快转机制,在版本线图上因为合并而显示2个内容完全相同的版本,这中间的版本是多余的,如果删除的话会使版本线图更精简。当现在的版本和将来要合并的版本是一样的时候,我们就可以使用快转机制。
现在我们回退到合并前的版本,然后使用默认的快转机制合并。

可以看到合并后不显示develop分支的记录了,但这不代表develop分支没有了,在develop分支下有着和master下近乎一样的版本线图。

4.7 跟多合并的方法

git merge --no-ff -no-commit 分支名,合并之后到工作区,但没有提档,没有生成版本号。工作中的场景多为:需要合并到工作区,看结果是否正确,没问题后再进行提档。

git merge --no-ff -squash 分支名 ,在合并前,develop分支下有多个版本,需要合并

如上图,develop分支下有v2、v3两个版本,都是master所没有的,我们可以对v2和v3进行合并

输入后需要输入git commit提交,并输入一些合并信息。

git merge -squash 分支名
git reset --hard ORIG_HEAD ,回到合并之前。

4.8 一次性删除所有已经合并的分支

git branch --merged 查看所有已经合并的分支
git branch --no-merged 查看所有还未合并的分支
以上两种操作,是不会显示自身的,例如在master下就不会显示master。

对于这些已经合并的分支,需要把它们删除
git branch --merged | egrep -v "(^\*|master|develop)" | xargs git branch -d
其中master和develop是我们不想删除的

也可以使用同样的操作把所有未合并的分支,把--merged换成--no-merged,因为是未合并的,要用-D(强制删除)代替-d。

5.GitHub

5.1 本地仓库推送到远端仓库
  创建远端仓库后github上提示了我们一系列操作

连接

上图origin就是代替的远端地址。现在已经连接上了。

git push --set-upstream origin master推送到远端仓库
origin是地址,master是给远端仓库的名字
git push -u origin main,不过好像现在要把master改为main了,因为黑人运动(master-slave)...
上面两句的意思是一样的。

输入后是要输入你设置的密码的。
打开GitHub发现确实上传了。

5.2 主仓库作为服务器

现在GitHub傻姑娘创建一个仓库,仓库名一定要为 用户名.github.io

之前我们已经有了git remote了,所以现在把它删掉

这个地址就成了服务器的地址,可以直接在浏览器上运行。

5.3 如何获取远端项目

git clone支持四种url协议。
从GitHub上下载的只有项目,没有.git文件。
git clone git@github.com:cp777-AC/newrepo.git

克隆下来的是默认分支(一般是master)

克隆下来的仓库是有.git 文件的。克隆下来后会自动给我们checkout一个分支。
那可以不自动checkout一个分支吗?答案是可以的。
git clone --no-checkout git@github.com:cp777-AC/newrepo.git 就可以了。在后面添加新名字可以改名

查看状态,发现有些绿色字体,出现的原因是没有checkout具体分支。

在checkout后就没有那些绿色字体了。
我们什么时候会使用--no-checkout这种方式克隆呢?通常是在一个大型项目,内容太多,我们想要快速克隆下来,克隆完后自己决定切到哪个分支下。

git clone --bare git@github.com:cp777-AC/newrepo.git克隆一个裸的仓库信息,和.git内的信息非常相似。再输入git clone--no-checkout 刚拉下来的仓库地址 新的仓库名 就可以实现本地克隆。

6.多人协同开发

6.1 Push

git push -u origin master
在我们push后,输入git branch -a (a就是all的意思)
红色的 remotes/origin/master是远端追踪分支
*master是本地分支

GitHub上的master是远端分支

本地分支和远端分支都可以看到实际内容的,
而远端追踪分支是看不到的,它的作用是连接远端分支和远端追踪分支。

已经连接了remote,也有了远端追踪分支,所以直接git push就行了,因为有了远端追踪分支,我们push时,它会直到我们要push到哪个分支下。

现在远端分支和本地分支完全一样了。

现在我们在本地新建一个develop分支,也push到远端分支上。

直接git push是不好用的,因为master有个远端追踪分支,它知道要追踪到哪个分支下,而develop并没有。所以还是先用git push -u origin develop


现在develop也有远端追踪分支了。远端分支也有2个了。
只有第一次需要写-u origin,后面就不用了。
如果有多个分支要一次性push,可以git push --all(暂时不用,有点乱。。)

这时远端分支增加了三个新的分支。(虽然new1、2、3没有建立远端追踪分支,因为有建立远端分支的master,所以在git push --all可以把所有搜push上去)

6.2 Pull

git pull = git fetch + git merge ,就是从远端分支拉去下来并与本地分支合并。
输入git remote -v

可以看到git fetch和git push地址是一样的。
如果同事B在修改内容push上去(相当与修改了远端分支的内容),这时我们直接push时会报错的。

要先pull下来,再做修改,再push上去。(现在有个问题?如果两个程序员都再早上pull下来,晚上该怎么push呢?还没想好。。)
2020.10.29
突然想到这个问题?
晚上上传前再pull一次,之后再push上去。因为pull=fetch + merge,所以pull下来后,自己更改的已经与远端的合并了。

6.3 删除远端分支

  如果远端分支有新分支,本地分支没有,这是可以git pull下来。

上图可以看到有远端追踪分支的new-1,但是没有本地分支new-1.所以直接切换到new-1,会自动给我们建立新分支new-1,内容和远端分支new-1一样。(要注意的一点是不要切换到远端追踪分支)
如何想删除本地和远端的new-1呢?
git branch -d new-1是删除本地分支。
git push origin --delete new-1是删除远端分支和远端追踪分支。

7.实现Git Rebase(场景四)

git rebase -i [startpoint] [endpoint]
其中-i的意思是--interactive,即弹出交互式的界面让用户编辑完成合并操作,[startpoint] [endpoint]则指定了一个编辑区间,如果不指定[endpoint],则该区间的终点默认是当前分支的HEAD。
如果有冲突,解决冲突后输入git rebase --continue再次提交。

先从远端分支master克隆到本地

在master分支不做修改,在本地新建develop分支,并且修改2次,分别为版本A和版本B。

现在develop的2次提交记录可以rebase了,之在与master分支合并。



删除了B,只剩下了A,rebase成功了。
master合并dedvelop之前,我们需要pull一下master,看远端分支有无变化。

合并之后push上去就完成了。

远端分支和本地分支上也只有A的记录。
我觉得git rebase和git merge --squash作用非常类似。
参考链接:https://www.jianshu.com/p/684a8ae9dcf1
rebase小结:
rebase 可以尽可能保持 master 分支干净整洁,并且易于识别 author
squash 可以保持 master 分支干净,但是 master 中 author 都是 maintainer,而不是原 owner

总结

  经过一段时间的学习,我切实感受到了git为什么是当今最好用的版本控制工具。回想到本科期间单人做项目,没有使用git,那时候手动版本控制太麻烦了,每次做改动都要先拷贝一份,然后不小心做了错误操作,奥很久才能找到还原回来。git不仅单人工作很方便,多人协同开发方面也做的非常完善。总之就是,git是你遇到就会爱上的工具!

posted @ 2020-10-14 21:31  程鹏777  阅读(174)  评论(0)    收藏  举报