GIT (分布式版本控制系统)
简介
Git(读音为/gɪt/。)是一个开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理。
Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。
什么是Git,Git可以用来干嘛?
GIt是什么?
Git是目前世界上最先进的分布式版本控制系统
Git可以用来干嘛?
如果你用Microsoft Word写过长篇大论,那你一定有这样的经历:
想删除一个段落,又怕将来想恢复找不回来怎么办?有办法,先把当前文件“另存为……”一个新的Word文件,再接着改,改到一定程度,再“另存为……”一个新文件,这样一直改下去,最后你的Word文档就“魂飞魄散”了!
过了一周,你想找回被删除的文字,但是已经记不清删除前保存在哪个文件里了,只好一个一个文件去找,真麻烦。
看着一堆乱七八糟的文件,想保留最新的一个,然后把其他的删掉,又怕哪天会用上,还不敢删,真郁闷。
更要命的是,有些部分需要你的财务同事帮助填写,于是你把文件Copy到U盘里给她(也可能通过Email发送一份给她),然后,你继续修改Word文件。一天后,同事再把Word文件传给你,此时,你必须想想,发给她之后到你收到她的文件期间,你作了哪些改动,得把你的改动和她的部分合并,真困难。
于是你想,如果有一个软件,不但能自动帮我记录每次文件的改动,还可以让同事协作编辑,这样就不用自己管理一堆类似的文件了,也不需要把文件传来传去。如果想查看某次改动,只需要在软件里瞄一眼就可以,岂不是很方便?
这个软件用起来就应该像这个样子,能记录每次文件的改动:
版本 | 用户 | 说明 | 日期 |
---|---|---|---|
1 | 张三 | 删除了软件服务条款5 | 7/12 10:38 |
2 | 张三 | 增加了License人数限制 | 7/12 18:09 |
3 | 李四 | 财务部门调整了合同金额 | 7/13 9:51 |
4 | 张三 | 延长了免费升级周期 | 7/14 15:17 |
Git与SVN的区别
GIT不仅仅是个版本控制系统,它也是个内容管理系统(CMS),工作管理系统等。
如果你是一个具有使用SVN背景的人,你需要做一定的思想转换,来适应GIT提供的一些概念和特征。
Git 与 SVN 区别点:
- GIT是分布式的,SVN不是:这是GIT和其它非分布式的版本控制系统,例如SVN,CVS等,最核心的区别。
- GIT把内容按元数据方式存储,而SVN是按文件:所有的资源控制系统都是把文件的元信息隐藏在一个类似.svn,.cvs等的文件夹里。
- GIT分支和SVN的分支不同:分支在SVN中一点不特别,就是版本库中的另外的一个目录。
- GIT没有一个全局的版本号,而SVN有:目前为止这是跟SVN相比GIT缺少的最大的一个特征。
- GIT的内容完整性要优于SVN:GIT的内容存储使用的是SHA-1哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏。
安装Git
最早Git是在Linux上开发的,很长一段时间内,Git也只能在Linux和Unix系统上跑。不过,慢慢地有人把它移植到了Windows上。现在,Git可以在Linux、Unix、Mac和Windows这几大平台上正常运行了。
Git 各平台安装包下载地址为:http://git-scm.com/downloads
在Linux平台安装Git
Git 的工作需要调用 curl,zlib,openssl,expat,libiconv 等库的代码,所以需要先安装这些依赖工具。
在有 yum 的系统上(比如 Fedora)或者有 apt-get 的系统上(比如 Debian 体系),可以用下面的命令安装:
各 Linux 系统可以使用其安装包管理工具(apt-get、yum 等)进行安装:
Debian/Ubuntu
Debian/Ubuntu Git 安装命令为:
1 | $ apt-get install libcurl4-gnutls-dev libexpat1-dev gettext \ |
Centos/RedHat
如果你使用的系统是 Centos/RedHat 安装命令为:
1 | |
在Mac OS X上安装Git
如果你正在使用Mac做开发,有两种安装Git的方法。
一是安装homebrew,然后通过homebrew安装Git,具体方法请参考homebrew的文档:http://brew.sh/ 。
第二种方法更简单,也是推荐的方法,就是直接从AppStore安装Xcode,Xcode集成了Git,不过默认没有安装,你需要运行Xcode,选择菜单“Xcode”->“Preferences”,在弹出窗口中找到“Downloads”,选择“Command Line Tools”,点“Install”就可以完成安装了。
在Windows上安装Git
在Windows上使用Git,可以从Git官网直接下载安装程序,(网速慢的同学请移步国内镜像),然后按默认选项安装即可。
安装完成后,在开始菜单里找到“Git”->“Git Bash”,蹦出一个类似命令行窗口的东西,就说明Git安装成功!
Git配置
Git 提供了一个叫做 git config 的工具,专门用来配置或读取相应的工作环境变量。
这些环境变量,决定了 Git 在各个环节的具体工作方式和行为。这些变量可以存放在以下三个不同的地方:
- /etc/gitconfig 文件:系统中对所有用户都普遍适用的配置。若使用 git config 时用 –system 选项,读写的就是这个文件。
- ~/.gitconfig 文件:用户目录下的配置文件只适用于该用户。若使用 git config 时用 –global 选项,读写的就是这个文件。
- 当前项目的 Git 目录中的配置文件(也就是工作目录中的 .git/config 文件):这里的配置仅仅针对当前项目有效。每一个级别的配置都会覆盖上层的相同配置,所以 .git/config 里的配置会覆盖 /etc/gitconfig 中的同名变量。
在 Windows 系统上,Git 会找寻用户主目录下的 .gitconfig 文件。主目录即 $HOME 变量指定的目录,一般都是 C:\Documents and Settings\$USER。
此外,Git 还会尝试找寻 /etc/gitconfig 文件,只不过看当初 Git 装在什么目录,就以此作为根目录来定位。
用户信息
配置个人的用户名称和电子邮件地址:
1 | |
因为Git是分布式版本控制系统,所以,每个机器都必须自报家门:你的名字和Email地址。你也许会担心,如果有人故意冒充别人怎么办?这个不必担心,首先我们相信大家都是善良无知的群众,其次,真的有冒充的也是有办法可查的。
如果用了 –global 选项,那么更改的配置文件就是位于你用户主目录下的那个,以后你所有的项目都会默认使用这里配置的用户信息。
如果要在某个特定的项目中使用其他名字或者电邮,只要去掉 –global 选项重新配置即可,新的设定保存在当前项目的 .git/config 文件里。
注意: 用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。
查看配置信息
要检查已有的配置信息,可以使用 git config –list 命令:
1 | |
Git工作流程
一般工作流程如下:
- 克隆 Git 资源作为工作目录。
- 在克隆的资源上添加或修改文件。
- 如果其他人修改了,你可以更新资源。
- 在提交前查看修改。
- 提交修改。
- 在修改完成后,如果发现错误,可以撤回提交并再次修改并提交。
Git工作区、暂存区和版本库
基本概念
- 工作区:就是你在电脑里能看到的目录。
- 暂存区:英文叫stage, 或index。一般存放在 “.git目录下” 下的index文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。
- 版本库:工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。
下面这个图展示了工作区、版本库中的暂存区和版本库之间的关系:
图中左侧为工作区,右侧为版本库。在版本库中标记为 “index” 的区域是暂存区(stage, index),标记为 “master” 的是 master 分支所代表的目录树。
图中我们可以看出此时 “HEAD” 实际是指向 master 分支的一个”游标”。所以图示的命令中出现 HEAD 的地方可以用 master 来替换。
图中的 objects 标识的区域为 Git 的对象库,实际位于 “.git/objects” 目录下,里面包含了创建的各种对象及内容。
当对工作区修改(或新增)的文件执行 “git add” 命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。
当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树。
当执行 “git reset HEAD” 命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。
当执行 “git rm –cached“ 命令时,会直接从暂存区删除文件,工作区则不做出改变。
当执行 “git checkout .” 或者 “git checkout –“ 命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区的改动。
当执行 “git checkout HEAD .” 或者 “git checkout HEAD“ 命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。
Git创建版本库(仓库)
什么是版本库呢?版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
所以,创建一个版本库非常简单,首先,选择一个合适的地方,创建一个空目录:
1 | |
pwd
命令用于显示当前目录,cd
命令用于切换目录,mkdir
用于创建文件夹,在我的Windows上,这个仓库位于E:/Git
.
注意:文件夹应避免使用中文,防止乱码。
让文件夹成为真正的仓库
上面步骤只是创建了一个文件夹,严格意义上并不算是一个Git版本库,so,现在,我们让这个文件夹变成一个真正的仓库。
输入git init
命令把这个目录变成Git可管理的仓库
1 | |
瞬间Git就把仓库建好了,而且告诉你是一个空的仓库(empty Git repository),细心的读者可以发现当前目录下多了一个.git的目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。
如果你没有看到.git目录,那是因为这个目录默认是隐藏的,用ls -ah命令就可以看见。
把文件添加到版本库
首先这里再明确一下,所有的版本控制系统,其实只能跟踪文本文件的改动,比如TXT文件,网页,所有的程序代码等等,Git也不例外。版本控制系统可以告诉你每次的改动,比如在第5行加了一个单词“Linux”,在第8行删了一个单词“Windows”。而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化,只能把二进制文件每次改动串起来,也就是只知道图片从100KB改成了120KB,但到底改了啥,版本控制系统不知道,也没法知道。
不幸的是,Microsoft的Word格式是二进制格式,因此,版本控制系统是没法跟踪Word文件的改动的,前面我们举的例子只是为了演示,如果要真正使用版本控制系统,就要以纯文本方式编写文件。
建议:编辑文本最好使用Notepad++
代替记事本,因为微软自带记事本会在每个文件开头添加0xefbbbf(十六进制)的字符。
现在,我们编写一个readme.txt文件,内容如下:
1 | Hello,Git! |
注意:文件应该放在Git
文件夹里,因为这是一个Git仓库,放到其他地方Git会找不到这个文件。
Git add
通过git add
告诉Git,把文件添加到仓库(暂存区):
1 | |
执行上面的命令,没有任何显示,代表执行成功。
Git commit
通过git commit
告诉Git,把文件提交到仓库(版本库):
1 | |
其中:git commit -m "wrote a readme file"
中的-m "xxx"
代表本次提交的说明。
例如:$ git commit -m "本次提交解决了若干个BUG"
当然,提交说明也可以带表情,具体表情参考Github表情提交指南。
Git 基本操作
获取和创建项目命令
git init
此命令在创建版本库的时候就讲过,git init
命令就是将一个文件夹初始化为一个Git仓库,创建成功后在该文件夹下会生成一个.git
的文件夹.
例如:
1 | |
现在你能看见该仓库中的.git
文件了。
1 | |
注意:这里的ls -a
中的ls
就是列表,清单的意思,在终端就是显示当前目录的所有文件,而-a
的意思是显示隐藏文件。
git clone
使用 git clone
拷贝一个 Git 仓库到本地,让自己能够查看该项目,或者进行修改。
1 | |
url
是你想克隆(复制)的网上仓库。
例如:我们克隆Github上的项目
1 | |
克隆完成后,在当前目录下会生成一个 HTML5_Learning 目录:
1 | |
至此,一个Github上的项目就克隆完成了,你可以尝试修改或者查看。
常用命令
git add
此命令前面就讲过了,这里再举一个例子,这次我们往暂存区添加多个文件:
1 | |
我们分别向暂存区提交了多个语言hello world
程序源文件,touch
代表我们新建这几个文件,git status
的意思是查看当前暂存区的状态。
扩展: 通过git add .
可以将该文件夹的东西都添加到暂存区。
git status
git status
用于查看在你上次提交之后是否有修改,便于查看当前暂存区的状态,通过增加-s
参数可以获得简短的输出结果,如果没有加该参数将会得到详细的输出。
例如:
1 | |
补充说明:A
的意思是文件添加到了暂存区,而AM
的意思是文件添加到暂存区后在工作区又有所改动。
git diff
执行 git diff 来查看执行 git status 的结果的详细信息。
git diff 命令显示已写入缓存与已修改但尚未写入缓存的改动的区别。git diff 有如下几个主要的应用场景。
- 尚未缓存的改动(工作区):git diff
- 查看已缓存的改动(暂存区): git diff –cached
- 查看已缓存的与未缓存的所有改动(工作区与暂存区):git diff HEAD
- 显示摘要而非整个 diff:git diff –stat
例如我们利用notepad++
编辑器修改hello.c
文件内容如下:
1 | |
现在我们使用git diff file
命令来看看结果:
1 | |
果然,git将整个文件的变化都列了出来。
扩展:diff
就是单词different
的缩写,其原意是“区别,不同”。
git commit
此命令在讲述创建版本库的时候就介绍过,主要用于将暂存区的东西提交到版本库中。
例如:
1 | |
我们将暂存区的内容提交到版本库后再次执行git status
命令
1 | |
此时,我们发现,暂存区的内容已经全部提交到版本库中,只有一条工作区内容被修改的记录。
git reset HEAD
git reset HEAD file
命令用于取消已缓存(暂存区)的内容。
可以理解为:撤销暂存区的修改。可单个撤销,也可以全部撤销。
这里,我们先提交hello.c源文件
1 | |
好的,现在hello.c源文件已经进入了暂存区了,那么,如果我不想提交该内容,该怎么解决呢?其实,通过git reset HEAD
命令是可以丢弃掉暂存区里的内容的。
现在,我们来试试吧!
1 | |
果然,暂存区的内容被撤销掉了。
要点:git reset HEAD filename
实现单个或多个文件在暂存区中撤销,git reset HEAD
实现整个暂存区的撤销。
注意:如果你提交暂存区后,在工作区又进行了修改,git reset HEAD
命令并不会让你在工作区的内容回退到上一次提交的数据,该只是单纯的丢弃掉暂存区里的内容。
git checkout – file
git checkout --file
命令用于取消未缓存的内容(工作区)。
可以理解为:丢弃工作区的修改,回到上次修改的样子。
那么,我们现在来试试
1 | |
在这里,我们使用git status
命令可以看到该文件只是在工作区进行了修改,接着我们使用了cat
命令查看了文件内容,发现里面是C语言源代码,然后我们使用了git checkout -- hello.c
命令丢弃了本次修改。等我们再次使用cat
命令查看hello.c内容时发现里面已经没有内容了,这是为什么呢?
原来这里的git checkout -- <file>
命令的结果分为了两种情况:
- 当文件在工作区修改后还没有被添加到暂存区,此时使用该命令,会使工作区回到和版本库一模一样的状态。
- 当文件在工作区修改后被添加到了暂存区,此时使用该命令,会使工作区回到把文件添加到暂存区后的状态
那么,这里就是第一种状态了,因为我们并没有提交到暂存区,而版本库的hello.c
里面恰恰是没有内容的。(因为当时并没有写内容)
git rm
如果只是简单地从工作目录中手工删除文件,运行 git status
时就会在 Changes not staged for commit
的提示。
要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除,然后提交。可以用以下命令完成此项工作
1 | |
如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项 -f
1 | |
如果把文件从暂存区域移除,但仍然希望保留在当前工作目录中,换句话说,仅是从跟踪清单中删除,使用 –cached 选项即可
1 | |
我们先在hello.py
增加如下代码
1 | print("hello") |
然后,我们将文件添加到暂存区后,在暂存区中删除hello.py
文件
1 | |
可以看到,我们在暂存区移除了该文件,现在,我们将工作区的文件也移除掉。
1 | |
至此,基本命令到此结束。
Git查看提交历史
git log
在使用 Git 提交了若干更新之后,又或者克隆了某个项目,想回顾下提交历史,我们可以使用 git log
命令查看。
1 | |
git log –oneline
git log --oneline
命令用来查看历史记录的简洁的版本。
1 | |
git log –graph
git log --graph
命令,用于查看历史中什么时候出现了分支、合并。以下为相同的命令,开启了拓扑图选项:
1 | |
git log –author
git log --author
命令用于查看指定用户的提交日志。
示例如下:
1 | |
Git版本回退
早在前面就说过Git能干什么的特性.
你可以理解为Git就是一个时空穿梭机,能够回退版本和前进版本。
现在,我们来修改一下readme.txt
的内容。
1 | |
扩展:echo " " >> file
是一个批处理命令,用于清空文件内容后,将” “里的内容写入到文件中。
我们尝试提交一下
1 | |
我们现在来回忆一下我们一共提交了哪几个版本的readme.txt
文件。
版本一:
hello,Git
版本二:
HuiProgramer is handsome
记不住也没事,我们可以通过git log
命令来查看:
1 | |
可以看到第一次提交就是开始介绍GIt仓库时写的readme.txt
,里面的内容是hello,Git
.
而最近一次提交就是我们重新修改了readme.txt
后提交的,里面的内容是HuiProgramer is handsome
(表脸).
那么,如果我想要回到上一个版本该怎么办呢?
其实很简单只需要执行git reset --hard HEAD^
就行了,其中^
代表上一个版本,^^
代表上上个版本,以此类推。
1 | |
Look,我们已经回到了上一个版本了,现在我们打开readme.txt
文件看看。
hello,Git
哇,果然是这样,但我又如何回去呢?就像我们现在坐着时空穿梭机回到了过去,应该怎么回到现在呢?
其实很简单,我们可以通过上次执行git log
命令拿到的commit
后面的版本号再次穿梭就可以回来了。
1 | |
注意:版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。
再次查看readme.txt
的内容
HuiProgramer is handsome
嗯,不错,内容全回来了,还是一如既往的表脸。
扩展:假如我关闭了git bash
这个终端,然后又回退了版本,输入git log
又不显示,该怎么办呢?其实很简单,你只要输入git reflog
命令就能再次看到你的提交记录了。
1 | |
Git与Github
什么是Github
GitHub是一个面向开源及私有软件项目的托管平台,因为只支持git 作为唯一的版本库格式进行托管,故名gitHub。
简单来说就是一个网上的仓库,而Git就是本地的仓库。
拥有自己的Github
注册Github账号
点击这里进行注册。
注册完成后,登录…
创建自己的项目(仓库/版本库)
点击+号进行创建
填写项目名字,点击完成即可。
完成后,会得到项目的提交地址。
创建SSH Key
打开Git Bash
终端,输入:
1 | |
你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个Key
也不是用于军事目的,所以也无需设置密码。
如果一切顺利的话,可以在用户主目录里找到.ssh
目录,里面有id_rsa
和id_rsa.pub
两个文件,这两个就是SSH Key
的秘钥对,id_rsa
是私钥,不能泄露出去,id_rsa.pub
是公钥,可以放心地告诉任何人。
绑定SSH Key
进入Github,点击头像,点击Settings
。
点击SSH and GPG Keys
点击New SSH key
可以看到此页面Title
可以随便取一个,Key
可以去C:\Users\Administrator\.ssh
下找到一个id_rsa.pub
的文件,双击打开后复制里面的全部内容粘贴到Key
下的框框里,然后点击Add SSH Key
.
提交项目到Github
git remote
通过git remote add origin git@github.com:YourName/RepositoryName.git
即可建立一个与当前分支关联的提交名字。
1 | |
解析:YourName
为你的Github账户名,RepositoryName
为你创建的项目名。
注意:这里的origin
可以任你更改,前提你得记住,后面提交要用到。
git push
通过git push -u origin master
命令可将本地项目推送到Github的仓库中。
1 | $ git push -u Golang master |
Look,我们已经成功提交本地版本库到网上仓库了。
由于远程库是空的,我们第一次推送master
分支时,加上了-u
参数,Git
不但会把本地的master
分支内容推送的远程新的master
分支,还会把本地的master
分支和远程的master
分支关联起来,在以后的推送或者拉取时就可以简化命令。
现在,我们打开我们的远程仓库看看,是否提交成功。
嗯,不错,成功了,以后就这样提交。
git pull
通过git pull origin master
可将网上仓库拉取到本地来完成同步。
1 | |
现在就会看到,本地项目和网上仓库一模一样了。
Git分支管理
几乎每一种版本控制系统都以某种形式支持分支。使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作。
有人把 Git 的分支模型称为”必杀技特性”,而正是因为它,将 Git 从版本控制系统家族里区分出来。
分支的创建和切换
创建分支命令:
1 | |
切换分支命令:
1 | git checkout (branchname) |
例如:
1 | |
扩展:git checkout -b (branchname)
命令可以创建并切换新分支,从而实现在该分支中的操作。