1. 程式人生 > >GIT科普系列2:git程式碼檢出與日常維護

GIT科普系列2:git程式碼檢出與日常維護

背景:

由於公司內部目前採用git來進行程式碼管理,因此近期會逐步更新部分關於git的介紹。一來是為了給大家提供一個集中學習和參考的地方;二來是希望通過具體的示例來給出git的操作指南。雖然之前介紹過很多關於git的使用但不夠系統,不夠集中。這次希望通過幾篇連續的博文給出一個簡單的git實用指南。
既然提到了開發程式碼的版本管理,那麼首要的就是從目標倉庫中獲取開發程式碼,即SVN中常用的檢出(checkout)。本系列就從git的程式碼檢出(git checkout?)開始!

git的幾個關鍵概念

相較於SVN等其他版本管理系統,GIT的組織結構略顯複雜,但正是因為這樣,才使得GIT這一分散式的版本管理系統更能適應多變的開發環境和協同需求。在SVN中只有本地副本(working copy)和版本庫(repository)兩大部分,在GIT中結構複雜一些,包括:

  • working tree:也叫working directory.與SVN的本地副本有些類似,但也有不同。官方手冊對working tree的介紹為:

    The working directory is a single checkout of one version of the project. These files are pulled out of the compressed database in the Git directory and placed on disk for you to use or modify.

  • index:也叫staging area.可以簡單的理解為緩衝區。

    官方手冊對index的介紹為:

    The staging area is a file, generally contained in your Git directory, that stores information about what will go into your next commit. It’s sometimes referred to as the “index”, but it’s also common to refer to it as the staging area.

  • repository分為local repository(本地的版本庫)和remote repository(遠端倉庫)。由於git採用無中心分散式設計,因此本質上來說local repository與remote repository的地位是相同的,內容也幾乎相同(遠端倉庫可能為了協同開發會繫結相關整合指令碼),唯一的不同就是remote repository可以被更多的開發者訪問,而local repository僅僅是個人工作環境的維護。

    官方手冊對於repository(即.git目錄)的介紹為:

    The Git directory is where Git stores the metadata and object database for your project. This is the most important part of Git, and it is what is copied when you clone a repository from another computer.

為了說清楚上述幾個概念,這裡節選了幾幅比較形象的示意圖:

這裡寫圖片描述


這裡寫圖片描述


這裡寫圖片描述


如上述圖片所示,正式因為在本地存在著完完整整的倉庫目錄(.git目錄),在工作副本和倉庫之間添加了Staging Area緩衝區,才使得git能夠實現本地和遠端雙重管理。

git的日常操作流workflow

官方文件給出了git版本管理系統下的檔案的各種狀態,包括untracked、unmodified、modified、staged。如下圖所示:

這裡寫圖片描述

git不單單是一個版本管理系統,其實可以看成是一個mini的檔案系統,用於管理檔案和日常的所有操作。git中的所有操作會使得git檔案系統內部的各檔案狀態在上述四種之間來回切換。如下面兩張截圖所給出的git的各種日常操作指令。
這裡寫圖片描述


這裡寫圖片描述

【備註】:這裡要注意,不要將git所管理的檔案的狀態(untracked、unmodified、modified、staged)與git自身的階段(working tree、index、local repository、remote repository)相混淆。
(1)working tree中的檔案可以包含多種狀態,例如從版本庫上一次提交snapshot檢出的檔案處於tracked和unmodified狀態、本地對tracked檔案的任何修改會使檔案處於modified和staged(git add操作即可)狀態,本地新增檔案或其他操作會使得檔案處於untracked狀態;
(2)index中的檔案只能處於modified和staged狀態(同時處於);
(3)repository(local or remote)中檔案的狀態只能處於unmodified的跟蹤狀態。——當然這句話還有待考究,這裡嚴格意義來說是從repository中檢出的檔案只能處於unmodified的tracked狀態,因為你只能從別的倉庫中將unmodified的tracked狀態的檔案拉倒本地。但原始的倉庫中其實有可能存在著其他狀態的檔案,但是對外來講是隱藏的、透明的。

git的程式碼“檢出”

其他版本庫管理系統(例如SVN)一般都會使用checkout來直接檢出服務端的版本庫(即開發程式碼)到本地工作目錄,而git checkout卻不能實現這一功能,原因是:

If you’re familiar with other VCS systems such as Subversion, you’ll notice that the command is “clone” and not “checkout”. This is an important distinction – instead of getting just a working copy, Git receives a full copy of nearly(伺服器會存在部分hooks或者其他檔案,詳情參考Git Internals) all data that the server has. Every version of every file for the history of the project is pulled down by default when you run git clone.

上文提到了git採用分散式無中心化設計,因此檢出程式碼的同時需要將完整的倉庫下載到本地。所以簡單的checkout是不能完成這項任務的,在git中應該使用git clone將遠端倉庫檢出到本地。

1. git clone的整體流程如下:

Created with Raphaël 2.1.0locallocalremoteremotegit cloneinit .git dirpull all in .gitadd remote urlscheckout latest working copyjust do anything by yourself

從上圖可以看出git clone也是一個複合高階指令,內部會執行git init(在本地初始化git repository,即建立.git隱藏目錄)、git pull(將遠端倉庫的幾乎所有拷貝到本地)、git remote add(將遠端倉庫的URL連線新增到記錄),以及git checkout(將預設的倉庫的master分支程式碼拷貝到本地工作目錄working directory和緩衝區index)。
既然提到了其他的指令,這裡一併介紹一下git pull、git checkout。

2. git pull的整體流程如下:

Created with Raphaël 2.1.0locallocalremoteremotegit pullsub-command:git fetchsub-command:git mergejust do anything by yourself

【備註】:這裡要注意git pull與git fetch的區別,由上圖可以看出git pull是git fetch和git merge的複合指令,由此可以得出git fetch只負責將檔案檢出到本地,但並不會與當前工作區的程式碼進行合併。官方說明如下:

It’s important to note that the git fetch command only downloads the data to your local repository – it doesn’t automatically merge it with any of your work or modify what you’re currently working on.
【zssure】:即git fetch從伺服器取回本地沒有的檔案,但不會自動merge本地working copy。不帶引數的git fetch會取回所有的分支。

3. git checkout的整體流程如下:

Created with Raphaël 2.1.0locallocalremoteremotegit checkoutchecking out fileschecking out commitschecking out all branchesdo anything by yourself

git的checkout

1. checkout的底層機制

通過上面兩個章節“git的日常操作流workflow”“git的程式碼‘檢出’”,我們大致瞭解瞭如何從倉庫中將git所管理的各種檔案拉取到本地,以及本地檔案可能所處的各種狀態。接下來就說一下日常最容易碰到的問題,諸如簡單的git add、git commit和git reset就不贅述了,這幾個指令日常使用最頻繁就是將本地修改新增到git的倉庫中進行管理和儲存。這裡著重介紹的是checkout,尤其是當需要同時管理多個分支,在多個分支之間切換時;甚至是當需要與同一個源的其他倉庫進行合併時(即我們通常所說的多人協同開發)。

git checkout有多重指令格式,例如:

git checkout [-q] [-f] [-m] [<branch>]
git checkout [-q] [-f] [-m] --detach [<branch>]
git checkout [-q] [-f] [-m] [--detach] <commit>
git checkout [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]
git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>…​
git checkout [-p|--patch] [<tree-ish>] [--] [<paths>…​]

但是最終checkout所做的事情就是將命令列對應的版本庫中或者index中的檔案拷貝出來,貼上到working directory(如果引數是版本庫也會拷貝到index區域)區域中——這裡與SVN等版本管理的checkout有些類似。這裡給出一個最簡單的本地切換分支的示意圖:

這裡寫圖片描述

摘自圖解GIT

當git checkout後面跟的是一個本地(或遠端)分支、或者HEAD(即預設的當前分支)時,實際的操作如上圖所示:
- 1)會將本地的working directory和index的內容設定成與制定的分支的最近一次提交內容相一致,即目標分支中存在的任何檔案都會被拷貝到index和working directory中
- 2)當前狀態存在而目標分支下沒有的檔案會從index和directory中移除,
- 3)兩個提交都存在的檔案保持不變。

由此我們可以看出git checkout在分支切換時(分支切換使用最頻繁),用來判別切換前後index和working directory狀態的依據是兩個分支的最後一次提交,而跟index和working directory的狀態沒有多大關係。

2. checkout多分支維護

這句話什麼意思呢,讓我們用示例來看一下:
1)假設本地有兩個分支branch1和branch2.此刻兩個分支剛merge完成,內容一致。此刻我們在branch分支上。新建git-dirty-test.txt檔案,並輸入一行string。可以使用git add來新增到index區域。但不commit。

這裡寫圖片描述

在最終執行git commit將本次修改提交儲存到branch1之前,我們可以在branch1和branch2之間任意自由切換,且不會丟棄掉當前的修改。無論是否將新增的git-dirty-test.txt檔案新增進了index區域。具體執行情況如下:
#無論何時,一進來後先檢視當前狀態
D:\ZSDevelops\Git\git-dirty-test>git status
On branch official-master
Your branch is up-to-date with 'official/master'.
nothing to commit, working directory clean
#新建一個txt檔案,再一次檢視狀態。此刻並沒有執行git add指令
D:\ZSDevelops\Git\git-dirty-test>git status
On branch official-master
Your branch is up-to-date with 'official/master'.
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        git-dirty-test.txt

nothing added to commit but untracked files present (use "git add" to track)
#檢視本地分支狀態
D:\ZSDevelops\Git\git-dirty-test>git branch -v
  master          5fcbcaa Merge branch 'cache-enhancements'
* official-master 5fcbcaa Merge branch 'cache-enhancements'
#切換分支到master分支
D:\ZSDevelops\Git\git-dirty-test>git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
#再一次檢視當前狀態
D:\ZSDevelops\Git\git-dirty-test>git status
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        git-dirty-test.txt

nothing added to commit but untracked files present (use "git add" to track)

上文可以看出新增的檔案依然在working directory中,分支切換也沒有受阻,並且working directory的中間狀態untracked也沒有被破壞。
其實即使執行了git add指令依然可以自由切換分支,並不會影響working directory和index任何東西。執行過程如下:

D:\ZSDevelops\Git\git-dirty-test>git add git-dirty-test.txt

D:\ZSDevelops\Git\git-dirty-test>git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   git-dirty-test.txt


D:\ZSDevelops\Git\git-dirty-test>git checkout official-master
A       git-dirty-test.txt
Switched to branch 'official-master'
Your branch is up-to-date with 'official/master'.

D:\ZSDevelops\Git\git-dirty-test>git status
On branch official-master
Your branch is up-to-date with 'official/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   git-dirty-test.txt

2)這一次我們將git-dirty-test.txt檔案commit到branch1中。然後切換分支到branch看一下。

這裡寫圖片描述
D:\ZSDevelops\Git\git-dirty-test>git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

D:\ZSDevelops\Git\git-dirty-test>git branch -v
* master          9eee183 [ahead 1] git dirty file test
  official-master 5fcbcaa Merge branch 'cache-enhancements'

D:\ZSDevelops\Git\git-dirty-test>git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   git-dirty-test.txt

no changes added to commit (use "git add" and/or "git commit -a")

D:\ZSDevelops\Git\git-dirty-test>git checkout official-master
error: Your local changes to the following files would be overwritten by checkout:
        git-dirty-test.txt
Please commit your changes or stash them before you can switch branches.
Aborting

一旦執行git commit後,此時index、working directory和branch1的最後一次提交的snapshot三者同步了。此時在切換到分支branch2上。可以發現之前可以看到的在index和working directory中儲存的git-dirty-test.txt檔案消失了。這是因為git-dirty-test.txt檔案在commit後已經歸屬於branch1了,按照之前講的checkout的執行策略,屬於現有分支但不屬於目標分支的檔案會被移除。
3)最後我們再來做一次操作,首先將分支再一次切回branch1。然後對git-dirty-test.txt檔案進行修改,新增新的一行string。然後再次嘗試切換分支到branch2.執行過程如下:

#切回之前的分支,這裡是master分支
D:\ZSDevelops\Git\git-dirty-test>git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)
#檢視分支切換狀態
D:\ZSDevelops\Git\git-dirty-test>git branch -v
* master          9eee183 [ahead 1] git dirty file test
  official-master 5fcbcaa Merge branch 'cache-enhancements'
#修改檔案後,檢視狀態
D:\ZSDevelops\Git\git-dirty-test>git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   git-dirty-test.txt

no changes added to commit (use "git add" and/or "git commit -a")
#嘗試切換到新的分支official-master,出現了錯誤
D:\ZSDevelops\Git\git-dirty-test>git checkout official-master
error: Your local changes to the following files would be overwritten by checkout:
        git-dirty-test.txt
Please commit your changes or stash them before you can switch branches.
Aborting

上述過程出現了錯誤,無法執行checkout執行。究其原因是因為此刻checkout原本應該執行與2)中類似的操作,將git-dirty-test.txt檔案從index和working directory中移除,但是當前working directory(如果git add了話就是index)的內容還未儲存到branch1分支中,還處於臨時狀態。而此刻如果移除git-dirty-test.txt檔案,就會導致當前本地的修改丟失。因此此刻git進行了錯誤提示。

3. checkout錯誤的dirty狀態

至此我們清楚了checkout是如何完成分支切換的,以及什麼情況下才能切換。這也就是官方所說的只有在非“dirty”狀態下才能自由執行checkout指令。官方對於dirty的描述如下:

這種情況在實際程式碼開發過程中會經常遇到,尤其是多個分支切換時。例如我們採用“主線開發、分支發版”的方式,假設已經發布了一款產品的分支為branch-publish1,此刻你在master分支上研發新的功能模組,由於還沒有完工,所以沒有執行git commit。突然發現了重大bug,公司要求你立刻切換到branch-publish1產品分支進行修復。最笨最笨的辦法就是你把當前master的專案資料夾備份,重新建一個資料夾檢出branch-publish1的程式碼進行修復後再commit。
但是正確的操作步驟如下:

  1. stash your current change or
  2. reset –hard HEAD (if you do not mind losing those minor changes) or
  3. checkout -f (When switching branches, proceed even if the index or the working tree differs from HEAD. This is used to throw >away local changes. ).摘自Does git “dirty” mean files not staged, or not committed? (glossary conflict)

之所以需要上述操作是因為當前你所操作的working directory和index處於dirty狀態。

總結:

此篇博文著重介紹了git最常用的工作流程,對於多分支維護給出了具體的示例。通過本文的介紹已經基本能夠應付日常80%的開發維護工作。後續會進一步介紹git的高階知識。






作者:[email protected]
時間:2016-07-30

相關推薦

GIT科普系列2git程式碼日常維護

背景: 由於公司內部目前採用git來進行程式碼管理,因此近期會逐步更新部分關於git的介紹。一來是為了給大家提供一個集中學習和參考的地方;二來是希望通過具體的示例來給出git的操作指南。雖然之前介紹過很多關於git的使用但不夠系統,不夠集中。這次希望通過幾篇連

Git學習系列2 初配置及結構

一 初配置 安裝完成之後,在開始選單裡面找到 "Git --> Git Bash",如下 需要配置使用者名稱和郵箱,如果不清楚是否已配置,可用 git config user.name 和git config user.email進行檢視。 如果沒有進行配置,用gi

Git學習筆記2使用前的配置&建立第一個倉庫並配置local使用者資訊&給檔案重新命名的簡便方法

  git使用前的配置: 1.配置User資訊 配置user.name 和 user.email git config --global user.name 'yourName' git config --global user.email 'yourEmail' 這裡的e

Git使用教程2版本庫的建立和新增檔案

        上篇部落格,我們一起了解了分散式版本控制系統Git的簡單內容,知道了分散式版本控制的優勢,同時也給大家分享了Git的安裝過程。本篇博文和大家一起討論,Git版本庫的建立和簡單操作。  

Git版本號控制Git分支處理

rgb 方法 發現 速度 pip 命令 ria p s 你會 http://blog.csdn.net/pipisorry/article/details/46958699分支的意義創建分支能夠避免提交代碼後對主分支的影響,同一時候也使你有了相對獨立的開發環境。假設你準備

Azure Stack技術深入淺出系列2談Azure Stack在私有雲/混合雲生態中的定位

azure stack 雲計算 微軟 azure一、 國內私有雲業務前景就在今年4月,工信部發布《雲計算發展三年行動計劃(2017-2019)》,提出2019年雲計算產業規模將達到4300億。雲計算已成為國家新一代信息產業發展的重要戰略。從中國整個IT投入來看,政府、央企、國企以及大型民企占據主要份額,這些企

Block系列2Block內存管理

style gin elf font str 內存管理 art ber implement ViewController.h #import <UIKit/UIKit.h> @interface ViewController : UIViewCont

centos7.4安裝監控軟件系列2nagios

nagios 監控 與系列1的Cacti類似,nagios也是一種監控軟件,涉及插件較多,可靈活監控服務器資源,使用更廣泛;nagios自身是沒有監控功能的,它所有的功能都是通過調用插件來完成的,它的工作模式有兩種:主動監控、被動監控。【被動監控】nagios通過nsca進行被動監控。就是由被檢測端服

Git學習筆記(2)之Git版本回退

.com 分享 nbsp class 文檔 行修改 通過 分享圖片 font     當我們成功將修改過的文件提交到了Git版本庫了之後,突然發現自己有一個問題改錯了,這個時候我們想回退到上一次的版本該怎麽辦呢?幸好Git記錄的是修改,這樣我們就能通過一些方法回退到修改之前

LIVE555學習2live555程式碼目錄結構

文章目錄 1 原始碼下載 2 文件說明 3 簡單介紹 4 目錄結構 4.1 UsageEnvironment 4.2 groupsock 4.3 liveMedia 4.4 BasicUsageEnviron

Git 子模塊git submodule

iba 官方 克隆 子模塊 分享 help fbo 主目錄 mode imtianx 2018年03月08日閱讀 2057 Git 子模塊:git submodule 工作中,可能會遇到在一個Git倉庫 中添加 其他 Git 倉庫的場景。比如,在項目中引用第三方庫。或

招聘靠譜程式設計師系列1 程式碼風格優化糾錯

問:風格糾錯 答: 使用NS_ENUM而不是C語言型別的列舉enum typedef NS_ENUM(NSInteget, XBYGender) { //使用gender比sex正式 XBYGenderMan, XBYGenderWo

JVM系列2HotSpot虛擬機器物件

1.物件建立過程:   ①.類載入檢查:當java虛擬機器遇到一條new指令時,首先會去檢查該指令的引數能否在常量池中定位到這個類的符號引用,並且檢查這個符號引用代表的類是否已被載入、解析、初始化過,如果沒有,則必須先執行相應的類載入過程。 ②.分配記憶體:類載入檢查完成後,虛擬機器將為新物件分配記憶體

SonarQube學習系列2Maven+SonarQube 最佳實踐

本文記錄了Maven工程使用SonarQube完成程式碼評估,並對其中指定模組進行排除和專案許可權管理等 有些程式碼是使用相關外掛或工具生成的,這些程式碼通常存在高冗餘或書寫不規範現象,不符合程式碼質量要求,但不影響使用,應排除在程式碼質量評估之外 本文基於上一

RxJava2 系列 (2)背壓和Flowable

背壓(Back Pressure)的概念最初並不是在響應式程式設計中提出的,它最初用在流體力學中,指的是後端的壓力, 通常用於描述系統排出的流體在出口處或二次側受到的與流動方向相反的壓力。 在響應式程式設計中,我們可以將產生資訊的部分叫做上游或者叫生產者,處理產

WPF自學教程系列2如何在xaml檔案新增引用?

我們在寫WPF控制元件的時候,經常會需要在xaml檔案新增一些引用, 如System.Windows.Forms引用,但是在xaml檔案沒有提供類似於 using System.Windows.Forms; 的語法, 因此我們需要通過 xmlns關鍵字來新增引用。具體實現如

安卓BroadcastReceiver元件使用系列2給多個廣播接收者傳送廣播和有序廣播的使用

給多個廣播接收者傳送廣播、有序廣播的使用在安卓開發中是經常使用的方式,下面我們來介紹一下它的使用方法。 整體思路:在xml檔案中放置兩個Button控制元件,給這兩個Button控制元件設定點選事件,在第一個點選事件中傳遞一個數據,設定一個動作併發送廣播,在第二個點選事件中

RxJava系列2RxJava簡單入門

一.擴充套件的觀察者模式 Observable和Subscriber能完成任何事情,你的Observable可以是一個數據庫查詢,Subscriber獲得查詢結果然後將其顯示在螢幕上。你的Observable可以是螢幕上的一個點選,Subscrib

Gradle For Android系列2自定義Build配置

在上一章節中我們學習了Gradle的用法,以及如何建立Android專案以及如何從Eclipse中將專案轉換到Android Studio中。這一章節將介紹構建檔案配置的更多細節,以及一些有用的構建任務,並深入Gradle的Android外掛。 在本章中,

Glide 系列-2主流程原始碼分析(4.8.0)

Glide 是 Android 端比較常用的圖片載入框架,這裡我們就不再介紹它的基礎的使用方式。你可以通過檢視其官方文件學習其基礎使用。這裡,我們給出一個 Glide 的最基本的使用示例,並以此來研究這個整個過程發生了什麼: Glide.with(fragment).load(myU