创建版本库及第一次提交
首先看一下当前Git的版本
1 | $ git --version |
在开始Git之前,我们需要设置一下Git的环境变量,这个设置是一次性的工作。即这些设置会在全局文件(用户主目录的 .gitconfig
)或系统文件(/etc/gitconfig
)中做永久记录。
- 告诉Git当前用户的姓名和邮件地址,配置的用户名和邮件地址将在版本库提交时作为提交者的用户名和邮件地址。
1 | $ git config --global user.name "wanghongbo" |
设置一些Git别名,以便可以使用更为简洁的子命令。
例如:输入git ci
即相当于git commit -s
,输入git st
即相当于git -p status
。- 如果拥有系统管理员权限,希望注册的命令别名能被所有用户使用,可执行如下命令:
1
2
3
4$ git config --system alias.br branch
$ git config --system alias.ci "commit -s"
$ git config --system alias.co checkout
$ git config --system alias.st "-p status"
- 如果只在本用户使用,执行下面的命令:
1 | $ git config --global alias.st status |
创建Git版本库
下面就从一个空目录开始初始化版本库,这个版本库命名为“git-demo”。首先建立一个新的工作目录,进入该目录后,执行git init
创建版本库
1 | $ cd /d/workspace |
从上面版本库初始化后的输出中,可以看到执行git init
命令在工作区创建了隐藏目录.git
。
1 | $ ls -aF |
这个隐藏的.git
目录就是Git版本库(又叫仓库,repository)。.git
版本库目录所在的目录,即/d/workspace/git-demo
目录称为工作区,目前工作区除了包含一个隐藏的.git
目录外空无一物。
下面在工作区中创建一个文件:welcome.txt,内容就是一行“Hello Git.”。
1 | $ echo "Hello Git." >> welcome.txt |
为了将这个新建立的文件添加到版本库,需要执行下面的命令:
1 | $ git add welcome.txt |
Git和大部分其他版本控制系统都需要再执行一个提交操作,对于Git执行git commit
命令完成提交。在提交过程中需要输入提交说明,这个要求对Git是强制性的,不像CVS、SVN等允许空白的提交说明。
在Git提交时,如果在命令行不提供提交说明(没有使用 -m 参数),Git会自动打开一个编辑器,要求您在其中输入提交说明,输入完毕保存退出。
下面进行提交,为了说明方便,使用 -m 参数直接给出提交说明。
1 | $ git commit -m "初始化的第一次提交" |
从上面的命令及输出可以看出:
- 通过-m参数设置提交说明为:“初始化的第一次提交”。该提交说明也显示在命令输出的第一行中。
- 命令输出的第一行还显示了当前处于名为master的分支上,提交ID为e55c54c,且该提交是该分支的第一个提交,即根提交(root-commit)。根提交和其他提交的区别在于没有关联的父提交。
- 命令输出的第二行开始显示本次提交所做修改的统计:修改了一个文件,包含一行的插入。
思考:Git工作区下为什么有个.git目录?
Git及其他分布式版本控制系统的一个显著特点:版本库位于工作区的根目录下。对于Git,版本库位于工作区根目录下的.git
目录中,且仅此一处,在工作区的子目录下则没有任何其他跟踪文件或目录。Git为什么要这样设计呢?
对于CVS,工作区的根目录及每个子目录下都有一个CVS
目录,该目录中包含几个配置文件,建立了对版本库的追踪。
如CVS
目录下的Entries
文件记录了从版本库检出到工作区的文件的名称、版本和时间戳等,这样就可以通过对
工作区文件时间戳的改变来判断文件是否更改。这样设计的好处是,可以将工作区移动到任何其他目录,工作区和版本
控制服务器的映射关系保持不变,这样工作区依然能正常工作。甚至还将工作区的某个子目录移动到其他位置,形成
新的工作区,在新的工作区仍然能完成版本控制相关的操作。但缺点也很多,例如工作区文件修改了,因为没有
原始文件做比对,因此向服务器提交修改的时候只能对整个文件进行传输而不能仅传输文件的改动部分。还有
一个风险就是信息泄露,通过扫描CVS/Entries
文件就能得到目录下的文件列表,造成信息泄露。
对于SVN,工作区的根目录和每一个子目录下都有一个.svn
目录,该目录不仅包含了类似CVS的跟踪目录下的
配置文件,还包含了当前工作区下每一个文件的拷贝。多出文件的原始拷贝让某些svn命令可以脱离版本库执行,
还可以在由客户端向服务器提交时,仅仅对文件改动的内容提交,因此改动的文件可以和原始拷贝进行差异比较。
缺点除了像CVS跟踪目录造成信息泄露,还导致加倍占用工作区的空间。再有就是当在工作区目录下针对文件内容
进行搜索时,会因为.svn
目录下文件的原始拷贝,导致搜索的结果加倍,而出现混乱的搜索结果。
Git的设计,将版本库放在工作区根目录下,所有的版本控制操作(除了和远程版本库之间的相互操作)都是在
本地即可完成,不向SVN只有寥寥几个命令才能脱离网络执行。而且Git也不存在安全泄露(只要保护好.git
目录),
也没有SVN本地文件搜索结果混乱的问题,Git甚至还提供了一条git grep
命令来更好地搜索工作区的文件内容。
1 | $ git grep "Hello" |
当工作区中包含子目录,在子目录中执行Git命令时,如何定位版本库?
实际上,Git工作区目录下执行操作时,会对目录依次向上递归查找.git
目录,找到.git
目录就是工作区对应的版本库,.git
所在的目录就是工作区的根目录,文件.git/index
记录了工作区文件的状态(实际上是暂存区的状态)。
例如,在非Git工作区执行git
命令,会因找不到.git
目录而报错。
1 | $ cd /d/workspace/webstrom |
怎样获取Git版本库的位置以及工作区根目录位置?
- 先在工作区下建立目录
a/b/c
,进入该目录中。
1 | $ mkdir -p a/b/c` |
- 显示版本库
.git
目录所在的位置。
1 | $ git rev-parse --git-dir |
- 显示工作区根目录
1 | $ git rev-parse --show-toplevel |
- 相对于工作区根目录的相对目录
1 | $ git rev-parse --show-prefix |
- 显示从当前目录(cd)后退(up)到工作区的根的深度。
1 | $ git rev-parse --show-cdup |
思考:git config 命令参数的区别?
在之前使用过的git config
命令,有的使用了--global
参数,有的使用了--system
参数,这两个参数有什么区别?
- 执行下面的命令,将打开
D:/workspace/git-demo/.git/config
文件进行编辑。
1 | $ cd /d/workspace/git-demo/ |
- 执行下面的命令,将打开
C:/Users/ASUS/.gitconfig
(用户主目录下的.gitconfig
文件)全局配置文件进行编辑。
1 | $ git config -e --global |
- 执行下面的命令,将打开
E:/developerTools/git/mingw64/etc/gitconfig
系统级配置文件进行编辑。
1 | $ git config -e --system |
Git的三个配置文件分别是版本库级别的配置、全局配置(用户主目录下)和系统级配置(Git安装目录下)。
其中版本库级别配置文件的优先级最高,全局配置文件次之,系统级配置文件优先级最低。这样的优先级设置
可以让版本库.git
目录下的config
文件中的配置可以覆盖用户主目录下的Git环境配置,而用户主目录下的配置也可以覆盖系统的Git配置文件。
执行之前的三个git config
命令,会看到这三个级别配置文件的格式和内容,采用INI文件格式。如下:
1 | $ cat /d/workspace/git-demo/.git/config |
git config
命令可以用来读取和更改INI配置文件的内容。使用git config <section>.<key>
,来读取INI配置文件中某个配置的键值。例如读取[core]
小节的bare
的属性值,命令如下:
1 | $ git config core.bare |
如果想要更改或配置INI文件中某个属性的值也很简单,命令格式:git config <section>.<key> <value>
。
1 | $ git config a.b learningGit |
打开.git/config
文件会看到增加的内容:
1 | [a] |
思考:是谁提交的?
在一开始我们为Git设置了user.name
和user.email
全局环境变量,如果不设置会怎样?执行下面的命令,删除Git全局配置文件中关于user.name
和user.email
的设置:
1 | $ git config --unset --global user.name |
这样关于用户名和邮件的设置都被清空了,执行下面的命令将看不到输出
1 | $ git config user.name |
下面再进行一次提交,看看提交过程有什么不同,以及提交之后显示的提交者是谁?
1 | $ git commit --allow-empty -m "这是谁提交的?" |
上面的命令使用了--allow-empty
参数,这是因为没有对工作区的文件进行任何修改,Git默认是不会执行提交,使用了--allow-empty
参数后,允许执行空白提交。
从提交结果看到,因为没有设置user.name
和user.email
,提交结果中Git提供了详细的帮助指引来告诉如何设置必须的变量,以及如何修改之前提交中出现的错误的提交者信息。
为了保证提交时提交者和作者信息的正确性,重新恢复user.name
和user.email
的设置。
1 | $ git config --global user.name "wanghongbo" |
然后执行下面的命令,重新修改最新的提交,修正提交者的错误信息。
1 | $ git commit --amend --allow-empty --reset-author |
说明:
- 参数
--amend
是对刚刚的提交进行修补,这样就可以改正之前错误的提交,而不会产生新的提交; - 参数
--allow-empty
是因为要进行修改的提交实际上是一个空白提交,Git默认不允许空白提交; - 参数
--reset-author
的含义是将提交者的ID重置,否则会影响最新的Commit的ID。这条命令也会重置AuthorDate的信息。
通过日志可以看到提交者的信息已经改正了。
1 | $ git log --pretty=fuller |