前言
本篇博文记录博主在使用 Git 遇到的种种特殊的情况,以及相应的处理过程;
reset commit / 撤销提交
场景
在使用 Github 的时候,之前将某个超过 100M 的内容提交到了本地的某个 commit 中,结果 github 拒绝 push,原因是不让 push 超过 100M 的文件;而这个 100M 的文件是一个临时的垃圾文件,所以,本身也是不需要 push 到 Github 上的;
Github 的报错信息如下,
1 | remote: Resolving deltas: 100% (305/305), completed with 94 local objects. |
this exceeds GitHub's file size limit of 100.00 MB
解决思路
在本地找到该 commit,然后在 commit 中删除该 100M 的文件
在本地找到该 commit,然后直接撤销
上述两种方式,方法 #1 是最好的,也不会影响到之前的本地 commit 的历史记录;但是 Git 不支持这样的操作,只支持要么撤销(reset)、要么 push;所以只能选择方式 #2 进行解决;那么解决的办法就聚焦到了如何找到包含该 100M 文件的 commit 分支,然后将其撤销;
具体操作
查找未 push 的 commit 历史版本
具体对应如下的三种操作,
git status
1 | $ git status |
正如上面所看到的,它只会显示大概的信息;有几个未提交的 commit;
git cherry -v
1 | $ git cherry -v |
正如上面所看到的那样,会显示未提交 commit 的版本号
以及comments
,默认的提交顺序是自上而下,越下面的越晚提交;
git log master ^origin/master
1 | $ git log master ^origin/master |
会显示作者信息,版本号,更重要的是会显示提交日期
;
定位版本
通过查找未 push 的 commit 历史版本,虽然可以找到没有 push 的 commits 历史,但是,仍然不知道在哪个 commit 分支上,提交了这个超过 100M 的文件,到底该撤销哪个 commit 分支呢?当然,你可以直接把最初的那个 commit 分支给撤销,这样,后续的 commit 分支也就连带的撤销了,但是这样做的代价太大了,你所有本地的 commit 都丢失了,万一,你需要某个 commit 分支的记录呢?所以,这里的做法最好还是老老实实的一个一个分支去检查检查吧;需要使用到 git log 命令
git log versionnum –stat
从最初的 commit 版本 e2bd2b90279dd0eaaa17dd40b08df9ddc64c7280 开始查找,我们得到如下的记录
1 | $ git log e2bd2b90279dd0eaaa17dd40b08df9ddc64c7280 --stat |
很明显,我们找到了这个超过 100M 的文件 public.zip,注意描述Bin 0 -> 143242439 bytes
,这个表示该文件是从 0 -> 100M 新增的文件;
1 | public.zip | Bin 0 -> 143242439 bytes |
那么接下来,我们就需要对这个分支 e2bd2b90279dd0eaaa17dd40b08df9ddc64c7280 进行撤销操作
reset 撤销分支
撤销包含两种方式git reset <versionnum>
和git reset --hard <versionnum>
,两种方式的共同点都是将当前 commit 历史撤销至
当前 versionnum 对应的 commit 版本,也就是说,将 commit 历史回退到当前的 commit versionnum 的历史版本;不同的是,git reset
只会撤销 commit 分支,并不会修改本地文件;git reset --hard
不但会撤销 commit 分支并且会覆盖本地文件使其与 versionnum 中的 commit 文件保持一致;使用的时候一定要特别注意;
好了,我们知道了,reset 命令是把 commit 历史回退到指定的历史版本分支上,那么,因为该 public.zip 是由最早的未 push 的 commit 分支 e2bd2b90279dd0eaaa17dd40b08df9ddc64c7280 所提交的,所以,我们需要回退到的是它的上一个 commit 分支上;因为该分支是已经提交过的 commit 分支,所以需要通过命令git log
查找所有的 commit 历史记录,
1 | $ git log |
从所有提交的 commit 分支上可以看到,上一个分支是 a0155252eb093910e7fa8ea87d96afe1dff978e1,所以,我们要做的,就是将当前未 push 的历史分支回退到该分支上,这里,我们只是想撤销 100M 的文件而不想撤销本地文件中的其它修改,所以,我们不使用--hard
命令;
1 | $ git reset a0155252eb093910e7fa8ea87d96afe1dff978e1 |
再次查看未 push 的 commit 本地记录,1
$ git cherry -v
不再返回任何信息,
再试试 git log1
2
3
4
5$ git log
commit a0155252eb093910e7fa8ea87d96afe1dff978e1
Author: shangyang <comedshang@163.com>
Date: Fri May 12 22:11:39 2017 +0800
linux commands curl
可以看到,最新的提交就是刚回退至的这个版本 a0155252eb093910e7fa8ea87d96afe1dff978e1 linux commands curl;
这样,本地的提交中不再包含这个 100M 的文件 public.zip 了;
重新 commit
由reset 撤销分支我们通过git reset
命令已经成功的回滚了 commit 的历史记录,但是,它并不会覆盖本地的文件,所以,需要手动的将本地文件中的 public.zip 文件删除;
然后,再次 commit1
2$ git add .
$ git commit -m "rsa math"
最后,push1
2
3
4
5$ git push
...
remote: Resolving deltas: 100% (172/172), completed with 90 local objects.
To https://github.com/comedsh/raw.github.io.git
a015525..36ba8da master -> master
Ok,这次我们成功了;
ignore 已经提交的文件
如果某个文件已经提交,比如某个配置文件 config.ini,此时,如果再将其加入 .gitignore 文件中,是不会生效的,因为 Git 已经将其加入版本控制中了,那么如何解决呢?执行如下步骤,
首先,在 .gitignore 文件将 config.ini 加入
1
echo config.ini >> .gitignore
再次,使用 git rm –cached 命令对其进行删除,表示,将会从你本地的 repository 对 .gitingore 文件删除,但是该文件将会继续保留在你本地的 workspace 中,
1
git rm --cached config.ini
最后,提交
1
git commit -m 'start ignoring config.ini'
这样,已经提交过的 config.ini 就可以被忽略了,也就是说,.gitignore 中对 config.ini 的忽略生效了;如果需要再次使得 config.ini 文件生效,那么只需要将 config.ini 的配置从 .gitingore 中进行删除即可;
上述的方式唯一的弊端是,如果一旦 push,服务器上的 config.ini 文件也会被删除;如果想要本地保存一份本地版本的 ini 文件,最好是重新创建一个新的配置文件,比如 config_local.ini,然后忽略掉它;