本回答基于git am
提供有趣命令,并通过示例逐步呈现。
目标
- 您希望将一个或多个文件从一个存储库移动到另一个存储库。
- 您想保留它们的历史记录。
- 但是您不关心保留标签和分支。
- 您接受重命名文件(以及重命名目录中的文件)的有限历史记录。
过程
- 使用命令
git log --pretty=email -p --reverse --full-index --binary
以电子邮件格式提取历史记录。
- 重新组织文件树并在历史记录中更新文件名更改[可选]
- 使用
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
清理临时目录
destination。
export historydir=/tmp/mail/dir
rm -rf "$historydir"
清理您的代码库源代码。
git commit ... # Commit your working files
rm .gitignore # Disable gitignore
git clean -n # Simulate removal
git clean -f # Remove untracked file
git checkout .gitignore # Restore gitignore
将每个文件的历史记录提取为电子邮件格式
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
2. 重新组织文件树并更新文件名的历史记录[可选]
假设您想将这三个文件移动到该仓库的其他位置(可以是同一个仓库)。
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB
│ ├── dirB1
│ │ ├── file33
│ │ └── file44
│ └── dirB2
│ └── file5
└── dirH
└── file77
因此,重新组织你的文件:
cd /tmp/mail/dir
mkdir dirB
mv subdir dirB/dirB1
mv dirB/dirB1/file3 dirB/dirB1/file33
mv dirB/dirB1/file4 dirB/dirB1/file44
mkdir 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
你的另一个代码库现在是:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB ^
│ ├── dirB1 | New files
│ │ ├── file33 | with
│ │ └── file44 | history
│ └── dirB2 | kept
│ └── file5 v
└── dirH
└── file77
使用
git status
命令以查看准备推送的提交数量 :-)
注意:由于历史记录已重写,以反映路径和文件名更改:
(即与先前仓库中的位置/名称进行比较)
- 无需
git mv
更改位置/文件名。
- 无需
git log --follow
访问完整历史记录。
额外技巧:检测仓库内已重命名/移动的文件
列出已重命名的文件:
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'
更多自定义选项:您可以使用选项
--find-copies-harder
或
--reverse
完成命令
git log
。您还可以使用
cut -f3-
删除前两列,并使用完整模式'{.* => .*}'进行筛选。
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'
git fetch p2 && git merge p2
而是用git fetch p2 && git branch .. && git merge p2
?编辑:好的,看起来你想在一个名为p2的新分支中获取更改,而不是当前分支。 - Lekensteyngit filter-repo
是进行此操作的正确工具,而非filter-branch
。 - Ed Randall