Git相关操作介绍

Git的安装及相关操作详解


DevOps: DevOps(Development和Operations的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。
它是一种重视“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通合作的文化、运动或惯例。透过自动化“软件交付”和“架构变更”的流程,来使得构建、测试、发布软件能够更加地快捷、频繁和可靠。

DevOps对应用程序发布的影响
(1)减少变更范围
与传统的瀑布式开发模型相比,采用敏捷或迭代式开发意味着更频繁的发布、每次发布包含的变化更少。由于部署经常进行,因此每次部署不会对生产系统造成巨大影响,应用程序会以平滑的速率逐渐生长。
(2)加强发布协调
靠强有力的发布协调人来弥合开发与运营之间的技能鸿沟和沟通鸿沟;采用电子数据表、电话会议、即时消息、企业门户(wiki、sharepoint)等协作工具来确保所有相关人员理解变更的内容并全力合作。
(3)自动化
强大的部署自动化手段确保部署任务的可重复性、减少部署出错的可能性。

需求

1.更小、更频繁的变更──意味着更少的风险

2.让开发人员更多地控制生产环境

3.更多地以应用程序为中心来理解基础设施

4.定义简洁明了的流程

5.尽可能地自动化

6.促成开发与运营的协作

一、Git相关简介

Git:目前世界上最先进的分布式版本控制系统(没有之一)。
创作人:林纳斯.拖瓦兹
所谓版本控制系统就是可以记录每次文件的变动,方便日后进行查看。

版本文件名用户说明日期
1service.doc张三删除了软件服务条款57/12 10:00
2service.doc李四增加了License人数限制7/23 9:20
3service.doc张三财务部门调整了合同金额7/27 11:30

公开的代码仓库

github.com  #国外
gitee.com  #国内--码云

版本控制系统分类
集中式:代码集中存储在中央服务器,开发者的客户端只有部分自己的代码,假如中央服务器出问题,会出现数据丢失。
传统的版本控制系统:CVS和SVN
分布式:每台服务器都拥有所有代码,任意一台服务器崩溃,从其他服务器复制过来就好。
Git、Mercurial和Bazaar
分布式版本控制系统各有特点,但最快、最简单也最流行的依然是Git!

二、安装Git及其相关操作

1.安装Git
可以直接yum安装,也可以从Git官网下载源码,然后解压,依次输入:./config,make,make install这几个命令安装就好了。

[root@git ~]# yum -y install epel-release
[root@git ~]# yum -y install git

2.声明自己的名字和邮箱

[root@git ~]# git config --global user.name "zj"
[root@git ~]# git config --global user.email "zj@163.com"

因为Git是分布式版本控制系统,所以每个机器都必须自报家门:你的名字和Email地址。你也许会担心,如果有人故意冒充别人怎么办?这个不必担心,真的有冒充的也是有办法可查的。
注意git config命令的–global参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。
3.创建版本库目录
什么是版本库呢?版本库又名仓库(repository),可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。

[root@git ~]# mkdir /data
[root@git ~]# cd  /data
[root@git data]# git init  #初始化当前目录为版本库会自动生成.git目录
[root@localhost .git]# ls
branches  config  description  HEAD  hooks  info  objects  refs

可以发现当前目录下多了一个.git的目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,改乱了,就把Git仓库给破坏了。

	branches  分支目录
	config    定义目录特有的配置选项
	description 仅供git web使用
	HEAD 	  指定当前的分支
	hooks	  git钩子文件
	info 	  包含一个全局排除文件(exclude)
	objects	  存放所有的数据内容 
	refs	  指针文件
	index     暂存区文件

3.上传代码步骤
首先说明一下,所有的版本控制系统,其实只能跟踪文本文件的改动,比如TXT文件,网页,所有的程序代码等等,Git也不例外。版本控制系统可以告诉你每次的改动,比如在第5行加了一个单词“Linux”,在第8行删了一个单词“Windows”。而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但没法跟踪文件的变化,只能把二进制文件每次改动串起来,也就是只知道图片从100KB改成了120KB,但到底改了啥,版本控制系统不知道,也没法知道。
不幸的是,Microsoft的Word格式是二进制格式,因此版本控制系统是没法跟踪Word文件的改动的,如果要真正使用版本控制系统,就要以纯文本方式编写文件。
因为文本是有编码的,使用标准的UTF-8编码,所有语言使用同一种编码,既没有冲突,又被所有平台所支持。
(1)编写一个readme.txt文件,一定要放到data目录下(子目录也行)。

[root@git data]# vim readme.txt
git is a version control system.
git is free software.

(2)把一个文件放到Git仓库需要两步。

[root@git data]# git add readme.txt  #告诉Git把文件添加到仓库;
[root@git data]# git commit -m "write a readme file"  #告诉Git把文件提交到仓库;
[master(root-commit) a5a9087] write a readme file
 1 file changed, 2 insertions(+)
 create mode 100644 readme.txt

git commit命令,-m后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录。
为什么Git添加文件需要add,commit一共两步呢?
因为commit可以一次提交很多文件,所以你可以多次add不同的文件,例如:

# git add file1.txt
# git add file2.txt file3.txt
# git commit -m "add 3 files."

(3)查看文件修改前后的区别

[root@git data]# vim readme.txt
Git is a distributed version control system.
Git is free software.
[root@git data]# 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: readme.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
[root@git data]# git diff readme.txt
diff --git a/readme.txt b/readme.txt
index 1668572..4d53f0e 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,2 @@
-git is a version control system.
+git is a distributed version control system.
 git is free software.

查看修改前后的difference,显示的格式正是Unix通用的diff格式。
知道了对readme.txt作了什么修改后,再把它提交到仓库就放心多了。

[root@git data]# git add readme.txt
[root@git data]# git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: readme.txt
#
[root@git data]# git commit -m "add distributed"
[master 5a93099] add distributed
 1 file changed, 1 insertion(+), 1 deletion(-)
[root@git data]# git status
# On branch master
nothing to commit (working directory clean)

(4)版本回退
不断对文件进行修改,然后不断提交修改到版本库里,每当你觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在Git中被称为commit。一旦你把文件改乱了,或者误删了文件,还可以从最近的一个commit恢复,然后继续工作,而不是把几个月的工作成果全部丢失。
现在readme.txt文件一共有2个版本被提交到Git仓库里了。

版本1:write a readme file
版本2:add distributed

当然在实际工作中,我们不可能记得一个几千行的文件每次都改了什么内容,不过不必担心,版本控制系统肯定有某个命令可以告诉我们历史记录,在Git中,我们用git log命令查看:

[root@git data]# git log
commit 5a93099542338d04bced4ef62b716541036e4c36
Author: zj <zj@163.com>
Date:   Fri Dec 4 12:08:46 2020 +0800

    add distributed

commit a5a9087691bb781a831f2708a8858c5a1e21dbe7
Author: zj <zj@163.com>
Date:   Fri Dec 4 11:49:06 2020 +0800

    write a readme file

git log命令显示从最近到最远的提交日志,我们可以看到2次提交,最近的一次是add distributed,最早的一次是write a readme file。如果嫌输出信息太多,看得眼花缭乱的,可以试试加上–pretty=oneline参数:

[root@git data]# git log --pretty=oneline
5a93099542338d04bced4ef62b716541036e4c36 add distributed
a5a9087691bb781a831f2708a8858c5a1e21dbe7 write a readme file

需要提示的是,你看到的一大串类似5a9309954…6e4c36的是commit id(版本号),和SVN不一样,Git的commit id不是1,2,3……递增的数字,而是一个SHA1计算出来的一个非常大的数字,用十六进制表示。为什么commit id需要用这么一大串数字表示呢?因为Git是分布式的版本控制系统,后面我们还要研究多人在同一个版本库里工作,如果大家都用1,2,3……作为版本号,那肯定就冲突了。
把readme.txt回退到上一个版本,也就是“write a readme file”的那个版本。
首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,也就是最新的提交5a9309954…6e4c36,上一个版本就是HEAD^, 上上一个版本就是HEAD^^, 当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100。
现在,我们要把当前版本“add distributed”回退到上一个版本“write a readme file”,就可以使用git reset命令:

[root@git data]# git reset --hard HEAD^
HEAD is now at a5a9087 write a readme file

看看readme.txt的内容是不是版本"write a readme file":

[root@git data]# cat readme.txt 
git is a version control system.
git is free software.

用git log再看看现在版本库的状态:

[root@git data]# git log
commit a5a9087691bb781a831f2708a8858c5a1e21dbe7
Author: zj <zj@163.com>
Date:   Fri Dec 4 11:49:06 2020 +0800

    write a readme file

最新的那个版本已经看不到了,但是如果还想找到它,办法其实还是有的,只要上面的命令行窗口还没有被关掉,你就可以找到那个版本的commit id,于是就可以指定回到某个版本:

[root@git data]# git reset --hard 5a93099
HEAD is now at 5a93099 add distributed
[root@localhost data]# cat readme.txt 
git is a distributed version control system.
git is free software.

Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD重指向add distributed,然后顺便把工作区的文件更新了。
现在你回退到了某个版本,关掉了电脑,第二天早上就后悔了,想恢复到新版本怎么办?在Git中,总是有办法的。Git提供了一个命令git reflog用来记录你的每一次命令:

[root@git data]# git reflog
5a93099 HEAD@{0}: reset: moving to 5a93099
a5a9087 HEAD@{1}: reset: moving to HEAD^
5a93099 HEAD@{2}: commit: add distributed
a5a9087 HEAD@{3}: commit (initial): write a readme file

(5)工作区和暂存区
Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。
工作区(Working Directory):就是你在电脑里能看到的目录data就是一个工作区。
版本库(Repository):工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。
在这里插入图片描述
前面讲了把文件往Git版本库里添加的时候,是分两步执行的:
第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;
第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。
因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以git commit就是往master分支上提交更改。
现在我们先对readme.txt做个修改,比如加上一行内容:

[root@git data]# vim readme.txt
git is a distributed version control system.
git is free software.
git has a mutable index called stage.

然后在工作区新增一个license文本文件并用git status查看状态。

[root@git data]# vim LICENSE
hello
[root@git data]# 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: readme.txt
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# LICENSE
no changes added to commit (use "git add" and/or "git commit -a")

Git非常清楚地告诉我们,readme.txt被修改了,而LICENSE还从来没有被添加过,所以它的状态是Untracked。
现在使用两次命令git add,把readme.txt和LICENSE都添加后,用git status再查看一下。

[root@git data]# git add readme.txt
[root@git data]# git add LICENSE
[root@git data]# git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: LICENSE
# modified: readme.txt
#

现在暂存区的状态就变成这样了
在这里插入图片描述
git add命令实际上就是把要提交的所有修改放到暂存区(Stage),然后执行git commit就可以一次性把暂存区的所有修改提交到分支。

[root@git data]# git commit -m "how stage works"
[master a32b354] how stage works
 2 files changed, 2 insertions(+)
 create mode 100644 LICENSE
[root@git data]# git status
# On branch master
nothing to commit (working directory clean)

一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的。现在版本库变成了这样,暂存区就没有任何内容了。
在这里插入图片描述
(6)管理修改
现在已经完全掌握了暂存区的概念。下面要讨论的就是,为什么Git比其他版本控制系统设计得优秀,因为Git跟踪并管理的是修改,而非文件。
那么什么是修改?比如你新增了一行,这就是一个修改,删除了一行,也是一个修改,更改了某些字符,也是一个修改,删了一些又加了一些,也是一个修改,甚至创建一个新文件,也算一个修改。
为什么说Git管理的是修改,而不是文件呢?通过下述演示步骤进行说明。
第一步,对readme.txt做一个修改,比如加一行内容:

[root@git data]# vim readme.txt
git is a distributed version control system.
git is free software.
git has a mutable index called stage.
git tracks changes.

然后添加

[root@git data]# git add readme.txt
[root@git data]# git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: readme.txt
#

然后再修改readme.txt

[root@git data]# vim readme.txt
git is a distributed version control system.
git is free software.
git has a mutable index called stage.
git tracks changes of files.

提交并查看状态

[root@git data]# git commit -m "git tracks changes"
[master d4f25b6] git tracks changes
1 file changed, 1 insertion(+)
[root@git data]# 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: readme.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

怎么第二次的修改没有被提交?
回顾一下操作过程:
第一次修改后git add,第二次修改后git commit。
Git管理的是修改,当你用git add命令后,在工作区的第一次修改被放入暂存区准备提交,但是在工作区的第二次修改并没有放入暂存区,所以git commit只负责把暂存区的修改提交了,也就是第一次的修改被提交了,第二次的修改不会被提交。
用git diff HEAD – readme.txt命令可以查看工作区和版本库里面最新版本的区别。

[root@git data]# git diff HEAD -- readme.txt
diff --git a/readme.txt b/readme.txt
index 76d770f..a9c5755 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,4 +1,4 @@
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
-Git tracks changes.
+Git tracks changes of files.

可见第二次修改确实没有被提交。
那怎么提交第二次修改呢?你可以继续git add再git commit,也可以别着急提交第一次修改,先git add第二次修改,再git commit,就相当于把两次修改合并后一块提交了。
(7)撤销修改
当我们修改文件在准备提交前发现了某些错误,想要避免这个错误取消修改时。

[root@git data]# vim readme.txt
git is a distributed version control system.
git is free software.
git has a mutable index called stage.
git tracks changes of files.
my god.

既然错误发现得很及时,就可以很容易地纠正它。你可以删掉最后一行,手动把文件恢复到上一个版本的状态。如果用git status查看一下

[root@git data]# 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: readme.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

可以发现Git会告诉你,git checkout – file可以丢弃工作区的修改:

[root@git data]# git checkout -- readme.txt

命令git checkout – readme.txt意思就是,把readme.txt文件在工作区的修改全部撤销,这里有两种情况:
一种是readme.txt自修改后还没有被放到暂存区,现在撤销修改就回到和版本库一模一样的状态;
一种是readme.txt已经添加到暂存区后,又作了修改,现在撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit或git add时的状态。
现在,看看readme.txt的文件内容:

[root@git data]# cat readme.txt
git is a distributed version control system.
git is free software.
git has a mutable index called stage.
git tracks changes of files.

文件内容果然复原了。
git checkout - - file命令中的- -很重要,没有- -,就变成了“切换到另一个分支”的命令。
现在假定做了修改还git add到暂存区了:

[root@git data]# vim readme.txt
git is a distributed version control system.
git is free software.
git has a mutable index called stage.
git tracks changes of files.
my god.
[root@git data]# git add readme.txt

庆幸的是在commit之前,你发现了这个问题。用git status查看一下,修改只是添加到了暂存区,还没有提交:

[root@git data]# git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: readme.txt
#

Git同样告诉我们,用命令git reset HEAD file可以把暂存区的修改撤销掉,重新放回工作区:

[root@git data]# git reset HEAD readme.txt
Unstaged changes after reset:
M readme.txt

git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。
再用git status查看一下,现在暂存区是干净的,工作区有修改:

[root@git data]# 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: readme.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

丢弃工作区的修改

[root@git data]# git checkout -- readme.txt
[root@git data]# git status
# On branch master
nothing to commit (working directory clean)

(8)删除文件
在Git中,删除也是一个修改操作,先添加一个新文件test.txt到Git并且提交:

[root@git data]# vim test.txt
ni
[root@git data]# git add test.txt
[root@git data]# git commit -m "add test.txt"
[master ced4c1e] add test.txt
 1 file changed, 1 insertion(+)
 create mode 100644 test.txt

一般情况下,你通常直接在文件管理器中把没用的文件删了,或者用rm命令删了:

[root@git data]# rm test.txt

这个时候,Git知道你删除了文件,因此工作区和版本库就不一致了,git status命令会立刻告诉你哪些文件被删除了:

[root@git data]# git status
# On branch master
# Changes not staged for commit:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted: test.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit:

[root@git data]# git rm test.txt
rm 'test.txt'
[root@git data]# git commit -m "remove test.txt"
[master 2d5e923] remove test.txt
 1 file changed, 1 deletion(-)
 delete mode 100644 test.txt

现在文件就从版本库中被删除了。
另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:

[root@git data]# git checkout -- test.txt

git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
命令git rm用于删除一个文件。如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。
4.远程仓库
Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上。怎么分布呢?最早,肯定只有一台机器有一个原始版本库,此后,别的机器可以“克隆”这个原始版本库,而且每台机器的版本库其实都是一样的,并没有主次之分。
找一台电脑充当服务器的角色,每天24小时开机,其他每个人都从这个“服务器”仓库克隆一份到自己的电脑上,并且各自把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交。
完全可以自己搭建一台运行Git的服务器,不过还有更容易的方法,那就是GitHub。从名字就可以看出,这个网站就是提供Git仓库托管服务的,所以只要注册一个GitHub账号,就可以免费获得 Git 远程仓库。
由于你的本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以需要一点设置:
第1步:创建SSH Key。

[root@git data]# ssh-keygen -t rsa -C "youremail@example.com"

你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可。由于这个Key只是测试,所以也无需设置密码。
如果一切顺利的话,可以在用户主目录里找到.ssh目录,里面有id_rsa和id_rsa.pub两个文件,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。
第2步:登陆GitHub,打开“右上角头像下拉里的Settings”,选择“SSH and GPG keys”页面:然后点击“New SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容。
为什么GitHub需要SSH Key呢?
因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。
当然GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。
最后友情提示,在GitHub上免费托管的Git仓库,任何人都可以看到(但只有你自己才能改)。所以不要把敏感信息放进去。
如果你不想让别人看到Git库,有两个办法,一个是交点保护费,让GitHub把公开的仓库变成私有的,这样别人就看不见了(不可读更不可写)。另一个办法是自己动手,搭一个Git服务器,因为是你自己的Git服务器,所以别人也是看不见的。这个方法相当简单,公司内部开发必备。
确保你拥有一个GitHub 账号后,然后继续下面的操作。
添加远程库
现在的情景是,你已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作,真是一举多得。
首先登陆GitHub,然后在右上角找到“New repository”按钮,创建一个新的仓库。
在Repository name填入data,其他保持默认设置,点击“Create repository”按钮,就成功地创建了一个新的Git仓库。
目前,在GitHub上的这个data仓库还是空的,GitHub告诉我们,可以从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后把本地仓库的内容推送到GitHub仓库。
现在,我们根据GitHub的提示,在本地的data仓库下运行命令:

[root@git data]# git remote add origin git@github.com:snow-heart/data.git

请千万注意把上面的snow-heart替换成你自己的GitHub账户名。添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。
下一步就可以把本地库的所有内容推送到远程库上:

[root@git data]# git push -u origin master
Counting objects: 19, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (19/19), done.
Writing objects: 100% (19/19), 13.73 KiB, done.
Total 23 (delta 6), reused 0 (delta 0)
To git@github.com:michaelliao/learngit.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.

把本地库的内容推送到远程,用git push命令,实际上是把当前分支master推送到远程。由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
推送成功后,可以立刻在GitHub页面中看到远程库的内容已经和本地一模一样。
从现在起,只要本地作了提交,就可以通过命令:

[root@git data]# git push origin master

把本地master分支的最新修改推送至GitHub,现在你就拥有了真正的分布式版本库!
SSH警告
当你第一次使用Git的clone或者push命令连接GitHub时,会得到一个警告。

The authenticity of host 'github.com (xx.xx.xx.xx)' can't be established.
RSA key fingerprint is xx.xx.xx.xx.xx.
Are you sure you want to continue connecting (yes/no)?

这是因为Git使用SSH连接,而SSH连接在第一次验证GitHub服务器的Key时,需要你确认GitHub的Key的指纹信息是否真的来自GitHub的服务器,输入yes回车即可。
Git会输出一个警告,告诉你已经把GitHub的Key添加到本机的一个信任列表里了。

Warning: Permanently added 'github.com' (RSA) to the list of known hosts.

这个警告只会出现一次,后面的操作就不会有任何警告了。
如果你实在担心有人冒充GitHub服务器,输入yes前可以对照GitHub的RSA Key的指纹信息是否与SSH连接给出的一致。
分布式版本系统的最大好处之一是在本地工作完全不需要考虑远程库的存在,也就是有没有联网都可以正常工作,而SVN在没有联网的时候是拒绝干活的!当有网络的时候,再把本地提交推送一下就完成了同步,真是太方便了!
从远程库克隆
先有本地库,后有远程库的时候,如何关联远程库。
现在,假设我们从零开发,那么最好的方式是先创建远程库,然后从远程库克隆。
首先,登陆GitHub,创建一个新的仓库,名字叫gitskills。创建时勾选Initialize this repository with a README,这样GitHub会自动为我们创建一个README.md文件。
远程库已经准备好了,下一步是用命令git clone克隆一个本地库:

[root@git ~]# git clone git@github.com:snow-heart/gitskills.git
Cloning into 'gitskills'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (3/3), done.
[root@git ~]# cd gitskills
[root@git gitskills]# ls
README.md

注意把Git库的地址换成你自己的,然后进入gitskills目录看看,已经有README.md文件了。
如果有多个人协作开发,那么每个人各自从远程克隆一份就可以了。你也许还注意到,GitHub给出的地址不止一个,还可以用https://github.com/snow-heart/gitskills.git这样的地址。实际上,Git支持多种协议,默认的git://使用ssh,但也可以使用https等其他协议。
使用https除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh协议而只能用https。但是通过ssh支持的原生git协议速度最快。
5.步骤详解
(1)上传到暂存区

git add readme.txt  #单个文件上传

git add .	 #当前目录所有文件上传

git status		#查看暂存区的状态

git rm --cached readme.txt	 #把文件从暂存区撤销

git checkout -- readme.txt  #已经提交到暂存区,之后再修改内容出错,想回到上次暂存区版本

(2)上传到版本库

[root@git data]# git commit -m "add readme.txt file"

(3)修改readme.txt内容,使用diff命令查看异同

[root@git data]# git diff readme.txt

如果想要提交:git add … ; git commit …

(4)查看版本状态,比对工作区和版本库的异同,并提示

[root@git data]# git status

(5)查看历史记录

[root@git data]# git log  #显示详细信息
[root@git data]# git log --oneline  #显示摘要信息

(6)版本回滚

[root@git data]# git reset --hard HEAD^  #回滚到上个版本,HEAD^^上上版本,HEAD~10回滚到上10个版本
```bash
[root@git data]# git reset --hard 2a42a3d  #通告git log 的版本号码回滚,仅写前7位就可
注意:版本回滚前,尽量备份一下当前文件

(7)回滚动作命令日志

[root@git data]# git reflog

(8)git config 常用配置选项

	git config -e 编辑配置文件 
		git config --local -e 编辑仓库级别配置文件
		git config --global -e 编辑用户级别配置文件
		git config --system -e 编辑系统级别配置文件
	git config 添加配置项目 
		git config --global user.email “you@example.com”
		git config --global user.name “Your Name”
	git config 添加别名
		git config --global -e
		添加:	
		[alias]
        st = status
        co = checkout
        br = branch
        mg = merge
        ci = commit
        md = commit --amend
        dt = difftool
        mt = mergetool
        last = log -1 HEAD
        cf = config
        line = log --oneline

6.分支
区别于主代码库,创建出来用于新增功能或模块的分支库,仅用户自己有权访问,修改后合并,一般用于更新版本或添加补丁。
(1)首先创建dev分支,然后切换到dev分支

[root@git data]# git checkout -b dev
Switched to a new branch 'dev'

git checkout命令加上-b参数表示创建并切换,相当于以下两条命令

git branch dev
git checkout dev

(2)查看分支

[root@git data]# git branch
* dev
  master

git branch命令会列出所有分支,当前分支前面会标一个*号。
然后就可以在dev分支上正常提交,比如对readme.txt做个修改,加上一行:

Creating a new branch is quick.

(3)提交

[root@git data]# git add readme.txt 
[root@git data]# git commit -m "branch test"
[dev fec145a] branch test
1 file changed, 1 insertion(+)

(4)dev分支的工作完成,切换回master分支

[root@git data]# git checkout master
Switched to branch 'master'

切换回master分支后,再查看一个readme.txt文件,刚才添加的内容不见了!因为那个提交是在dev分支上,而master分支此刻的提交点并没有变。
(5)现在把dev分支的工作成果合并到master分支上

[root@git data]# git merge dev
Updating d17efd8..fec145a
Fast-forward
readme.txt | 1 + 
1 file changed, 1 insertion(+)

git merge命令用于合并指定分支到当前分支。合并后再查看readme.txt的内容,就可以看到和dev分支的最新提交是完全一样的。
注意到上面的Fast-forward信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。当然也不是每次合并都能Fast-forward,还有其他方式的合并。
(6)合并完成后删除dev分支

[root@git data]# git branch -d dev
Deleted branch dev (was fec145a).

删除后查看branch,就只剩下master分支。

[root@git data]# git branch
* master

(7)分支合并冲突的解决
准备新的feature1分支

[root@git data]# git checkout -b feature1
Switched to a new branch 'feature1'

修改readme.txt最后一行,改为:

Creating a new branch is quick AND simple.

在feature1分支上提交

[root@git data]# git add readme.txt 
[root@git data]# git commit -m "AND simple"
[feature1 75a857c] AND simple
1 file changed, 1 insertion(+), 1 deletion(-)

切换到master分支

[root@git data]# git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.

Git还会自动提示我们当前master分支比远程的master分支要超前1个提交。
在master分支上把readme.txt文件的最后一行改为:

Creating a new branch is quick & simple.

提交

[root@git data]# git add readme.txt 
[root@git data]# git commit -m "& simple"
[master 400b400] & simple
1 file changed, 1 insertion(+), 1 deletion(-)

现在master分支和feature1分支各自都分别有新的提交。
这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突。

[root@git data]# git merge feature1
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.

Git告诉我们readme.txt文件存在冲突,必须手动解决冲突后再提交。git status也可以告诉我们冲突的文件。

[root@git data]# git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Unmerged paths:
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified: readme.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

我们可以直接查看readme.txt的内容:

[root@git data]# cat readme.txt
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1

Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,我们修改如下后保存

Creating a new branch is quick and simple.

再提交

[root@git data]# git add readme.txt 
[root@git data]# git commit -m "conflict fixed"
[master 59bc1cb] conflict fixed

用带参数的git log也可以看到分支的合并情况

[root@git data]# git log --graph --pretty=oneline --abbrev-commit
* 59bc1cb conflict fixed
|\
| * 75a857c AND simple
* | 400b400 & simple
|/
* fec145a branch test
...

最后删除feature1分支

[root@git data]# git branch -d feature1
Deleted branch feature1 (was 75a857c).

总结:当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。
用git log - -graph命令可以看到分支合并图。
(8)分支管理策略
通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
下面验证一下- -no-ff方式的git merge。
首先创建并切换dev分支:

[root@git data]# git checkout -b dev
Switched to a new branch 'dev'

修改readme.txt文件,并提交一个新的commit:

[root@git data]# git add readme.txt 
[root@git data]# git commit -m "add merge"
[dev 6224937] add merge
1 file changed, 1 insertion(+)

切换回master:

[root@git data]# git checkout master
Switched to branch 'master'

准备合并dev分支,请注意- -no-ff参数,表示禁用Fast forward:

[root@git data]# git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
readme.txt | 1 + 1 file changed, 1 insertion(+)

因为本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去。
合并后用git log看看分支历史:

[root@git data]# git log --graph --pretty=oneline --abbrev-commit
* 7825a50 merge with no-ff
|\
| * 6224937 add merge
|/
* 59bc1cb conflict fixed
...

(9)分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面操作;
操作基本上都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;
你和你的小伙伴们每个人都在dev分支上操作,每个人都有自己的分支,时不时地往dev分支上合并就可以了。
总结:合并分支时,加上- -no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。
(10)bug分支
软件开发中,bug就像家常便饭一样。有了bug就需要修复,在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它,但是当前正在dev上进行的工作还没有提交:

[root@git data]# git status
# On branch dev
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: hello.py
#
# 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: readme.txt
#

并不是不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是必须在两个小时内修复该bug,怎么办?
幸好Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:

[root@git data]# git stash
Saved working directory and index state WIP on dev: 6224937 add merge
HEAD is now at 6224937 add merge

现在用git status查看工作区,就是干净的(除非有没有被Git管理的文件),因此可以放心地创建分支来修复bug。
首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支:

[root@git data]# git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 6 commits.
[root@git data]# git checkout -b issue-101
Switched to a new branch 'issue-101'

现在修复bug,需要把“Git is free software …”改为“Git is a free software …”,然后提交:

[root@git data]# git add readme.txt 
[root@git data]# git commit -m "fix bug 101"
[issue-101 cc17032] fix bug 101
1 file changed, 1 insertion(+), 1 deletion(-)

修复完成后,切换到master分支,并完成合并,最后删除issue-101分支:

[root@git data]# git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 2 commits.
[root@git data]# git merge --no-ff -m "merged bug fix 101" issue-101
Merge made by the 'recursive' strategy.
readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
[root@git data]# git branch -d issue-101
Deleted branch issue-101 (was cc17032).

现在接着回到dev分支工作

[root@git data]# git checkout dev
Switched to branch 'dev'
[root@git data]# git status
# On branch dev
nothing to commit (working directory clean)

工作区是干净的,刚才的工作现场存到哪去了?用git stash list命令看看:

[root@git data]# git stash list
stash@{0}: WIP on dev: 6224937 add merge

工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,有两个办法:
一是用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;另一种方式是用git stash pop,恢复的同时把stash内容也删了:

[root@git data]# git stash pop
# On branch dev
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: hello.py
#
# 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: readme.txt
#
Dropped refs/stash@{0} (f624f8e5f082f2df2bed8a4e09c12fd2943bdd40)

再用git stash list查看,就看不到任何stash内容了。
你可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令:

git stash apply stash@{0}

(11)Feature分支
软件开发中,总有无穷无尽的新的功能要不断添加进来。添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以每添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。
现在接到了一个新任务:开发代号为Vulcan的新功能。
于是准备开发:

[root@git data]# git checkout -b feature-vulcan
Switched to a new branch 'feature-vulcan'

开发完毕提交

[root@git data]# git add vulcan.py
[root@git data]# git status
# On branch feature-vulcan
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: vulcan.py
#
[root@git data]# git commit -m "add feature vulcan"
[feature-vulcan 756d4af] add feature vulcan
1 file changed, 2 insertions(+)
create mode 100644 vulcan.py

切回dev,准备合并:

[root@git data]# git checkout dev

一切顺利的话,feature分支和bug分支是类似的,合并,然后删除。但是就在此时,接到上级命令,因经费不足,新功能必须取消!虽然白干了,但是这个分支还是必须就地销毁:

[root@git data]# git branch -d feature-vulcan
error: The branch 'feature-vulcan' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature-vulcan'.

销毁失败。Git友情提醒,feature-vulcan分支还没有被合并,如果删除,将丢失掉修改,如果要强行删除,需要使用命令git branch -D feature-vulcan。
现在我们强行删除:

[root@git data]# git branch -D feature-vulcan
Deleted branch feature-vulcan (was 756d4af).

(12)多人协作
当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且远程仓库的默认名称是origin。
查看远程库的信息,用git remote:

[root@git data]# git remote
origin

用git remote -v显示更详细的信息:

[root@git data]# git remote -v
origin git@github.com:snow-heart/data.git (fetch)
origin git@github.com:snow-heart/data.git (push)

上面显示了可以抓取和推送的origin的地址。如果没有推送权限,就看不到push的地址。
(13)推送分支
就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样Git就会把该分支推送到远程库对应的远程分支上:

[root@git data]# git push origin master

如果要推送其他分支,比如dev,就改成:

[root@git data]# git push origin dev

并不是一定要把本地分支往远程推送,那么,哪些分支需要推送,哪些不需要呢?
• master分支是主分支,因此要时刻与远程同步;
• dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
• bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
• feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。
(14)抓取分支
多人协作时,大家都会往master和dev分支上推送各自的修改。
现在模拟一个你的小伙伴,可以在另一台电脑(注意要把SSH Key添加到GitHub)或者同一台电脑的另一个目录下克隆:

[root@git git]# git clone git@github.com:snow-heart/data.git
Cloning into 'learngit'...
remote: Counting objects: 46, done.
remote: Compressing objects: 100% (26/26), done.
remote: Total 46 (delta 16), reused 45 (delta 15)
Receiving objects: 100% (46/46), 15.69 KiB | 6 KiB/s, done.
Resolving deltas: 100% (16/16), done.

当你的小伙伴从远程库clone时,默认情况下,你的小伙伴只能看到本地的master分支。不信可以用git branch命令看看:

[root@git git]# git branch
* master

现在你的小伙伴要在dev分支上开发,就必须创建远程origin的dev分支到本地,于是他用这个命令创建本地dev分支:

[root@git git]# git checkout -b dev origin/dev

现在他就可以在dev上继续修改,然后时不时地把dev分支push到远程:

[root@git git]# git commit -m "add /usr/bin/env"
[dev 291bea8] add /usr/bin/env
1 file changed, 1 insertion(+)
[root@git git]# git push origin dev
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 349 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:michaelliao/learngit.git
fc38031..291bea8 dev -> dev

你的小伙伴已经向origin/dev分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送:

[root@git data]# git add hello.py 
[root@git data]# git commit -m "add coding: utf-8"
[dev bd6ae48] add coding: utf-8 1 file changed, 1 insertion(+)
[root@git data]# git push origin dev
To git@github.com:michaelliao/learngit.git
! [rejected] dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@github.com:snow-heart/data.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

推送失败,因为你的小伙伴的最新提交和你试图推送的提交有冲突,解决办法也很简单,Git已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后在本地合并,解决冲突,再推送:

[root@git data]# git pull
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From github.com:michaelliao/learngit
fc38031..291bea8 dev -> origin/dev
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details
git pull <remote> <branch>
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream dev origin/<branch>

git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接:

[root@git data]# git branch --set-upstream dev origin/dev
Branch dev set up to track remote branch dev from origin.

再次pull

[root@git data]# git pull
Auto-merging hello.py
CONFLICT (content): Merge conflict in hello.py
Automatic merge failed; fix conflicts and then commit the result.

这回git pull成功,但是合并有冲突,需要手动解决,解决的方法和分支管理中的解决冲突完全一样。解决后,提交,再push:

[root@git data]# git commit -m "merge & fix hello.py"
[dev adca45d] merge & fix hello.py
[root@git data]# git push origin dev
Counting objects: 10, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 747 bytes, done.
Total 6 (delta 0), reused 0 (delta 0)
To git@github.com:michaelliao/learngit.git
291bea8..adca45d dev -> dev

因此多人协作的工作模式通常是这样:

1.首先可以试图用git push origin branch-name推送自己的修改;

2.如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;

3.如果合并有冲突,则解决冲突,并在本地提交;

4.没有冲突或者解决掉冲突后,再用git push origin branch-name推送就能成功!

如果git pull提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令git branch - -set-upstream branch-name origin/branch-name。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值