赞
踩
目录
什么是“版本控制”? 版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。在 CODE CHINA
中,我们对保存着软件源代码的文件作版本控制,但实际上,你可以对任何类型的文件进行版本控制。
使用版本控制系统通常还意味着,就算你乱来一气把整个项目中的文件改的改删的删,你也照样可以轻松恢复到原先的样子。 但额外增加的工作量却微乎其微。
版本控制系统发展可以分为三个阶段:
Git是一个开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理。
Git 官网下载对应系统的软件了,下载地址为 git-scm.com或者gitforwindows.org
无脑安装,修改安装地址,其他默认。 安装完成如图。
好了,当你当完成了 Git
的安装后,接下来我们就需要对 Git
进行一些必要的环境配置。
通常情况下,每台计算机上只需要配置一次 Git
,当 Git
程序升级时会保留配置信息。 你可以在任何时候再次通过运行 git config
命令来修改它们。
git config
Git
自带一个 git config
的工具来帮助设置控制 Git
外观和行为的配置变量。
安装完 Git
之后,要做的第一件事就是设置你的用户名和邮件地址。 这一点很重要,因为每一个 Git
提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改:
- $ git config --global user.name "凌贤文"
- $ git config --global user.email lingxw@zjnu.edu.cn
再次强调,如果使用了 --global
选项,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情, Git
都会使用那些信息。
当你想针对特定项目使用不同的用户名称与邮件地址时,可以在那个项目目录下运行没有 --global
选项的命令来配置。
git config --list
命令来列出所有 Git
当时能找到的配置。
通过以下命令查看所有的配置以及它们所在的文件
$ git config --list --show-origin
$ git config --global color.ui true
让 Git
显示颜色,会让命令输出看起来更醒目
日常使用中,我们一般不需要从头开始编辑.gitignore
文件,已经有各种现成的种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:GitCode / Gitignore · GitCode
忽略文件的原则是:
Java
编译产生的.class文件;让我们来看一个例子:
假设你在 Windows
下进行 Python
开发,Windows
会自动在有图片的目录下生成隐藏的缩略图文件,如果有自定义目录,目录下就会有Desktop.ini
文件,因此你需要忽略 Windows
自动生成的垃圾文件:
.gitignore 文件
- # Windows:
- Thumbs.db
- ehthumbs.db
- Desktop.ini
然后,继续忽略Python
编译产生的.pyc、.pyo、dist
等文件或目录:
.gitignore 文件
- # Python:
- *.py[cod]
- *.so
- *.egg
- *.egg-info
- dist
- build
加上你自己定义的文件,最终得到一个完整的.gitignore
文件,内容如下:
.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
。
有些时候,你想添加一个文件到 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行规则忽略了该文件,于是我们就可以知道应该修订哪个规则。
还有些时候,当我们编写了规则排除了部分文件时:
- # 排除所有.开头的隐藏文件:
- .*
- # 排除所有.class文件:
- *.class
但是我们发现.*
这个规则把.gitignore
也排除了,并且App.class
需要被添加到版本库,但是被*.class
规则排除了。
添加例外规则
这个时候,虽然可以用git add -f
强制添加进去,但我们建议你可以添加两条例外规则:
- # 排除所有.开头的隐藏文件:
- .*
- # 排除所有.class文件:
- *.class
-
- # 不排除.gitignore和App.class:
- !.gitignore
- !App.class
把指定文件排除在.gitignore
规则外的写法就是!+文件名
,所以,只需把例外文件添加进去即可。
除了通过 配置忽略文件 来提高git commit
时的便捷性外,Git
中还有一种可以让大家在敲入 Git
命令时偷懒的办法——那就是配置 Git
别名。
配置 git status
/commit/checkout/branch
比如在使用git status
命令时,我们可以通过配置别名的方式将其配置为git st
,这样在使用时是不是就比输入 git status
简单方便很多呢?
我们只需要敲一行命令,告诉 Git
,以后st
就表示status
:
$ git config --global alias.st status
当然还有别的命令可以简写,很多人都用co
表示checkout
,ci
表示commit
,br
表示branch
:
配置别名
- $ git config --global alias.co checkout
- $ git config --global alias.ci commit
- $ git config --global alias.br branch
配置完成以上别名后,以后提交就可以简写成:
$ git ci -m "sth."
git reset HEAD file
再比如git reset HEAD file
命令,他可以把暂存区的修改撤销掉(unstage),重新放回工作区。既然是一个unstage
操作,就可以配置一个unstage
别名:
$ git config --global alias.unstage 'reset HEAD'
当你敲入命令:
$ git unstage test.py
实际上 Git
执行的是:
$ git reset HEAD test.py
这些自定义的Git
配置文件通常都存放在仓库的.git/config
文件中。
我们先来认识一下版本库——Repository,接下来我们所有提到的 Git
基础命令,都是基于版本库的。
版本库又名仓库,英文名 repository
,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git
管理起来,每个文件的修改、删除,Git
都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
首先,选择一个合适的地方,创建一个空目录:
第二步,通过git init
命令把这个目录变成 Git
可以管理的仓库:
- $ git init
- Initialized empty Git repository in /Users/xxm/learning-git/.git/
瞬间 Git
就把仓库建好了,而且告诉你是一个空的仓库(empty Git
repository),同时在当前目录下多了一个.git
的目录,这个目录是 Git
来跟踪管理版本库的,如果你没有看到 .git 目录,那是因为这个目录默认是隐藏的,用ls -ah
命令就可以看到了。
如果你想获得一份已经存在了的 Git
仓库的拷贝,比如说,你想为某个开源项目贡献自己的一份力,这时就要用到 git clone
命令,Git
克隆的是该 Git
仓库服务器上的几乎所有数据,而不是仅仅复制完成你的工作所需要文件。
git clone
当你执行 git clone
命令的时候,默认配置下远程 Git
仓库中的每一个文件的每一个版本都将被拉取下来。
克隆仓库的命令是 git clone <url>
。 比如,要克隆 Git
的链接库 libgit2,可以用下面的命令:
git clone https://codechina.csdn.net/codechina/help-docs
这会在当前目录下创建一个名为 help-docs
的目录,并在这个目录下初始化一个 .git
文件夹, 从远程仓库拉取下所有数据放入 .git
文件夹,然后从中读取最新版本的文件的拷贝。 如果你进入到这个新建的 help-docs
文件夹,你会发现所有的项目文件已经在里面了,准备就绪等待后续的开发和使用。
自定义本地仓库名称
当然如果你想在克隆远程仓库的时候,自定义本地仓库的名字也是可以的,你可以通过额外的参数指定新的目录名:
$ git clone https://codechina.csdn.net/codechina/help-docs mydocs
这会执行与上一条命令相同的操作,但目标目录名变为了 mydocs
。
Git 支持多种数据传输协议。 上面的例子使用的是 https://
协议,不过你也可以使用 git://
协议或者使用 SSH
传输协议,比如 user@server:path/to/repo.git
。
接下来,我们来尝试在已经准备好的 Git
仓库中编辑一个readme.txt
文件,内容如下:
- Git is a version control system.
- Git is free software.
接下来,我们可以通过2个命令将刚创建好的readme.txt
添加到Git
仓库:
第一步,用命令git add
告诉 Git
,把文件添加到仓库:
$ git add readme.txt
执行上面的命令,没有任何显示,说明添加成功。
第二步,用命令git commit
告诉 Git
,把文件提交到仓库:
- $ git commit -m "wrote a readme file"
- [master (root-commit) 50ed06b] wrote a readme file
- 1 file changed, 2 insertions(+)
- create mode 100644 readme.txt
这里简单解释一下
git commit
命令,-m
后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录。
git commit
命令执行成功后会告诉你:
readme
.txt文件)为什么 Git
添加文件需要add,commit
一共两步呢?因为commit
可以一次提交很多文件,所以你可以多次add
不同的文件,比如:
- $ git add file1.txt
- $ git add file2.txt file3.txt
- $ git commit -m "add 3 files."
Git
仓库当前状态变化我们已经成功地添加并提交了一个readme.txt
文件,接下来让我们继续修改readme.txt
文件,改成如下内容:
- Git is a distributed version control system.
- Git is free software.
查看 git status
结果
现在,运行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
被修改了,但并没有告诉我们具体修改的内容是什么,假如刚好是上周修改的,等到周一来班时,已经记不清上次怎么修改的readme.txt
,这个时候我们就需要用git diff
这个命令查看相较于上一次暂存都修改了些什么内容了:
运行 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.
- (END)
git diff
顾名思义就是查看 difference,显示的格式正是 Unix 通用的 diff
格式,可以从上面的输出看到,我们在第一行添加了一个distributed
单词。
知道了对readme.txt
作了什么修改后,再把它提交到仓库就放心多了,提交修改和提交新文件是一样的两步,第一步是git add
:
git add
$ git add readme.txt
同样没有任何输出。在执行第二步git commit
之前,我们再运行git status
看看当前仓库的状态:
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**
- $ git commit -m "add distributed"
- [master e55063a] add distributed
- 1 file changed, 1 insertion(+), 1 deletion(-)
提交后,我们再用git status
命令看看仓库的当前状态:
** git status**
- $ git status
- On branch master
- nothing to commit, working tree clean
Git告诉我们当前没有需要提交的修改,而且,工作目录是干净(working tree clean)的。
如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline
参数:
** git log --pretty=oneline**
- $ git log --pretty=oneline
- e55063ad7f97dd979e4f94e12d2bc44a25a0fd55 (HEAD -> master) add distributed
- 50ed06bd62fd34afbe501e6f2a4af73ccbe187f0 wrote a readme file
- (END)
每提交一个新版本,实际上 Git 就会把它们自动串成一条时间线。如果使用可视化工具或者之前在 git 自定义配置中介绍的 git lg
命令,就可以更清楚地看到提交历史的时间线:
- $ git lg
- * e55063a - (HEAD -> master) add distributed (4 minutes ago) <Miykael_xxm>
- * 50ed06b - wrote a readme file (6 minutes ago) <Miykael_xxm>
- (END)
作为一个优秀的版本控制系统,Git 能够让我们查看每一次提交的记录。在日常的工作中,我们可以随时对 Git 仓库中的内容进行修改,,每当你觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在 Git中 被称为commit / 提交
。一旦你把文件改乱了,或者误删了文件,还可以从最近的一个commit
恢复,然后继续工作,而不是把几个月的工作成果全部丢失。
** git log**
在 Git 中,我们可以通过git log
命令查看全部的commit
记录:
- $ git log
- commit e55063ad7f97dd979e4f94e12d2bc44a25a0fd55 (HEAD -> master)
- Author: Miykael_xxm <xiongjiamu@gmail.com>
- Date: Fri Nov 27 16:08:04 2020 +0800
-
- add distributed
-
- commit 50ed06bd62fd34afbe501e6f2a4af73ccbe187f0
- Author: Miykael_xxm <xiongjiamu@gmail.com>
- Date: Fri Nov 27 16:06:11 2020 +0800
-
- wrote a readme file
- (END)
git log
命令显示从最近到最远的提交日志,我们可以看到2次提交,最近的一次是add distributed
,最早的一次是wrote a readme file
。
这个时候,假设我们需要将 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**
- $ git reset --hard HEAD^
- HEAD is now at 50ed06b wrote a readme file
现在让我们看看readme.txt
的内容是不是版本wrote a readme file
:
- $ cat readme.txt
- Git is a version control system.
- Git is free software.
果然还原到最初wrote a readme file
这个版本了。
Git 的版本回退速度非常快,因为 Git 在内部有个指向当前版本的HEAD
指针,当你回退版本的时候,Git 仅仅是把HEAD
从指向add distributed
:
** HEAD 指针移动记录**
- ┌────┐
- │HEAD│
- └────┘
- │
- └──> ○ add distributed
- │
- ○ wrote a readme file
改为指向wrote a readme file
:
- ┌────┐
- │HEAD│
- └────┘
- │
- │ ○ add distributed
- │ │
- └──> ○ wrote a readme file
然后顺便把工作区的文件更新了。所以你让HEAD
指向哪个版本号,你就把当前版本定位在哪。
现在,你回退到了某个版本,关掉了电脑,第二天早上就后悔了,想恢复到新版本怎么办?找不到新版本的commit id
怎么办?
好在 Git 提供了一个命令git reflog
用来记录你的每一次命令,当你用git reset --hard HEAD^
回退到wrote a readme file
版本时,再想恢复到add distributed
,就可以通过git reflog
命令找到add distributed
的commit id
。
** git reflog**
- $ git reflog
- 50ed06b (HEAD -> master) HEAD@{0}: reset: moving to HEAD~
- e55063a HEAD@{1}: reset: moving to HEAD
- e55063a HEAD@{2}: commit: add distributed
- 50ed06b (HEAD -> master) HEAD@{3}: commit (initial): wrote a readme file
- ESC
从上面的输出可以看到,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
做个修改,比如加上一行内容:
- 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
现在,暂存区的状态就变成这样了.
所以,git add
命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit
就可以一次性把暂存区的所有修改提交到分支。
- $ git commit -m "understand how stage works"
- [master 599dbdb] understand how stage works
- 2 files changed, 2 insertions(+)
- create mode 100644 LICENSE
一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的:
- $ git status
- On branch master
- nothing to commit, working tree clean
现在版本库变成了这样,暂存区就没有任何内容了
在任何一个阶段,你都有可能想要撤消某些操作。接下来,我们将会学习几个撤消你所做修改的基本命令。 注意,有些撤消操作是不可逆的。 这是在使用 Git
的过程中,会因为操作失误而导致之前的工作丢失的少有的几个地方之一。
有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 --amend 选项的提交命令来重新提交:
git commit --amend
- $ git commit --amend
-
这个命令会将暂存区中的文件提交。 如果自上次提交以来你还未做任何修改(例如,在上次提交后马上执行了此命令), 那么快照会保持不变,而你所修改的只是提交信息。
文本编辑器启动后,可以看到之前的提交信息。 编辑后保存会覆盖原来的提交信息。
例如,你提交后发现忘记了暂存某些需要的修改,可以像下面这样操作:
- $ git commit -m 'initial commit'
- $ git add forgotten_file
- $ git commit --amend
最终你只会有一个提交——第二次提交将代替第一次提交的结果。
当你在修补最后的提交时,并不是通过用改进后的提交 原位替换 掉旧有提交的方式来修复的, 理解这一点非常重要。从效果上来说,就像是旧有的提交从未存在过一样,它并不会出现在仓库的历史中。
修补提交最明显的价值是可以稍微改进你最后的提交,而不会让“啊,忘了添加一个文件”或者 “小修补,修正笔误”这种提交信息弄乱你的仓库历史。
接下来我们看看如何操作暂存区和工作目录中已修改的文件。 这些命令在修改文件状态的同时,也会提示如何撤消操作。例如,你已经修改了两个文件并且想要将它们作为两次独立的修改提交, 但是却意外地输入 git add *
暂存了它们两个。如何只取消暂存两个中的一个呢? git status
命令提示了你:
- $ git add *
- $ git status
- On branch master
- Changes to be committed:
- (use "git reset HEAD <file>..." to unstage)
-
- renamed: LICENSE -> LICENSE.md
- modified: readme.txt
在 “Changes to be committed” 文字正下方,提示使用 git reset HEAD <file>...
来取消暂存。 所以,我们可以这样来取消暂存 readme.txt
文件:
git reset HEAD
- $ git reset HEAD readme.txt
- Unstaged changes after reset:
- M readme.txt
-
- $ git status
- On branch master
- Changes to be committed:
- (use "git reset HEAD <file>..." to unstage)
-
- renamed: LICENSE -> LICENSE.md
-
- 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
这个命令有点儿奇怪,但是起作用了。 readme.txt
文件已经是修改未暂存的状态了。
git reset
确实是个危险的命令,如果加上了--hard
选项则更是如此。 然而在上述场景中,工作目录中的文件尚未修改,因此相对安全一些。
如果你并不想保留对 readme.txt
文件的修改怎么办? 你该如何方便地撤消修改——将它还原成上次提交时的样子(或者刚克隆完的样子,或者刚把它放入工作目录时的样子)? 幸运的是,git status
也告诉了你应该如何做。 在最后一个例子中,未暂存区域是这样:
- 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
它非常清楚地告诉了你如何撤消之前所做的修改。 让我们来按照提示执行:
git checkout – file
- $ git checkout -- readme.txt
- $ git status
- On branch master
- Changes to be committed:
- (use "git reset HEAD <file>..." to unstage)
-
- renamed: LICENSE -> LICENSE.md
可以看到那些修改已经被撤消了。
请务必记得
git checkout -- <file>
是一个危险的命令。 你对那个文件在本地的任何修改都会消失——Git 会用最近提交的版本覆盖掉它。 除非你确实清楚不想要对那个文件的本地修改了,否则请不要使用这个命令。
在 Git
中,删除也是一个修改操作,我们先添加一个新文件test.txt
到 Git
并且提交:
- $ git add test.txt
-
- $ git commit -m "add test.txt"
- [master c67077f] 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 restore <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 5c7e5ea] remove test.txt
- 1 file changed, 1 deletion(-)
- delete mode 100644 test.txt
现在,文件就从版本库中被删除了。
小提示:先手动删除文件,然后使用
git rm
和git add
效果是一样的。
另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:
$ git checkout -- test.txt
git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
注意:从来没有被添加到版本库就被删除的文件,是无法恢复的!
好了,以上这些就是 git
中关于撤销以及删除文件的相关内容了,让我们来回顾一下:
git checkout -- file
git reset HEAD <file>
,就回到了场景1,第二步按场景1操作场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,可以用命令git reset --hard commit_id
,不过前提是没有推送到远程库。
前面我们了解到,Git 保存的不是文件的变化或者差异,而是一系列不同时刻的 快照 。
在进行提交操作时,Git 会保存一个提交对象(commit object)。 知道了 Git
保存数据的方式,我们可以很自然的想到——该提交对象会包含一个指向暂存内容快照的指针。 但不仅仅是这样,该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。 首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象, 而由多个分支合并产生的提交对象有多个父对象,
为了更加形象地说明,我们假设现在有一个工作目录,里面包含了三个将要被暂存和提交的文件。 暂存操作会为每一个文件计算校验和,然后会把当前版本的文件快照保存到 Git 仓库中 (Git 使用 blob
对象来保存它们),最终将校验和加入到暂存区域等待提交:
- $ git add readme.txt test.md LICENSE
- $ git commit -m 'The initial commit of my project'
当使用 git commit
进行提交操作时,Git 会先计算每一个子目录(本例中只有项目根目录)的校验和, 然后在 Git
仓库中这些校验和保存为树对象。随后,Git 便会创建一个提交对象, 它除了包含上面提到的那些信息外,还包含指向这个树对象(项目根目录)的指针。 如此一来,Git 就可以在需要的时候重现此次保存的快照。
现在,Git 仓库中有五个对象:三个 blob
对象(保存着文件快照)、一个 树 对象 (记录着目录结构和 blob
对象索引)以及一个 提交 对象(包含着指向前述树对象的指针和所有提交信息)。
这里用到再说,有点复杂。
Git 的标签虽然是版本库的快照,但其实它就是指向某个commit
的指针(跟分支很像对不对?但是分支可以移动,标签不能移动),所以,创建和删除标签都是瞬间完成的。
tag
就是一个让人容易记住的有意义的名字,它跟某个commit
绑在一起。
在 Git
中创建标签非常简单,首先,切换到需要打标签的分支上:
- $ git branch
- * dev
- master
- $ git checkout master
- Switched to branch 'master'
然后,敲命令git tag <name>
就可以打一个新标签:
$ git tag v1.0
可以用命令git tag
查看所有标签:
- $ git tag
- v1.0
默认标签是打在最新提交的commit
上的。有时候,如果忘了打标签,比如,现在已经是周五了,但应该在周一打的标签没有打,怎么办?
方法是找到历史提交的commit id
,然后打上就可以了:
- $ git log --pretty=oneline --abbrev-commit
- 5c7e5ea (HEAD -> master) remove test.txt
- c67077f add test.txt
- fb8b190 conflict fixed
- 06e5e3a & simple
- 2443c95 add simple
- 4aac6c7 branch test
- 599dbdb understand how stage works
- 1985ccf append GPL
- 27f9df6 add distributed
- e372504 wrote a readme file
比方说要对conflict fixed
这次提交打标签,它对应的commit id
是fb8b190
,敲入命令:
$ git tag v0.9 fb8b190
再用命令git tag
查看标签:
- $ git tag
- v0.9
- v1.0
注意,标签不是按时间顺序列出,而是按字母排序的。可以用git show
查看标签信息:
- $ git show v0.9
- commit fb8b1903706eeaa8141894f1af859f0dfee3e0d9 (tag: v0.9)
- Merge: 06e5e3a 2443c95
- Author: Miykael_xxm <xiongjiamu@gmail.com>
- Date: Sun Nov 29 20:18:26 2020 +0800
-
- conflict fixed
-
- (END)
可以看到,v0.9确实打在conflict fixed
这次提交上。
还可以创建带有说明的标签,用-a
指定标签名,-m
指定说明文字:
$ git tag -a v0.1 -m "version 0.1 released" 1985ccf
用命令git show <tagname>
可以看到说明文字:
- $ git show v0.1
- tag v0.1
- Tagger: Miykael_xxm <xiongjiamu@gmail.com>
- Date: Sun Nov 29 22:06:05 2020 +0800
-
- version 0.1 released
-
- commit 1985ccf5b6710edf3bcd7c0700e6d96cab335e61 (tag: v0.1, tag: remove)
- Author: Miykael_xxm <xiongjiamu@gmail.com>
- Date: Fri Nov 27 16:14:27 2020 +0800
-
- append GPL
-
- diff --git a/readme.txt b/readme.txt
- index 9247db6..8443d23 100644
- --- a/readme.txt
- +++ b/readme.txt
- @@ -1,2 +1,2 @@
- Git is a distributed version control system.
- -Git is free software.
- +Git is free software distributed under the GPL.
- (END)
注意:标签总是和某个
commit
挂钩。如果这个commit
既出现在master
分支,又出现在dev
分支,那么在这两个分支上都可以看到这个标签。
如果标签打错了,也可以删除:
- $ git tag -d v0.1
- Deleted tag 'v0.1' (was 3be0bf5)
因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。
如果要推送某个标签到远程,使用命令git push origin <tagname>
:
- $ git push origin v1.0
- Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
- To codechina.csdn.net:xiongjiamu/learing.git
- * [new tag] v1.0 -> v1.0
或者,一次性推送全部尚未推送到远程的本地标签:
- $ git push origin --tags
- Enumerating objects: 1, done.
- Counting objects: 100% (1/1), done.
- Writing objects: 100% (1/1), 170 bytes | 170.00 KiB/s, done.
- Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
- To codechina.csdn.net:xiongjiamu/learing.git
- * [new tag] remove -> remove
- * [new tag] v0.1 -> v0.1
- * [new tag] v0.9 -> v0.9
如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:
- $ git tag -d remove
- Deleted tag 'remove' (was 1985ccf)
然后,从远程删除。删除命令也是push
,但是格式如下:
- $ git push origin :refs/tags/remove
- To codechina.csdn.net:xiongjiamu/learing.git
- - [deleted] remove
要看看是否真的从远程库删除了标签,可以登录 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
签名的标签Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。