软件开发划时代意义的技术-Git
一.Git的简介与历史
首先感谢孟宁老师的细心指导,带我进一步的了解了Git的历史和它的使用方法。同时也解决了很多之前有关于Git的疑惑,通过阅读老师的文章我比较系统的学习了关于Git从构建、使用和消亡的一系列方法。也是这篇文章重要的参考文献:https://mp.weixin.qq.com/s/Km5KuXPETvG0wCGHrvj9Vg
1.Git介绍
Git(是一个开源的分布式版本控制C系D统,可以有效、高速地处理从很小到非常大的项目版本管理,它是目前世界上最先进的分布式版本控制系统。Git 是用于 Linux内核开发的版本控制工具。与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持,使源代码的发布和交流极其方便。 Git 的速度很快,这对于诸如 Linux kernel 这样的大项目来说自然很重要。 Git 最为出色的是它的合并跟踪(merge tracing)能力。
Git是一种非常流行的分布式版本控制系统,它和其他版本控制系统的主要差别在于Git只关心文件数据的整体是否发生变化,而大多数版本其他系统只关心文件内容的具体差异,这类系统(CVS,Subversion,Perforce,Bazaar 等等)每次记录有哪些文件作了更新,以及都更新了哪些行的什么内容。Git另一个比较好的地方在于绝大多数操作都可以在本地执行,而每个本地都可以从服务器获取一份完整的仓库代码,而且在没网的时候仍然可以修改和使用大部分命令,在方便的时候再跟服务器进行同步,这样可以更好的实现多人联合编程。

2.Git的历史
Git是一种很厉害的版本控制器,那么我们来看看git的诞生史,了解历史会对我们学习git有很大的帮助。很多人都知道,Linus在1991年创建了开源的Linux,从此,Linux系统不断发展,已经成为最大的服务器系统软件了。Linus虽然创建了Linux,但Linux的壮大是靠全世界热心的志愿者参与的,这么多人在世界各地为Linux编写代码,那Linux的代码是如何管理的呢?事实是,在2002年以前,世界各地的志愿者把源代码文件通过diff的方式发给Linus,然后由Linus本人通过手工方式合并代码!你也许会想,为什么Linus不把Linux代码放到版本控制系统里呢?不是有CVS、SVN这些免费的版本控制系统吗?因为Linus坚定地反对CVS和SVN,这些集中式的版本控制系统不但速度慢,而且必须联网才能使用。有一些商用的版本控制系统,虽然比CVS、SVN好用,但那是付费的,和Linux的开源精神不符。
不过,到了2002年,Linux系统已经发展了十年了,代码库之大让Linus很难继续通过手工方式管理了,社区的弟兄们也对这种方式表达了强烈不满,于是Linus选择了一个商业的版本控制系统BitKeeper,BitKeeper的东家BitMover公司出于人道主义精神,授权Linux社区免费使用这个版本控制系统。
安定团结的大好局面在2005年就被打破了,原因是Linux社区牛人聚集,不免沾染了一些梁山好汉的江湖习气。开发Samba的Andrew试图破解BitKeeper的协议(这么干的其实也不只他一个),被BitMover公司发现了(监控工作做得不错!),于是BitMover公司怒了,要收回Linux社区的免费使用权。Linus可以向BitMover公司道个歉,保证以后严格管教弟兄们,嗯,这是不可能的。实际情况是这样的:Linus花了两周时间自己用C写了一个分布式版本控制系统,这就是Git!一个月之内,Linux系统的源码已经由Git管理了!牛是怎么定义的呢?大家可以体会一下。Git迅速成为最流行的分布式版本控制系统,尤其是2008年,GitHub网站上线了,它为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub,包括jQuery,PHP,Ruby等等。历史就是这么偶然,如果不是当年BitMover公司威胁Linux社区,可能现在我们就没有免费而超级好用的Git了。
如今GitHub+git的开发一直模式已经被很多企业和个人所喜爱,成为了大部分企业软件开发必不可缺的工具。
二.Git的基本指令与安装
要想熟练的运用Git我们就需要了解它的一些基本指令:
git clone
从远程仓库克隆一个版本库到本地。
# 默认在当前目录下创建和版本库名相同的文件夹并下载版本到该文件夹下
$ git clone <远程仓库的网址>
# 指定本地仓库的目录
$ git clone <远程仓库的网址> <本地目录>
# -b 指定要克隆的分支,默认是master分支
$ git clone <远程仓库的网址> -b <分支名称> <本地目录>
git init
初始化项目所在目录,初始化后会在当前目录下出现一个名为 .git 的目录。
# 初始化本地仓库,在当前目录下生成 .git 文件夹
$ git init
git status
查看本地仓库的状态。
# 查看本地仓库的状态
$ git status
# 以简短模式查看本地仓库的状态
# 会显示两列,第一列是文件的状态,第二列是对应的文件
# 文件状态:A 新增,M 修改,D 删除,?? 未添加到Git中
$ git status -s
git checkout
检出命令,用于创建、切换分支等。
# 切换到已存在的指定分支
$ git checkout <分支名称>
# 创建并切换到指定的分支,保留所有的提交记录
# 等同于 "git branch" 和 "git checkout" 两个命令合并
$ git checkout -b <分支名称>
# 创建并切换到指定的分支,删除所有的提交记录
$ git checkout --orphan <分支名称>
# 替换掉本地的改动,新增的文件和已经添加到暂存区的内容不受影响
$ git checkout <文件路径>
git add
把要提交的文件的信息添加到暂存区中。当使用 git commit 时,将依据暂存区中的内容来进行文件的提交。
# 把指定的文件添加到暂存区中
$ git add <文件路径>
# 添加所有修改、已删除的文件到暂存区中
$ git add -u [<文件路径>]
$ git add --update [<文件路径>]
# 添加所有修改、已删除、新增的文件到暂存区中,省略 <文件路径> 即为当前目录
$ git add -A [<文件路径>]
$ git add --all [<文件路径>]
# 查看所有修改、已删除但没有提交的文件,进入一个子命令系统
$ git add -i [<文件路径>]
$ git add --interactive [<文件路径>]
git commit
将暂存区中的文件提交到本地仓库中。
# 把暂存区中的文件提交到本地仓库,调用文本编辑器输入该次提交的描述信息
$ git commit
# 把暂存区中的文件提交到本地仓库中并添加描述信息
$ git commit -m "<提交的描述信息>"
# 把所有修改、已删除的文件提交到本地仓库中
# 不包括未被版本库跟踪的文件,等同于先调用了 "git add -u"
$ git commit -a -m "<提交的描述信息>"
# 修改上次提交的描述信息
$ git commit --amend
git diff
比较版本之间的差异。
# 比较当前文件和暂存区中文件的差异,显示没有暂存起来的更改
$ git diff
# 比较暂存区中的文件和上次提交时的差异
$ git diff --cached
$ git diff --staged
# 比较当前文件和上次提交时的差异
$ git diff HEAD
# 查看从指定的版本之后改动的内容
$ git diff <commit ID>
# 比较两个分支之间的差异
$ git diff <分支名称> <分支名称>
# 查看两个分支分开后各自的改动内容
$ git diff <分支名称>...<分支名称>
git remote
操作远程库。
# 列出已经存在的远程仓库
$ git remote
# 列出远程仓库的详细信息,在别名后面列出URL地址
$ git remote -v
$ git remote --verbose
# 添加远程仓库
$ git remote add <远程仓库的别名> <远程仓库的URL地址>
# 修改远程仓库的别名
$ git remote rename <原远程仓库的别名> <新的别名>
# 删除指定名称的远程仓库
$ git remote remove <远程仓库的别名>
# 修改远程仓库的 URL 地址
$ git remote set-url <远程仓库的别名> <新的远程仓库URL地址>
git branch
操作 Git 的分支命令。
# 列出本地的所有分支,当前所在分支以 "*" 标出
$ git branch
# 列出本地的所有分支并显示最后一次提交,当前所在分支以 "*" 标出
$ git branch -v
# 创建新分支,新的分支基于上一次提交建立
$ git branch <分支名>
# 修改分支名称
# 如果不指定原分支名称则为当前所在分支
$ git branch -m [<原分支名称>] <新的分支名称>
# 强制修改分支名称
$ git branch -M [<原分支名称>] <新的分支名称>
# 删除指定的本地分支
$ git branch -d <分支名称>
# 强制删除指定的本地分支
$ git branch -D <分支名称>
了解了基本的指令之后,我们可以开始安装Git了,如果你是VSCode用户的话,那么它将会自带Git。如果没有安装的话可以参考https://www.liaoxuefeng.com/wiki/896043488029600/896067074338496
三.五大场景中运用Git
LeetCode是大家熟知的练习算法技术的网站,接下来我将建立一个LeetCode代码的repository为例子,说明Git的使用方法:
场景一:Git本地版本库的建立
1.首先初始化一个版本库,如果安装了VSCode的情况下可以通过单击初始化存储库来完成初始化:

或者使用命令行完成git版本的初始化:初始化项目所在目录,初始化后会在当前目录下出现一个名为 .git 的目录。
# 初始化本地仓库,在当前目录下生成 .git 文件夹
$ git init
2.初始化完成后,我们可以查看工作区的状态在VSCode中以绿色U标记的文件为没有添加到版本库进行跟踪的文件(Untracked files)、以橙色M标记的文件为已修改(Modified)未提交的文件(Changes not staged for commit):

或者可以使用命令git status来查看当前工作区状态:查看本地仓库的状态。
# 查看本地仓库的状态
$ git status
# 以简短模式查看本地仓库的状态
# 会显示两列,第一列是文件的状态,第二列是对应的文件
# 文件状态:A 新增,M 修改,D 删除,?? 未添加到Git中
$ git status -s
3. 之后就能够将更改区列表里面的文件添加到暂存区了,VSCode中只需要点击文件旁边的“+”就可以完成操作:

或者使用命令git add命令完成添加操作:把要提交的文件的信息添加到暂存区中。当使用 git commit 时,将依据暂存区中的内容来进行文件的提交
# 把指定的文件添加到暂存区中
$ git add <文件路径>
# 添加所有修改、已删除的文件到暂存区中
$ git add -u [<文件路径>]
$ git add --update [<文件路径>]
# 添加所有修改、已删除、新增的文件到暂存区中,省略 <文件路径> 即为当前目录
$ git add -A [<文件路径>]
$ git add --all [<文件路径>]
# 查看所有修改、已删除但没有提交的文件,进入一个子命令系统
$ git add -i [<文件路径>]
$ git add --interactive [<文件路径>]
希望将文件从暂存区中移除的话可以点击文件旁边的“-”号,或者使用git reset命令完成撤销操作:
git reset HEAD FILES # 指定文件或文件列表
git reset HEAD
4.接下来就能够将暂存区中的文件提交到本地仓库了,在VSCode中可以点击“✅”图标完成commit操作,或者使用git commit命令完成提交的操作:
# 把暂存区中的文件提交到本地仓库,调用文本编辑器输入该次提交的描述信息
$ git commit
# 把暂存区中的文件提交到本地仓库中并添加描述信息
$ git commit -m "<提交的描述信息>"
# 把所有修改、已删除的文件提交到本地仓库中
# 不包括未被版本库跟踪的文件,等同于先调用了 "git add -u"
$ git commit -a -m "<提交的描述信息>"
# 修改上次提交的描述信息
$ git commit --amend

5.提交完成之后暂存区的文件就已经存储到本地仓库中了,在这里需要介绍一下HEAD指针:这要从git的分支说起,git 中的分支,其实本质上仅仅是个指向 commit 对象的可变指针。git 是如何知道你当前在哪个分支上工作的呢?其实答案也很简单,它保存着一个名为 HEAD 的特别指针。在 git 中,它是一个指向你正在工作中的本地分支的指针,可以将 HEAD 想象为当前分支的别名。在这里我们就可以通过git log命令来查看当前的日志信息:

如果此时你希望项目恢复到上次提交的版本状态,这时候仓库的重要性就显示出来了,可以通过git reset命令完成版本的回退:下面的命令可以让HEAD回退到任意一个版本,比如HEAD^表示HEAD的前一个版本、HEAD^^表示HEAD的前两个版本、HEAD~100表示HEAD的前100个版本,也可以用版本号字符串来指定任意一个版本。
git reset --hard HEAD^^
git reset --hard HEAD~100
git reset --hard 128个字符的commit-id
git reset --hard 简写为commit-id的头几个字符
为了体现版本回退,我们再次提交一次“Second commit”,这个时候HEAD指针指向Second commit,执行git reset --hard HEAD^命令之后:回退到First commit
可以通过git reflog命令查看HEAD之后的提交记录,便于回到未来:

场景二:Git远程版本库的用法
1.克隆一个远程版本库
首先,在Github上创建一个新的仓库:
接下来可以使用VSCode中克隆存储库的按钮,或者使用git clone命令完成从远程库的克隆:
# 默认在当前目录下创建和版本库名相同的文件夹并下载版本到该文件夹下 $ git clone <远程仓库的网址> # 指定本地仓库的目录 $ git clone <远程仓库的网址> <本地目录> # -b 指定要克隆的分支,默认是master分支 $ git clone <远程仓库的网址> -b <分支名称> <本地目录>

2.然后就可以使用git remote -v指令查看远程存储库的信息:
# 列出已经存在的远程仓库
$ git remote
# 列出远程仓库的详细信息,在别名后面列出URL地址
$ git remote -v
$ git remote --verbose
# 添加远程仓库
$ git remote add <远程仓库的别名> <远程仓库的URL地址>
# 修改远程仓库的别名
$ git remote rename <原远程仓库的别名> <新的别名>
# 删除指定名称的远程仓库
$ git remote remove <远程仓库的别名>
# 修改远程仓库的 URL 地址
$ git remote set-url <远程仓库的别名> <新的远程仓库URL地址>

3.接下来就可以通过场景一的操作来完成对本地仓库的修改了,修改完毕之后通过git push命令将本地仓库推送到远程仓库中去,在这里我添加一个main.cpp的文件修改本地仓库内容之后,推送到远程仓库中,效果如下:

# 把本地仓库的分支推送到远程仓库的指定分支
$ git push <远程仓库的别名> <本地分支名>:<远程分支名>
# 删除指定的远程仓库的分支
$ git push <远程仓库的别名> :<远程分支名>
$ git push <远程仓库的别名> --delete <远程分支名>
4.如果想要下载远程库中的内容可以通过使用git pull命令来完成该操作。同时VSCode自带了进行push和pull的操作:

场景三:在团队项目中进行分叉与合并
Git的存在让团队项目的开发效率有了质的飞跃,每个个体之间可以在开发自己的模块时可以暂时不考虑其他开发人员对项目的改变,只需要在最后将每个人的开发分为一个一个的开发分支既可解决问题。接下来我想说明在团队开发中,如何创造分支,以及合并分支:
1.拷贝远程版本库中的文件:这里可以利用场景二的知识,利用git clone命令完成对版本库的拷贝。
git clone https://DOMAIN_NAME/YOUR_NAME/REPO_NAME.git git pull
2.创建一个属于自己的分支,利用git branch命令:
# 列出本地的所有分支,当前所在分支以 "*" 标出
$ git branch
# 列出本地的所有分支并显示最后一次提交,当前所在分支以 "*" 标出
$ git branch -v
# 创建新分支,新的分支基于上一次提交建立
$ git branch <分支名>
# 修改分支名称
# 如果不指定原分支名称则为当前所在分支
$ git branch -m [<原分支名称>] <新的分支名称>
# 强制修改分支名称
$ git branch -M [<原分支名称>] <新的分支名称>
# 删除指定的本地分支
$ git branch -d <分支名称>
# 强制删除指定的本地分支
$ git branch -D <分支名称>

3.在完成创造自己的分支后,可以对项目进行修改了:
git add FILES
git commit -m "commit log"

4.最后就能够进行分支合并的操作了:首先回到master分支,然后同步最新的远程版本库内容,最后将自己的分支合并到主分支上,就能够将自己的开发结果推送到远程版本库中了。这里将会用到git merge指令,将分支进行合并,下面是指令的执行顺序:
git checkout main;//切换到主分支
git pull;//确保现在是远程版本库的最新版本
git merge lemonbranch;//将自己的分支与主分支合并
git push;//将本地仓库推送到远程版本库中

之后GIthub上的远程版本库的文件结构将会是这样的:

场景四:Git Rebase的运用

1.首先我们创建三个提交A,B,C,然后使用git log指令查看commit信息
2.使用git rebase命令,进入编辑模式,界面如下:

3.然后我们修改B与C,将pick改为S,意味与前一个commit合并,之后将会出现如下界面,在这个界面下可以修改注释信息:
4.保存之后合并就成功了,我们就完成了commit的合并,通过git log来查看commit的信息:

5.接下来将项目提交到远程版本库就完成了如下节点网络:

场景五:Fork+Pull Request
为了解决开源社区松散团队的协作问题,Github提供了Fork+ Pull request的协作开发工作流程。
当你想更正别人仓库里的Bug或者向别人仓库里贡献代码时,要走Fork+ Pull request的协作开发工作流程:
-
先 fork(分叉) 别人的仓库,相当于拷贝一份;
-
做一些 bug fix或其他的代码贡献;
-
发起 Pull request 给原仓库;
-
原仓库的所有者 review Pull request,如果没有问题的话,就会 merge Pull request 到原仓库中。
1.第一步点击fork按钮,创建一个版本库:

这样就能够拥有一个复制的版本库:

2.然后可以在这个版本库中进行自己工作,完成需要的修改。
3.之后创建一个新的pull requst,里面可以查看自己改变的信息:

4.然后可以处理变更的信息,没有问题的话提交pull请求即可:

四.Git的一些进阶操作
1.如何修改origin仓库的信息?
添加origin仓库信息:
git remote add origin <git仓库地址>
查看origin仓库信息:
git config get --remote.origin.url
git remote -v
git remote show origin
删除origin仓库信息:
git remote rm origin
2.遇到冲突了怎么办?
最通用的办法是用编辑器打开冲突的源文件进行修改,可能会发生遗留,且体验不好,通常需要借助 git mergetool 命令。在 Mac 系统下,运行 git mergetool <文件名> 可以开启配置的第三方工具进行 merge,默认的是 FileMerge 应用程序,还可以配置成 Meld 或 kdiff3,体验更佳。但是最好是能养成以下习惯,来尽量避免冲突:1.在开始修改代码前先 git pull 一下;2将业务代码进行划分,尽量不要多个人在同一时间段修改同一文件;3通过 Gitflow 工作流 也可以提升 git 流程效率,减少发生冲突的可能性。
3.如何在git log中查看修改的文件列表:
默认的 git log 会显示较全的信息,且不包含文件列表。使用 --name-status 可以看到修改的文件列表,使用 --oneline 可以将参数简化成一行。
git log --name-status --oneline
每次手动加上参数很麻烦,可以通过自定义快捷命令的方式来简化操作:
git config --global alias.ls 'log --name-status --oneline --graph'
运行以上配置后,可通过 git ls 命令来实现「自定义 git log」效果,通过该方法也可以创建 git st 、git ci 等一系列命令,以便沿用 svn 命令行习惯。
git config --global alias.st 'status --porcelain'
更多 git log 参数,可通过 git help log 查看手册。
如果是看上一次提交的版本日志,直接运行 git show 即可。
参考资料:
1.https://mp.weixin.qq.com/s/Km5KuXPETvG0wCGHrvj9Vg
2.https://www.jianshu.com/p/93318220cdce
3.https://www.cnblogs.com/itech/p/5188942.html
4.https://baike.baidu.com/item/GIT/12647237

浙公网安备 33010602011771号