1. 程式人生 > >Python學習之Git基本操作

Python學習之Git基本操作

版本庫建立

什麼是版本庫呢?版本庫又名倉庫,英文名repository,你可以簡單理解成一個目錄,這個目錄裡面的所有檔案都可以被Git管理起來,每個檔案的修改、刪除,Git都能跟蹤,以便任何時刻都可以追蹤歷史,或者在將來某個時刻可以“還原”。

所以,建立一個版本庫非常簡單,首先,選擇一個合適的地方,建立一個空目錄:

1

2

3

4

5

mkdir git_trainning

cd git_trainning/

 

$ git init

Initialized empty Git repository 

in /Users/alex/git_trainning/.git/

瞬間Git就把倉庫建好了,而且告訴你是一個空的倉庫(empty Git repository),細心的讀者可以發現當前目錄下多了一個.git的目錄,這個目錄是Git來跟蹤管理版本庫的,沒事千萬不要手動修改這個目錄裡面的檔案,不然改亂了,就把Git倉庫給破壞了。

如果你沒有看到.git目錄,那是因為這個目錄預設是隱藏的,用ls -ah命令就可以看見。

把檔案新增到版本庫

首先這裡再明確一下,所有的版本控制系統,其實只能跟蹤文字檔案的改動,比如TXT檔案,網頁,所有的程式程式碼等等,Git也不例外。版本控制系統可以告訴你每次的改動,比如在第5行加了一個單詞“Linux”,在第8行刪了一個單詞“Windows”。而圖片、視訊這些二進位制檔案,雖然也能由版本控制系統管理,但沒法跟蹤檔案的變化,只能把二進位制檔案每次改動串起來,也就是隻知道圖片從100KB改成了120KB,但到底改了啥,版本控制系統不知道,也沒法知道。

不幸的是,Microsoft的Word格式是二進位制格式,因此,版本控制系統是沒法跟蹤Word檔案的改動的,前面我們舉的例子只是為了演示,如果要真正使用版本控制系統,就要以純文字方式編寫檔案。

因為文字是有編碼的,比如中文有常用的GBK編碼,日文有Shift_JIS編碼,如果沒有歷史遺留問題,強烈建議使用標準的UTF-8編碼,所有語言使用同一種編碼,既沒有衝突,又被所有平臺所支援。

言歸正傳,現在我們編寫一個first_git_file.txt檔案,內容如下:

1

2

3

4

$ vim first_git_file.txt

 

first time using git, excited!

第一次用git哈哈

一定要放到git_trainning目錄下(子目錄也行),因為這是一個Git倉庫,放到其他地方Git再厲害也找不到這個檔案。

和把大象放到冰箱需要3步相比,把一個檔案放到Git倉庫只需要兩步。

第一步,用命令git add告訴Git,把檔案新增到倉庫:

1

$ git add first_git_file.txt

執行上面的命令,沒有任何顯示,說明新增成功。

第二步,用命令git commit告訴Git,把檔案提交到倉庫:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

$ git commit -m "commit my first git file"

 

[master (root-commit) 621e6e4] commit my first git file

 Committer: Alex Li <[email protected]>

Your name and email address were configured automatically based

on your username and hostname. Please check that they are accurate.

You can suppress this message by setting them explicitly. Run the

following command and follow the instructions in your editor to edit

your configuration file:

 

    git config --global --edit

 

After doing this, you may fix the identity used for this commit with:

 

    git commit --amend --reset-author

 

 1 file changed, 2 insertions(+)

 create mode 100644 first_git_file.txt

中間紅色部分的意思是,你在往git庫裡提交程式碼時,你需要告訴git你是誰,這樣git就會紀錄下來是誰改的程式碼,其實就是為了日後查詢方便,你只需要提供一個名字和郵件地址就可以,這裡我的git直接通過主機名自己建立了一個,但你可以通過git config --global --edit修改

簡單解釋一下git commit命令,-m後面輸入的是本次提交的說明,可以輸入任意內容,當然最好是有意義的,這樣你就能從歷史記錄裡方便地找到改動記錄。

嫌麻煩不想輸入-m "xxx"行不行?確實有辦法可以這麼幹,但是強烈不建議你這麼幹,因為輸入說明對自己對別人閱讀都很重要。

為什麼Git新增檔案需要add,commit一共兩步呢?因為commit可以一次提交很多檔案,所以你可以多次add不同的檔案,比如:

1

2

3

$ git add file1.txt

$ git add file2.txt file3.txt

$ git commit -m "add 3 files."

 

程式碼回滾

4.1程式碼修改並提交

我們已經成功地新增並提交了一個first_git_file.txt檔案,現在,是時候繼續工作了,於是,我們繼續修改first_git_file.txt檔案,改成如下內容:

1

2

3

First time using git, excited! update ...

insert line here...

第一次用git哈哈

現在,執行git status命令看看結果:

1

2

3

4

5

6

7

8

9

$ git status

On branch master

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:   first_git_file.txt

 

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

雖然Git告訴我們first_git_file.txt被修改了,但如果能看看具體修改了什麼內容,自然是很好的。比如你休假兩週從國外回來,第一天上班時,已經記不清上次怎麼修改的readme.txt,所以,需要用git diff這個命令看看:

1

2

3

4

5

6

7

8

9

10

11

$ git diff first_git_file.txt

diff --git a/first_git_file.txt b/first_git_file.txt

index 2d13c2c..248d853 100644

--- a/first_git_file.txt

+++ b/first_git_file.txt

@@ -1,3 +1,4 @@

-first time using git, excited!

+First time using git, excited! update ...

 insert line here...

 第一次用git哈哈

+insert line again haha...

輸出中+號綠色顯示的就是修改或新增的內容,-號紅色顯示的就是去掉或被修改的內容

知道了對first_git_file.txt 作了什麼修改後,再把它提交到倉庫就放心多了,提交修改和提交新檔案是一樣的兩步,第一步是git add:

1

2

3

4

5

$ git add . # .  代表把當前目錄下所有改動的檔案都提交到程式碼庫

Alexs-MacBook-Pro:git_trainning alex$ git commit -m "commit changes"

[master 50ad6b5] commit changes

 Committer: Alex Li <[email protected]>

 1 file changed, 1 insertion(+)

提交後,我們再用git status命令看看倉庫的當前狀態:

1

2

3

$ git status

# On branch master

nothing to commit (working directory clean)

Git告訴我們當前沒有需要提交的修改,而且,工作目錄是乾淨(working directory clean)的。

4.2 程式碼回滾

現在,你已經學會了修改檔案,然後把修改提交到Git版本庫,現在,再練習一次,修改first_git_file.txtt檔案如下: 

1

2

3

4

5

First time using git, excited! update ...

insert line here..改之前的.

第一次用git哈哈

insert line again haha...

加點新內容

然後嘗試提交:

1

2

3

4

5

$ git add first_git_file.txt

$ git commit -m "add new content"

[master 4459657] add new content

 Committer: Alex Li <[email protected]>

 1 file changed, 2 insertions(+), 1 deletion(-)

像這樣,你不斷對檔案進行修改,然後不斷提交修改到版本庫裡,就好比玩RPG遊戲時,每通過一關就會自動把遊戲狀態存檔,如果某一關沒過去,你還可以選擇讀取前一關的狀態。有些時候,在打Boss之前,你會手動存檔,以便萬一打Boss失敗了,可以從最近的地方重新開始。Git也是一樣,每當你覺得檔案修改到一定程度的時候,就可以“儲存一個快照”,這個快照在Git中被稱為commit。一旦你把檔案改亂了,或者誤刪了檔案,還可以從最近的一個commit恢復,然後繼續工作,而不是把幾個月的工作成果全部丟失。

現在,我們回顧一下first_git_file.txt檔案一共有幾個版本被提交到Git倉庫裡了: 

版本1

1

2

first time using git, excited!

第一次用git哈哈

版本2

1

2

3

first time using git, excited!

insert line here...

第一次用git哈哈

版本3

1

2

3

4

first time using git, excited!

insert line here...

第一次用git哈哈

insert line again haha...

版本4

1

2

3

4

5

First time using git, excited! update ...

insert line here..改之前的.

第一次用git哈哈

insert line again haha...

加點新內容

當然了,在實際工作中,我們腦子裡怎麼可能記得一個幾千行的檔案每次都改了什麼內容,不然要版本控制系統幹什麼。版本控制系統肯定有某個命令可以告訴我們歷史記錄,在Git中,我們用git log命令檢視:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

$ git log

commit 445965781d1fd0d91e76d120450dd18fd06c7489

Author: Alex Li <[email protected]local>

Date:   Tue Oct 4 18:44:29 2016 +0800

 

    add new content

 

commit be02137bb2f54bbef0c2e99202281b3966251952

Author: Alex Li <[email protected]local>

Date:   Tue Oct 4 17:55:16 2016 +0800

 

    update again

 

commit 50ad6b526810bb7ccfea430663757ba2337b9816

Author: Alex Li <[email protected]local>

Date:   Tue Oct 4 17:46:51 2016 +0800

 

    commit changes

 

commit 621e6e44d04fa6a1cdc37826f01efa61b451abd1

Author: Alex Li <[email protected]local>

Date:   Tue Oct 4 17:42:50 2016 +0800

 

    commit my first git file

git log命令顯示從最近到最遠的提交日誌,我們可以看到4次提交,最近的一次是add new content,上一次是update again,最早的一次是commit my first git file。 如果嫌輸出資訊太多,看得眼花繚亂的,可以試試加上--pretty=oneline引數:

1

2

3

4

5

$ git log --pretty=oneline

445965781d1fd0d91e76d120450dd18fd06c7489 add new content

be02137bb2f54bbef0c2e99202281b3966251952 update again

50ad6b526810bb7ccfea430663757ba2337b9816 commit changes

621e6e44d04fa6a1cdc37826f01efa61b451abd1 commit my first git file

需要友情提示的是,你看到的一大串類似3628164...882e1e0的是commit id(版本號),和SVN不一樣,Git的commit id不是1,2,3……遞增的數字,而是一個SHA1計算出來的一個非常大的數字,用十六進位制表示,而且你看到的commit id和我的肯定不一樣,以你自己的為準。為什麼commit id需要用這麼一大串數字表示呢?因為Git是分散式的版本控制系統,後面我們還要研究多人在同一個版本庫裡工作,如果大家都用1,2,3……作為版本號,那肯定就衝突了。

 

回滾回滾回滾

好了,現在我們啟動時光穿梭機,準備把first_git_file.txt回退到上一個版本,也就是“update again”的那個版本,怎麼做呢?

首先,Git必須知道當前版本是哪個版本,在Git中,用HEAD表示當前版本,也就是最新的提交be02137bb2f54bbef0c2e99202281b3966251952(注意我的提交ID和你的肯定不一樣),上一個版本就是HEAD^,上上一個版本就是HEAD^^,當然往上100個版本寫100個^比較容易數不過來,所以寫成HEAD~100

現在,我們要把當前版本“add new content”回退到上一個版本“update again”,就可以使用git reset命令:

1

2

$ git reset --hard HEAD^

HEAD is now at be02137 update again

此時再看你的檔案內容,果然就退回去了

1

2

3

4

5

$ more first_git_file.txt

First time using git, excited! update ...

insert line here...

第一次用git哈哈

insert line again haha...

此時還可以繼續再往前回退一個版本,不過且慢,然我們用git log再看看現在版本庫的狀態:

1

2

3

4

$ git log --pretty=oneline

be02137bb2f54bbef0c2e99202281b3966251952 update again

50ad6b526810bb7ccfea430663757ba2337b9816 commit changes

621e6e44d04fa6a1cdc37826f01efa61b451abd1 commit my first git file

最新的那個版本add new content已經看不到了!好比你從21世紀坐時光穿梭機來到了19世紀,想再回去已經回不去了,腫麼辦?

辦法其實還是有的,只要上面的命令列視窗還沒有被關掉,你就可以順著往上找啊找啊,找到那個add new content的commit id是445965781d1fd0d91e76d120450dd18fd06c7489

,於是就可以指定回到未來的某個版本:

1

2

git reset --hard 4459657

HEAD is now at 4459657 add new content

版本號沒必要寫全,前幾位就可以了,Git會自動去找。當然也不能只寫前一兩位,因為Git可能會找到多個版本號,就無法確定是哪一個了。

再小心翼翼地看看first_git_file.txt的內容:

1

2

3

4

5

First time using git, excited! update ...

insert line here..改之前的.

第一次用git哈哈

insert line again haha...

加點新內容

果然,我胡漢三又回來了。

Git的版本回退速度非常快,因為Git在內部有個指向當前版本的HEAD指標,當你回退版本的時候,Git僅僅是把HEAD從指向add new content

現在,你回退到了某個版本,關掉了電腦,第二天早上就後悔了,想恢復到新版本怎麼辦?找不到新版本的commit id怎麼辦?

在Git中,總是有後悔藥可以吃的。當你用$ git reset --hard HEAD^回退到update again版本時,再想恢復到最新add new content的版本,就必須找到add new contentL的commit id。Git提供了一個命令git reflog用來記錄你的每一次命令:

1

2

3

4

5

6

7

8

9

10

11

$ git reflog

4459657 [email protected]{0}: reset: moving to 4459657

be02137 [email protected]{1}: reset: moving to HEAD^

4459657 [email protected]{2}: commit: add new content

be02137 [email protected]{3}: reset: moving to be02137bb

50ad6b5 [email protected]{4}: reset: moving to 50ad6b5

621e6e4 [email protected]{5}: reset: moving to 621e6e44

50ad6b5 [email protected]{6}: reset: moving to HEAD^

be02137 [email protected]{7}: commit: update again

50ad6b5 [email protected]{8}: commit: commit changes

621e6e4 [email protected]{9}: commit (initial): commit my first git file

終於舒了口氣,第二行顯示add new content的commit id是4459657,現在,你又可以乘坐時光機回到未來了。

 

 

 

工作區和暫存區

Git和其他版本控制系統如SVN的一個不同之處就是有暫存區的概念。

先來看名詞解釋。

工作區(Working Directory

就是你在電腦裡能看到的目錄,比如我的git_trainning資料夾就是一個工作區:

1

2

ls git_trainning/

first_git_file.txt

版本庫(Repository)

工作區有一個隱藏目錄.git,這個不算工作區,而是Git的版本庫。

Git的版本庫裡存了很多東西,其中最重要的就是稱為stage(或者叫index)的暫存區,還有Git為我們自動建立的第一個分支master,以及指向master的一個指標叫HEAD。

 

 

分支和HEAD的概念我們以後再講。

前面講了我們把檔案往Git版本庫裡新增的時候,是分兩步執行的:

第一步是用git add把檔案新增進去,實際上就是把檔案修改新增到暫存區;

第二步是用git commit提交更改,實際上就是把暫存區的所有內容提交到當前分支。

因為我們建立Git版本庫時,Git自動為我們建立了唯一一個master分支,所以,現在,git commit就是往master分支上提交更改。

你可以簡單理解為,需要提交的檔案修改通通放到暫存區,然後,一次性提交暫存區的所有修改。

俗話說,實踐出真知。現在,我們再練習一遍,先對first_git_file.txt做個修改,比如加上一行內容:

1

2

3

4

5

6

First time using git, excited! update ...

insert line here..改之前的.

第一次用git哈哈

insert line again haha...

加點新內容

update v5

然後,在工作區新增一個readme.md文字檔案(內容隨便寫)。

先用git status檢視一下狀態:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

$ git status

On branch master

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:   first_git_file.txt

 

Untracked files:

  (use "git add <file>..." to include in what will be committed)

 

    readme.md

 

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

Git非常清楚地告訴我們,first_git_file.txt被修改了,而readme.md還從來沒有被新增過,所以它的狀態是Untracked。

現在,使用命令git add . ,再用git status再檢視一下:

1

2

3

4

5

6

7

8

$ git add .

$ git status

On branch master

Changes to be committed:

  (use "git reset HEAD <file>..." to unstage)

 

    modified:   first_git_file.txt

    new file:   readme.md

現在,暫存區的狀態就變成這樣了:

 

(盜圖關係, 這裡readme.txt = first_git_file.txt , LICENSE = readme.md)

所以,git add命令實際上就是把要提交的所有修改放到暫存區(Stage),然後,執行git commit就可以一次性把暫存區的所有修改提交到分支。

1

2

3

4

$ git commit -m "知道暫存區stage的意思了"

[master 9d65cb2] 知道暫存區stage的意思了

 2 files changed, 2 insertions(+)

 create mode 100644 readme.md

一旦提交後,如果你又沒有對工作區做任何修改,那麼工作區就是“乾淨”的:

1

2

3

$ git status

On branch master

nothing to commit, working directory clean

現在版本庫變成了這樣,暫存區就沒有任何內容了:

 

(盜圖關係, 這裡readme.txt = first_git_file.txt , LICENSE = readme.md)

 暫存區是Git非常重要的概念,弄明白了暫存區,就弄明白了Git的很多操作到底幹了什麼。

 

撤銷修改

自然,你是不會犯錯的。不過現在是凌晨兩點,你正在趕一份工作報告,你在readme.md中添加了一行:

1

2

3

#git study repo

git is great

but my stupid boss still prefers SVN.

在你準備提交前,一杯咖啡起了作用,你猛然發現了“stupid boss”可能會讓你丟掉這個月的獎金!

既然錯誤發現得很及時,就可以很容易地糾正它。你可以刪掉最後一行,手動把檔案恢復到上一個版本的狀態。如果用git status檢視一下:

1

2

3

4

5

6

7

8

9

git status

On branch master

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:   readme.md

 

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

你可以發現,Git會告訴你,git checkout -- file可以丟棄工作區的修改:

1

2

3

4

$ git checkout -- readme.md

more readme.md

#git study repo

你剛才新增的2行罵老闆的話就被撤銷了,

命令git checkout -- readme.md意思就是,把readme.md檔案在工作區的修改全部撤銷,這裡有兩種情況:

一種是readme.md自修改後還沒有被放到暫存區,現在,撤銷修改就回到和版本庫一模一樣的狀態;

一種是readme.md已經新增到暫存區後,又作了修改,現在,撤銷修改就回到新增到暫存區後的狀態。

總之,就是讓這個檔案回到最近一次git commit或git add時的狀態。

git checkout -- file命令中的--很重要,沒有--,就變成了“切換到另一個分支”的命令,我們在後面的分支管理中會再次遇到git checkout命令

 

現在假定是凌晨3點,你不但寫了一些胡話,還git add到暫存區了!!!!

1

2

3

4

5

6

7

8

cat readme.md

Git is a distributed version control system.

Git is free software distributed under the GPL.

Git has a mutable index called stage.

Git tracks changes of files.

My stupid boss still prefers SVN.

 

$ git add readme.md

慶幸的是,在commit之前,你發現了這個問題。用git status檢視一下,修改只是新增到了暫存區,還沒有提交: 

1

2

3

4

5

6

$ git status

On branch master

Changes to be committed:

  (use "git reset HEAD <file>..." to unstage)

 

    modified:   readme.md

Git同樣告訴我們,用命令git reset HEAD file可以把暫存區的修改撤銷掉(unstage),重新放回工作區:

1

2

3

$ git reset HEAD readme.md

Unstaged changes after reset:

M   readme.md

git reset命令既可以回退版本,也可以把暫存區的修改回退到工作區。當我們用HEAD時,表示最新的版本。

再用git status檢視一下,現在暫存區是乾淨的,工作區有修改

1

2

3

4

5

6

7

8

9

$ git status

# On branch master

# 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:   readme.md

#

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

還記得如何丟棄工作區的修改嗎?

1

2

3

4

$ git checkout -- readme.md

 

more readme.md

#git study repo

整個世界終於清靜了!

 

刪除操作

在Git中,刪除也是一個修改操作,我們實戰一下,先新增一個新檔案test.txt到Git並且提交:

1

2

3

4

5

$ git add .

$ git commit -m "add test.txt"

[master a8fa95a] add test.txt

 1 file changed, 0 insertions(+), 0 deletions(-)

 create mode 100644 test.txt

一般情況下,你通常直接在檔案管理器中把沒用的檔案刪了,或者用rm命令刪了

1

rm test.txt

這個時候,Git知道你刪除了檔案,因此,工作區和版本庫就不一致了,git status命令會立刻告訴你哪些檔案被刪除了:

1

2

3

4

5

6

7

8

9

$ git status

On branch master

Changes not staged for commit:

  (use "git add/rm <file>..." to update what will be committed)

  (use "git checkout -- <file>..." to discard changes in working directory)

 

    deleted:    test.txt

 

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

現在你有兩個選擇,一是確實要從版本庫中刪除該檔案,那就用命令git rm刪掉,並且git commit:

1

2

3

4

5

6

7

x$ git rm test.txt

rm 'test.txt'

 

$ git commit -m "remove test"

[master 03df00a] remove test

 1 file changed, 0 insertions(+), 0 deletions(-)

 delete mode 100644 test.txt

現在,檔案就從版本庫中被刪除了。

二是刪錯了,因為版本庫裡還有呢,所以可以很輕鬆地把誤刪的檔案恢復到最新版本:

1

$ git checkout -- test.txt

git checkout其實是用版本庫裡的版本替換工作區的版本,無論工作區是修改還是刪除,都可以“一鍵還原”。