《Git权威指南》学习笔记

前些日子买了蒋鑫老师的Git权威指南,为了更好地学习Git,将学习过程中感觉应该记下来的东西记录下来,遂成此文。此文在学习过程当中,不断更新。

这个博客是在GitHub上搭建的,搭建的过程中,我对git产生了浓厚的兴趣,遂买了蒋鑫老师的这本《Git权威指南》。写此文的目的,一是督促自己学习,二是作为以后的参考笔记,三希望对别人也能够有所帮助。

配置用户名和邮箱

$git config --global user.name "xiaoji121"
$git config --global user.email "dong.fly.ming@gmail.com"

删除Git全局配置文件中关于user.name和user.email的设置

$git config --unset --global user.name
$git config --unset --global user.email

设置命令别名

$git config --global alias.st status
$git config --global alias.ci commit
$git config --global alias.co checkout
$git config --global alias.br branch

开启控制台颜色显示

$git config --global color.ui true

初始化版本库

$git init demo

在工作区中建立目录 a/b/c,进入到该目录中:

$cd /path/to/my/workspace/demo/
$mkdir -p a/b/c
$cd /path/to/my/workspace/demo/a/b/c

显示版本库.git目录所在位置

$git rev-parse --git-dir

显示工作区根目录

$git rev-parse --show-toplevel

/path/to/my/workspace/demo

相对于工作区根目录的相对目录

$git rev-parse --show-prefix

a/b/c

显示从当前目录(cd)后退(up)到工作区的根的深度

$git rev-parse --show-cdup

../../../

提交

$git commit --allow-empty -m 'who does commit?'
$git commit --amend --allow-empty --reset-author

--allow-empty参数后充许执行空白提交

--amend是对刚刚提交进行修补

打印日志

$git log --pretty=fuller
$git log --stat
$git log --pretty=online

工作区、版本库、暂存区原理图

工作区、版本库、暂存区原理图

图中左侧为工作区,右侧为版本库。在版本库中标记为 "index" 的区域是暂存区(stage, index),标记为 "master" 的是 master 分支所代表的目录树。

图中我们可以看出此时 "HEAD" 实际是指向 master 分支的一个“游标”。所以图示的命令中出现 HEAD 的地方可以用 master 来替换。

图中的 objects 标识的区域为 Git 的对象库,实际位于 ".git/objects" 目录下。

当对工作区修改(或新增)的文件执行 "git add" 命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID 被记录在暂存区的文件索引中。

当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树。

当执行 "git reset HEAD" 命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。

当执行 "git rm --cached " 命令时,会直接从暂存区删除文件,工作区则不做出改变。

当执行 "git checkout ." 或者 "git checkout -- " 命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区的改动。

当执行 "git checkout HEAD ." 或者 "git checkout HEAD " 命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改 动。

Git diff 魔法

  1. 工作区和暂存区比较

    $git diff

  2. 暂存区和HEAD比较

    $git diff --cached

  3. 工作区和HEAD比较

    $git diff HEAD

保存当前工作进度

$git stash

打印日志,并可以提供短小的提交ID

$git log --graph --oneline

git reset

可以将“游标”指向任意一个存在的提交ID。

$git reset --hard HEAD^

HEAD^ 代表了HEAD的父提交,这条命令就相当于将master重置到上一个老的提交上。

重置到任意一个版本

$git reset --hard 版本号

使用重置命令很危险,会彻底地丢弃历史。那么,还能够通过浏览提交历史的办法找到丢弃的提交ID,再使用重置命令恢复历史吗?不可能!因为重置让提交历史也改变了。

用reflog挽救错误的重置

虽然不能通过浏览提交历史的办法找到丢弃的提交ID,但我们有reflog。

$git reflog show master | head -5

输出类似如下:
93dd33d master@{0}: 93dd33d : updating HEAD
343eed3 master@{1}: HEAD^: updating HEAD
33hff22 master@{2}: commit: does master commit ?
……
……

\<refname>@{\<n>},这个表达式的含义是引用\<refname>之前第\<n>次改变时的SHA1哈希值。

重置master为两次改变之前的值

$git reset --hard master@{2}

深入git reset

用法一:git reset [-q] [\<commit>] [--] \<paths>

用法二:git rest [--soft | --mixed | --hard | --merge | --keep] [-q] [\<commit>]

用法一不会重置引用,更不会改变工作区,而是用指定提交状态(\<commit>)下的文件(\<paths>)替换掉暂存区中的文件。例如命令git reset HEAD \<path>相当于取消执行之前执行的git add \<paths>命令时改变的暂存区。

用法二则会重置引用。根据不同的选项,可以对暂存区或工作区进行重置。

使用参数 --hard,如:git reset --hard \<commit>.会执行

  1. 替换引用的指向。引用指向新的提交ID。

  2. 替换暂存区。替换后,暂存区的内容和引用指向的目录树一致。

  3. 替换工作区。替换后,工作区的内容变得和暂存区一致,也和HEAD所指向的目录树内容相同。

使用参数 --soft,如:git reset --soft \<commit>.会执行1。即只更改引用的指向,不改变暂存区和工作区。

使用参数 --mixed或不使用参数(默认为--mixed),如:git reset \<commit>.会执行1和2。即更改引用的指向及重置暂存区,但是不改变工作区。

命令 git reset

仅用HEAD所指向的目录树重置暂存区,工作区不会受到影响,相当于将之前的git add命令更新到暂存区的内容撤出暂存区。引用也未改变,因为引用重置到HEAD相当于没有重置。

命令 git reset HEAD

同上

命令 git reset -- filename

仅将文件filename的改动撤出暂存区,暂存区中的其他文件不改变。相当于对命令 git add filename的反向操作。

命令 git reset HEAD filename

同上

命令 git reset --soft HEAD^

工作区和暂存区不改变,但是引用向前回退一次。当对最新的提交说明或提交的更改不满意时,撤销最新的提交以便重新提交。

命令 git reset HEAD^

工作区不改变,但是暂存区会回退到上一次提交之前,引用也会回退一次。

命令 git reset --mixed HEAD^

同上

命令 git reset --hard HEAD^

彻底撤销最近的提交。引用回退到前一次,而且工作区和暂存区都会回退到上一次提交的状态。自上一次以来的提交全部丢失。

git checkout命令

命令:git checkout branch

检出branch分支。更新HEAD以指向branch分支,以及用branch指向的树更新暂存区和工作区。

命令:git checkout

汇总显示工作区、暂存区与HEAD的差异。

命令:git checkout HEAD

同上

命令:git checkout -- filename

用暂存区中filename文件来覆盖工作区中的filename文件。相当于取消自上次执行git add filename以来(如果执行过)的本地修改。这个命令很危险,因为对于本地的修改会悄无声息地覆盖,毫不留情。

命令:git checkout branch -- filename

维持HEAD的指向不变。用branch所指向的提交中的filename替换暂存区和工作区中相应文件。注意会将暂存区和工作区中的filename文件直接覆盖。

命令:git checkout -- . 或写作git checkout .

注意 git checkout 命令后的参数为一个点。这条命令最危险!会取消所有本地的修改(相对于暂存区)。相当于用暂存区的所有文件直接覆盖本地文件,不给用户任何确认的机会!

恢复进度

查看保存的进度

$git stash list

从最近保存的进度进行恢复

$git stash pop

删除本地多余的目录和文件

可以使用git clean命令。先来测试运行以便看看哪些文件和目录会被删除,以免造成误删。

$git clean -nd

真正开始强制删除多余的目录和文件

$git clean -fd

整个世界都清净了。

使用git stash

命令:git stash

保存当前的工作进度。会分别对暂存区和工作区的状态进行保存。

命令:git stash list

显示进度列表。此命令显然暗示了git stash可以多次保存工作进度,并且在恢复的时候进行选择。

命令:git stash pop [--index] [\<stash>]

如果不使用任何参数,会恢复最新保存的工作进度,并将恢复的工作进度从存储的工作列表中清除。如果提供\<stash>参数(来自git stash list显示的列表),则从该\<stash>中恢复。恢复完毕也将从进度列表中删除\<stash>。

选项--index除了恢复工作区的文件外,还尝试恢复暂存区。

本地删除

$ rm *.txt

执行这个命令,文件只是在本地进行了删除,尚未添加到暂存区。也就是说直接在工作区删除,对暂存区和版本库没有任何影响。

执行git rm 命令删除文件

$ git rm *.txt
$ git commit -m 'delete files'

命令git add -u 快速标记删除

当执行了rm *.txt后可以直接执行git add -u 命令将本地文件的变更(修改、删除)全部记录到暂存区中。

恢复删除的文件

执行下面的命令可以从历史(前一次提交)中恢复welcome.txt文件

$ git cat-file -p HEAD~1:welcome.txt > welcome.txt

也可以使用git show命令取代git cat-file -p命令,效果相同

$ git show HEAD~1:welcome.txt > welcome.txt

使用git checkout命令则最为简洁实用

$ git checkout HEAD~1 -- welcome.txt

移动文件

$ git mv welcome.txt README

使用git add -i 选择性添加

$ git add -i

当执行这个命令时会出现一个交互性界面,如图:

文件忽略

执行下面的命令可以在目录下创建一个名为.gitignore的文件,把要忽略的文件写在其中,文件名可以使用通配符。注意:第二行开头的右尖括号是cat命令的提示符,不是用户的输入。

$ cat > .gitignore << EOF
> hello
> *.o
> *.h
> EOF

文档归档

基于最新提交建立归档文件latest.zip

$ git archive -o latest.zip HEAD

只将某个目录建立到归档partial.tar中

$git archive -o partial.tar HEAD src

基于里程碑v1.0建立归档,并且为归档中的文件添加目录前缀1.0

$git archive --format=tar --prefix=1.0/ v1.0 | gzip > foo-1.0.tar.gz

blog comments powered by Disqus