Git 屬性
一些設定值(settings)也能指定到特定的路徑,這樣,Git 只對這個特定的子目錄或某些檔案應用這些設定值。這些針對特定路徑的設定值被稱為 Git 屬性(attributes),可以在你目錄中的 .gitattributes
檔內進行設置(通常是你專案的根目錄),當你不想讓這些屬性檔和專案檔案一同提交時,也可以在 .git/info/attributes
檔進行設置。
使用屬性,你可以對個別檔案或目錄定義不同的合併策略,讓 Git 知道怎樣比較非文字檔,在你提交或簽出(check out)前讓 Git 過濾內容。你將在這個章節裏瞭解到能在自己的專案中使用的屬性,以及一些實例。
二進位檔案
你可以用 Git 屬性讓其知道哪些是二進位檔案(以防 Git 沒有識別出來),以及指示怎樣處理這些檔,這點很酷。例如,一些文字檔是由機器產生的,而且無法比較,而一些二進位檔案可以比較 — 你將會瞭解到怎樣讓 Git 識別這些檔。
識別二進位檔案
某些檔案看起來像是文字檔,但其實是看做為二進位資料。例如,在 Mac 上的 Xcode 專案含有一個以 .pbxproj
結尾的檔,它是由記錄設置項的 IDE 寫到磁碟的 JSON 資料集(純文字 javascript 資料類型)。雖然技術上看它是由 ASCII 字元組成的文字檔,但是你並不想這麼看它,因為它確實是一個輕量級資料庫 — 如果有兩個人改變了它,你沒辦法合併它們,diff 通常也幫不上忙,只有機器才能進行識別和操作,於是,你想把它當成二進位檔案。
讓 Git 把所有 pbxproj
檔當成二進位檔案,在 .gitattributes
文件中加上下面這行:
現在,Git 不會嘗試轉換和修正 CRLF(回車換行)問題;也不會當你在專案中執行 git show 或 git diff 時,嘗試比較不同的內容。在 Git 1.6 及之後的版本中,可以用一個巨集代替 -crlf -diff
:
Diffing Binary Files
在 Git 1.6 及以上版本中,你能利用 Git 屬性來有效地比較二進位檔案。可以設置 Git 把二進位資料轉換成文本格式,然後用一般 diff 來做比較。
MS Word files
這個特性很酷,而且鮮為人知,因此我會結合實例來講解。首先,你將使用這項技術來解決最令人頭疼的問題之一:對 Word 文檔進行版本控制。每個人都知道 Word 是最可怕的編輯器,奇怪的是,每個人都在使用它。如果想對 Word 文件進行版本控制,你可以把檔案加入到 Git 倉庫中,每次修改後提交即可。但這樣做有什麼好處?如果你像平常一樣執行 git diff
命令,你只能得到如下的結果:
你不能直接比較兩個 Word 文件版本,除非人工細看,對吧?Git 屬性能很好地解決此問題,把下面這行加到 .gitattributes
文件:
當你要看比較結果時,如果檔副檔名是 ”doc”,Git 會使用 ”word” 篩檢程式(filter)。什麼是 ”word” 篩檢程式呢?你必須設置它。下面你將設定 Git 使用 strings
程式,把 Word 文檔轉換成可讀的文字檔,之後再進行比較:
This command adds a section to your .git/config
that looks like this:
現在 Git 知道了,如果它要在在兩個快照之間做比較,而其中任何一個檔檔名是以 .doc
結尾,它應該要對這些檔執行 ”word” 篩檢程式,也就是定義為執行 strings
程式。這樣就可以在比較前把 Word 檔轉換成文字檔。
下面展示了一個實例,我把此書的第一章納入 Git 管理,在一個段落中加入了一些文字後保存,之後執行 git diff
命令,得到結果如下:
Git 成功且簡潔地顯示出我增加的文字 ”(See Chapter 3)”。雖然有些瑕疵 -- 在末尾顯示了一些隨機的內容 -- 但確實可以比較了。如果你能找到或自己寫個 Word 到純文字的轉換器的話,效果可能會更好。不過因為 strings
可以在大部分 Mac 和 Linux 系統上運行,所以在初次嘗試對各種二進位格式檔進行類似的處理,它是個不錯的選擇。
OpenDocument Text files
The same approach that we used for MS Word files (*.doc
) can be used for OpenDocument Text files (*.odt
) created by OpenOffice.org.
Add the following line to your .gitattributes
file:
Now set up the odt
diff filter in .git/config
:
OpenDocument files are actually zip’ped directories containing multiple files (the content in an XML format, stylesheets, images, etc.). We’ll need to write a script to extract the content and return it as plain text. Create a file /usr/local/bin/odt-to-txt
(you are free to put it into a different directory) with the following content:
And make it executable
Now git diff
will be able to tell you what changed in .odt
files.
Image files
你還能用這個方法解決另一個有趣的問題:比較影像檔。方法之一是對 JPEG 檔執行一個篩檢程式,把 EXIF 資訊捉取出來 — EXIF 資訊是記錄在大部分圖像格式裏面的 metadata。如果你下載並安裝了 exiftool
程式,可以用它把圖檔的 metadata 轉換成文本,於是至少 diff 可以用文字呈現的方式向你展示發生了哪些修改:
如果你把專案中的一個影像檔替換成另一個,然後執行 git diff
命令的結果如下:
你可以很容易看出來,檔案的大小跟影像的尺寸都發生了改變。
關鍵字擴展
使用 SVN 或 CVS 的開發人員經常要求關鍵字擴展。這在 Git 中主要的問題是,你無法在一個檔案被提交後再修改它,因為 Git 會先對該檔計算 checksum。然而,你可以在檔案 check out 之後注入(inject)一些文字,然後在提交前再把它移除。Git 屬性提供了兩種方式來進行。
首先,你可以把某個 blob 的 SHA-1 checksum 自動注入檔案的 $Id$ 欄位。如果在一個或多個檔案上設置了此欄位,當下次你 check out 該分支的時候,Git 會用 blob 的 SHA-1 值替換那個欄位。注意,這不是 commit 物件的 SHA,而是 blob 本身的:
下次 check out 這個檔案的時候,Git 注入了 blob 的 SHA 值:
然而,這個結果的用處有限。如果你在 CVS 或 Subversion 中用過關鍵字替換,你可以包含一個日期值 -- 而這個 SHA 值沒什麼幫助,因為它相當地隨機,也無法區分某個 SHA 跟另一個 SHA 比起來是比較新或是比較舊。
因此,你可以撰寫自己的篩檢程式,在提交或 checkout 文件時替換關鍵字。有兩種篩檢程式,”clean” 和 ”smudge”。在 .gitattributes
檔中,你能對特定的路徑設置一個篩檢程式,然後設置處理檔案的腳本,這些腳本會在檔案 check out 前(”smudge”,見圖 7-2)和提交前(”clean”,見圖7-3)被執行。這些篩檢程式能夠做各種有趣的事。
Figure 7-2. “smudge” filter 在 checkout 時執行
Figure 7-3. “clean” filter 在檔案被 staged 的時候執行
這裡舉一個簡單的例子:在提交前,用 indent(縮進)程式過濾所有C原始程式碼。在 .gitattributes
檔中設置 ”indent” 篩檢程式過濾 *.c
文件:
然後,通過以下配置,讓 Git 知道 ”indent” 篩檢程式在遇到 ”smudge” 和 ”clean” 時分別該做什麼:
於是,當你提交 *.c
檔時,indent 程式會被觸發,在把它們 check out 之前,cat 程式會被觸發。但 cat
程式在這裡沒什麼實際作用。這樣的組合,使C原始程式碼在提交前被 indent 程式過濾,非常有效。
另一個有趣的例子是類似 RCS 的 $Date$ 關鍵字擴展。為了演示,需要一個小腳本,接受檔案名參數,得到專案的最新提交日期,最後把日期寫入該檔。下面用 Ruby 腳本來實現:
該腳本從 git log
命令中得到最新提交日期,找到檔案中所有的 $Date$ 字串,最後把該日期填到 $Date$ 字串中 — 此腳本很簡單,你可以選擇你喜歡的程式設計語言來實現。把該腳本命名為 expand_date
,放到正確的路徑中,之後需要在 Git 中設置一個篩檢程式(dater
),讓它在 check ou 檔案時使用 expand_date
,在提交時用 Perl 清除之:
這個 Perl 小程式會刪除 $Date$ 字串裡多餘的字元,恢復 $Date$ 原貌。到目前為止,你的篩檢程式已經設置完畢,可以開始測試了。打開一個檔,在檔中輸入 $Date$ 關鍵字,然後設置 Git 屬性:
如果把這些修改提交,之後再 check out,你會發現關鍵字被替換了:
雖說這項技術對自訂應用來說很有用,但還是要小心,因為 .gitattributes
檔會隨著專案一起提交,而篩檢程式(例如:dater
)不會,所以,它不會在所有地方都成功運作。當你在設計這些篩檢程式時要注意,即使它們無法正常工作,也要讓整個專案運作下去。
匯出倉庫
Git 屬性在將專案匯出歸檔(archive)時也能發揮作用。
export-ignore
當產生一個 archive 時,可以告訴 Git 不要匯出某些檔案或目錄。如果你不想在 archive 中包含一個子目錄或檔案,但想將他們納入專案的版本管理中,你能對應地設置 export-ignore
屬性。
例如,在 test/
子目錄中有一些測試檔,在專案的壓縮包中包含他們是沒有意義的。因此,可以增加下面這行到 Git 屬性檔中:
現在,當運行 git archive 來創建專案的壓縮包時,那個目錄不會在 archive 中出現。
export-subst
還能對 archives 做一些簡單的關鍵字替換。在第2章中已經可以看到,可以以 --pretty=format
形式的簡碼在任何檔中放入 $Format:$
字串。例如,如果想在專案中包含一個叫作 LAST_COMMIT
的檔,當運行 git archive
時,最後提交日期自動地注入進該檔,可以這樣設置:
執行 git archive
後,打開該檔,會發現其內容如下:
合併策略
通過 Git 屬性,還能對專案中的特定檔案使用不同的合併策略。一個非常有用的選項就是,當一些特定檔案發生衝突,Git 不會嘗試合併他們,而使用你這邊的來覆蓋別人的。
如果專案的一個分支有歧義或比較特別,但你想從該分支合併,而且需要忽略其中某些檔,這樣的合併策略是有用的。例如,你有一個資料庫設置檔 database.xml,在兩個分支中他們是不同的,你想合併一個分支到另一個,而不弄亂該資料庫檔,可以設置屬性如下:
如果合併到另一個分支,database.xml 檔不會有合併衝突,顯示如下:
這樣,database.xml會保持原樣。
Last updated