Chapter2 基础
参考自 https://git-scm.com/book/zh/v1/Git-%E5%9F%BA%E7%A1%80
2.1 获取项目的仓库
-
在工作目录中初始化新仓库
如果要对本地的某个项目进行版本管理,只需要导航到项目所在目录,执行如下指令:
$ git init
执行完毕后,目录下会出现一个 .git 文件夹,所有 git 需要的数据和资源都保存在这个目录中。
接下来,将项目中需要进行版本管理的文件纳入到 git 版本控制中:
$ git add *.c $ git add README $ git commit -m 'initial project version'
add 命令告诉 git, 该项目的那些文件需要被跟踪管理。commit 命令则是将文件暂存区的文件快照保存到本地数据库中。
-
从现有仓库克隆
如果相对某个开源项目出份力,可以先把该项目的 git 仓库复制一份出来,这就需要用到
git clone
命令。注意,这跟 svn 的checkout
不一样。git clone
命令会克隆项目的所有历史数据(每一个文件的每一个版本),而checkout
只是拉取服务器上项目的最新版本。克隆仓库的命令格式为
git clone [url]
, 比如,要克隆 Ruby 语言的 git 代码仓库 Grit, 可以用下面的命令:$ git clone git://github.com/schacon/grit.git
这个命令执行后,会在当前目录下新建一个 grit 目录,其中包含一个 .git 目录,用于保存下载下来的所有版本记录,然后从中取出最新版本的文件拷贝。
2.2 把跟新记录到仓库
工作目录中的文件不外乎是两种状态:已跟踪或未跟踪。已跟踪的文件是本来就被纳入版本控制的文件,在上次快照中有它们的记录,工作一段时间后,它们的状态可能是 未更新、已修改或已放入暂存区;而其他所有文件都属于未跟踪。初次克隆某个仓库时,工作目录中的所有文件都属于已跟踪文件,且状态都为未修改。
在编辑过某些文件之后,git 将这些文件标记为已修改。我们逐步把这些修改过的文件放到暂存区域,直到最后一次性提交所有这些暂存起来的文件,如此重复。
使用 git 时的文件状态变化周期如下图所示:
检查当前文件状态
使用 git status
命令可以查看哪些文件当前处于什么状态。
上一节我们 clone 了 grit 项目,在这个项目的根目录新建一个 README 文件。然后运行 git status
将会看到以下输出:
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
README
nothing added to commit but untracked files present (use "git add" to track)
在状态报告中可以看出 README 出现在 "Untracked files" 下面。表示 README 还没有被 git 跟踪。
跟踪新文件
使用 git add
命令可以跟踪一个新文件:
$ git add README
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: README
可以看到 README 的状态变成了 "Changes to be committed" 表示它变成了已暂存状态。如果此时提交,那么该文件此时此刻的版本将被留存到历史记录里。其实 git add
的潜台词就是把目标文件的快照放到已暂存区域,也就是 add file into staged area,这就好理解后续 add 操作的实际意义了。
暂存已修改文件
修改一下目录下 benchmarks.rb 文件,再用 git status
查看一下当前状态:
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: README
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: benchmarks.rb
可以看到 benchmarks.rb 被放到了 "Changes to be committed", 这说明已跟踪文件的内容发生了变化,但还没有放到暂存区。要把它放到暂存区的话,还是用之前的 git add
命令就好了,这个命令的执行效果和上一小节一样,就不展示了。
忽略某些文件
如果不希望某些文件纳入 git 的管理。可以在 根目录 创建一个名为 .gitignore
的文件,列出要忽略的文件模式。
- 所有空行或者以注释符号 # 开头的行都会被 git 忽略;
- 匹配模式最后跟反斜杠 (/) 说明要忽略的是目录;
- 要忽略指定模式以外的文件或目录,可以在模式前加上感叹号 (!) 取反;
- 可以使用标准 glob 模式匹配:
- 星号 * 匹配零个或多个任意字符;
- 问号 ? 只匹配一个任意字符;
- [abc] 匹配方括号中的某个字符;
- [0-9] 匹配所有 0 到 9 的数字;
下面是一个 .gitignore 文件的例子:
# 这行是注释
# 忽略所有 .a 结尾的文件
*.a
# 但 lib.a 除外
!lib.a
# 只忽略根目录下的 TODO.txt 文件,不包括子目录
/TODO.txt
# 会忽略 doc/*.txt 但不包括子目录
doc/*.txt
# 忽略 doc 目录下所有 *.txt 文件
doc/**/*.txt
# 忽略 build/ 目录下的所有文件
build/
查看具体修改
修改包括两方面:工作目录的修改(相对于暂存区);暂存区的修改(相对于仓库);
要显示工作目录与暂存区文件之间的差异,可以使用 git diff
命令:
$ git diff
diff --git a/benchmarks.rb b/benchmarks.rb
index 3cb747f..da65585 100644
--- a/benchmarks.rb
+++ b/benchmarks.rb
@@ -36,6 +36,10 @@ def main
@commit.parents[0].parents[0].parents[0]
end
+ run_code(x, 'commits 1') do
+ git.commits.size
+ end
+
run_code(x, 'commits 2') do
log = git.commits('master', 15)
log.size
要显示暂存区与已提交文件之间的差异,可以使用 git diff --staged
(或者用旧版 git diff --cached
):
$ git diff --cached
diff --git a/README b/README
new file mode 100644
index 0000000..03902a1
--- /dev/null
+++ b/README2
@@ -0,0 +1,5 @@
+grit
+ by Tom Preston-Werner, Chris Wanstrath
+ http://github.com/mojombo/grit
+
+Grit is a Ruby library for extracting information from a Git repository
提交更新
提交更新使用如下指令:
$ git commit
上述会唤起编辑器来让你输入提交日志。也可以在后面加上 -m
参数写上简单日志:
$ git commit -m "some log text"
git commit
会将暂存区中的文件提交到仓库中。如果想要省略 git add
直接把工作目录的文件修改提交到仓库,可以使用 -a
选项:
$ git commit -a -m "skip git add"
移除文件
从仓库中移除一个文件,或者说不再跟踪某个文件,需要两个步骤:
将文件从暂存区中移除,使用
git rm
命令,这个文件会从工作目录中删除,并不再被 Git 跟踪;-
将移除操作提交到仓库,使用
git commit
命令;$ git mv file_from file_to
实际上它相当于
$ mv README.txt README $ git rm README.txt $ git add README
如果不想删掉工作目录中的文件,仅是移除 git 对文件的跟踪,可以使用 --cached
选项:
$ git rm --cached readme.txt
git rm
指令后面也可以接 glob 模式匹配,下述指令将删除 log/ 目录下所有 .log 文件:
$ git rm log/\*.log
移动文件
要进行文件移动操作,使用如下命令:
$ git mv file_from file_to
实际上它相当于
$ mv README.txt README
$ git rm README.txt
$ git add README
2.3 查看提交历史
提交了若干更新后,可以用 git log
命令查看,它还有一些有空的选项,具体可以参考 原文
除了使用 git log
指令,还可以在 bash 里输入 gitk, 打开 gitk 可视化工具来查看。
2.4 撤销操作
这一节介绍撤销操作相关的命令。
撤销最后一次提交
有的时候提交完了才发现有些文件没有提交,或者提交信息写错了。想要重写刚刚的提交操作,可以使用 --amend
选项来重新提交:
$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend
如上,第一次提交时忘记了一个文件,通过 git add
加上那个文件之后,再次使用 --amend
来提交。这样做的话最后只会产生一次提交,并且你有机会重写提交日志。
取消已经暂存的文件
如果不小心执行了 git add
命令,把不想提交的代码也提交到了暂存区,这个时候可以看一下 git status
中给出的恢复提示:
$ git add .
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: README.txt
modified: benchmarks.rb
如上,根据提示,只要执行 git reset HEAD <file>
命令,就可以取消已经暂存的文件。
取消对文件的修改
如果修改了工作目录中的文件,想会退到未修改之前的状态,这个时候可以看一下 git status
给出的提示:
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: benchmarks.rb
如上,根据提示,只要执行 git checkout -- <file>
命令,就可以会退到未修改时的版本。
2.5 远程仓库的使用
远程仓库是指托管在网络上的项目仓库,关于这部分的知识还没有理解透彻,这里先记下操作方法,在实践中继续领会。
查看当前的远程仓库:
要查看当前配置有哪些远程仓库,可以使用 git remote
命令。它会列出每个远程仓库的名字:
$ git clone git://github.com/schacon/ticgit.git
Cloning into 'ticgit'...
remote: Reusing existing pack: 1857, done.
remote: Total 1857 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (1857/1857), 374.35 KiB | 193.00 KiB/s, done.
Resolving deltas: 100% (772/772), done.
Checking connectivity... done.
$ cd ticgit
$ git remote
origin
git remote -v
可以显示出对应的地址:
$ git remote -v
origin git://github.com/schacon/ticgit.git (fetch)
origin git://github.com/schacon/ticgit.git (push)
添加远程仓库
要添加一个新的远程仓库,使用 git remote add [shortname] [url]
命令,其中的 shortname 是为远程仓库取的别名。
$ git remote
origin
$ git remote add pb git://github.com/paulboone/ticgit.git
$ git remote -v
origin git://github.com/schacon/ticgit.git
pb git://github.com/paulboone/ticgit.git
抓取远程仓库中的数据
上述指令只是将远程仓库的 url 存在了本地,并为它取了一个别名,并没有拉取仓库的数据。要拉取数据,使用 git fetch [shortname]
指令:
$ git fetch pb
remote: Counting objects: 58, done.
remote: Compressing objects: 100% (41/41), done.
remote: Total 44 (delta 24), reused 1 (delta 0)
Unpacking objects: 100% (44/44), done.
From git://github.com/paulboone/ticgit
* [new branch] master -> pb/master
* [new branch] ticgit -> pb/ticgit
可以看到, pb 仓库里有两个新分支, master 和 ticgit.
需要注意的是 fetch 命令只是将远程仓库中的数据拉取到本地,并不会自动合并到当前工作分支(也就是说只是把数据存到了本地仓库里,并没有把数据提取到工作目录中)。
如果设置了某个分支用于跟踪某个远程仓库的分支。可以使用 git pull
命令抓取数据,并将数据合并到本地仓库的当前分支。实际上,默认情况下 git clone
命令本质上就是自动创建了本地的 master 分支用于跟踪远程仓库中的 master 分支(假设远程仓库确实有 master 分支)。所以一般我们运行 git pull
,目的都是要从原始克隆的远端仓库中抓取数据后,合并到工作目录的当前分支。
推送数据到远程仓库
要将本地仓库中的数据推送到远程仓库,可以使用 git push [remote-name] [branch-name]
。如果我们要把本地的 master 分支推动到 origin 服务器上,可以使用如下命令:
$ git push origin master
查看远程仓库信息
你可以使用 git remote show [remote-name]
命令来查看某个远程仓库的信息。
$ git remote show origin
* remote origin
URL: git://github.com/schacon/ticgit.git
Remote branch merged with 'git pull' while on branch master
master
Tracked remote branches
master
ticgit
远程仓库的删除和重命名
使用 git remote rename
可以修改远程仓库的别名:
$ git remote rename pb paul
$ git remote
origin
paul
使用 git remote rm
命令可以移除不需要的远程仓库:
$ git remote rm paul
$ git remote
origin
2.6 打标签
显示已有标签
使用 git tag
可以显示已有的标签:
$ git tag
v0.1
v1.3
新建标签
Git 使用的标签有两种类型:轻量级的(lightweight) 和 含附注的(annotated)。我们一般推荐后者,因为它能够携带更详细的信息。
含附注的标签
要创建含附注的标签,只需要加上 -a
参数即可:
$ git tag -a v1.4 -m 'my version 1.4'
$ git tag
v0.1
v1.3
v1.4
轻量级标签
直接输入标签名,即可创建轻量级标签:
$ git tag v1.4-lw
$ git tag
v0.1
v1.3
v1.4
v1.4-lw
v1.5