Git从入门到精通-第四章-更新仓库

目录

检查当前文件状态 

追踪新文件

暂存已修改的文件(git add作用总结)

简化输出状态

忽略文件

查看已暂存和未暂存的修改 git diff

提交更新 git commit

跳过使用暂存区

前言:在工作区和Git仓库间设置暂存区的意义

1. ​精准控制提交内容(颗粒度更细)​

2. ​避免误提交​

3. ​多阶段修改的拆分与合并​

4. ​高效构建提交对象​

5. ​与工作流工具集成​

git commit -a操作并没有跳过暂存区

移除文件

移动文件


注:本文以proGit图文为基础

现在我们的计算机上有一个真实项目的Git仓库,并从这个仓库中检出了所有文件的工作副本。通常,你会对这些文件做修改,每完成一个阶段的目标,想要记录下它时,就将他提交到仓库

注意⚠️:在工作目录下的每一个文件就这两种状态: 已追踪未追踪 。已追踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后,它们的状态可能是未修改,已修改或已放入暂存区。简而言之,已追踪的文件就是Git已经知道的文件

工作目录中除已追踪文件外的其他所有文件,它们既不存在于上次快照的记录中,也没有被放入暂存区。 初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态,因为它们是刚从Git仓库中提出的

在编辑过某些文件后,由于自上次提交后你对它们做了修改,Git将它们标记为已修改文件。在工作时,你可以选择性地将这些修改过的文件放入暂存区,然后提交所有已暂存的修改,如此往复

Git从入门到精通-第四章-更新仓库_第1张图片

检查当前文件状态 

可以用 git status 命令查看哪些文件处于什么状态。如果在克隆仓库后立刻使用此命令,会看到类似这样的输出:

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

这说明现在的工作目录相当干净 clean 。换句话说,所有已追踪的文件在上次提交后都未被更改过,此外,上面信息还表明当前目录下没有出现任何处于未跟踪的新文件,否则Git会列出来。最后,该命令还显示了当前所在分支 'origin/master' ,这是默认的分支名,并告诉你这个分支痛远程服务器上对应的分支没有偏离(我们在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 ..." to include in what will be committed)
  
    README

nothing added to commit but untracked files present (use "git add" to
track)

可以看到 Untracked files 下出现了 README 。未追踪的文件意味着Git在之前的快照(提交)中没有这些文件;Git不会自动将之纳入追踪范围,除非你明明白白地告诉它“我需要追踪该文件”,这样可以让你不必担心产生的二进制文件或者不想被追踪的文件被包含进来

追踪新文件

使用命令 git add 开始追踪一个文件,在这个例子中我们想要追踪名为 README 的文件,那么运行

git add README

这时我们再运行 git status 命令会看到 README 文件已被追踪,并处于暂存 Changes to be committed状态:

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git restore --staged ..." to unstage)

    new file: README

如果此时提交,那么该文件在你运行 git add 时的版本被留存在后续的历史记录中。 之前我们使用 git init 后就运行了 git add 的命令,开始追踪当前目录下的文件。

如果 git add 使用文件或目录的路径作为参数,那么会开始递归地追踪该目录下的所有文件

暂存已修改的文件(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 ..." to unstage)

    new file: README

Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working
directory)
  
    modified: CONTRIBUTING.md

可以看到,CONTRIBUTING.md 文件和 README 在 git add 之前不一样,README是新文件,未暂存时位于Untracked,而我们在原有文件更改后是位于在 Changes not staged for commit下,而且它不是 new file ,而是 modified ,说明已追踪文件的内容发生了变化,但还没有放到暂存区。要暂存这次更新,需要运行 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 ..." to unstage)
  
    new file: README
    modified: CONTRIBUTING.md

可以看到现在两个文件都在committed中暂存了,下次提交时就会一并记录到仓库中

假如现在我们第二次更改了 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 ..." to unstage)
  
    new file: README
    modified: CONTRIBUTING.md

Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." to discard changes in working
directory)
  
    modified: CONTRIBUTING.md

可以看到两个 CONTRIBUTING.md ,这说明再次更改后我们想当于产生了一个新版本的CONTRIBUTING.md,它会和前面已经暂存的旧版本的 CONTRIBUTING.md 区分,所以我们需要再次 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 ..." to unstage)
  
    new file: README
    modified: CONTRIBUTING.md

简化输出状态

我们可以用 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 的文件,列出要忽略的文件,下面给个例子

$ 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 的数)。 使用星号**配任意中间目a/**/z 可以匹配 a/z a/b/z a/b/c/z

再看一个 .gitignore 文件的例子:

# 忽略所有的 .a 文件
*.a

# 但跟踪所有的 lib.a,即便你在前面忽略了 .a 文件
!lib.a

# 只忽略当前目录下的 TODO 文件,而不忽略 subdir/TODO
/TODO

# 忽略任何目录下名为 build 的文件夹
build/

# 忽略 doc/notes.txt,但不忽略 doc/server/arch.txt
doc/*.txt

# 忽略 doc/ 目录及其所有子目录下的 .pdf 文件
doc/**/*.pdf

GitHub 有一个对数种项目及语言.gitignore 文件可以在https://github.com/github/gitignore 到它。

在最简单的情下,一个仓库可能只下有一个 .gitignore 文件,它递归用到整个仓库中。 然下也可以有额外.gitignore 文件中的 .gitignore文件中的只作用于它所在的目中。 (Linux 核的源码库拥206 .gitignore 文件。)

查看已暂存和未暂存的修改 git diff

git status 命令的出对于来说过于而你想改了什么地方,可以用 git diff 命令。 稍后我们会介绍 git diff常可能用它来回答个问题:当前些更新暂存? 有哪些更新已暂存次提交虽然 git status 已经过在相文件名的方式回答了这个问题,但 git diff 文件补丁式更加具了改变。

再次修README 文件暂存,然后编辑 CONTRIBUTING.md 文件暂存运行 status 命令将会看到

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD ..." to unstage)
  
    modified: README

Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." 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 --satged 命令,这条命令将对比已暂存文件与最后一次提交的文件差异:

$ 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 status 查看被暂存改或未被暂存修改。 如果终端出)起来如下

$ 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 ..." to unstage)
  
    modified: CONTRIBUTING.md

Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." 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 --cashed 查看已经暂存起来的变化--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 文件异,也可以使用图形化的工具或部 diff 工具来较差异。 可以使用 git difftool 命令来emerge vimdiff 软件(包括商业软件diff 结果。 使用 git difftool --tool-help 命令来看你系统支持哪Git Diff

提交更新 git commit

现在我们的暂存区已经准备就绪,可以提交了,在此之前,请务必确认还有什么已修改或者新建的文件还没有 git add 否则这些变化无法包含在这次提交中,只会留在本地磁盘。所以每次提交前先 git status 看下,然后再

git commit

这样会启动你选择的文本编辑器输入提交说明

动的编辑Shell EDITOR 定的,一vim emacs。 当然也可以按照 第一章 介绍的方式, 使用 git config --global core.editor 命令你喜欢的编辑器

编辑器会显示类似下面的文本信息

# 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 的输出,放在注释行里,另外还有一个,供你输入提交说可以去这些,不过也没关系,多少能帮你回想起这更新的容有哪些。

可以用 -v 选项查看,这会将你所作的更改的 diff 出呈现在编辑器中,以便让次提交作出改。

退编辑时,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 设计中的暂存区(Staging Area,或称为索引 Index)是为了提供更灵活、可控的代码管理机制。虽然它增加了一个步骤,但带来了以下核心优势:


1. ​精准控制提交内容(颗粒度更细)
  • 问题​:直接提交工作区改动时,所有未保存的修改会被一次性提交,可能导致无关代码混入提交记录。
  • 解决方案​:暂存区允许你选择性地添加文件(或代码块)到提交中。例如:
    git add file1.txt       # 添加某个文件
    git add -p file2.txt    # 交互式选择文件中的部分代码块
  • 结果​:提交历史更清晰,每个提交仅包含逻辑相关的改动。

2. ​避免误提交
  • 问题​:工作区可能包含调试代码、临时文件或未完成的修改,直接提交可能污染仓库。
  • 解决方案​:暂存区作为“缓冲区”,需显式添加改动,用户可通过 git status 检查确认后再提交。
  • 结果​:减少意外提交无用文件的风险(如 .log.tmp 等)。

3. ​多阶段修改的拆分与合并
  • 场景​:若同时修改了功能 A 和 B,但需要分两次提交。
  • 操作​:
    git add featureA/         # 暂存功能 A
    git commit -m "Add feature A"
    git add featureB/         # 暂存功能 B
    git commit -m "Add feature B"
  • 结果​:无需频繁切换代码状态即可拆分逻辑独立的修改。

4. ​高效构建提交对象
  • 技术细节​:Git 的提交是基于暂存区的快照,而非直接对比工作区。暂存区记录文件的元数据(如 SHA-1、时间戳),使得提交时只需打包已暂存的内容,无需全量扫描工作区。
  • 优势​:提交操作更快,尤其适合大型项目。

5. ​与工作流工具集成
  • 高级用法​:暂存区支持与 git stash(临时保存未提交的改动)、git checkout -- (撤销未暂存的修改)等命令协作,提供更灵活的工作流。

为什么不能绕过暂存区?

  • Git 提供了 git commit -a 选项,可以直接提交所有已跟踪文件的修改,相当于自动执行 git add -u + git commit。但这依然依赖暂存区的机制,且无法处理未跟踪的新文件或部分代码块。

总结

暂存区是 Git 强大功能的核心设计之一,它通过分离 ​工作区​ → ​暂存区​ → ​仓库​ 的流程,赋予开发者精细控制提交内容的能力。虽然初期可能感觉繁琐,但熟悉后能显著提升代码管理的效率和代码库的可维护性。

git commit -a操作并没有跳过暂存区

虽然proGit中写了“Git 提供了一个过使用暂存区域的方式”,但是我个人认为并没有跳过暂存区,而是自动将已追踪文件的修改添加到暂存区后再提交(省略 git add 操作)

git commit -a 想当于下面的两句话

git add -u       # 将所有已跟踪(tracked)文件的修改添加到暂存区(不包含新文件)
git commit       # 提交暂存区的内容

假设之前已经有已经在暂存区的文件,在git commit -a后会随着新的添加到暂存区的文件一起提交

总结一下 git commit -a 的作用:

  1. 自动将工作区中所有已跟踪文件的最新修改添加到暂存区​(相当于执行 git add -u
  2. 提交暂存区中所有内容​(包括之前手动添加的 + 本次自动添加的)

除了未追踪的文件都会被提交

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add ..." to update what will be committed)
  (use "git checkout -- ..." 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(-)

到了提交之前不git add 文件“CONTRIBUTING.md”了。 这是因为 -a 选项使本次提交了所有修改过的文件。 这很方便,但是要小心,有时这个选项会将要的文件加到提交中。

移除文件

Git 文件,就必须文件地说,是暂存区域),然后提交。可以用 git rm 命令完工作,并工作目定的文件,这样以后就不出现在未文件清单中了。

只是简单工作目文件运行 git status 时就“Changes not staged for commit” 部分(也就是 未暂存清单

$ 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 ..." to update what will be committed)
  (use "git checkout -- ..." 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 ..." to unstage)
  
    deleted: PROJECTS.md

下一次提交时,该文件就不版本管了。 如之前改过或已经暂存区的文件则必须使用强制选项 -fforce 首字母)。 这是一种安全特性,用于防止加到快的数,这 样的数据不能Git 恢复

是,我们想文件从 Git 仓库亦即暂存区域),但希望在当前工作目录中。 换句话说,你想文件,但是并不Git 继续。 当.gitignore 文件,不小 心把一个很大的日文件或一.a 这样的生成文件加到暂存区时,这一做法其有用。 为达到这一目的,使用 --cached 选项

git rm --cashed README

git rm 命令后面可以文件或者目的名,也可以使用 glob 式。

git rm log/\*.log

注意星号 * 之前的斜杠 \, 因为 Git 有它自文件方式,所以我不用 shell 来帮展开。此命令删log/ 展名为 .log 的所有文件类似

git rm \*~

该命令会删除所有名字以 ~ 结尾的文件

移动文件

不像其它的 VCS 系统Git 并不文件作。 如Git 中重命名了文件仓库存储数据并不现出这是一改名作。 不过 Git 非常,它究竟什么于具是如何做到的,我们

然如此,当你看Git mv 命令时一定困惑不已。 要在 Git 中对文件改名,可以这么做

git mv file_from file_to

预期般常工作。 上,便此时查看状态,也明白于重命名作的说明:

$ 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 ..." to unstage)
  
    renamed: README.md -> README

其实运行了 git mv 相当于运行了下面三条命令:

mv README.md README
git rm README.md
git add README

如此作,Git 会意到这是一重命名,所以不管何种方式结果都一样。 一的区在于,git mv 是一命令三条命令,直接使用 git mv 便得多。 不过在使用其工具重命名文件时,得在提交前 git rm 除旧文件名,git add 加新文件名。

你可能感兴趣的:(工具,git)