此时,您应该在本地计算机上拥有一个真正的 Git存储库,并在您面前拥有其所有文件的签出或工作副本。通常,每当项目达到要记录的状态时,您就需要开始进行更改并将这些更改的快照提交到存储库中。
请记住,工作目录中的每个文件都可以处于以下两种状态之一:已跟踪或未跟踪。跟踪的文件是上一个快照中的文件;它们可以不修改,修改或上演。简而言之,跟踪文件是Git知道的文件。
未跟踪的文件就是所有其他内容-工作目录中不在上一个快照中且不在暂存区中的所有文件。第一次克隆存储库时,所有文件都会被跟踪和未修改,因为Git只是将它们签出,并且您还没有进行任何编辑。
在编辑文件时,Git会将其视为已修改,因为自上次提交以来已对其进行了更改。在工作时,您有选择地暂存这些已修改的文件,然后提交所有已暂存的更改,然后重复该循环。
图8.文件状态的生命周期。
检查文件状态
用于确定git status命令处于哪个状态的主要工具。如果在克隆后直接运行此命令,则应看到类似以下内容的内容:
<span style="color:#333333"><code class="language-console">$ git status On branch master Your branch is up-to-date with 'origin/master'. nothing to commit, working directory clean</code></span>这意味着您有一个干净的工作目录。换句话说,没有任何跟踪文件被修改。Git也看不到任何未跟踪的文件,否则它们将在此处列出。最后,该命令会告诉您您所在的分支,并告知您该分支尚未与服务器上的同一分支分开。现在,该分支始终是“ master”,这是默认设置。您在这里不必担心。 Git分支将详细介绍分支和参考。
假设您向项目中添加了一个新文件,即一个简单README文件。如果该文件以前不存在,然后运行git status,则会看到未跟踪的文件,如下所示:
<span style="color:#333333"><code class="language-console">$ echo 'My Project' > README $ 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)</code></span>您可以看到新README文件处于未跟踪状态,因为它位于状态输出中“未跟踪的文件”标题下。基本上,未跟踪意味着Git会看到您在上一个快照中没有的文件(提交);在您明确要求Git之前,Git不会开始将其包含在提交快照中。这样做是为了确保您不会意外地包括生成的二进制文件或其他您不想包含的文件。您确实想开始包含README,因此让我们开始跟踪文件。
跟踪新文件
为了开始跟踪新文件,请使用命令git add。要开始跟踪README文件,可以运行以下命令:
$ git add README如果再次运行status命令,则可以看到README现在已跟踪文件并准备将其提交:
<span style="color:#333333"><code class="language-console">$ 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</code></span>您可以说它是分阶段的,因为它在“要更改的更改”标题下。如果此时提交,则在运行时文件的版本git add就是后续历史快照中的版本。您可能还记得,当您git init早先运行时,然后又运行了git add <files> —那是为了开始跟踪目录中的文件。该git add命令采用文件或目录的路径名;如果是目录,则该命令以递归方式添加该目录中的所有文件。
让我们更改一个已经被跟踪的文件。如果更改了以前跟踪的文件CONTRIBUTING.md,然后git status再次运行命令,则会得到如下所示的内容:
<span style="color:#333333"><code class="language-console">$ 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: CONTRIBUTING.md</code></span>该CONTRIBUTING.md文件将出现在名为“未暂存的更改未提交”的部分下-这意味着已跟踪的文件已在工作目录中被修改但尚未暂存。要登台,请运行git add命令。 git add是一个多用途命令-您可以使用它来开始跟踪新文件,暂存文件以及执行其他操作,例如将合并冲突的文件标记为已解决。将其更多地视为“将此内容精确地添加到下一个提交”而不是“将该文件添加到项目中”可能会有所帮助。git add现在运行以暂存CONTRIBUTING.md文件,然后git status再次运行:
<span style="color:#333333"><code class="language-console">$ git add CONTRIBUTING.md $ 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 modified: CONTRIBUTING.md</code></span>这两个文件都已暂存,将进入下一个提交。此时,假设您记得在CONTRIBUTING.md提交之前要进行的一项小更改。您再次打开它并进行更改,就可以提交了。但是,让我们再运行git status一次:
<span style="color:#333333"><code class="language-console">$ vim CONTRIBUTING.md $ 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 modified: CONTRIBUTING.md 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: CONTRIBUTING.md</code></span>有没有搞错?现在CONTRIBUTING.md被列为已暂存和未暂存。那怎么可能?事实证明,Git与运行git add命令时的阶段完全相同。如果现在提交,则CONTRIBUTING.md上次运行git add命令时的版本是该命令如何进入提交的方式,而不是运行时在工作目录中显示的文件的版本git commit。如果在运行后修改文件git add,则必须git add再次运行以暂存文件的最新版本:
<span style="color:#333333"><code class="language-console">$ git add CONTRIBUTING.md $ 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 modified: CONTRIBUTING.md</code></span>虽然git status输出非常全面,但也很罗word。Git还具有简短的状态标志,因此您可以以更紧凑的方式查看更改。如果运行,git status -s或者git status --short从命令中获得了更为简化的输出:
<span style="color:#333333"><code class="language-console">$ git status -s M README MM Rakefile A lib/git.rb M lib/simplegit.rb ?? LICENSE.txt</code></span>未跟踪的??新文件旁边有一个,已添加到暂存区域的新文件有一个A,已修改的文件有一个M,依此类推。输出有两列-左列指示临时区域的状态,右列指示工作树的状态。因此,例如在该输出中,README文件已在工作目录中修改但尚未暂存,而lib/simplegit.rb文件已修改并暂存。已对其Rakefile进行了修改,分段和再次修改,因此对它所做的更改既可以分段也可以分段。
通常,您会拥有一类文件,这些文件是您不希望Git自动添加甚至不显示给您的文件。这些通常是自动生成的文件,例如日志文件或构建系统生成的文件。在这种情况下,您可以创建文件列表模式以匹配名为的文件.gitignore。这是一个示例.gitignore文件:
<span style="color:#333333"><code class="language-console">$ cat .gitignore *.[oa] *~</code></span>第一行告诉Git忽略任何以“ .o”或“ .a”结尾的文件-对象和归档文件,它们可能是构建代码的产物。第二行告诉Git忽略名称以波浪号(~)结尾的所有文件,许多文本编辑器(例如Emacs)使用该文件来标记临时文件。您可能还包括一个log,tmp或pid目录。自动生成的文档;等等。.gitignore在开始之前为新存储库设置文件通常是一个好主意,因此您不会意外提交您确实不希望在Git存储库中使用的文件。
可以放入.gitignore文件中的模式的规则如下:
空行或以开头的行将#被忽略。
标准的glob模式起作用,并将在整个工作树中递归地应用。
您可以使用正斜杠(/)开头的样式,以避免递归。
您可以使用正斜杠(/)结束模式以指定目录。
您可以通过以感叹号(!)开头来否定模式。
球形模式类似于shell使用的简化正则表达式。星号(*)匹配零个或多个字符;[abc]匹配括号内的任何字符(在本例中为a,b或c);问号(?)与单个字符匹配;和用连字符([0-9])分隔的括号匹配它们之间的任何字符(在本例中为0到9)。您也可以使用两个星号来匹配嵌套目录。a/**/z将匹配a/z,a/b/z,a/b/c/z,等等。
这是另一个示例.gitignore文件:
<span style="color:#333333"><code># ignore all .a files *.a # but do track lib.a, even though you're ignoring .a files above !lib.a # only ignore the TODO file in the current directory, not subdir/TODO /TODO # ignore all files in any directory named build build/ # ignore doc/notes.txt, but not doc/server/arch.txt doc/*.txt # ignore all .pdf files in the doc/ directory and any of its subdirectories doc/**/*.pdf</code></span>小费
如果您想要项目的起点,GitHub会.gitignore在https://github.com/github/gitignore上为数十个项目和语言维护一个相当全面的优秀文件示例列表。
注意
在简单的情况下,存储库.gitignore的根目录中可能只有一个文件,该文件递归地应用于整个存储库。但是,也可能.gitignore在子目录中有其他文件。这些嵌套.gitignore文件中的规则仅适用于它们所在目录下的文件。(Linux内核源存储库有206个.gitignore文件。)
深入探讨多个.gitignore文件的细节超出了本书的范围。man gitignore有关详细信息,请参见。
如果该git status命令对您来说太含糊-您想确切地知道您所做的更改,而不仅仅是更改了哪些文件,则可以使用该git diff命令。稍后我们将git diff详细介绍,但您可能最常使用它来回答以下两个问题:您做了哪些更改但尚未上演?您打算上演什么?尽管git status通常通过列出文件名来回答这些问题,但仍会git diff向您显示添加和删除的确切行(即补丁程序)。
假设您README再次编辑并CONTRIBUTING.md暂存文件,然后在不暂存的情况下编辑文件。如果您运行git status命令,您将再次看到以下内容:
<span style="color:#333333"><code class="language-console">$ 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) modified: 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: CONTRIBUTING.md</code></span>要查看已更改但尚未上演的内容,请键入git diff其他参数:
<span style="color:#333333"><code class="language-console">$ git diff diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8ebb991..643e24f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,7 +65,8 @@ branch directly, things can get messy. Please include a nice description of your changes when you submit your PR; if we have to read the whole diff to figure out why you're contributing in the first place, you're less likely to get feedback and have your change -merged in. +merged in. Also, split your changes into comprehensive chunks if your patch is +longer than a dozen lines. If you are starting to work on a particular area, feel free to submit a PR that highlights your work in progress (and note in the PR title that it's</code></span>该命令将工作目录中的内容与暂存区中的内容进行比较。结果告诉您尚未上演的更改。
如果您想查看下一次提交的阶段,可以使用git diff --staged。此命令将您分阶段的更改与上一次提交进行比较:
<span style="color:#333333"><code class="language-console">$ git diff --staged diff --git a/README b/README new file mode 100644 index 0000000..03902a1 --- /dev/null +++ b/README @@ -0,0 +1 @@ +My Project</code></span>重要的是要注意,git diff它本身不会显示自上次提交以来所做的所有更改-仅显示仍未暂存的更改。如果您上演了所有更改,git diff则将不输出任何信息。
再举一个例子,如果您暂存CONTRIBUTING.md文件然后对其进行编辑,则可以git diff用来查看文件中暂存的更改和未暂存的更改。如果我们的环境如下所示:
<span style="color:#333333"><code class="language-console">$ git add CONTRIBUTING.md $ echo '# test line' >> CONTRIBUTING.md $ 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) modified: CONTRIBUTING.md 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: CONTRIBUTING.md</code></span>现在,您可以使用git diff来查看尚未暂存的内容:
<span style="color:#333333"><code class="language-console">$ git diff diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 643e24f..87f08c8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -119,3 +119,4 @@ at the ## Starter Projects See our [projects list](https://github.com/libgit2/libgit2/blob/development/PROJECTS.md). +# test line</code></span>并git diff --cached查看您到目前为止已经上演了什么(--staged并且--cached是同义词):
<span style="color:#333333"><code class="language-console">$ git diff --cached diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8ebb991..643e24f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,7 +65,8 @@ branch directly, things can get messy. Please include a nice description of your changes when you submit your PR; if we have to read the whole diff to figure out why you're contributing in the first place, you're less likely to get feedback and have your change -merged in. +merged in. Also, split your changes into comprehensive chunks if your patch is +longer than a dozen lines. If you are starting to work on a particular area, feel free to submit a PR that highlights your work in progress (and note in the PR title that it's</code></span>注意
外部工具中的Git Diff
git diff在本书的其余部分中,我们将继续以各种方式使用该命令。如果您更喜欢图形或外部差异查看程序,则有另一种查看这些差异的方法。如果您运行git difftool而不是git diff,则可以在软件中查看任何这些差异,例如emerge,vimdiff以及更多(包括商业产品)。运行git difftool --tool-help以查看系统上可用的功能。
现在您已经按照需要的方式设置了暂存区,您可以提交更改。请记住,所有尚未暂存的内容(自创建git add以来从未运行过的所有已创建或修改的文件)都不会进入此提交。它们将作为修改后的文件保留在您的磁盘上。在这种情况下,假设您上次运行时git status,您看到一切都已上演,那么您就可以提交更改了。提交的最简单方法是键入git commit:
$ git commit这样做将启动您选择的编辑器。(这是由shell的EDITOR环境变量设置的,通常是vim或emacs,尽管您可以使用入门中git config --global core.editor看到的命令使用所需的任何内容对其进行配置)。
编辑器显示以下文本(此示例是Vim屏幕):
<span style="color:#333333"><code># Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # Your branch is up-to-date with 'origin/master'. # # Changes to be committed: # new file: README # modified: CONTRIBUTING.md # ~ ~ ~ ".git/COMMIT_EDITMSG" 9L, 283C</code></span>您可以看到默认的提交消息包含已git status注释掉的命令的最新输出,并在顶部有一个空行。您可以删除这些注释并输入提交消息,也可以将它们留在此处以帮助您记住要提交的内容。(为更明确地提醒您已修改的内容,您可以将-v选项传递给git commit。这样做还可以将更改的差异放入编辑器中,以便您可以确切地看到要提交的更改。)退出时编辑器,Git使用该提交消息(删除了注释和差异)创建您的提交。
另外,您可以commit通过在-m标志后指定它来在命令中内联键入提交消息,如下所示:
<span style="color:#333333"><code class="language-console">$ git commit -m "Story 182: Fix benchmarks for speed" [master 463dc4f] Story 182: Fix benchmarks for speed 2 files changed, 2 insertions(+) create mode 100644 README</code></span>现在,您已经创建了第一次提交!您可以看到提交为您提供了一些有关其自身的输出:提交给(master)的分支,提交具有的SHA-1校验和(463dc4f),更改了多少文件以及有关提交中添加和删除的行的统计信息。
请记住,提交记录了您在临时区域中设置的快照。您没有上演的所有内容仍坐在那里进行修改;您可以再次提交以将其添加到您的历史记录中。每次执行提交时,都会记录项目的快照,以后可以还原或比较该快照。
尽管它对于完全按照所需的方式制作提交很有用,但临时区域有时比工作流程中所需的要复杂一些。如果要跳过暂存区域,Git提供了一个简单的快捷方式。-a在git commit命令中添加选项后,Git会在提交之前自动暂存已跟踪的每个文件,从而使您可以跳过该git add部分:
<span style="color:#333333"><code class="language-console">$ git status On branch master Your branch is up-to-date with 'origin/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: CONTRIBUTING.md 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 file changed, 5 insertions(+), 0 deletions(-)</code></span>注意,在这种情况下,提交前不必git add在CONTRIBUTING.md文件上运行。这是因为该-a标志包括所有更改的文件。这很方便,但是要小心;有时,此标志将导致您包括不需要的更改。
要从Git中删除文件,您必须将其从跟踪文件中删除(更准确地说,将其从暂存区中删除),然后提交。该git rm命令会执行此操作,并且还会从工作目录中删除该文件,因此下次您不会将其视为未跟踪的文件。
如果你只是删除您的工作目录中的文件,它显示了在“不改变上演承诺”(即未分级)您的区域git status输出:
<span style="color:#333333"><code class="language-console">$ rm PROJECTS.md $ git status On branch master Your branch is up-to-date with 'origin/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: PROJECTS.md no changes added to commit (use "git add" and/or "git commit -a")</code></span>然后,如果运行git rm,它将分阶段删除文件:
<span style="color:#333333"><code class="language-console">$ git rm PROJECTS.md rm 'PROJECTS.md' $ 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) deleted: PROJECTS.md</code></span>下次提交时,文件将消失并且不再被跟踪。如果您修改了文件或已经将其添加到临时区域,则必须使用该-f选项强制删除。这是一项安全功能,可防止意外删除尚未记录在快照中且无法从Git恢复的数据。
您可能想要做的另一件事是将文件保留在工作树中,但将其从暂存区中删除。换句话说,您可能希望将文件保留在硬盘上,但不再需要Git对其进行跟踪。如果您忘记向.gitignore文件中添加某些内容并意外地将其暂存,例如大型日志文件或一堆已.a编译的文件,这将特别有用。为此,请使用以下--cached选项:
$ git rm --cached README您可以将文件,目录和文件全局模式传递给git rm命令。这意味着您可以执行以下操作:
$ git rm log/\*.log注意\。前面的反斜杠()*。这是必要的,因为Git会执行自己的文件名扩展,除了扩展Shell的文件名。此命令将删除目录中所有具有.log扩展名的log/文件。或者,您可以执行以下操作:
$ git rm \*~此命令将删除所有名称以~。结尾的文件。
与许多其他VCS系统不同,Git不会明确跟踪文件移动。如果您在Git中重命名文件,则Git中不会存储任何元数据来告诉您您已重命名该文件。但是,Git在事后弄清楚这一点很聪明–我们稍后将处理检测文件移动的问题。
因此,Git有一个mv命令有点令人困惑。如果要在Git中重命名文件,可以运行以下命令:
$ git mv file_from file_to而且效果很好。实际上,如果您运行类似的命令并查看状态,您会发现Git认为它是重命名的文件:
<span style="color:#333333"><code class="language-console">$ git mv README.md 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) renamed: README.md -> README</code></span>但是,这等效于运行如下代码:
<span style="color:#333333"><code class="language-console">$ mv README.md README $ git rm README.md $ git add README</code></span>Git指出这是隐式的重命名,因此,以这种方式或使用mv命令重命名文件都没有关系。唯一真正的区别是git mv一个命令而不是三个命令—这是一个便捷功能。更重要的是,您可以使用任何喜欢的工具来重命名文件,并在提交前稍后解决添加/ rm问题。