目录
git工作目录下文件的状态
git工作目录下的文件的状态变化周期,如下图:
你工作目录下的每一个文件都不外乎这两种状态:已跟踪或未跟踪。 已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后,它们的状态可能处于未修改,已修改或已放入暂存区。 工作目录中除已跟踪文件以外的所有其它文件都属于未跟踪文件,它们既不存在于上次快照的记录中,也没有放入暂存区。 初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态。
总结:文件会处于未跟踪状态、已跟踪状态(处于工作区状态(未修改、已修改)、处于暂存区(也叫索引区)状态、处于本地仓库的提交状态)
提交git commit
git commit -v:会启用 shell 的环境变量 $EDITOR
所指定的软件,一般都是 vim 或 emacs,用 -v
选项,这会将你所做的改变的 diff 输出放到编辑器中从而使你知道本次提交具体做了哪些修改。
git commit -a -v:启用vim,把所有已经跟踪过的文件(包括已经暂存的和未暂存的文件)暂存起来一并提交,从而跳过 git add
步骤。
删除文件git rm
llb@llb-PC MINGW64 /e/git-pro/pro0 (master)
$ echo "xyz" >> c.c ; cat c.c
xyz
llb@llb-PC MINGW64 /e/git-pro/pro0 (master)
$ git status ; git rm c.c
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
c.c
nothing added to commit but untracked files present (use "git add" to track)
fatal: pathspec 'c.c' did not match any files //刚添加的文件处于无跟踪状态,使用git rm无法删除掉,只能使用rm删
//添加到暂存区,然后测试删除情况
llb@llb-PC MINGW64 /e/git-pro/pro0 (master)
$ git add c.c ; git status
warning: LF will be replaced by CRLF in c.c.
The file will have its original line endings in your working directory
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: c.c //c.c处于暂存态(将要提交的该版本的快照区)
//暂存区的文件无法删除,除非加上-f,彻底删除;或者加上--cached选项只删暂存区的快照
llb@llb-PC MINGW64 /e/git-pro/pro0 (master)
$ git rm c.c
error: the following file has changes staged in the index:
c.c
(use --cached to keep the file, or -f to force removal)
//为了增加复杂性,我们让c.c文件同时处于修改态和暂存态
llb@llb-PC MINGW64 /e/git-pro/pro0 (master)
$ echo "123" >> c.c ; git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: c.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: c.c
//再来删除,提示只能用 -f彻底删除出工作区
llb@llb-PC MINGW64 /e/git-pro/pro0 (master)
$ git rm c.c
error: the following file has staged content different from both the
file and the HEAD:
c.c
(use -f to force removal)
//但是我们只想删除暂存区,不想彻底删除;只有让c.c添加到暂存区之后再删除
llb@llb-PC MINGW64 /e/git-pro/pro0 (master)
$ git add c.c ; git status ; git rm c.c
warning: LF will be replaced by CRLF in c.c.
The file will have its original line endings in your working directory
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: c.c
error: the following file has changes staged in the index:
c.c
(use --cached to keep the file, or -f to force removal)
//使用--cached删除时保留一份在工作区间
llb@llb-PC MINGW64 /e/git-pro/pro0 (master)
$ git rm --cached c.c
rm 'c.c'
//发现使用--cached删除后,c.c处于无跟踪态
llb@llb-PC MINGW64 /e/git-pro/pro0 (master)
$ git status ; ls
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
c.c
nothing added
总结删除:如果暂存区有快照的话,git rm删除无效,git rm -f可以从工作区间彻底删除(删除暂存区的快照数据,还有工作区的数据(包括无跟踪态或未修改态或已修改态的数据)),并且下一次提交到仓库区时(如果该文件以前提交过的话),该文件就不再纳入版本管理了,不想彻底从工作区间删除出去,则使用git rm --cached只删除暂存区,这样会保留文件为无跟踪态。如果暂存区无快照的话,根据提示删除即可。
请注意,工作区、暂存区和本地仓库区都可能存储有代表某一个文件的数据,但代表这个文件的3个区中的数据可能有差异,而工作区中的文件就可以有无跟踪态、未修改态(曾经添加到暂存区过,还提交到仓库区过)、已修改态这三个不一样状态的数据。
不要相信优快云草稿箱的自动保存功能,以下的文字都是我重新记录的。
git diff:查看尚未暂存的文件更新了哪些部分。
git diff --cached:查看已暂存的文件将要添加到下次提交里的内容,还允许使用 git diff --staged 。
Git 提供了一个跳过使用暂存区域的方式, 只要在提交的时候,给 git commit
加上 -a
选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add
步骤。
删除修改过的并且已经放到暂存区域的文件,工作区数据和暂存区快照数据都会被删除,git rm -f files,-f处于安全考虑,用于防止误删还没有添加到快照的数据,这样的数据不能被 Git 恢复。
$ ls ; git status
a.c b.c c.c
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: a.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: a.c
Untracked files:
(use "git add <file>..." to include in what will be committed)
c.c
$ git rm a.c
error: the following file has staged content different from both the
file and the HEAD:
a.c
(use -f to force removal)
$ git rm -f a.c
rm 'a.c'
$ ls
b.c c.c
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: a.c
Untracked files:
(use "git add <file>..." to include in what will be committed)
c.c
$ git commit -m "delete files"
[master dccbfd4] delete files
1 file changed, 16 deletions(-)
delete mode 100644 a.c
$ ls ; git status
b.c c.c
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
c.c
nothing added to commit but untracked files present (use "git add" to track)
但是并不想让 Git 继续跟踪,只是保留在工作区版本的数据,又想删除暂存区版本的数据,使用git rm -f --cached files:
//b.c暂存区有一个版本,工作区还有一个修改版本,暂存区的修改内容为:int d = 000;
$ ls ; git status
b.c c.c
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: b.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: b.c
Untracked files:
(use "git add <file>..." to include in what will be committed)
c.c
$ git rm -f --cached b.c
rm 'b.c'
//删除了暂存区的文件b.c,保留了修改后在工作区的版本,并且删除b.c这个操作可以使用git commit提交到本地仓库
$ ls ; git status ; cat b.c
b.c c.c
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: b.c
Untracked files:
(use "git add <file>..." to include in what will be committed)
b.c
c.c
int add(int a, int b){
return a+b;
}
int main(){
return;
}
int b = 333;
int c = 666;
int d = 000;
$ git commit
On branch master
//这个例子是提交全部修改过或者未跟踪的文件到暂存区,然后使用--cached项删除暂存区的数据,这会保留一份工作区的数据
$ git status ; git add * ; git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
b.c
c.c
nothing added to commit but untracked files present (use "git add" to track)
warning: LF will be replaced by CRLF in b.c.
The file will have its original line endings in your working directory
warning: LF will be replaced by CRLF in c.c.
The file will have its original line endings in your working directory
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: b.c
new file: c.c
$ git rm --cached *.c
rm 'b.c'
rm 'c.c'
$ ls
b.c c.c
$ git commit
On branch master
Untracked files:
b.c
c.c
nothing added to commit but untracked files present
查看提交信息
查看有树形分支的提交信息
git log --pretty=format:"%h %s" --graph:--graph这个选项添加了一些ASCII字符串来形象地展示你的分支、合并历史。
$ git log --pretty=format:"%h %s" --graph
* b6f0848 git commit -s
* 2b497b8 Please enter the commit message for your changes. Lines starting
* dccbfd4 delete files
* f804d95 the status of a.c is staged,and b.c is modified and is not staged!
* e837cb6 test commit!
* 9f71199 xxx
* f7b03a8 init
查看最近2次提交的内容差异
git log -p -2
$ git log -p -2
commit 168a51359d0fa5944930d95f7a63509cf47bc833 (HEAD -> master)
Author: LHB <1524325112@qq.com>
Date: Thu Mar 7 16:12:07 2019 +0800
test git commit
diff --git a/d.c b/d.c
new file mode 100644
index 0000000..f04115a
--- /dev/null
+++ b/d.c
@@ -0,0 +1,2 @@
+dddddd
+dddddd
commit b6f0848d097a88604c79c13802b3e5b75e141dfc
Author: LHB <1524325112@qq.com>
Date: Thu Mar 7 16:04:00 2019 +0800
git commit -s
Signed-off-by: LHB <1524325112@qq.com>
diff --git a/b.c b/b.c
new file mode 100644
index 0000000..32df0d9
--- /dev/null
+++ b/b.c
撤销操作
git commit --amend:如果自上次提交以来你还未做任何修改(例如,在上次提交后马上执行了此命令),那么快照会保持不变,而你所修改的只是再次提交提交时的信息。例如,你提交后发现忘记了暂存某些需要的修改,可以像下面这样操作:
$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend
撤消对已跟踪文件的修改 - 将它还原成上次提交时的样子(或者刚克隆完的样子,或者刚把它放入工作目录时的样子):use "git checkout -- <file>..." to discard changes in working directory。这是一个危险的命令,你对那个文件做的任何修改都会消失。
撤销暂存的文件:git reset HEAD <file>...
来取消暂存区域的文件。
撤消合并,有两种方法来解决如下图的这个问题(现在想撤销合并节点M):
1.修复引用,你在错误的 git merge
后运行 git reset --hard HEAD~
,这会重置分支指向(重置引用)所以它们看起来像这样:
移动引用实际上会丢失那些改动。 如果有任何其他提交在合并之后创建了,那么这个方法也会无效;移动引用实际上会丢失那些改动。
2.还原提交,你在master分支上执行如下命令:
$ git revert -m 1 HEAD
[master b1d8379] Revert "Merge branch 'topic'"
让还原提交的历史看起来如下图(^M
与 C6
有完全一样的内容,所以从这儿开始就像合并从未发生过):
现在你在 topic
中增加工作,然后想再次使用git merge topic合并:
其实这时候可以安全地合并,但我不明白这种总情况下,git为什么不让很好地合并,而且官方gitbook给的解释是:Git 只会引入被还原的合并 之后 的修改---意思是C2,^M
,C7三方合并的时候,C7只会引入C7的修改,C3和C4的修改被丢弃。所以C8这个合并节点合并有问题或者不让合并。官方说要想很好地再次合并,必须先撤消 还原原始的合并,然后在合并:
$ git revert ^M
[master 09f0126] Revert "Revert "Merge branch 'topic'""
$ git merge topic
M
与 ^M
抵消了。 ^^M
事实上合并入了 C3
与 C4
的修改,C8
合并了 C7
的修改,所以现在 topic
已经很好地被合并了。
上面的撤销用例都用到了reset(重置)和checkout(检出)这两个关键的命令,他们可以很好地实现撤销工作,你想撤销什么?撤销刚刚提交的(部分或所有)文件到祖辈的某一次提交的版本上?撤销刚刚提交的(部分或所有)文件到祖辈的某一次提交的版本上,并且让索引区的(部分或所有)文件也撤销到这个版本上?撤销刚刚提交的(部分或所有)文件到祖辈的某一次提交的版本上,不仅让索引区的(部分或所有)文件也撤销到这个版本上,工作区的(部分或所有)文件也被撤销到这个版本上?还能怎么撤销?具体参看我的git(2)。
git tag
Git 可以给历史中的某一个提交打上标签,以示重要。
列出标签:
$ git tag
v0.1
v1.3
//Git 自身的源代码仓库包含标签的数量超过 500 个。 如果只对 1.8.5 系列感兴趣
$ git tag -l 'v1.8.5*'
v1.8.5
v1.8.5-rc0
v1.8.5-rc1
v1.8.5-rc2
v1.8.5-rc3
v1.8.5.1
v1.8.5.2
v1.8.5.3
v1.8.5.4
v1.8.5.5
创建标签:轻量标签(lightweight)与附注标签(annotated).
轻量标签很像一个不会改变的分支 - 它只是一个特定提交的引用。附注标签是存储在 Git 数据库中的一个完整对象。 它们是可以被校验的;其中包含打标签者的名字、电子邮件地址、日期时间;还有一个标签信息;并且可以使用 GNU Privacy Guard (GPG)签名与验证。 通常建议创建附注标签,这样你可以拥有以上所有信息。
创建附注标签:$ git tag -a v1.4 -m 'my version 1.4'
$ git tag -a v1.4 -m 'my version 1.4'
$ git tag
v0.1
v1.3
v1.4
$ git show v1.4
tag v1.4
Tagger: Ben Straub <ben@straub.cc>
Date: Sat May 3 20:19:12 2014 -0700
my version 1.4
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -0700
changed the version number
创建轻量标签不需要使用 -a
、-s
或 -m
选项,只需要提供标签名字:
$ git tag v1.4-lw
$ git tag
v0.1
v1.3
v1.4
v1.4-lw
v1.5
$ git show v1.4-lw
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon <schacon@gee-mail.com>
Date: Mon Mar 17 21:52:11 2008 -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 tag -a v1.2 9fceb02
$ git tag
v0.1
v1.2
v1.3
v1.4
v1.4-lw
v1.5
$ git show v1.2
tag v1.2
Tagger: Scott Chacon <schacon@gee-mail.com>
Date: Mon Feb 9 15:32:16 2009 -0800
version 1.2
commit 9fceb02d0ae598e95dc970b74767f19372d61af8
Author: Magnus Chacon <mchacon@gee-mail.com>
Date: Sun Apr 27 20:43:35 2008 -0700
updated rakefile
...
传送标签到远程仓库服务器上。默认情况下,git push
命令并不会传送标签到远程仓库服务器上。必须显式地推送标签到共享服务器上: git push origin [tagname]。
想要一次性推送很多标签,也可以使用带有 --tags
选项的 git push
命令:git push origin --tags
删除标签:
$ git tag -d v1.4-lw
Deleted tag 'v1.4-lw' (was e7d5add)
//应该注意的是上述命令并不会从任何远程仓库中移除这个标签,你必须使用 git push <remote> :refs/tags/<tagname> 来更新你的远程仓库:
$ git push origin :refs/tags/v1.4-lw
To /git@github.com:schacon/simplegit.git
- [deleted] v1.4-lw
Git 别名
通过 git config
文件来轻松地为每一个命令设置一个别名.
$ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.ci commit
$ git config --global alias.st status
$ git config --global alias.unstage 'reset HEAD --' <======>
$ git unstage fileA
$ git reset HEAD -- fileA
分支
你切换其他分支之前,要留意你的工作目录和暂存区里那些还没有被提交的修改,它可能会和你即将检出的分支产生冲突从而阻止 Git 切换到该分支。 最好的方法是,在你切换分支之前,保持好一个干净的状态(工作区的修改和暂存区的修改都已提交)。 有一些方法可以绕过这个问题(即,保存进度(stashing) 和 修补提交(commit amending)),我们会在 储藏与清理 中看到关于这两个命令的介绍。
在分支合并操作没有需要解决的分歧时——就叫做 “快进(fast-forward)”——因为一分支的当前提交是另一个分支的最后提交的祖辈提交,只需要把这个分支的指针向前移动到另一分支的提交上。
若两个分支上的最后提交节点没有祖孙辈的关系,然后一个分支合并另一分支的话,有可能会产生冲突。遇到冲突时的分支,我们可以合并冲突的文件,合并规则是:你必须选择使用由 =======
分割的两部分中的一个,或者你也可以自行合并这些内容,并且 <<<<<<<
, =======
, 和 >>>>>>>
这些行被完全删除了。例如:
<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
please contact us at support@github.com
</div>
>>>>>>> iss53:index.html
解决为:
<div id="footer">
please contact us at email.support@github.com
</div>
之后再提交这次修改后的冲突。
删除分支
git branch -v:查看每一个分支的最后一次提交。
git branch --merged:查看哪些分支已经合并到当前分支,这些分支是被合并之后再无发展新的提交节点,在这个列表中分支名字前没有 *
号的分支通常可以使用 git branch -d
删除掉;你已经将它们的工作整合到了另一个分支,所以并不会失去任何东西。
$ git branch --merged
* master
show
$ git branch -d show
Deleted branch show (was 82e262d).
git branch --no-merged:查看哪些分支尚未合并到当前分支,这些分支可以使用git branch -D
选项强制删除它。
整合分支
1.Fast-forward操作使得master合并到了hotfix的指向节点:
$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
index.html | 2 ++
1 file changed, 2 insertions(+)
Fast-forward在测试的时候, 可能因为合并的时候,master分支最后提交节点是分支的最后提交节点的祖辈节点,如果对同一文件都有修改的话,只是让分支的最后提交节点对该文件的修改来完全覆盖master的修改。
2.三方合并(C2,C4,C5三方合并为C6)
三方合并 是三个节点之间的合并,如果对同一文件都有有修改的话,一般都会产生冲突,需要启用合并工具(例如mergetool)来解决冲突。
3.变基
先再 experiment分支上基于共同祖先C2之后的每一个节点依次执行变基操作到master,图中只有一个节点C4:
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command
丢弃了experiment分支上原来的节点C4,变基到master分支上,还是叫experiment分支:
回到master分支,执行一次Fast-forward合并操作。
$ git checkout master
$ git merge experiment
不要对在你的仓库外有副本的分支执行变基。出了问题你的同事也会因此鄙视你。
清理工作目录中的文件
git clean -f -d:移除工作目录中所有未追踪的文件以及空的子目录。
it clean -d -n:做一次删除演习,告诉你将要 移除什么。
使用交互式选项删除:git clean -f -i
$ git status
On branch iss53
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: a.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: a.c
modified: d.c
Untracked files:
(use "git add <file>..." to include in what will be committed)
e.c
//交互式选项-i
$ git clean -f -i
Would remove the following item:
e.c
*** Commands ***
1: clean 2: filter by pattern 3: select by numbers
4: ask each 5: quit 6: help
What now> a
Remove e.c [y/N]? y
Removing e.c
储藏
当你在一个分支上工作了一段时间,这时候需要切换回另一个分支进行工作,此时这个分支的工作目录下有未跟踪的文件e.c,修改过的跟踪文件a.c,和暂存的文件a.c和d.c,这时候需要将修改和暂存,或者连同未跟踪的保存到一个栈上,等到切换回该分支时可以从栈上恢复成原来离开该分支前的状态。
llb@llb-PC MINGW64 /e/git-pro/pro0 (iss53)
$ ls ; git status
a.c b.c c.c d.c e.c index.html
On branch iss53
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: a.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: a.c
modified: d.c
Untracked files:
(use "git add <file>..." to include in what will be committed)
e.c
我们使用git stash [save]把修改过的跟踪文件和暂存的文件保存到栈上,或者使用git stash --all连同未跟踪的文件也保存到栈上,切换回该分支时使用git stash list查看栈上的储藏,使用git stash apply --index来使用栈顶的储藏,使用git stash apply stash@{n} --index来使用栈上第n个储藏。
$ git stash --all
warning: LF will be replaced by CRLF in e.c.
The file will have its original line endings in your working directory
Saved working directory and index state WIP on iss53: 1b66d98 Merge branch 'master' into iss53
$ git status
On branch iss53
nothing to commit, working tree clean
#之前有2个储藏,刚刚生成的储藏在栈顶
$ git stash list
stash@{0}: WIP on iss53: 1b66d98 Merge branch 'master' into iss53
stash@{1}: WIP on iss53: 1b66d98 Merge branch 'master' into iss53
stash@{2}: WIP on iss53: 1b66d98 Merge branch 'master' into iss53
#切换分支
$ git checkout master ; ls
Switched to branch 'master'
b.c c.c d.c
llb@llb-PC MINGW64 /e/git-pro/pro0 (master)
$ git status
On branch master
nothing to commit, working tree clean
#切换回iss53分支
$ git checkout iss53
Switched to branch 'iss53'
llb@llb-PC MINGW64 /e/git-pro/pro0 (iss53)
$ ls;git status
b.c c.c d.c index.html
On branch iss53
nothing to commit, working tree clean
$ git stash list
stash@{0}: WIP on iss53: 1b66d98 Merge branch 'master' into iss53
stash@{1}: WIP on iss53: 1b66d98 Merge branch 'master' into iss53
stash@{2}: WIP on iss53: 1b66d98 Merge branch 'master' into iss53
#使用栈顶的储藏---刚刚储藏的储藏
$ git stash apply --index
On branch iss53
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: a.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: a.c
modified: d.c
Untracked files:
(use "git add <file>..." to include in what will be committed)
e.c
切换回分支并启用了离开该分支前的储藏,此时该分支恢复了原来离开前的状态,就可以继续安心地工作了。
你的打赏是我奋笔疾书的动力!
支付宝打赏:
微信打赏: