-
A1. 附录A:Git在其他环境中的使用
- A1.1 图形界面
- A1.2 Git在Visual Studio中
- A1.3 Git在Visual Studio Code中
- A1.4 Git在IntelliJ/PyCharm/WebStorm/PhpStorm/RubyMine中
- A1.5 Git在Sublime Text中
- A1.6 Git在Bash中
- A1.7 Git在Zsh中
- A1.8 Git在PowerShell中
- A1.9 总结
-
A2. 附录B:将Git嵌入你的应用程序
-
A3. 附录C:Git命令
2.2 Git基础 - 记录仓库中的更改
记录仓库中的更改
此时,您应该在本地机器上拥有一个正式的Git仓库,以及所有文件的一个检出或工作副本。通常,您希望开始进行更改,并在每次项目达到想要记录的状态时将这些更改的快照提交到您的仓库。
请记住,工作目录中的每个文件都可以处于两种状态之一:已跟踪或未跟踪。已跟踪文件是指上次快照中的文件,以及任何新暂存的文件;它们可以是未修改的、已修改的或已暂存的。简而言之,已跟踪文件是Git知道的文件。
未跟踪文件是其他所有内容——工作目录中不在上次快照中且不在暂存区域中的任何文件。当您首次克隆一个仓库时,所有文件都将被跟踪且未修改,因为Git刚刚检出它们,并且您还没有编辑任何内容。
当您编辑文件时,Git 会将其视为已修改,因为自上次提交以来您已对其进行了更改。在您工作时,您可以选择性地暂存这些已修改的文件,然后提交所有这些已暂存的更改,然后循环重复。
检查文件状态
用于确定哪些文件处于哪种状态的主要工具是git status
命令。如果您在克隆后直接运行此命令,您应该会看到类似以下内容
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
这意味着您有一个干净的工作目录;换句话说,您的所有已跟踪文件都没有被修改。Git 也看不到任何未跟踪的文件,否则它们将在此处列出。最后,该命令会告诉您当前位于哪个分支,并告知您它与服务器上相同分支没有发生分歧。目前,该分支始终是master
,这是默认分支;您无需在此处担心它。 Git分支将详细介绍分支和引用。
注意
|
GitHub 在 2020 年年中将默认分支名称从 但是,Git 本身仍然使用 |
假设您向项目中添加了一个新文件,一个简单的README
文件。如果该文件之前不存在,并且您运行git status
,您将看到您的未跟踪文件,如下所示
$ 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)
您可以看到您的新README
文件未被跟踪,因为它位于状态输出中的“未跟踪文件”标题下。未跟踪基本上意味着 Git 看到一个您在之前的快照(提交)中没有的文件,并且尚未暂存;在您明确指示它这样做之前,Git 不会开始将其包含在您的提交快照中。这样做是为了避免您意外地开始包含生成的二进制文件或您不想包含的其他文件。您确实希望开始包含README
,因此让我们开始跟踪该文件。
跟踪新文件
为了开始跟踪新文件,您使用命令git add
。要开始跟踪README
文件,您可以运行以下命令
$ git add README
如果您再次运行状态命令,您会看到您的README
文件现在已跟踪并已暂存以进行提交
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: README
您可以看出它已暂存,因为它位于“要提交的更改”标题下。如果您此时提交,则在您运行git add
时文件的版本将包含在随后的历史快照中。您可能还记得,当您之前运行git init
时,随后运行了git add <files>
——这是为了开始跟踪目录中的文件。git add
命令接受文件或目录的路径名;如果是目录,则该命令会递归地添加该目录中的所有文件。
暂存已修改的文件
让我们更改一个已跟踪的文件。如果您更改了一个名为CONTRIBUTING.md
的先前已跟踪的文件,然后再次运行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: CONTRIBUTING.md
CONTRIBUTING.md
文件出现在名为“未暂存以供提交的更改”的部分下——这意味着已跟踪的文件已在工作目录中修改,但尚未暂存。要暂存它,您需要运行git add
命令。git add
是一个多用途命令——您使用它来开始跟踪新文件,暂存文件,以及执行其他操作,例如将合并冲突文件标记为已解决。将其更多地视为“将此确切内容添加到下一个提交”而不是“将此文件添加到项目”可能会有所帮助。让我们现在运行git add
来暂存CONTRIBUTING.md
文件,然后再次运行git status
$ 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
两个文件都已暂存,并将进入您的下一个提交。此时,假设您记得在提交之前要对CONTRIBUTING.md
进行一个小小的更改。您再次打开它并进行更改,然后您准备提交。但是,让我们再次运行git status
$ 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
怎么回事?现在CONTRIBUTING.md
同时列为已暂存和未暂存。这是怎么回事?事实证明,Git 准确地暂存了您运行git add
命令时的文件。如果您现在提交,则上次运行git add
命令时CONTRIBUTING.md
的版本将进入提交,而不是您运行git commit
时工作目录中文件的外观版本。如果您在运行git add
后修改了文件,则必须再次运行git add
以暂存文件的最新版本
$ 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
简短状态
虽然git status
输出非常全面,但也相当冗长。Git 还具有一个简短状态标志,以便您可以更简洁地查看更改。如果您运行git status -s
或git status --short
,您将从命令中获得更简化的输出
$ git status -s
M README
MM Rakefile
A lib/git.rb
M lib/simplegit.rb
?? LICENSE.txt
未跟踪的新文件旁边带有??
,已添加到暂存区的新文件带有A
,已修改的文件带有M
,依此类推。输出有两列——左列指示暂存区的状态,右列指示工作树的状态。例如,在该输出中,README
文件在工作目录中已修改但尚未暂存,而lib/simplegit.rb
文件已修改并已暂存。Rakefile
已修改、暂存,然后再次修改,因此它既有已暂存的更改也有未暂存的更改。
忽略文件
通常,您会有一类不想让 Git 自动添加或甚至显示为未跟踪的文件。这些通常是自动生成的文件,例如日志文件或构建系统生成的文件。在这种情况下,您可以创建一个列出匹配它们的模式的文件,命名为.gitignore
。这是一个.gitignore
文件示例
$ cat .gitignore
*.[oa]
*~
第一行告诉 Git 忽略以“.o”或“.a”结尾的任何文件——可能是构建代码的产物对象和存档文件。第二行告诉 Git 忽略所有名称以波浪号(~
)结尾的文件,许多文本编辑器(如 Emacs)使用它来标记临时文件。您还可以包含日志、tmp 或 pid 目录;自动生成的文档;等等。在开始使用新仓库之前为其设置.gitignore
文件通常是一个好主意,这样您就不会意外地提交实际上不想包含在 Git 仓库中的文件。
您可以放在.gitignore
文件中的模式规则如下
-
空行或以
#
开头的行将被忽略。 -
标准的 glob 模式有效,并将递归地应用于整个工作树。
-
您可以以正斜杠(
/
)开头模式以避免递归。 -
您可以以正斜杠(
/
)结尾模式以指定目录。 -
您可以通过在模式开头使用感叹号(
!
)来否定模式。
Glob 模式类似于 shell 使用的简化正则表达式。星号(*
)匹配零个或多个字符;[abc]
匹配括号内的任何字符(在本例中为 a、b 或 c);问号(?
)匹配单个字符;括号括起来的字符之间用连字符分隔([0-9]
)匹配它们之间的任何字符(在本例中为 0 到 9)。您还可以使用两个星号来匹配嵌套目录;a/**/z
将匹配a/z
、a/b/z
、a/b/c/z
等。
这是另一个.gitignore
文件示例
# 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
提示
|
如果您想要项目的起点,GitHub 在https://github.com/github/gitignore维护了数十个项目和语言的良好 |
注意
|
在简单的情况下,仓库在其根目录中可能只有一个 本书不涉及多个 |
查看已暂存和未暂存的更改
如果git status
命令对您来说太模糊——您想知道您到底更改了什么,而不仅仅是哪些文件已更改——您可以使用git diff
命令。我们将在后面更详细地介绍git diff
,但您可能最常使用它来回答这两个问题:您更改了什么但尚未暂存?以及您暂存了什么即将提交?虽然git status
通过列出文件名非常笼统地回答了这些问题,但git diff
显示了您添加和删除的确切行——换句话说,就是补丁。
假设您再次编辑并暂存README
文件,然后编辑CONTRIBUTING.md
文件但不暂存它。如果您运行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)
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
要查看您已更改但尚未暂存的内容,请键入不带其他参数的git diff
$ 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
该命令比较工作目录中的内容与暂存区中的内容。结果告诉您您所做的尚未暂存的更改。
如果您想查看已暂存的内容,这些内容将进入您的下一个提交,您可以使用git diff --staged
。此命令将您暂存的更改与上次提交进行比较
$ 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
需要注意的是,git diff
本身不会显示自上次提交以来所做的所有更改——仅显示仍未暂存的更改。如果您已暂存所有更改,则git diff
将不会输出任何内容。
举另一个例子,如果您暂存CONTRIBUTING.md
文件,然后对其进行编辑,您可以使用git diff
查看已暂存的文件中的更改和未暂存的更改。如果我们的环境如下所示
$ 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
现在您可以使用git diff
查看仍未暂存的内容
$ 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
以及git diff --cached
查看您已暂存的内容(--staged
和--cached
是同义词)
$ 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
注意
|
在外部工具中使用 Git Diff
在本书的其余部分,我们将继续以各种方式使用 |
提交更改
现在您的暂存区已按您想要的方式设置,您可以提交更改。请记住,任何仍未暂存的内容——您自编辑以来未运行git add
的任何已创建或修改的文件——都不会进入此提交。它们将作为工作目录中修改的文件保留。在本例中,假设您上次运行git status
时,看到所有内容都已暂存,因此您已准备好提交更改。最简单的提交方法是键入git commit
$ git commit
这样做将启动您选择的编辑器。
注意
|
这由 shell 的 |
编辑器显示以下文本(此示例为 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
# Your branch is up-to-date with 'origin/master'.
#
# Changes to be committed:
# new file: README
# modified: CONTRIBUTING.md
#
~
~
~
".git/COMMIT_EDITMSG" 9L, 283C
您可以看到默认的提交信息包含了git status
命令的最新输出(已注释)以及顶部的一个空行。您可以删除这些注释并键入您的提交信息,或者保留它们以帮助您记住您正在提交的内容。
注意
|
为了更明确地提醒您修改了什么内容,您可以向 |
退出编辑器后,Git 会使用该提交信息(已去除注释和差异)创建您的提交。
或者,您可以使用commit
命令内联键入您的提交信息,方法是在-m
标志后指定它,如下所示
$ 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
现在您已创建了第一个提交!您可以看到提交为您提供了一些关于自身的信息:您提交到的分支(master
),提交的 SHA-1 校验和(463dc4f
),更改的文件数量以及提交中添加和删除的行数统计信息。
请记住,提交记录了您在暂存区中设置的快照。您未暂存的任何内容仍然存在并被修改;您可以进行另一个提交以将其添加到您的历史记录中。每次执行提交时,您都记录了项目的快照,以后可以恢复或比较该快照。
跳过暂存区
尽管暂存区对于精确地制作您想要的提交非常有用,但在您的工作流程中,它有时会比您需要的稍微复杂一些。如果您想跳过暂存区,Git 提供了一个简单的快捷方式。向git commit
命令添加-a
选项会使 Git 在执行提交之前自动暂存所有已跟踪的文件,从而使您可以跳过git add
部分
$ 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 'Add new benchmarks'
[master 83e38c7] Add new benchmarks
1 file changed, 5 insertions(+), 0 deletions(-)
请注意,在这种情况下,您不必在提交前对CONTRIBUTING.md
文件运行git add
。这是因为-a
标志包含所有已更改的文件。这很方便,但要小心;有时此标志会导致您包含不需要的更改。
删除文件
要从 Git 中删除文件,您必须将其从跟踪的文件中删除(更准确地说,将其从暂存区中删除),然后提交。git rm
命令执行此操作,还会从您的工作目录中删除文件,因此下次您不会将其视为未跟踪的文件。
如果您只是从工作目录中删除文件,它会显示在git status
输出的“未暂存以提交的更改”(即未暂存)区域中
$ 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")
然后,如果您运行git rm
,它将暂存文件的删除操作
$ 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
下次您提交时,该文件将消失且不再被跟踪。如果您修改了文件或已将其添加到暂存区,则必须使用-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 将其视为已重命名的文件
$ 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
但是,这等效于运行以下命令
$ mv README.md README
$ git rm README.md
$ git add README
Git 会隐式地确定这是一个重命名操作,因此您是否以这种方式或使用mv
命令重命名文件都没有关系。唯一的真正区别是git mv
是一个命令而不是三个命令——它是一个便利函数。更重要的是,您可以使用任何您喜欢的工具来重命名文件,并在提交之前处理add
/rm
。