2017-08-11 55 views
3

當使用--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-filterhttps://stackoverflow.com/a/14783391/529977一個有用的例子寫日誌的改寫的對象

一些想法: 帶着這個--commit-filter「映射文件」,我在理論上能夠

  1. 過濾器的所有標籤不重寫的樹設置
    • 怎麼也找不到到FI濾波器的樹信息
  2. 迭代疑問標記列表
  3. 通過git log --oneline -1 ${tag}
  4. 讀取原始提交點查找原樹的歷史被稱爲是任何提交改寫
    • 正向查找很難過
    • alternativly從任何改寫承諾找到標籤下去歷史
  5. 移動標籤的第一場比賽在新的樹
    • 已知的問題:如何保存的所有信息,我不想重新標記經典的方式
  6. 跳過標記,如果有隻提交之後另一個標籤改寫
    • 如何確定的問題提交有標籤

其他想法,我所做的是:

  • 找到任何「類似」在原來的樹比較git log -1 --format="%an%ae%at%cn%ce%ct%s" | sha1sum提交,然後遍歷歷史到下一個已知的標籤,但這種接近上述想法

聽起來仍然很難,即使我沒有一個好主意來解決這些步驟...任何其他的想法或已知的解決方案(!!)歡迎!

+0

嗨,剛開始賞金的寬限期,想獲得它。我只是想提醒你,以防第一次通知/電子郵件丟失。 – timakro

+0

@timakro對不起,但:您認爲哪個寬限期?我不記得設立了一個賞金..但呢?! –

+0

其他人設置賞金,但它現在跑了^^。沒關係,但不是你的錯。 – timakro

回答

1
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 
+0

有趣的是:你發現'git_commit_non_empty_tree'和'--tag-name-filter cat'一起導致移動標籤和正確處理。 謝謝 我甚至懷疑我寫了一個誤導性的例子,用A - > B,其中B對我來說比A新,但是對你來說是反過來的。 原生解決方案導致移動,其中E被標記爲R,S,T--這在我看來是正確的! –

+0

我想用'--commit-filter'和'git_commit_non_empty_tree'來更改/去掉相關部分,因爲我認爲您的答案反過來由於錯誤的A-> B而反映了歷史順序所需的解決方案在示例中的順序?或者,也許你可能在我接受它作爲答案之前自己改變它? –

+0

@childno͡。德請編輯我的答案,刪除所有不需要解決您的問題。我會接受你的編輯。 – timakro