Git
是一种分布式版本控制系统 (Distributed Version Control System DVCS) 。这种系统下,客户端不只是简单地拉取某个版本的文件,而是把整个记录文件版本的数据库(即整个代码仓库)都克隆到本地系统上。这样以来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。因为每一次的克隆工作,实际上都是一次对代码仓库的完整备份。
Git 中所有数据在存储前都会计算校验和,然后以校验和来引用某个版本的文件,该校验和是根据文件的内容或目录结构使用 SHA-1 哈希算法计算出来的,比如:
24b9da6552252987aa493b52f8696cd6d3b00373
Git 数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。
Git 最重要的地方是有三个区:
$ git config --global user.name "Jack Cheng"
$ git config --global user.email "767833640@qq.com"
如果使用了 --global
选项,则该命令只需要提交一次,无论你以后在系统中执行何种操作,Git 都会使用这种配置。如果你想针对某个特定的项目使用不同的用户名称和邮箱,可以在那个项目目录下运行没有 --global
选项的命令来配置。
$ git config --list
$ git config user.name
$ git help config
$ git help push
$ git init
这个命令将创建一个名为 .git
的子目录,这个字目录含有你初始化的 Git 仓库中所有的必须文件,这些文件是 Git 仓库的骨干。但是这只是做了一个初始化操作,在当地文件夹的项目里的文件还没有被跟踪。使用 git add
命令来跟踪文件,使用 git commit
命令来提交文件到本地的 Git 仓库中。
$ git status
可以查看当前仓库哪些文件处于未跟踪状态,哪些文件已经放入暂存区等待提交。
可以看到 .DS_Store
文件位于 Untracked files
标题下,表示这个文件是新创建的未被跟踪的文件,Git 仓库中不存在这个文件的信息。
可以看到这个时候 .gitignore
文件位于 Changes to be commited
这个标题下面,说明这个文件已经被移入暂存区了,处于已暂存的状态。
$ git add 文件名
git add
不仅可以跟踪新文件并放到暂存区,还能把已修改的文件也放到暂存区,这是一个多功能命令。放到暂存区的这些文件在下次提交时将会一并提交到 Git 仓库中。因此对于 git add
命令的最好翻译是 “添加内容到下一次的提交中”。
$ git add 文件目录
此时 git add
命令将递归地跟踪该目录下的所有文件,并把目录下的所有文件都放入暂存区。
$ git diff
git diff
命令可以查看当前工作目录中已修改的文件和暂存区的文件的差异(注意只是和暂存区的差异,不是和上次提交以来的差异,因此如果你把所有已经修改的文件都添加到暂存区后,git diff
将不会返回任何东西)
$ git diff --staged 或者
$ git diff --cached
$ git commit -m "说明当前做了什么,以后版本回退时可以轻易找到"
注意提交的都是放在暂存区的文件,每一次的提交的版本都会在历史记录中记录下来,以后都可以回到这个版本。(只有 commit 以后的版本可以回退)
$ git log
使用 git log
命令查看历史提交记录:
$ git log --pretty=oneline
为了简化查看,可以使用 git log --pretty=oneline
参数:
$ git reset --hard commit_id
找到要回退的版本的前面的 commit_id
,然后使用 git reset --hard commit_id
命令来回退到想要的版本,只需要打出 commit_id
的前几个字母即可,Git 会自动查找对应的 id
:
可以看到此时项目的最新版本已经回退到了 3950d
的版本。
$ git reflog
回退以后,会发现之前的最新版本 9fd77
已经不在 git log
的目录中了,此时假如我们又想回到之前的最新的版本怎么办?首先使用 git reflog
命令来查找提交 9fd77
时的记录:
git reset --hard commit_id
可以看到我们在回退前最新一次提交的 commit_id
是 9fd77b1
,因此我们再用 git reset --hard 9fd77b1
来进入到回退前的最新版本:
因此,我们也就从过去回到现在了。
有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 --amend 选项的提交命令来重新提交:
👉 git commit --amend
例如,你提交后发现忘记了暂存某些需要的修改,可以像下面这样操作:
$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend
有时候我们会有一些文件不需要纳入 Git 的管理,比如上面的 .DS_Store
,此时就应该编写 .gitignore
文件来列出要 Git 仓库忽略的文件模式。该文件的格式规范如下:
以 #
开头的行为 Git 的注释。
使用 /
放在文件名的开头可以防止递归地忽略所有非当前目录中的该文件。比如
TODO
会忽略 Git 仓库中所有目录下的 TODO
文件,但如果只希望 Git 忽略当前目录下的 TODO
文件,而不要忽略其它目录下的 TODO
文件,则应该写成这样:
/TODO
使用 /
放在文件名的末尾表明这个文件是一个目录,Git 将会忽略这个目录下的所有文件。
如果希望 Git 只记录某一个特定的文件,而忽略除了这个文件以外的所有文件,可以在这个文件名前面使用 !
取反。但是这种功能一般是用于以下情况:
# 忽略所有的 .a 文件
*.a
# 但是所有的 lib.a 文件不能被忽略
!lib.a
指定文件的格式一般要使用正则表达式:
*
匹配 0 个或多个任意字符;[abc]
可以匹配任何一个在方框号中的字符(在这个例子中是要么匹配一个a,要么匹配一个b,要么匹配一个c);?
只匹配一个任意字符;[0-9]
表示匹配在 0 到 9 范围内的所有数字。**
表示匹配任意的中间目录,比如 learn/**/git
可以匹配 learn/git
、learn/no/git
或者 learn/no/python/git
。看一个 .gitignore
文件的例子:
# 忽略所有以 .a 结尾的文件
*.a
# 不能忽略所有 lib.a 文件
!lib.a
# 仅仅忽略当前目录下的 TODO 文件
/TODO
# 忽略 build 目录下的所有文件
build/
# 仅仅忽略 doc 一个目录下的所有 .txt 文件
doc/*.txt
# 忽略 doc 目录下(包括子目录)的所有 .pdf 文件
doc/**/*.pdf
最常见的情况是,在 .gitignore
文件中未列出对应的文件,有时为了贪求效率,使用 git add .
把所有文件都放到暂存区域,包括把一些不希望加入 Git 版本管理的日志文件等也放进去了,这个时候我们希望可以从暂存区域中删除这些不希望被管理的文件,但是使这些文件仍然被存放在工作目录上,而不被 Git 继续跟踪。此时可以使用 git rm --cached filename
命令:
这样就可以把误添加的文件从暂存区中移除,而防止下一次 commit
时加入到 Git 仓库中去。
还有一种情况就是,我希望删除的文件已经 commit
或者 add
了,即已经被跟踪了,但我希望完全删除这个文件,即把工作目录中的这个文件也删了,这时我们就可以先在本地项目目录中删除这个文件,然后再使用 git rm filename
命令把该文件从已跟踪的文件清单中一并删除:
这个是文件已经 add
但没有 commit
的情况,使用 git rm
命令就直接清空了,如果文件之前有过 commit
,而你又把想删除的文件从工作目录中删除了:
可以看到删除文件的操作记录在 Changes not staged for commit
标题下, 意味着你需要把这个删除的操作再提交一遍,使得 Git 仓库知道这个文件已经删除了,不应该再被跟踪了。
如果要在 Git 中对某些文件进行重命名,可以使用 git mv original_name target_name
命令:
执行这个命令后,可以看到在工作目录中的 test.cpp
也被重命名为 main.cpp
了,这个时候只要提交这次重命名操作就可以了。