版本控制及Git的使用
什么是版本控制
版本控制可以理解为跟踪记录文本文件的历史修改。在开发过程中各种文档,代码等修改都是一步一步进行的。有时会出现需要撤回修改(代码写崩了)、回顾修改历史等需求,将这些修改进行跟踪记录就能实现上述需求。否则靠保存多个副本的方式实现上述效果,将会无比繁琐其难用。同时多人协作开发时,版本控制可以实现一个团队的协同合作。由于版本控制只能对文本的修改进行跟踪管理,所以图片、视频、world等二进制文件只能得到其修改记录,但无法得到知道具体的修改内容,使得无法撤回这类文件上的修改。
而目前有两种主流的版本跟踪系统:
- 一种是以svn为代表集中式的,即所有数据集中存储在一台机器上,工作时需要在该机器上下载数据,修改后再提交到该机器。这种系统在没有网络时无法进行工作,显得其不够便捷。
- 另一种是以git为代表的分布式版本控制系统,这种分布式在每台机器上都保存了数据,使得每台机器都能独自工作,在需要协作时,将各自的修改发生给对方即可,使得分布式版本控制系统更加便捷。当许多人共同协作时,可以划分出一台专门用于分发修改的服务器(如著名的github网站),队员自己从该专门服务器上获取数据,可以更加便捷地分发数据更更多人。
Git使用
Git即支持在本地进行版本控制,也可以通过分享修改进行协作开发的版本控制。
Git重要概念
版本库:一个其所有文件的修改可以被跟踪管理的目录。
如何得到版本库:
1.新建一个目录或者选中一个存在的目录
2.通过命令
git init
赋予该目录跟踪管理修改的能力通过上诉两步,可以得到含有一个.git目录的目录,更加确切的说,.git目录才是版本库。而.git目录以外的为工作区。
- 暂存区:版本库中存储一种重要组成部分,通过暂存区,我们可以批处理式地提交修改,也可以暂时屏蔽当前暂存区,转去做中间的任务,中间任务完成后再回到之前的暂存区进行工作。使得修改的提交更加灵活。只有添加到暂存区的修改才能被提交到版本库。
- 工作区:就是上述的除.git目录外的目录,工作区的文件才能保存到版本库中。同时工作区中还能包括被版本库忽略的文件,如一个c++项目的debug目录。
- head指针:记录版本库当前指向的提交节点的一个指针,通过该指针可以使版本库在各提交节点中来回穿梭,配合各种命令达到切换、恢复工作区文件等效果。即版本库中可以反馈到工作区的内容由head指向控制确定。
- 分支:指向某提交节点的一个指针。一般使用该指针标记跟踪一个提交节点,类似以所跟踪的提交节点为原始状态形成一条新的提交节点链路(即暂时向前标记提交节点,使得原来的提交链路不变,而是否将所提前的提交节点添加到原链路中后面再决定)。给予git更加灵活的版本控制能力,以及更加安全的工作方式(在最重要分支中创建一个分支,在分支上工作,完成后合并分支,然后删除分支)
Git大致使用流程
上图显示了和远程仓库协作的大致流程
- 将其它机器的仓库
git clone
过来(此时得到另外机器上的版本库及存在着由该版本库记录的文件的工作区)或者在本地新建一个仓库。 - 使用checkout创建并切换分支或者branch到已经存在的分支上,然后在工作区工作。
- 对于与远程仓库有交互的情况时。其它机器上的版本库进行了更新,且本地的也有了一些更新(还没有到push的时候),不方便重新克隆,可根据是否直接合并选择pull或者fetch相应文件到本地中。
- 将需要被跟踪记录修改的文件
git add
提交到暂存区 - 将暂存区的修改
git commit -m '提交说明'
提交仓库中使得该修改能被跟踪(记录提交说明说明这这次修改的大体情况,以便于跟踪回顾修改,故一定要写得有意义且不可缺少)。 - 如果需要和其他人共享自己的被版本管理的文件,就可以将自己的版本库push给对方或者专门分发文件的机器。当然,如果远程仓库更新了则需要先pull下来,然后再push上去。
- 合并分支,在分支上的开发完成且代码得到了验证,就可以进行分支合并操作以得到记录被接受代码的分支(master分支或者dev分支)。当然和远程仓库交互时也有合并分支的操作以及相应的冲突处理。
Git重要功能命令
创建仓库
git init
将当前目录创建成一个仓库,即该目录的文件的修改可以被跟踪记录。
仓库状态查看
git status
查看当前仓库状态,如哪些文件被修改了,是否添加到暂存区了。暂存区里面有哪些有待提交的修改。
修改具体内容的查看
git diff
参看某个文本文件修改的具体内容
查看修改提交记录
git log
参看修改提交记录,这里就显示了提交说明的重要性,通过提交说明可以有效地追踪修改提交。git log --graph
可以查看分支合并图以更好地掌握分支情况。
查看commit id
git reflog
可以查看id以及对应的提交说明,以便于后面的版本回溯(包括回到以前,以及回到更近的以前)
版本回溯
git reset --hard id
将head指针指向id所代表的提交,且将工作区相关文件的内容修改成该提交的内容。
撤销工作区的修改
git checkout --文件
将工作区的修改进行撤回(原理就使用版本库的文件内容覆盖掉工作区的文件内容),即将工作区文件内容恢复到前一次提交的状态。注意不要少--
否则就是切换分支的命令。当要撤回的修改已经添加到暂存区时,还需要使用git reset HEAD 文件
消除该文件添加到暂存区的修改,否则提交后使得该文件版本库和工作区的内容不同。如果是工作区的文件被误删除了,也可以使用该命令覆盖工作区该文件内容(不过只能恢复到版本库中(head)指向的内容)
删除版本库的文件
git rm 文件 + git commit
将文件从版本库中删除,使得版本库无该文件的内容且不在跟踪该文件的修改。如果是工作区的该文件也想被删除(否则会显示工作区有未提交的文件,可以现在不管或者用.ignore方式忽略它),则直接在本地删除该文件即可。
创建并切换分支
git checkout -b 分支名
-b标识分支,如果没有-b表示切换(将head指针指向分支所指向的提交节点,但是分支指向的默认值不变)一个分支。git checkout -b 分支名 远程仓库/远程仓库分支名
在本地创建一个和远程仓库对应分支相同的分支。
创建分支
git branch 分支名
创建一个指向head指向提交节点的指针,以该提交节点为最初状态向前标记若干提交节点。
切换分支
git switch 分支名
更加直观的切换分支命令,git checkout
命令容易混淆。
查看分支情况
git branch
显示所有分支及head所指向分支。
快速合并分支(无法看到什么时候发生了合并)
git merge 分支名
将分支名的分支和当前的分支合并。分支所提前标注的提交节点链路加入到head所标注的链路中,head指向分支的指向,即当前分支指向的最新提交节点默认值发生变化。如果在分支切换点以后,原分支向前延伸了提交节点链,此时进行合并时可能发生分支冲突,我们需要手动修改发生冲突的文件的内容。未发生冲突的文件会自行合并。发生冲突时会在最终链条中增加一个解决冲突的提交节点,否则没有,仿佛没有发生合并,一直是在主分支上进行修改提交。
非快速合并分支
git merge --no-ff -m '提交说明' 分支名
这种合并方式会形成一个新的提交,故需要提交说明,这时无论发生冲突与否,都会有一个新提交产生,这样就能看到什么时候发生了合并。
删除分支
git branch -d 分支名
将分支(即将该指针)删除掉。即分支所标示的提交节点链路要么被合并使用,要么被舍弃,后续无法访问该链路上的提交节点。当不运行直接删除时(如分支未合并时),可用-D进行强制删除分支。
隐藏工作现场
有时你当前工作还没有完成,故无法提交,此时需要中断当前工作进行其它工作,此时使用git stash
进行当前暂存区和工作区的隐藏,此时仿佛你之前的工作没有进行过,可以放心进行中间工作。等中间工作完成后,使用git stash list
查看之前隐藏内容,然后使用git stash pop stash@{stash id号}
(这时隐藏缓存会被清空,如果使用git stash apply@{stash id号}
只恢复不清空,还需要使用git stash drop@{stash id号}
进行清空)进行原场景的恢复,以便继续之前被中断的工作。
复制提交到当前分支
git cherry-pick <commit>
将修改提交复制到当前分支,一般只在单个提交需要复制到当前分支时使用(如主分支中bug被修改了,此时从bug修复前创建出的分支都有该bug,故需要将bug提交进行复制,以保证分支中代码的正确)。
克隆远程仓库
git clone
可以将其他机器上的仓库复制到本地中,以达到备份或者协作开发的目的。此时只有远程仓库的master分支被复制到本地,且将远程和本地仓库的master分支间建立了绑定关系,如果要复制远程仓库的其它分支要使用git clone 远程仓库名\分支名
。
推送当前分支远程仓库
git push
将当前分支推送到远程仓库中,以达到备份或者协作开发的目的。第一次推送时使用命令git push -u
不但可以推送分支,还能建立远程仓库和本地仓库上分支的联系关系以便后面简化push命令。
解除本地和远程仓库分支间的绑定关系
git remote rm
只是删除分支间的绑定关系,即后面push或fetch时需要指定分支关系,命令相对没有那么简洁。如果需要真正地删除远程的仓库,需要去远程机器上操作。
查看远程仓库信息
git remote -v
显示可以full或fetch的远程仓库的信息,包括地址,名字等。
获取远程仓库更新到本地
git pull
将远程仓库某分支的更新提交复制到本地上,并进行分支合并,这种命令在协作开发时经常用到,其需要使用git branch --set-upstream-to 本地仓库分支名 远程仓库名/远程仓库分支名
进行远程仓库和本地仓库的分支间的关系绑定,如果合并时出现冲突,需要解决冲突然后提交。git fetch
只获取更新而不自动合并,是否合并靠自己在本地操作。
展直提交历史
git rebase
将pull命令后的提交记录线修整成一条直线,以防止提交记录显得异常杂乱而不便于使用。
标签标记提交记录
git tag name
可以给当前分支最新提交打上标签,即一个别名,以更加直观的形式标记提交。git tag name commit-id
给给定id的提交打上标签。git show 标签名
可以查看标签名所标识的提交的信息。git tag -a 标签名 -m '说明'
可以给指定标签写上说明,使用git show 标签名
查看时就能看到该说明。git tag -d 标签名
可以删除标签。git tag
可以显示所有标签名。git push 远程仓库名 标签名
可以推送标签到远程仓库。git push 远程仓库名 --tags
推送所有未推送标签至远程仓库中。git pushi 远程仓库名:refs/tags/标签名
删除远程仓库相应标签。
Git常见工作模式
只本地使用
可以借助git实现修改的记录,以便随时回溯版本。能防止工作目录文件的误删除。还能协助开发,以防止某些实验性代码污染以前的工作成果。
其工作模式为:
将项目目录进行git管理==》开发时创建分支,在分支上工作==》出现相应错误时进行处理。
示例:
我这里有一个完成部分功能的网盘项目,现需要使用git进行本地管理。其流程如下:
- 进入我的代码所在目录:F:\Data\Qt_5.9.8\Cloud
- 在代码所在目录执行
git init
命令使得此目录被git管理 - 由于有些文件不需要被跟踪,如本项目的.pro.user文件。可在.gitignore文件中指定目录中被git忽略的文件。windows下,目录中如果没有.gitignore文件,就创建一个txt文本,然后将其连同后缀名改为.gitignore即可。本项目的初始.gitignore文件内容如下:
- 使用
git add
命令将需要被git管理的文件提交到暂存区。本项目的执行初始add命令后的内容(使用git status
查看) - 使用
git commit -m "实现了客户端和服务器端的通讯,各拓展方式已被设计好"
进行第一次的修改提交,便使得该项目被git管理。执行命令后的内容(使用git log
查看): - 后续如果发现上述文件被污染了,或者被误删除了,可以使用git将其恢复到此时被提交的各文件的内容。以达到代码备份以及回溯代码的效果。
- 后续开发时,我将创建一个分支,并在分支上进行工作,直到某个功能被实现且代码被验证后,进行分支合并操作,以更有效地进行代码开发工作。
与他人协作开发
可以借助git更加方便地进行协作,且各修改可记录跟踪,易于管理。
其工作模式为:
创建一个共享仓库==》参与协作开发人员进行克隆或者推送==》参与人员在自己本地创建分支并在分支上开发==》推送自己的提交到远程库或者拉取远程库的最新提交。
示例
本地使用的示例中得到的代码仓库,我想进行进一步的备份,想实现开源,有时我可能在另外的地方换了一台电脑进行开发,同时我也想请求其他人帮我开发部分功能。如何达到上述等需求呢。可以将上述仓库推送到一个 远程仓库中。
本示例选用的远程仓库是gitee网站维护的,其示例如下:
- 在gitee中创建一个仓库:Cloud: 一个可拓展的网盘 (gitee.com)
- 使用
git remote add gitee/Cloud git@gitee.com:xianghanfengtrue/cloud
实现本地仓库关联一个名为gitee/Cloud的远程仓库。使得后续可以使用git push gitee/Cloud 分支名
或者git pull gitee/Cloud 分支名
等命令实现本地仓库分支和远程仓库的分支的同步。执行命令后的效果(使用git remote -v
查看): - 使用
git push gitee/Cloud master
将此时本地仓库中master分支中的修改提交推送到远程仓库中,以达到备份、开源、协作开发等目的。推送前远程仓库内容:推送后远程仓库内容:
- 此时远程仓库就有我代码的备份了。即使我的机器发生了毁灭性的故障,也能得到远程仓库中的项目内容。
- 如果我换了一台机器继续开发,可以在另一台机器使用
git clone git@gitee.com:xianghanfengtrue/cloud
命令将该远程仓库进行克隆。然后在该机器上创建分支进行开发,并在适当的时机合并分支,然后将该机器上的代码push到远程仓库中。当我返回后,可以将远程仓管的代码pull下来。至此我便获得了在另一台机器上的开发成果。邀请别人帮忙开发时大致流程也如上所述。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。