[譯] Git:透過命令學概念 —— 第一部分

Git:透過命令學概念 —— 第一部分

用交互式的教程教你 Git 的原理,而非羅列經常使用命令。html

因此,你想正確地使用 Git 嗎?前端

但你確定不想僅僅學一些操做命令,你還想要理解其背後的原理,對吧?android

那麼本文就是爲你量身定作的!ios

讓咱們快點開動吧!git


本文的落筆點基於 Rachel M. Carmena 撰寫的 如何教授 Git 一文中說起的常規概念。github

網上有不少重方法輕原理的 Git 教程,但我仍是挖掘到了兼得兩者的寶貴資源(也是本教程的靈感源泉),那就是 git BookReference page後端

所以,若是你讀完了本文還意猶未盡,就快點擊上面兩個連接一探究竟吧!我真心但願本教程中介紹的概念,能幫你理解另外兩篇文章中詳解的其餘 Git 功能。bash



概覽

下圖中有四個盒子。其中一個盒子獨佔一隅;其餘三個盒子併爲一組,這三個盒子構成了開發環境(Development Environment)編輯器

Git 組成

先從那個單獨的盒子提及。當你更新了工做內容,並想要和其餘人共享你更改的內容,或者你想獲取到別人更改的內容,那麼遠程倉庫(Remote Repository)就是大家用來傳送更改內容的地方。若是你已經用過其餘的版本控制系統了,那這種流程對你來講一點都不陌生。分佈式

相對於遠程倉庫(Remote Repository),開發環境(Development Environment)則是你的本地倉庫。 開發環境由三部分組成:工做目錄(Working Directory)、暫存區(Staging Area)和本地倉庫(Local Repository)。在開始使用 Git 後,咱們對這幾塊區域的理解會逐漸加深。

在電腦中選一個地方做爲開發環境。 在根目錄或者任意你喜歡之處放置你的項目均可以。只不過不用給開發環境特地新建一個文件夾了。

獲取遠程倉庫

如今,咱們要把一個遠程倉庫的內容抓到電腦本地上。

建議使用本倉庫(github.com/UnseenWizza… 若是你不是在 GitHub 上閱讀本文,就點擊此連接來實操)。

git clone https://github.com/UnseenWizzard/git_training.git 命令來實現這一步操做

但若要跟隨本教程操做,你會須要把你的更改從開發環境回傳到遠程倉庫中,而 GitHub 不容許用戶隨意更改其餘用戶的倉庫,所以你最好建立一個教程倉庫的 fork 版本以供使用。fork 按鈕在 GitHub 倉庫頁面的右上角。

如今你獲取到了筆者的遠程倉庫的副本,接下來就把該副本拉到你的電腦中。

使用 git clone https://github.com/{YOUR USERNAME}/git_training.git 命令將遠程倉庫複製到本地。

以下圖所示,該命令將遠程倉庫複製到兩個地方:工做目錄本地倉庫

如今你應該明白了,這就是 Git 分佈式版本控制的原理。本地倉庫遠程倉庫的克隆體,毫無二致。惟一的區別就是,這個克隆體是不與其餘人共享的。

git clone 命令的另外一個做用是新建一個文件夾。在你本地會出現一個名爲 git_training 的文件夾。打開它。

克隆遠程倉庫

添加新文件

遠程倉庫中已經有一個文件了。該文件名爲 Alice.txt,在倉庫中孑然一身。咱們來新建一個文件,命名爲 Bob.txt,與 Alice 做伴。

剛纔的操做是向工做目錄中新增文件。

工做目錄中有兩種文件:已跟蹤文件 —— 由 Git 看管着的文件,未跟蹤文件 ——(暫時)沒有被 Git 看管的文件。

運行 git status 命令能夠查看工做目錄中的版本狀態,輸出結果會告訴你目前處於哪條分支,本地倉庫是否與遠程倉庫同步,以及哪些文件分別處於已跟蹤(tracked)狀態和未跟蹤(untracked)狀態。

你會看到,Bob.txt 處於未跟蹤狀態,git status 命令甚至會告訴你如何改變文件狀態。

以下圖所示,當你按照提示執行 git add Bob.txt 命令後,Bob.txt 文件會被加入到暫存區暫存區中收集了全部你但願加入到倉庫中的更改。

把更改添加到暫存區

當把全部更改(目前只有 Bob.txt)都添加進暫存區,你就能夠把更改提交(commit)到本地倉庫了。

你所提交的更改是一些有特定含義的工做內容,所以當運行了 git commit 後,你須要在自動打開的文本編輯器中寫下你的更改說明。保存並關閉文本編輯器後,你的提交內容就被添加到本地倉庫中了。

提交到本地倉庫

git commit -m "Add Bob" 命令讓你可以直接在命令中編輯提交說明。但你應該養成書寫規範易讀的提交說明這種良好習慣,所以不要一蹴而就,仍是乖乖用文本編輯器吧。

如今,你作的更改就進入本地倉庫了,本地倉庫適合存儲那些不須要共享或暫時還不能共享的工做內容。

那麼爲了把提交的更改共享到遠程倉庫,你須要推送(push)一下。

推送到遠程倉庫

運行 git push 命令後,更改內容就會被髮送到遠程倉庫中。下圖展現了推送後的倉庫狀態。

推送更改後的倉庫狀態

更改

截至目前,咱們只是新增了一個文件。而版本控制更有趣的部分就是更改文件。

回過頭來看 Alice.txt 文件。

Alice.txt 文件裏有一些文字,而 Bob.txt 文件裏並無,那咱們就給 Bob.txt 添加上 Hi!! I'm Bob. I'm new here. 這句話。

若是你如今再運行 git status 命令,你會看到 Bob.txt 的狀態變成了已修改(modified)。

在此狀態下,此處更改僅存在於工做目錄中。

若想查看工做目錄中具體的更改細節,你能夠運行 git diff 命令,輸出結果以下:

diff --git a/Bob.txt b/Bob.txt
index e69de29..3ed0e1b 100644
--- a/Bob.txt
+++ b/Bob.txt
@@ -0,0 +1 @@
+Hi!! I'm Bob. I'm new here.
複製代碼

照舊運行 git add Bob.txt 命令。顯然,新的更改進入了暫存區

想查看剛剛標記爲暫存(staged)狀態的更改的話,試試再運行一次 git diff 命令!你會發現這一次輸出結果是空的。這是由於 git diff 命令只在工做目錄中有效。

那麼要想看到已經處於暫存狀態的更改的內容,咱們須要運行 git diff --staged 命令,這樣才能看到上次那樣的輸出結果。

哎呀呀,我才發現我剛剛在『Hi』後面多加了一個感嘆號。這樣可不行,得再一次改動 Bob.txt 文件,保留一個感嘆號就好了。

刪掉多餘的感嘆號後,再運行 git status 命令,能夠看到有兩處更改,一處是暫存狀態的添文字添加,另外一處時是在工做目錄中對感嘆號的刪除。

咱們用 git diff 命令比較一下工做目錄暫存區中的更改,看看自從上次標記暫存後發生了什麼變化。

diff --git a/Bob.txt b/Bob.txt
index 8eb57c4..3ed0e1b 100644
--- a/Bob.txt
+++ b/Bob.txt
@@ -1 +1 @@
-Hi!! I'm Bob. I'm new here.
+Hi! I'm Bob. I'm new here.
複製代碼

發生的更改正如咱們所願,接着用 git add Bob.txt 命令暫存文件當前的狀態。。

如今咱們能夠提交剛纔的更改了。此次我們使用 git commit -m "Add text to Bob" 命令,由於只是作了小小的改動,寫一行說明足矣。

咱們知道,如今那些更改已經進入本地倉庫了。

咱們可能會想知道剛剛提交了什麼更改,想知道更改先後有什麼不一樣。

咱們能夠經過『比較提交』獲得答案。

在 Git 中,每次提交操做都對應一個惟一的哈希值,咱們能夠用某個哈希值來引用對應的提交。

若是用 git log 命令查看一下日誌,咱們不單會看到一系列帶哈希值做者日期的提交操做,還會看到本地倉庫的狀態和關於遠程分支的最新本地信息。

此刻 git log 命令的運行結果大概以下:

commit 87a4ad48d55e5280aa608cd79e8bce5e13f318dc (HEAD -> master)
Author: {YOU} <{YOUR EMAIL}>
Date:   Sun Jan 27 14:02:48 2019 +0100

    Add text to Bob

commit 8af2ff2a8f7c51e2e52402ecb7332aec39ed540e (origin/master, origin/HEAD)
Author: {YOU} <{YOUR EMAIL}>
Date:   Sun Jan 27 13:35:41 2019 +0100

    Add Bob

commit 71a6a9b299b21e68f9b0c61247379432a0b6007c 
Author: UnseenWizzard <nicola.riedmann@live.de>
Date:   Fri Jan 25 20:06:57 2019 +0100

    Add Alice

commit ddb869a0c154f6798f0caae567074aecdfa58c46
Author: Nico Riedmann <UnseenWizzard@users.noreply.github.com>
Date:   Fri Jan 25 19:25:23 2019 +0100

    Add Tutorial Text

      Changes to the tutorial are all squashed into this commit on master, to keep the log free of clutter that distracts from the tutorial

      See the tutorial_wip branch for the actual commit history
複製代碼

在日誌中,咱們能發現幾處有意思的細節:

  • 前兩個提交的操做人是本身。
  • 最開始添加 Bob.txt 文件的提交是遠程倉庫master 分支的 HEAD。等說到分支和拉取遠程更改的部分時,咱們再展開探討這個『HEAD』。
  • 本地倉庫中最新的提交就是咱們剛剛作的更改,而此刻咱們知道了其哈希值是什麼。

注意,你實際操做的提交的哈希值跟文中的例子是不同的。若是你好奇 Git 是如何生成那些修改 ID 的,能夠看看這篇有趣的文章

咱們能夠經過 git diff <commit>^! 命令來比較這前後兩次提交,命令中的 ^! 告訴 Git 要比較的是指定哈希值的提交和它上一次的提交。那麼我這裏就是運行 git diff 87a4ad48d55e5280aa608cd79e8bce5e13f318dc^! 這樣的命令。

咱們還能用 git diff 8af2ff2a8f7c51e2e52402ecb7332aec39ed540e 87a4ad48d55e5280aa608cd79e8bce5e13f318dc 這樣的命令來比較任意兩次提交,一樣會輸出比較信息。注意,該命令的格式是 git diff <from commit> <to commit>,也就是說對於較新的提交,其哈希值要放在第二位。

下圖再次呈現了一個更改的不一樣階段,以及每一個階段用的不一樣的 diff 命令。

一個更改的不一樣階段以及相關的 diff 命令

如今咱們能夠確信提交的更改正是咱們想要的,能夠放心大膽地運行 git push 命令了。

分支

Git 的另外一個偉大之處就是分支。分支是你使用 Git 時不可或缺的部分,藉助分支來工做很是便利。

其實,從一開始咱們就已經在使用分支了。

在克隆遠程倉庫時,開發環境會自動選中倉庫的主分支(master 分支)複製。

一般的 Git 工做流都是先在一個分支上作更改,再將其合併(merge)回 master 分支。

通常狀況下,你都是先在本身的分支上動工,等到完成現階段的工做,並確信可以合併的時候,再合併到 master 分支。

一些 Git 倉庫託管平臺(如 GitLabGitHub 等)也提供保護分支的功能,意思是並不是全部人都能把更改推送到受保護的分支。在這些託管平臺中,master 分支通常都是默認受保護的。

別擔憂,當咱們須要用到這些託管平臺的時候,自會深刻研究其細節。

眼下,咱們得建立一條分支,在該分支上作一些更改。有時候你只是想要嘗試本身作些東西,不想污染 master 分支的工做狀態;有時候你沒有推送更改到 master 分支的權限。此時,一條新的分支正是你所須要的。

本地倉庫遠程倉庫中都容許有多條分支。每一個新建的分支,都是你當前所在分支中已經提交的內容的副本。

來動手改一改 Alice.txt 文件吧!在第二行增長一些文字怎麼樣?

咱們想共享要作的更改,但不想立馬就併入 master 分支,所以得先用 git branch <branch name> 命令新建一條分支。

具體來講就是用 git branch change_alice 命令建立一個名爲 change_alice 的分支。

這一步操做給本地倉庫新增了一條分支。

工做目錄暫存區其實並不會和多條分支聯動,你提交的更改老是會進入到當前分支。

你能夠把 Git 中的分支想象成指針,一根指向一系列提交內容的指針。每當你進行提交操做時,當前指針指向哪裏,更改的內容就提交到哪裏。

單純新建一條分支,並不能直接連到倉庫,那只是豎起了一根指針而已。

其實,本地倉庫當前的狀態,能夠視爲另外一根指針,其名爲 HEAD,它指向的是你當前的分支和當前的提交內容。

可能文字描述有點複雜,下面的示意圖能夠幫你理清這些頭緒:

建立分支後的狀態

使用 git checkout change_alice 命令能夠切換到新分支。本操做實質上是把 HEAD 移到了你所指定的分支上了。

一般咱們都是建立分支後切換到該分支上,所以能夠用帶有 -b 選項的 checkout 命令,一鼓作氣地新建完切換過去,不用分兩步走了,畢竟牛仔很忙的。

落實到具體操做就是運行 git checkout -b change_alice 命令,實現了建立並切換到 change_alice 分支。

切換分支後的狀態

可能你已經注意到了,工做目錄並無什麼變化。那是由於對 Alice.txt 文件的修改尚未關聯到當前分支上。

如今你能夠像在 master 分支上那樣,執行 addcommit 命令,把更改標記爲暫存(在這個節點,更改內容和分支仍然沒有相互關聯)並提交change_alice 分支上。

如今只剩一步沒邁出去了。你能夠試試運行 git push 命令,把更改推送到遠程倉庫

你會看到以下報錯信息和一條(Git 一如既往地提供的)解決建議:

fatal: The current branch change_alice has no upstream branch.(嚴重錯誤:沒有上游分支對應當前的 change_alice 分支。)
To push the current branch and set the remote as upstream, use(若想推送當前分支並設置遠程倉庫爲上游分支,請使用)

    git push --set-upstream origin change_alice
複製代碼

我們可不是隨意盲從的人,對吧。我們得弄明白到底發生了什麼。因此何爲上游分支(upstream branch)?何爲遠程分支(remote)

還記得以前咱們用 clone 命令複製了遠程倉庫吧?那時候,遠程倉庫不僅是包含本篇文章和 Alice.txt 文件,其實是有兩條分支在其中。

一條是 master 分支,咱們開始動工並一路推動的分支;另外一條是 tutorial_wip 分支,我把本教程中全部的更改都提交到這條分支上了。

當咱們把遠程倉庫的內容複製到開發環境中時,一些額外操做潛移默化地發生了。

Git 把本地倉庫遠程分支設置爲你所克隆的遠程倉庫,並賦予其一個默認名稱 origin

本地倉庫能夠追蹤多個不一樣名稱的遠程分支,但本教程中,咱們將緊盯住 origin 而不考慮其餘分支。

然後,Git 複製了兩條遠程分支到本地倉庫中,並最終切換到了 master 分支。

同時,另外一個操做暗搓搓地啓動了。當簽出(checkout)一條分支,且此分支的名稱與遠程分支匹配時,你會獲得一條新的本地分支,本地和遠程的分支相互關聯。那麼咱們說,這條遠程分支就是本地分支上游分支(upstream branch)。

在上文中的那些示意圖中,你只能看到一條本地分支。而使用 git branch 命令則能查看一系列本地分支。

假如你想看到本地倉庫中關聯的遠程分支,可使用 git branch -a 命令列出它們。

遠程分支和本地分支

明白了這些,咱們就能夠踏踏實實地遵守建議運行 git push --set-upstream origin change_alice 命令了,這樣就把本地分支中的更改推送到了一條新的遠程分支中。命令生效後,遠程倉庫中會建立一條名爲 change_alice 的分支,而且本地change_alice 分支會追蹤遠程的新分支。

另外有一種操做,適用於想用本地分支追蹤遠程倉庫中已有的內容這種需求。想象這樣的場景:一位同事已經推送了一些更改,而這些更改與你本地分支的工做內容有依賴關係,那就須要將兩者整合到一塊兒。因而就要用到 git branch --set-upstream-to=origin/change_alice 這條命令,把 change_alice 分支的上游分支設爲新的遠程分支,以便於追蹤同事的更改。

操做完成後,到 GitHub 上的遠程倉庫中看看,你的分支已經建立就緒,能夠被其餘人看到並協同工做。

很快咱們將談到如何把別人的更改拉到本身的開發環境中,但目前咱們仍是使用分支來介紹更多的概念,這些概念會在咱們把遠程倉庫向本地更新時粉墨登場。

歡迎繼續閱讀本系列其餘文章:

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索