记录每次更新到仓库 —— Git 学习笔记 10

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013490896/article/details/82315773

记录每次更新到仓库


咱们接着很多天以前的 取得Git仓库 这篇文章继续说。

文件的状态

不管是通过哪种方法,现在我们已经有了一个仓库,并从这个仓库中取出了所有文件的拷贝。接下来,对这些文件作些修改,在完成了一个阶段的目标之后,提交本次更新到仓库。

需要说明的是,工作目录下面的所有文件都不外乎这两种状态:已知(已跟踪)的和未知的。已跟踪的文件是指已经被纳入版本控制的文件,未知的文件又分为两种:未跟踪和和已忽略(这个以后再说)。

已跟踪的文件可分为以下几种状态:

  1. 已提交(或未修改)
  2. 已修改
  3. 已暂存

这里写图片描述

初次克隆某个仓库时,工作目录中的所有文件都属于已跟踪文件,且状态为未修改(unmodified)。在编辑过某些文件之后,Git 将这些文件标记为已修改(modified)。然后可以用"git add"命令把这些文件添加到暂存区,这时候它们的状态就是已暂存(staged);再然后用“git commit”把这些暂存的文件提交到本地仓库后,它们的状态又变成了未修改(unmodified)。

这里写图片描述

三个区域

上面是围绕着文件状态变化来说的,也可以围绕着工作目录、暂存区、本地版本库来说明。

Git 系统跟踪的文件一般有 2 种状态,未修改(或已提交)和已修改。未修改意味着工作目录下的文件内容和最近一次提交的修订内容一致,很安全地存放在版本库中;如果工作目录下的文件和最近一次提交的版本存在差异,则被认为是已修改的文件。

不过,在 Git 系统内部,还有一个被称为索引(index)或暂存区(staging area)的区域,它用来存储将要提交的信息。git add 命令用来把已修改的文件加入索引,这将导致 Git 为其生成当前版本的快照。此时这个文件的状态就是已暂存(staged)。
这里写图片描述

检查当前文件状态

要确定哪些文件当前处于什么状态,可以用 git status 命令。如果在克隆仓库之后立即执行此命令,会看到类似这样的输出:

$ git status
On branch master
nothing to commit, working directory clean

这说明你现在的工作目录是干净的。换句话说,所有已跟踪文件在上次提交后都未被修改过。此外,上面的信息还表明,当前目录下没有任何未跟踪的新文件,否则 Git 会在这里列出来。最后,还显示了当前所在的分支是 master,这是默认的分支名称(分支以后再说,这里先不用管)。

现在让我们创建一个新文件 README,内容是什么无所谓,保存后运行 git status

$ 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 不会自动将其纳入跟踪范围,除非你明明白白地告诉它“我需要跟踪该文件”。

跟踪新文件

使用命令 git add 开始跟踪一个新文件。所以,要跟踪 README 文件,只需运行:

$ git add README

此时再运行 git status 命令,会看到:

$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: README

只要在"Changes to be committed"这行下面列出的文件,就说明是已暂存状态。如果此时提交,那么该文件此时此刻的版本将被存入仓库。在 git add 后面可以指明要跟踪的文件或目录。如果是目录,就说明要递归跟踪该目录下的所有文件及子目录。

其实 git add 的潜台词就是把目标文件的快照放入暂存区域,同时未曾跟踪过的文件标记为已跟踪。

取消跟踪(un-tracking)文件

假如想忽略某个已经跟踪的文件,可以用命令

 git rm --cached  <file>

注意,把 <file > 替换成具体的文件名。

举例:

$ git status --ignored
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   world.c

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        oops

可以看到,world.c 是一个已经被跟踪的文件。

$ git rm --cached world.c
rm 'world.c'

$ git status --ignored
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        deleted:    world.c

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        oops
        world.c
$ ls
oops  world.c

当不再跟踪 world.c 后, 工作区中的 world.c 不受影响。

重新跟踪(re-tracking)文件

如果想跟踪一个已经被忽略的文件,可以用

 git add -f <file>

例如:

$ git add -f world.c
$ git status --ignored
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   world.c

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        oops

暂存已修改文件

我们修改一个已跟踪的文件(我这里是 world.c),再运行 git status

$ echo "hello world" >>world.c

$ 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:   world.c

no changes added to commit (use "git add" and/or "git commit -a")

文件 world.c 出现在“Changes not staged for commit”下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。现在让我们运行 git add 将 world.c 放到暂存区,然后再看看 git status 的输出。

注意: git add 命令是个多功能命令,根据目标文件的状态不同,此命令的效果也不同:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态(这个以后再说)等。

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   world.c

现在 world.c 已暂存,下次提交时其快照就会永久储存在仓库。假设此时,你突然想起来world.c还需要再改一下,比如加一行注释。重新编辑保存后,准备提交。不过且慢,在提交之前再运行一遍 git status 看看。

$ echo "//this is comment" >>world.c

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   world.c

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:   world.c

怎么回事?world.c 文件出现了两次!一次已暂存,一次未暂存,是不是 Git 搞错了?

实际上 Git 只不过暂存了你运行 git add 命令时的版本,如果现在提交,那么提交的是添加注释前的版本,而非当前工作目录中的版本。所以,运行了 git add 之后又作了修订的文件,需要重新运行 git add 把最新版本暂存起来

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   world.c

忽略某些文件

对于某些文件,我们不希望把它们纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。通常它们都是些自动生成的文件,比如日志文件、编译过程中创建的临时文件等。我们可以创建一个名为 .gitignore 的文件,在里面列出要忽略的文件模式。来看一个实际的例子:

$ cat .gitignore
*.[oa]
*~

*.[oa]告诉 Git 忽略所有以 .o 或 .a 结尾的文件。*~告诉 Git 忽略所有以波浪符(~)结尾的文件,许多文本编辑软件(比如 Emacs)都用这样的文件名保存副本。

文件 .gitignore 的格式规范如下:
• 所有空行或者以 # 开头的行都会被 Git 忽略。
• 可以使用标准的 glob 模式匹配。
• 匹配模式可以以(/)开头防止递归。
• 匹配模式可以以(/)结尾指定目录。
• 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。

所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。

  1. 星号(*)匹配零个或多个任意字符;
  2. [abc] 匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);
  3. 问号(?)只匹配一个任意字符;
  4. 如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配 (比如 [0-9] 表示匹配所有 0 到 9 的数字)。
  5. 使用两个星号(**) 表示匹配任意中间目录,比如a/**/z 可以匹配 a/z, a/b/z 或 a/b/c/z等。

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

# 忽略.a文件
*.a
# 但是跟踪 lib.a
!lib.a
# 忽略当前目录下的 TODO,但是不忽略子目录下的 TODO
/TODO
# 忽略 build 目录下的所有文件
build/
# 例如忽略 doc/notes.txt, 但是不忽略 doc/server/arch.txt
doc/*.txt
# 忽略 doc 目录下的所有 .pdf 
doc/**/*.pdf

注意:Git 允许在版本库中任何目录下有.gitignore文件。每个 .gitignore 文件都只影响该目录及其所有子目录。

由于本文是针对初学者的,所以说得略简单。如果你想了解关于 Git 忽略文件的更多内容,可以参考我的博文: 忽略某些文件

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

如果 git status 命令的输出对于你来说过于笼统,你想知道具体修改了什么地方,可以用 git diff 命令。git diff 命令可以帮我们回答两个问题:

  1. 有哪些更新未暂存?
  2. 有哪些更新已暂存?

尽管 git status 已经通过在相应栏下列出文件名的方式回答了这2个问题,但是 git diff 将通过文件补丁的格式(合并格式)显示修改了哪些行。

比如我的工作目录下有一个 README.md 文件,里面已经有一些内容了,它的状态是未修改。现在修改它,但是不暂存。

$ echo 1234 >> README.md

运行 git diff 命令

$ git diff
diff --git a/README.md b/README.md
index 26573a9..78ea874 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,2 @@
 my_repo
+1234

可以清楚看到,增加了一行“1234”。要了解合并格式的 diff,可以参考我的博文 diff命令输出格式解读

如果运行

$ git add README.md

也就是把修改提交到暂存区,这时候再次运行 git diff

$ git diff

什么输出都没有,也就说明工作区的更新都已经暂存了。若要查看已暂存的更新,可以用 git diff --cached 命令。(Git 1.6.1 及更高版本还允许使用 git diff --staged,效果是相同的。)

$ git diff --cached
diff --git a/README.md b/README.md
index 26573a9..78ea874 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,2 @@
 my_repo
+1234


$ git diff --staged
diff --git a/README.md b/README.md
index 26573a9..78ea874 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,2 @@
 my_repo
+1234

你也许会问,能不能比较当前工作目录和最近一次提交之间的差异。当然可以,用命令git diff HEAD,这里就不举例了。

下图列出了这三个命令的区别。

这里写图片描述

提交更新

现在的暂存区已经准备妥当可以提交了。 在此之前,请一定要确认还有什么修改过的或新建的文件还没有 git add 过,否则提交的时候不会记录这些还没暂存起来的变化。 这些修改过的文件只保留在本地磁盘。所以,每次准备提交前,先用 git status 查看下,是不是都已暂存起来了,然后再运行提交命令 git commit

$ git commit

这种方式会启动文本编辑器以便输入本次提交的说明。默认会启用 shell 的环境变量 $EDITOR 所指定的软件,一般都是 vim 或 emacs。当然也可以使用 git config --global core.editor 命令设定你喜欢的编辑器。比如

$ git config --global core.editor "emacs"

我是Windows操作系统,用命令 git config --list查了一下,我的配置是

core.editor='d:\PF\Notepad++\notepad++.exe' -multiInst -notabbar -nosession -noPlugin

可见,我用的软件是 notepad++.

当我运行 git commit命令后,编辑器会显示类似下面的文本信息:

这里写图片描述

可以看到,默认的提交消息包含最后一次运行 git status 的输出,放在注释行里,另外开头还有一空行,供我们输入提交说明。退出编辑器时,Git 会丢掉注释行,用输入的信息生成一次提交。

另外,也可以在 git commit 命令后添加 -m 选项,将提交信息与命令放在同一行,如下所示:

$ git commit -m "initialize"
[master 0b6ab8c] initialize
 1 file changed, 1 deletion(-)
 delete mode 100644 hello.c

可以看到,Git 会告诉我们,当前是在哪个分支(master)提交的,本次提交的 SHA-1 校验和是什么(0b6ab8c),以及在本次提交中,有多少文件修订过,多少行添加和删改过。

请记住,提交时写入仓库的是放在暂存区的快照。任何还未暂存的修改仍然在磁盘上,可以在以后暂存并提交。

每一次进行提交操作,都是对项目作一次快照,以后可以退回到这个状态,或者把某两个提交进行比较。

跳过暂存区

尽管使用暂存区可以精心准备每一次提交,但有时候显得麻烦。 Git 提供了一个跳过使用暂存区的方式, 只要在提交的时候,给 git commit 加上 -a 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤:

例如,我修改了一个文件 change_log.md,但是没有暂存

$ 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:   change_log.md

no changes added to commit (use "git add" and/or "git commit -a")

我直接提交:

$ git commit -a -m "commit change log skip the index"
[master 32ee0ac] commit change log skip the index
 1 file changed, 1 insertion(+)

提交成功了。

注意,这种方法仅对已经跟踪的文件有效,如果是 untracked 的文件,是无法提交的。比如

$ touch change_log.md
$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        change_log.md

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

这时候,我试着提交:

$ git commit -a -m "add change log"
On branch master
Untracked files:
        change_log.md

nothing added to commit but untracked files present

你瞧,提交失败了,Git 说没有东西可提交。

删除文件

在 Git中,删除也是一个修改操作。我们先添加一个新文件test.txt到Git并且提交:

$ touch test.txt
$ git add test.txt
$ git commit -m "add test.txt"
[master 92c6e9d] add test.txt
 1 file changed, 1 insertion(+)
 create mode 100644 test.txt

一般情况下,我们会用rm命令删除:

$ rm test.txt

这个时候,Git 知道你删除了该文件。因此,工作区和版本库就不一致了,用git status命令查看一下:

$ 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:    test.txt

no changes added to commit (use "git add" and/or "git commit -a")


现在你有两个选择:

  1. 确实要从版本库中删除该文件
  2. 误删了,想把这个文件找回来

对于1,用命令git rm删掉,并且git commit

$ git rm test.txt
rm 'test.txt'

$ git commit -m "rm test.txt"
[master 6c0b39a] rm test.txt
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 test.txt


现在,文件就从版本库中被删除了。

对于2,可以用git checkout -- <file>把文件从版本库里找回来。例如:

$ git checkout -- test.txt


这里的git checkout其实是用版本库里的版本来更新索引,同时覆盖工作目录中对应的文件,无论工作区的文件是被修改还是被删除,都可以“一键还原”。

其实,如果确实要从版本库删除某个文件,只需要2个步骤。

git rm <file>
git commit

git rm表示从工作区删除文件,并且把这个变更暂存(就像 git add 一样);git commit表示在版本库实现这个变更。

需要说明的是,如果文件修改了,且修改未提交,删除就会失败。例如:

$ echo 11 >>oops
$ 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:   oops

no changes added to commit (use "git add" and/or "git commit -a")

$ git rm oops
error: the following file has local modifications:
    oops
(use --cached to keep the file, or -f to force removal)
# 以上是未暂存的情况



$ git add oops

$ git rm oops
error: the following file has changes staged in the index:
    oops
(use --cached to keep the file, or -f to force removal)
# 以上是暂存了但是未提交的情况

这时候需要加 -f 强行删除。

$ git rm oops -f
rm 'oops'

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        deleted:    oops

$ ls


移动文件

在 Git 中可以执行下面的命令重命名文件:

git mv <old_file> <new_file>

例如:

$ git mv banana.c grape.c

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        renamed:    banana.c -> grape.c


$ git commit -m xx
[master 0c89153] xx
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename banana.c => grape.c (100%)


参考资料

【1】《精通Git(第2版)》,Scott Chacon & Ben Straub,人民邮电出版社

【2】 https://www.liaoxuefeng.com/

猜你喜欢

转载自blog.csdn.net/u013490896/article/details/82315773
今日推荐