Git入门的好文章,转载自青豆前端 » git基础 – 记录每次更新到仓库
记录每次更新到仓库html
如今咱们手上已经有了一个真实项目的 Git 仓库,并从这个仓库中取出了全部文件的工做拷贝。接下来,对这些文件做些修改,在完成了一个阶段的目标以后,提交本次更新到仓库。前端
请记住,工做目录下面的全部文件都不外乎这两种状态:已跟踪或未跟踪。已跟踪的文件是指原本就被归入版本控制管理的文件,在上次快照中有它们的记录,工做一段时间后,它们的状态多是未更新,已修改或者已放入暂存区。而全部其余文件都属于未跟踪文件。它们既没有上次更新时的快照,也不在当前的暂存区域。初次克隆某个仓库时,工做目录中的全部文件都属于已跟踪文件,且状态为未修改。git
在编辑过某些文件以后,Git 将这些文件标为已修改。咱们逐步把这些修改过的文件放到暂存区域,直到最后一次性提交全部这些暂存起来的文件,如此重复。因此使用 Git 时的文件状态变化周期如图 2-1 所示。github
18333fig0201-tn1正则表达式
图 2-1. 文件的状态变化周期shell
检查当前文件状态vim
要肯定哪些文件当前处于什么状态,能够用 git status 命令。若是在克隆仓库以后当即执行此命令,会看到相似这样的输出:编辑器
$ git status On branch master nothing to commit, working directory clean
这说明你如今的工做目录至关干净。换句话说,全部已跟踪文件在上次提交后都未被更改过。此外,上面的信息还代表,当前目录下没有出现任何处于未跟踪的新文件,不然 Git 会在这里列出来。最后,该命令还显示了当前所在的分支是 master,这是默认的分支名称,实际是能够修改的,如今先不用考虑。下一章咱们就会详细讨论分支和引用。工具
如今让咱们用 vim 建立一个新文件 README,保存退出后运行 git status 会看到该文件出如今未跟踪文件列表中:ui
$ vim README $ git status On branch 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”下面。未跟踪的文件意味着Git在以前的快照(提交)中没有这些文件;Git 不会自动将之归入跟踪范围,除非你明明白白地告诉它“我须要跟踪该文件”,于是不用担忧把临时文件什么的也纳入版本管理。不过如今的例子中,咱们确实想要跟踪管理 README 这个文件。
跟踪新文件
使用命令 git add 开始跟踪一个新文件。因此,要跟踪 README 文件,运行:
$ git add README
此时再运行 git status 命令,会看到 README 文件已被跟踪,并处于暂存状态:
$ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: README
只要在 “Changes to be committed” 这行下面的,就说明是已暂存状态。若是此时提交,那么该文件此时此刻的版本将被留存在历史记录中。你可能会想起以前咱们使用 git init 后就运行了 git add 命令,开始跟踪当前目录下的文件。在 git add 后面能够指明要跟踪的文件或目录路径。若是是目录的话,就说明要递归跟踪该目录下的全部文件。(译注:其实 git add 的潜台词就是把目标文件快照放入暂存区域,也就是 add file into staged area,同时不曾跟踪过的文件标记为须要跟踪。这样就好理解后续 add 操做的实际意义了。)
暂存已修改文件
如今咱们修改下以前已跟踪过的文件 benchmarks.rb,而后再次运行 status 命令,会看到这样的状态报告:
$ git status On branch 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 not staged for commit” 这行下面,说明已跟踪文件的内容发生了变化,但尚未放到暂存区。要暂存此次更新,须要运行 git add 命令(这是个多功能命令,根据目标文件的状态不一样,此命令的效果也不一样:能够用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等)。如今让咱们运行 git add 将 benchmarks.rb 放到暂存区,而后再看看 git status 的输出:
$ git add benchmarks.rb $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: README modified: benchmarks.rb
如今两个文件都已暂存,下次提交时就会一并记录到仓库。假设此时,你想要在 benchmarks.rb 里再加条注释,从新编辑存盘后,准备好提交。不过且慢,再运行 git status 看看:
$ vim benchmarks.rb $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: README modified: benchmarks.rb 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 文件出现了两次!一次算未暂存,一次算已暂存,这怎么可能呢?好吧,实际上 Git 只不过暂存了你运行 git add 命令时的版本,若是如今提交,那么提交的是添加注释前的版本,而非当前工做目录中的版本。因此,运行了 git add 以后又做了修订的文件,须要从新运行 git add 把最新版本从新暂存起来:
$ git add benchmarks.rb $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: README modified: benchmarks.rb
忽略某些文件
通常咱们总会有些文件无需归入 Git 的管理,也不但愿它们总出如今未跟踪文件列表。一般都是些自动生成的文件,好比日志文件,或者编译过程当中建立的临时文件等。咱们能够建立一个名为 .gitignore 的文件,列出要忽略的文件模式。来看一个实际的例子:
$ cat .gitignore *.[oa] *~
第一行告诉 Git 忽略全部以 .o 或 .a 结尾的文件。通常这类对象文件和存档文件都是编译过程当中出现的,咱们用不着跟踪它们的版本。第二行告诉 Git 忽略全部以波浪符(~)结尾的文件,许多文本编辑软件(好比 Emacs)都用这样的文件名保存副本。此外,你可能还须要忽略 log,tmp 或者 pid 目录,以及自动生成的文档等等。要养成一开始就设置好 .gitignore 文件的习惯,以避免未来误提交这类无用的文件。
文件 .gitignore 的格式规范以下:
全部空行或者以注释符号 # 开头的行都会被 Git 忽略。 可使用标准的 glob 模式匹配。 匹配模式最后跟反斜杠(/)说明要忽略的是目录。 要忽略指定模式之外的文件或目录,能够在模式前加上惊叹号(!)取反。
所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。星号(*)匹配零个或多个任意字符;[abc]匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?)只匹配一个任意字符;若是在方括号中使用短划线分隔两个字符,表示全部在这两个字符范围内的均可以匹配(好比 [0-9] 表示匹配全部 0 到 9 的数字)。
咱们再看一个 .gitignore 文件的例子:
# 此为注释 – 将被 Git 忽略 # 忽略全部 .a 结尾的文件 *.a # 但 lib.a 除外 !lib.a # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO /TODO # 忽略 build/ 目录下的全部文件 build/ # 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt doc/*.txt # ignore all .txt files in the doc/ directory doc/**/*.txt
A **/ pattern is available in Git since version 1.8.2.
查看已暂存和未暂存的更新
实际上 git status 的显示比较简单,仅仅是列出了修改过的文件,若是要查看具体修改了什么地方,能够用 git diff 命令。稍后咱们会详细介绍 git diff,不过如今,它已经能回答咱们的两个问题了:当前作的哪些更新尚未暂存?有哪些更新已经暂存起来准备好了下次提交? git diff 会使用文件补丁的格式显示具体添加和删除的行。
假如再次修改 README 文件后暂存,而后编辑 benchmarks.rb 文件后先别暂存,运行 status 命令将会看到:
$ git status On branch 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
要查看还没有暂存的文件更新了哪些部分,不加参数直接输入 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 --cached 命令。(Git 1.6.1 及更高版本还容许使用 git diff --staged,效果是相同的,但更好记些。)来看看实际的效果:
$ 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 diff 不过是显示尚未暂存起来的改动,而不是此次工做和上次提交之间的差别。因此有时候你一会儿暂存了全部更新过的文件后,运行 git diff 后却什么也没有,就是这个缘由。
像以前说的,暂存 benchmarks.rb 后再编辑,运行 git status 会看到暂存先后的两个版本:
$ git add benchmarks.rb $ echo '# test line' >> benchmarks.rb $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: benchmarks.rb 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 diff 看暂存先后的变化:
$ git diff diff --git a/benchmarks.rb b/benchmarks.rb index e445e28..86b2f7c 100644 --- a/benchmarks.rb +++ b/benchmarks.rb @@ -127,3 +127,4 @@ end main() ##pp Grit::GitRuby.cache_client.stats +# test line
而后用 git diff --cached 查看已经暂存起来的变化:
$ git diff --cached diff --git a/benchmarks.rb b/benchmarks.rb index 3cb747f..e445e28 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 add 过,不然提交的时候不会记录这些还没暂存起来的变化。因此,每次准备提交前,先用 git status 看下,是否是都已暂存起来了,而后再运行提交命令 git commit:
$ git commit
这种方式会启动文本编辑器以便输入本次提交的说明。(默认会启用 shell 的环境变量 $EDITOR 所指定的软件,通常都是 vim 或 emacs。固然也能够按照第一章介绍的方式,使用 git config --global core.editor 命令设定你喜欢的编辑软件。)
编辑器会显示相似下面的文本信息(本例选用 Vim 的屏显方式展现):
# Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # Changes to be committed: # new file: README # modified: benchmarks.rb # ~ ~ ~ ".git/COMMIT_EDITMSG" 10L, 283C
能够看到,默认的提交消息包含最后一次运行 git status 的输出,放在注释行里,另外开头还有一空行,供你输入提交说明。你彻底能够去掉这些注释行,不过留着也不要紧,多少能帮你回想起此次更新的内容有哪些。(若是以为这还不够,能够用 -v 选项将修改差别的每一行都包含到注释中来。)退出编辑器时,Git 会丢掉注释行,将说明内容和本次更新提交到仓库。
另外也能够用 -m 参数后跟提交说明的方式,在一行命令中提交更新:
$ git commit -m "Story 182: Fix benchmarks for speed" [master 463dc4f] Fix benchmarks for speed 2 files changed, 3 insertions(+) create mode 100644 README
好,如今你已经建立了第一个提交!能够看到,提交后它会告诉你,当前是在哪一个分支(master)提交的,本次提交的完整 SHA-1 校验和是什么(463dc4f),以及在本次提交中,有多少文件修订过,多少行添改和删改过。
记住,提交时记录的是放在暂存区域的快照,任何还未暂存的仍然保持已修改状态,能够在下次提交时归入版本管理。每一次运行提交操做,都是对你项目做一次快照,之后能够回到这个状态,或者进行比较。
跳过使用暂存区域
尽管使用暂存区域的方式能够精心准备要提交的细节,但有时候这么作略显繁琐。Git 提供了一个跳过使用暂存区域的方式,只要在提交的时候,给 git commit 加上 -a 选项,Git 就会自动把全部已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤:
$ git status On branch master 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 no changes added to commit (use "git add" and/or "git commit -a") $ git commit -a -m 'added new benchmarks' [master 83e38c7] added new benchmarks 1 files changed, 5 insertions(+)
看到了吗?提交以前再也不须要 git add 文件 benchmarks.rb 了。
移除文件
要从 Git 中移除某个文件,就必需要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),而后提交。能够用 git rm 命令完成此项工做,并连带从工做目录中删除指定的文件,这样之后就不会出如今未跟踪文件清单中了。
若是只是简单地从工做目录中手工删除文件,运行 git status 时就会在 “Changes not staged for commit” 部分(也就是未暂存清单)看到:
$ rm grit.gemspec $ git status On branch master Changes not staged for commit: (use "git add/rm <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) deleted: grit.gemspec no changes added to commit (use "git add" and/or "git commit -a")
而后再运行 git rm 记录这次移除文件的操做:
$ git rm grit.gemspec rm 'grit.gemspec' $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) deleted: grit.gemspec
最后提交的时候,该文件就再也不归入版本管理了。若是删除以前修改过而且已经放到暂存区域的话,则必需要用强制删除选项 -f(译注:即 force 的首字母),以防误删除文件后丢失修改的内容。
另一种状况是,咱们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然但愿保留在当前工做目录中。换句话说,仅是从跟踪清单中删除。好比一些大型日志文件或者一堆 .a 编译文件,不当心归入仓库后,要移除跟踪但不删除文件,以便稍后在 .gitignore 文件中补上,用 --cached 选项便可:
$ git rm --cached readme.txt
后面能够列出文件或者目录的名字,也可使用 glob 模式。比方说:
$ git rm log/\*.log
注意到星号 * 以前的反斜杠 \,由于 Git 有它本身的文件模式扩展匹配方式,因此咱们不用 shell 来帮忙展开(译注:实际上不加反斜杠也能够运行,只不过按照 shell 扩展的话,仅仅删除指定目录下的文件而不会递归匹配。上面的例子原本就指定了目录,因此效果等同,但下面的例子就会用递归方式匹配,因此必须加反斜杠。)。此命令删除全部 log/ 目录下扩展名为 .log 的文件。相似的好比:
$ git rm \*~
会递归删除当前目录及其子目录中全部 ~ 结尾的文件。
移动文件
不像其余的 VCS 系统,Git 并不跟踪文件移动操做。若是在 Git 中重命名了某个文件,仓库中存储的元数据并不会体现出这是一次更名操做。不过 Git 很是聪明,它会推断出究竟发生了什么,至于具体是如何作到的,咱们稍后再谈。
既然如此,当你看到 Git 的 mv 命令时必定会困惑不已。要在 Git 中对文件更名,能够这么作:
$ git mv file_from file_to
它会恰如预期般正常工做。实际上,即使此时查看状态信息,也会明白无误地看到关于重命名操做的说明:
$ git mv README.txt README $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) renamed: README.txt -> README
其实,运行 git mv 就至关于运行了下面三条命令:
$ mv README.txt README $ git rm README.txt $ git add README
如此分开操做,Git 也会意识到这是一次更名,因此无论何种方式都同样。固然,直接用 git mv 轻便得多,不过有时候用其余工具批处理更名的话,要记得在提交前删除老的文件名,再添加新的文件名。