[英]Is it possible to move/rename files in Git and maintain their history?
我想重命名/移動 Git 中的項目子樹,將其從
/project/xyz
到
/components/xyz
如果我使用普通的git mv project components
,那么xyz project
所有提交歷史都會丟失。 有沒有辦法移動它以保持歷史?
Git 檢測重命名而不是通過提交來持久化操作,因此使用git mv
還是mv
並不重要。
log
命令接受一個--follow
參數,該參數在重命名操作之前繼續歷史,即,它使用啟發式搜索類似的內容:
http://git-scm.com/docs/git-log
要查找完整歷史記錄,請使用以下命令:
git log --follow ./path/to/file
簡短的回答是否定的。 在 Git 中重命名文件並記住歷史記錄是不可能的。 這是一種痛苦。
有傳言說git log --follow
--find-copies-harder
會起作用,但它對我不起作用,即使文件內容的更改為零,並且已使用git mv
進行了移動。
(最初我使用 Eclipse 在一個操作中重命名和更新包,這可能讓 Git 感到困惑。但這是一件非常常見的事情。如果只執行mv
然后執行commit
和mv
--follow
似乎確實有效不會太遠。)
Linus 說你應該全面了解軟件項目的全部內容,而不需要跟蹤單個文件。 好吧,可悲的是,我的小腦袋無法做到這一點。
真的很煩,這么多人漫不經心地重復了 Git 自動跟蹤移動的說法。 他們浪費了我的時間。 Git 不做這樣的事情。 按照設計(!)Git 根本不跟蹤移動。
我的解決方案是將文件重命名回其原始位置。 更改軟件以適應源代碼管理。 使用 Git,您似乎只需要第一次就正確地“git”。
不幸的是,這破壞了 Eclipse,它似乎使用了--follow
。 git log --follow
有時不會顯示具有復雜重命名歷史的文件的完整歷史記錄,即使git log
。 (我不知道為什么。)
(有一些太聰明的 hack 會返回並重新提交舊工作,但它們相當可怕。請參閱 GitHub-Gist: emiller/git-mv-with-history 。)
簡而言之:如果Subversion 這樣做是錯誤的,那么 Git 這樣做也是錯誤的 - 這樣做不是某些(錯誤!)功能,而是一個錯誤。
可以重命名文件並保持歷史記錄不變,盡管這會導致文件在存儲庫的整個歷史記錄中被重命名。 這可能僅適用於痴迷 git-log-lovers,並且有一些嚴重的影響,包括:
現在,既然你還和我在一起,你可能是一個重命名一個完全隔離的文件的獨立開發者。 讓我們使用filter-tree
移動文件!
假設您要將old
文件移動到文件夾dir
並將其命名為new
這可以用git mv old dir/new && git add -u dir/new
,但這打破了歷史。
反而:
git filter-branch --tree-filter 'if [ -f old ]; then mkdir dir && mv old dir/new; fi' HEAD
將重做分支中的每個提交,在每次迭代的刻度中執行命令。 當你這樣做時,很多事情都會出錯。 我通常會測試該文件是否存在(否則它尚未移動),然后執行必要的步驟以根據我的喜好硬拔樹。 在這里,您可以通過文件 sed 來更改對文件的引用等。 把自己打昏! :)
完成后,文件被移動並且日志完好無損。 你感覺像一個忍者海盜。
還; 當然,只有將文件移動到新文件夾時才需要 mkdir 目錄。 if將避免在歷史記錄中創建此文件夾早於您的文件存在。
git log --follow [file]
將通過重命名向您展示歷史。
我願意:
git mv {old} {new}
git add -u {new}
我想重命名/移動 Git 中的項目子樹,將其從
/project/xyz
到
/組件/xyz
如果我使用普通的
git mv project components
,那么xyz
項目的所有提交歷史都會丟失。
否(8 年后,Git 2.19,2018 年第三季度),因為 Git 會檢測目錄 rename ,現在有更好的文檔記錄。
請參閱提交 b00bf1c , 提交 1634688 , 提交 0661e49 , 提交 4d34dff , 提交 983f464 , 提交 c840e1a , 提交 9929430 (2018 年 6 月 27 日),以及提交 d4e8065 2018 年 6 月 2018 年 6 月 2018 日cdrenja 提交 d4e8062 ( 新人 18 cdrenja 2 提交 18 cdren newren
(由Junio C gitster
合並-- gitster
-- in commit 0ce5a69 ,2018 年 7 月 24 日)
現在在Documentation/technical/directory-rename-detection.txt
:
例子:
當所有
x/a
、x/b
和x/c
都移動到z/a
、z/b
和z/c
,很可能同時添加的x/d
也希望移動到z/d
提示整個目錄“x
”移動到“z
”。
但它們還有許多其他情況,例如:
歷史的一側重命名
x -> z
,另一側將一些文件重命名為x/e
,導致合並需要進行傳遞重命名。
為了簡化目錄重命名檢測,這些規則由 Git 強制執行:
目錄重命名檢測適用時的幾個基本規則限制:
- 如果給定目錄在合並的兩側仍然存在,我們不認為它已被重命名。
- 如果要重命名的文件的子集有文件或目錄妨礙(或將相互妨礙),“關閉”這些特定子路徑的目錄重命名並向用戶報告沖突.
- 如果歷史的另一側將目錄重命名為您的歷史側重命名的路徑,則忽略來自歷史另一側的特定重命名以進行任何隱式目錄重命名(但警告用戶)。
您可以在t/t6043-merge-rename-directories.sh
看到很多測試,其中也指出:
- a) 如果重命名將目錄拆分為兩個或多個其他目錄,重命名次數最多的目錄“獲勝”。
- b) 避免對路徑進行目錄重命名檢測,如果該路徑是合並任一側的重命名源。
- c) 僅當歷史的另一側是進行重命名的人時,才對目錄應用隱式目錄重命名。
git log --pretty=email
將文件的提交歷史轉換為電子郵件補丁git am
將這些文件(電子郵件)轉換回 Git 提交以保留歷史記錄。示例:提取file3
、 file4
和file5
歷史記錄
my_repo
├── dirA
│ ├── file1
│ └── file2
├── dirB ^
│ ├── subdir | To be moved
│ │ ├── file3 | with history
│ │ └── file4 |
│ └── file5 v
└── dirC
├── file6
└── file7
設置/清理目的地
export historydir=/tmp/mail/dir # Absolute path
rm -rf "$historydir" # Caution when cleaning the folder
以電子郵件格式提取每個文件的歷史記錄
cd my_repo/dirB
find -name .git -prune -o -type d -o -exec bash -c 'mkdir -p "$historydir/${0%/*}" && git log --pretty=email -p --stat --reverse --full-index --binary -- "$0" > "$historydir/$0"' {} ';'
不幸的是,選項--follow
或--find-copies-harder
不能與--reverse
結合使用。 這就是為什么在重命名文件時(或重命名父目錄時)歷史會被刪除的原因。
電子郵件格式的臨時歷史記錄:
/tmp/mail/dir
├── subdir
│ ├── file3
│ └── file4
└── file5
Dan Bonachea建議在第一步中反轉 git log 生成命令的循環:不是每個文件運行 git log 一次,而是在命令行上使用文件列表運行一次,並生成一個統一的日志。 這種方式修改多個文件的提交在結果中保持單個提交,並且所有新提交保持其原始相對順序。 請注意,在(現在統一的)日志中重寫文件名時,這也需要在下面的第二步中進行更改。
假設您想在另一個倉庫中移動這三個文件(可以是同一個倉庫)。
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB # New tree
│ ├── dirB1 # from subdir
│ │ ├── file33 # from file3
│ │ └── file44 # from file4
│ └── dirB2 # new dir
│ └── file5 # from file5
└── dirH
└── file77
因此重新組織您的文件:
cd /tmp/mail/dir
mkdir -p dirB/dirB1
mv subdir/file3 dirB/dirB1/file33
mv subdir/file4 dirB/dirB1/file44
mkdir -p dirB/dirB2
mv file5 dirB/dirB2
您的臨時歷史記錄現在是:
/tmp/mail/dir
└── dirB
├── dirB1
│ ├── file33
│ └── file44
└── dirB2
└── file5
更改歷史記錄中的文件名:
cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'
你的另一個回購是:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
└── dirH
└── file77
從臨時歷史文件中應用提交:
cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am --committer-date-is-author-date
--committer-date-is-author-date
保留原始提交時間戳( Dan Bonachea的評論)。
你的另一個回購現在是:
my_other_repo
├── dirF
│ ├── file55
│ └── file56
├── dirB
│ ├── dirB1
│ │ ├── file33
│ │ └── file44
│ └── dirB2
│ └── file5
└── dirH
└── file77
使用git status
查看准備推送的提交量:-)
要列出已重命名的文件:
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'
更多自定義:您可以使用選項--find-copies-harder
--reverse
或--reverse
完成命令git log
。 您還可以使用cut -f3-
和cut -f3-
complete pattern '{.* => .*}'
刪除前兩列。
find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'
我遇到了“在不丟失歷史記錄的情況下重命名文件夾”的問題。 要修復它,請運行:
$ git mv oldfolder temp && git mv temp newfolder
$ git commit
$ git push
重命名目錄或文件(我對復雜情況了解不多,所以可能會有一些警告):
git filter-repo --path-rename OLD_NAME:NEW_NAME
在提到它的文件中重命名目錄(可以使用回調,但我不知道如何):
git filter-repo --replace-text expressions.txt
expressions.txt
是一個文件,里面充滿了像literal:OLD_NAME==>NEW_NAME
這樣的行(可以將Python 的RE 與regex:
或glob 與glob:
)。
要重命名提交消息中的目錄:
git-filter-repo --message-callback 'return message.replace(b"OLD_NAME", b"NEW_NAME")'
也支持 Python 的正則表達式,但它們必須用 Python 手動編寫。
如果存儲庫是原始的,沒有遠程,則必須添加--force
以強制重寫。 (在執行此操作之前,您可能希望創建存儲庫的備份。)
如果您不想保留引用(它們將顯示在 Git GUI 的分支歷史記錄中),則必須添加--replace-refs delete-no-add
。
我按照這個多步驟過程將代碼移動到父目錄並保留歷史記錄。
第 0 步:從 'master' 創建一個分支 'history' 用於保管
第 1 步:使用git-filter-repo工具重寫歷史記錄。 下面的這個命令將文件夾 'FolderwithContentOfInterest' 移動到一個級別並修改了相關的提交歷史
git filter-repo --path-rename ParentFolder/FolderwithContentOfInterest/:FolderwithContentOfInterest/ --force
第 2 步:此時 GitHub 存儲庫丟失了其遠程存儲庫路徑。 添加遠程參考
git remote add origin git@github.com:MyCompany/MyRepo.git
第 3 步:在存儲庫上拉取信息
git pull
第四步:連接本地丟失分支和源分支
git branch --set-upstream-to=origin/history history
步驟 5:如果出現提示,解決文件夾結構的合並沖突
第 6 步:推!!
git push
注意:修改后的歷史記錄和移動的文件夾似乎已經提交。 enter code here
完畢。 代碼移動到父目錄/所需目錄保持歷史完整!
雖然作為 Git 的核心,Git 管道不會跟蹤重命名,但如果您願意,您與 Git 日志“瓷器”一起顯示的歷史記錄可以檢測到它們。
對於給定的git log
使用 -M 選項:
git log -p -M
使用當前版本的 Git。
這也適用於git diff
等其他命令。
有一些選項可以使比較或多或少嚴格。 如果您重命名文件而不同時對文件進行重大更改,則 Git 日志和朋友可以更輕松地檢測到重命名。 出於這個原因,有些人在一次提交中重命名文件並在另一次提交中更改它們。
每當您要求 Git 查找文件被重命名的位置時,CPU 使用都會產生成本,因此是否使用它以及何時使用取決於您。
如果您希望始終在特定存儲庫中使用重命名檢測報告您的歷史記錄,您可以使用:
git config diff.renames 1
從一個目錄移動到另一個文件進行檢測。 下面是一個例子:
commit c3ee8dfb01e357eba1ab18003be1490a46325992
Author: John S. Gruber <JohnSGruber@gmail.com>
Date: Wed Feb 22 22:20:19 2017 -0500
test rename again
diff --git a/yyy/power.py b/zzz/power.py
similarity index 100%
rename from yyy/power.py
rename to zzz/power.py
commit ae181377154eca800832087500c258a20c95d1c3
Author: John S. Gruber <JohnSGruber@gmail.com>
Date: Wed Feb 22 22:19:17 2017 -0500
rename test
diff --git a/power.py b/yyy/power.py
similarity index 100%
rename from power.py
rename to yyy/power.py
請注意,這在您使用 diff 時都有效,而不僅僅是使用git log
。 例如:
$ git diff HEAD c3ee8df
diff --git a/power.py b/zzz/power.py
similarity index 100%
rename from power.py
rename to zzz/power.py
作為試用,我在功能分支中的一個文件中做了一個小改動並提交了它,然后在主分支中我重命名了文件,提交了,然后在文件的另一部分進行了小改動並提交了。 當我轉到功能分支並從 master 合並時,合並重命名了文件並合並了更改。 這是合並的輸出:
$ git merge -v master
Auto-merging single
Merge made by the 'recursive' strategy.
one => single | 4 ++++
1 file changed, 4 insertions(+)
rename one => single (67%)
結果是一個工作目錄,文件重命名,兩個文本都進行了更改。 因此,盡管 Git 沒有明確跟蹤重命名,但它仍有可能做正確的事情。
這是對舊問題的較晚答案,因此當時其他答案對於 Git 版本可能是正確的。
首先創建一個僅重命名的獨立提交。
然后將文件內容的任何最終更改放入單獨的提交中。
只需使用以下命令移動文件和舞台:
git add .
在提交之前,您可以檢查狀態:
git status
這將顯示:
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: old-folder/file.txt -> new-folder/file.txt
我使用 Git 版本 2.26.1 進行了測試。
摘自GitHub 幫助頁面。
我移動文件然后做
git add -A
將所有已刪除/新文件放入存儲區。 這里git意識到文件被移動了。
git commit -m "my message"
git push
我不知道為什么,但這對我有用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.