假设你在的公司要上线一个新功能,你们开发团队为实现这个新功能,写了大约5000行代码,上线没2天,就发现这个功能用户并不喜欢,你老板让你去掉这个功能,你怎么办?你说简单,直接把5000行代码去掉就行了,但是我的亲,说的简单,你的这个功能写了3周时间,但你还能记得你是新增加了哪5000行代码么?所以你急需要一个工具,能帮你记录每次对代码做了哪些修改,并且可以轻易的把代码回滚到历史上的某个状态。 这个神奇的工具就叫做版本控制。
版本控制工具主要实现2个功能:
在开发中,这是刚需,必须允许可以很容易对产品的版本进行任意回滚,版本控制工具实现这个功能的原理简单来讲,就是你每修改一次代码,它就帮你做一次快照
一个复杂点的软件,往往不是一个开发人员可以搞定的,公司为加快产品开发速度,会招聘一堆跟你一样的开发人员开发这个产品,拿微信来举例,现在假设3个人一起开发微信,A开发联系人功能,B开发发文字、图片、语音通讯功能,C开发视频通话功能, B和C的功能都是要基于通讯录的,你说简单,直接把A开发的代码copy过来,在它的基础上开发就好了,可以,但是你在他的代码基础上开发了2周后,这期间A没闲着,对通讯录代码作了更新,此时怎么办?你和他的代码不一致了,此时我们知道,你肯定要再把A的新代码拿过来替换掉你手上的旧通讯录功能代码, 现在人少,3个人之间沟通很简单,但想想,如果团队变成30个人呢?来回这样copy代码,很快就乱了, 所以此时亟需一个工具,能确保一直存储最新的代码库,所有人的代码应该和最新的代码库保持一致
1、VSS-- Visual Source Safe
此工具是Microsoft提供的,是使用的相当普遍的工具之一,他可以与VS.net进行无缝集成,成为了独立开发人员和小型开发团队所适合的工具,基本上Window平台上开发的中小型企业,当规模较大后,其性能通常是无法忍受的,对分支与并行开发支持的比较有限。
2、CVS--Concurrent Versions System,
此工具是一个开源工具,与后面提到的SVN是同一个厂家:Collab.Net提供的。
CVS是源于unix的版本控制工具,对于CVS的安装和使用最好对unix的系统有所了解能更容易学习,CVS的服务器管理需要进行各种命令行操作。目前,CVS的客户端有winCVS的图形化界面,服务器端也有CVSNT的版本,易用性正在提高。
此工具是相当著名,使用得相当广泛的版本控制工具之一,使用成熟的“Copy-Modify-Merge"开发模型,可以大大的提高开发效率,适合于项目比较大,产品发布频繁,分支活动频繁的中大型项目。
3、SVN --CollabNet Subversion
此工具是在CVS 的基础上,由CollabNet提供开发的,也是开源工具,应用比较广泛。
他修正cvs的一些局限性,适用范围同cvs,目前有一些基于SVN的第三方工具,如TortoiseSVN,是其客户端程序,使用的也相当广泛。在权限管理,分支合并等方面做的很出色,他可以与Apache集成在一起进行用户认证。
不过在权限管理方面目前还没有个很好用的界面化工具,SVNManger对于已经使用SVN进行配置的项目来说,基本上是无法应用的,但对于从头开始的项目是可以的,功能比较强大,但是搭建svnManger比较麻烦。
是一个跨平台的软件,支持大多数常见的操作系统。作为一个开源的版本控制系统,Subversion 管理着随时间改变的数据。 这些数据放置在一个中央资料档案库中。 这个档案库很像一个普通的文件服务器, 不过它会记住每一次文件的变动。 这样你就可以把档案恢复到旧的版本, 或是浏览文件的变动历史。Subversion 是一个通用的系统, 可用来管理任何类型的文件, 其中包括了程序源码。
4. GIT
因为最初是从Linux起家的,非常依赖文件系统的一些特性,这些在 Linux 下表现的很好,而 Windows 下特别糟糕Git 中文教程
Git是一个开源的分布式版本控制系统,用以有效、高速的处理从很小到非常大的项目版本管理.
Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。
Torvalds 开始着手开发 Git 是为了作为一种过渡方案来替代 BitKeeper,后者之前一直是 Linux 内核开发人员在全球使用的主要源代码工具。开放源码社区中的有些人觉得 BitKeeper 的许可证并不适合开放源码社区的工作,因此 Torvalds 决定着手研究许可证更为灵活的版本控制系统。尽管最初 Git 的开发是为了辅助 Linux 内核开发的过程,但是我们已经发现在很多其他自由软件项目中也使用了 Git。例如 最近就迁移到 Git 上来了,很多 Freedesktop 的项目也迁移到了 Git 上。
5、BitKeeper
是由BitMover公司提供的,BitKeeper自称是“分布式”可扩缩SCM系统。
不是采用C/S结构,而是采用P2P结构来实现的,同样支持变更任务,所有变更集的操作都是原子的,与svn,cvs一致。
很多人都知道,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网站上线了(github是一个基于git的代码托管平台,付费用户可以建私人仓库,我们一般的免费用户只能使用公共仓库,也就是代码要公开。),它为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub,包括jQuery,PHP,Ruby等等。
历史就是这么偶然,如果不是当年BitMover公司威胁Linux社区,可能现在我们就没有免费而超级好用的Git了。
今天,GitHub已是:
官网:https://git-scm.com/downloads
什么是版本库呢?版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
所以,创建一个版本库非常简单,首先,选择一个合适的地方,创建一个空目录:
shuais-MBP:~ dandyzhang$ mkdir git_test shuais-MBP:~ dandyzhang$ cd git_test shuais-MBP:git_test dandyzhang$ git init Initialized empty Git repository in /Users/dandyzhang/git_test/.git/
瞬间Git就把仓库建好了,而且告诉你是一个空的仓库(empty Git repository),细心的读者可以发现当前目录下多了一个.git
的目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。
如果你没有看到.git
目录,那是因为这个目录默认是隐藏的,用ls -ah
命令就可以看见。
shuais-MBP:git_test dandyzhang$ ls -ah . .. .git
首先这里再明确一下,所有的版本控制系统,其实只能跟踪文本文件的改动,比如TXT文件,网页,所有的程序代码等等,Git也不例外。版本控制系统可以告诉你每次的改动,比如在第5行加了一个单词“Linux”,在第8行删了一个单词“Windows”。而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化,只能把二进制文件每次改动串起来,也就是只知道图片从100KB改成了120KB,但到底改了啥,版本控制系统不知道,也没法知道。
不幸的是,Microsoft的Word格式是二进制格式,因此,版本控制系统是没法跟踪Word文件的改动的,前面我们举的例子只是为了演示,如果要真正使用版本控制系统,就要以纯文本方式编写文件。
因为文本是有编码的,比如中文有常用的GBK编码,日文有Shift_JIS编码,如果没有历史遗留问题,强烈建议使用标准的UTF-8编码,所有语言使用同一种编码,既没有冲突,又被所有平台所支持。
言归正传,现在我们编写一个first_git_file.txt文件,内容如下:
shuais-MBP:git_test dandyzhang$ vim first_git_file.txt first time using git, hello git!
一定要放到git_trainning目录下(子目录也行),因为这是一个Git仓库,放到其他地方Git再厉害也找不到这个文件。
和把大象放到冰箱需要3步相比,把一个文件放到Git仓库只需要两步。
第一步,用命令git add
告诉Git,把文件添加到仓库:
shuais-MBP:git_test dandyzhang$ git add first_git_file.txt
执行上面的命令,没有任何显示,说明添加成功。
这时候我们再创建一个文件readme,不add到仓库
shuais-MBP:git_test dandyzhang$ vim Readme this is readme file
此时使用status查看状态:
shuais-MBP:git_test dandyzhang$ git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: first_git_file.txt Untracked files: (use "git add <file>..." to include in what will be committed) Readme
add后的文件first_git_file.txt被追踪了,而没有添加进git仓库的则不会追踪,空文件夹也不会显示。
第二步,用命令git commit
告诉Git,把文件提交到仓库,即上文被追踪的文件已经被管理:
shuais-MBP:git_test dandyzhang$ git commit hint: Waiting for your editor to close the file... # Please enter the commit message for your changes. Lines starting # with ‘#‘ will be ignored, and an empty message aborts the commit. # # Committer: 衰 Zzz <dandyzhang@shuais-MBP.lan> # # On branch master # # Initial commit # # Changes to be committed: # new file: first_git_file.txt # # Untracked files: # Readme # ~ ~ ~ ~ ~ ~ ~ "~/git_test/.git/COMMIT_EDITMSG" 16L, 321C
这里又进入到vim编辑模式,git在提示你写入文件新建或更改的理由,原因或备注,加入备注在首行:
git first test... # Please enter the commit message for your changes. Lines starting # with ‘#‘ will be ignored, and an empty message aborts the commit. # # Committer: 衰 Zzz <dandyzhang@shuais-MBP.lan> # # On branch master # # Initial commit # # Changes to be committed: # new file: first_git_file.txt # # Untracked files: # Readme #
保存:
shuais-MBP:git_test dandyzhang$ git commit [master (root-commit) 8dfa740] git first test... Committer: 衰 Zzz <dandyzhang@shuais-MBP.lan> Your name and email address were configured automatically based on your username and hostname. Please check that they are accurate. You can suppress this message by setting them explicitly. Run the following command and follow the instructions in your editor to edit your configuration file: git config --global --edit After doing this, you may fix the identity used for this commit with: git commit --amend --reset-author 1 file changed, 1 insertion(+) create mode 100644 first_git_file.txt
此时查看status:
shuais-MBP:git_test dandyzhang$ git status On branch master Untracked files: (use "git add <file>..." to include in what will be committed) Readme nothing added to commit but untracked files present (use "git add" to track)
发现只有readme存在。
中间红色部分的意思是,你在往git库里提交代码时,你需要告诉git你是谁,这样git就会纪录下来是谁改的代码,其实就是为了日后查询方便,你只需要提供一个名字和邮件地址就可以,这里我的git直接通过主机名自己创建了一个,但你可以通过git config --global --edit修改
简单解释一下git commit
命令,-m
后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录。
嫌麻烦不想输入-m "xxx"
行不行?确实有办法可以这么干,但是强烈不建议你这么干,因为输入说明对自己对别人阅读都很重要。
为什么Git添加文件需要add
,commit
一共两步呢?因为commit
可以一次提交很多文件,所以你可以多次add
不同的文件,相当于修改了项目的多个地方的代码最后统一commit,比如:
$ git add file1.txt $ git add file2.txt file3.txt $ git commit -m "add 3 files."
我们已经成功地添加并提交了一个first_git_file.txt文件,现在,是时候继续工作了,于是,我们继续修改first_git_file.txt文件,改成如下内容:
shuais-MBP:git_test dandyzhang$ vim first_git_file.txt first time using git, hello git! update(one) insert one line here....
现在,运行git status
命令看看结果:
shuais-MBP:git_test dandyzhang$ git status On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: first_git_file.txt Untracked files: (use "git add <file>..." to include in what will be committed) Readme no changes added to commit (use "git add" and/or "git commit -a")
虽然Git告诉我们first_git_file.txt被修改了,但如果能看看具体修改了什么内容,自然是很好的。比如你休假两周从国外回来,第一天上班时,已经记不清上次怎么修改的readme.txt,所以,需要用git diff
这个命令看看:
shuais-MBP:git_test dandyzhang$ git diff first_git_file.txt diff --git a/first_git_file.txt b/first_git_file.txt index 5f48045..7581c01 100644 --- a/first_git_file.txt +++ b/first_git_file.txt @@ -1 +1,2 @@ -first time using git, hello git! +first time using git, hello git! update(one) +insert one line here....
输出中+号绿色显示的就是修改或新增的内容,-号红色显示的就是去掉或被修改的内容
知道了对first_git_file.txt 作了什么修改后,再把它提交到仓库就放心多了,提交修改和提交新文件是一样的两步,第一步是git add:
shuais-MBP:git_test dandyzhang$ git add . # 代表把当前目录下所有改动的文件都提交到代码库 shuais-MBP:git_test dandyzhang$ git commit -m ‘commit changes 2‘ [master f31af7b] commit changes 2 Committer: 衰 Zzz <dandyzhang@shuais-MBP.lan> Your name and email address were configured automatically based on your username and hostname. Please check that they are accurate. You can suppress this message by setting them explicitly. Run the following command and follow the instructions in your editor to edit your configuration file: git config --global --edit After doing this, you may fix the identity used for this commit with: git commit --amend --reset-author 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 Readme shuais-MBP:git_test dandyzhang$
如果上面的操作,add之后我们插入一个status操作,会得到提示,一个文件被修改另一个未被跟踪。
commit之后再查看status:
shuais-MBP:git_test dandyzhang$ git status
On branch master
nothing to commit, working tree clean
shuais-MBP:git_test dandyzhang$
这时本地文件状态就全部被清空了。Git告诉我们当前没有需要提交的修改,而且,工作目录是干净(working directory clean)的。
此时,再修改下readme:
shuais-MBP:git_test dandyzhang$ vim Readme this is readme file insert one line...second times. shuais-MBP:git_test dandyzhang$ git add . shuais-MBP:git_test dandyzhang$ git commit -m ‘second time modified readme.‘ [master f557e4f] second time modified readme. Committer: 衰 Zzz <dandyzhang@shuais-MBP.lan> Your name and email address were configured automatically based on your username and hostname. Please check that they are accurate. You can suppress this message by setting them explicitly. Run the following command and follow the instructions in your editor to edit your configuration file: git config --global --edit After doing this, you may fix the identity used for this commit with: git commit --amend --reset-author 1 file changed, 1 insertion(+)
上面已经又修改过文件了,有兴趣的话可以多测试几次
像这样,你不断对文件进行修改,然后不断提交修改到版本库里,就好比玩RPG游戏时,每通过一关就会自动把游戏状态存盘,如果某一关没过去,你还可以选择读取前一关的状态。有些时候,在打Boss之前,你会手动存盘,以便万一打Boss失败了,可以从最近的地方重新开始。Git也是一样,每当你觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在Git中被称为commit
。一旦你把文件改乱了,或者误删了文件,还可以从最近的一个commit
恢复,然后继续工作,而不是把几个月的工作成果全部丢失。
现在,测试过几次之后,大家应该也不会很清晰的记得文件的修改情况和版本明细了。并且在实际工作中,我们脑子里怎么也不可能记得一个几千行的文件每次都改了什么内容,不然要版本控制系统干什么。版本控制系统肯定有某个命令可以告诉我们历史记录,在Git中,我们用git log
命令查看:
shuais-MBP:git_test dandyzhang$ git log commit f557e4fcd45978b358f21b75e87cfe2f80f381b7 (HEAD -> master) Author: 衰 Zzz <dandyzhang@shuais-MBP.lan> Date: Wed Jul 4 22:59:02 2018 +0800 second time modified readme. commit f31af7bf79a2b0114eb9d1adcac82b4fa7128a2b Author: 衰 Zzz <dandyzhang@shuais-MBP.lan> Date: Wed Jul 4 22:50:20 2018 +0800 commit changes 2 commit 8dfa74052ee22ef3f86b51424172df60c23778c6 Author: 衰 Zzz <dandyzhang@shuais-MBP.lan> Date: Wed Jul 4 22:27:56 2018 +0800 git first test...
git log
命令显示从最近到最远的提交日志,可以看到日志一共记录了,第一次测试跟两次修改;如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline
参数:
shuais-MBP:git_test dandyzhang$ git log --pretty=oneline f557e4fcd45978b358f21b75e87cfe2f80f381b7 (HEAD -> master) second time modified readme. f31af7bf79a2b0114eb9d1adcac82b4fa7128a2b commit changes 2 8dfa74052ee22ef3f86b51424172df60c23778c6 git first test...
需要友情提示的是,你看到的一大串类似3628164...882e1e0
的是commit id
(版本号),和SVN不一样,Git的commit id
不是1,2,3……递增的数字,而是一个SHA1计算出来的一个非常大的数字,用十六进制表示,而且你看到的commit id
和我的肯定不一样,以你自己的为准。为什么commit id
需要用这么一大串数字表示呢?因为Git是分布式的版本控制系统,后面我们还要研究多人在同一个版本库里工作,如果大家都用1,2,3……作为版本号,那肯定就冲突了。
好了,现在我们启动时光穿梭机,准备把readme回退到上一个版本,即‘commit changes 2’版本,怎么做呢?
首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD
表示当前版本,也就是最新的提交f557e4fcd45978b358f21b75e87cfe2f80f381b7(注意我的提交ID和你的肯定不一样),上一个版本就是HEAD^
,上上一个版本就是HEAD^^
,当然往上100个版本写100个^
比较容易数不过来,所以写成HEAD~100
。
小插曲:
写的太忘我,忘记添加global user.mail&user.name参数。目前,以我的测试,如果不设置话,文件是不会回滚的。 为什么设置? 很大程度上,一方面日志里记录了,更新的用户的名字邮箱,如果遇到问题或者不理解的地方,可以及时联系开发者。
所以,别激动,先添加参数,添加好再修改一次文件:
shuais-MBP:git_test dandyzhang$ git config --global user.email ‘wuzdandz@163.com‘
shuais-MBP:git_test dandyzhang$ git config --global user.name ‘dandy zhang‘
shuais-MBP:git_test dandyzhang$ vim Readme # 这里就不再费劲记录了,随便加点东西就好了。
shuais-MBP:git_test dandyzhang$ git add .
shuais-MBP:git_test dandyzhang$ git commit # 忘记加备注了。。。。
[master b958914] update readme third times
1 file changed, 1 insertion(+)
这时候查看日志:
现在,我们要把当前版本“update readme third times”回退到上一个版本“second time modified readme.”,就可以使用git reset
命令:
shuais-MBP:git_test dandyzhang$ git reset --hard HEAD^
HEAD is now at f557e4f second time modified readme.
shuais-MBP:git_test dandyzhang$ vim Readme
this is readme file
insert one line...second times.
完美!
这时候,也许你会重新查看log:
shuais-MBP:git_test dandyzhang$ git log commit f557e4fcd45978b358f21b75e87cfe2f80f381b7 (HEAD -> master) Author: 衰 Zzz <dandyzhang@shuais-MBP.lan> Date: Wed Jul 4 22:59:02 2018 +0800 second time modified readme. commit f31af7bf79a2b0114eb9d1adcac82b4fa7128a2b Author: 衰 Zzz <dandyzhang@shuais-MBP.lan> Date: Wed Jul 4 22:50:20 2018 +0800 commit changes 2 commit 8dfa74052ee22ef3f86b51424172df60c23778c6 Author: 衰 Zzz <dandyzhang@shuais-MBP.lan> Date: Wed Jul 4 22:27:56 2018 +0800 git first test...
发现最新的一版已经不见了。假如是真的要回滚到对应的版本倒是还好,可是,如果是没注意选错了,就没办法更改了么?
办法其实还是有的,只要上面的命令行窗口还没有被关掉,你就可以顺着往上找啊找啊,找到那个add new content的commit id是
b9589146dd0eeb4f6b6eb5347e7cb373a1c55533,于是就可以指定回到未来的某个版本:
shuais-MBP:git_test dandyzhang$ git reset --hard b95891
HEAD is now at b958914 update readme third times
版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。
这时候再,小心翼翼!!的查看一下readme的内容:
shuais-MBP:git_test dandyzhang$ vim Readme
this is readme file
insert one line...second times.
insert one line...third time.
发现版本真的改回来了。
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD
指针,当你回退版本的时候,Git仅仅是把HEAD从指向update readme third times
现在,你总算是可以回退到了某个版本,开心的一毛,关掉了电脑,第二天早上就后悔了,想恢复到新版本怎么办?找不到新版本的commit id怎么办?
在Git中,总是有后悔药可以吃的。当你用$ git reset --hard HEAD^回退到update again版本时,再想恢复到最新add new content的版本,就必须找到add new contentL的commit id。Git提供了一个命令git reflog用来记录你的每一次命令:
shuais-MBP:git_test dandyzhang$ git reflog
b958914 (HEAD -> master) HEAD@{0}: reset: moving to b95891
f557e4f HEAD@{1}: reset: moving to HEAD^
b958914 (HEAD -> master) HEAD@{2}: commit: update readme third times
f557e4f HEAD@{3}: commit: second time modified readme.
f31af7b HEAD@{4}: commit: commit changes 2
8dfa740 HEAD@{5}: commit (initial): git first test...
这里,就不演示再回滚然后拿最前面的ID,再复原的过程了。记得reflog这个命令的作用即可,git的回滚的意义,在于将指针指向你所需要回到的某一个版本,但并不会删掉其他的版本。
也许你还会问这样一个问题,如果回滚代码到过去,再在过去的代码里修改了一些东西,commit之后,后面的版本还存在么?
就像链表一样,链子断开把新的加了进入?还是相当于时光机一般,我回到过去,修改或杀死了小时候的我,未来的我还在么?
其实这里需要解释一下git的版本存储机制:
如图,如果回到二的版本阶段,修改了代码,git会重新定义一个版本。说的恐怖一点,这有点类似于你创造了一个平行宇宙。。
原文:https://www.cnblogs.com/wuzdandz/p/9266139.html