Git的三个层次
这是关于Git最重要的概念,一个Git项目有 三个工作区:
- 工作目录(Working Directory): 针对某个版本(经常是当前版本)独立提取出来的内容,供我们使用或修改。可以感性地理解为我们平时看到的操作系统上的项目文件夹。
- Git仓库(.git Directory): 整个项目所有版本的历史记录数据库。所有修改最终都要提交到这里。
- 暂存区域(Staging Area): 我们对工作区文件的修改不会直接提交给Git仓库,而是先保存在这个暂存区域,等待下次提交(commit)给Git仓库。暂存区域也叫”索引(index)“。
用大白话讲就是:Git仓库才是最终代码的储存点。工作目录相当于一个草稿本。我们先在草稿本上写写画画,并没有直接在Git仓库上改动,只是在草稿本上改动了。然后工作目录上的草稿和Git仓库的底稿之间的不同部分在被提交到Git仓库之前,会被先放到暂存区域,这是第二层草稿。最终确认以后才把暂存区域的改动内容提交到Git仓库。所以在一段代码被最终提交到Git仓库之前,需要先经过工作目录和暂存区两个草稿本。
Git配置的三个层次
git config --system #针对所有用户的通用设置
git config --global #针对当前用户的通用设置
git config #针对当前用户某个项目的具体设置
每一个级别覆盖上一级别的配置。
所以装了git
之后,每次可以先用--global
级别配置自己的通用用户名和邮箱。以后个别项目需要用不同的用户名和邮箱,就可以用没有--global
参数的命令来配置只对当前项目有效的用户名和邮箱。
GitHub传输协议
比如我克隆一个项目到本地,可以用SSH
协议,把RSA公钥存在Github上,可以不用输入密码。
git clone git@github.com:helloShen/git-practices.git
关于怎么在Github上设公钥,参见, Setting SSH Key : https://zxbyoyoyo.github.io/2017/02/28/rsaKey.html
可以用HTTPS
协议,
git clone https://github.com/helloShen/git-practices.git
用HTTPS
的优点是:比SSH
更方便,而且一般不会被防火墙或者代理拦截。缺点是每次都要输入密码。当然可以把用户名和密码加入url
,就不用每次都输入。
git remote set-url origin https://name:password@github.com/repo.git
也可以安装osxkeychain helper
来帮我们自动输入,参见
Github Doc: https://help.github.com/articles/caching-your-github-password-in-git/
git init
cd ~/project-path/
git init
git init
命令能生成包括.git
在内的整个Git仓库骨架。
git add
根据Git的”工作区”,”暂存区”,”Git仓库”的三层结构,概念图如下图所示,
git add <filename>
add
命令的效果是:追踪<filename>
文件,并把<filename>
文件放入暂存区域,将在下次提交给Git仓库。
add
命令实际执行的是:为每一个文件计算校验和(使用我们在 起步 中提到的 SHA-1 哈希算法),然后会把当前版本的文件快照保存到 Git 仓库中(Git 使用 blob 对象 来保存它们),最终将校验和加入到暂存区域等待提交。
我更喜欢把add
命令叫做:
把这个文件的改动纳入下次提交中。
git status
查看哪些文件处于什么状态,
git status
报告看上去是这个样子,
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: README
modified: CONTRIBUTING.md
可以用-s
参数,查看一个更简短的状态报告,
git status -s
报告看上去是这样,
$ git status -s
M README
MM Rakefile
A lib/git.rb
M lib/simplegit.rb
?? LICENSE.txt
.gitignore文件
一般一个项目里出了项目的源代码,还会有很多自动生成的中间文件,如果不希望把他们包含到项目追踪文件中,可以把他们加入.gitignore
文件。这样git每次都会自动忽略以上文件。
一个Java
项目标准的.gitignore
文件,大概长这样(Github默认Java项目标配.gitignore
文件)。.class
文件和.jar
等等文件会被忽略。
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
git diff
git diff
用来查看,工作区和暂存区的区别。看看工作区有没有什么修改没有被添加到暂存区。
git diff
然后用git diff --cached
或者git diff --staged
查看已经暂存起来的变化。
git diff --cached
git diff --staged
用-v
参数,可以让我知道知道本次提交具体做了哪些修改。
git diff -v
git commit
git commit
命令用来将暂存区的资料提交到Git仓库。
git commit
给 git commit
加上 -a
选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add
步骤。
git commit –amend 不增加提交次数的提交
如果我刚刚提交了一次重要的更新,比如commit编号是ad24e...
。但后来又发现少提交了一个文件。补进来之后,又不想增加一个新的提交记录。最好是以上一次提交的身份,补全文件。就可以用--amend
参数。
git commit --amend
比如,下面这样最终只会有一个提交 - 第二次提交将代替第一次提交的结果。
$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend
撤销修改(危险操作)
假设文件yourfile.md
已经存至暂存区,而且已经提交Git仓库。
还是看上面这张图,需要先用reset
把暂存区的yourfile.md
回滚至前一次提交的状态。这里的HEAD
是Git仓库里指向当前分支的一个引用。
git reset HEAD -- yourfile.md
然后再用checkout
将工作区版本回滚至暂存区的版本。checkout
命令本意是:从暂存区域中拷贝文件到工作目录。
git checkout -- yourfile.md
如果在reset
命令后面加了--hard
就会同时执行“暂存区回滚至前一次提交的状态”,“再将工作区回滚至前一次提交的状态”。相当于一下子撤销当前工作区和暂存区的所有修改记录,完全回到上一次提交的版本。要慎重使用。
git reset -- hard HEAD
另外一种做法,用checkout
,也能一下子撤销暂存区和工作区的修改,只要在checkout
后面指定恢复到HEAD
指向的版本。
git checkout HEAD -- yourfile.md
直接删除某次提交 (更危险)
假设现在HEAD
引用指向master
分支的最后一次提交版本。HEAD^
就代表HEAD
引用的父版本节点,就是上一次提交。
git reset HEAD^
这个操作直接把最新一次提交版本完全从Git仓库删除。但不删除暂存区和工作区的内容。但也是危险操作。
git rm 删除文件
git rm <filename>
本意表示:从暂存区删除某文件,此文件此后不再纳入版本管理。
在rm
后面加上-f
参数,可以同时从工作区删除此文件。
git rm -f <filename>
如果只是简单地删除文件,文件还是一直在版本管理的跟踪文件列表里。
只删除暂存区的文件,保留工作区文件
如果我们想要保留工作区的文件,只删除保留在暂存区的该文件,可以在rm
后面加上--cached
。
git rm --cached
删除所有提交中的某文件
如果Git仓库里混入了一个很大的没用的文件,为了让Git仓库不保留此文件对象,必须从所有包含此文件的提交中删除此文件,这需要下面这个命令,
git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch <FOLDERNAME>"
git mv 移动文件
git mv
命令的本意是:把文件从一个地址移动到另一个地址。
git mv <path1> <path2>
也可以用来改文件名,
git mv path/fileA.md path/fileB.md
把path/fileA.md
移动到path/fileB.md
,就相当于把fileA.md
的名字改成path/fileB.md
。
git log 查看提交日志
git log
查看的是Git仓库现存的历史版本。
git log
显示如下,
commit 2a38048dee55d5d2fe3b53048aca384fe0d3dfcb
Author: helloShen <symantec__@hotmail.com>
Date: Wed Mar 1 01:13:09 2017 -0500
Effective Java source code
加上-p
参数,用来显示每次提交间的区别。
git log -p
加上-num
参数,提示只显示最近两次提交。
git log -2
加上--stat
参数,列出所有被修改过的文件、有多少文件被修改了以及被修改过的文件的哪些行被移除或是添加了。作为每次commit的一个总结。
另外一个常用的选项是 --pretty
。这个选项可以指定使用不同于默认格式的方式展示提交历史。这个选项有很多子选项,比如oneline
,short
,full
,fuller
和format
。
比如,--pretty=oneline
可以把每个commit在一行内显示。
$ git log --pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 changed the version number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test
a11bef06a3f659402fe7563abf99ad00de2209e6 first commit
--pretty=format
可以按照一定的格式显示commit统计信息,
$ 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
当--pretty=format
和--graph
一起使用,可以用ASCII码图形显示,commit记录。
$ git log --pretty=format:"%h %s" --graph
* 2d3acf9 ignore errors from SIGCHLD on trap
* 5e3ee11 Merge branch 'master' of git://github.com/dustin/grit
|\
| * 420eac9 Added a method for getting the current branch.
* | 30e367c timeout code and tests
* | 5a09431 add timeout protection to grit
* | e1193f8 support for heads with slashes in them
|/
* d6016bc require time for xmlschema
* 11d191e Merge branch 'defunkt' into local
详细信息,请参与官方文档。
远程仓库
查看远程仓库列表
git remote -v
添加新的远程仓库
git remote add remote_name git@github.com:helloShen/git-practices.git
删除旧的远程仓库
git remote remove remote_name
修改远程仓库参数
可以用rename
命令为远程仓库重命名。
git remote rename old_remote_name new_remote_name
从远程仓库抓取数据
git fetch remote_name
这个命令会访问远程仓库,从中拉取所有你还没有的数据。执行完成后,你将会拥有那个远程仓库中所有分支的
引用,可以随时合并或查看。必须注意 git fetch
命令会将数据拉取到你的本地仓库 - 它并不会自动合并或修改你当前的工作。当准备好时你必须手动将其合并入你的工作。
git merge remote_name/branch_name
上面这条merge
命令中的remote_name/branch_name
是指定本地分支和远程仓库remote_name
的branch_name
分支合并。
可以使用 git pull
命令来自动的抓取然后合并远程分支到当前分支。这样更简单。
git pull remote_name
默认情况下,git clone
命令会自动设置本地 master
分支跟踪克隆的远程仓库的 master
分支(或不管是什么名字的默
认分支)。运行 git pull
通常会从最初克隆的服务器上抓取数据并自动尝试合并到当前所在的分支。
推送到远程仓库
git push remote_name master
查看远程仓库信息
如果想要查看某一个远程仓库的更多信息,可以使用
git remote show remote_name
标签
历史提交中,总有些提交比其他提交显得更重要,比如说beta
,v1.0
等等。这时候可以对这些提交打标签。方便以后查看和访问。
查看标签
打tag
简单的命令,可以查看现有tag列表。
git tag
后面加上一些条件,可以查看符合这些条件的特定tag,比如-l
参数加上<pattern>
可以,用pattern
来筛选tag,
git tag -l 'v1.2.5*' #只查看v1.2.5系列
查看标签详细信息
show
命令可以查看附有某个标签的提交的详细信息。
git show v1.4
创建标签
创建轻量级标签
直接用tag
命令,后面加上标签名,就能为最新提交版本创建轻量级标签。除了标签名本身,没有其他任何信息。
git tag v1.4
在标签名后,再加上某次提交开头的几位校验和,就可以给任意一个历史提交创建标签,
git tag -a v1.2 9fceb02
创建附注标签
在tag
命令后,加上-a
参数,表示创建“附注标签”。可以用-m
参数添加一条描述信息。
git tag -a v1.4 -m 'my version 1.4'
删除标签
要删除标签,可以在tag
命令后,加上-d
参数,
git tag -d <tag_name>
别名
可以通过git config
来为一个命令设置别名。别忘了,git的config
有三个级别,--system
,--global
和默认项目级别。
$ git config --global alias.co checkout
比如上面这条命令,就可以为当前用户给checkout
命令,设置一个缩略语co
。