:::

虛擬機器轉移(V2V) : Windows 2003從VirtualBox到KVM

布丁布丁吃布丁

虛擬機器轉移(V2V) : Windows 2003從VirtualBox到KVM

image

跟前幾篇講實體機器虛擬化(Physical-to-Virtual,P2V)不同,這一篇要講的是虛擬機器VirtualBox 4.2.6)轉移到另一種虛擬機器Proxmox VE 1.9底下的KVM)的過程,亦即所謂的虛擬機器轉移(Virtual-to-Virtual,V2V,或是遷移migration)。這次轉移的客體作業系統(Guest OS)是Winodws 2003,儘管只是把VirtualBox上製作的VMDK虛擬機器硬碟映像檔換到KVM執行,但還是有很多細節需要注意。以下記錄大致上的過程供大家參考。


為什麼要做V2V?

目前我常用的免費虛擬機器技術有兩種:適合工作站使用的VirtualBox,以及適合伺服器用的KVM

我常常在自己的電腦用VirtualBox安裝作業系統來進行各種測試,VirtualBox可以調整的參數多、網路設定也很方便,操作起來很容易。另一方面,我會將開發到穩定狀態、不太需要常常修改的系統放置到Proxmox VE的KVM上,讓系統提供服務就好,我們平常就不太再去更動作業系統的基層設定。

從VirtualBox到KVM這之間的轉換,就需要V2V的協助。

V2V作法概述:利用共同支援vmdk硬碟格式

200px-Vmware.svg

多虧於VMware如此盛行,VMware制定的vmdk(Virtual Machine Disk,虛擬機器硬碟映像檔)幾乎成為了各家虛擬機器爭相支援的對象,這包括了VirtualBox與KVM。

image

因此簡單的想法,就是利用VirtualBox製作vmdk硬碟檔,再把它放到KVM環境中運作。

題外話,我寫這篇的時候才發現原來VirtualBox也支援建立了KVM主要用的虛擬機器硬碟格式QEMU增強硬碟,不過我想即使用QEMU來建立虛擬機器,轉移Windows 2003的時候依然會出現以下的問題。

Windows 2003的V2V步驟

經過了幾次失敗的嘗試之後,我參考了Alex寫的Converting Windows VMWare machines to KVM,將目前看來可行的作法整理如下。步驟中會用到兩個名詞:來源端是指VirtualBox虛擬機器,目的端則是指KVM虛擬機器。

1. 來源建立虛擬機器時用vmdk

image

就如前面所說,這篇V2V是利用共同支援的虛擬機器硬碟格式vmdk,所以建立VirtualBox虛擬機器時請選擇VMDK

image

硬碟大小其實只要符合作業系統最低需求即可,不過有趣的地方在於,似乎因為VMware建立vmdk預設的大小都是8GB,所以許多Virtual Appliance都是8GB。在這邊我們也參考一下傳統,設成8GB。

2. 來源端使用mergeide.reg

2013-02-11_142222

如果你的Windows 2003是裝在IDE控制器上,那麼第一個步驟就是使用mergeide.reg免空Box.net)。請下載這個檔案,然後在來源虛擬機器中執行(如上圖),調整虛擬機器的登錄檔即可。

mergeide.reg是來自於Alex教學中的一個登錄檔,但是連Alex也不知道原作者是誰。在此我也要感謝這位作者的提供,他的mergeide.reg真的幫了我們很大的忙。

以下是mergeide.reg的檔案內容:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\primary_ide_channel]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="atapi"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\secondary_ide_channel]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="atapi"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\*pnp0600]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="atapi"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\*azt0502]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="atapi"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\gendisk]
"ClassGUID"="{4D36E967-E325-11CE-BFC1-08002BE10318}"
"Service"="disk"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#cc_0101]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="pciide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_0e11&dev_ae33]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="pciide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_1039&dev_0601]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="pciide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_1039&dev_5513]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="pciide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_1042&dev_1000]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="pciide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_105a&dev_4d33]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="pciide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_1095&dev_0640]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="pciide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_1095&dev_0646]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="pciide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_1097&dev_0038]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="pciide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_10ad&dev_0001]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="pciide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_10ad&dev_0150]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="pciide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_10b9&dev_5215]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="pciide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_10b9&dev_5219]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="pciide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_10b9&dev_5229]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="pciide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_1106&dev_0571]
"Service"="pciide"
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_8086&dev_1222]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="intelide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_8086&dev_1230]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="intelide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_8086&dev_2411]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="intelide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_8086&dev_2421]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="intelide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_8086&dev_7010]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="intelide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_8086&dev_7111]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="intelide"

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CriticalDeviceDatabase\pci#ven_8086&dev_7199]
"ClassGUID"="{4D36E96A-E325-11CE-BFC1-08002BE10318}"
"Service"="intelide"

;Add driver for Atapi (requires atapi.sys in drivers directory)

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\atapi]
"ErrorControl"=dword:00000001
"Group"="SCSI miniport"
"Start"=dword:00000000
"Tag"=dword:00000019
"Type"=dword:00000001
"DisplayName"="Standard IDE/ESDI Hard Disk Controller"
"ImagePath"=hex(2):53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,44,00,\
52,00,49,00,56,00,45,00,52,00,53,00,5c,00,61,00,74,00,61,00,70,00,69,00,2e,\
00,73,00,79,00,73,00,00,00

;Add driver for intelide (requires intelide.sys in drivers directory)

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\IntelIde]
"ErrorControl"=dword:00000001
"Group"="System Bus Extender"
"Start"=dword:00000000
"Tag"=dword:00000004
"Type"=dword:00000001
"ImagePath"=hex(2):53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,44,00,\
52,00,49,00,56,00,45,00,52,00,53,00,5c,00,69,00,6e,00,74,00,65,00,6c,00,69,\
00,64,00,65,00,2e,00,73,00,79,00,73,00,00,00


;Add driver for pciide (requires pciide.sys and pciidex.sys in drivers directory)

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PCIIde]
"ErrorControl"=dword:00000001
"Group"="System Bus Extender"
"Start"=dword:00000000
"Tag"=dword:00000003
"Type"=dword:00000001
"ImagePath"=hex(2):53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,44,00,\
52,00,49,00,56,00,45,00,52,00,53,00,5c,00,70,00,63,00,69,00,69,00,64,00,65,\
00,2e,00,73,00,79,00,73,00,00,00

3. 來源端完全關機

image

接著要把來源端電腦完全關機。不能只是儲存機器狀態喔,必須是完全關機。

4. 目的端建立新的虛擬機器

image

本篇使用的目的端KVM是透過Proxmox VE 1.9來控管,這邊我們先建立一個新的虛擬機器。以下是需要注意的參數:

  • Type: Fullyvirtualized (KVM)
  • Image Format: vmdk
  • DIsk space (GB): 8 (同來源端)
  • Disk type: IDE (同來源端)
  • Memory (MB): 256 (同來源端)
  • Guest Type: Windows 2003

image

設置完成,請注意Proxmox VE自動配給的VMID,在此是158。

5. 來源端vmdk傳送到目的端

2013-02-11_145222

接下來就要把來源端的vmdk硬碟檔案傳送到目的端伺服器上。Proxmox VE在配置KVM虛擬機器時都有規定好的路徑,如果VMID為158,那麼158的虛擬機器硬碟檔案就放在以下路徑:

/var/lib/vz/images/158

請將來源端的vmdk上傳到該路徑底下吧。

6. 目的端替換硬碟

image

當vmdk上傳到目的端的指定路徑之後,我們切換到Hardware這一頁,你會發現剛剛上傳的vmdk已經在下面列表了。

image

接著我們先移除原本的硬碟,按下原本硬碟左邊的紅色向下鍵頭按鈕image,它會跳出DELETE選單。按下去之後,原本的硬碟會從虛擬機器上拔除,但是檔案還是在Proxmox VE裡面喔。

image

然後按下Hardware device list for VM 158左邊的紅色下向箭頭image,點下「Add a hard disk」。

image

Existing Disks選擇剛剛上傳的vmdk,然後按下add按鈕。

image

vmdk插上KVM的虛擬機器囉。

7. 目的端關閉KVM功能

image

切換到Options這一頁,然後在Disable KVM這選項後面打鉤,按下save儲存。(感謝來自neozeed的教學)

2013-02-11_161833

如果沒有做這個選項的話,目的端開機時會遇到「INTERNAL_POWER_ERROR」或其他各種問題的藍色當機畫面。

我找到現在仍不知道開關Disable KVM的意義何在。就連Griffon的Blog也是教說關掉KVM就好,目前也只能照做囉。

8. 目的端開機、檢查是否順利運作

image

回到Status,按下「Start」按鈕。

image

然後再按下Open VNC console,來看看虛擬機器現在的狀況。

image

順利的話,就進入Windows 2003囉。


結語:不要被藍白當機畫面打擊士氣了!

Image 11

這個藍白畫面是我在這次V2V過程中最常看到的景象,實在是非常無奈。我想到頭來還是Windows 2003綁定硬體,導致VirtualBox的IDE控制器轉換到KVM上的時候不合所導致的。出現這種藍白當機畫面,大多都是硬體上的問題。只是在不知道解法的時候總是會很無奈。希望這篇記錄的方法可以幫助大家在做V2V更加順利啊。

(more...)

Windows XP實體機器虛擬化 (P2V) 心得分享

布丁布丁吃布丁

Windows XP實體機器虛擬化 (P2V) 心得分享

image

最近因為家裡需求,我又研究起實體機器虛擬化(Physical-to-Virtual,P2V)。這次把實體機器的Windows XP轉換成了VirtualBox可運作的虛擬機器。在操作中遇到了些問題,最後終於能夠成功地解決。這些過程在此整理成小心得跟大家分享,

小故事:用實體機器虛擬化來保存舊有系統

我媽在工作的時候常常同時使用兩台電腦:一台是工作用的電腦,效能比較好;一台是舊電腦,跑Window XP SP2,Pentium 4、32GB、256 RAM,可是它卻裝著公司多年前購買的重要系統,工作時不得不使用它。她在同時操作這兩台電腦,總是有很多不方便,像是資料交換啦、舊電腦運作速度緩慢啦、必須同時開啟兩台實體機器浪費電啦,而且舊電腦也面臨著硬體大壽將近的難關。

因此,我就用之前我在Blog介紹的Acronis True Image備份檔案轉換成虛擬機器的方法,將我媽的舊電腦轉換成虛擬機器,放在比較快速的工作用電腦來運作。多虧VirtualBox客戶端高度整合化提供的滑鼠指標整合(Mouse pointer integration),操作虛擬化之後的舊電腦不僅比之前透過遠端桌面操作還要方便,而且在工作用電腦中運作速度還比原本的舊電腦要快得多呢。

不過轉換過程並沒有這麼順利,在此記錄我解決這些問題的作法,供他人參考。這一篇算是在補充之前寫的用Acronis True Image來實作實體機器虛擬化(P2V),因此細節作法請搭配著前一篇文來看,其他小問題就是看這一篇囉。

VMware vCenter Converter Standalone轉換時記得搭配sysprep使用

2013-02-10_215327

使用VMware vCenter Converter Standalone轉換Windows XP時,請務必配合Windows XP的大量部屬工具Windows XP Server Pack 2 Deployment Tools使用。下載後請解壓縮到以下路徑,Windows 7以上的路徑為:「C:\ProgramData\VMware\VMware vCenter Converter Standalone\sysprep\xp」,Windows XP的路徑為:「C:\Documents and Settings\All Users\Application Data\VMware\VMware vCenter Converter Standalone\sysprep\xp」。

在Windows 2003以前的Windows,它記錄著現在運作電腦的硬體環境,每次開機時都必須與此環境相符,這導致我們無法輕易地將Windows XP從實體機器轉換到虛擬機器運作環境上。使用大量部屬工具可以幫助我們解除這個限制,讓我們更容易把Windows XP放到虛擬機器中執行。

Windows 7似乎就比較沒有這方面的問題,而Linux本來就很容易相容於不同的硬體環境,要從實體機器轉換成虛擬機器更是輕而易舉。

VirtualBox設定注意事項

image

當我們利用VMware vCenter Converter Standalone把tib硬碟備份映像檔轉換成vmdk虛擬機器映像檔之後,我們就可以拿它來當成VirtualBox虛擬機器的硬碟,建立一台新的虛擬機器使用。只是這台虛擬機器嘚的預設參數可能還是無法讓他順利運作,通常還有一些參數配置需要我們去調整。

無法顯示Windows XP讀取畫面→啟用 IO APIC

image

Input Output APIC (IO APIC)是藉由使用超過16個中斷呼叫的功能而提高系統的運作效率,儘管VirtualBox提示說它可能會降低系統效能,但也有人說感覺不出來效能差異

image

一開始我在BIOS結束後,應該要顯示Windows XP讀取畫面時,它連畫面都跳不出來。後來我開啟這個選項後,它終於能夠順利進入Windows XP開機畫面。

image

終於順利讀取。

Windows XP讀取中進入藍白畫面:檢查硬碟設定是否為IDE

image

有時候建立VirtualBox虛擬機器時,它會自動把硬碟的控制器設為SATA。可是你要看原本實體機器裝在Windows XP的硬碟是那一類型,再來選擇對應的控制器。以舊電腦來說通常都是IDE控制器。這一選錯就是會不斷進入藍白當機畫面,請務必注意。

Windows XP讀取中還是進入藍白畫面:修改硬碟讀取類型為ICH6

image

我在上述檢查中確認是用IDE控制器無誤,但還是開機時進入藍白畫面當機。後來我才發現原來還要改IDE控制器的類型。VirtualBox預設選用PIIX4,VMware好像也是,但是我改成ICH6之後,Windows XP終於不會卡在藍白當機畫面,而能夠順利進入Windows作業系統。

因此我發現到,如果要做實體機器虛擬化的話,通常控制器的設定才是問題的關鍵吧。

如果還是不行?VirtualBox還有很多可以調整的地方

image

VIrtualBox有趣的地方就是可以設定的參數非常多,我自己也不是很清楚所有選項的意義何在。如果你在做實體機器虛擬化時遇到無法開啟的問題,不妨先檢查這些參數是否與實體機器環境相合,可能就是解決問題的關鍵。

然而令人擔心的是,比起VirtualBox,KVM似乎就沒有這麼多參數可以設定了。因此從實體機器轉換到KVM虛擬化的時候,可能還會遇到很多難關吧。

結語:實體機器虛擬化的價值

數位典藏的術語中,實體機器虛擬化(P2V)是屬於主要策略中的「模擬」(Emulation)保存方法。一方面既能將舊電腦中的完整系統做一個備份,另一方面又能夠讓舊系統再次提供服務。所以P2V其實不只是資管技術專業之一,其實他也是圖書資訊學領域會需要的技術喔!大家一起來研究虛擬機器吧!

(more...)

GitHub入門 Part.3 GIT版本控制實作教學

布丁布丁吃布丁

GitHub入門 Part.3 GIT版本控制實作教學

image

繼前一篇配置好git運作環境之後,這篇就是開始教大家實際上的操作。這包括了之前概念介紹時用到的Pull、Push、Commit、Conflict、Branch跟Check out等等常用的動作,並且跟每天上下班的情境做個結合,讓大家能夠把git版本控制融入到日常作業中。

  1. Part.1 版本控制介紹
  2. Part.2 工具安裝與環境配置
  3. Part.3 Git版本控制實作教學

投影片

(Google DocSkyDriveBox.net)

這次也跟前一篇一樣做了投影片方便講解,但是投影片沒辦法寫很多細節,細節的部份請看下面文字敘述。

上班開始工作:Pull

image

上班囉!每天開工的第一件工作,就是先把遠端伺服器最新的程式碼下載下來,更新本機端的程式碼吧。

image

在儲存庫的資料夾底下按右鍵,開啟「Git 同步…」。

image

點下「拉取」(Pull)按鈕。如果順利的話,你的本機儲存庫就會直接跟遠端伺服器同步,把程式碼都更新為最新的狀態了。

工作中的習慣動作:Commit

image

當我們為了某些工作修改了儲存庫的原始碼之後,你會發現有些檔案的標示不太一樣。

image那些被修改的檔案前面都會有紅色驚嘆號「」的標示。這表示他的檔案狀態被標示為「已修改」。

image其他「未修改」的檔案則是維持原本的綠色打鉤「ˇ」標示。

當我們修改告一段落之後,我們就可以把目前的版本狀態做一次commit。

image

首先是在儲存庫按右鍵,執行「Git 提交 –> "gh-pages"」。後面的「gh-pages」表示現在的分支,一般來說,通常會是「Master」主幹。

image

接著會跳出Commit對話視窗。上面訊息記錄你必須輸入一些文字,記錄你這次修改的內容與目的。中間的變動列表會列出所有你這次修改的檔案。確認無誤之後,按下確定。

image

出現藍色文字表示Commit成功。

image

Commit之後,剛剛呈現「已修改」紅色驚嘆號的「.gitignore」檔案已經恢復成「未修改」的綠色鉤鉤。

你可以時常Commit你的儲存庫,Commit的間隔越短越好,但是每次Commit的版本內容最好都是確定可以運作的狀態,而不是改到一半無法運作的系統。

到目前為止,每次Commit其實都是在你本機端進行而已。這跟遠端的GitHub沒有關係,也跟你團隊中其他夥伴的電腦檔案沒有關係。

下班收拾工作:Push

image

我們今天的工作已經告一個段落了,準備把修改過很多次的程式碼送回遠端伺服器,也就是GitHub上。這時候我們要做的動作就是Push。

image

跟Pull一樣的,我們在儲存庫資料夾按下右鍵,開啟「Git 同步…」。

image

不同的是,我們這次要做的動作是Push(推送),請按下「推送」按鈕開始把本機端的儲存庫送到遠端伺服器GitHub上。如果順利的話,你就可以看到藍色的成功文字。

還不能那麼快下班:Conflict

image

有很多時候Push並不會這麼順利。舉例來說,上圖Computer A跟Computer B對同一份檔案做修改,然後下班時分別將該份檔案Push到伺服器,這樣就會發生衝突 Conflict,其中一方的Push會失敗。

發生衝突的原因:互相衝突的程式碼

團隊合作中很容易發生衝突,現在Computer A跟Computer B都修改了index.html。以下是Computer A對index.html的修改內容:

   1: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   2: "http://www.w3.org/TR/html4/loose.dtd">
   3: <html xmlns="http://www.w3.org/1999/xhtml">
   4:     <head>
   5:         <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
   6:         <title>New Web Project</title>
   7:     </head>
   8:     <body>
   9:         <h1>New Web Project Page</h1>
  10:         <!-- test 2 -->
  11:     </body>
  12: </html>

然後是Computer B對index.html的修改內容:

   1: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   2: "http://www.w3.org/TR/html4/loose.dtd">
   3: <html xmlns="http://www.w3.org/1999/xhtml">
   4:     <head>
   5:         <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
   6:         <title>New Web Project</title>
   7:     </head>
   8:     <body>
   9:         <h1>New Web Project Page</h1>
  10:         <!-- test 1 -->
  11:     </body>
  12: </html>

其中這兩個檔案的第10行內容是不一樣的,這就是發生衝突的原因。

然後他們修改之後各別Push到了伺服器上,這順序是這樣:

  1. Computer A Push了index.html,Push成功。
  2. GitHub上最新的index.html是Computer A的版本。
  3. Computer B Push了index.html,Push失敗。

image

現在Computer B嘗試Push,可是他失敗了,因為衝突發生,GitHub無法直接把Computer B的儲存庫加入伺服器中。

接下來,我就是要介紹發生衝突的時候,怎麼使用TortoiseGit來做合併、解決衝突。

1. 發生衝突的話就先Pull吧

image

既然遠端伺服器不接受你的Push,那就代表遠端伺服器有比較新的版本。我們必須先Pull取回遠端伺服器的版本來做個檢查。

2. 處理衝突清單

image

這份衝突清單會列出Git發現本機端跟伺服器端不同的檔案。請雙擊檔案名稱,我們來一一解決這些衝突。

3. 合併

image

TortoiseGit提供了一個精美的編輯器來合併程式碼:TortoiseGitMerge。從上圖中你可以看到左上角是「對方的 – REMOTE」程式碼,也就是代表位於GitHub上最新的程式碼端;右上角則是「我的 – LOCAL」,也就是本機端的程式碼;而下方「已合併 – index.html」則是合併雙方程式碼之後的結果。

請試著把遠端與本地端做個整合,然後再按照上圖所示位置「標記為已解決」。

4. Commit已解決的版本

image

解決這些已衝突的檔案之後,記得還是要做一次Commit喔。

5. 再度Push

image

接著我們再做一次Push。

image

這次的Push就會成功了。

如果又失敗,那可能是又發生衝突了。請再繼續Merge吧!

新功能開發:開啟Branch

image

接下來我們來實作分支的功能。現在我們試著在下一次commit的時候建立一個新的分支,叫做「demo-branch」。

image

要建立一個分支很容易,就是在Commit的時候,把「新建分支」打鉤,然後在「提交至」設定新分支的名稱「demo-branch」就可以了。

2013-02-05_200100

於是現在儲存庫所在的分支就會變成你新建的分支。按下右鍵時Git提交後面的名稱就是現在所在的分支。

接著你就可以在這個分支修改你要的功能,而不用擔心常常跟其他人衝突囉。

完成新功能開發:合併Branch

當我們的新功能已經開發完成之後,接下來我們就要把分支demo-branch現在的狀態合併到主幹master上。在TortoiseGit裡面有一套作法,請照著以下步驟來操作看看。

1. 切換分支:Checkout

image

首先,我們要先把儲存庫目前的版本HEAD切換回主要分支master上。這個動作叫做Checkout。

image

作法是在儲存庫按右鍵,進入TortoiseGit中的「切換/取出」。

image

先切換到你最終要保留的分支。雖然上圖是寫gh-pages,但通常會是master,也就是穩定的版本。

這個Checkout功能也可以用來切換到標籤、特定版本,方便我們快速回溯到之前的狀態,以便我們偵錯。

2. 合併分支

image

當你按右鍵的時候,會看到目前儲存庫的狀態移到了分支gh-pages中。然後接著我們使用的是TortoiseGit中的「合併」功能。

image

請選擇你要拿來合併的分支,也就是你剛剛開發完新功能的分支。

image

如果你的修改只是比master多推進幾個版本,Git會用Fast-forward merge來進行合併,這不太會有問題。如果順利的話,就會看到上圖藍字的訊息,表示成功。

3. 發生衝突

image

如果在你開了新分支之後,master又有往前推進幾個commit,那麼合併時就比較容發生衝突。按下左下角的「解決」吧。

2013-02-05_204319

這時候跳出了commit對話視窗,下面有變動的檔案清單。

回到檔案總管來看,index.html被標示了黃色三角形驚嘆號image,代表這個檔案發生了衝突,需要我們解決。

4. 解決衝突

2013-02-05_204533

在發生衝突的檔案index.html上按右鍵,然後進入TortoiseGit選擇「編輯衝突」。

image

然後會跳出TortoiseGitMerge編輯器。就跟之前在處理Push的衝突一樣,請在下方把衝突處理完,然後點選「標記為已解決」。

5. Commit解決衝突的版本

2013-02-05_204805

接著我們再做一次Commit,把解決衝突的版本儲存起來。到目前為止,我們順利地把demo-branch合併到master分支,但這只是本機端作業而已。

6. Push已合併的版本

2013-02-05_205025

最後我們把已經修改好了版本Push到GitHub。因為我自己測試時做了幾次解決衝突的過程,所以中間版本列表很多。其中你可以看到最上面一層會有Merge branch的訊息,表示在這之間我們的確做了一次合併分支。

image

如果Push成功,那麼我們合併分支的工作就大功告成囉!


結語:Git還有很多功能!

我在跟實驗室的同學們介紹完上述的基本功能之後,大家對於Git還是有很多好奇的地方。像是有沒有單一檔案回溯功能啦、或是應該安排時間統一進行Merge,以免一直發生Conflict。大家的意見都很有意思,也加深了我對Git的期許。

上述的基本功能只是基本中的基本,實務上Git還有重新接樹Rebase、全新樹Stashing、里程碑Tag等功能,而GitHub常見的開發模式也並非上述介紹的單一中心式(所有人都連到KALS儲存庫做開發)、而是聯合管理員式(大家都Fork各自的KALS標註系統,然後透過Pull Request來整合到正統Blessed的KALS標註系統,可以想像成是大型Branch的感覺)等方式。想更進一步了解這些概念的人可以參考Littlebtc的「寫給大家的Git教學」投影片

而TortoiseGit還有許多功能是我們尚未探索到。可能隨著協同作業複雜化,未來我們會繼續探究更多其他的功能吧。

希望透過這些文章的介紹,大家也能一起來co-work,Coding Together吧!

(more...)