2017/5/30

善用 Android Studio 的異動管理功能

身為一個開發人員,每天的工作就是在不斷地異動 Source Code 中度過。增加新的、修改舊的、刪掉不要的,而每一個異動都會對應到特定的目的,像是為了新的需求、修改 Bug、重構程式等等。

很多時候,異動的目的在工作的過程中是混在一起的,例如開發新功能的同時,也有可能在修正之前的問題。在自己的工作環境中,這些異動混在一起通常都不會有什麼問題產生。只不過這些工作的成果終究是要交付出去的,而問題總在於這些目的卻不一定是在同一個時間點被交付。如果所有的異動都混在一起,要隔離出需要交付的部份,勢必要花費一番工夫才能辦得到。

而這樣的工作要靠人工來逐個 Block、逐個 File 來分辨識,不但耗時,同時也極有可能出現疏漏。因為一個修改就有可能牽涉到十幾個 Files,再加上 IDE 自動產生或管理的加一加可能就有成百上千之數。自己經手的異動都不一定能精確的掌握,更何況是數量在數倍、完全不是自己產生的內容。

人工應付不來,就得要靠工具的輔助。就如同在「如何寫好程式」一文中提到,善用工具是寫好程式的功課之一。以開發 Android 時所使用的 Android Studio 來說,雖然是由 IntelliJ IDEA Community 版本進化而來,但不代表功能上就很陽春。針對本文提到的問題,其實有內建了相當方便的功能,可以協助開發者解決這類工作上的問題。

Android Studio 提供的異動管理功能

Changelist

這是一個以 File 為單位,把異動內容給分門別類的功能。透過這個功能,可以把修改過的 File 進行分組。當有異動內容需要被交付時,可以直接以分好的組別為單位交付。像是要進行 Commit 時,則可以指定特定的 Changelist 來 Commit,不在分組內的 Files 則不會受影響。

要使用這個功能可以先進入 Version Control Tool Window,Menu 的位置在【View -> Tool Windows -> Version Control】。開啟之後可以看見如下圖示的內容:


在 Local Changes 的 Tab 中,可以看到有一個 Default 的字樣,這就是 Android Studio 預先產生好的 Changelist。如果沒有特別指定,所有被異動的 Files 都會被歸在這個 Changelist 之下。在操作上可以使用 Tool Window 中左方的按鈕來新增一個 Changelist,新增時可設定此 Changelist 為 Active,代表之後所有還沒被異動的 File,在異動後都會被歸到這個 Changelist 之下。

要在 Changelist 之間移動 File 也非常地直覺,可以使用拖拉項目的方式,或是在項目上按下滑鼠右鍵選擇【Move to Another Changelist...】即可。

當要進行 Commit 時,就可以在如下的「Commit Changes」畫面中,最上方的下拉清單選擇對應的 Changelist。

選擇不同的 Changelist 時,Changelist 的名稱會預設成為 Commit Message 的內容。

由於 Changelist 是以 File 為單位,所以會有一個限制是同一個 File 不能同時歸屬於二個 Changelist。一旦編輯了不在 Active Changelist 中的 File,Android Studio 就會出現以下的警告:


可以看見 Tab 上的檔名變成了紅色,這是 Android Studio 遇到異動衝突預設的反應,這部份可以透過點選畫面中最右方的按鈕來調整。

這時如果只是忘了切換 Active Changelist,可以選擇【Ignore】或是【Switch changelist】。但若真的是二個不同的修改項目都異動到同一個 File,那就得選擇一個適當的策略。

當修改的內容不會有交互的影響,也就是說二個修改項目的結果可以共存在同一個 File 之中,則可以選擇【Move changes】把 File 移到最先要被 Commit 的 Changelist 中。

反之,修改的內容是互斥的時候,就要先保留其中一個版本、還原回修改前的狀態後,再開始另一個項目的修改。這個方式在 Android Studio 中也有提供了對應的功能來達成,在這篇文章的稍後會提到。

Changelist 在使用的情境上,還可以用來區隔一定會修改,但卻沒有要 Commit 的 File。例如有一些程式運作時需要的設定檔,內容中記錄的是 Production 的參數,在開發時就必須要進行修改才能做偵錯。這時就可以預先新增好一個專用的 Changelist,把這類的 Files 在修改之後歸進去。未來在 Commit 時才不致一時疏忽,把開發環境的設定參數給 Commit,造成後續建置上的問題。


Label

Label 主要是作用在【VCS -> Local History -> Show History】的 Window 上,如下圖所示:
在 Window 的左側,可以看到第一個和第二個 History 項目中間,夾了一個 Sample Label 的文字,這個文字是使用【VCS -> Local History -> Put Label...】功能放上去的。

透過這個功能,可以在進行一些實驗性的調整之前,先標定好目前 Source Code 狀態。當調整不如預期時,就可以不用花精神去回想做了哪些的修改,再一一去做回復。有了 Label 就可以在 History 的清單中找到所標定的 Source Code 狀態,使用【Revert】的功能,直接回到調整前的狀態,相當地省事又有效率。


Shelf

字面上的意義就是架子,是一個用來擺放文件夾的架子。而文件夾則是前面所提到的 Changelist 的快照,所以當 Changelist 發生衝突時,就可以利用 Shelf 把 Changelist 當下的狀態保留起來,等到衝突的情況解決了之後,再把原本異動的內容還原回來。

要把 Changelist 放到架子上,可以從 Menu 中選擇【VCS -> Shelve Changes...】。
可以由上圖看到,畫面和 Commit 差不多。完成之後,會在 Version Control Tool Window 中多出一個 Shelf 的 Tab,同時被 Shelve 的 Files 會回到異動前的狀態。在 Shelf 的 Tab 上,可以管理 Shelve 過的項目,像是 Unshelve、Rename、Delete。

在 Unshelve 的過程中,如果沒有出現內容衝突,則會自動套用 Shelf 中保留的異動狀態。如果內容出現衝突時,則會顯示以下的 Window,要求決定所需套用的版本:
Shelf 除了應用在工作項目的切換之外,如果所開發的 Project 有多個 Branch,在 Branch 還沒有相互 Merge 之前,也可以使用 Shelf 來轉移、把異動過程套用在不同的 Branch 上。這一點在異動的 Files 數量龐大時,就可以顯現出效率上差別,一個批次就可以完成工作,不用再一個個 File 來比對,並且擔心是否有異動的內容遺漏了。


Patch

Patch 可以算是 Shelf 的外帶版本,外帶去哪?就是把異動的內容帶出 Android Studio 的環境之外。使用 Menu 中【VCS -> Create Patch...】的功能,可以把原本要新增到 Shelf 的項目,改為產生一個實體的 File。操作的畫面和 Shelve Changes 一模一樣,只是在按下 Window 上【Create Patch...】的按鈕後,會出現以下的畫面,以便指定 File 儲存的位置。
基本上 Shelf 的項目和 Patch 可以互換,在 Tool Window 中 Shelf 的項目上可以觸發 Create Patch 的動作,讓 Shelf 的項目轉成 Patch。反之,也可以在 Shelf 的 Tab 上 Import Patches 成為 Shelf 的項目。在產生 Shelf 項目和 Patch 時,還有一點最大的差異是 Patch 產生之後,並不會將內容回復到異動之前,而是維持修改後的狀態。

從 Menu 中選擇【VCS -> Apply Patch...】後,可以把 Patch 的內容套用回目前的工作環境中,套用的過程和 Shelf 差不多,遇到內容衝突時也同樣會出現相同的畫面,來決定要選用的版本。

在應用上,Shelf 的項目能做的 Patch 都能做,除此之外 Patch 還可以用來在不同的 Android Studio 環境之間移轉。可以用來將工作的狀態由公司的環境中移至家中的環境,以便在離開公司之後仍可接續未完成的部份。或者是可以把 Patch 交給不同的開發人員,用來進行協同合作、Review Code 等工作。


和版本控管工具的比較

如果在開發時使用 Git 做為版本控管的工具,其實以上的功能 Git 大多都可以做到。Android Studio 則是在原有的版本控管機制之外,提供不同的選項,對於不熟悉版本控管工具的人來說有莫大的幫助。而對於用慣了原本工具的人來說,要怎麼使用還是得看每個人的習慣、對工具的喜好程度。只不過在面對不同的情況之下,多學會一種工具的使用,在應對的策略上也能產生更多的彈性。