文章

Git 奇淫技巧

Git 奇淫技巧

Git 奇淫技巧

Git clone 加速

有时候我们对 github 的仓库进行 clone 的时候,会发现很慢,甚至是龟速,很不够效率。好在有一个简单且快捷的方法来倍速提升 clone 效率。

1
git clone https://github.com/flutter/flutter.git --config "http.proxy=192.168.1.6:1611"
  • 通过 --config "http.proxy=192.168.1.6:1611" 设置代理
  • 其中 192.168.1.6:1611 是代理的地址,需要自己搭建或者可用的

上面的配置好,再次执行,基本上可以得到百倍的提效。

修改 Git 提交非当前时间

git revision 的时间是以本地时间记录的,只需要将本机时间修改成你想提交的时间就可以了

修复而非新建提交 (git commit –amend)

假设提交之后,你意识到自己犯了一个拼写错误。你可以重新提交一次,并附上描述你的错误的提交信息 (这样的话,你就有了 2 个 commit 记录)。但是,还有一个更好的方法(前提是提交尚未推送到远程分支),那么按照下面步骤简单操作一下就可以了:

  1. 修正你的错误(如 build. gradle)
  2. 将修正的文件暂存
1
git add build.gradle
  1. 在提交的时候,运行 git commit –amend 命令,将会把最近一次的变更追加到你最新的提交。同时也会给你一个编辑提交信息的机会
1
git commit –amend
  1. 准备好之后 wq 保存,将干净的分支推送到远程分支。
  2. SourceTree 勾选 Amend last commit

Git 跨仓库迁移代码文件,并保留 git 历史记录

A 仓库全部迁移到 B 仓库,并保留 git 历史记录

A mirror 方式 push B

比如:我又一个私有仓库在码云上,现在 github 私有仓库免费,我想迁移到 github 上。

  1. Github 新建一个仓库 TheMonkeyKingAssistant
  2. 进入到码云所在的代码仓库
  3. 执行命令:git push --mirror git@github.com:hacket/TheMonkeyKingAssistant.git;这样 push 的代码就会带 git log
  4. 去 Github 拉取代码就行了

给 B 添加一个 A 仓库的 remote url

git 项目初始化后,默认情况下会有一个叫 origin 的远程仓库。我们也可以用 git remote add repo_A [repo_A的仓库地址] 来新增一个 “ 远程 “ 仓库,然后使用 git pull,将多个仓库的代码拉下来并 merge。这样便可以实现 A 仓库全部迁移到 B 仓库,并保留 git 历史记录。
注意这里的 [repo_A的仓库地址] 也可以是本地仓库路径。比如我这里添加了两个 repo,repo-A 是位于本地路径的 “ 远程 “ 仓库。

A 仓库中部分子目录文件迁移到 B 仓库,并保留 git 历史记录

Git filter-branch 重写历史

git filter-branch 是 git 提供的重写历史的命令,我们可以用 subdirectory-filter 来实现重写 git 项目子路径的 git 历史记录:

git filter-branch -f --subdirectory-filter <directory> 重写子路径 directory 的 git 历史记录

subdirectory-filter 可以从 git 项目中过滤出给定子目录的 git 历史记录,同时会把给定的子目录作为 git 项目的根目录

对于一个多模块项目,有一天想把其中一个模块迁移成单独的项目,因为有很多提交,所以想带着提交记录一起迁移。本质上就是:git 迁移项目中的某个目录到新项目,包括那一个目录的提交记录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 克隆一份新的旧项目, 选择一个分支,比如dev分支
$ git clone -b dev 旧项目git地址

$ cd my-project

# 假如我只需要 service 目录下的东西和其提交记录
$ git filter-branch -f --subdirectory-filter AppSamples/SiDebugKit

# 现在,该项目只剩下 AppSamples/SiDebugKit里的东西了

# 推送到新的git地址
$ git push --mirror 新的git地址

# 然后,在新地址里捣鼓,删掉旧的分支的信息

原仓库:

执行 git filter-branch -f --subdirectory-filter AppSamples/SiDebugKit

注意:

  1. 只保留 AppSamples/SiDebugKit 目录下的文件,其他文件会被删除
  2. 如果新的 git 地址不为空,如果提交到 master,会把之前的给覆盖掉
  3. 如果有 submodule,非空的目录删除不了
将仓库 A 迁移到仓库 B (仓库 B 已经存在了,且有自己的代码)
  1. 裁剪仓库 A
1
2
3
4
5
6
# 裁剪仓库A
git filter-branch -f --subdirectory-filter AppSamples/SiDebugKit
# 裁剪后的仓库A,会以AppSamples/SiDebugKit为根目录,怎么样让提交到B仓库是带目录的?
mkdir SiDebugKit
git add. 
git commit "add SiDebugKit"

操作后的目录:

  1. 合并到仓库 B
1
2
3
4
5
6
7
8
9
# 先进入到仓库B
## 给仓库B添加一个remote,这个remote是本地裁剪后的仓库A
git remote add from_repo_A /Users/xxx/OpenSources/temp/king-assist/
## 查看仓库B所有的remote仓库
git remote -v
## 从from_repo_A的master拉取代码
git pull from_repo_A master --allow-unrelated-histories 
## 删除仓库B的remote:from_repo_A
git remote rm from_repo_A

步骤 2 操作完后,仓库 B 存在两个不相关的 remote:origin/master,from_repo_A/master,如果直接将 from_repo_A/master 合并到 master 会报错:

需要加上参数 --allow-unrelated-histories

git merge master –allow-unrelated-histories

git-filter-repo 三方库

  1. 新的仓库需要为空的 git 库

Git 将当前修改提交至其他分支

还未 commit 的

当前处于 A 分支,需要将此次的代码提交至 B 分支则可以进行以下操作

1
2
3
4
5
6
7
8
9
10
11
12
13
//在没有进行commit之前可以进行一下操作
 
1、通过git stash将工作区恢复到上次提交的内容,同时备份本地所做的修改
git stash
2、然后切换至B分支
git checkout B
3、从git栈中获取到最近一次stash进去的内容,恢复工作区的内容,获取之后,会删除栈中对应的stash
git stash pop
4、在进行正常的提交代码步骤即可
git add /src/main/..
5、git commit -m "功能开发"
6、git pull origin   分支名称
7、git push origin   分支名称

已经 commit 的 cherry pick

v 11.6.0 的已经提交到本地了,现在需要提交到 v 11.7.0 远端分支上

首先切换到本地的 v 11.7.0 分支,然后选中 v 11.6.0 要提交到 v 11.7.0 分支的 commit,

然后 Cherry pick 到 v 11.7.0 分支上,最后 push v 11.7.0 分支到远端分支上。

git 上做文件大小写重命名坑

git 大小写不敏感

设置 git 库为大小写敏感(不建议)

1
git config core.ignorecase false

用这种方法进行重命名,用 git status 就可以识别出修改了,但是不推荐用这种方式,因为在更新这种修改的时候会有麻烦。

使用 git mv 命令(仅当 core. ignorecase 为 true 时可用

1
 git mv ABC.java Abc.java

此时的状态是 renamed,git commit 即可。

core.ignorecase 为 false 时,更新时错误

core. ignorecase 不为 true 时会出现如下错误

1
2
3
4
error:
 The following untracked working tree files would be overwritten by merge:

        Abc.java

或者在切换分支等操作的时候莫名出现这样的错误,解决方法都是将 core. ignorecase 设置为 true,然后再进行操作。

Git 行尾换行符 line-ending(CRLF&LF)

遇到的问题

在 Windows 平台上,会出现如下的 warning:

1
2
3
$ git add app.wxss
warning: LF will be replaced by CRLF in app.wxss.
The file will have its original line endings in your working directory.

Why?

  1. Windows 在换行的时候,同时使用了回车符 (carriage-return character) 和换行符 (linefeed character);
  2. Mac 和 Linux 系统 (很老的 mac 系统才是 CR,可以参见 wiki),此时,仅仅使用了换行符 (linefeed character)。
  3. 同时呢,Windows 的许多编辑器还悄悄滴将 LF 修改成了 CRLF 格式的行结束符,或者在你敲回车的时候,CRLF 格式的行结束符就产生了。当然,这一切都发生在同时存在 Windows 和非 Windows 的跨平台工作中,如果大家都是同一种操作系统,那么,就天下太平了。

warning 中所说的 LF 和 CRLF 分别是 linefeedcarriage-return&linefeed

  • Git 为什么 LF->CRLF
    Git 有一个针对性的功能:当添加到暂存区时,自动将 CRLF 转换成 LF;反之,当检出时,自动将 LF 转换成 CRLF。
    可以通过设置 core.autocrlf 来开启这个功能

cherry-pick Git 中只 merge 部分 commit

git cheery-pick(单个 commit)

在 Git 1.7.2 以上的版本引入了一个 cheery-pick 的命令可以只 merge 部分的 commit 而不用直接把整个分支 merge 过来:

1
git cherry-pick <commit 号>

1
git cherry-pick e43a6fd3e94888d76779ad79fb568ed180e5fcdf

这样就只会把这个 e43a6fd3e94888d76779ad79fb568ed180e5fcdf commit 的内容 pull 到当前的分支,不过你会得到一个新的 commit。这样就可以按需 merge 需要的 commit, 而不需要的就可以直接废弃咯。

多个 commit

可以用空格指定多个 commit:

1
git cherry-pick b8dcc42 3370c39

范围 merge

cherry-pick 可以范围 merge ,使用两次版本间使用 .. 连起来:

1
git cherry-pick A..B

这样会把从从版本 A(不包含)到 B(包含)即(A,B] 的版本 pull 到当前分支
甚至,可以使用多段,同样使用空格隔开:

1
git cherry-pick A..B C..D E..F

注:中间需要自己解决冲突,若出现冲突,可以尝试使用 git mergetool 使用 GUI 工具解决

本文由作者按照 CC BY 4.0 进行授权