Git 操作技巧

前言

本篇博文记录博主在使用 Git 遇到的种种特殊的情况,以及相应的处理过程;

reset commit / 撤销提交

场景

在使用 Github 的时候,之前将某个超过 100M 的内容提交到了本地的某个 commit 中,结果 github 拒绝 push,原因是不让 push 超过 100M 的文件;而这个 100M 的文件是一个临时的垃圾文件,所以,本身也是不需要 push 到 Github 上的;

Github 的报错信息如下,

1
2
3
4
5
remote: Resolving deltas: 100% (305/305), completed with 94 local objects.
remote: error: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com.
remote: error: Trace: cf50c028af75b9519f1a8c8c9c41f535
remote: error: See http://git.io/iEPt8g \for more information.
remote: error: File public.zip is 136.61 MB; this exceeds GitHub's file size limit of 100.00 MB

this exceeds GitHub's file size limit of 100.00 MB

解决思路

  1. 在本地找到该 commit,然后在 commit 中删除该 100M 的文件

  2. 在本地找到该 commit,然后直接撤销

上述两种方式,方法 #1 是最好的,也不会影响到之前的本地 commit 的历史记录;但是 Git 不支持这样的操作,只支持要么撤销(reset)、要么 push;所以只能选择方式 #2 进行解决;那么解决的办法就聚焦到了如何找到包含该 100M 文件的 commit 分支,然后将其撤销;

具体操作

查找未 push 的 commit 历史版本

具体对应如下的三种操作,

git status

1
2
3
$ git status
On branch master
Your branch is ahead of 'origin/master' by 3 commits.

正如上面所看到的,它只会显示大概的信息;有几个未提交的 commit;

git cherry -v

1
2
3
4
$ git cherry -v
+ e2bd2b90279dd0eaaa17dd40b08df9ddc64c7280 encryption rsa mathmatical theroy~
+ caca9689399de17fecfa3a297a184dec1f00790e rsa math
+ 91906be5af6ba20e70010a4087f33a93ecacf9e1 rsa math

正如上面所看到的那样,会显示未提交 commit 的版本号以及comments,默认的提交顺序是自上而下,越下面的越晚提交;

git log master ^origin/master

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ git log master ^origin/master
commit 91906be5af6ba20e70010a4087f33a93ecacf9e1
Author: shangyang <comedshang@163.com>
Date: Fri May 19 23:50:45 2017 +0800

rsa math

commit caca9689399de17fecfa3a297a184dec1f00790e
Author: shangyang <comedshang@163.com>
Date: Fri May 19 23:46:39 2017 +0800

rsa math

commit e2bd2b90279dd0eaaa17dd40b08df9ddc64c7280
Author: shangyang <comedshang@163.com>
Date: Fri May 19 14:32:30 2017 +0800

encryption rsa mathmatical theroy~

会显示作者信息,版本号,更重要的是会显示提交日期

定位版本

通过查找未 push 的 commit 历史版本,虽然可以找到没有 push 的 commits 历史,但是,仍然不知道在哪个 commit 分支上,提交了这个超过 100M 的文件,到底该撤销哪个 commit 分支呢?当然,你可以直接把最初的那个 commit 分支给撤销,这样,后续的 commit 分支也就连带的撤销了,但是这样做的代价太大了,你所有本地的 commit 都丢失了,万一,你需要某个 commit 分支的记录呢?所以,这里的做法最好还是老老实实的一个一个分支去检查检查吧;需要使用到 git log 命令

git log versionnum –stat

从最初的 commit 版本 e2bd2b90279dd0eaaa17dd40b08df9ddc64c7280 开始查找,我们得到如下的记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ git log e2bd2b90279dd0eaaa17dd40b08df9ddc64c7280 --stat
commit e2bd2b90279dd0eaaa17dd40b08df9ddc64c7280
Author: shangyang <comedshang@163.com>
Date: Fri May 19 14:32:30 2017 +0800

encryption rsa mathmatical theroy~

.DS_Store | Bin 8196 -> 8196 bytes
.deploy_git | 2 +-
_config.yml | 2 +-
db.json | 2 +-
package.json | 6 +-
public.zip | Bin 0 -> 143242439 bytes
public/2016/12/16/hexo-base-concept/index.html | 38 +++-
public/2016/12/16/hexo-hello-world/index.html | 38 +++-
public/2016/12/17/docker-start/index.html | 38 +++-
public/2016/12/18/docker-aufs/index.html | 42 +++--
public/2016/12/18/ubuntu-start/index.html | 133 ++++++++++++--
public/2016/12/20/docker-basics/index.html | 42 +++--
public/2016/12/20/docker-image/index.html | 38 +++-
public/2016/12/20/linux-commands/index.html | 87 ++++++---
....

很明显,我们找到了这个超过 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
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git log
.....
commit e2bd2b90279dd0eaaa17dd40b08df9ddc64c7280
Author: shangyang <comedshang@163.com>
Date: Fri May 19 14:32:30 2017 +0800

encryption rsa mathmatical theroy~

commit a0155252eb093910e7fa8ea87d96afe1dff978e1
Author: shangyang <comedshang@163.com>
Date: Fri May 12 22:11:39 2017 +0800

linux commands curl
......

从所有提交的 commit 分支上可以看到,上一个分支是 a0155252eb093910e7fa8ea87d96afe1dff978e1,所以,我们要做的,就是将当前未 push 的历史分支回退到该分支上,这里,我们只是想撤销 100M 的文件而不想撤销本地文件中的其它修改,所以,我们不使用--hard命令;

1
2
$ git reset a0155252eb093910e7fa8ea87d96afe1dff978e1
....

再次查看未 push 的 commit 本地记录,

1
$ git cherry -v

不再返回任何信息,

再试试 git log

1
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 文件删除;

然后,再次 commit

1
2
$ git add .
$ git commit -m "rsa math"

最后,push

1
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 已经将其加入版本控制中了,那么如何解决呢?执行如下步骤,

  1. 首先,在 .gitignore 文件将 config.ini 加入

    1
    echo config.ini >> .gitignore
  2. 再次,使用 git rm –cached 命令对其进行删除,表示,将会从你本地的 repository 对 .gitingore 文件删除,但是该文件将会继续保留在你本地的 workspace 中,

    1
    git rm --cached config.ini
  3. 最后,提交

    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,然后忽略掉它;