![](/img/trans.png)
[英]How do I re-root a eclipse project to a parent folder without break it?
[英]How do I Re-root a git repo to a parent folder while preserving history?
我在/foo/bar/baz
有一個 git repo,有一個很大的提交歷史和多個分支。
我現在希望/foo/qux
與/foo/bar/baz
位於同一個/foo/qux
中,這意味着我需要它們都位於以/foo
根的/foo
。 但是,我想保留對/foo/bar/baz
所做更改的歷史記錄。
我首先想到了 git format-patch,然后是 apply,但不保留提交消息。
所以,
我需要重新root repo
(1) 到任意更高的祖先目錄 (2) 同時通過使它看起來像我一直在提交到/foo/bar/baz
來保留我的提交歷史
您想要的是git filter-branch
,它可以將整個存儲庫移動到子樹中,通過使其看起來好像一直都是這樣來保留歷史記錄。 在使用之前備份您的存儲庫!
這就是魔法。 在/foo/bar
,運行:
git filter-branch --commit-filter '
TREE="$1";
shift;
SUBTREE=`echo -e 040000 tree $TREE"\tbar" | git mktree`
git commit-tree $SUBTREE "$@"' -- --all
這將使/foo/bar
存儲庫具有另一個“bar”子目錄,其中包含其整個歷史記錄中的所有內容。 然后你可以將整個 repo 上移到foo
級別並向其中添加baz
代碼。
更新:
好的,這是怎么回事。 提交是指向“樹”的鏈接(將其視為代表整個文件系統子目錄內容的 SHA)加上一些“父”SHA 和一些元數據鏈接作者/消息/等。 git commit-tree
命令是將所有這些組合在一起的低級位。 --commit-filter
的參數被視為 shell 函數並在過濾過程中代替git commit-tree
運行,並且必須像它一樣運行。
我正在做的是獲取第一個參數,要提交的原始樹,並構建一個新的“樹對象”,通過另一個低級 git 命令git mktree
表示它位於子文件夾中。 要做到這一點,我必須向它輸入一些看起來像 git 樹的東西,即一組(模式 SP 類型 SP SHA TAB 文件名)行; 因此是 echo 命令。 當我鏈接到真正的commit-tree
時, mktree
的輸出然后被替換為第一個參數; "$@"
是一種完整傳遞所有其他參數的方法,已經用shift
去除了第一個參數。 有關信息,請參閱git help mktree
和git help commit-tree
。
因此,如果您需要多個級別,則必須嵌套一些額外級別的樹對象(這未經過測試,但這是一般想法):
git filter-branch --commit-filter '
TREE="$1"
shift
SUBTREE1=`echo -e 040000 tree $TREE"\tbar" | git mktree`
SUBTREE2=`echo -e 040000 tree $SUBTREE1"\tb" | git mktree`
SUBTREE3=`echo -e 040000 tree $SUBTREE2"\ta" | git mktree`
git commit-tree $SUBTREE3 "$@"' -- --all
這應該將實際內容向下移動到a/b/bar
(注意相反的順序)。
更新:綜合改進來自下面 Matthew Alpert 的回答。 沒有-- --all
這僅適用於當前簽出的分支,但由於問題是詢問整個存儲庫,因此這樣做比逐個分支更有意義。
與其創建新的存儲庫,不如將當前存儲庫中的內容移動到正確的位置:在當前目錄中創建一個新的目錄bar
並將當前內容移動到其中(因此您的代碼在/foo/bar/bar
)。 然后在新的bar
目錄 ( /foo/bar/baz
) 旁邊創建一個baz
目錄。 mv /foo /foo2; mv /foo2/bar /foo; rmdir /foo2
mv /foo /foo2; mv /foo2/bar /foo; rmdir /foo2
就完成了:)。
Git 的重命名跟蹤意味着您的歷史記錄仍然有效,而 Git 的內容散列意味着即使您移動了內容,您仍然引用存儲庫中的相同對象。
我有一個似乎沒有人說過的解決方案:
我特別需要的是將父目錄中的文件包含在我的存儲庫中(有效地將 repo 向上移動一個目錄)。
我通過以下方式實現了這一目標:
git mv
)git add
)git commit
) 中。我希望這可以幫助下一個人 - 我可能只是度過了一個無腦的一天,但我發現上面的答案過於詳盡和可怕(對於我需要的東西。)我知道這類似於上面 Andrew Aylett 的答案,但我的情況似乎有點不同,我想要一個更一般的看法。
在大多數正常情況下,git 會根據其位置(即 .git 目錄)查看所有文件,而不是使用絕對文件路徑。
因此,如果您不介意歷史記錄中的提交表明您已將所有內容向上移動,那么有一個非常簡單的解決方案,即移動 git 目錄。 唯一有點棘手的是確保 git 理解文件是相同的,並且它們只是相對於他移動:
# Create sub-directory with the same name in /foo/bar
mkdir bar
# Move everything down, notifying git :
git mv file1 file2 file3 bar/
# Then move everything up one level :
mv .git ../.git
mv bar/* .
mv .gitignore ../
# Here, take care to move untracked files
# Then delete unused directory
rmdir bar
# and commit
cd ../
git commit
唯一需要注意的是,在移動到新目錄時正確更新 .gitignore,以避免暫存不需要的文件或忘記一些文件。
在某些設置中,當 git 看到與已刪除文件完全相同的新文件時,它會設法自行確定文件已被移動。 在這種情況下,解決方案更簡單:
mv .git ../.git
mv .gitignore ../.gitignore
cd ../
git commit
再次,小心你的 .gitignore
這增加了 Walter Mundt 接受的答案。 我寧願評論他的回答,但我沒有聲譽。
所以 Walter Mundt 的方法效果很好,但它一次只對一個分支有效。 在第一個分支之后,可能會出現需要 -f 強制執行操作的警告。 因此,要一次對所有分支執行此操作,只需在末尾添加“---all”:
git filter-branch --commit-filter '
tree="$1";
shift;
subtree=`echo -e 040000 tree $tree"\tsrc" | git mktree`
git commit-tree $subtree "$@"' -- --all
並且要對特定分支執行此操作,請將它們的名稱添加到末尾,盡管我無法想象您為什么只更改某些分支的目錄結構。
在 git filter-branch 的手冊頁中閱讀有關此內容的更多信息。 但是,請注意使用此類命令后可能會出現推送困難的警告。 只要確保你知道你在做什么。
對於這種方法的任何潛在問題,我很感激。
這特別回答了“我如何將我的 git repo 向上移動一個或多個目錄並使它看起來總是那樣? ”
隨着git >= 2.22.0
git filter-repo
的出現,可以利用git filter-repo
來重寫歷史記錄,使其看起來好像該父目錄一直是其中的一部分。
這與@Walter-Mundt 的答案使用git filter-branch
完成的事情相同,但更簡單,執行起來也不那么脆弱。
請注意,現在git filter-repo
被git filter-branch
宣傳為更安全的替代方案。
因此,鑒於您的存儲庫位於/foo/bar/baz
並且您想將其向上移動到/foo
首先,為了防止在重寫歷史記錄時對工作區中的文件進行任何更改,暫時將存儲庫變成所謂的“裸”存儲庫,如下所示:
cd /foo/bar/baz
git config --local --bool core.bare true
實際的歷史重寫現在可以直接在.git
目錄中完成:
cd ./.git
git filter-repo --path-rename :bar/baz/
這將重寫 repo 的完整歷史記錄,就好像每條路徑總是預先添加bar/baz/
一樣(如果 repo 的根目錄向上兩級,他們就會這樣做)。 此操作不會影響實際文件,因為它現在是一個裸存儲庫。
最后,再次將其打開,將.git
目錄移動到指定位置,然后重置:
git config --local --bool core.bare false
cd ..
mv ./.git ../..
cd ../..
git reset
我認為, git reset
取消了存儲庫被關閉並再次返回的后遺症。 在執行git reset
之前嘗試git status
以了解我的意思。
最終的git status
現在應該證明一切正常,對/foo/qux
一些新的未跟蹤文件進行模數處理。
警告- 如果您在未克隆的存儲庫上嘗試上述操作,則git filter-repo
將拒絕發揮其魔力,除非您--force
它... 准備好備份並考慮自己警告。
1 除了已接受的答案,這幫助我讓它工作:當我將列出的文本放入 shell 腳本時,由於某種原因 -e 被保留了。 (很可能是因為我太厚,無法使用 shell 腳本)
當我刪除 -e 並移動引號以包含所有內容時,它起作用了。 SUBTREE2= echo "040000 tree $SUBTREE1 modules" | git mktree
echo "040000 tree $SUBTREE1 modules" | git mktree
請注意, $SUBTREE1 和 modules 之間有一個制表符,它與 -e 應該解釋的 \\t 相同。
您可以在foo
創建一個 git repo 並通過git submodules
引用baz
和bar
。
然后bar
和baz
共存,保留了完整的歷史。
如果你真的只想要一個 repo (foo),其中包含 bar 和 baz 歷史,那么一些移植技術或子樹合並策略是有序的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.