前言
最近版本管理使用了自己搭建的gitlab服务器,git命令大家肯定都或多或少用过,以前用的时候也只是git add . git commit等等几个简单的命令,并没有深入去了解Git的内部原理。这次面对可以会出现各种管理的冲突,我觉得有必要深入学习一下Git的原理和使用。下面就一步一步来开始这趟旅程吧!
首先申明一下,这系列文章可能会有点枯燥,大部分都是讲解一些命令用法概念,并没有很新奇的玩意。
Git命令概览
输入git,Git就会不带任何参数的列出他的选项和最常用的子命令。
$ git
usage: git [--version] [--help] [-C <path>] [-c name=value]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
<command> [<args>]
These are common Git commands used in various situations:
start a working area (see also: git help tutorial)
clone Clone a repository into a new directory
init Create an empty Git repository or reinitialize an existing one
work on the current change (see also: git help everyday)
add Add file contents to the index
mv Move or rename a file, a directory, or a symlink
reset Reset current HEAD to the specified state
rm Remove files from the working tree and from the index
examine the history and state (see also: git help revisions)
bisect Use binary search to find the commit that introduced a bug
grep Print lines matching a pattern
log Show commit logs
show Show various types of objects
status Show the working tree status
grow, mark and tweak your common history
branch List, create, or delete branches
checkout Switch branches or restore working tree files
commit Record changes to the repository
diff Show changes between commits, commit and working tree, etc
merge Join two or more development histories together
rebase Reapply commits on top of another base tip
tag Create, list, delete or verify a tag object signed with GPG
collaborate (see also: git help workflows)
fetch Download objects and refs from another repository
pull Fetch from and integrate with another repository or a local branch
push Update remote refs along with associated objects
'git help -a' and 'git help -g' list available subcommands and some
concept guides. See 'git help <command>' or 'git help <concept>'
to read about a specific subcommand or concept.
要想得到一个完整的git子命令列表(看着有点让人害怕),可以输入git help -a.
$ git help -a
usage: git [--version] [--help] [-C <path>] [-c name=value]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
<command> [<args>]
add fsck-objects rebase--helper
add--interactive gc receive-pack
am get-tar-commit-id reflog
annotate grep remote
apply gui remote-ext
archimport gui--askpass remote-fd
archive gui--askyesno remote-ftp
askpass gui.tcl remote-ftps
bisect hash-object remote-http
bisect--helper help remote-https
blame http-backend repack
branch http-fetch replace
bundle http-push request-pull
cat-file imap-send rerere
check-attr index-pack reset
check-ignore init rev-list
check-mailmap init-db rev-parse
check-ref-format instaweb revert
checkout interpret-trailers rm
checkout-index log send-email
cherry ls-files send-pack
cherry-pick ls-remote sh-i18n--envsubst
citool ls-tree shortlog
clean mailinfo show
clone mailsplit show-branch
column merge show-index
commit merge-base show-ref
commit-tree merge-file stage
config merge-index stash
count-objects merge-octopus status
credential merge-one-file stripspace
credential-manager merge-ours submodule
credential-store merge-recursive submodule--helper
credential-wincred merge-resolve subtree
cvsexportcommit merge-subtree svn
cvsimport merge-tree symbolic-ref
cvsserver mergetool tag
daemon mktag unpack-file
describe mktree unpack-objects
diff mv update-index
diff-files name-rev update-ref
diff-index notes update-server-info
diff-tree p4 upload-archive
difftool pack-objects upload-pack
difftool--helper pack-redundant var
fast-export pack-refs verify-commit
fast-import patch-id verify-pack
fetch prune verify-tag
fetch-pack prune-packed web--browse
filter-branch pull whatchanged
fmt-merge-msg push worktree
for-each-ref quiltimport write-tree
format-patch read-tree
fsck rebase
可以从提示中看出,只有少数选项使用与git。大多数选项以[ARDS]的形式出现提示中,适用于特定的子命令。
例如,–version选项影响git命令,并显示版本号。
$ git --version
git version 2.13.0
为了方便起见,每个git子命令的文档都可以通过git help subcommand、git --help subcommand 或者git subcommand --help
来查看。
Git命令能理解“短”和“长”选项。例如git commit 命令将下面两条命令视为等价的
$ git commit -m "Fixed bug"
$ git commit --message="Fixed bug"
缩写形式-m使用了一个连字符,而长形式–message使用了两个连字符(这符合GNU的长选项扩展),有些选项只存在一种形式。
最后,可以通过“裸双破折号”的约定来分离一些列参数。例如,使用双破折号来分离命令行的控制部分与操作数部分,如文件名等。
$ git diff -w master origin -- tools/Makefile
有时候需要使用双破折号分离并显式表示文件名,否则可能会误认为他们是命令的另一部分。例如,如果恰好有一个文件和一个标签都叫main.c,然后你会看到不同的行为。
# 检出名为main.c的标签
$ git checkout main.c
# 检出名为main.c的文件
$ git checkout --mainc.
快速使用
初始化一个版本库
上面说了很多命令,为了实际见识Git的操作,我们可以新建一个版本库,添加一些内容,进行管理。
我新建了一个repo目录,并且进入到了此目录下,操作如下:
$ echo 'My new repo' > index.html
执行git init
,将repo转化为Git版本库。
$ git init
Initialized empty Git repository in C:/Users/zhangguang/Desktop/repo/.git/
Git不关心你是从一个完全空白的目录还是一个装满文件的目录开始的。这两种情况下,将目录转换到Git版本库的过程是一样的。为了显示目录是一个GIt版本库,git init
命令创建了一个隐藏目录,在项目的顶层目录,名为.git
。不同于CVS和SVN,git只有唯一的一个顶层.git
目录。
初始仓库完成后,repo目录下面一切保持不变,只是增加了一个.git的隐藏目录。Git将这个目录当做项目的工作目录,或者修改文件的目录。隐藏在.git内的版本库由Git维护。
向版本库中添加文件
为了管理内容,你必须明确地把它放入版本库中,这种有意识的步骤将重要文件与临时文件分离开来。
使用git add file
将file添加到版本库中。
$ git add index.html
注意:如果目录中已经有了很多文件,使用git add .命令让Git把当前目录及子目录中的文件都添加到版本库里(参数”.”,点或者UNIX说法中的”dot”,是当前目录的简写)。
在add之后,Git知道index.html这个文件是要留在版本库里。到目前为止,Git还只是暂存(staged)了这个文件,这是提交之前的中间步骤。Git将add和commit这两部分开以避免频繁的变化,一个commit可以提交多次累积下来的add。试想一个如果删除一个文件夹,修改两三行代码,都要去更新一次版本库,这样将会耗费大量的时间去做重复性的操作。
运行git status,显示中间状态的index.html
$ git status
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: index.html
这个命令显示新闻界index.html健在下一个提交的时候添加到版本库里。
提交的时候,除了会提交目录和文件内容的实际变化,Git还会在每次提交的时候记录其他一些元数据,包括日志消息和本次变更的作者。一条完全限定的git commit命令必须提供日志消息和作者:
$ git commit -m"Initial contents of repo"\--author="Test Repo@test.com"
[master (root-commit) c773409] Initial contents of repo--author=Test Repo@test.com
1 file changed, 1 insertion(+)
create mode 100644 index.html
注意:可以在交互式编辑器回话期间创建日志消息。
提交之后再次查看状态,显示没有突出的、暂存的变更需要提交。
$ git status
On branch master
nothing to commit, working tree clean
Git提示,工作目录是干净的,这意味着工作目录里不包含任何与版本库种不同的或者更改过的文件。
在对版本进行多次提交之前,需要让Git知道你的名字和邮件地址。
$ git config user.name "your username"
$ git config user.email "your email"
再次提交
为了了解更多的Git特性,我们需要创建一个稍微复杂的变更历史。我们来提交一个队index.html文件的修改,打开这个文件,转换成HTML保存。
$ vim index.html
$ cat index.html
<html>
<body>
My new repo.
</body>
</html>
提交:
$ git commit index.html -m"Convert to HTML"
这里提个小问题太,为什么这里不用git add index.html而是直接commit了?
查看提交,git log命令会查收版本库里一系列单独提交的历史。
$ git log
commit 306f94dc429956fc94a3b6ebcb0b3d075ce37366 (HEAD -> master)
Author: Repo@test.com
Date: Thu Nov 1 00:24:51 2018 +0800
Convert to HTML
commit c773409a4d6da80e69858095be509f0fcf0c8632
Author: Repo@test.com
Date: Thu Nov 1 00:10:37 2018 +0800
Initial contents of repo--author=Test Repo@test.com
条目顺序按照最新到最老的顺序列出来,每个条目显示了提交作者名字和email地址,提交日期,变更的日志信息和提交的内部识别码。
为了查看特定提交的更加详细的信息,可以使用git show
命令带一个提交码。
$ git show c773409a4d6da80e69858095be509f0fcf0c8632
commit c773409a4d6da80e69858095be509f0fcf0c8632
Author: Repo@test.com
Date: Thu Nov 1 00:10:37 2018 +0800
Initial contents of repo--author=Test Repo@test.com
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..7343a90
--- /dev/null
+++ b/index.html
@@ -0,0 +1 @@
+My new repo
如果在执行git show命令的时候没有显示的指定提交码,它将只显示最近一次提交的详细信息。
另一种产看方式是使用show-branch,提供当期那开发分支简洁的当行摘要。
$ git show-branch --more=10
[master] Convert to HTML
[master^] Initial contents of repo--author=Test Repo@test.com
参数–more=10表示额外10个版本,但是因为目前仓库只有两个版本,所以全部显示了。master是默认分支名。
查看差异
每次我们提交了之后都会希望能够看到哪些文件发生了改变,更详细的话希望知道更改了那几行代码。对于这个需求我们可以使用git diff
命令。
$ git diff c773409a4d6da80e69858095be509f0fcf0c8632
diff --git a/index.html b/index.html
index 7343a90..16d8662 100644
--- a/index.html
+++ b/index.html
@@ -1 +1,5 @@
-My new repo
+<html>
+<body>
+My new repo.
+</body>
+</html>
命名为c773409a4d6da80e69858095be509f0fcf0c8632
是较新的版本,因此每行新的内容面前都有一个加号(+)。
版本库内文件的删除和重命名
在版本库里面删除和添加一个文件是类似的,除了使用的命令是git rm
。假设在你的仓库里有一个不再需要的文件:poem.html,可以运行命令如下。
$ git rm poem.html
rm 'poem.html'
$ git commit -m"Remove a poem"
[master 7a02d23] Remove a poem
1 file changed, 1 deletion(-)
delete mode 100644 poem.html
和添加操作一样,删除操作也是两步:git rm
表示你想要删除这个文件的意图并暂存这个变更,接着git commit
在版本库里实现这个变更。
可以通过git rm
和git add
命令来间接为一个文件命名。
假如你都版本库里存在一个foo.html
的文件,需要命名为bar.html
:
$ mv foo.html bar.html
$ git rm foo.html
rm 'foo.html'
$ git add bar.html
$ ls
bar.html index.html
在这个序列里,必须先执行mv foo.html bar.html
,以防git rm命令会把foo.html从系统中永久删除。
也可以使用更简洁的git mv来实现相同的操作:
$ git mv foo.html bar.html
在任意情况下,暂存的变更都必须随后进行提交。
$ git commit -m"Moved foo to bar"
[master 8b72b0c] Moved foo to bar
1 file changed, 0 insertions(+), 0 deletions(-)
rename foo.html => bar.html (100%)
Git在对文件的移动操作上与其他同类系统不同,他利用一个基于两个文件版本内容相似度的机制。
创建版本库副本
如果已经建立了一个厨师版本库,可以使用git clone命令来创建一个完整的副本,或者叫克隆。这就是人们如何通过Git在相同的文件上做自己的项目,并保持和其他版本库的同步。
$git clone repo repoOfCopy
虽然这两个Git版本库里面包含相同的对象文件目录,但还有一些细微的差别,如果想探索其中的不同,可以使用如下命令。
$ls -lsa repo repoOfCopy
$diff -r repo repoOfCopy
配置文件
Git的配置文件全都是简单的.ini文件风格的文本文件。他们记录了很多Git命令使用的各种选项和设置。
Git支持不同层次的配置文件,按优先级递减的顺序如下:
.git/config
版本库特定的配置设置,可用–file选项修改,是默认选项,这些配置拥有最高优先级。
~/.gitconfig
用户特定的配置设置。可用–global选项修改。
/etc/gitconfig
这是系统范围的配置设置,如果你拥有他的写权限,可以用–system选项修改它。这些设置的优先级最低。
可以用git config -l
命令查看配置:
$ git config -l
总结
这一章介绍了很多基本命令的使用,但是关于git的一些基本概念和关于Git是如何工作的还没有讲到。例如Git是如何存储文件的每个版本的?一次提交由什么组成?那一串长长的提交码是哪里来的?分支的概念该如何理解?下一篇将介绍Git的一些概念。