簡體   English   中英

我如何變基 git 超級項目更改子模塊的哈希值?

[英]How do I rebase a git superproject changing the hashes of the submodules?

背景

假設我們有兩個 git 存儲庫,一個是另一個的子模塊( A將是超級項目, B將是子模塊)。 項目A本身不是源代碼,而是一個收集和跟蹤有關其子模塊的信息的項目。 A repo 很少(如果有的話)存在於本地機器上,而是一堆腳本讓它保持更新。

有一天,有人意識到 repo B應該更好地使用 LFS,並使用git lfs migrate import清理了 repo。 我有一個B的舊散列和新散列的列表。

我做了什么

由於 repo A恰好是線性的(無分支),我能夠執行git rebase --root -i ,將所有提交更改為edit ,並運行一個簡單的 bash 腳本,將子模塊重置為新的哈希值。 這是腳本的示例:

#!/bin/bash
#set the submodule path and input files
submodulePath=foo
newHashesFile=NewHashes.txt
originalHashesFile=OriginalHashes.txt

while [ (test -d "$(git rev-parse --git-path rebase-merge)" || test -d "$(git rev-parse --git-path rebase-apply)" ) ]; do
    numLines=`git ls-files --stage | grep $submodulePath | wc -l`
    if [ $numLines = 1 ];
    then
        oldHash=`git ls-files --stage | grep $submodulePath | sed -e 's/^160000 \([^ ]*\) 0.*$/\1/g'`
        echo oldHash: $oldHash
    else
        echo merge conflict
        oldHash=`git ls-files --stage | grep $submodulePath | grep '^160000 \([^ ]*\) 3.*' | sed -e 's/^160000 \([^ ]*\) 3.*$/\1/g'`
        echo oldHash: $oldHash    
    fi

    lineNumber=`grep -n $oldHash $originalHashesFile | sed -e 's/^\([^:]*\):.*/\1/g'`
    newHash=`head -n $lineNumber $newHashesFile | tail -n 1`

    if [ ! $lineNumber ];
    then
        echo Hash not changed
    else
        cd $submodulePath
        git reset --hard $newHash
        cd ../
    fi

    git add $submodulePath/
    git commit --amend
    git rebase --continue
done

問題

所有這一切都奏效了,但我想知道是否有更簡單的方法來做到這一點,因為我想我會被要求再次這樣做。 這個問題有兩個部分。

  1. 有沒有一種簡單的方法可以告訴 git 您希望默認為edit而不是pick ,而不依賴於編輯器?
  2. 有沒有更簡單的方法告訴 git 執行腳本的操作? 如果我從超級項目中執行git lfs migrate import ,會有幫助嗎?

有沒有一種簡單的方法可以告訴 git 您希望默認為編輯而不是選擇,而不依賴於編輯器?

不,但是,有一種方法可以將命令序列編輯器設置為與其他編輯器不同的編輯器:設置環境變量GIT_SEQUENCE_EDITOR 因此,例如,您可以執行以下操作:

GIT_SEQUENCE_EDITOR="sed -i '' s/^pick/edit/" git rebase -i ...

(假設您的sed有一個-i以這種方式工作,等等)。

有沒有更簡單的方法告訴 git 執行腳本的操作?

Given that you want to update each gitlink hash, I'd use git filter-branch (rather than git rebase ) to do it, with an --index-filter that does the gitlink hash updates. 我不確定這是否更簡單,但更直接。 索引過濾器本身將包括使用git ls-files --stage類似於您執行此操作的方式,但它本身可能使用生成的sed腳本或awk腳本。 Generated-sed 可能會更快,而 awk 會更簡單,特別是如果您有一個現代 awk ,您可以在其中讀取 hash 映射。

多年來不得不這樣做幾次之后,我聽取了torek 的建議,並將過於冗長的 bash 腳本編寫為單個git filter-branch 我將其張貼在這里,供其他用戶和未來的我使用。

首先,只是為了闡明我是如何執行lfs migrate import的(而且我確定我為其中一些行走了很長的路):

# Make sure we have the up-to-date remote branches
git submodule update --init SubmodulePath/
cd SubmodulePath/
git fetch --all

# Create local branches that mirror the remote ones
git branch -lr | grep -v "origin/HEAD" | sed 's/^.*origin\///' | 
   xargs -I @ git branch @ origin/@ --force

#Find all files that git identifies as binary and create the lfs migrate command, then run it
git log --all --numstat | grep '^-' | cut -f3 | sed 's|^.*/\(.*\)|\1|' | sed 's|^.*\.\([^.]*\)|\1|' |
   sort -u --ignore-case | sed 's|\([^0-9]\)|[\L\1\U\1]|g' | awk '{print}' ORS=',*.' |
   sed 's|^\(.*\),\*\.$|git lfs migrate import --everything --object-map=LFSImport.txt --include="*.\1"|' | . /dev/stdin

然后我將 LFSImport 移動到另一個目錄(我也將它提交給子模塊 repo)並使用index-filter運行filter-branch

git filter-branch -f --index-filter '
   numLines=`git ls-files --stage | grep SubmodulePath | wc -l`
   if [ $numLines = 1 ];
   then
     echo 
     oldHash="$(git rev-parse --quiet --verify :SubmodulePath)"
     echo oldHash: $oldHash
     newHash="$(grep  $oldHash /path/to/LFSImport.txt | cut -d , -f2)"
     echo newHash: $newHash
     git update-index --add --cacheinfo 160000 $newHash SubmodulePath
   fi
   ' HEAD

我可能應該在$newHash上添加一個檢查以查看它是否不為空(它在我的一次提交中,但我手動只是將它設置為其他不存在的東西)。 正如 torek 提到的,這更干凈、更快,而且工作得很好,如果不是更好的話。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM