1.(本次作业要求来自:https://edu.cnblogs.com/campus/gzcc/GZCC-16SE1/homework/2103
2. 我的Github远程仓库地址: https://github.com/llgeill/llg-centos-git--test
3. 我的Github远程仓库地址截图
很多人都知道,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了。
如果要想了解分布式是什么意思,那么我们得先去了解它的对立面集中式
工具:git
内容:布式版本控制系统没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样,你工作的时候,就不需要联网了,因为版本库就在你自己的电脑上。既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比方说你在自己电脑上改了文件A,你的同事也在他的电脑上改了文件A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。
优点:和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了。在实际使用分布式版本控制系统的时候,其实很少在两人之间的电脑上推送版本库的修改,因为可能你们俩不在一个局域网内,两台电脑互相访问不了,也可能今天你的同事病了,他的电脑压根没有开机。因此,分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。当然,Git的优势不单是不必联网这么简单,后面我们还会看到Git极其强大的分支管理,把SVN等远远抛在了后面(svn其实也有分支功能,不过是在服务器上的分支)
可以直接从官网下载相应操作系统的Git然后进行安装,当然也可以使用命令行的方式
yum install -y git
https://git-scm.com/
用户名和邮箱地址的作用
修改用户名和邮箱地址
查看用户名和邮箱地址
以下所有操作都在centos上操作完成
版本库又名仓库,英文名repository,你可以简单理解成一个文件夹,这个文件夹里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。所以,创建一个版本库非常简单。
首先,选择一个合适的地方,创建一个空目录
#创建一个文件件 [llg@localhost 桌面]$ mkdir llg-test-git #进入文件夹目录 [llg@localhost 桌面]$ cd llg-test-git/ #显示当前文件夹路径 [llg@localhost llg-test-git]$ pwd
接着,最重要的一步 通过git init 的命令将当前文件夹变成一个版本库
[llg@localhost llg-test-git]$ git init 初始化空的 Git 版本库于 /home/llg/桌面/llg-test-git/.git/ [llg@localhost llg-test-git]$
最后,通过ls -al 命令,发现多了个git文件夹。这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。
[llg@localhost llg-test-git]$ ls -al 总用量 4 drwxrwsr-x 3 llg llg 18 9月 14 19:48 . drwsrwsrwt. 13 llg llg 4096 9月 14 19:44 .. drwxrwsr-x 7 llg llg 119 9月 14 19:48 .git [llg@localhost llg-test-git]$
首先需要注意的是版本管理的受众范围
首先,使用vi创建一个文件并且添加一些内容。确保此文件必须在这个版本仓库里面也就是我们恶毒llg-test-git文件夹,不然放在其他地方是不能被版本控制的。
[llg@localhost llg-test-git]$ vi llg.txt
[llg@localhost llg-test-git]$
接着,将文件显示的通过git add命令添加到版本库里面,不过在这之前我们使用git status 来查看一下还没使用git add命令时候的状态和使用git add之后的状态,比较一下不同。通过比较,我们发现了当使用git add 命令的时候其实是建立起了文件跟踪的功能,之后使用git status 就可以看到有一个新文件
[llg@localhost llg-test-git]$ git status # 位于分支 master # # 初始提交 # # 未跟踪的文件: # (使用 "git add <file>..." 以包含要提交的内容) # # llg.txt 提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪) [llg@localhost llg-test-git]$
[llg@localhost llg-test-git]$ git add llg.txt [llg@localhost llg-test-git]$ git status # 位于分支 master # # 初始提交 # # 要提交的变更: # (使用 "git rm --cached <file>..." 撤出暂存区) # # 新文件: llg.txt # [llg@localhost llg-test-git]$
[llg@localhost llg-test-git]$ git commit -m "git练习测试" [master(根提交) abde635] git练习测试 1 file changed, 1 insertion(+) create mode 100644 llg.txt [llg@localhost llg-test-git]$
疑问:为什么Git添加文件需要add
,commit
一共两步呢?
因为commit
可以一次提交很多文件,所以你可以多次add
不同的文件,比如下图,可以跟踪多个文件然后全部一次性提交到版本库里面.
[llg@localhost llg-test-git]$ vi one.txt [llg@localhost llg-test-git]$ vi two.txt [llg@localhost llg-test-git]$ vi three.txt [llg@localhost llg-test-git]$ git add one.txt [llg@localhost llg-test-git]$ git add two.txt [llg@localhost llg-test-git]$ git add three.txt [llg@localhost llg-test-git]$ git commit -m "这次测试一次提交三个文件" [master 44449dc] 这次测试一次提交三个文件 3 files changed, 4 insertions(+) create mode 100644 one.txt create mode 100644 three.txt create mode 100644 two.txt [llg@localhost llg-test-git]$
[llg@localhost llg-test-git]$ git status
# 位于分支 master
无文件要提交,干净的工作区
[llg@localhost llg-test-git]$
在学习版本回退之前我们线来了解下两个常用的命令git status和git diff
刚刚在创建版本库的时候我们已经使用了,用来查看git的状态,一般可以拿来查看我们做的某些操作之后的状态,例如修改文件,添加文件等等
例如我们修改一个文件然后使用git status查看,发现了提示llg.txt是修改过的文件
[llg@localhost llg-test-git]$ vi llg.txt [llg@localhost llg-test-git]$ git status # 位于分支 master # 尚未暂存以备提交的变更: # (使用 "git add <file>..." 更新要提交的内容) # (使用 "git checkout -- <file>..." 丢弃工作区的改动) # # 修改: llg.txt # 修改尚未加入提交(使用 "git add" 和/或 "git commit -a") [llg@localhost llg-test-git]$
由于文件修改过后,我们有时候可能想知道具体修改了什么内容,位置在那里,区别是什么等等。,所以我们需要git sdiff命令来获得这个修改前后的对照信息
[llg@localhost llg-test-git]$ git diff diff --git a/llg.txt b/llg.txt index f77faef..2f3d837 100644 --- a/llg.txt +++ b/llg.txt @@ -1 +1 @@ -my name is liliguang +My name is liliguang.I created three files a moment ago. [llg@localhost llg-test-git]$
[llg@localhost llg-test-git]$ vi one.txt [llg@localhost llg-test-git]$ vi two.txt [llg@localhost llg-test-git]$ git diff diff --git a/llg.txt b/llg.txt index f77faef..2f3d837 100644 --- a/llg.txt +++ b/llg.txt @@ -1 +1 @@ -my name is liliguang +My name is liliguang.I created three files a moment ago. diff --git a/one.txt b/one.txt index 25ab921..34144e1 100644 --- a/one.txt +++ b/one.txt @@ -1 +1,2 @@ this is one +llg diff --git a/two.txt b/two.txt index 42b3fbc..0f62931 100644 --- a/two.txt +++ b/two.txt @@ -1,2 +1,2 @@ this is two - +llg [llg@localhost llg-test-git]$
[llg@localhost llg-test-git]$ vi two.txt [llg@localhost llg-test-git]$ git diff diff --git a/llg.txt b/llg.txt index f77faef..2f3d837 100644 --- a/llg.txt +++ b/llg.txt @@ -1 +1 @@ -my name is liliguang +My name is liliguang.I created three files a moment ago. diff --git a/one.txt b/one.txt index 25ab921..34144e1 100644 --- a/one.txt +++ b/one.txt @@ -1 +1,2 @@ this is one +llg diff --git a/two.txt b/two.txt index 42b3fbc..e730358 100644 --- a/two.txt +++ b/two.txt @@ -1,2 +1,6 @@ this is two - +llg +sss +aaa +bbb +wef [llg@localhost llg-test-git]$
版本回退的意思就是回退到某一次commit那里,这优点类似与打通关游戏,你可以在任意通过的关卡中来往穿梭
首先,我们需要使用git log 命令来找出我们之前的commit信息。从下图可以看到提交顺序是按照最新日期排序的,最新的在最顶部,commit 后面的就是commit ID 了,后面的版本回退都靠它
[llg@localhost llg-test-git]$ git log commit 3956cc36e1017c14e79d9de0944bb9baa6d2ff51 Author: llg <903857227@qq.com> Date: Fri Sep 14 20:35:26 2018 +0800 测试两个文件提交留一个文件不提交 commit 44449dc5261b8a66bac14ce302519f26895d0163 Author: llg <903857227@qq.com> Date: Fri Sep 14 20:11:22 2018 +0800 这次测试一次提交三个文件 commit abde635c93434ac4172caa4e0c6383e9dfc3d522 Author: llg <903857227@qq.com> Date: Fri Sep 14 20:07:31 2018 +0800 git练习测试 [llg@localhost llg-test-git]$
--pretty=oneline
参数
[llg@localhost llg-test-git]$ git log --pretty=oneline
3956cc36e1017c14e79d9de0944bb9baa6d2ff51 测试两个文件提交留一个文件不提交
44449dc5261b8a66bac14ce302519f26895d0163 这次测试一次提交三个文件
abde635c93434ac4172caa4e0c6383e9dfc3d522 git练习测试
[llg@localhost llg-test-git]$
首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD
表示当前版本,也就是最新的提交3956cc...
(注意我的提交ID和你的肯定不一样),上一个版本就是HEAD^
,上上一个版本就是HEAD^^
,当然往上100个版本写100个^
比较容易数不过来,所以写成HEAD~100
。
我们要把当前版本<测试两个文件提交留一个文件不提交>回退到上一个版本<这次测试一次提交三个文件>,就可以使用git reset
命令:
从下图我们可以分析出,其实HEAD代表的是头指针,毕竟这个git使用c语言写的,我们对C应该也是不陌生的,所以这是一个双向链表,可以向前也可以向后,只要我们知道commit ID。
[llg@localhost llg-test-git]$ git reset --hard HEAD HEAD 现在位于 3956cc3 测试两个文件提交留一个文件不提交 [llg@localhost llg-test-git]$ git reset --hard HEAD^ HEAD 现在位于 44449dc 这次测试一次提交三个文件 [llg@localhost llg-test-git]$ git reset --hard HEAD^ HEAD 现在位于 abde635 git练习测试 [llg@localhost llg-test-git]$
当我们跳的比较远的时候,也可以直接指定commit ID 来跳转
[llg@localhost llg-test-git]$ git reset --hard 3956cc3
HEAD 现在位于 3956cc3 测试两个文件提交留一个文件不提交
[llg@localhost llg-test-git]$
当我们想要回到之前最新的版本的时候,我们会想到用git log,但是很不幸我们发现之前的版本信息都没有了。所以我们还有一个命令用来找回之前的commit ID ,就是git reflog命令了。
[llg@localhost llg-test-git]$ git log commit abde635c93434ac4172caa4e0c6383e9dfc3d522 Author: llg <903857227@qq.com> Date: Fri Sep 14 20:07:31 2018 +0800 git练习测试 [llg@localhost llg-test-git]$
可以拿到所有的操作记录
从下面我们可以找到带有commit字样的就是我们之前创建的版本,但是只提供了ID的七为数,但是没关系,git会自动识别出来
[llg@localhost llg-test-git]$ git reflog abde635 HEAD@{0}: reset: moving to HEAD^ 44449dc HEAD@{1}: reset: moving to HEAD^ 3956cc3 HEAD@{2}: commit: 测试两个文件提交留一个文件不提交 44449dc HEAD@{3}: commit: 这次测试一次提交三个文件 abde635 HEAD@{4}: commit (initial): git练习测试 [llg@localhost llg-test-git]$ git reset --hard 3956cc3 HEAD 现在位于 3956cc3 测试两个文件提交留一个文件不提交 [llg@localhost llg-test-git]$
首先是官图解释,从下图可以看出如果我们想直接提交文件到master分支上,那么直接使用 git commit -a即可
接着再看看其余大佬绘制的图
工作区有一个隐藏目录.git
,这个不算工作区,而是Git的版本库。
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master
,以及指向master
的一个指针叫HEAD
。
假如我们在commit之后没有任何操作,那么我们暂存区就是空的
当我们添加了文件或者修改了文件,并且使用了add命令那么暂存区stage就又有了文件
当我们使用commit提交之后,那么早存区又变成了空
git checkout -- file
。首先修改工作区的文件,不执行add操作
检查git状态,发现提示使用git checkout -- file 命令撤回操作 ,执行之后发现已经成功撤回
[llg@localhost llg-test-git]$ git status # 位于分支 master # 尚未暂存以备提交的变更: # (使用 "git add <file>..." 更新要提交的内容) # (使用 "git checkout -- <file>..." 丢弃工作区的改动) # # 修改: one.txt # 修改尚未加入提交(使用 "git add" 和/或 "git commit -a") [llg@localhost llg-test-git]$
[llg@localhost llg-test-git]$ git checkout -- one.txt
[llg@localhost llg-test-git]$
git reset HEAD <file>
,就回到了场景1,第二步按场景1操作。首先修改文件并且通过add放到暂存区
[llg@localhost llg-test-git]$ git status # 位于分支 master # 要提交的变更: # (使用 "git reset HEAD <file>..." 撤出暂存区) # # 修改: one.txt # [llg@localhost llg-test-git]$
根据提示使用git reset HEAD <file> 撤出暂存区,执行成功
[llg@localhost llg-test-git]$ git status # 位于分支 master # 要提交的变更: # (使用 "git reset HEAD <file>..." 撤出暂存区) # # 修改: one.txt # [llg@localhost llg-test-git]$ git reset HEAD one.txt 重置后撤出暂存区的变更: M one.txt [llg@localhost llg-test-git]$ git status # 位于分支 master # 尚未暂存以备提交的变更: # (使用 "git add <file>..." 更新要提交的内容) # (使用 "git checkout -- <file>..." 丢弃工作区的改动) # # 修改: one.txt # 修改尚未加入提交(使用 "git add" 和/或 "git commit -a") [llg@localhost llg-test-git]$
此处的删除文件是指当一个文件提交到版本库后,也就是master分支。如果在工作区删除这个文件,那么将会使工作区文件与版本库文件不对应,在这里需要分清两种使用情况。
那么就应该使用git rm 命令
[llg@localhost llg-test-git]$ rm delete.txt [llg@localhost llg-test-git]$ git rm -- delete.txt rm ‘delete.txt‘ [llg@localhost llg-test-git]$ git status # 位于分支 master # 要提交的变更: # (使用 "git reset HEAD <file>..." 撤出暂存区) # # 删除: delete.txt # [llg@localhost llg-test-git]$
当然我们通过git status 命令可以看出这一操作只是保留在了暂存区,所以我们还需要commit命令提交这一更改,其实可以把这一命令想象成修改的命令
[llg@localhost llg-test-git]$ git commit -m "正式从版本库中删除该文件" [master ad66495] 正式从版本库中删除该文件 1 file changed, 1 deletion(-) delete mode 100644 delete.txt [llg@localhost llg-test-git]$ git status # 位于分支 master 无文件要提交,干净的工作区 [llg@localhost llg-test-git]$
根据git status 提示的方法,我们可以从版本库中checkout中某些文件
[llg@localhost llg-test-git]$ git checkout -- delete.txt
另外一个一个简单的方法就是重新把版本库更新回来,但是这个前提是你得保证只有这么一个删除文件,如果有其他文件修改了并且未放到暂存区,那么就非常可怕了。
[llg@localhost llg-test-git]$ git reset --hard HEAD^ HEAD 现在位于 eda17f9 这是一个将要被删除的文件 [llg@localhost llg-test-git]$ ls -al 总用量 24 drwxrwsr-x 3 llg llg 98 9月 15 19:56 . drwsrwsrwt. 13 llg llg 4096 9月 14 19:44 .. -rw-rw-r-- 1 llg llg 34 9月 15 19:56 delete.txt drwxrwsr-x 8 llg llg 183 9月 15 19:56 .git -rw-rw-r-- 1 llg llg 57 9月 14 21:06 llg.txt -rw-rw-r-- 1 llg llg 16 9月 14 22:05 one.txt -rw-rw-r-- 1 llg llg 14 9月 14 21:06 three.txt -rw-rw-r-- 1 llg llg 13 9月 14 21:06 two.txt [llg@localhost llg-test-git]$
既然git的其中一个目的就是为了协作开发,那么远程仓库就是为了这一个目的的。通过将仓库放在一个远程的服务器上,让所有人都可以访问这个仓库,可以一起更新和提交。
实际情况往往是这样,找一台电脑充当服务器的角色,每天24小时开机,其他每个人都从这个“服务器”仓库克隆一份到自己的电脑上,并且各自把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交。
完全可以自己搭建一台运行Git的服务器,不过现阶段,为了学Git先搭个服务器绝对是小题大作。好在这个世界上有个叫GitHub的神奇的网站,从名字就可以看出,这个网站就是提供Git仓库托管服务的,所以,只要注册一个GitHub账号,就可以免费获得Git远程仓库。
在继续阅读后续内容前,请自行注册GitHub账号。由于你的本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以,需要一点设置:
第1步:创建SSH Key。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa
和id_rsa.pub
这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:
[llg@localhost ~]$ ssh-keygen -t rsa -C "903857227@qq.com"
如果一切顺利的话,可以在用户主目录里找到.ssh
目录,里面有id_rsa
和id_rsa.pub
两个文件,这两个就是SSH Key的秘钥对,id_rsa
是私钥,不能泄露出去,id_rsa.pub
是公钥,可以放心地告诉任何人。
第2步:登陆GitHub,打开“Account settings”,“SSH Keys”页面:
然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub
文件的内容:
[llg@localhost ~]$ cd .ssh/
[llg@localhost .ssh]$ ls -al 总用量 16 drwsrwsrwt. 2 llg llg 57 9月 15 20:28 . drwsrwsrwt. 39 llg llg 4096 9月 15 19:59 .. -rw------- 1 llg llg 1679 9月 15 20:29 id_rsa -rw-r--r-- 1 llg llg 398 9月 15 20:29 id_rsa.pub -rw-r--r-- 1 llg llg 175 4月 27 20:05 known_hosts [llg@localhost .ssh]$ vi id_rsa.pub
为什么GitHub需要SSH Key呢?因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。
当然,GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。
最后友情提示,在GitHub上免费托管的Git仓库,任何人都可以看到喔(但只有你自己才能改)。所以,不要把敏感信息放进去。
如果你不想让别人看到Git库,有两个办法,一个是交点保护费,让GitHub把公开的仓库变成私有的,这样别人就看不见了(不可读更不可写)。另一个办法是自己动手,搭一个Git服务器,因为是你自己的Git服务器,所以别人也是看不见的。这个方法我们后面会讲到的,相当简单,公司内部开发必备。
确保你拥有一个GitHub账号后,我们就即将开始远程仓库的学习。
首先我们github上登陆自己帐号后创建一个仓库
接着我们可以看到创建完仓库后给我们的提示
根据上边提示输入命令
[llg@localhost llg-test-git]$ git remote add origin https://github.com/llgeill/llg-centos-git--test.git [llg@localhost llg-test-git]$ git push -u origin master Username for ‘https://github.com‘: 903857227@qq.com Password for ‘https://903857227@qq.com@github.com‘: Counting objects: 12, done. Delta compression using up to 4 threads. Compressing objects: 100% (6/6), done. Writing objects: 100% (12/12), 1.01 KiB | 0 bytes/s, done. Total 12 (delta 0), reused 0 (delta 0) remote: remote: Create a pull request for ‘master‘ on GitHub by visiting: remote: https://github.com/llgeill/llg-centos-git--test/pull/new/master remote: To https://github.com/llgeill/llg-centos-git--test.git * [new branch] master -> master 分支 master 设置为跟踪来自 origin 的远程分支 master。
git remote add origin git@server-name:path/repo-name.git
;git push -u origin master
第一次推送master分支的所有内容;git push origin master
推送最新修改;首先我们从github上创建一个远程仓库
首先在github上找到可以克隆的地址
下一步是用命令git clone
克隆一个本地库,我们可以发现项目已经克隆下来
[llg@localhost 桌面]$ git clone git@github.com:llgeill/llg-centos-git-test-1.git 正克隆到 ‘llg-centos-git-test-1‘... The authenticity of host ‘github.com (192.30.253.112)‘ can‘t be established. RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8. RSA key fingerprint is MD5:16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added ‘github.com,192.30.253.112‘ (RSA) to the list of known hosts. remote: Counting objects: 3, done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 接收对象中: 100% (3/3), done. [llg@localhost 桌面]$ ls -al 总用量 40 drwsrwsrwt. 14 llg llg 4096 9月 15 21:10 . drwsrwsrwt. 39 llg llg 4096 9月 15 20:39 .. drwxr-xr-x 11 llg llg 196 9月 2 17:11 1-Brixton版教程示例 drwxr-xr-x 34 llg llg 4096 9月 2 15:57 2-Dalston版教程示例 -rw-r--r-- 1 root root 12288 4月 23 20:58 .android-studio.desktop.swp drwxrwxr-x 10 llg llg 302 9月 13 22:23 dist -rwsrwsrwt 1 llg llg 1968 5月 2 08:27 .keystore drwxrwsr-x 5 llg llg 118 9月 14 08:38 llg drwxrwsr-x 3 llg llg 35 9月 15 21:10 llg-centos-git-test-1 drwxrwsr-x 3 llg llg 80 9月 15 20:01 llg-test-git drwxrwsr-x 5 llg llg 131 9月 7 20:13 llg-user-gateway drwxrwsr-x 6 llg llg 151 9月 14 08:51 llg-web-springboot-class drwxr-xr-x 39 llg llg 4096 9月 2 19:41 SpringCloudBook-master drwx------ 14 llg llg 315 9月 3 07:46 spring-cloud-llg drwxrwsr-x 5 llg llg 61 9月 5 18:53 untitled drwxr-xr-x 12 llg llg 4096 6月 29 2016 计算机组成原理201407
测试从本地仓库推送回远程仓库
[llg@localhost llg-centos-git-test-1]$ vi llg.txt [llg@localhost llg-centos-git-test-1]$ git add llg.txt [llg@localhost llg-centos-git-test-1]$ git commit -m "测试" [master 4bdb6c4] 测试 1 file changed, 1 insertion(+) create mode 100644 llg.txt [llg@localhost llg-centos-git-test-1]$ git status # 位于分支 master # 您的分支领先 ‘origin/master‘ 共 1 个提交。 # (使用 "git push" 来发布您的本地提交) # 无文件要提交,干净的工作区 [llg@localhost llg-centos-git-test-1]$ git push -u origin masterWarning: Permanently added the RSA host key for IP address ‘192.30.253.113‘ to the list of known hosts. Counting objects: 4, done. Delta compression using up to 4 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 274 bytes | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@github.com:llgeill/llg-centos-git-test-1.git 07b4bcc..4bdb6c4 master -> master 分支 master 设置为跟踪来自 origin 的远程分支 master。
分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN。
如果两个平行宇宙互不干扰,那对现在的你也没啥影响。不过,在某个时间点,两个平行宇宙合并了,结果,你既学会了Git又学会了SVN!
分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
其他版本控制系统如SVN等都有分支管理,但是用过之后你会发现,这些版本控制系统创建和切换分支比蜗牛还慢,简直让人无法忍受,结果分支功能成了摆设,大家都不去用。
但Git的分支是与众不同的,无论创建、切换和删除分支,Git在1秒钟之内就能完成!无论你的版本库是1个文件还是1万个文件。
在版本回退里,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master
分支。HEAD
严格来说不是指向提交,而是指向master
,master
才是指向提交的,所以,HEAD
指向的就是当前分支。
一开始的时候,master
分支是一条线,Git用master
指向最新的提交,再用HEAD
指向master
,就能确定当前分支,以及当前分支的提交点:
当我们创建新的分支,例如dev
时,Git新建了一个指针叫dev
,指向master
相同的提交,再把HEAD
指向dev
,就表示当前分支在dev
上
git checkout
命令加上-b
参数表示创建分支并切换分支
[llg@localhost llg-test-git]$ git checkout -b dev 切换到一个新分支 ‘dev‘ [llg@localhost llg-test-git]$
上面的命令等价与下面两条命令的结合
[llg@localhost llg-test-git]$ git branch dev [llg@localhost llg-test-git]$ git checkout dev 切换到分支 ‘dev‘
我们可以使用git branch 查看当前分支的情况
[llg@localhost llg-test-git]$ git branch * dev master
你看,Git创建一个分支很快,因为除了增加一个dev
指针,改改HEAD
的指向,工作区的文件都没有任何变化!
不过,从现在开始,对工作区的修改和提交就是针对dev
分支了,比如新提交一次后,dev
指针往前移动一步,而master
指针不变
[llg@localhost llg-test-git]$ vi one.txt [llg@localhost llg-test-git]$ git add one.txt [llg@localhost llg-test-git]$ git commit -m "在分支上提交版本信息" [dev 8c8117b] 在分支上提交版本信息 1 file changed, 2 insertions(+) [llg@localhost llg-test-git]$ git status # 位于分支 dev 无文件要提交,干净的工作区 [llg@localhost llg-test-git]$
当我们重新切换到master分支时候发现one.txt根本没有变化,因为我们commit的是dev分支
[llg@localhost llg-test-git]$ git checkout master 切换到分支 ‘master‘ [llg@localhost llg-test-git]$ vi one.txt [llg@localhost llg-test-git]$ git checkout dev 切换到分支 ‘dev‘ [llg@localhost llg-test-git]$ vi one.txt
假如我们在dev
上的工作完成了,就可以把dev
合并到master
上。Git怎么合并呢?最简单的方法,就是直接把master
指向dev
的当前提交,就完成了合并 。注意下面的Fast-forwar 信息,Git告诉我们这次合并是“快进模式”,也就是直接把master
指向dev
的当前提交,所以合并速度非常快。当然,也不是每次合并都能Fast-forward
,我们后面会讲其他方式的合并。
[llg@localhost llg-test-git]$ git checkout master 切换到分支 ‘master‘ [llg@localhost llg-test-git]$ git merge dev 更新 3956cc3..8c8117b Fast-forward one.txt | 2 ++ 1 file changed, 2 insertions(+)
合并完分支后,甚至可以删除dev
分支。删除dev
分支就是把dev
指针给删掉,删掉后,我们就剩下了一条master
分支
[llg@localhost llg-test-git]$ git branch dev * master [llg@localhost llg-test-git]$ vi one.txt [llg@localhost llg-test-git]$ git branch -d dev 已删除分支 dev(曾为 8c8117b)。 [llg@localhost llg-test-git]$ git branch * master
因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master
分支上工作效果是一样的,但过程更安全。
git branch
git branch <name>
git checkout <name>
git checkout -b <name>
git merge <name>
git branch -d <name>
首先创建一个分支并且提交了一次版本仓库,主分支也提交了一次版本仓库,如图所示
这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突
[llg@localhost llg-test-git]$ git merge fantasy
自动合并 dele
冲突(内容):合并冲突于 dele
自动合并失败,修正冲突然后提交修正的结果。
果然冲突了!Git告诉我们,readme.txt
文件存在冲突,必须手动解决冲突后再提交。git status
也可以告诉我们冲突的文件
[llg@localhost llg-test-git]$ git status # 位于分支 master # 您的分支领先 ‘origin/master‘ 共 7 个提交。 # (使用 "git push" 来发布您的本地提交) # # 您有尚未合并的路径。 # (解决冲突并运行 "git commit") # # 未合并的路径: # (使用 "git add <file>..." 标记解决方案) # # 双方修改: dele # 修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
我们可以直接查看dele的内容,Git用<<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容
我们手动修改冲突位置,之后提交文件到暂存区并且commit
[llg@localhost llg-test-git]$ git add dele [llg@localhost llg-test-git]$ git commit -m "cscs" [master b1dd3cc] cscs
现在,master
分支和feature1
分支变成了下图所示
用带参数的git log --graph
也可以看到分支的合并情况
* commit b1dd3cc1e90369e4fe2fc93f5a75b46a8918973f |\ Merge: 478f0bc 43149ab | | Author: llg <903857227@qq.com> | | Date: Sun Sep 16 15:10:03 2018 +0800 | | | | cscs | | | * commit 43149abf7f5724e74f64b394521778ceaded39b1 | | Author: llg <903857227@qq.com> | | Date: Sun Sep 16 15:01:24 2018 +0800 | | | | ccc | | * | commit 478f0bc2b144ac6fce457e05c9e596e314bd1f11 | | Author: llg <903857227@qq.com> | | Date: Sun Sep 16 15:02:12 2018 +0800 | | | | ss | | * | commit a22d668c065ad42c38232850fad8082250aa63a2 |\ \ Merge: 57e8b85 1575642 | |/ Author: llg <903857227@qq.com> | | Date: Sun Sep 16 14:55:13 2018 +0800 | | | | Merge branch ‘fantasy‘ | | | | ceshi | |
通常,合并分支时,如果可能,Git会用Fast forward
模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward
模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
下面我们实战一下--no-ff(禁用Fast forward)
方式的git merge,一些前提条件略过
[llg@localhost llg-test-git]$ git merge --no-ff -m "merge with no-ff" dev Merge made by the ‘recursive‘ strategy. dele | 1 + 1 file changed, 1 insertion(+) 合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。 [llg@localhost llg-test-git]$ git log --graph --pretty=oneline --abbrev-commit * 3a5456c merge with no-ff || * b68ba37 add merge |/ * 0ed0fe3 测试 * b1dd3cc cscs || * 43149ab ccc * | 478f0bc ss * | a22d668 Merge branch ‘fantasy‘ |\ | |/ | * 1575642 xx * | 57e8b85 Merge branch ‘fantasy‘ |\ | |/ | * 87bc5d7 ss * | 709521d 版本冲突 * | 8c8117b 在分支上提交版本信息 |/ * 3956cc3 测试两个文件提交留一个文件不提交 * 44449dc 这次测试一次提交三个文件 * abde635 git练习测试
我们来看看不适用no-ff下的策略,发现会直接合并,分支信息不能再找到
[llg@localhost llg-test-git]$ git log --graph --pretty=oneline --abbrev-commit * 8699b49 dele * 3a5456c merge with no-ff || * b68ba37 add merge |/ * 0ed0fe3 测试 * b1dd3cc cscs || * 43149ab ccc * | 478f0bc ss * | a22d668 Merge branch ‘fantasy‘ |\ | |/ | * 1575642 xx * | 57e8b85 Merge branch ‘fantasy‘ |\ | |/ | * 87bc5d7 ss * | 709521d 版本冲突 * | 8c8117b 在分支上提交版本信息 |/ * 3956cc3 测试两个文件提交留一个文件不提交 * 44449dc 这次测试一次提交三个文件 * abde635 git练习测试
Fast forward
模式,merge后就像这样。
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev
分支合并到master
上,在master
分支发布1.0版本;
你和你的小伙伴们每个人都在dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
软件开发中,bug就像家常便饭一样。有了bug就需要修复,在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101
来修复它,但是,等等,当前正在dev
上进行的工作还没有提交:
[llg@localhost llg-test-git]$ git status # 位于分支 master # 您的分支领先 ‘origin/master‘ 共 15 个提交。 # (使用 "git push" 来发布您的本地提交) # # 尚未暂存以备提交的变更: # (使用 "git add <file>..." 更新要提交的内容) # (使用 "git checkout -- <file>..." 丢弃工作区的改动) # # 修改: dele # 修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?
幸好,Git还提供了一个stash
功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作,再通过git status 查看发现工作区已经干净。
[llg@localhost llg-test-git]$ git stash Saved working directory and index state WIP on master: 7dec792 ss HEAD 现在位于 7dec792 ss [llg@localhost llg-test-git]$ git status # 位于分支 master # 您的分支领先 ‘origin/master‘ 共 15 个提交。 # (使用 "git push" 来发布您的本地提交) # 无文件要提交,干净的工作区
master
分支上修复,就从master
创建临时分支
[llg@localhost llg-test-git]$ git checkout -b issue-101 切换到一个新分支 ‘issue-101‘
现在修复bug,我们通过修改文件模拟这一个过程
[llg@localhost llg-test-git]$ vi delete.txt [llg@localhost llg-test-git]$ git add delete.txt [llg@localhost llg-test-git]$ git commit -m "ss" [issue-101 e0b9de9] ss 1 file changed, 1 insertion(+)
修复完成后,切换到master
分支,并完成合并,最后删除issue-101
分支
[llg@localhost llg-test-git]$ git checkout master 切换到分支 ‘master‘ 您的分支领先 ‘origin/master‘ 共 15 个提交。 (使用 "git push" 来发布您的本地提交) [llg@localhost llg-test-git]$ git merge --no-ff -m "merge bug fix 101" issue-101 Merge made by the ‘recursive‘ strategy. delete.txt | 1 + 1 file changed, 1 insertion(+)
太棒了,原计划两个小时的bug修复只花了5分钟!
现在,是时候接着回到dev
分支干活了,切换到dev分支然后用git stash list
命令看看
[llg@localhost llg-test-git]$ git stash list stash@{0}: WIP on master: 7dec792 ss
工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,有两个办法:
一是用git stash apply
恢复,但是恢复后,stash内容并不删除,你需要用git stash drop
来删除;
另一种方式是用git stash pop
,恢复的同时把stash内容也删了:
[llg@localhost llg-test-git]$ git stash pop # 位于分支 master # 您的分支领先 ‘origin/master‘ 共 17 个提交。 # (使用 "git push" 来发布您的本地提交) # # 尚未暂存以备提交的变更: # (使用 "git add <file>..." 更新要提交的内容) # (使用 "git checkout -- <file>..." 丢弃工作区的改动) # # 修改: dele # 修改尚未加入提交(使用 "git add" 和/或 "git commit -a") 丢弃了 refs/stash@{0} (fa2bbbe411ba20c94501e912ec120fa944c06b92)
git stash list
查看,就看不到任何stash内容了
[llg@localhost llg-test-git]$ git stash list
[llg@localhost llg-test-git]$
git stash
一下,然后去修复bug,修复后,再git stash pop
,回到工作现场
当需要开发一个新功能的时候,最好创建一个新的分支进行代码编辑,但是因为一些情况不需要这个分支了,但是这个分支从来没有合并过,所以要使用 git branch -D 的方式强制删除
[llg@localhost llg-test-git]$ vi feichuan.txt.swp [llg@localhost llg-test-git]$ git add feichuan.txt.swp [llg@localhost llg-test-git]$ git commit -m "ss" [feature-vulcan 4cd8736] ss 1 file changed, 1 insertion(+) create mode 100644 feichuan.txt.swp [llg@localhost llg-test-git]$ git checkout master M dele 切换到分支 ‘master‘ 您的分支领先 ‘origin/master‘ 共 17 个提交。 (使用 "git push" 来发布您的本地提交) [llg@localhost llg-test-git]$ git branch -D feature-vulcan 已删除分支 feature-vulcan(曾为 4cd8736)。
在上一节我们看到了,多人在同一个分支上协作时,很容易出现冲突。即使没有冲突,后push的童鞋不得不先pull,在本地合并,然后才能push成功。
每次合并再push后,分支变成了这样
总之看上去很乱,有强迫症的童鞋会问:为什么Git的提交历史不能是一条干净的直线?
其实是可以做到的!
Git有一种称为rebase的操作,有人把它翻译成“变基”。
先不要随意展开想象。我们还是从实际问题出发,看看怎么把分叉的提交变成直线。
在和远程分支同步后,我们对hello.py
这个文件做了两次提交。用git log
命令看看:
$ git log --graph --pretty=oneline --abbrev-commit * 582d922 (HEAD -> master) add author * 8875536 add comment * d1be385 (origin/master) init hello * e5e69f1 Merge branch ‘dev‘ || * 57c53ab (origin/dev, dev) fix env conflict | || | * 7a5e5dd add env | * | 7bd91f1 add new env ...
注意到Git用(HEAD -> master)
和(origin/master)
标识出当前分支的HEAD和远程origin的位置分别是582d922 add author
和d1be385 init hello
,本地分支比远程分支快两个提交。
现在我们尝试推送本地分支:
$ git push origin master To github.com:michaelliao/learngit.git ! [rejected] master -> master (fetch first) error: failed to push some refs to ‘git@github.com:michaelliao/learngit.git‘ hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing hint: to the same ref. You may want to first integrate the remote changes hint: (e.g., ‘git pull ...‘) before pushing again. hint: See the ‘Note about fast-forwards‘ in ‘git push --help‘ for details.
很不幸,失败了,这说明有人先于我们推送了远程分支。按照经验,先pull一下:
$ git pull remote: Counting objects: 3, done. remote: Compressing objects: 100% (1/1), done. remote: Total 3 (delta 1), reused 3 (delta 1), pack-reused 0 Unpacking objects: 100% (3/3), done. From github.com:michaelliao/learngit d1be385..f005ed4 master -> origin/master * [new tag] v1.0 -> v1.0 Auto-merging hello.py Merge made by the ‘recursive‘ strategy. hello.py | 1 + 1 file changed, 1 insertion(+)
再用git status
看看状态:
$ git status On branch master Your branch is ahead of ‘origin/master‘ by 3 commits. (use "git push" to publish your local commits) nothing to commit, working tree clean
加上刚才合并的提交,现在我们本地分支比远程分支超前3个提交。
用git log
看看:
$ git log --graph --pretty=oneline --abbrev-commit * e0ea545 (HEAD -> master) Merge branch ‘master‘ of github.com:michaelliao/learngit || * f005ed4 (origin/master) set exit=1 * | 582d922 add author * | 8875536 add comment |/ * d1be385 init hello ...
对强迫症童鞋来说,现在事情有点不对头,提交历史分叉了。如果现在把本地分支push到远程,有没有问题?
有!
什么问题?
不好看!
有没有解决方法?
有!
这个时候,rebase就派上了用场。我们输入命令git rebase
试试:
$ git rebase First, rewinding head to replay your work on top of it... Applying: add comment Using index info to reconstruct a base tree... M hello.py Falling back to patching base and 3-way merge.. Auto-merging hello.py Applying: add author Using index info to reconstruct a base tree... M hello.py Falling back to patching base and 3-way merge... Auto-merging hello.py
输出了一大堆操作,到底是啥效果?再用git log
看看:
$ git log --graph --pretty=oneline --abbrev-commit * 7e61ed4 (HEAD -> master) add author * 3611cfe add comment * f005ed4 (origin/master) set exit=1 * d1be385 init hello
原本分叉的提交现在变成一条直线了!这种神奇的操作是怎么实现的?其实原理非常简单。我们注意观察,发现Git把我们本地的提交“挪动”了位置,放到了f005ed4 (origin/master) set exit=1
之后,这样,整个提交历史就成了一条直线。rebase操作前后,最终的提交内容是一致的,但是,我们本地的commit修改内容已经变化了,它们的修改不再基于d1be385 init hello
,而是基于f005ed4 (origin/master) set exit=1
,但最后的提交7e61ed4
内容是一致的。
这就是rebase操作的特点:把分叉的提交历史“整理”成一条直线,看上去更直观。缺点是本地的分叉提交已经被修改过了。
最后,通过push操作把本地分支推送到远程:
Mac:~/learngit michael$ git push origin master Counting objects: 6, done. Delta compression using up to 4 threads. Compressing objects: 100% (5/5), done. Writing objects: 100% (6/6), 576 bytes | 576.00 KiB/s, done. Total 6 (delta 2), reused 0 (delta 0) remote: Resolving deltas: 100% (2/2), completed with 1 local object. To github.com:michaelliao/learngit.git f005ed4..7e61ed4 master -> master
再用git log
看看效果:
$ git log --graph --pretty=oneline --abbrev-commit * 7e61ed4 (HEAD -> master, origin/master) add author * 3611cfe add comment * f005ed4 set exit=1 * d1be385 init hello
远程分支的提交历史也是一条直线。
rebase操作可以把本地未push的分叉提交历史整理成直线;
rebase的目的是使得我们在查看历史提交的变化时更容易,因为分叉的提交需要三方对比。
发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。
Git的标签虽然是版本库的快照,但其实它就是指向某个commit的指针(跟分支很像对不对?但是分支可以移动,标签不能移动),所以,创建和删除标签都是瞬间完成的。
Git有commit,为什么还要引入tag?
“请把上周一的那个版本打包发布,commit号是6a5819e...”
“一串乱七八糟的数字不好找!”
如果换一个办法:
“请把上周一的那个版本打包发布,版本号是v1.2”
“好的,按照tag v1.2查找commit就行!”
所以,tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。
在Git中打标签非常简单,首先,切换到需要打标签的分支上
[llg@localhost llg-test-git]$ git branch dev issue-101 * master [llg@localhost llg-test-git]$ git checkout dev 切换到分支 ‘dev‘
然后,敲命令git tag <name>
就可以打一个新标签
[llg@localhost llg-test-git]$ git tag v1.0
默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,比如,现在已经是周五了,但应该在周一打的标签没有打,怎么办?
方法是找到历史提交的commit id,然后打上就可以了:
[llg@localhost llg-test-git]$ git log --pretty=oneline --abbrev-commit 8699b49 dele 3a5456c merge with no-ff b68ba37 add merge 0ed0fe3 测试 b1dd3cc cscs 478f0bc ss 43149ab ccc a22d668 Merge branch ‘fantasy‘ 1575642 xx 57e8b85 Merge branch ‘fantasy‘ 709521d 版本冲突 87bc5d7 ss 8c8117b 在分支上提交版本信息 3956cc3 测试两个文件提交留一个文件不提交 44449dc 这次测试一次提交三个文件 abde635 git练习测试 [llg@localhost llg-test-git]$ git tag v1.0 [llg@localhost llg-test-git]$ git tag v0.9 3a5456c [llg@localhost llg-test-git]$ git tag v0.9 v1.0
[llg@localhost llg-test-git]$ git tag v0.8 v0.9 v1.0
注意,标签不是按时间顺序列出,而是按字母排序的。可以用git show <tagname>
查看标签信息
[llg@localhost llg-test-git]$ git show v0.9 commit 3a5456c07d50d99437d01fbf538a41a4556d7504 Merge: 0ed0fe3 b68ba37 Author: llg <903857227@qq.com> Date: Sun Sep 16 16:04:56 2018 +0800 merge with no-ff
还可以创建带有说明的标签,用-a
指定标签名,-m
指定说明文字
[llg@localhost llg-test-git]$ git tag -a v0.8 -m "version 0.8 released" b68ba37 [llg@localhost llg-test-git]$ git show v0.8 tag v0.8 Tagger: llg <903857227@qq.com> Date: Mon Sep 17 09:31:27 2018 +0800 version 0.8 released commit b68ba371b04d5b7b6305454c160d7ef88178ea2d Author: llg <903857227@qq.com> Date: Sun Sep 16 16:04:19 2018 +0800 add merge diff --git a/dele b/dele index 20c0828..a36d773 100644 --- a/dele +++ b/dele @@ -10,3 +10,4 @@ sad end 1111111111111111111111 +2222222222222222222222
如果标签打错了,也可以删除
[llg@localhost llg-test-git]$ git tag -d v0.8 已删除 tag ‘v0.8‘(曾为 658e2ab)
因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。
如果要推送某个标签到远程,使用命令git push origin <tagname>
[llg@localhost llg-test-git]$ git push origin v1.0 Username for ‘https://github.com‘: 903857227@qq.com Password for ‘https://903857227@qq.com@github.com‘: Total 0 (delta 0), reused 0 (delta 0) To https://github.com/llgeill/llg-centos-git--test.git * [new tag] v1.0 -> v1.0
或者,一次性推送全部尚未推送到远程的本地标签:
[llg@localhost llg-test-git]$ git push origin --tag Username for ‘https://github.com‘: 903857227@qq.com Password for ‘https://903857227@qq.com@github.com‘: Total 0 (delta 0), reused 0 (delta 0) To https://github.com/llgeill/llg-centos-git--test.git * [new tag] v0.9 -> v0.9
首先在本地删除标签
[llg@localhost llg-test-git]$ git tag -d v0.8 已删除 tag ‘v0.9‘(曾为 3a5456c)
然后,从远程删除。删除命令是 git push origin :refs/tags/xxx
[llg@localhost llg-test-git]$ git push origin :refs/tags/v0.8 Username for ‘https://github.com‘: 903857227@qq.com Password for ‘https://903857227@qq.com@github.com‘: To https://github.com/llgeill/llg-centos-git--test.git - [deleted] v0.8
最后去官网github上查看tag是否被删除,发现确实被删除了
我们一直用GitHub作为免费的远程仓库,如果是个人的开源项目,放到GitHub上是完全没有问题的。其实GitHub还是一个开源协作社区,通过GitHub,既可以让别人参与你的开源项目,也可以参与别人的开源项目。
在GitHub出现以前,开源项目开源容易,但让广大人民群众参与进来比较困难,因为要参与,就要提交代码,而给每个想提交代码的群众都开一个账号那是不现实的,因此,群众也仅限于报个bug,即使能改掉bug,也只能把diff文件用邮件发过去,很不方便。
但是在GitHub上,利用Git极其强大的克隆和分支功能,广大人民群众真正可以第一次自由参与各种开源项目了。
如何参与一个开源项目呢?比如人气极高的bootstrap项目,这是一个非常强大的CSS框架,你可以访问它的项目主页https://github.com/twbs/bootstrap,点“Fork”就在自己的账号下克隆了一个bootstrap仓库,然后,从自己的账号下clone:
[llg@localhost 桌面]$ git clone git@github.com:llgeill/bootstrap.git 正克隆到 ‘bootstrap‘... remote: Counting objects: 126027, done. remote: Compressing objects: 100% (40/40), done. remote: Total 126027 (delta 44), reused 45 (delta 35), pack-reused 125952 接收对象中: 100% (126027/126027), 123.12 MiB | 4.97 MiB/s, done. 处理 delta 中: 100% (83420/83420), done.
一定要从自己的账号下clone仓库,这样你才能推送修改。如果从bootstrap的作者的仓库地址git@github.com:twbs/bootstrap.git
克隆,因为没有权限,你将不能推送修改。
Bootstrap的官方仓库twbs/bootstrap
、你在GitHub上克隆的仓库my/bootstrap
,以及你自己克隆到本地电脑的仓库,他们的关系就像下图显示的那样:
如果你想修复bootstrap的一个bug,或者新增一个功能,立刻就可以开始干活,干完后,往自己的仓库推送。
如果你希望bootstrap的官方库能接受你的修改,你就可以在GitHub上发起一个pull request。当然,对方是否接受你的pull request就不一定了。
使用GitHub时,国内的用户经常遇到的问题是访问速度太慢,有时候还会出现无法连接的情况(原因你懂的)。
如果我们希望体验Git飞一般的速度,可以使用国内的Git托管服务——码云(gitee.com)。
和GitHub相比,码云也提供免费的Git仓库。此外,还集成了代码质量检测、项目演示等功能。对于团队协作开发,码云还提供了项目管理、代码托管、文档管理的服务,5人以下小团队免费。
码云的免费版本也提供私有库功能,只是有5人的成员上限。
使用码云和使用GitHub类似,我们在码云上注册账号并登录后,需要先上传自己的SSH公钥。选择右上角用户头像 -> 菜单“修改资料”,然后选择“SSH公钥”,填写一个便于识别的标题,然后把用户主目录下的.ssh/id_rsa.pub
文件的内容粘贴进去:
如果我们已经有了一个本地的git仓库(例如,一个名为learngit的本地库),如何把它关联到码云的远程库上呢?
首先,我们在码云上创建一个新的项目,选择右上角用户头像 -> 菜单“控制面板”,然后点击“创建项目”:
由于刚才已经关联了github,如果用同一个tag orgin的话将会关联失败,所以我用了orgins 关联码云
[llg@localhost llg-test-git]$ git remote add origin git@gitee.com:eill/llg-test-git.git fatal: 远程 origin 已经存在。 [llg@localhost llg-test-git]$ git push -u origin master Username for ‘https://github.com‘: 903857227@qq .com Password for ‘https://903857227@qq.com@github.com‘: 分支 master 设置为跟踪来自 origin 的远程分支 master。 Everything up-to-date
这样一来,我们的本地库就可以同时与多个远程库互相同步:
[llg@localhost llg-test-git]$ git remote rm origin [llg@localhost llg-test-git]$ git remote -v origins git@gitee.com:eill/centos-git-gitee-test.git (fetch) origins git@gitee.com:eill/centos-git-gitee-test.git (push)
码云也同样提供了Pull request功能,可以让其他小伙伴参与到开源项目中来。你可以通过Fork我的仓库:https://gitee.com/liaoxuefeng/learngit,创建一个your-gitee-id.txt
的文本文件, 写点自己学习Git的心得,然后推送一个pull request给我,这个仓库会在码云和GitHub做双向同步。
在安装Git一节中,我们已经配置了user.name
和user.email
,实际上,Git还有很多可配置项。
比如,让Git显示颜色,会让命令输出看起来更醒目
[llg@localhost llg-test-git]$ git config --global color.ui true
有些时候,你必须把某些文件放到Git工作目录中,但又不能提交它们,比如保存了数据库密码的配置文件啦,等等,每次git status
都会显示Untracked files ...
,有强迫症的童鞋心里肯定不爽。
好在Git考虑到了大家的感受,这个问题解决起来也很简单,在Git工作区的根目录下创建一个特殊的.gitignore
文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。
不需要从头写.gitignore
文件,GitHub已经为我们准备了各种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:https://github.com/github/gitignore
忽略文件的原则是:
.class
文件;举个例子:
假设你在Windows下进行Python开发,Windows会自动在有图片的目录下生成隐藏的缩略图文件,如果有自定义目录,目录下就会有Desktop.ini
文件,因此你需要忽略Windows自动生成的垃圾文件:
# Windows:
Thumbs.db
ehthumbs.db
Desktop.ini
然后,继续忽略Python编译产生的.pyc
、.pyo
、dist
等文件或目录:
# Python: *.py[cod] *.so *.egg *.egg-info dist build
加上你自己定义的文件,最终得到一个完整的.gitignore
文件,内容如下:
# Windows: Thumbs.db ehthumbs.db Desktop.ini # Python: *.py[cod] *.so *.egg *.egg-info dist build # My configurations: db.ini deploy_key_rsa
最后一步就是把.gitignore
也提交到Git,就完成了!当然检验.gitignore
的标准是git status
命令是不是说working directory clean
。
使用Windows的童鞋注意了,如果你在资源管理器里新建一个.gitignore
文件,它会非常弱智地提示你必须输入文件名,但是在文本编辑器里“保存”或者“另存为”就可以把文件保存为.gitignore
了。
有些时候,你想添加一个文件到Git,但发现添加不了,原因是这个文件被.gitignore
忽略了:
$ git add App.class The following paths are ignored by one of your .gitignore files: App.class Use -f if you really want to add them.
如果你确实想添加该文件,可以用-f
强制添加到Git:
$ git add -f App.class
或者你发现,可能是.gitignore
写得有问题,需要找出来到底哪个规则写错了,可以用git check-ignore
命令检查:
$ git check-ignore -v App.class .gitignore:3:*.class App.class
Git会告诉我们,.gitignore
的第3行规则忽略了该文件,于是我们就可以知道应该修订哪个规则。
忽略某些文件时,需要编写.gitignore
;
.gitignore
文件本身要放到版本库里,并且可以对.gitignore
做版本管理!
有没有经常敲错命令?比如git status
?status
这个单词真心不好记。
如果敲git st
就表示git status
那就简单多了,当然这种偷懒的办法我们是极力赞成的。
我们只需要敲一行命令,告诉Git,以后st
就表示status
(这个功能在linux上也有)
[llg@localhost llg-test-git]$ git config --global alias.st status [llg@localhost llg-test-git]$ git st # 位于分支 dev # 尚未暂存以备提交的变更: # (使用 "git add <file>..." 更新要提交的内容) # (使用 "git checkout -- <file>..." 丢弃工作区的改动) # # 修改: dele # 修改尚未加入提交(使用 "git add" 和/或 "git commit -a")
配置Git的时候,加上--global
是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。
配置文件放哪了?每个仓库的Git配置文件都放在.git/config
文件中:
$ cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true ignorecase = true precomposeunicode = true [remote "origin"] url = git@github.com:michaelliao/learngit.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "master"] remote = origin merge = refs/heads/master [alias] last = log -1
别名就在[alias]
后面,要删除别名,直接把对应的行删掉即可。
而当前用户的Git配置文件放在用户主目录下的一个隐藏文件.gitconfig
中:
$ cat .gitconfig [alias] co = checkout ci = commit br = branch st = status [user] name = Your Name email = your@email.com
给Git配置好别名,就可以输入命令时偷个懒。我们鼓励偷懒。
在远程仓库一节中,我们讲了远程仓库实际上和本地仓库没啥不同,纯粹为了7x24小时开机并交换大家的修改。
GitHub就是一个免费托管开源代码的远程仓库。但是对于某些视源代码如生命的商业公司来说,既不想公开源代码,又舍不得给GitHub交保护费,那就只能自己搭建一台Git服务器作为私有仓库使用。
搭建Git服务器需要准备一台运行Linux的机器,强烈推荐用Ubuntu或Debian,这样,通过几条简单的apt
命令就可以完成安装。
假设你已经有sudo
权限的用户账号,下面,正式开始安装。(在这里我使用的是Centos)
第一步,安装git
[llg@localhost llg-test-git]$ sudo yum install git
第二步,创建一个git
用户,用来运行git
服务
[llg@localhost llg-test-git]$ sudo adduser git
第三步,创建证书登录:
收集所有需要登录的用户的公钥,就是他们自己的id_rsa.pub
文件,把所有公钥导入到/home/git/.ssh/authorized_keys
文件里,一行一个。
第四步,初始化Git仓库:
先选定一个目录作为Git仓库,假定是/srv/sample.git
,在/srv
目录下输入命令:
[llg@localhost llg-test-git]$ sudo git init --bare sample.git
初始化空的 Git 版本库于 /home/llg/桌面/llg-test-git/sample.git/
Git就会创建一个裸仓库,裸仓库没有工作区,因为服务器上的Git仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的Git仓库通常都以.git
结尾。然后,把owner改为git(用户权限)
[llg@localhost llg-test-git]$ sudo chown -R git:git sample.git
第五步,禁用shell登录:
出于安全考虑,第二步创建的git用户不允许登录shell,这可以通过编辑/etc/passwd
文件完成。找到类似下面的一行:
git:x:1001:1001:,,,:/home/git:/bin/bash
改为:
git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell
这样,git
用户可以正常通过ssh使用git,但无法登录shell,因为我们为git
用户指定的git-shell
每次一登录就自动退出。
第六步,克隆远程仓库:
现在,可以通过git clone
命令克隆远程仓库了,在各自的电脑上运行:
[llg@localhost llg-test-git]$ git clone git@127.0.0.1:/llg-test-git/sample.git/
剩下的推送就简单了。
如果团队很小,把每个人的公钥收集起来放到服务器的/home/git/.ssh/authorized_keys
文件里就是可行的。如果团队有几百号人,就没法这么玩了,这时,可以用Gitosis来管理公钥。
这里我们不介绍怎么玩Gitosis了,几百号人的团队基本都在500强了,相信找个高水平的Linux管理员问题不大。
有很多不但视源代码如生命,而且视员工为窃贼的公司,会在版本控制系统里设置一套完善的权限控制,每个人是否有读写权限会精确到每个分支甚至每个目录下。因为Git是为Linux源代码托管而开发的,所以Git也继承了开源社区的精神,不支持权限控制。不过,因为Git支持钩子(hook),所以,可以在服务器端编写一系列脚本来控制提交等操作,达到权限控制的目的。Gitolite就是这个工具。
这里我们也不介绍Gitolite了,不要把有限的生命浪费到权限斗争中。
经过两天的学习,站在巨人的肩膀上,我把命令都执行了一遍,大概有了个印象。
Git虽然极其强大,命令繁多,但常用的就那么十来个,掌握好这十几个常用命令,你已经可以得心应手地使用Git了。
友情附赠国外网友制作的Git Cheat Sheet,建议打印出来备用:
Git的官方网站:http://git-scm.com
作者:廖雪峰
链接:https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000
來源:廖雪峰的官方网站
原文:https://www.cnblogs.com/liliguang/p/9661646.html