关于git相关的学习-3

2016-06-27 | 阅读

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

git与其他版本控制系统的主要差别在于 对待数据的方法. 其他版本控制系统一般以文件变更列表的方式储存信息,将保存的信息看做一组基本文件和每个文件随事件逐步累积的差异 .而git将数据看做小型文件系统的一组快照,git保存数据状态时,是将全部文件制作一个快照并保存快照的索引.Git对待数据更像是一个快照流.

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

Git中,绝大多数操作都是只需要访问本地文件和资源,一般不需要来自网络上其他计算机的信息.因为Git在本地保存了项目的完整记录.

Git 保证完整性

Git通过SHA-1散列来校验文件,这个散列结果为一个40位的16进制字符串.

Git 有三种状态

  • committed :已提交,表示数据已经安全的保存到本地数据库中.
  • modified : 已修改,表示修改了文件,但是没有保存到数据库中.
  • staged : 已暂存 , 表示对一个已修改的文件做了标记,使之包含在下次提交的快照中.

再结合Git项目的三个工作区域的概念:

  • Working Directory: 工作目录是对项目的某个版本独立提取出来的内容,即当前本地所操作的文件.
  • Staging Area : 暂存区域,是一个文件,保存了下次将提交的文件列表消息,一般在Git仓库目录中.也被称为索引.
  • Repository : git仓库目录,即.git文件夹,是git用来保存项目的元数据和对象数据库的地方,是git最重要的部分.从其他机器克隆仓库时,就是拷贝这里的数据.

Git的工作流程如下:

  1. 在工作目录修改文件
  2. 暂存文件,将文件快照放到暂存区域.
  3. 提交更新,找到暂存区域的文件,将快照永久性的存储到Git仓库目录.

配置Git

Git 自带一个 git config 来设置控制 Git。 这些变量存储在三个不同的位置:

  1. /etc/gitconfig 文件: 包含系统上每一个用户及他们仓库的通用配置。 如果使用带有 --system 选项的 git config 时,它会从此文件读写配置变量。

  2. ~/.gitconfig~/.config/git/config 文件:只针对当前用户。 可以传递 --global 选项让 Git 读写此文件。

  3. 当前使用仓库的 Git 目录中的 config 文件(就是 .git/config):针对该仓库。

低级的设置会覆盖高级的设置,即.git/config的处理优先级是最高的.

一般首先要配置用户信息:

git config --global user.name "XXX"
git config --global user.email XXX@example.com

通过git config --list命令输出所有配置.

通过git <verb> --help或者git help <verb>来查看使用手册.

创建Git仓库

创建仓库通过命令git init方法。添加--bare选项,表示创建一个仓库,不含工作目录,只含.git下的文件,即只作为远程仓库来使用.

克隆仓库通过 git clone命令.仓库的URL有多种,可以是https协议,也可以是git://协议,也可以是ssh协议.

记录更新到仓库

工作目录下的文件分为两种状态 :已跟踪和未跟踪.已跟踪的文件指那些被纳入版本控制的文件,在上一次快照中有它们的记录.而未跟踪的文件,即不存在与上一次快照中,也没有放入暂存区.对于工作目录下地文件,加上一个Untracked状态后,生命周期如下:

通过git status可以查看当前文件状态 :

On branch master
Your branch is up-to-date with 'origin/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:   2016-06-20-iostips12.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	2016-06-26-git3.md

no changes added to commit (use "git add" and/or "git commit -a")

在这里可以看到,有一个文件处于modified状态,一个文件处于Untracked状态.

跟踪新文件

对于工作目录下添加的新文件,git不会自行跟踪,需要通过git add命令来跟踪文件,如:

git add 2016-06-26-git3.md

这时再次执行git status,看见文件已经处于暂存状态了:

On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   2016-06-26-git3.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:   2016-06-20-iostips12.md

通过git add 加文件路径,以添加指定目录下所有文件,通过git add .来添加当前目录与递归子目录下所有文件.

暂存已修改文件

上面查询状态时,还有一个已经修改的已被追踪文件,我们需要将其添加到暂存区,也需要通过git add命令,即这个命令不止可以跟踪新的文件,也可以将已修改的文件放入暂存区,还能用于在合并时,把冲突的文件标记为已解决状态.

对于已经放入暂存区的文件,再次修改时,会出现以下情况:

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

    new file:   2016-06-26-git3.md
    modified:   2016-06-20-iostips12.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:   2016-06-20-iostips12.md

当再次修改文件时,2016-06-20-iostips12.md文件会同时出现在暂存区和未暂存区.git commit命令提交的是暂存区域的内容,而不是工作目录的当前区域,所以提交前,要将最新版本的内容进行暂存,即对于再次修改的文件要再次执行git add命令.

状态简览

使用git status输出地内容太详细繁琐了,我们可以使用git status -s或者 git status --short命令来输出简短的状态报告:

$ git status -s
 M README
MM Rakefile
A  lib/git.rb
M  lib/simplegit.rb
?? LICENSE.txt

上面示例输出中, M表示文件被修改,但是没有放入暂存区,而MM表示文件修改后已经被放入暂存区, A 表示新添加到暂存区的文件,而??表示新添加的未被跟踪的文件.

忽略文件

有些文件我们不需要放入版本管理中,我们通过创建一个.gitignore的文件来列出要忽略的文件模式.文件.gitignore的格式规范如下:

  • 所有空行或者以 # 开头的行都会被 Git 忽略。
  • 可以使用标准的 glob 模式匹配。
  • 匹配模式可以以(/)开头防止递归。
  • 匹配模式可以以(/)结尾指定目录。
  • 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。

所谓的 glob模式是指 shell 所使用的简化的正则表达式。 星号(*)匹配零个或多个任意字符;[abc] 匹配任何一个列在方括号中的字符([abc]表示要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)。 使用两个星号(*) 表示匹配任意中间目录,比如a/**/z 可以匹配 a/z, a/b/z 或 a/b/c/z等。

我们再看一个 .gitignore 文件的例子:

# no .a files
*.a

# but do track lib.a, even though you're ignoring .a files above
!lib.a

# only ignore the TODO file in the current directory, not subdir/TODO
/TODO

# ignore all files in the build/ directory
build/

# ignore doc/notes.txt, but not doc/server/arch.txt
doc/*.txt

# ignore all .pdf files in the doc/ directory
doc/**/*.pdf

查看修改

通过git diff命令可以查看工作目录中当前修改的具体内容,但区别的是 尚未暂存的文件与已暂存区域快照之间的差异, 而不是 自上次提交以来所做的所有改动.

如果需要比较已暂存的修改,可以通过git diff --cached命令.

提交更新

使用git commit命令来提交更新.执行git commit后,会启动文本编辑器,编辑器中显示以下文本:

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
#	new file:   README
#	modified:   CONTRIBUTING.md
#
~
~
~
".git/COMMIT_EDITMSG" 9L, 283C

会空出第一行,以输入提交声明,而后面会跟上提交的具体更新内容,即通过git status获得的结果.

也可以直接通过git commit -m "xxx",来提交.

每次提交的内容只是 暂存区的快照,不是工作目录的当前情况.

通过git commit -a -m "xxx"命令,会将已经跟踪的文件暂存起来,然后提交,但是不能提交未跟踪的文件.

移除文件

通过git rm命令来移除文件,同时将文件从已跟踪文件列表中移除.下一次提交的时候,文件就不会在纳入到版本管理中了.

如果删除之前文件已经被放到暂存区域,就需要使用-f选项来强制删除.

当我们需要将文件从暂存区域移除,但是保留在当前工作目录中时.即我们想要把一个已暂存的文件放入到.gitignore中,可以通过--cached选项.

git rm命令后可以列出文件和目录的名字,也可以使用glob模式,即:

git rm log/\*.log

移动文件

通过git mv命令可以移动文件,如:

git mv README.md README

然后我们查看git status,发现:

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

    renamed:    README.md -> README

git记录了renamed操作.

git mv命令相当于执行了下面三条命令:

$ mv README.md README
$ git rm README.md
$ git add README

查看提交历史

通过git log命令来查看提交历史.默认情况下输出的是 所有的更新,并列出每次更新的SHA-1校验和,作者名字与邮箱地址,提交时间和说明.如-2,只显示最近两条提交记录.git log有很多选项供使用:

  • -p : 输出每次提交的内容差异.
  • --stat : 输出每次提交的简略统计信息 ,如 :

       README           |  6 ++++++
       Rakefile         | 23 +++++++++++++++++++++++
       lib/simplegit.rb | 25 +++++++++++++++++++++++++
       3 files changed, 54 insertions(+)
    
  • --pretty : 使用指定模式展示信息,即使用时通过--pretty=xxx来指定模式,模式有很多种,比如online : 每次提交放在一行中显示,只显示SHA-1值和提交说明. 还有short fullfuller这三种基本不会用到. 比较有意思的是 format模式,可以自行定制输出格式 :

      git log --pretty=format:"%h - %an, %ar : %s"
      ca82a6d - Scott Chacon, 6 years ago : changed the version number
      085bb3b - Scott Chacon, 6 years ago : removed unnecessary test
      a11bef0 - Scott Chacon, 6 years ago : first commit
    

format提供的常用选项 有:

	%H // 提交对象(commit)的完整哈希字串
	%h //提交对象的简短哈希字串
	%T // 树对象(tree)的完整哈希字串
	%t // 树对象的简短哈希字串
	%P //父对象(parent)的完整哈希字串
	%p //父对象的简短哈希字串
	%an //作者(author)的名字
	%ae //作者的电子邮件地址
	%ad //作者修订日期(可以用 --date= 选项定制格式)
	%ar //作者修订日期,按多久以前的方式显示
	%cn //提交者(committer)的名字
	%ce //提交者的电子邮件地址
	%cd //提交日期
	%cr //提交日期,按多久以前的方式显示
	%s //提交说明

通过`format`,可以实现对git日志的格式化加工.
  • --graph : 在输出时,添加一些ASCII字符,以形象的展示分支与合并的历史.

限制日志输出长度

之前提到-2,只显示最近两条提交,实际上这是-<n>的写法,其中n可以是任何整数.

还可以按照时间去限制,如--since--until选项,以限制时间,如列出近两周内的提交:

$ git log --since=2.weeks

这两个选项支持的值是多种格式的,如具体某一天:

git log --since="2008-01-15" 

或者相对多久 :

git log --since="2 years 1 day 3 minutes ago" 

还可以设置匹配选项,如 --author选项指定作者,--committer指定提交者,还可以使用--grep来搜索说明中的关键字.

通过-S选项来列出那些添加或者移除了某些字符串的提交,如想要找出添加或移除了某个函数的引用的提交:

git log -Sfunction_name

最后,git log后可以跟路径,以指定输出路径上的目录或文件相关的提交 ,用两个--来隔开之前的选项与要限定的路径名称:

git log -p -- 2015-09-28-AES.md

撤销操作

使用git commit --amend重新提交,但是只是修改提交的内容 :

$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend

即将git commit --amend时的暂存区的数据覆盖到上一次initial commit的提交上.

取消暂存文件

通过git reset HEAD <file>来取消已经在暂存区的文件.如执行git add .后,却又想要将多个文件分多次提交,这时需要取消已经在暂存区的数据:

git reset HEAD CONTRIBUTING.md

撤销对文件的修改

如果想要将刚才修改的文件,恢复成上一次提交的状态,通过git checkout -- [file]命令.