我们最近完成了从Mercurial到Git的转换,一切顺利,我们甚至能够获得所需的转换,使所有内容在存储库中看起来/工作相对正确。我们添加了.gitignore
并且正在进行中。
但是,只要我们加入/合作任何旧的功能分支,我们就会遇到一些极端的减速。稍微探索一下,我们发现,因为.gitignore
只是在我们查看其他提交而没有合并时才添加到develop
分支中,因此它会窒息,试图分析我们所有的构建工件(二进制文件)等...因为这些旧分支没有.gitignore
文件。
我们想要做的是有效地插入一个带有.gitignore的新root提交,以便它可以追溯填充所有头文件/标签。我们很乐意重写历史记录,我们的团队规模相对较小,所以当历史重写完成后,让每个人都停止这个操作并重新拉出他们的存储库是没有问题的。
我发现有关将master重新设置为新的root提交的信息,这适用于master,问题是它使我们的功能分支在旧历史树上分离,它还会重播整个历史记录使用新的提交日期/时间。
任何想法或者我们在这个想法上运气不好吗?
答案 0 :(得分:8)
您要执行的操作将涉及两个阶段:追溯添加具有合适.gitignore
的新根,并清理历史记录以删除不应添加的文件。 git filter-branch
命令可以同时执行这两项操作。
考虑一下你的历史代表。
$ git lola --name-status
* f1af2bf (HEAD, bar-feature) Add bar
| A .gitignore
| A bar.c
| D main.o
| D module.o
| * 71f711a (master) Add foo
|/
| A foo.c
| A foo.o
* 7f1a361 Commit 2
| A module.c
| A module.o
* eb21590 Commit 1
A main.c
A main.o
为清楚起见,*.c
文件代表C源文件,*.o
是应该被忽略的编译目标文件。
在条形图功能分支上,您添加了一个合适的.gitignore
和已删除的目标文件,这些文件不应该被跟踪,但您希望该政策反映在导入的任何位置。
请注意,git lola
是non-standard但有用的别名。
git config --global alias.lola \
'log --graph --decorate --pretty=oneline --abbrev-commit --all'
按如下所示创建新的根提交。
$ git checkout --orphan new-root
Switched to a new branch 'new-root'
git checkout
文档指出了新的孤儿分支可能出现意料之外的状态。
如果要启动记录一组与 start_point 完全不同的路径的断开连接的历史记录,则应在创建孤立分支后立即清除索引和工作树通过从工作树的顶层运行
git rm -rf .
。之后,您将准备好准备新文件,重新填充工作树,从其他地方复制它们,提取tarball等。
继续我们的例子:
$ git rm -rf .
rm 'foo.c'
rm 'foo.o'
rm 'main.c'
rm 'main.o'
rm 'module.c'
rm 'module.o'
$ echo '*.o' >.gitignore
$ git add .gitignore
$ git commit -m 'Create .gitignore'
[new-root (root-commit) 00c7780] Create .gitignore
1 file changed, 1 insertion(+)
create mode 100644 .gitignore
现在历史似乎是
$ git lola
* 00c7780 (HEAD, new-root) Create .gitignore
* f1af2bf(bar-feature) Add bar
| * 71f711a (master) Add foo
|/
* 7f1a361 Commit 2
* eb21590 Commit 1
这有点误导,因为它让new-root看起来像是bar-feature的后代,但它确实没有父级。
$ git rev-parse HEAD^
HEAD^
fatal: ambiguous argument 'HEAD^': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
记下孤儿的SHA,因为稍后你会需要它。在这个例子中,它是
$ git rev-parse HEAD
00c778087723ae890e803043493214fb09706ec7
我们希望git filter-branch
进行三次广泛的更改。
.gitignore
,除非已存在。在命令行上,显示为
git filter-branch \
--parent-filter '
test $GIT_COMMIT = eb215900cd15ca2cf9ded74f1a0d9d25f65eb2bf && \
echo "-p 00c778087723ae890e803043493214fb09706ec7" \
|| cat' \
--index-filter '
git rm --cached --ignore-unmatch "*.o"; \
git ls-files --cached --error-unmatch .gitignore >/dev/null 2>&1 ||
git update-index --add --cacheinfo \
100644,$(git rev-parse new-root:.gitignore),.gitignore' \
--tag-name-filter cat \
-- --all
说明:
--parent-filter
选项挂钩您的新根提交。
eb215...
是旧根提交的完整SHA, cf。 git rev-parse eb215
--index-filter
选项包含两部分:
git rm
会删除整个树中匹配*.o
的所有内容,因为glob模式是由git而不是shell引用和解释的。.gitignore
与git ls-files
,如果不存在,请指向新根目录。cat
。--
终止选项,--all
是所有参考的简写。您看到的输出类似于
Rewrite eb215900cd15ca2cf9ded74f1a0d9d25f65eb2bf (1/5)rm 'main.o'
Rewrite 7f1a361ee918f7062f686e26b57788dd65bb5fe1 (2/5)rm 'main.o'
rm 'module.o'
Rewrite 71f711a15fa1fc60542cc71c9ff4c66b4303e603 (3/5)rm 'foo.o'
rm 'main.o'
rm 'module.o'
Rewrite f1af2bf89ed2236fdaf2a1a75a34c911efbd5982 (5/5)
Ref 'refs/heads/bar-feature' was rewritten
Ref 'refs/heads/master' was rewritten
WARNING: Ref 'refs/heads/new-root' is unchanged
您的原件仍然安全。例如,主分支现在位于refs/original/refs/heads/master
下。查看新重写分支中的更改。准备好删除备份后,运行
git update-ref -d refs/original/refs/heads/master
您可以在一个命令中编写一个命令来覆盖所有备份引用,但我建议您仔细检查每个命令。
最后,新的历史是
$ git lola --name-status
* ab8cb1c (bar-feature) Add bar
| M .gitignore
| A bar.c
| * 43e5658 (master) Add foo
|/
| A foo.c
* 6469dab Commit 2
| A module.c
* 47f9f73 Commit 1
| A main.c
* 00c7780 (HEAD, new-root) Create .gitignore
A .gitignore
观察所有目标文件都已消失。在bar-feature中对.gitignore
的修改是因为我使用了不同的内容来确保它被保留。为了完整性:
$ git diff new-root:.gitignore bar-feature:.gitignore
diff --git a/new-root:.gitignore b/bar-feature:.gitignore
index 5761abc..c395c62 100644
--- a/new-root:.gitignore
+++ b/bar-feature:.gitignore
@@ -1 +1,2 @@
*.o
+*.obj
新根参考不再有用,所以用
处理它$ git checkout master
$ git branch -d new-root
答案 1 :(得分:-1)
免责声明:这是理论上的(基于文档),我没有这样做。 克隆并尝试。
根据我的理解,您从未提交过的文件,而这些文件将被您想要添加到历史记录根目录的.gitignore
过滤。
因此,如果您将主分支重新绑定到仅包含.gitignore的newroot提交,您实际上不会修改提交的内容,之后您应该能够重新设置任何和所有其他分支。在新的提交中,rebase将为你完成工作。
由于提交的内容相同,修补程序ID应保持不变,并且rebase仅应用必要的内容。
你需要逐个重新定义每个分支,但这很容易编写脚本。
可在以下部分找到更多信息in the git rebase documentation: 从页面末尾的UPSTREAM REBASE中恢复。
编辑:好的,没关系,经过测试,并且不能完全按照这种方式工作。您必须“手动”为新历史记录中的每个分支指定rebase点,这很痛苦。 仍然可以使用,但它显然是一个比接受的答案更糟糕的解决方案。