是的
- 您可以使用
git log --pretty=email 将文件的提交历史转换为电子邮件补丁
- 您在新目录中重新组织这些文件并重命名它们
- 您将这些文件(电子邮件)转换回 Git 提交以使用
git am 保留历史记录。
限制
- 不保留标签和分支
- 路径文件重命名(目录重命名)时历史被删
通过示例逐步解释
1。以电子邮件格式提取历史记录
示例:提取file3、file4 和file5 的历史记录
my_repo
├── dirA
│ ├── file1
│ └── file2
├── dirB ^
│ ├── subdir | To be moved
│ │ ├── file3 | with history
│ │ └── file4 |
│ └── file5 v
└── dirC
├── file6
└── file7
设置/清理目的地
export historydir=/tmp/mail/dir # Absolute path
rm -rf "$historydir" # Caution when cleaning the folder
以电子邮件格式提取每个文件的历史记录
cd my_repo/dirB
find -name .git -prune -o -type d -o -exec bash -c 'mkdir -p "$historydir/${0%/*}" && git log --pretty=email -p --stat --reverse --full-index --binary -- "$0" > "$historydir/$0"' {} ';'
很遗憾,选项 --follow 或 --find-copies-harder 不能与 --reverse 组合使用。这就是为什么在重命名文件(或重命名父目录)时会删除历史记录的原因。
电子邮件格式的临时历史记录:
/tmp/mail/dir
├── subdir
│ ├── file3
│ └── file4
└── file5
Dan Bonachea 建议在第一步中反转 git log 生成命令的循环:与其对每个文件运行一次 git log,不如在命令行上使用文件列表只运行一次并生成单个统一日志.这样,修改多个文件的提交在结果中仍然是一个提交,并且所有新提交都保持其原始的相对顺序。请注意,在(现在统一的)日志中重写文件名时,这也需要在下面的第二步中进行更改。
2。重新组织文件树并更新文件名
假设您想将这三个文件移动到另一个 repo 中(可以是同一个 repo)。
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB # New tree
│ ├── dirB1 # from subdir
│ │ ├── file33 # from file3
│ │ └── file44 # from file4
│ └── dirB2 # new dir
│ └── file5 # from file5
└── dirH
└── file77
因此重新组织您的文件:
cd /tmp/mail/dir
mkdir -p dirB/dirB1
mv subdir/file3 dirB/dirB1/file33
mv subdir/file4 dirB/dirB1/file44
mkdir -p dirB/dirB2
mv file5 dirB/dirB2
您的临时历史现在是:
/tmp/mail/dir
└── dirB
├── dirB1
│ ├── file33
│ └── file44
└── dirB2
└── file5
同时更改历史记录中的文件名:
cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'
3。应用新的历史记录
你的另一个仓库是:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
└── dirH
└── file77
应用来自临时历史文件的提交:
cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am --committer-date-is-author-date
--committer-date-is-author-date 保留原始提交时间戳(Dan Bonachea 的评论)。
你的另一个仓库现在是:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB
│ ├── dirB1
│ │ ├── file33
│ │ └── file44
│ └── dirB2
│ └── file5
└── dirH
└── file77
使用 git status 查看准备推送的提交数量 :-)
额外技巧:检查 repo 中重命名/移动的文件
列出已重命名的文件:
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'
更多自定义:您可以使用选项--find-copies-harder 或--reverse 完成命令git log。您还可以使用 cut -f3- 和 grepping 完整模式 '{.* => .*}' 删除前两列。
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'