2014/12/26

使用 Git 來備份每天的工作成果

備份的重要性

很多人對於版本控管系統的刻板印象都是認為只有軟體開發那種高科技領域才用得到,可是事實上在很多中小型的開發團隊中,版本控管也不見得被應用在開發流程中。有不少的開發人員甚至還是使用人工的方式在做備份,例如:將每天的工作成果以目錄為單位備份並使用日期做為目錄名稱以協助辨識,更有不少人是完全不做備份、交由老天來決定。

而生活總是充滿變化與挑戰的,先不說工作的成果有可能因為一個意外就一瞬間化為烏有,在工作的過程中就有可能會面臨很多的選擇與決定。每一個決定都可能會對最終的成果造成重大的影響、左右成敗,但不見得在每一個抉擇的當下都有足夠的經驗來判斷哪一個決定對結果是好的。

就像 Nicolas Cage 主演的電影 Next 中,劇情後段尋找炸彈的那個場景特效一樣,尋找炸彈的路徑有很多條,每一條路徑又有很多個分歧點,產生很多不同的可能性。可惜的是我們並沒有像劇中的主角有預知能力,沒有辦法選擇一條可以產出最好的結果路徑。不過,真的不行嗎?的確我們不俱有預知的能力,但是我們卻可以在事後發覺當初的決定不恰當時,回到決定的那個路徑點重新做選擇,這就是版本控管的概念。

所以並不是只有軟體開發的工作才有需要應用版本控管系統,只要是工作產出有涉及到電子檔案的儲存,就應該要考慮使用版本控管系統來保障工作的成果。有了版本控管系統在工作出現抉擇時,相對要承受的心理壓力也會小了許多,因為就算選定的結果不如預期還是有個備援方案可以簡單地重新做出選擇。

以程式開發的一段過程來做為例子:假設有一段寫好了的邏輯,但後來發現有效能上的問題,想要針對這個問題調整程式。一來心理上會捨不得目前的成果,二來也許新的邏輯並不是那麼有把握可以改善效能、或是不確定會對其他的功能產生什麼衝擊。這時如果可以先把整個程式碼現在的狀態備份下來,就可以毫無顧忌地進行構想中的修改,一旦出現了什麼問題還可以再還原回來、做不同的嘗試。

當然如果需求只有備份,前面提到的人工方式也沒有什麼不好,那使用版本控管系統的誘因是什麼?就像前面提到的情境,到終點前的分歧點永遠不會只有一個,檔案結構的確也可以應付樹狀分支的備分結構,在管理上卻是人力和時間的不斷投入。既然已經有現成的系統可以協助,為什麼不使用系統、讓時間和精力投注在更有價值的事務上?

有開發過 Native iOS 應用程式的人應該都知道 Xcode 有提供 Snapshot 的功能,我覺得這個功能很實用,我在其他的開發工具上還沒有看過。當我遇到前面舉例的情況時,就會使用 Snapshot 把程式碼的狀態備份起來,在持續工作的過程中如果想要參照原本程式的寫法,還可以使用差異比對的功能來檢視問題。但由於每一次 Snapshot 的項目是沒有前後關係,當情況變得複雜時就會出現追蹤不易的問題,會被強迫由原本的思緒中抽離、以進行一些管理的動作,在效率上來說並不是很理想。所以 Snapshot 大多只是應用在小幅度的修改,或是用來嘗試不熟悉的函式,大幅度的修改還是會回到版本控管的機制中處理。

資料庫系統的 Transaction Log 機制也是一個例子,由於 Transaction Log 是把對資料庫內資料的變化做循序式的記錄,跟人工備份的模式差不多。一旦出現人為操作的失誤導致資料異常,通常 DBA 都會先問清楚發生的時間點,再把資料庫由上次全備份的狀態以 Transaction Log 還原到附近的時間點,再逐步的套用 Log 中的指令,以排除有問題的資料修改。像這樣一次只發生一個操作失誤還可以應付,一旦是多重的失誤要由 Transaction Log 來回復到期望的狀態就不是件簡單的事了。

為什麼選擇 Git

在早期版本控管的系統大多定位在多人協同合作,都是以 Centralized 的模式為主,一定要有一台提供版本控管服務的伺服器才能運作,這應該也是一般人望之卻步的原因。然而,隨著 DVSC 概念的興起,以目前最熱門的 Git 功能來看,要應用在個人的工作版本管理上似乎就不再那麼遙不可及。

不過平心而論,以 Git 的親和度對很多非科技從業人員來說算不上是友善。在操作上大部份還是以指令輸入為主,圖形介面的選擇及整合上還是有很大的空間。如果非科技的從業人員覺得自己的工作成果真的很重要,想要使用 Git 來做為版本控管的機制,可能要有會投入一段不算短學習時間的心理準備。所幸 Git 的資源還算豐富,甚至還有翻譯好的中文教學文件

在這裡要小小的抱怨一下,有很多的專家可能會認為有能力輸入指令完成作業才是專業的表現,雖然我自己也是資訊從業人員,但是對於這種以虐待自己為豪的心態一直不太能理解。這樣的人在不同領域好像都有,例如汽車的自動排擋設計明明就是一項便利、好用的輔助功能,但是還是會有一批人鼓吹純手排才是王道的理念。對我來說圖形介面和指令就像金頭腦裡的選擇題與問答題,怎麼看都是選擇題比較容易吧,我也不是那位當了醫師的孔子後代啊。

抱怨歸抱怨,因為工作上的需要,再加上選擇不多、也考慮到網路上可取得的資源,Git 還是目前的首選。

Git 的運作概念

在學習 Git 的運作過程中,由於過去一直都是使用 Centralized 的版本控管系統,花了一點時間來適應和轉換原本的觀念。Git 記錄版本的模式是根據操作人員的指示,把特定時間點的所有檔案內容保存下來形成一個個的 Commit,並且依據產生的先後關係串成一條一條的 Commit 鏈。

每一個 Commit 鏈就像之前提到的電影特效,是把你所嘗試過的路徑記錄下來。但也不要想得太過美好,記錄的時間點是由操作的人決定並不是即時的,即便是使用 OS X 的 Time Machine 來做備份也是有時間差的,並不是無段式的。有在玩 RPG 的人,就把 Commit 當做是存檔來看就行了。有做存檔,萬一角色掛了或卡關才有辦法召喚 S/L 大法。

換個說法,想像你目前正在一個巨大迷宮的入口,而你手上可以利用的只有繩子。想要順利的走出迷宮可以做的就是:在迷宮的入口處綁上繩子的一頭,繩子其他部份留在手上、邊走邊放,做為走過路徑的記錄。迷宮很大、岔路很多,你不想試完每一種可能,所以你就使用你的第六感、在你覺得有可能的分岔點於繩子上打個結個為記錄,以方便之後回來做不同方向嘗試的起點,這個結在 Git 的術語裡就是 Commit。但每一個結都長的一樣,怎麼區分?所以你可能會想要在結上貼個貼紙,寫個簡單的說明,這個就是 Tag。

正常的情況之下,如果走到死胡同,則會再循線回到最近的一個有結的岔路口,在岔路口用另一條繩子綁在原本的繩子上,用同樣的方式留下走過的記錄,直到碰到另一個死胡同再重複之前的程序。在岔路分出一條不同的線的動作就叫 Branch,如果岔路之後不是走到死胡同,而是遇到之前放的另一條線則是叫 Merge。

如果你突然心靈福至,覺得之前的某一個岔路有可能走出迷宮,於是你想先暫時放下目前這條線的探索,試試看另一條線。幾次之後發現了一個意想不到的情況,每一條線長得都差不多,已經開始分不清楚哪一條線是哪一條。所以必須要在每一條線的最後一個繩結貼上貼紙。每一條線的最末端增加了一個結,就把貼紙移到新的繩結上,用來代表每一個分支的最新狀態。這種貼紙在 Git 中就是 Branch, Git 所謂的 Branch 就資料的設計來看,嚴格來說並不是指一整條線,是某一個時間點的檔案內容狀態。同時 Git 會設定其中一條線的名字是 master,做為初始時預設產生的 Branch。如果要問為什麼貼紙一定要貼在繩結上?別忘了,之前提過 Git 還沒有神奇到可以提供無段式的資料存檔快照,所以有記錄才能做關連,而切換分支的這個動作在 Git 中稱為 checkout。

剛才有提到二條以上的線可能會 Merge 到同一個繩結上,如果這個繩結又正好是最後一個,以之前一段說明的邏輯,這個繩結上就會有二個以上用來表示分支的貼紙,當然還可能會有自訂、不是用來表示分支的貼紙。像這種代表分支的貼紙在 Git 中是由系統控制的,Git 會替我們決定要把貼紙移到哪一個繩結上。另外還有一個特別的貼紙叫 HEAD,是用來表示目前工作中檔案內容狀態的 Commit 位置。如果 Git 只是單純地把貼紙由前一個繩結移到新的繩結上,術語上叫 Fast forward,顧名思義就是在沒有更動分支與 Commit 的結構下,快速的推進了版本記錄。

在先前有提到 Branch 和 Tag 都是用來指向某一個 Commit,在 Git 中 Commit 是由 SHA1 所產生的字串用來當做唯一識別碼,很多的 Git 指令參數都可以自動識別所輸入的參數值是 Branch、Tag 或是唯一識別碼,並指向對應的 Commit。由於唯一識別碼很長,在用來做為參數時可以不用全部輸入,只要輸入開頭的斷個字元即可,例如只打前四個字元。

在閱讀教學文件以了解 Git 的運作邏輯的過程中,看到很多用來表示 Repository 狀態的圖例,像這個。一開始以為箭頭標示的方向是時間演進的歷程,所以在看圖理解文字意義時會有些障礙。其實並不是,後來才發現跟 UML 的類別圖概念比較接近,是以程式資料結構的角度,用來表達每一個 Commit 的內容是源自於哪一個 Commit。

除了之前提到的概念之外,還有一個概念是之後在操作 Git 時會很頻繁地使用到,那就是 Staging Area。在看完文件的說明之後,感覺上這個過程像購物網站的購物車,當你對某個商品有興趣時,如果只是勾選起來並選好數量,會發現其實還不能結帳。這時必須要先把想要買的商品加到購物車裡,購物車就是 Git 裡的 Staging Area。同樣地,當你覺得你想要加購商品,你得再把加購的資料送到購物車裡。所以,當有對檔案進行修改後,要使用 add 指令將檔案加入 Staging Area 中。而此時下 commit 指令所保留的是你執行 add 時當下的檔案,而不是最後的版本。也就是如果你 add 完之後,又再次修改同個檔案,commit 出去的檔案是 add 指令前的版本。

以上說明的內容只是用來便於理解 Git 的運作概念,如果有去閱讀過閱讀教學文件第九章的內容,會發現實際的情況和繩結跟貼紙的說法上還是有差距的。

Git 的備份規畫與環境設置

接下來要開始根據 Git 的特性做一些備份環境的規劃,首先是用來存放 Git 的 Repository 位置,為了簡化備份的程序,所有的工作檔案都規劃放在同一個目錄下,例如:Workspace。不同工作內容則可以使用 Branch 來做區隔,例如:daily/work1、daily/work2。這樣的做法可以避免設定太多的 Repository,在管理工作上不會太過複雜,也不用特別記錄什麼工作的 Repository 在哪個目錄下。做 Daily Backup 時也只需要在一個 Repository 下 Commit 的指令,就可以把一天工作的成果給保留下來。

就算是一天之中在不同的 Branch 間切換進行不同的工作,由於 Git 的特性,切換 Branch 前會先要求你清空 Staging Area 的內容,不管是 Commit 或放棄修改的內容,所以不致於會漏了做備份。如果不是工作告一段落,只是想要暫時切換到另的 Branch,待會又要再切回來,Git 也有提供對應的功能。

使用的指令示範如下:
  • git stash
    會先把 Staging Area 中修改過的內容暫存下來。
  • git stash list
    列出被暫存的清單。
  • git stash pop
    把最後一次暫存的內容再還原回 Staging Area。
  • git stash apply
    取出最後一次的暫存資料. 但是資料不移除。
  • git stash clear
    把所有暫存的資料都清除掉。
  • git reset HEAD <filename> 
    把指定的檔案從 Staging Area 狀態更改回 Unstaging 的狀態,如果檔案還沒 Commit 過則會是  Untracked,但是檔案內容仍然保持修改後的樣子。
  • git checkout <filename>
    把在 Unstaging 狀態的指定檔案內容還原到最後 Commit 的樣子。
  • git checkout -f
    將所有檔案都以最後一次 Commit 的版本覆蓋,有修改的內容都會被還原。
  • git checkout <commit ref>
    將所有檔案都以指定 Commit 的版本覆蓋,同樣地,有修改的內容都會被還原。如果使用 Tag 來指定 Commit,且不是 Branch 上最後的 Commit, 會出現 "detached HEAD" 的訊息,此時所取得的內容為快照,是不能更改檔案的內容。如果要更改,必須要在這個 Commit 上建立一個 Branch。

當然這樣的規畫是以個人備份的角度來考量,由於不同的工作專案都混同一個 Repository 裡,當所有的工作 merge 回 master branch 時會讓 master branch 的版本控管變得很複雜。所以多人協同合作要進行版本控管時,並不建議使用這樣的規畫,還是要依據專案的特性與分工上的需求來審慎地規劃 Repository。

決定好了 Repository 的位置後接下來就是要先對 Repository 做初始化的工作,只要切換到預計做為 Repository 的目錄,並且執行以下指令即可完成初始化的工作: 使用的作業系統沒有內建 Git 時,要先進行安裝的程序,相關的資訊可以參考教學文件中的說明

如果 Repository 所在的目錄於就初始化之前已經有檔案了,可以先做一次 Commit,指令如下所示: 但是在 Commit 之前如果有一些程式產生的暫存檔,像是 Word 會在編輯時會產生一些 ~ 開頭檔名的檔案、或是編譯程式時會產生很多的中介檔,應該要先按照教學文件的說明把檔案給預先排除掉。

這項功能主要是用在工具所產生的暫存檔,因為這類型的檔案變動很頻繁、被刪除了並不影響工作。如果將這類的檔案列入版本控管,會有大量增加儲存空間的疑慮。像是開發工具在編譯檔案時會出現很多中介檔,刪掉並不影響開發工作的進行,只要再重新編譯檔案就會再產生。所以這類的檔案最好是列入忽略清單中,以有效控制 Repository 的大小。不過,需注意的是要被忽略的檔案,但是如果過濾條件是在被 Git 列入追蹤之後才加入,就算符合設定的條件,檔案並不會被自動解除追蹤、要手動移除,所以前一段才會提到要在 Commit 之前進行。

如果真的有檔案在 Checkin 之後,不希望再被 Git 追蹤,可以使用以下的指令來達成: 以下的指令可以顯示被 Git 所追蹤的檔案清單: 如果以上的指令所顯示的中文檔名出現奇怪的編碼時,可以使用以下的指令來嘗試解決問題: 製作 .gitignore 的細節可以參考官方網站的說明,如果把 .gitignore 的檔案放在 Repository 的目錄下,則這個設定的內容可以被 Checkin 並 Push 到遠端與同一個工作小組的成員共用,但只影響這個 Repository。如果要讓所有 Repository 都套用同一個設定,可以使用以下的指令: 以上示範的指令中,.gitignore_global 是共用設定的檔案名稱,檔名可以自行調整,共用設定一般的習慣位置都是放在登入帳號的 Home 路徑之下。

由於 .gitignore 在內容會隨著作業系統平台、使用的電子檔編輯工具而需要做不同的設定,尤其是在進行程式開發工作時經常會需要跨語言和工具。然而,要收集每一個語言編譯器或工具使用的暫存檔是一項很耗時的工作,而一般的情況下很少會去這麼深入的了解。所幸在 GitHub 上有提供了範本可供參考,在第一層列出了很多程式語言需要的設定,在 Global 的目錄下則是列出了相關工具及作業系統平台所需的設定。

另外還有一種情況是有些檔案希望被列入追蹤,但是又不希望被異動並上傳。像是應用在小組共用的初始設定,成員在 Clone 可以取得並且去修改,但所修改的內容並不會被 Commit。如此可以保障當新成員加入時,會使用小組預設的狀態或環境、減少新手問題,等新成員熟悉之後就可以自行依習慣調整,但又不會影響初始設定。
  • git update-index --assume-unchanged <file name>
    會讓 Git 忽略指定的檔案被修改的結果,並且不會被顯示在 git status 或 git diff 指令結果中,更不會被 Commit。
  • git update-index --no-assume-unchanged <file name>
    讓指定的檔案重新列入到被追蹤的範圍。


Git 的日常操作

完成了以上的設定就可以開始使用 Git 做為日常工作產出的備份機制,但是在進行任何的檔案編輯工作之前應該先建立並切換到 Branch,例如:daily,避免在 master 上進行。主要的考量是在於每天的備份會產生一到多個的 Commit,全部都串在 master 上會讓 master 的歷史記錄顯得很龐雜。所以如果把每天備份產生的 Commit 集中到另一個 Branch 去,等到有可以交付的版本再 Merge 回 master,相對的來說 master 的歷史記錄會比較有管理上的優勢。例如:今天客戶需要之前某次交付的檔案內容,就可以切回到 master 快速地在精簡的記錄中找到需要的 Commit。如果客戶還想要針對這個版本做調整,也可以直接建立出 Branch 以因應修改的需求。

如果 Git 是要用來做程式開發工作的備份,可以參考「A successful Git branching model」這篇文章的說明。以我的工作經驗來看,這篇文章中所做的 Branch 規畫已經可以滿足大部份在程式開發的過程中會出現的情境。

產生 Branch 指令如下:
  • git branch <branch name> [<commit ref>]
    產生一個新的 Branch,如果有提供第二個參數,會使用第二個參數所代表的 Commit 來產生 Branch,否則就使用目前的 Commit。
  • git checkout -b <branch name> [<commit ref>]
    和第一個一樣,不同的是第二個指令在產生完 Branch 之後會立刻切換到所新增的 Branch。

產生 Branch 時發現名字打錯了,可以使用以下的指令來進行修改: 如果覺得使用 Branch 太過複雜了,只想單純的使用 master 來做每天的備份,但是又想要有一個方法可以識別 Commit 的內容。這時候 Tag 就可以派上用場,當然也不是只有這個情境下才可以使用。在 Git 中 Tag 有二種類型:Lightweight 和 Annotated。Lightweight 照字面的意思就是輕量化的版本,只能輸入一串文字做為名稱,例如:v1.0、v2.3。而 Annotated 就可以有比較完整的內容,名稱、Email、日期和額外的附註內容。官網是建議使用 Annotated 類型的 Tag,因為在多人協同工作時可確保提供足夠的資訊。但是用來做為個人的備分工具,其實可以使用輕量化的版本就行了。

以下列出 Tag 相關的管理指令:
  • git tag
    可以列出目前可查詢的 Tag 清單。
  • git tag -l <filter>
    可顯示符合指定條件的 Tag 清單。
  • git tag <tag name>
    可以產生一個 Lightweight 的 Tag 在目前的 Commit 上。
  • git tag -a <tag name> -m <tag message>
    可以產生一個 Annotated 的 Tag 及對應的注釋內容。
  • git tag -a <tag name> -m <tag message> <commit ref>
    可以在指定的歷史 Commit 中新增 Tag 及對應的注釋內容。
  • git tag -s <tag name> -m <tag message>
    可以產生一個簽名過的 Annotated Tag 及對應的注釋內容。
  • git tag -v <tag name>
    可以驗證 Tag 簽名的正確性,執行時會需要有簽署者的公開金鑰。
  • git tag -d <tag name>
    刪除本地指定名稱的 Tag。
  • git tag <tag name> <name of other branch>
    會在指定的 Branch 中最後一個 Commit 上產生 Tag。

想要查詢曾經 Commit 的記錄,可以使用以下的指令:
  • git log 
    列出與目前 Branch 相關的 Commit 記錄。
  • git log --all
    列出所有的 Commit 記錄。
  • git log --stat --summary
    在 git log 顯示的資訊上,加列每個版本間的更動檔案和行數。
  • git log <file name>
    只列出有包含指定檔案被異動的 Commit 清單。
  • git log <directory> 
    只列出有包含指定目錄被異動的 Commit 清單。
  • git log --since="2 weeks ago"
    只列出最後這 2 週的 Commit 清單。
  • git log --pretty=oneline
    列出 Commit 清單時,每一個 Commit 只使用一行來顯示資訊。
  • git log --pretty=format:'%h was %an, %ar, message: %s'
    列出 Commit 清單時,每一個 Commit 以自訂的字串樣式來顯示資訊。
  • git log --pretty=format:'%h : %s' --graph
    列出 Commit 清單時,會有簡單的文字圖形化來呈現分支的狀態。
  • git log --pretty=format:'%h : %s' --topo-order --graph
    列出 Commit 清單時,依照主分支排序。
  • git log --pretty=format:'%h : %s' --date-order --graph
    列出 Commit 清單時,依照時間排序。

要檢視 Commit 中所保存的檔案內容,可以使用以下的指令:
  • git show <commit ref>[:<file name>]
    顯示指定 Commit 的檔案內容,如果有指定檔案名稱則只顯示指定檔案的內容。
  • git show HEAD
    顯示後最 Commit 的檔案內容。
  • git show HEAD^
    顯示後最 Commit 前一個版本的檔案內容。
  • git show HEAD^^
    顯示後最 Commit 前二個版本的檔案內容。
  • git show HEAD~4
    顯示後最 Commit 前四個版本的檔案內容。

當有需要對比對不同版本間的差異時,可以使用以下的指令:
  • git diff
    顯示工作中檔案與 Staging Area 間的差異。
  • git diff --cached
    顯示 Staging Area 與最後 Commit 結果間的差異。
  • git diff <commit ref> <commit ref>
    顯示二個指定 Commit 間的差異。
  • git grep "hello" <commit ref>
    搜尋指定 Commit 中所有的檔案內容中是否有 hello 字串。

在工作告一段落需要有一個可以交付的版本時,可以使用以下的指令把最新的 Commit 合併回 master 去: 如果在 Merge 的時候沒有使用 --no-ff 的參數,以之前規畫的模式,會讓 master 直接指向 daily 的最後一個 Commit,這並不是原本設計想要的結果。因為 Merge 之後的 Branch 結構就跟一開始沒有分出 Branch 的結果相同,只會有一條 Branch。使用了 --no-ff 的參數,會讓新增加出來的 Commit 分別指向 daily 的最後一個 Commit 及 Merge 前 master 所在的 Commit,如此 Branch 的結構上就會形成二條線。

或是覺得在工作告一段落之後,先前的每日備份已經沒有保留的必要,可以使用以下的指令來讓所有的 Commit 被壓縮成一個: 當想要挑選某幾天備份的內容來整合,可以使用以下的指令: 如果要挑選多個 Commit 合併為一個,可以加上 -n 指令。在執行指令時 Git 就不會先做 Commit,這樣可以不斷挑選要合併的 Commit,最後再以 git commit 指令送入 Repository 即可。


進行遠端 Repository 同步

使用 Git 來進行每日備份,雖然可以用來保障每天的工作成果,但還是有一定的風險存在著。畢竟所有的資料還是放在同一個磁碟裡,一旦磁碟出現問題,所有的資料一樣會消失不見。所以如果可以有一個遠端的 Repository 把 Commit 給推送過去,保障就可以再多加一層。

以下指令可以為 Repository 增加一個遠端 Repository 的連結資訊,並且進行上傳的工作: 'origin' 是 Git 預設的遠端名稱,每一個 Repository 都可以多個遠端的 Repository 建立連結資訊,並且給定一個字串做為識別。如果在 Push 時沒有指定哪一個遠端 Repository,Git 會預設代人號 origin 的連結資訊。

管理遠端 Repository 的相關指令如下:
  • git remote show
    顯示目前已設定連結資訊的遠端 Repository。
  • git remote update
    更新所有遠端 Repository 的 Branch 資訊。
  • git branch -r
    列出所有遠端 Repository 的 Branch 清單。

在指定要 Push 的 Branch 資訊時,標準的格式是 refs/heads/<branch name> 但可以省略為 <branch name>。同時 Push 時也需要指定本地和遠端的 Branch 資訊並以冒號分隔,如果名稱一樣也有省略的方式。所以以下的三個指令會達成相同的效果: 要從遠端下載 Commit 的資訊,可以使用以下的指令: 同樣地,如果不指定要下載哪一個遠端的 Repository,會使用預設的 origin。下載所取得的遠端 Branch 不會自動在本地端建立,要使用 checkout 來建立,像以下所示範的指令。如果本地端已經有對應的 Branch 則要以 Merge 的程序把 Commit 整合到本地的 Repository 中。 另外還有一個指令可以在下載後,於沒有衝突的情況下,一併對有對應關係的 Branch 執行 Merge 的程序,指令示範如下: 想要刪除遠端的 Branch,可以參考以下示範的指令: 有使用 Tag 來識別 Commit 時需要特別注意,Tag 預設是不會被 Push 到遠端。以下是 Tag 要 Push 到遠端的相關指令:
  • git push origin --follow-tags
    在 Push 的同時,一併將關連到的 Tag 上傳到遠端。
  • git push origin : <tag name>
    可以把指定的 Tag 給 Push 到遠端的 Repository 去。
  • git push origin --tags :
    一次 Push 所有遠端不存在的 Tag 到遠端去。
  • git push origin :refs/tags/<tag name>
    刪除遠端的 tag。
  • git fetch origin —-tags
    一次下載所有遠端的 Tag。

如果嫌每次 Push 都要記得下參數太麻煩,可以修改 Repository 裡 .git/config 檔案的 remote 資訊為以下的內容:

[remote "origin"]
    url = ...
    fetch = +refs/heads/*:refs/remotes/origin/*
    push = +refs/heads/*
    push = +refs/tags/*

修改過後,只要執行 git push origin 就可以把所有的 Branch 和 Tag 強制上傳,但如果只要傳特定的內容則要在參數中指定名稱把以上的設定覆寫掉。這個方法是在做個人工作備份的情境下使用,在多人分工的情境下並不建議使用,因為很容易不小心就把臨時性的 Branch 上傳,以致弄亂了 Server 上的 Repository 內容。

沒有執行過以下的指令,每次輸入 git push origin 都會出現警告訊息。在指令中 push.default 被設定為 matching 時會把二邊都存在的同名 Branch 全部進行同步,如果是 simple 則只會同步目前所在的 Branch。






0 意見:

張貼留言