Git详细介绍和使用

git /gɪt/ 是一个开源的分布式版本控制系统

最初是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件

介绍

版本控制

版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。可以对任何类型的文件进行版本控制,便于不同的开发者协同工作

集中式版本控制

集中化的版本控制系统是为了让不同系统上的开发者协同工作。例如 SVN,它会有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人通过客户端连接到这台服务器,拉取最新的文件或者是提交更新

在这个系统中,每个人可以看到项目中其他人的工作,管理员也能很好的掌握和分配每个开发者的权限。但由于版本库是集中在服务器上的,如果出现了中央服务器的单点故障,在这个时间内,谁都无法提交更新,而且整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险

分布式版本控制

分布式的版本控制解决了集中化版本控制的一些问题,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份

更进一步,许多这类系统都可以指定和若干不同的远端代码仓库进行交互。籍此,你就可以在同一个项目中,分别和不同工作小组的人相互协作。你可以根据需要设定不同的协作流程,比如层次模型式的工作流,而这在以前的集中式系统中是无法实现的

Git 介绍

直接记录快照,而非差异比较

Git 保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照

Git 把数据看作是对小型文件系统的一组快照。 每次提交更新,或在 Git 中保存项目状态时,它会对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。Git 对待数据更像是一个快照流

近乎所有操作都是本地执行

在 Git 中的绝大多数操作都只需要访问本地文件和资源,因为你在本地磁盘上就有项目的完整历史,所以大部分操作看起来瞬间完成,

Git 保证完整性

Git 中所有数据在存储前都计算校验和,然后以校验和来引用

Git 一般只添加数据

执行的 Git 操作,几乎只往 Git 数据库中增加数据

Git安装

Linux 上安装

Linux 上使用 yum 安装,yum install git

1
$ sudo yum install git

Windows 上安装

官网上下载安装包安装即可

https://git-scm.com/downloads

配置用户信息

安装完后需要设置用户名称和邮件地址,这样做很重要,因为每一个 Git 的提交都会使用这些信息,并且它会写入到你的每一次提交中,不可更改

1
2
$ git config --global user.name "zou"
$ git config --global user.email zouxq412@foxmail.com

配置完后可以使用 git config --list 查看配置信息

使用了 –global 选项,表示全局配置,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情, Git 都会使用那些信息。 当你想针对特定项目使用不同的用户名称与邮件地址时,可以在那个项目目录下运行没有 –global 选项的命令来配置

Git 工作区、暂存区和版本库

在了解 Git 的基本操作之前,我们先来了解 Git 工作区、暂存区和版本库的概念

在 Git 中的文件有三种状态:已提交(committed): 表示数据已经安全的保存在本地数据库中;已修改(modified): 表示修改了文件,但还没保存到数据库中;已暂存(staged): 表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中

由此也引入了 Git 项目的三个工作区域的概念:

  • 工作区: 就是项目文件所在的目录
  • 暂存区: stage 或 index。一般存放在 .git/index 文件中,所以我们把暂存区有时也叫作索引
  • 版本库: 工作区下隐藏目录 .git,这里记录着仓库的版本信息和历史记录

    下面这个图展示了工作区、版本库中的暂存区和版本库之间的关系:

下面我们通过创建仓库和基本操作来了解这三个工作区域的关系

Git 仓库创建及基本操作

创建 Git 仓库

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

创建并初始化仓库

git init 是用来初始化一个 Git 仓库的

1
git init

该命令会在当前目录下创建一个 .git 的目录,这个子目录含有你初始化的 Git 仓库中所有的必须文件。这个时候只是对仓库做了初始化的操作,假如该目录下原本就有文件,需要对这些文件进行版本控制,可以通过 git add <file> 跟踪这些文件并提交 git commit -m <message>

1
2
git add .
git commit -m 'initial project version'

上面是把目录下的所有文件提交到了仓库中

克隆现有的仓库

如果想获取现有 Git 仓库的拷贝,需要用 git clone <repo>git clone <repo> <directory>

repo 是仓库地址,directory 是本地仓库的名称,即目录名称

如下面我要克隆 github 上的一个仓库,并制定本地仓库的名称为 mylibname

1
git clone https://github.com/xxxx/xxxx mylibname

基本操作

工作目录下的每一个文件有这两种状态:

  • 已跟踪 已经纳入了版本控制的文件,在上一次快照中有它们的记录,它们的状态可能处于 未修改已修改已放入暂存区
  • 未跟踪 即还没有加入到版本控制的文件

新增的未跟踪文件(untracked),和编辑过被 Git 记为已修改的文件(modified)。 通过 git add 放入暂存区(staged),然后 git commit 提交所有暂存了的修改,提交后的文件即为未修改文件(unmodified),如此反复

git文件的状态变化周期

操作命令:

  • git status 查看文件状态
  • git add 添加文件到暂存区,包括未跟踪和修改的文件
  • git diff 查看修改内容
  • git commit 提交更新,添加暂存区的内容到本地仓库
  • git reset HEAD 取消暂存
  • git rm 移除文件
  • git mv 移动文件

这里我新建一个 demo 目录作为仓库,并初始化这个仓库

接下来我们使用上面新建的仓库 demo,并对里面的文件做一些操作

先创建一个新的文件 touch readme.txt

查看当前文件状态

要查看仓库目录下哪些文件处于状态状态,使用 git status 命令

  • Untracked files 未被跟踪的文件
  • Changes not staged for commit 修改的文件
  • Changes to be committed 暂存状态文件

可以看到新建的文件 readme.txt 的文件状态为 untracked,并提示我们要通过 git add <file> 命令去跟踪我们需要提交的文件

如果使用 git status -s 命令或 git status --short 命令,将得到一种更为紧凑的格式输出

跟踪新文件/ 暂存已修改的文件

使用命令 git add <file> 开始跟踪一个文件或暂存修改的文件,会把文件添加到暂存区

1
git add readme.txt

再次使用 git status 命令查看,会看到 readme.txt 文件已经被跟踪,并处于暂存状态。Changes to be committed 下的文件就是处于暂存状态

git add 命令使用文件或目录的路径作为参数;如果参数是目录的路径,该命令将递归地跟踪该目录下的所有文件

该命令还用来暂存已修改的文件,就是 Changes not staged for commit下的文件

查看已暂存和未暂存的修改

这个时候假如我们对暂存的文件进行了修改

1
echo 'My Project' > readme.txt

使用 git status 命令查看,我们可以看到多出了一个未被暂存,被修改状态的文件,而使用 git status 命令并不能看到具体修改的地方,这时可以用 git diff 来查看未暂存的文件更新的部分

输入 git diff 来查看未暂存的文件更新的部分

该命令比较的是工作目录中当前文件和暂存区域快照之间的差异, 也就是修改之后还没有暂存起来的变化内容。若要查看已暂存的将要添加到下次提交里的内容,可以用 git diff --cached 命令

  • git diff 尚未暂存的改动
  • git diff --cached 查看已暂存的改动

提交更新

当暂存区的文件已经准备妥当可以提交了,可以通过命令 git commit 来提交,或者直接加入说明 git commit -m <message>

1
git commit

这种方式会启动文本编辑器以便输入本次提交的说明(这里的编辑器是 vim)

使用 git commit -m <message>

请记住,提交时记录的是放在暂存区域的快照。 任何还未暂存的仍然保持已修改状态,可以在下次提交时纳入版本管理。 每一次运行提交操作,都是对你项目作一次快照,以后可以回到这个状态,或者进行比较

跳过使用暂存区域

Git 提供了一个跳过使用暂存区域的方式, 只要在提交的时候,给 git commit 加上 -a 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤

取消暂存

git reset HEAD <file>... 用来取消已暂存的内容

我们修改下 readme.txt ,并 git add 到暂存区

1
2
echo 'modify content' > readme.txt
git add readme.txt

通过 git status 可以看到 readme.txt 已经添加到暂存区了

假如这时不想把它提交更新并加入到下一次的快照当中,就可以通过git reset HEAD <file> 去取消已暂存的内容

1
git reset HEAD readme.txt

撤消对文件的修改

如果你想撤销对文件的修改,也就是还原到未修改前的状态,可以使用 git checkout -- [file] 命令。该命令会取消你对该文件做的修改内容。

移除文件

假如我们现在去删除 readme.txt 这个文件,再通过 git status 查看状态

它是会记录我们的删除操作,标记为未暂存的文件

然后再运行 git rm <file> 记录此次移除文件的操作

下次提交更新 git commit 之后,该文件就不再纳入版本控制管理中了

如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项 -f,即git rm -f <file>

如果把文件从暂存区域移除,但仍然希望保留在当前工作目录中,换句话说,仅是从跟踪清单中删除,使用 --cached 选项即可

移动文件

git mv 命令用于移动或重命名一个文件、目录、软连接。

我们先撤销对 readme.txt 文件的删除

1
2
git reset HEAD readme.txt
git checkout -- readme.txt

然后使用 git mv 对其重命名

1
git mv readme.txt README.md

Git 主要功能

远程仓库(github)

上面我们有关的命名操作都是本地执行,如果需要分享代码或与其他开发人员协同工作,就需要连接远程仓库了。这个远程仓库是指托管在因特网或其他网络中的你的项目的版本库

这里我们使用 github 作为远程仓库

添加远程仓库

运行 git remote add <shortname> <url> 添加一个新的远程 Git 仓库

这里是以 github 做为远程仓库为例,本地 Git 仓库和 GitHub 仓库之间的传输是通过SSH加密的,我们需要先配置验证信息

使用以下命令生成 SSH Key

1
ssh-keygen -t rsa -C "zouxq412@foxmail.com"

这里的邮箱为你 github 账号绑定的邮箱,生成 SSH Key 后我们去到它保存的路径 ~/.ssh 下打开 id_rsa.pub 复制里面的公钥

然后我们去到 github 的设置中心找到 SSH and GPG keys,点击 New SSH key 设置 SSH 公钥

将复制的 SSH 公钥添加并保存

为了验证是否成功,使用命令 ssh -T git@github.com

接下来我们在 github上去新建一个仓库

创建成功后,显示以下信息

上面会有一些提示,允许我们创建一个新的本地仓库或把已存在的仓库推到 github仓库上,这里我们直接把直接的 demo 上传到这个 github 仓库中

1
2
git remote add origin https://github.com/Morgan412/demo.git
git push -u origin master

第一次会让我们输入 github 的账号密码

git push 会把本地仓库的内容推送到远程仓库中,这时可以刷新下 github 的仓库页面,会发现已经有上传的内容了

推送到远程仓库的命令为: git push [remote-name] [branchname]

查看当前的远程仓库

查看你已经配置的远程仓库服务器,可以运行 git remote 命令, 它会列出你指定的每一个远程服务器的简写,也可以指定选项 -v,会显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL

如果想要查看某一个远程仓库的更多信息,可以使用 git remote show [remote-name] 命令

从远程仓库中抓取与拉取

从远程仓库获取数据的命令

1
git fetch [remote-name]

这个命令会访问远程仓库,从中拉取所有你还没有的数据,git fetch origin 会抓取克隆(或上一次抓取)后新推送的所有工作。 必须注意 git fetch 命令会将数据拉取到你的本地仓库,当它并不会自动合并或修改你当前的工作,这个时候我们可以运行 git merge [alias]/[branch] 来合并分支

现在我们在 github 的远程仓库中修改 README.md 文件,然后运行 git fetch origin

可以看到信息提示远程仓库的 master 有更新,这时再运行 git merge origin/master

README.md 文件被合并更新

如果你有一个分支设置为跟踪一个远程分支,可以使用 git pull <remote> <branch> 命令来自动的抓取然后合并远程分支到当前分支。默认情况下,git clone 命令会自动设置本地 master 分支跟踪克隆的远程仓库的 master 分支。 运行 git pull 通常会从最初克隆的服务器上抓取数据并自动尝试合并到当前所在的分支。

远程仓库的移除与重命名

git remote rename 去修改一个远程仓库的简写名
git remote rm [name] 可以去移除一个远程仓库

标签管理

Git 可以给历史中的某一个提交打上标签,可以使用这个功能来标记发布结点(如v1.0),标签也是版本库的一个快照

命令:

  • git tag 查看所有的标签
  • git tag <name> 创建标签
  • git tag -v <name> -m <message> 创建附注标签
  • git push origin [tagname] 推送标签到远程仓库
  • git tag -d <tagname> 删除本地仓库标签
  • git push <remote> :refs/tags/<tagname> 更新远程仓库标签

创建标签

创建标签命令 git tag <name>

git tag -v <name> -m <message> 可以创建一个附注标签

首先我们要切换到需要打标签的分支上,然后使用创建标签命令 git tag <name>

1
git tag v1.0.0

git tag 可以查看所有的标签

后期打标签

默认情况下,标签是打在最新提交 commit 上的,假如我想对过去的某个历史快照节点打标签呢?其实也是可以的

git log --pretty=oneline --abbrev-commit 查看历史 commit id,然后打上对应标签即可

1
2
3
4
$ git log --pretty=oneline --abbrev-commit
459f6fc (HEAD -> master, origin/master) Update README.md
e241626 修改内容
a9dec5d first commit

如我要给 “修改内容” 这次 commit 打上标签

1
git tag v1.0.1 e241626

标签操作

共享标签

默认情况下,git push 命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到共享服务器上。 这个过程就像共享远程分支一样,你可以运行 git push origin [tagname]

如果想要一次性推送很多标签,也可以使用带有 --tags 选项的 git push 命令。 这将会把所有不在远程仓库服务器上的标签全部传送到那里

删除标签

使用命令 git tag -d <tagname> 可以删除掉本地仓库上的标签

上面的命令并不会从任何远程仓库中移除这个标签,如果要移除远程仓库的标签必须使用 git push <remote> :refs/tags/<tagname> 来更新你的远程仓库

分支管理

分支就像是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN

如果两个平行宇宙互不干扰,那对现在的你也没啥影响。不过,在某个时间点,两个平行宇宙合并了,结果,你既学会了Git又学会了SVN!

在实际工作中,当你需要完成某个需要,而这个需求你可能需要2天才能完成,这时你可以新建一个分支,在分支上的任何改动都是不影响其他分支的,当你完成了需求之后,再合并分支

命令:

  • git branch 列出分支,当前分支前面会标一个*
  • git branch <branchname> 创建分支
  • git checkout <branchname> 切换分支
  • git checkout -b <branchname> 创建并切换分支
  • git merge 合并分支
  • git branch -d <branchname> 删除分支

分支的创建和合并

创建分支

1
2
git branch <branchname>
git checkout <branchname>

想要新建一个分支并同时切换到那个分支上,你可以运行一个带有 -b 参数的 git checkout 命令,等同于上面两个命令

1
git checkout -b newb

git branch 列出分支,当前分支前面会标一个*

合并分支

一旦某分支有了独立内容,你终究会希望将它合并回到你的主分支

使用 git merge 命令将任何分支合并到当前分支中去

假如我们现在对新的分支内容有了修改,现在要合并到主分支中

合并冲突

当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成

解决冲突就是把 Git 合并失败的文件手动编辑为我们希望的内容,再提交