如何在git过滤器分支上保留用于修剪空子目录或子目录过滤器的标记
当使用--prune-empty
和/或--subdirectory-filter=…
重写历史记录git filter-branch --tag-name-filter cat …
时,您将了解到已删除标记的提交。 到目前为止这是合理的,并按设计工作。如何在git过滤器分支上保留用于修剪空子目录或子目录过滤器的标记
问题/目标
我现在要存档是:保存在附近的重写标签提交
例如:
从A -> B(tag: foo) -> C -> D -> E
启动(其中E比D更新于C ...)
运行git filter-branch
我得到任何
得到
A' -> B'(tag: foo)' -> E
(^良好的情况下)或:
A' -> D' -> E'
(^不好的情况下)
我” m试图得到的是:A'(tag: foo)' -> D' -> E'
自A'
代表什么已被标记在B
一些研究: 我stumpled过第一件事情是在Git: Is there a way to figure out where a commit was cherry-pick'ed from?git cherry
但这并不似乎很大帮助找到差异SIND树木间断。
相反,我已经找到了--commit-filter
https://stackoverflow.com/a/14783391/529977一个有用的例子写日志的改写的对象
一些想法: 带着这个--commit-filter
“映射文件”,我在理论上能够
- 过滤器的所有标签不重写的树设置
- 怎么也找不到到FI滤波器的树信息
- 迭代疑问标记列表
- 通过
git log --oneline -1 ${tag}
- 读取原始提交点查找原树的历史被称为是任何新提交改写
- 正向查找很难过
- alternativly从任何改写承诺找到标签下去历史
- 移动标签的第一场比赛在新的树
- 已知的问题:如何保存的所有信息,我不想重新标记经典的方式
- 跳过标记,如果有只提交之后另一个标签改写
- 如何确定的问题提交有标签
其他想法,我所做的是:
- 找到任何“类似”在原来的树比较
git log -1 --format="%an%ae%at%cn%ce%ct%s" | sha1sum
提交,然后遍历历史到下一个已知的标签,但这种接近上述想法
听起来仍然很难,即使我没有一个好主意来解决这些步骤...任何其他的想法或已知的解决方案(!!)欢迎!
Deleted: * * * * * *
Tags: R S T U V W
Commits: A -> B -> C -> D -> E -> F -> G -> H -> I -> J -> K -> L -> M -> N
预期输出:
Tags: R T V W
Commits: A -> B -> E -> G -> H -> I -> L -> N
我们将与--prune-empty
所以我们正在创造空提交对应该将其删除的提交可以测试这个。我们来设置测试存储库。
git init
touch n && git add n && git commit -m "N"
git commit --allow-empty -m "M"
touch l && git add l && git commit -m "L"
git commit --allow-empty -m "K"
git commit --allow-empty -m "J"
touch i && git add i && git commit -m "I"
touch h && git add h && git commit -m "H"
touch g && git add g && git commit -m "G"
git commit --allow-empty -m "F"
touch e && git add e && git commit -m "E"
git commit --allow-empty -m "D"
git commit --allow-empty -m "C"
touch b && git add b && git commit -m "B"
touch a && git add a && git commit -m "A"
git tag W $(git log --pretty=oneline --grep=M | cut -d " " -f1)
git tag V $(git log --pretty=oneline --grep=K | cut -d " " -f1)
git tag U $(git log --pretty=oneline --grep=F | cut -d " " -f1)
git tag T $(git log --pretty=oneline --grep=E | cut -d " " -f1)
git tag S $(git log --pretty=oneline --grep=D | cut -d " " -f1)
git tag R $(git log --pretty=oneline --grep=C | cut -d " " -f1)
首先我们要创建一个文件,其中包含所有标记名称以及它们指向的提交哈希。
for i in $(git tag); do echo $i; git log -1 --pretty=oneline $i | cut -d " " -f1; done > ../tags
当运行git filter-branch
提交哈希将改变。为了跟踪这些变化,我们创建一个文件,其中包含从旧提交哈希到新提交哈希的映射。这样做的诀窍显示为here。
的--subdirectory-filter=...
命令会再看看这样的:
git filter-branch --subdirectory-filter=... --commit-filter 'echo -n "${GIT_COMMIT}," >>/tmp/commap; git commit-tree "[email protected]" | tee -a /tmp/commap'
因为有我们需要改变某些事情了--commit-filter
的--prune-empty
选项冲突。的--prune-empty
文档在这里帮助:
有些过滤器会产生离开树触及空的提交。这个选项指示git-filter-branch删除这样的提交,如果它们只有一个或零个未修剪的父母;因此合并提交将保持不变。此选项不能与
--commit-filter
一起使用,但通过在提交过滤器中使用提供的git_commit_non_empty_tree
函数可以实现相同的效果。
因此,我们将用于此测试的--prune-empty
命令如下所示。在运行该命令之前,确保/tmp/commap
不存在或为空。
git filter-branch --commit-filter 'echo -n "${GIT_COMMIT}," >>/tmp/commap; git_commit_non_empty_tree "[email protected]" | tee -a /tmp/commap'
mv /tmp/commap ../commap
现在我们跑git filter-branch
并收集应对标签所需的所有信息。我们将不得不删除标签,我们将不得不更改提交标签。我们在这里很幸运,git只是在.git/refs/tags/TAGNAME
中存储了提交散列标记。
现在剩下的是写一个脚本来自动更正标签。这是我用Python写的。
def delete(tagname):
print('git tag -d {}'.format(tagname))
def move(tagname, tagref):
print('echo "{}" > .git/refs/tags/{}'.format(tagref, tagname))
tags = {}
with open('tags') as tagsfile:
for i, line in enumerate(tagsfile):
if i%2 == 0:
tagname = line[:-1]
else:
# if there are multiple tags on one commit
# we discard all but one
tagref = line[:-1]
if tagref in tags:
delete(tags[tagref])
tags[tagref] = tagname
commap = []
with open('commap') as commapfile:
for line in commapfile:
old, new = line[:-1].split(',')
commap.append((old, new))
lastnew = None
takentag = None
for old, new in commap:
if old in tags:
if takentag:
delete(takentag)
takentag = tags[old]
if new != lastnew:
# commit was not deleted
if takentag:
move(takentag, new)
takentag = None
lastnew = new
该脚本输出调整标签所需的命令。在我们的例子是这样的输出:
echo "0593fe3aa7a50d41602697f51f800d34b9887ba3" > .git/refs/tags/W
echo "93e65edf18ec8e33e5cc048e87f8a9c5270dd095" > .git/refs/tags/V
git tag -d U
echo "41d9e45de069df2c8f2fdf9ba1d2a8b3801e49b2" > .git/refs/tags/T
git tag -d S
echo "a0c4c919f841295cfdb536fcf8f7d50227e8f062" > .git/refs/tags/R
粘贴命令Git仓库看起来如预期控制台后:
$ git log
8945e933c1d8841ffee9e0bca1af1fce84c2977d A
a0c4c919f841295cfdb536fcf8f7d50227e8f062 B
41d9e45de069df2c8f2fdf9ba1d2a8b3801e49b2 E
6af1365157d705bff79e8c024df544fcd24371bb G
108ddf9f5f0a8c8d1e17042422fdffeb147361f2 H
93e65edf18ec8e33e5cc048e87f8a9c5270dd095 I
0593fe3aa7a50d41602697f51f800d34b9887ba3 L
5200d5046bc92f4dbe2aee4d24637655f2af5d62 N
$ git tag
R
T
V
W
$ git log -1 --pretty=oneline R
a0c4c919f841295cfdb536fcf8f7d50227e8f062 B
$ git log -1 --pretty=oneline T
41d9e45de069df2c8f2fdf9ba1d2a8b3801e49b2 E
$ git log -1 --pretty=oneline V
93e65edf18ec8e33e5cc048e87f8a9c5270dd095 I
$ git log -1 --pretty=oneline W
0593fe3aa7a50d41602697f51f800d34b9887ba3 L
有趣的是:你发现'git_commit_non_empty_tree'和'--tag-name-filter cat'一起导致移动标签和正确处理。 谢谢 我甚至怀疑我写了一个误导性的例子,用A - > B,其中B对我来说比A新,但是对你来说是反过来的。 原生解决方案导致移动,其中E被标记为R,S,T--这在我看来是正确的! –
我想用'--commit-filter'和'git_commit_non_empty_tree'来更改/去掉相关部分,因为我认为您的答案反过来由于错误的A-> B而反映了历史顺序所需的解决方案在示例中的顺序?或者,也许你可能在我接受它作为答案之前自己改变它? –
@childno͡。德请编辑我的答案,删除所有不需要解决您的问题。我会接受你的编辑。 – timakro
嗨,刚开始赏金的宽限期,想获得它。我只是想提醒你,以防第一次通知/电子邮件丢失。 – timakro
@timakro对不起,但:您认为哪个宽限期?我不记得设立了一个赏金..但呢?! –
其他人设置赏金,但它现在跑了^^。没关系,但不是你的错。 – timakro