Git安装、配置和使用
目录
一、Git 简介
什么是“版本控制”? 版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。在 CODE CHINA
中,我们对保存着软件源代码的文件作版本控制,但实际上,你可以对任何类型的文件进行版本控制。
使用版本控制系统通常还意味着,就算你乱来一气把整个项目中的文件改的改删的删,你也照样可以轻松恢复到原先的样子。 但额外增加的工作量却微乎其微。
版本控制系统发展可以分为三个阶段:
- 本地版本控制系统
- 集中式版本控制系统
- 分布式版本控制系统
Git是一个开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理。
二、Git安装
在 Windows 上安装
Git 官网下载对应系统的软件了,下载地址为
或者
无脑安装,修改安装地址,其他默认。 安装完成如图。
三、Git环境配置
好了,当你当完成了 Git
的安装后,接下来我们就需要对 Git
进行一些必要的环境配置。
通常情况下,每台计算机上只需要配置一次 Git
,当 Git
程序升级时会保留配置信息。 你可以在任何时候再次通过运行 git config
命令来修改它们。
Git
自带一个 git config
的工具来帮助设置控制 Git
外观和行为的配置变量。
配置用户名和邮件地址
安装完 Git
之后,要做的第一件事就是设置你的用户名和邮件地址。 这一点很重要,因为每一个 Git
提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改:
再次强调,如果使用了 --global
选项,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情, Git
都会使用那些信息。
当你想针对特定项目使用不同的用户名称与邮件地址时,可以在那个项目目录下运行没有 --global
选项的命令来配置。
检查配置
git config --list
命令来列出所有 Git
当时能找到的配置。
通过以下命令查看所有的配置以及它们所在的文件
Git 颜色配置
让 Git
显示颜色,会让命令输出看起来更醒目
Git忽略文件配置
日常使用中,我们一般不需要从头开始编辑.gitignore
文件,已经有各种现成的种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:
忽略文件的原则是:
- 忽略操作系统自动生成的文件,比如缩略图等;
- 忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如
Java
编译产生的.class文件; - 忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。
让我们来看一个例子:
假设你在 Windows
下进行 Python
开发,Windows
会自动在有图片的目录下生成隐藏的缩略图文件,如果有自定义目录,目录下就会有Desktop.ini
文件,因此你需要忽略 Windows
自动生成的垃圾文件:
.gitignore 文件
然后,继续忽略Python
编译产生的.pyc、.pyo、dist
等文件或目录:
.gitignore 文件
加上你自己定义的文件,最终得到一个完整的.gitignore
文件,内容如下:
.gitignore 文件
最后一步就是把.gitignore
也提交到 Git
,就完成了!当然检验.gitignore
的标准是git status
命令是不是说working directory clean
。
有些时候,你想添加一个文件到 Git
,但发现添加不了,原因是这个文件被.gitignore
忽略了:
强制添加被忽略文件
如果你确实想添加该文件,可以用-f
强制添加到 Git
:
或者你发现,可能是.gitignore
写得有问题,需要找出来到底哪个规则写错了,可以用git check-ignore
命令检查:
检查忽略规则
Git会告诉我们,.gitignore
的第3行规则忽略了该文件,于是我们就可以知道应该修订哪个规则。
还有些时候,当我们编写了规则排除了部分文件时:
但是我们发现.*
这个规则把.gitignore
也排除了,并且App.class
需要被添加到版本库,但是被*.class
规则排除了。
添加例外规则
这个时候,虽然可以用git add -f
强制添加进去,但我们建议你可以添加两条例外规则:
把指定文件排除在.gitignore
规则外的写法就是!+文件名
,所以,只需把例外文件添加进去即可。
Git 配置别名
除了通过 配置忽略文件 来提高git commit
时的便捷性外,Git
中还有一种可以让大家在敲入 Git
命令时偷懒的办法——那就是配置 Git
别名。
配置 git status
/commit/checkout/branch
比如在使用git status
命令时,我们可以通过配置别名的方式将其配置为git st
,这样在使用时是不是就比输入 git status
简单方便很多呢?
我们只需要敲一行命令,告诉 Git
,以后st
就表示status
:
当然还有别的命令可以简写,很多人都用co
表示checkout
,ci
表示commit
,br
表示branch
:
配置别名
配置完成以上别名后,以后提交就可以简写成:
配置 git reset HEAD file
再比如git reset HEAD file
命令,他可以把暂存区的修改撤销掉(unstage),重新放回工作区。既然是一个unstage
操作,就可以配置一个unstage
别名:
当你敲入命令:
实际上 Git
执行的是:
Git 配置文件
这些自定义的Git
配置文件通常都存放在仓库的.git/config
文件中。
四、Git的使用
我们先来认识一下版本库——Repository,接下来我们所有提到的 Git
基础命令,都是基于版本库的。
版本库又名仓库,英文名 repository
,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git
管理起来,每个文件的修改、删除,Git
都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
在已存在目录中初始化仓库 —— git init
首先,选择一个合适的地方,创建一个空目录:
第二步,通过git init
命令把这个目录变成 Git
可以管理的仓库:
瞬间 Git
就把仓库建好了,而且告诉你是一个空的仓库(empty Git
repository),同时在当前目录下多了一个.git
的目录,这个目录是 Git
来跟踪管理版本库的,如果你没有看到 .git 目录,那是因为这个目录默认是隐藏的,用ls -ah
命令就可以看到了。
克隆现有的仓库 —— git clone
如果你想获得一份已经存在了的 Git
仓库的拷贝,比如说,你想为某个开源项目贡献自己的一份力,这时就要用到 git clone
命令,Git
克隆的是该 Git
仓库服务器上的几乎所有数据,而不是仅仅复制完成你的工作所需要文件。
git clone
当你执行 git clone
命令的时候,默认配置下远程 Git
仓库中的每一个文件的每一个版本都将被拉取下来。
克隆仓库的命令是 git clone <url>
。 比如,要克隆 Git
的链接库 libgit2,可以用下面的命令:
这会在当前目录下创建一个名为 help-docs
的目录,并在这个目录下初始化一个 .git
文件夹, 从远程仓库拉取下所有数据放入 .git
文件夹,然后从中读取最新版本的文件的拷贝。 如果你进入到这个新建的 help-docs
文件夹,你会发现所有的项目文件已经在里面了,准备就绪等待后续的开发和使用。
自定义本地仓库名称
当然如果你想在克隆远程仓库的时候,自定义本地仓库的名字也是可以的,你可以通过额外的参数指定新的目录名:
这会执行与上一条命令相同的操作,但目标目录名变为了 mydocs
。
Git 支持多种数据传输协议。 上面的例子使用的是 https://
协议,不过你也可以使用 git://
协议或者使用 SSH
传输协议,比如 user@server:path/to/repo.git
。
编辑并添加文件
接下来,我们来尝试在已经准备好的 Git
仓库中编辑一个readme.txt
文件,内容如下:
接下来,我们可以通过2个命令将刚创建好的readme.txt
添加到Git
仓库:
第一步,用命令git add
告诉 Git
,把文件添加到仓库:
执行上面的命令,没有任何显示,说明添加成功。
提交变动到仓库
第二步,用命令git commit
告诉 Git
,把文件提交到仓库:
这里简单解释一下
git commit
命令,-m
后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录。
git commit
命令执行成功后会告诉你:
- 1 file changed:1个文件被改动(我们新添加的
readme
.txt文件) - 2 insertions:插入了两行内容(readme.txt有两行内容)
为什么 Git
添加文件需要add,commit
一共两步呢?因为commit
可以一次提交很多文件,所以你可以多次add
不同的文件,比如:
查看Git
仓库当前状态变化
我们已经成功地添加并提交了一个readme.txt
文件,接下来让我们继续修改readme.txt
文件,改成如下内容:
查看 git status
结果
现在,运行git status
命令看看结果:
git status
命令可以让我们时刻掌握仓库当前的状态,上面的命令输出告诉我们,readme.txt
被修改过了,但还没有准备提交的修改。
比较变动
虽然 Git
告诉我们readme.txt
被修改了,但并没有告诉我们具体修改的内容是什么,假如刚好是上周修改的,等到周一来班时,已经记不清上次怎么修改的readme.txt
,这个时候我们就需要用git diff
这个命令查看相较于上一次暂存都修改了些什么内容了:
运行 git diff
命令
git diff
顾名思义就是查看 difference,显示的格式正是 Unix 通用的 diff
格式,可以从上面的输出看到,我们在第一行添加了一个distributed
单词。
综合操作
知道了对readme.txt
作了什么修改后,再把它提交到仓库就放心多了,提交修改和提交新文件是一样的两步,第一步是git add
:
git add
同样没有任何输出。在执行第二步git commit
之前,我们再运行git status
看看当前仓库的状态:
git status
git status
告诉我们,将要被提交的修改包括readme.txt
,下一步,就可以放心地提交了:
** git commit**
提交后,我们再用git status
命令看看仓库的当前状态:
** git status**
Git告诉我们当前没有需要提交的修改,而且,工作目录是干净(working tree clean)的。
查看日志
如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline
参数:
** git log --pretty=oneline**
每提交一个新版本,实际上 Git 就会把它们自动串成一条时间线。如果使用可视化工具或者之前在 git 自定义配置中介绍的 git lg
命令,就可以更清楚地看到提交历史的时间线:
作为一个优秀的版本控制系统,Git 能够让我们查看每一次提交的记录。在日常的工作中,我们可以随时对 Git 仓库中的内容进行修改,,每当你觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在 Git中 被称为commit / 提交
。一旦你把文件改乱了,或者误删了文件,还可以从最近的一个commit
恢复,然后继续工作,而不是把几个月的工作成果全部丢失。
** git log**
在 Git 中,我们可以通过git log
命令查看全部的commit
记录:
git log
命令显示从最近到最远的提交日志,我们可以看到2次提交,最近的一次是add distributed
,最早的一次是wrote a readme file
。
Git 回退
这个时候,假设我们需要将 readme.txt
回退到上一个版本,也就是 wrote a readme file
的这个版本,我们需要怎么操作呢?
首先,Git 必须知道当前版本是哪个版本,在 Git 中,用HEAD
表示当前版本,也就是最新的提交e55063a
,上一个版本就是HEAD^
,上上一个版本就是HEAD^^
,当然往上100个版本写100个^
比较容易数不过来,所以写成HEAD~100
。
现在,我们要把当前版本add distributed
回退到上一个版本wrote a readme file
,就可以使用git reset
命令:
** git reset**
现在让我们看看readme.txt
的内容是不是版本wrote a readme file
:
果然还原到最初wrote a readme file
这个版本了。
Git 的版本回退速度非常快,因为 Git 在内部有个指向当前版本的HEAD
指针,当你回退版本的时候,Git 仅仅是把HEAD
从指向add distributed
:
** HEAD 指针移动记录**
改为指向wrote a readme file
:
然后顺便把工作区的文件更新了。所以你让HEAD
指向哪个版本号,你就把当前版本定位在哪。
Git重置
现在,你回退到了某个版本,关掉了电脑,第二天早上就后悔了,想恢复到新版本怎么办?找不到新版本的commit id
怎么办?
好在 Git 提供了一个命令git reflog
用来记录你的每一次命令,当你用git reset --hard HEAD^
回退到wrote a readme file
版本时,再想恢复到add distributed
,就可以通过git reflog
命令找到add distributed
的commit id
。
** git reflog**
从上面的输出可以看到,add distributed
的commit id
是e55063a
,现在,我们就可以通过 git reset --hard e55063a
切换到最新的版本上了。
工作区和暂存区
Git 和其他版本控制系统如 SVN 的一个不同之处就是有暂存区的概念。
就是你在电脑里能看到的目录,比如我的learngit文件夹就是一个工作区:
工作区有一个隐藏目录.git
,这个不算工作区,而是 Git 的版本库。
Git 的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)
的暂存区,还有 Git 为我们自动创建的第一个分支master,以及指向 master
的一个指针叫HEAD
。
前面讲了我们把文件往 Git 版本库里添加的时候,是分两步执行的:
- 第一步是用
git add
把文件添加进去,实际上就是把文件修改添加到暂存区; - 第二步是用
git commit
提交更改,实际上就是把暂存区的所有内容提交到当前分支。
因为我们创建 Git 版本库时,Git自动为我们创建了唯一一个master
分支,所以,现在,git commit
就是往 master
分支上提交更改。
你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。
现在,我们来试一下,先对readme.txt
做个修改,比如加上一行内容:
然后,在工作区新增一个LICENSE文本文件。
先用git status
查看一下状态:
Git非常清楚地告诉我们,readme.txt
被修改了,而LICENSE
还从来没有被添加过,所以它的状态是Untracked
。
现在,使用两次命令git add
,把readme.txt
和LICENSE
都添加后,用git status
再查看一下:
现在,暂存区的状态就变成这样了.
所以,git add
命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit
就可以一次性把暂存区的所有修改提交到分支。
一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的:
现在版本库变成了这样,暂存区就没有任何内容了
五、版本管理
撤消修改(git commit --amend)
在任何一个阶段,你都有可能想要撤消某些操作。接下来,我们将会学习几个撤消你所做修改的基本命令。 注意,有些撤消操作是不可逆的。 这是在使用 Git
的过程中,会因为操作失误而导致之前的工作丢失的少有的几个地方之一。
有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 --amend 选项的提交命令来重新提交:
git commit --amend
这个命令会将暂存区中的文件提交。 如果自上次提交以来你还未做任何修改(例如,在上次提交后马上执行了此命令), 那么快照会保持不变,而你所修改的只是提交信息。
文本编辑器启动后,可以看到之前的提交信息。 编辑后保存会覆盖原来的提交信息。
例如,你提交后发现忘记了暂存某些需要的修改,可以像下面这样操作:
最终你只会有一个提交——第二次提交将代替第一次提交的结果。
当你在修补最后的提交时,并不是通过用改进后的提交 原位替换 掉旧有提交的方式来修复的, 理解这一点非常重要。从效果上来说,就像是旧有的提交从未存在过一样,它并不会出现在仓库的历史中。
修补提交最明显的价值是可以稍微改进你最后的提交,而不会让“啊,忘了添加一个文件”或者 “小修补,修正笔误”这种提交信息弄乱你的仓库历史。
取消暂存的文件(git reset)
接下来我们看看如何操作暂存区和工作目录中已修改的文件。 这些命令在修改文件状态的同时,也会提示如何撤消操作。例如,你已经修改了两个文件并且想要将它们作为两次独立的修改提交, 但是却意外地输入 git add *
暂存了它们两个。如何只取消暂存两个中的一个呢? git status
命令提示了你:
在 “Changes to be committed” 文字正下方,提示使用 git reset HEAD <file>...
来取消暂存。 所以,我们可以这样来取消暂存 readme.txt
文件:
git reset HEAD
这个命令有点儿奇怪,但是起作用了。 readme.txt
文件已经是修改未暂存的状态了。
git reset
确实是个危险的命令,如果加上了--hard
选项则更是如此。 然而在上述场景中,工作目录中的文件尚未修改,因此相对安全一些。
撤消对文件的修改(git --checkout)
如果你并不想保留对 readme.txt
文件的修改怎么办? 你该如何方便地撤消修改——将它还原成上次提交时的样子(或者刚克隆完的样子,或者刚把它放入工作目录时的样子)? 幸运的是,git status
也告诉了你应该如何做。 在最后一个例子中,未暂存区域是这样:
它非常清楚地告诉了你如何撤消之前所做的修改。 让我们来按照提示执行:
git checkout – file
可以看到那些修改已经被撤消了。
请务必记得
git checkout -- <file>
是一个危险的命令。 你对那个文件在本地的任何修改都会消失——Git 会用最近提交的版本覆盖掉它。 除非你确实清楚不想要对那个文件的本地修改了,否则请不要使用这个命令。
删除文件
在 Git
中,删除也是一个修改操作,我们先添加一个新文件test.txt
到 Git
并且提交:
一般情况下,你通常直接在文件管理器中把没用的文件删了,或者用rm
命令删了:
这个时候,Git知道你删除了文件,因此,工作区和版本库就不一致了,git status命令会立刻告诉你哪些文件被删除了:
现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令git rm
删掉,并且git commit
:
现在,文件就从版本库中被删除了。
小提示:先手动删除文件,然后使用
git rm
和git add
效果是一样的。
另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:
git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
注意:从来没有被添加到版本库就被删除的文件,是无法恢复的!
小结
好了,以上这些就是 git
中关于撤销以及删除文件的相关内容了,让我们来回顾一下:
- 场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令
git checkout -- file
- 场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令
git reset HEAD <file>
,就回到了场景1,第二步按场景1操作
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,可以用命令git reset --hard commit_id
,不过前提是没有推送到远程库。
六、分支管理
前面我们了解到,Git 保存的不是文件的变化或者差异,而是一系列不同时刻的 快照 。
在进行提交操作时,Git 会保存一个提交对象(commit object)。 知道了 Git
保存数据的方式,我们可以很自然的想到——该提交对象会包含一个指向暂存内容快照的指针。 但不仅仅是这样,该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。 首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象, 而由多个分支合并产生的提交对象有多个父对象,
为了更加形象地说明,我们假设现在有一个工作目录,里面包含了三个将要被暂存和提交的文件。 暂存操作会为每一个文件计算校验和,然后会把当前版本的文件快照保存到 Git 仓库中 (Git 使用 blob
对象来保存它们),最终将校验和加入到暂存区域等待提交:
当使用 git commit
进行提交操作时,Git 会先计算每一个子目录(本例中只有项目根目录)的校验和, 然后在 Git
仓库中这些校验和保存为树对象。随后,Git 便会创建一个提交对象, 它除了包含上面提到的那些信息外,还包含指向这个树对象(项目根目录)的指针。 如此一来,Git 就可以在需要的时候重现此次保存的快照。
现在,Git 仓库中有五个对象:三个 blob
对象(保存着文件快照)、一个 树 对象 (记录着目录结构和 blob
对象索引)以及一个 提交 对象(包含着指向前述树对象的指针和所有提交信息)。
这里用到再说,有点复杂。
七、Git标签
Git 的标签虽然是版本库的快照,但其实它就是指向某个commit
的指针(跟分支很像对不对?但是分支可以移动,标签不能移动),所以,创建和删除标签都是瞬间完成的。
tag
就是一个让人容易记住的有意义的名字,它跟某个commit
绑在一起。
创建标签
在 Git
中创建标签非常简单,首先,切换到需要打标签的分支上:
然后,敲命令git tag <name>
就可以打一个新标签:
可以用命令git tag
查看所有标签:
默认标签是打在最新提交的commit
上的。有时候,如果忘了打标签,比如,现在已经是周五了,但应该在周一打的标签没有打,怎么办?
方法是找到历史提交的commit id
,然后打上就可以了:
比方说要对conflict fixed
这次提交打标签,它对应的commit id
是fb8b190
,敲入命令:
再用命令git tag
查看标签:
注意,标签不是按时间顺序列出,而是按字母排序的。可以用git show
查看标签信息:
可以看到,v0.9确实打在conflict fixed
这次提交上。
还可以创建带有说明的标签,用-a
指定标签名,-m
指定说明文字:
用命令git show <tagname>
可以看到说明文字:
注意:标签总是和某个
commit
挂钩。如果这个commit
既出现在master
分支,又出现在dev
分支,那么在这两个分支上都可以看到这个标签。
标签操作
如果标签打错了,也可以删除:
因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。
如果要推送某个标签到远程,使用命令git push origin <tagname>
:
或者,一次性推送全部尚未推送到远程的本地标签:
如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:
然后,从远程删除。删除命令也是push
,但是格式如下:
要看看是否真的从远程库删除了标签,可以登录 CODE CHINA
上查看。
小结
以上就是 git tag
操作的内容介绍了,现在让我们来回顾一下:
- 命令
git push origin <tagname>
可以推送一个本地标签; - 命令
git push origin --tags
可以推送全部未推送过的本地标签; - 命令
git tag -d <tagname>
可以删除一个本地标签; - 命令
git push origin :refs/tags/<tagname>
可以删除一个远程标签 - 命令
git tag -a <tagname> -m 'messages'
可以创建一个带附注的标签 - 命令
git tag -s <tagname> -m 'messages'
可以创建一个带gpg
签名的标签
- 点赞
- 收藏
- 关注作者
评论(0)