Alan Tsai 的學習筆記


學而不思則罔,思而不學則殆,不思不學則“網貸” 為現任微軟最有價值專家 (MVP)、微軟認證講師 (MCT) 、Blogger、Youtuber:記錄軟體開發的點點滴滴 著重於微軟技術、C#、ASP .NET、Azure、DevOps、Docker、AI、Chatbot、Data Science

[git]重新命名檔案的方式和如何在log檢查到改檔名前的歷史記錄

2017-06-18 Sunday
git

how to properly rename in git and show the log of previous rename

當持續在改檔案內容,容易發生當初為這個檔案取的名字已經不適合描述目前內容的情況,所以會對檔案名稱做修改。

最好的例子就是重構,當重構的時候很容易發生class名稱變換,這個時候會一起調整class的檔案檔名避免未來不好找。

不過當有天需要追蹤那個檔案的變更歷史的時候,會突然發現,只有 從新檔名那段開始有記錄,而舊檔名的記錄則看不到。這個對於從svn轉過來的使用者來說是無法理解的。

在這篇將會針對這個常見的問題來談到解決的方式和為什麼git會是這樣運作。

rename以前的log不見了
rename以前的log不見了

問題重現

基本上我們可以用以下的方式來重現這個問題:

  1. 先建立一個檔案 file.txt
  2. 把這個檔案commit上去
  3. 把檔案改名字change.txt
  4. 這個時候看狀態會變成 file.txt在被刪除狀態,而change.txt則是untrack的狀態
  5. add 加入到index (注意到這邊狀態變成了 renamed
  6. 這個時候用小烏龜對那個檔案做看 log - 會發現歷史不見了

建立的指令

建立的指令

log看不到建立出file.txt的記錄
log看不到建立出file.txt的記錄

解決方式

如果今天只是要看到包含改檔名前的日誌,下面是做法 - 如果對於為什麼會發生和git重命名的做法有興趣在往下看。

指令的方式

其實在git裡面針對某個檔案看日誌的時候,可以使用指令follow來包含變更名稱前的日誌:

git log -follow filex.txt

在各個ui裡面也有類似的設可以啟用:

TortoiseGit log 啟用方式

選取要檢查的檔案(以我們例子是change.txt),呼叫log的部分並且在最下面第3個按鈕 Walk Behaviour -> Follow Renames就可以看到。下面截圖就是有開啟的狀態,可以看到最早file.txt的建立也有看到了:

有follow參數的log
有follow參數的log

TortoiseGit blame啟用方式

如果對一個檔案做blame - 下面的log清單也需要開啟在 View -> Folle Rename才會看到

在blame日誌也包含rename
在blame日誌也包含rename

SourceTree log 啟用方式

在log裡面選change.txt,點右鍵,選擇 Log Selected,在左下角有個Follow renamed file

開啟單一檔案log開啟follow renamed files
source tree裡面啟用方式

發生原因 - git如何處理檔名變更

大部分第一次遇到這種情況都是在想,會不會是rename方式錯誤,是不是需要透過git的指令來rename就沒問題?

如果深入在研究,會發現git有提供一個指令叫做git mv(TortoiseGit也有一個對應的刪除功能)- 但是如果你用那個指令來刪除,會發現其實還是看不到日誌。

要了解這個,首先有件事情一定要記得的是,git和其他版控不同在於,git記錄的是內容變更,而不是檔案變更

所以其實git不在乎檔名有沒有變更,他看的是檔案內容的變更。所以在上面我們沒有透過git指令改名的時候,一開始在status顯示的是有個檔案被delete,然後有個檔案屬於untrack,但是當我們加到staging之後 (透過 git add)自動切換變成renamed狀態。

add之前顯示的狀態

add之前顯示的狀態

image
add之後變成rename

會有這種混搖是因為git觸發檢查的時間點不同,當status的時候還沒比對過資料,而在add等於進入準備建立commit,所以一比對,發現只是檔名不同(在git來說就是在不同的tree),因此它自動識別原來這是一個檔名變更。

這個時候有些gui就容易誤導使用者

以TortoiseGit來說,他沒有staging概念(應該說有,但是commit的那個畫面沒有這種感覺)會造成使用者擔心為什麼不是顯示重改檔名,造成他們不敢commit:

imagesourcetree commit畫面
(左圖)TortoiseGit的commit畫面 - 看起來和status未add之前看到的內容,(右圖)sourcetree預設有個stage畫面,因此看起來比較明顯

在git裡面重新命名怎麼做才正確

  • 當檔案內容沒有修改或者少量修改的時候,就依照喜歡的方式改檔名就好
  • 如果檔案內容有大幅度修改,建議改檔名和修改內容分兩個commit(一個改檔案內容,一個改檔名)
詳細說明如下:

有沒有必要呼叫 git mv

答案是:不需要

git mv = 改完檔名然後呼叫 git add

因為Windows使用者習慣直接透過檔案總管改檔名,因此專門為了省一個git add的指令而特意去呼叫git mv有點不方便,只是要知道顯示一個delete和一個untrack是正常即可。

當檔案內容和檔案名稱都有修改的情況下是否會不同?

假設檔案內容只改了一些 - git會夠聰明的知道,其實這是一個修改檔名+修改檔案內容。

但是當修改檔案內容很多也有修改檔名,git add還是會顯示一個delete和一個untrack

如果進入那種狀態,會有以下壞處:

  1. 未來在看commit的時候,不那麼直覺能夠看出是一個檔名修改+檔案內容修改
  2. -follow的參數就沒有用了 - 所以換檔名前的log沒法一次顯示

    當有檔案修改又有檔名修改無法用follow和網上說法有點不同

    以我解git是記錄檔案內容變更,理論上來說就算變成 一個delete一個untrack也應該要能夠用follow追蹤的到才對,但是實際上我測試的時候不行。

    這個和網路上的一些說法不一致,因此為了未來看起來方便和容易追蹤,建議還是分兩個commit,一個改檔名,一個改檔案內容。

    假設commit已經是一個delete和一個untrack,可以用rebase把它拆開成為兩個commit,一個改檔名,一個改檔案內容。


結語

在第一次接觸到git的rename的時候容易進入到以前以檔案修改為記錄的版控思維影響,因此希望這篇能夠讓了解發生的原因,並且記得:

  1. 如果logblame需要看到包含改檔名前的記錄,記得找找和follow有關的關鍵字做切換
  2. 改檔名不用擔心,可以直接用檔案總管修改然後add、commit即可
  3. 如果檔案有大改,然後又要改檔名,建議分成兩個commit

參考資料

  1. How to stage a rename without subsequent edits in git?
標籤:

如果文章對您有幫助,就請我喝杯飲料吧
街口支付QR Code
街口支付QR Code
台灣 Pay QR Code
台灣 Pay QR Code
Line Pay 一卡通 QR Code
Line Pay 一卡通 QR Code
街口支付QR Code
支付寶QR Code
街口支付QR Code
微信支付QR Code
2017-06-18 Sunday
git
comments powered by Disqus