Git简介

+1.Git简介

Git是目前世界上最先进的分布式版本控制系统(没有之一)。

1.1版本控制

1.手动控制版本

比较麻烦不方便使用

2.自动控制版本

能记录每次文件的改动:

 结束了手动管理多个“版本”的史前时代,进入到版本控制的20世纪。

vss:微软

cvs:开源

svn:google

git:分布式版本控制工具(标准)

1.2.Git的诞生

         很多人都知道,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了。

1.3分布式版本控制

有两个版本控制系统一个是集中式版本控制系统一个是分布式版本控制系统

1.集中式版本控制系统:版本库是集中存放在中央服务器的,用的都是自己的电 脑,所以要先从中央服务器取得最新的版本,然后开始工作,工作完成后再把自己的工作推送给中央服务器。

集中式版本控制系统最大的毛病就是必须联网才能工作,如果在局域网内还好,带宽够大,速度够 快,宽带小,网速慢。

2.分布式版本控制系统:首先,分布式版本控制系统根本没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这你工作的时候,就不需要联网了,因为版本库就在你自己的电脑上。

分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整 的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。        

 2.Git实战

2.1.安装Git

在Windows上使用Git,可以从Git官网直接下载安装程序,(网速慢的同学请移步国内镜像),然 后按默认选项安装即可。 安装完成后,在开始菜单里找到“Git”->“Git Bash”,蹦出一个类似命令行窗口的东西,就说明Git安 装成功。

安装完成后,还需要最后一步设置,在命令行输入:

2.2创建版本库

版本库又名仓库,英文名repository,可以简单理解成一个目录,这个目录里 面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪 历史,或者在将来某个时刻可以“还原”。

1.创建版本库

 如果你使用Windows系统,为了避免遇到各种莫名其妙的问题,请确保目录名(包括父目录)不包 含中文。通过 git init 命令把这个目录变成Git可以管理的仓库:

瞬间Git就把仓库建好了,而且是一个空的仓库(empty Git repository),当前目录下多了一个 .git 的目录,这个目录是Git来跟踪管理版本库的,不要手动修改这个目录里面的文件,改乱了,就把Git仓库给破坏了。

如果你没有看到 .git 目录,那是因为这个目录默认是隐藏的,用 ls -ah 命令就可以看见。

首先这里再明确一下,所有的版本控制系统,其实只能跟踪文本文件的改动,比如TXT文件,网 页,所有的程序代码等等,Git也不例外。

Microsoft的Word格式是二进制格式,因此,版本控制系统是没法跟踪Word文件的改动 的,前面我们举的例子只是为了演示,如果要真正使用版本控制系统,就要以纯文本方式编写文件。

2.添加文件到版本库

编写一个readme.txt文件

Git is a version control system.
Git is free software.

把这个文件放到learngit目录下(子目录下也可以),因为这是一个git仓库。                                      (1)用命令git add

 git add readme.txt

执行上述命令,没有任何显示,就对了

(2)用命令git commit告诉Git,把文件提交到仓库;

$ git commit -m "wrote a readme file"
[master (root-commit) eaadf4e] wrote a readme file
1 file changed, 2 insertions(+)
create mode 100644 readme.txt
$ git log
commit 6e4de12122b22ee3145dfa87af974d284f832dd2 (HEAD -> master)
Author: lxsong <lxsong77@163.com>
Date: Tue Jul 11 09:57:08 2023 +0800
wrote a readme file

git commit 命令, -m 后面输入的是本次提交的说明,可以输入任意内容,这样你就能从历史记录里方便地找到改动记录。

 git commit命令执行成功后会说,1 file changed :1个文件被改动(我们新添加的 readme.txt文件); 2 insertions :插入了两行内容(readme.txt有两行内容)。

commit 可以一次提交很多文件,所以你 可以多次 add 不同的文件,比如:

$ touch file1.txt,file2.txt,file3.txt
$ git add file1.txt
$ git add file2.txt file3.txt 或者git add . #添加所有untrack文件到stage

使用git status查看git状态

$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: file1.txt
new file: file2.txt
new file: file3.txt

 提交

$ git commit -m "add 3 files.

总结

git add . 把工作区文件添加到暂存区

git commit -m "xxxx":把暂存区(stage)文件提交到分支

git status:查看git版本库状态

git log:查看版本提交 

2.3版本控制

2.3.1修改readme.txt

修改readme.txt文件,改成如下内容:

Git is a distributed version control system.
Git is free software.

运行 git status 命令看看结果:

$ 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 status 命令可以让我们时刻掌握仓库当前的状态,上面的命令输出告诉我们, readme.txt 被修改过了,但还没有准备提交的修改。

虽然Git告诉我们 readme.txt 被修改了,看具体修改了什么内容,需要用 git diff 这个命令看看

$ git diff readme.txt
diff --git a/readme.txt b/readme.txt
index 46d49bf..9247db6 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.

git diff 顾名思义就是查看difference,显示的格式正是Unix通用的diff格式,可以从上面的命令 输出看到,我们在第一行添加了一个 distributed 单词。

知道了对 readme.txt 作了什么修改后,再把它提交到仓库就放心多了,提交修改和提交新文件是 一样的两步,第一步是 git add :

$ git add readme.txt

同样没有任何输出。在执行第二步 git commit 之前,我们再运行 git status 看看当前仓库的状 态:

$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: readme.txt

git status 告诉我们,将要被提交的修改包括 readme.txt ,下一步,就可以放心地提交了:

$ git commit -m "add distributed"
[master e475afc] add distributed
1 file changed, 1 insertion(+), 1 deletion(-)

提交后,我们再用 git status 命令看看仓库的当前状态:

$ git status
On branch master
nothing to commit, working tree clean
Git告诉我们当前没有需要提交的修改,而且,工作目录是干净(working tree clean)的。

2.3.2版本回退

1.准备三个版本

现在,你已经学会了修改文件,然后把修改提交到Git版本库,现在,再练习一次,修改readme.txt 文件如下:

Git is a distributed version control system.
Git is free software distributed under the GPL.

然后提交

$ git add readme.txt
$ git commit -m "append GPL"
[master 1094adb] append GPL
1 file changed, 1 insertion(+), 1 deletion(-)

像这样,你不断对文件进行修改,然后不断提交修改到版本库里,每当 文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在Git中被称为 commit 。 把文件改乱了,或者误删了文件,可以从最近的一个 commit 恢复。

现在,我们有四个版本readme.txt 文件一被提交到Git仓库里了:

版本一:wrote a readme file

Git is a version control system.
Git is free software.

版本二:add three file

file1, file2, file3

版本3:add distributed

file1, file2, file3

版本4:append GPL

Git is a distributed version control system.
Git is free software distributed under the GPL.

git log 命令查看版本:

$ git log
commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:06:15 2018 +0800
append GPL
commit e475afc93c209a690c39c13a46716e8fa000c366
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:03:36 2018 +0800
add distributedcommit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
append
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 20:59:18 2018 +0800
wrote a readme file

git log 命令显示从最近到最远的提交日志,我们可以看到3次提交,最近的一次是 append GPL ,上一次是 add distributed ,最早的一次是 wrote a readme file 。

输出信息太多,可以试试加上 --pretty=oneline 参数:

$ git log --pretty=oneline
1094adb7b9b3807259d8cb349e7df1d4d6477073 (HEAD -> master) append GPL
e475afc93c209a690c39c13a46716e8fa000c366 add distributed
eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0 wrote a readme file

你看到的一大串类似 1094adb... 的是 commit id (版本号),和SVN不一 样,Git的 commit id 不是1,2,3……递增的数字,而是一个SHA1计算出来的一个非常大的数字,用十 六进制表示,Git是分布式的版本控制系统,后面我们还要研究多人在同一个版本库里 工作,如果大家都用1,2,3……作为版本号,那肯定就冲突了。

2.版本回退

准备把 readme.txt 回退到上一个版本,也就是 add distributed 的那个版本

首先,Git必须知道当前版本是哪个版本,在Git中,用 HEAD 表示当前版本,也就是最新的提交 1094adb... ,上一个版本就是 HEAD^ ,上上一个版本就是 HEAD^^ ,当然往上100个版本写100个 ^ 比较容易数不过来,所以写成 HEAD~100 。

现在,我们要把当前版本 append GPL 回退到上一个版本 add distributed ,就可以使用 git reset 命令:

$ git reset --hard HEAD^
HEAD is now at e475afc add distributed

看看 readme.txt 的内容是不是版本 add distributed :

$ cat readme.txt
Git is a distributed version control system.
Git is free software.

被还原了。

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

$ git log
commit e475afc93c209a690c39c13a46716e8fa000c366 (HEAD -> master)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:03:36 2018 +0800
add distributed
commit eaadf4e385e865d25c48e7ca9c8395c3f7dfaef0
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 20:59:18 2018 +0800
wrote a readme file

最新的那个版本 append GPL 已经看不到了,那我们就可以顺着往上找,找到那个 append GPL 的 commit id 是 1094adb... ,于是就可以指定回到某个版本:

$ git reset --hard 1094a
HEAD is now at 83b0afe append GPL

版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会 找到多个版本号,就无法确定是哪一个了。

git reset 命令既可以回退版本,也可以把暂存区的修改回退到工作区。

当我们用 HEAD`时,表示最新的 版本。

--soft :仅仅移动本地库 HEAD 指针

--mixed: 移动本地库 HEAD 指针,重置暂存区

--hard:移动HEAD指针,重置暂存区,工作区,省略git checkout -- file

在查看readme.txt的内容:

$ cat readme.txt
Git is a distributed version control system.
Git is free software distributed under the GPL.

Git的版本回退速度非常快,因为Git在内部有个指向当前版本的 HEAD 指针,当你回退版本的时候, Git仅仅是把HEAD从指向 append GPL :

Git提供了一个命令 git reflog 用来记录你的每一次命令:

$ git reflog
e475afc HEAD@{1}: reset: moving to HEAD^
1094adb (HEAD -> master) HEAD@{2}: commit: append GPL
e475afc HEAD@{3}: commit: add distributed
eaadf4e HEAD@{4}: commit (initial): wrote a readme file
2.3.3工作区和暂存区

1. 工作区(Working Directory)

就是你在电脑里能看到的目录,比如我的 learngit 文件夹就是一个工作区:

2. 版本库(Repository)

工作区有一个隐藏目录 .git ,这个不算工作区,而是Git的版本库。 

(1)版本库基本状态 Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为 我们自动创建的第一个分支 master ,以及指向 master 的一个指针叫 HEAD 。

前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的:

第一步是用 git add 把文件添加进去,实际上就是把文件修改添加到暂存区;

第二步是用 git commit 提交更改,实际上就是把暂存区的所有内容提交到当前分支。

因为我们创建Git版本库时,Git自动为我们创建了唯一一个 master 分支,所以,现在, git commit 就是往 master 分支上提交更改。

可以理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。 先对 readme.txt 做个修改,比如加上一行内容: 

Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.

然后,在工作区新增一个 LICENSE 文本文件

先用 git status 查看一下状态:

$ 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 再查看一 下:

$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: LICENSE
modified: readme.txt

(2) add两次后的版本库状

git add .

执行后git版本库状态如下:

所以, git add 命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行 git commit 就可以一次性把暂存区的所有修改提交到分支。

$ git commit -m "understand how stage works"
[master e43a48b] understand how stage works
2 files changed, 2 insertions(+)
create mode 100644 LICENSE

 一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的:

$ git status
On branch master
nothing to commit, working tree clean

(3)commit后的版本库状态

2.3.4.撤销修改

上一个小节使用的

git reset --hard HEAD^ #
#等价于
git reset --mixed HEAD^
git checkout -- readme.txt

--hard:重置工作区和暂存区,修改HEAD指针

--mixed:重置暂存区,修改HEAD指针

--soft:只修改HEAD指针

git checkout:重置工作区到版本库 

你在 readme.txt 中添加了错误一行:

$ 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.
My stupid boss still prefers SVN.

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

$ 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 可以丢弃工作区的修改:

 $ git checkout -- readme.txt

命令 git checkout -- readme.txt 意思就是,把 readme.txt 文件在工作区的修改全部撤销,这里有 两种情况:

一种是 readme.txt 自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;

一种是 readme.txt 已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状 态。

总之,就是让这个文件回到最近一次 git commit 或 git add 时的状态。 现在,看看 readme.txt 的文件内容:

$ 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.

文件内容果然复原了。

git checkout -- file 命令中的 -- 很重要,没有 -- ,就变成了“切换到另一个分支”的命令,我们 在后面的分支管理中会再次遇到 git checkout 命令。 

$ 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.
My stupid boss still prefers SVN.
$ git add readme.txt

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

$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: readme.txt

git reset 命令既可以回退版本,也可以把暂存区的修改回退到工作区。

当我们用 HEAD 时,表示最新 的版本。

--soft :仅仅移动本地库 HEAD 指针

--mixed: 移动本地库 HEAD 指针,重置暂存区

--hard:移动HEAD指针,重置暂存区,工作区,省略git checkout -- file

再用 git status 查看一下,现在暂存区是干净的,工作区有修改

$ 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

总结:

#撤销修改
1:
git reset --hard HEAD^ readme.txt
--hard
--soft
--mixed
2:
git reset --mixed HEAD readme.txt
git checkout -- readme.txt
2.3.5删除文件

在Git中,删除也是一个修改操作,我们实战一下,先添加一个新文件 test.txt 到Git并且提交:

$ git add test.txt
$ git commit -m "add test.txt"
[master b84166e] add test.txt
1 file changed, 1 insertion(+)
create mode 100644 test.txt

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

$ rm test.txt

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

$ 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 :

$ git rm test.txt
rm 'test.txt'
$ git commit -m "remove test.txt"
[master d46f35e] remove test.txt
1 file changed, 1 deletion(-)
delete mode 100644 test.txt

现在,文件就从版本库中被删除了。

3.远程仓库

3.0使用远程仓库

完全可以自己搭建一台运行Git的服务器,不过也可以使用现有的Git远程库,应用比较广泛的如 下:

github

gitee

1.使用SSH协议:

ssh基于非对称加密的免密登录原理

 对称加密:密码本是一个 MD5

非对称加密,秘钥是一对(公钥/私钥) rsa

第1步:创建SSH Key。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有 id_rsa 和 id_rsa.pub 这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell (Windows下打开Git Bash),创建SSH Key:

$ ssh-keygen -t rsa -C "2048156726@qq.com
"
#用户宿主目录下.ssh c:\Users\Administrator\.ssh\

可以在用户主目录里找到 .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 文件的内容:

为什么GitHub需要SSH Key呢?因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人 冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。 当然,GitHub允许你添加多个Key。

假定你有若干电脑,你一会儿在公司提交,一会儿在家里提 交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。 

2. HTTS协议

当然不配置公钥也可以使用HTTS协议,在使用HTTPS协议时输入用户名/密码

3. 克隆 

使用https协议

git clone https://gitee.com/halo-dev/halo.git

使用ssh协议

 git clone git@gitee.com:halo-dev/halo.git

3.1添加远程库

首先,登陆Gitee,然后,在右上角找到“新建仓库”按钮,创建一个新的仓库:

注意不要选择初始化仓库和模板,要创建一个空的仓库

在Repository name填入 learngit ,其他保持默认设置,点击“创建仓库”按钮,就成功地创建了一个新 的Git仓库:

下面是gitee的提示步骤

Git 全局设置:

git config --global user.name "linfeng"
git config --global user.email "2048156726@qq.com"

创建 git 仓库:

mkdir learngit
cd learngit
git init
touch README.md
git add README.md
git commit -m "first commit"
git remote add origin git@gitee.com:lxsong77/learngit.git
git push -u origin "master"

已有仓库?

cd learngit
git remote add origin git@gitee.com:lxsong77/learngit.git
git push -u origin "master"

现在,我们根据GitHub的提示,在本地的 learngit 仓库下运行命令:

git remote add origin git@gitee.com:lxsong77/learngit.git

请千万注意,把上面的 michaelliao 替换成你自己的GitHub账户名,否则,你在本地关联的就是 我的远程库,关联没有问题,但是你以后推送是推不上去的,因为你的SSH Key公钥不在我的账户列表 中。

添加后,远程库的名字就是 origin ,这是Git默认的叫法,也可以改成别的,但是 origin 这个名 字一看就知道是远程库。

下一步,就可以把本地库的所有内容推送到远程库上:

$ git push -u origin master
Counting objects: 20, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (20/20), 1.64 KiB | 560.00 KiB/s, done.
Total 20 (delta 5), reused 0 (delta 0)
remote: Resolving deltas: 100% (5/5), done.
To 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 分支关联起来,在 以后的推送或者拉取时就可以简化命令。

提交 git push -u origin master ->省略 git push

拉取代码 git pull

推送成功后,可以立刻在gitee页面中看到远程库的内容已经和本地一模一样:

从现在起,只要本地作了提交,就可以通过命令:

$ git push origin master

把本地 master 分支的最新修改推送至GitHub,现在,你就拥有了真正的分布式版本库!

SSH警告

当你第一次使用Git的 clone 或者 push 命令连接GitHub时,会得到一个警告:

he 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.

3.2从远程库中克隆

打开另一个窗口模拟其他小伙伴下载远程库

cd f:\tmp
git clone git@gitee.com:lxsong77/learngit.git #克隆远程库到本地库
$ git log --pretty=oneline
64eb3d86d3b46095c006edd91dcf5f2f97ba4b7b (HEAD -> master, origin/master,
origin/HEAD) remove test.txt
18a4e330ee23f4df88e1cb2de7c3bf42fcd60d57 add test.txt
fc99176b6e2d3b2159b6503429611127b090ade0 understand how stage works
64800dbc350bdb87529df4a1efb5203028086926 append GPL
6793214fbc09a35878b9366a4bc3a62b4ab504dd add distributed
bd875ec1f86c22f203741414a238526bfca26279 add 3 files.
6e4de12122b22ee3145dfa87af974d284f832dd2 wrote a readme file

3.3分支管理

分支就是git世界的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里 努力学习SVN。

git相对于其他版本控制工具,分支功能非常快

3.3.1创建与合并分支

在版本回退里,你已经知道,每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。 截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即 master 分支。 HEAD 严格来说不是指 向提交,而是指向 master , master 才是指向提交的,所以, HEAD 指向的就是当前分支。

一开始的时候, master 分支是一条线,Git用 master 指向最新的提交,再用 HEAD 指向 master , 就能确定当前分支,以及当前分支的提交点:

 每次提交, master 分支都会向前移动一步,这样,随着你不断提交, master 分支的线也越来越 长。 当我们创建新的分支,例如 dev 时,Git新建了一个指针叫 dev ,指向 master 相同的提交,再把 HEAD 指向 dev ,就表示当前分支在 dev 上:

下面开始实战。首先,我们创建 dev 分支,然后切换到 dev 分支:

$ git checkout -b dev
Switched to a new branch 'dev'
git log --oneline
git log --pretty=oneline --abbrev-commit #和上面的命令等价

--pretty=oneline:显示单行

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

$ git branch dev
$ git checkout dev
Switched to branch 'dev'

然后,用 git branch 命令查看当前分支:

$ git branch
* dev
master

git branch 命令会列出所有分支,当前分支前面会标一个 * 号。

然后,我们就可以在 dev 分支上正常提交,比如对 readme.txt 做个修改,加上一行:

Creating a new branch is quick.

然后提交:

$ git add readme.txt
$ git commit -m "branch test"
[dev b17d20e] branch test
1 file changed, 1 insertion(+)

现在, dev 分支的工作完成,我们就可以切换回 master 分支:

$ git checkout master
Switched to branch 'master'

切换回 master 分支后,再查看一个 readme.txt 文件,刚才添加的内容不见了!因为那个提交是 在 dev 分支上,而 master 分支此刻的提交点并没有变:

 现在,我们把 dev 分支的工作成果合并到 master 分支上:

$ git merge dev
Updating d46f35e..b17d20e
Fast-forward
readme.txt | 1 +
1 file changed, 1 insertion(+)

git merge 命令用于合并指定分支到当前分支。合并后,再查看 readme.txt 的内容,就可以看 到,和 dev 分支的最新提交是完全一样的。

注意到上面的 Fast-forward 信息,Git告诉我们,这次合并是“快进模式”,也就是直接把 master 指 向 dev 的当前提交,所以合并速度非常快。 当然,也不是每次合并都能 Fast-forward ,我们后面会讲其他方式的合并。

合并完成后,就可以放心地删除 dev 分支了

$ git branch -d dev
Deleted branch dev (was b17d20e).

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

$ git branch
* master

因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这 和直接在 master 分支上工作效果是一样的,但过程更安全。

总结:

git checkout

git checkout -- file:把版本课的文件重置到工作区(撤销工作区的修改)

git checkout master:切换到master分支

git checkout -b dev:创建dev分支,并且切换到dev分支

git merge:合并分支

3.3.1版本冲突

准备新的 feature1 分支,继续我们的新分支开发:

$ git checkout -b feature1
Switched to a new branch 'feature1'
git branch feature1 #创建分支
git checkout feature1 #切换分支
git branch #查看分支

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

Creating a new branch is quick AND simple.

在 feature1 分支上提交:

$ git add readme.txt
$ git commit -m "AND simple"
[feature1 14096d0] AND simple
1 file changed, 1 insertion(+), 1 deletion(-)

切换到 master 分支:

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)

Git还会自动提示我们当前 master 分支比远程的 master 分支要超前1个提交。

在 master 分支上把 readme.txt 文件的最后一行改为:

Creating a new branch is quick & simple.

提交:

$ git add readme.txt
$ git commit -m "& simple"
[master 5dc6824] & simple
1 file changed, 1 insertion(+), 1 deletion(-)

现在, master 分支和 feature1 分支各自都分别有新的提交,变成了这样:

 这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲 突,我们试试看:

$ 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 也可以告诉我们冲突的文件:

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
(use "git push" to publish your local commits)
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")

我们可以直接查看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.

再提交:

$ git add readme.txt
$ git commit -m "conflict fixed"
[master cf810e4] conflict fixed

现在, master 分支和 feature1 分支变成了下图所示:

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

$ git log --graph --pretty=oneline --abbrev-commit
* cf810e4 (HEAD -> master) conflict fixed
|\
| * 14096d0 (feature1) AND simple
* | 5dc6824 & simple
|/
* b17d20e branch test
* d46f35e (origin/master) remove test.txt
* b84166e add test.txt
* 519219b git tracks changes
* e43a48b understand how stage works
* 1094adb append GPL
* e475afc add distributed
* eaadf4e wrote a readme file

最后,删除 feature1 分支:

$ git branch -d feature1
Deleted branch feature1 (was 14096d0).

完成工作

3.3.2 多人协作

当你从远程仓库克隆时,实际上Git自动把本地的 master 分支和远程的 master 分支对应起来了, 并且,远程仓库的默认名称是 origin 。

要查看远程库的信息,用 git remote :

$ git remote
origin

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

$ git remote -v
origin git@github.com:michaelliao/learngit.git (fetch)
origin git@github.com:michaelliao/learngit.git (push)

上面显示了可以抓取和推送的 origin 的地址。如果没有推送权限,就看不到push的地址。

(1)推送分支

推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就 会把该分支推送到远程库对应的远程分支上:

$ git push origin master

但是,并不是一定要把本地分支往远程推送

master 分支是主分支,因此要时刻与远程同步;

dev 分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;

bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个 bug;

feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。

总之,就是在Git中,分支完全可以在本地自己藏着玩,是否推送,视你的心情而定!

(2)抓取分支

多人协作时,大家都会往 master 和 dev 分支上推送各自的修改。

现在,模拟一个你的小伙伴,可以在另一台电脑(注意要把SSH Key添加到GitHub)或者同一台电 脑的另一个目录下克隆:

$ git clone git@github.com:michaelliao/learngit.git
Cloning into 'learngit'...
remote: Counting objects: 40, done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 40 (delta 14), reused 40 (delta 14), pack-reused 0
Receiving objects: 100% (40/40), done.
Resolving deltas: 100% (14/14), done.

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

$ git branch
* master

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

git checkout -b dev
git push origin dev
$ git checkout -b dev origin/dev #创建dev分支,同时连接远程的dev分支

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

#创建一个本地的bob分支
git checkout -b bob
touch en.txt
$ git add env.txt
$ git commit -m "add env"
[dev 7a5e5dd] add env
1 file changed, 1 insertion(+)
create mode 100644 env.txt
#切换dev分支
git checkout dev
git merge bob
$ git push origin dev
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 308 bytes | 308.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
f52c633..7a5e5dd dev -> dev

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

cd f:\mickey
git checkout -b dev origin/dev
#等价下面两个命令
git checkout -b dev
git branch --set-upstream-to=origin/dev dev
git checkout -b mickey
$ cat env.txt
env
$ git add env.txt
git commit -m "add new env"
[dev 7bd91f1] add new env
1 file changed, 1 insertion(+)
create mode 100644 env.txt
$ git push origin dev
To github.com:michaelliao/learngit.git
! [rejected] dev -> dev (non-fast-forward)
error: failed to push some refs to 'git@github.com:michaelliao/learngit.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

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

$ git pull
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-to=origin/<branch> dev

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

$ git branch --set-upstream-to=origin/dev dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.

再pull:

$ git pull
Auto-merging env.txt
CONFLICT (add/add): Merge conflict in env.txt
Automatic merge failed; fix conflicts and then commit the result.

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

$ git commit -m "fix env conflict"
[dev 57c53ab] fix env conflict
$ git push origin dev
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 621 bytes | 621.00 KiB/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
7a5e5dd..57c53ab dev -> de

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

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

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

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

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

如果 git pull 提示 no tracking information ,则说明本地分支和远程分支的链接关系没有创建, 用命令 git branch --set-upstream-to origin/ 。 这就是多人协作的工作模式,一旦熟悉了,就非常简单。

总结:查看远程库信息,使用 git remote -v ; 、

本地新建的分支如果不推送到远程,对其他人就是不可见的;

从本地推送分支,使用 git push origin branch-name ,如果推送失败,先用 git pull 抓取远 程的新提交;

在本地创建和远程分支对应的分支,使用 git checkout -b branch-name origin/branchname ,本地和远程分支的名称最好一致;

建立本地分支和远程分支的关联,使用 git branch --set-upstream branch-name origin/branch-name ;

从远程抓取分支,使用 git pull ,如果有冲突,要先处理冲突。

3.4标签管理

3.4.1.创建标签

在Git中打标签非常简单,首先,切换到需要打标签的分支上:

$ git branch
* dev
master
$ git checkout master
Switched to branch 'master'

然后,敲命令 git tag 就可以打一个新标签:

$ git tag v1.0

可以用命令 git tag 查看所有标签:

$ git tag
v1.0

默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,比如,现在已经是周五了, 但应该在周一打的标签没有打,怎么办?

方法是找到历史提交的commit id,然后打上就可以了:

$ git log --pretty=oneline --abbrev-commit
12a631b (HEAD -> master, tag: v1.0, origin/master) merged bug fix 101
4c805e2 fix bug 101
e1e9c68 merge with no-ff
f52c633 add merge
cf810e4 conflict fixed
5dc6824 & simple
14096d0 AND simple
b17d20e branch test
d46f35e remove test.txt
b84166e add test.txt
519219b git tracks changes
e43a48b understand how stage works
1094adb append GPL
e475afc add distributed
eaadf4e wrote a readme file

比方说要对 add merge 这次提交打标签,它对应的commit id是 f52c633 ,敲入命令:

$ git tag v0.9 f52c633

再用命令 git tag 查看标签:

$ git tag
v0.9
v1.0

注意,标签不是按时间顺序列出,而是按字母排序的。可以用 git show 查看标签信 息:

$ git show v0.9
commit f52c63349bc3c1593499807e5c8e972b82c8f286 (tag: v0.9)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:56:54 2018 +0800
add merge
diff --git a/readme.txt b/readme.txt

可以看到, v0.9 确实打在 add merge 这次提交上。 还可以创建带有说明的标签,用 -a 指定标签名, -m 指定说明文字:

$ git tag -a v0.1 -m "version 0.1 released" b0fbe25

用命令 git show 可以看到说明文字:

$ git show v0.1
tag v0.1
Tagger: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 22:48:43 2018 +0800
version 0.1 released
commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (tag: v0.1)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:06:15 2018 +0800
append GPL
diff --git a/readme.txt b/readme.txt

注意:标签总是和某个commit挂钩。如果这个commit既出现在master分支,又出现在dev分支, 那么在这两个分支上都可以看到这个标签。

总结: 命令 git tag 用于新建一个标签,默认为 HEAD ,也可以指定一个commit id;

命令 git tag -a -m "blablabla..." 可以指定标签信息;

命令 git tag 可以查看所有标签。

2. 操作标签如果

标签打错了,也可以删除:

$ git tag -d v0.1
Deleted tag 'v0.1' (was f15b0dd)

因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。 如果要推送某个标签到远程,使用命令 git push origin :

$ git push origin v1.0
Total 0 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
* [new tag] v1.0 -> v1.0

或者,一次性推送全部尚未推送到远程的本地标签:

$ git push origin --tags
Total 0 (delta 0), reused 0 (delta 0)
To github.com:michaelliao/learngit.git
* [new tag] v0.9 -> v0.9

如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:

$ git tag -d v0.9
Deleted tag 'v0.9' (was f52c633)

然后,从远程删除。删除命令也是push,但是格式如下:

$ git push origin :refs/tags/v0.9
To github.com:michaelliao/learngit.git
- [deleted] v0.9

要看看是否真的从远程库删除了标签,可以登陆GitHub查看。

小结:

命令 git push origin 可以推送一个本地标签;

命令 git push origin --tags 可以推送全部未推送过的本地标签;

命令 git tag -d 可以删除一个本地标签;

命令 git push origin :refs/tags/ 可以删除一个远程标签。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值