现在,让我们在项目下创建一个新的 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)
$ 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)newfile:README
只要在 Changes to be committed 这行下面的,就说明是已暂存状态。如果此时提交,那么该文件在运行 git add 时的版本将被留存在后续的历史记录中,可能会想起之前我们使用 git init 后就运行了 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 <file>..." to unstage)newfile:READMEChanges 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 出现在 Changes not staged for commit 这行下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。要暂存这次更新,需要运行 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)newfile: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)newfile: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
$ 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)newfile:README
modified:CONTRIBUTING.md
⑤ 状态简览
git status 命令的输出十分详细,但其用语有些繁琐。Git 有一个选项可以帮你缩短状态命令的输出,这样可以以简洁的方式查看更改。如果使用 git status -s 命令或 git status --short 命令,将得到一种格式更为紧凑的输出:
$ git status -s
M READMEMMRakefile
A lib/git.rb
M lib/simplegit.rb
??LICENSE.txt
新添加的未跟踪文件前面有 ?? 标记,新添加到暂存区中的文件前面有 A 标记,修改过的文件前面有 M 标记。输出中有两栏,左栏指明了暂存区的状态,右栏指明了工作区的状态。例如,上面的状态报告显示: README 文件在工作区已修改但尚未暂存,而 lib/simplegit.rb 文件已修改且已暂存,Rakefile 文件已修,暂存后又作了修改,因此该文件的修改中既有已暂存的部分,又有未暂存的部分。
如果 git status 命令的输出过于简略,而我们想知道具体修改了什么地方,可以用 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 <file>..." to unstage)
modified:READMEChanges 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
像之前说的,暂存 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 <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
## StarterProjectsSee our [projects list](https://github.com/libgit2/libgit2/blob/development/PROJECTS.md).+# test line
$ 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 add 过,否则提交的时候不会记录这些尚未暂存的变化。这些已修改但未暂存的文件只会保留在本地磁盘。因此,每次准备提交前,先用 git status 看下,所需要的文件是不是都已暂存起来了, 然后再运行提交命令 git commit,这样会启动选择的文本编辑器来输入提交说明:
$ git commit
编辑器会显示类似下面的文本信息(本例选用 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:
# newfile:README
# modified:CONTRIBUTING.md
#
~~~".git/COMMIT_EDITMSG" 9L, 283C
可以看到,默认的提交消息包含最后一次运行 git status 的输出,放在注释行里,另外开头还有一个空行,供输入提交说明,完全可以去掉这些注释行,不过留着也没关系,多少能帮你回想起这次更新的内容有哪些。
$ 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 newbenchmarks1 file changed,5insertions(+),0deletions(-)
提交之前不再需要 git add 文件“CONTRIBUTING.md”,这是因为 -a 选项使本次提交包含了所有修改过的文件。这很方便,但是要注意,有时这个选项会将不需要的文件添加到提交中。
如果只是简单地从工作目录中手工删除文件,运行 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 <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(译注:即 force 的首字母),这是一种安全特性,用于防止误删尚未添加到快照的数据,这样的数据不能被 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
$ git log
commit ca82a6dff817ec66f44342007202690a93763949
Author:ScottChacon<kody@gee-mail.com>Date:MonMar1721:52:112021-0700
changed the version number
commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author:ScottChacon<kody@gee-mail.com>Date:SatMar1516:40:332021-0700
removed unnecessary test
commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author:ScottChacon<kody@gee-mail.com>Date:SatMar1510:31:282021-0700first commit
$ git log --pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 changed the version number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test
a11bef06a3f659402fe7563abf99ad00de2209e6 first commit
最有意思的是 format ,可以定制记录的显示格式,这样的输出对后期提取分析格外有用——因为知道输出的格式不会随着 Git 的更新而发生改变:
$ git log --pretty=format:"%h - %an, %ar : %s"
ca82a6d -ScottChacon,6 years ago : changed the version number
085bb3b -ScottChacon,6 years ago : removed unnecessary test
a11bef0 -ScottChacon,6 years ago :first commit
$ git log --pretty=format:"%h %s"--graph
* 2d3acf9 ignore errors from SIGCHLD on trap
* 5e3ee11 Merge branch 'master' of git://github.com/dustin/grit
|\
|* 420eac9 Added a method for getting the current branch.*| 30e367c timeout code and tests
*| 5a09431 add timeout protection to grit
*| e1193f8 support for heads with slashes in them
|/* d6016bc require time for xmlschema
* 11d191e Merge branch 'defunkt' into local
来看一个实际的例子,如果要在 Git 源码库中查看 Junio Hamano 在 2021 年 7 月其间,除了合并提交之外的哪一个提交修改了测试文件,可以使用下面的命令:
$ git log --pretty="%h - %s"--author='Junio C Hamano'--since="2021-07-01" \
--before="2021-08-01"--no-merges -- t/
5610e3b -Fix testcase failure when extended attributes are in use
acd3b9e -Enhance hold_lock_file_for_{update,append}()API
f563754 - demonstrate breakage of detached checkout with symbolic link HEAD
d1a43f2 - reset --hard/read-tree --reset -u: remove unmerged newpaths
51a94af -Fix"checkout --track -b newbranch" on detached HEAD
b0ad11e - pull: allow "git pull origin $something:$current_branch" into an unborn branch
如何操作暂存区和工作目录中已修改的文件,这些命令在修改文件状态的同时,也会提示如何撤消操作。例如,已经修改了两个文件并且想要将它们作为两次独立的修改提交,但是却意外地输入 git add * 暂存了它们两个,如何只取消暂存两个中的一个呢? git status 命令提示:
$ git add *
$ git status
On branch master
Changes to be committed:(use "git reset HEAD <file>..." to unstage)
renamed:README.md ->README
modified:CONTRIBUTING.md
在 “Changes to be committed” 文字正下方,提示使用 git reset HEAD … 来取消暂存,所以,可以这样来取消暂存 CONTRIBUTING.md 文件:
$ git reset HEADCONTRIBUTING.md
Unstaged changes after reset:
M CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:(use "git reset HEAD <file>..." to unstage)
renamed:README.md ->READMEChanges 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 status 也告诉了我们应该如何做,在例子中,未暂存区域是这样:
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 checkout --CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:(use "git reset HEAD <file>..." to unstage)
renamed:README.md ->README
$ git remote show origin
* remote origin
URL: https://github.com/my-org/complex-project
FetchURL: https://github.com/my-org/complex-project
PushURL: https://github.com/my-org/complex-project
HEAD branch: master
Remote branches:
master tracked
dev-branch tracked
markdown-strip tracked
issue-43new(next fetch will store in remotes/origin)
issue-45new(next fetch will store in remotes/origin)
refs/remotes/origin/issue-11 stale (use 'git remote prune' to remove)Local branches configured for'git pull':
dev-branch merges with remote dev-branch
master merges with remote master
Local refs configured for'git push':
dev-branch pushes to dev-branch (up to date)
markdown-strip pushes to markdown-strip (up to date)
master pushes to master (up to date)
通过使用 git show 命令可以看到标签信息和与之对应的提交信息,输出显示了打标签者的信息、打标签的日期时间、附注信息,然后显示具体的提交信息:
$ git show v1.4
tag v1.4Tagger:BenStraub<ben@straub.cc>Date:SatMay320:19:122014-0700
my version 1.4
commit ca82a6dff817ec66f44342007202690a93763949
Author:ScottChacon<kody@gee-mail.com>Date:MonMar1721:52:112008-0700
changed the version number
$ git tag v1.4-lw
$ git tag
v0.1
v1.3
v1.4
v1.4-lw
v1.5
这时,如果在标签上运行 git show,不会看到额外的标签信息。命令只会显示出提交信息:
$ git show v1.4-lw
commit ca82a6dff817ec66f44342007202690a93763949
Author:ScottChacon<kody@gee-mail.com>Date:MonMar1721:52:112008-0700
changed the version number
③ 后期打标签
也可以对过去的提交打标签,假设提交历史是这样的:
$ git log --pretty=oneline
15027957951b64cf874c3557a0f3547bd83b3ff6 Merge branch 'experiment'
a6b4c97498bd301d84096da251c98a07c7723e65 beginning write support
0d52aaab4479697da7686c15f77a3d64d9165190 one more thing
6d52a271eda8725415634dd79daabbc4d9b6008e Merge branch 'experiment'
0b7434d86859cc7b8c3d5e1dddfed66ff742fcbc added a commit function
4682c3261057305bdd616e23b64b0857d832627b added a todo file
166ae0c4d3f420721acbb115cc33848dfcc2121a started write support
9fceb02d0ae598e95dc970b74767f19372d61af8 updated rakefile
964f16d36dfccde844893cac5b347e7b3d44abbc commit the todo
8a5cbc430f1a9c3d00faaeffd07798508422908a updated readme
$ git checkout 2.0.0Note: checking out '2.0.0'.You are in'detached HEAD' state.You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.If you want to create a newbranch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again.Example:
git checkout -b <new-branch>HEADis now at 99ada87...Merge pull request #89 from schacon/appendix-final
$ git checkout 2.0-beta-0.1PreviousHEAD position was 99ada87...Merge pull request #89 from schacon/appendix-finalHEADis now at df3f601... add atlas.json and cover image
$ git config --global alias.unstage 'reset HEAD --'
这会使下面的两个命令等价:
$ git unstage fileA
$ git reset HEAD-- fileA
这样看起来更清楚一些。通常也会添加一个 last 命令,像这样:
$ git config --global alias.last'log -1 HEAD'
这样,可以轻松地看到最后一次提交:
$ git last
commit 66938dae3329c7aebe598c2246a8e6af90d04646
Author:JoshGoebel<dreamer3@example.com>Date:TueAug2619:48:512008+0800
test for current head
Signed-off-by:ScottChacon<kody@example.com>