Git简介
git是什么?
Git 是目前世界上最先进的分布式版本控制系统(没有之一)。
什么又是版本控制系统?
举个例子:
如果你用 Microsoft Word 写过长篇大论,那你一定有这样的经历:
想删除一个段落,又怕将来想恢复找不回来怎么办?有办法,先把当前文件“另存为……”一个新的 Word文件,再接着改,改到一定程度,再“另存为……”一个新文件,这样一直改下去,最后你的 Word 文档变成了这样:
过了一周,你想找回被删除的文字,但是已经记不清删除前保存在哪个文件里了,只好一个一个文件去找,真麻烦。看着一堆乱七八糟的文件,想保留最新的一个,然后把其他的删掉,又怕哪天会用上,还不敢删,郁闷不郁闷?
更要命的是,有些部分需要你的财务同事帮助填写,于是你把文件 Copy 到 U 盘里给她(也可能通过Email 发送一份给她),然后,你继续修改 Word 文件。一天后,同事再把 Word 文件传给你,此时,你必须想想,发给她之后到你收到她的文件期间,你作了哪些改动,得把你的改动和她的部分合并,繁琐不繁琐?看到头都大了,于是git就诞生了。
git的演变历程(不要嫌啰嗦,真的很有趣!)
很多人都知道,linux 在 1991 年创建了开源的 Linux,从此,Linux 系统不断发展,已经成为最大的服务器系统软件了。
林纳斯·本纳第克特·托瓦兹虽然创建了Linux,但Linux的壮大是靠全世界热心的志愿者参与的,这么多人在世界各地为Linux编写代码,那 Linux 的代码是如何管理的呢?
事实是,在 2002 年以前,世界各地的志愿者把源代码文件通过 diff 的方式发给 linux,然后由 linux本人通过手工方式合并代码!
你也许会想,为什么 linux 不把 Linux 代码放到版本控制系统里呢?不是有 CVS、SVN 这些免费的版本控制系统吗?因为 linux 坚定地反对 CVS 和 SVN,这些集中式
的版本控制系统不但速度慢
,而且必须联网
才能使用。有一些商用的版本控制系统,虽然比 CVS、SVN 好用,但那是付费
的,和 Linux 的开源精神不符。
不过,到了 2002 年,Linux 系统已经发展了十年了,代码库之大让 linux 很难继续通过手工方式管理了,社区的弟兄们也对这种方式表达了强烈不满,于是 linux 选择了一个商业的版本控制系统 BitKeeper,
BitKeeper 的东家 BitMover 公司出于人道主义精神,授权 Linux 社区免费使用这个版本控制系统。
安定团结的大好局面在 2005 年就被打破了,原因是 Linux 社区牛人聚集,不免沾染了一些梁山好汉的“江湖习气”。开发 Samba 的 Andrew 试图破解 BitKeeper 的协议(这么干的其实也不只他一个),被BitMover 公司发现了(监控工作做得不错!),于是 BitMover 公司怒了,要收回 Linux 社区的免费使用权。
linux 可以向 BitMover 公司道个歉,保证以后严格管教弟兄们,嗯,这是不可能的。实际情况是这样的:
linux 花了两周时间
自己用 C 写了一个分布式版本控制系统
,这就是 Git!一个月
之内,Linux 系统的源码已经由 Git 管理了!牛是怎么定义的呢?大家可以体会一下。
Git 迅速成为最流行的分布式版本控制系统,尤其是 2008 年,GitHub 网站
上线了,它为开源项目免费提供 Git 存储,无数开源项目开始迁移至 GitHub,包括 jQuery,PHP,Ruby 等等。
历史就是这么偶然,如果不是当年 BitMover 公司威胁 Linux 社区,可能现在我们就没有免费而超级好用的 Git 了。
Git分布式版本控制系统的特点:
- 分布式版本控制系统根本没有“中央服务器”
- 工作的时候,不需要联网
- 分布式版本控制系统的安全性要高,冗余性高
- 极其强大的分支管理
- 远程管理
安装git
最早 Git 是在 Linux 上开发的,很长一段时间内,Git 也只能在 Linux 和 Unix 系统上跑。不过,慢慢地有人把它移植到了 Windows 上。现在,Git 可以在 Linux、Unix、Mac 和 Windows 这几大平台上正常运行了。
再linux上安装git
首先,你可以试着输入 git
,看看系统有没有安装 git:
$ git
The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git
像上面的命令,有很多 Linux 会友好地告诉你 Git 没有安装,还会告诉你如何安装 Git。如果你碰巧用 Debian
或Ubuntu Linux
,通过一条 sudo apt-get install git
就可以直接完成 Git 的安装,非常简单
如果是其他 Linux 版本,可以直接通过源码安装。先从 Git 官网下载源码(网站服务器在国外,建议翻墙访问,否则太慢或无法访问)
https://git-scm.com/download/linux
点进去后选择一个你想安装的版本下载下来
把源码包拷贝到Centos下你想要放的目录,解压,./configure后make,make ,make instll 依次执行
在解压前呢,先准备一下git所需要的依赖环境
# CentOS:
[-> #~ ] yum install -y curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-CBuilder perl-ExtUtils-MakeMaker
# Ubuntu:
[-> #~ ] apt-get install -y libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev
如果想以后git支持多文本,如.docx,.html等,可以做这一步(选做)
# CentOS:
[-> #~ ] yum install -y asciidoc xmlto docbook2x
# Ubuntu:
[-> #~ ] apt-get install -y asciidoc xmlto docbook2x
编译安装
tar zxf git-2.9.5.tar.gz
cd git-2.9.5/
make configure
./configure --prefix=/usr/local/git
make && make install
安装完成后呢,为了方便使用,优化执行路径:
ln -s /usr/local/git/bin/git /usr/bin/
可以试着使用git --version
来查看版本
在 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 官网下载:
https://git-scm.com/download/
然后选择win,进去就自动下载Git-2.26.2-64-bit.exe,下载完,直接运行
安装完成后,在开始菜单里找到“Git”->“Git Bash”
,蹦出一个类似命令行窗口的东西,就说明 Git 安装成功!
安装完成后,还要声明自己的名字和邮箱
$ git config --global user.name "Your Name"
$ git config --global user.email "[email protected]"
注意 git config 命令的--global
参数,用了这个参数,表示你这台机器上所有的 Git 仓库都会使用这个配置;
当然也可以对某个仓库指定不同的用户名和 Email 地址。
bit的基础操作
创建版本库目录并初始化
[root@localhost /]# mkdir git
[root@localhost /]# cd git/
[root@localhost git]# git init #初始化
Initialized empty Git repository in /git/.git/
[root@localhost git]#
什么是版本库呢?版本库又名仓库,英文名 repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被 Git 管理起来,每个文件的修改、删除,Git 都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
咦,/git/.git/
下的.git什么东西?
这个目录是 Git 来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把 Git 仓库给破坏了。
如果你没有看到.git 目录,那是因为这个目录默认是隐藏的,用 ls -ah 命令就可以看见。
注:
branched 分支目录
config 定义目录特有的配置选项
description 仅供git web使用
HEAD 指定当前的分支
hooks git钩子文件
info 包含一个全局排除文件(exclude)
objects 存放所有的数据内容
refs 指针文件
index 暂存区文件
把文件添加到版本库(或上传代码)
上传代码步骤分为2步:
1>把代码或文件上传到暂存区
2>再上传到版本库
首先,编写一个test.php
vim /git/test.php
<?
phpinfo();
?>
上传到暂存区
执行上面的命令,没有任何显示,这就对了
git add test.php
表示的是单个文件上传
git add .
表示的是当前目录所有文件上传
用命令 git commit 告诉 Git,把文件提交到仓库
[root@localhost git]# git commit -m 'add test.php'
[master (root-commit) 0af70ac] add test.php
1 file changed, 3 insertions(+)
create mode 100644 test.php
[root@localhost git]#
git commit
命令,-m
后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录。
为什么 Git 添加文件需要 add,commit 一共两步呢?
因为commit
可以一次提交很多文件,所以你可以多次 add 不同
的文件,相当于公交车一样,拉一车人会走,拉你一个人也走的道理;
如下:
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files."
我们已经成功添加并提交一个文件后,继续工作,于是,我们修改test.php
# This is a test script
<?
phpinfo();
?>
git status
命令可以让我们时刻掌握仓库当前的状态,下面的命令告诉我们,test.php被修改过了,但还没有准备提交的修改。
git diff
顾名思义就是查看 difference,显示的格式正是 Unix 通用的 diff 格式,可以从下面的命令输出看到:
[root@localhost git]# git diff test.php
diff --git a/test.php b/test.php
index 1442a01..3aa80b5 100644
--- a/test.php
+++ b/test.php
@@ -1,3 +1,4 @@
+# This is a test script
<?
phpinfo();
?>
[root@localhost git]#
知道了对 test.php作了什么修改后,再把它提交到仓库就放心多了,提交修改和提交新文件是一样的两步
[root@localhost git]# git add test.php
[root@localhost git]# git commit -m 'add zhushi'
[master 53ffab0] add zhushi
1 file changed, 1 insertion(+)
[root@localhost git]# git status
# On branch master
nothing to commit, working directory clean #提示我们没有要修改的,目录是干净的
[root@localhost git]#
当然了,在实际工作中,我们脑子里怎么可能记得一个几千行的文件每次都改了什么内容,不然要版本控制系统干什么。版本控制系统肯定有某个命令可以告诉我们历史记录,在 Git 中,我们用 git log 命令
查看,其中,
git log --oneline
#显示摘要信息
[root@localhost git]# git log
commit 53ffab060985864deae700ab84b294db9addcc54
Author: lzs <[email protected]>
Date: Thu Apr 30 06:08:21 2020 -0700
add zhushi
commit 0af70ac06b78b139b2648acf3b27c670114b70ba
Author: lzs <[email protected]>
Date: Thu Apr 30 04:17:59 2020 -0700
add test.php
[root@localhost git]# git log --oneline
53ffab0 add zhushi
0af70ac add test.php
版本回滚
git reset --hard HEAD^
#回滚到上个版本,HEAD^^上上版本,HEAD~10回滚到上10个版本
我们来做个测试:
[root@localhost git]# git reset --hard HEAD^
HEAD is now at 0af70ac add test.php
[root@localhost git]#
这就好比我们做时光机回去过去了,可是这里呆够了,又想回去了,该咋办?办法其实还是有的,只要上面的命令行窗口
还没有被关掉,你就可以顺着往上找啊找啊,找到你要回去的commit
的id是53ffab0
,还是可以的
命令:
git reset --hard 53ffab0 #通告git log 的版本号码回滚,仅写前7位就可
例子:
[root@localhost git]# git reset --hard 53ffab0
HEAD is now at 53ffab0 add test.php
[root@localhost git]# cat test.php
# This is a test script
<?
phpinfo();
?>
[root@localhost git]#
在 Git 中,总是有后悔药可以吃的。Git 提供了一个命令 git reflog
用来记录你的每一次命令:
工作区和暂存区
工作区: 就是你在电脑里能看到的目录
,比如我的 git目录
就是一个工作区
工作区有一个隐藏目录.git,这个不算工作区,而是 Git 的版本库。
Git 的版本库里存了很多东西,其中最重要的就是称为 stage
(或者叫 index)的暂存区,还有 Git 为我们自动创建的第一个分支 master
,以及指向 master 的一个指针叫 HEAD
。
因为我们创建 Git 版本库时,Git 自动为我们创建了唯一一个 master 分支
,所以,现在,git commit
就是往 master分支上提交更改。
他们之间的关系如下图:
撤销修改
场景 1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令 git checkout -- file
,把文件在工作区的修改全部撤销
场景 2:当你不但改乱了工作区某个文件的内容,还添加
到了暂存区时,想丢弃修改,分两步,第一步用命令 git reset HEAD file
,就回到了场景 1,第二步按场景 1 操作。
场景 3:已经提交了不合适的修改到版本库
时,想要撤销本次提交,参考版本回退,回退到之前的位置即可
例子:
[root@localhost git]# cat abc.txt #在原来的基础上加上一行qqqqqqqqq
aaaaaaaaaaaaaaaa
vbbbbbbbbbbbbbbbbbb
qqqqqqqqqqqqq
ccccccccccccc
[root@localhost git]# git add abc.txt
[root@localhost git]# git commit -m 'abc.txt'
[root@localhost git]# git reset HEAD abc.txt
Unstaged changes after reset:
M abc.txt
[root@localhost git]# git checkout -- abc.txt
[root@localhost git]# cat abc.txt
aaaaaaaaaaaaaaaa
vbbbbbbbbbbbbbbbbbb
ccccccccccccc
删除文件
在 Git 中,删除也是一个修改操作,现在,我们在工作区把abc.txt删除了,因此,工作区和版本库就不一致了,查看状态会立刻出现提示
现在你有两个选择,一是确实要从版本库
中删除该文件,那就用命令 git rm
删掉,并且 git commit
[root@localhost git]# git rm abc.txt
rm 'abc.txt'
[root@localhost git]# git commit -m 'remove abc.txt'
[master 7e9f706] remove abc.txt
1 file changed, 3 deletions(-)
delete mode 100644 abc.txt
[root@localhost git]# git log --oneline
7e9f706 remove abc.txt
6014302 abc.txt
53ffab0 add test.php
0af70ac add test.php
另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本
git checkout -- abc.txt
还有就是已经把版本库删除了。才发现误删了。也没关系,坐时光机回去呗
[root@localhost git]# ls
test.php
[root@localhost git]# git reset --hard HEAD^
HEAD is now at 6014302 abc.txt
[root@localhost git]#
[root@localhost git]# ls
abc.txt test.php
分支管理
什么是分支?
区别于主代码库,创建出来用于新增功能或模块的分支库,仅用户自己有权访问,修改后合并,一般用于更新版本或添加补丁。
例子:
分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习 Git 的时候,另一个你正在另一个平行宇宙里努力学习SVN。
如果两个平行宇宙互不干扰,那对现在的你也没啥影响。不过,在某个时间点,两个平行宇宙合并了,结果,你既学会了 Git 又学会了 SVN!
分支在实际中有什么用呢?
假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了 50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
分支工作的原理
在版本回退
里,你已经知道,每次提交,Git 都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在 Git 里,这个分支叫主分支
,即 master 分支
。HEAD
严格来说不是指向提交,而是指向 master,master才是指向提交的,所以,HEAD 指向的就是当前分支
一开始的时候,master 分支是一条线,Git master 指向最新的提交,再用 HEAD 指master,就能确定当前分支,以及当前分支的提交点;每次提交,master 分支都会向前移动一步,这样,随着你不断提交,master 分支的线也越来越长;
当我们创建新的分支,例如 dev
时,Git 新建了一个指针叫dev
,指向master
相同的提交,再把 HEAD
指向dev
,就表示当前分支在 dev
上:
从现在开始,对工作区的修改和提交就是针对 dev
分支了,比如新提交一次后,dev
指针往前移动一步,而master
指针不变
假如我们在 dev
上的工作完成了,就可以把dev
合并到 master
上。Git 怎么合并呢?最简单的方法,就是直接把 master指向 dev 的当前提交,就完成了合并
合并完分支后,甚至可以删除 dev 分支
。删除 dev 分支就是把 dev 指针给删掉,删掉后,我们就剩下了一条 master分支
.
分支的创建于合并
查看分支
[root@localhost git]# git branch
* master
[root@localhost git]#
创建分支
[root@localhost git]# git branch ops
[root@localhost git]# git branch
ops
* master
创建并切换分支
[root@localhost git]# git checkout -b dev
Switched to a new branch 'dev'
[root@localhost git]# git branch
* dev
master
ops
会发现*号跑到了dev分支上,表示的是dev
现在是当前分支,现在在dev分支里向git仓库添加文件
echo 'Hello Word!' > /git/hw.txt
git add hw.txt
git commit -m 'branch test'
合并分支
现在,dev 分支的工作完成,我们就可以切换回 master 分支
[root@localhost git]# git checkout master
Switched to branch 'master'
切换回 master 分支后,再查看一个 hw.txt 文件,刚才添加的内容不见了!因为那个提交是在 dev 分支上,而 master分支此刻的提交点并没有变
现在,我们把 dev 分支的工作成果合并到 master 分支上
[root@localhost git]# ls #合并前
test.php
[root@localhost git]# git merge dev
Updating 7e9f706..2dea9e9
Fast-forward
hw.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 hw.txt
[root@localhost git]# ls #合并后
hw.txt test.php
删除分支
合并完成后,就可以放心地删除 dev 分支了
[root@localhost git]# git branch -d ops
Deleted branch ops (was 7e9f706).
[root@localhost git]# git branch -d dev
Deleted branch dev (was 2dea9e9).
[root@localhost git]# git branch
* master
解决合并冲突
在建立一个人分支,修改hw.txt文件,然后git add ,接着git commit 放入本地仓库
切换到master分支上,再对hw.txt进行修改,然后发布到本地仓库
合并,会发现不会成功,但hw.txt的出处会给你标出来
[root@localhost git]# git checkout -b lzs
Switched to a new branch 'lzs'
[root@localhost git]# vim hw.txt
[root@localhost git]# git add hw.txt
[root@localhost git]# git commit -m 'lzs kw.txt'
[lzs 5df22d7] lzs kw.txt
[root@localhost git]# git checkout master
Switched to branch 'master'
[root@localhost git]# vim hw.txt
[root@localhost git]# git add hw.txt
[root@localhost git]# git commit -m 'master he.txt'
合并会发现
[root@localhost git]# git merge lzs
Auto-merging hw.txt
CONFLICT (content): Merge conflict in hw.txt
Automatic merge failed; fix conflicts and then commit the result.
文件不一样的地方会标记出来,在master上修改完,在上传仓库就好
[root@localhost git]# cat hw.txt
<<<<<<< HEAD
Hello word!
=======
Hello WORD!
>>>>>>> lzs
[root@localhost git]# vim hw.txt
[root@localhost git]# git add hw.txt
[root@localhost git]# git commit -m 'fixed hw.txt'
[root@localhost git]# cat hw.txt
Hello WOrd!
下一篇:Git的远程管理