跳到主要內容

[Development] git

本篇介紹幾個 git 會用到的基本 command line 指令

安裝 on Mac OS

[tested on macOS Catalina 10.15.1]

(可以用 Brew 安裝,若尚未安裝 Brew: https://brew.sh/index_zh-tw)

$ brew install git
$ git --version

git version 2.21.0 (Apple Git-122.2)

  github 官網就有提供 GUI 的應用,想用 GUI 的直接去下載安裝就行了,也有簡單明瞭的教學,非常容易。另外也有好幾個第三方的好用 GUI 介面,Google 一下比較一下選自己喜歡的也行。
  但以下還是用 command line 的方法來操作,因為這還是最 general 到哪都可以用的基本方法。因為實務上,比如 code 都放在公司的 server,你可能也是都要 ssh 到 sever 上去改 code,改完之後要上傳到 github。而公司的 server 就是一台 Linux 環境,很可能是沒有提供 GUI 讓你使用的,所以你就只能用 command line 的方式完成 git 的上傳。

Create Repo

  去本地一個你想要放置 git 專案的地方,比如我想把我之後的 git code 都放在我 Mac local 的 /Users/chungchris/git

$ cd /Users/chungchris/git
$ git init

  就會看到在此 git 目錄下產生一個隱藏的 .git 資料夾,這樣就完成了:

(base) Chris-MBP:git chungchris$ ls -al
total 0
drwxr-xr-x   3 chungchris  staff    96 11 21 11:01 .
drwxr-xr-x+ 53 chungchris  staff  1696 11 21 10:45 ..
drwxr-xr-x   9 chungchris  staff   288 11 21 11:01 .git

  所以如果之後想要把他刪掉,其實就是把 .git 刪掉就行了:

$ rm -rf .git/

  然而,一般來說,/Users/chungchris/git 下面我會想要放很多 project,而不是只有單一 project。而剛剛做的 git init 其實就是在本地新增一個 repo 的意思,而一個 project 可能會包含一個到數個 repo。
  所以我們更想做的應該是要在 /Users/chungchris/git 下面創一個 project 專屬的資料夾,然後把這個資料夾 init 為一個 git 的 repo,或者甚至是在 project 的資料夾下創造多個 repo。

$ pwd
/Users/chungchris/git

$ mkdir project20191121
$ cd project20191121/

$ git init
Initialized empty Git repository in /Users/chungchris/git/project20191121/.git/
(base) Chris-MBP:project20191121 chungchris$ ls -al
total 0
drwxr-xr-x  3 chungchris  staff   96 11 21 11:25 .
drwxr-xr-x  4 chungchris  staff  128 11 21 11:25 ..
drwxr-xr-x  9 chungchris  staff  288 11 21 11:25 .git

  比如我上面就創了一個名為 project20191121 的 folder,並將他做 git init,於是這個 folder 的 path 就從此成為了一個 git repo,如果你將來不希望他是一個 git repo,而只是一個普通的 folder,那就將 .git 刪掉就可以了。

  接下來我們設置身份,這在之後上傳雲端 git 的時候需要用到。
  首先他必須符合我們的 github 帳號,比如這是我的 github 帳號是 "chungchris"
那我就在本地這樣設置,注意大小寫要一樣才行:

$ git config --global user.name "chungchris"
$ git config --global user.email "imchungchris@hotmail.com"

$ git config --get --global user.name
chungchris
$ git config --get --global user.email
imchungchris@hotmail.com

Creste File

  接著我們試著在此 repo 下新增一個檔案:

$ vim first_try.txt
$ git status 
On branch master

No commits yet

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

first_try.txt


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

  比如上面我就新增了一個名為 first_try.txt 的文檔,然後我就可以透過 git status 來看一下我目前這個 repo 下的狀態,git status 在往後都會是一個非常基本且常用的指令。
  我們會看到 git 告訴我們在 master 這個 branch 上面,多出了一個尚未被 track/commit 的檔案 first_try.txt,並建議用 git add 指令將其納入 track。(branch 的概念我們往後再敘述,現在姑且都先在 master 上操作)

Commit

  於是我們就依照他的建議做 add:

$ git add first_try.txt 
$ git status 
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

new file:   first_try.txt


  此時再看一下 git status,first_try.txt 就變成了綠色,也就是被納入 track 了。

  接著 git 建議我們將其 commit:

$ git commit first_try.txt

  就會出現 commit message 的編輯頁:

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
#
# Initial commit
#
# Changes to be committed:
#       new file:   first_try.txt
#

~  

  你可以在此頁當中說明你 commit 這一筆 change 的原因,比如我們現在是新增了一個 file,那我們為什麼要新增這個 file,用途是什麼。未來如果我們做的事情是編輯了某個既有的 file,那原因可就要敘述為什麼做此改動,改動的方式是什麼之類的。這在實務上是非常重要的,因為其他在這個 project 上工作的人就可以透過這個 commit message 來快速知道- 誰、在哪個時間點、因為什麼原因、做了什麼改動,如果描述得不清不楚,那就變得要實際去做 code change review,那就比較辛苦耗時了。

  這裏我們就編輯一下 commit message,理由寫上 "Just for test",然後用 ESC 和 ":wq" 儲存完成編輯。
(另外,下方 '#' 開頭的幾行在之後都是不會出現的)

Just for test
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
#
# Initial commit
#
# Changes to be committed:
#       new file:   first_try.txt

#


$ git commit first_try.txt 

[master (root-commit) b028b64] Just for test
 1 file changed, 2 insertions(+)

 create mode 100644 first_try.txt

  到此 commit 就完成了,於是我們再看一次 git status:

$ git status 

On branch master

nothing to commit, working tree clean

  就告訴我們目前所做的更動都已經 commit 了。
  如果想要更改 commit msg 也是可以的:

$ git commit --amend

  如果想要做復原呢?
  比如我們剛剛在這個 repo 下做的事情就是新增了一個 first_try.txt 然後將其 commit 了,如果我要回到還沒有做這個動作的時候,我們可以用 reset 指令。
  但這時候我們可能會遇到一錯誤:

$ git reset HEAD^

fatal: ambiguous argument 'HEAD^': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:

'git <command> [<revision>...] -- [<file>...]

  這是因為我們剛剛做的事情是盤古開天第一件事,所以沒有 HEAD。所以我們嘗試再多做一點,比如現在我們再進一步對已經 commit 的 first_try.txt 做一次編輯,git status 就會看到 first_try.txt 又變為紅色,狀態顯示為 modified。

$ vim first_try.txt 
$ 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_try.txt


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

  這裏再介紹一個非常基本且常用的指令 git diff

$ git diff .

diff --git a/first_try.txt b/first_try.txt
index 0b1076d..751d415 100644
--- a/first_try.txt
+++ b/first_try.txt
@@ -1,2 +1,3 @@
 firstfirstfirst
+2222


  透過這個指令,可以看到我們所做的更改是在 first_try.txt 這個檔案新增了一行 "2222" 的文字。

  於是我們跟剛剛一下做 add 和 commit

$ git add -A .

$ git commit -m "make a small change"
[master ed597bd] make a small change
 1 file changed, 1 insertion(+)
(base) Chris-MBP:project20191121 chungchris$ git status .
On branch master

nothing to commit, working tree clean

  "git add -A ." 會將當前目錄下所有 untracked 都 add;
  "git commit -m" 在你的 commit message 只有短短幾個字的時候是很方便的,比如我現在想寫的 commit msg 只有 "make a small change";
  也可以用 “git commit -a -m " 進一步化兩行為一行。
  此時我們再看一下 git status,就又變為 clean 了。

  這個時候我們就可以嘗試做 reset,讓他變為還沒有新增 2222 那一行的狀態:

$ git reset HEAD^
Unstaged changes after reset:
M first_try.txt

$ 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_try.txt


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

  就可以看到做了 reset 之後,first_try.txt 又變回紅色的 untracked 狀態了,但此時 2222 還是在的喔,只是我們剛剛做的 add/commit 已經被復原了。
  reset HEAD 後面的上標三角是指次數,因為我現在只要回復一級,所以只有一個三角,若想要直接復原兩次 commit 那就給他兩個三角 HEAD^^

  現在 first_try.txt 已經回到了 untracked 狀態,那要怎麼真的把 2222 那行刪掉呢?可以用 checkout 指令。
  這裡要用 git 的概念來理解,我們剛剛做過兩件事,第一件事是新增了 first_try.txt,第二件事是進一步再編輯了一次 first_try.txt 新增了 "2222" 這一行,而第一件事已經 commit 了,第二件事曾經 commit 過但又被 reset 了,所以現在等於是第二件事情未 commit 的狀態。
  也就是說現在對 git 來說,他承認的官方狀態是我們做完第一件事的狀態,所以當我們使用了 checkout 這個指令,意思就是把當下 repo 承認的官方狀態給 checkout 出來,而這就覆蓋那個被我們就進一步編輯過的 first_try.txt,所以效果上就跟復原一樣。

$ git checkout .
Updated 1 path from the index

$ git status .
On branch master

nothing to commit, working tree clean

$ cat first_try.txt 
firstfirstfirst

  可以看到做完 checkout 後,repo 狀態又變為 clean 了,於是也可以理解所謂 clean 都是與 git 承認的官方狀態去做比較的。

Upload

  我們在本地端有一個 repo,在雲端也給他個相應的 repo,名稱可以不同的,因為對於本地來說,他其實就可以被視為僅是一個有了 git 屬性的 folder,跟實際上 repo 的名字沒有必然直接關係。
  比如我透過 github 網頁創了一個 repo:

  上面可以看到一行有 remote 的指令,顧名思義如果我們在 local 執行了這行指令,那原本單純都只是在我們 local 操作的 git repo,就和這個剛剛在 github 上創造的名為 test 的 repo 產生了連結:

$ git remote add origin https://github.com/chungchris/test.git

  接著我們便可以把 local 做的改動傳到雲端了,當然指的是已經 local commit 的改動。
  上傳的這個動作在 git 中叫做 push:

$ git push origin master
Username for 'https://github.com': chungchris
Password for 'https://chungchris@github.com': 
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 240 bytes | 240.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/chungchris/test.git

 * [new branch]      master -> master

  上面可以看到他要求我輸入帳號密碼,這可以藉由 config 簡化,稍後再述。
  此時我們去網頁上看,就可以看到 first_try.txt 已經被上傳了:


  並且它的內容是我們早先 local commit 版本:

  點進去就看了了我們的 commit msg 以及做過的改動:

  另外,多筆 commit 是可以一次 push 到 remote 的,只是上面例子當中只有一筆。

  回到剛剛的 git push 指令,我們下的是 "git push origin master"。
  第一個參數是 remote 是誰,因為我們更早下過
    "git remote add origin https://github.com/chungchris/test.git",
  所以自然 git 就知道這裡的 origin 指的是 https://github.com/chungchris/test.git
  第二個參數是 branch 名稱,一樣 branch 的概念我們之後再一次一起敘述。
  但這裡可以先提的是,我們本地的 repo 已經和 remote 的 repo 產生連結,實務上,我們基本上就認定在這個 folder 底下的工作就都 default 視為要傳到該特定 remote 的特定 branch 了,如果這個需求成立,我們可以做進一步的綁定

$ git checkout master
Already on 'master'

$ git branch -u origin/master
Branch 'master' set up to track remote branch 'master' from 'origin'.

  從此我們要在這個 local repo 底下工作,要 push 東西到 remote 時,就直接 git push 就可以了,不需再指定 remote 和 branch。上述也可以透過第一次做 push 時加上 -u 參數來完成,u 指的是 upstream。
  接下來我們嘗試在 local 再對 first_try.txt 做進一步編輯並 commit 後上傳:

$ vim first_try.txt 

$ git status .
On branch master
Your branch is up to date with 'origin/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_try.txt

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

$ git commit -a -m "add one more line"
[master c89866e] add one more line
 1 file changed, 1 insertion(+), 1 deletion(-)
(base) Chris-MBP:project20191121 chungchris$ git status .
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 293 bytes | 293.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/chungchris/test.git

   7f2499f..c89866e  master -> master

在 github 上就又可以看到了


Pull

  前面講的是本地創造然後上傳,接下來將的就是下載,這是一個更常用的情境,有可能是下載別人的 repo 做進一步更改再上傳回去做貢獻,或者是下載自己的 repo 做進一步的開發。
  我們一樣就用剛剛已經在 github 上創造的 test 這個 repo 為例,假設這個 repo 就是我今天要下載下來本地編輯的 repo:
  到 github 的該 repo,就會看到 clone/download 的選項:

  這裏我在本地創了個新 folder 然後透過 git clone 把 code 抓下來:

$ mkdir repo_test
$ cd repo_test/
$ pwd
/Users/chungchris/repo_test

$ git clone https://github.com/chungchris/test.git
Cloning into 'test'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 6 (delta 0), reused 3 (delta 0), pack-reused 3
Unpacking objects: 100% (6/6), done.

$ ls -al
total 0
drwxr-xr-x   3 chungchris  staff    96 11 21 13:12 .
drwxr-xr-x+ 54 chungchris  staff  1728 11 21 13:09 ..

drwxr-xr-x   4 chungchris  staff   128 11 21 13:12 test

$ cd test/
$ ls -al
total 8
drwxr-xr-x   4 chungchris  staff  128 11 21 13:12 .
drwxr-xr-x   3 chungchris  staff   96 11 21 13:12 ..
drwxr-xr-x  12 chungchris  staff  384 11 21 13:12 .git
-rw-r--r--   1 chungchris  staff   52 11 21 13:12 first_try.txt

  可以看到抓下來的 test 本身就已經維持是一個 git 的 repo 了。
  於是我們嘗試對 first_try.txt 做編輯,然後一樣 commit 後 push:

$ vim first_try.txt 

$ git commit -a -m "make a contribution"
[master 40b2085] make a contribution
 1 file changed, 3 insertions(+), 1 deletion(-)

$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 294 bytes | 294.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/chungchris/test.git

   c89866e..40b2085  master -> master

  在 github 上就又可以看到我們的改動了:





留言

這個網誌中的熱門文章

[Development] Deep Learning over Python 深度學習基於 Python 1/3

  用 Python 練習實作深度學習,主要包含 Tensorflow 以及 SVM,著重於實作範例,未深入涉及各 DL 演算法模型原理及其數學。 大綱 [Development] Deep Learning over Python  深度學習基於 Python  1/3 Basic Tools: 基於 python- numpy, matplotlib, scipy, Pandas [Development] Deep Learning over Python 深度學習基於 Python 2/3 Keras (基於 Tensorflow)  Tensorflow [Development] Deep Learning over Python 深度學習基於 Python 3/3 Support Vector Machine (SVM) 強化學習 Reinforcement Learning (RL) Reference: 本篇基於  Udemy 課程 "吳佳諺- Python 深度學習"  為架構出發衍生,其他引用資料個別在段落中標示 Basic Tools numpy import numpy as np from scipy import linalg   # linear algebra import matplotlib.pyplot as plt myarray = np.array([[1, 2, 3], [4, 5, 6]]) print('myarray:', myarray) myarray_allzero = np.zeros((3, 5), dtype = np.int16) print('myarray_allzero:', myarray_allzero) myarray_2 = np.arange(15, dtype = np.int64) print('myarray_2:', myarray_2) myarray_2 = myarray_2.reshape((3, 5)) print('myarray_2 reshape:', myarray_2) myarray: [[1 2 3...

[Development] Deep Learning over Python 深度學習基於 Python 2/3

  用 Python 練習實作深度學習,主要包含 Tensorflow 以及 SVM,著重於實作範例,未深入涉及各 DL 演算法模型原理及其數學。 大綱 [Development] Deep Learning over 深度學習基於 Python Python 1/3 Basic Tools: 基於 python- numpy, matplotlib, scipy, Pandas [ Development] Deep Learning over Python  深度學習基於 Python  2/3 Keras (基於 Tensorflow) , 使用 Sequential 模型(範例 實作 "MINST 手寫辨識") CNN (Convolutional Neural Networks) 卷積神經網路 (範例 實作 "MINST 手寫辨識") RNN (Recurrent Neural Networks) 遞歸(迴)神經網路 (範例 實作 "IMPb 評論辨識") Tensorflow 基礎應用 NN 基本作用原理 CNN (Convolutional Neural Networks) 卷積神經網路 (範例 實作 "MINST 手寫辨識") [Development] Deep Learning over Python 深度學習基於 Python 3/3 Support Vector Machine (SVM) 強化學習 Reinforcement Learning (RL) Reference: 本篇基於 Udemy 課程 "吳佳諺- Python 深度學習" 為架構出發衍生,其他引用資料個別在段落中標示 Keras Keras 是一个用 Python 编写的高级神经网络 API,它能够以  TensorFlow ,  CNTK , 或者  Theano  作为后端运行 Keras 的核心数据结构是  model ,一种组织网络层的方式。最简单的模型是  Sequential 顺序模型 ,它由多个网络层线性堆叠。对于更复杂的结构,你应该使用  Keras 函数式 API ,它允许构建任意的...

[Development] Deep Learning over Python 深度學習基於 Python 3/3

  用 Python 練習實作深度學習,主要包含 Tensorflow 以及 SVM,著重於實作範例,未深入涉及各 DL 演算法模型原理及其數學。 大綱 [Development] Deep Learning over Python 深度學習基於 Python 1/3 Basic Tools: 基於 python- numpy, matplotlib, scipy, Pandas [Development] Deep Learning over Python 深度學習基於 Python 2/3 Keras (基於 Tensorflow)  Tensorflow [Development] Deep Learning over Python 深度學習基於 Python 3/3 Support Vector Machine (SVM) 萬用分類機 Reinforcement Learning (RL) 強化學習 Reference: 本篇基於  Udemy 課程 "吳佳諺- Python 深度學習"  為架構出發衍生,其他引用資料個別在段落中標示 Support Vector Machine (SVM) 萬用分類機   SVM 就是用統計上風險最小化的方法,來產生一個分類的超平面 (hyperplane),也就是找到一個決策邊界 (decision boundary),該邊界可以讓「兩類」之間的邊界 margins 最大化。   SVM 是一個二元分類器,也就是只能做非黑即白的分類判斷。   如果資料只有二維,如在二維直線座標系中標記了許多點,若這些點概念上可以被分成兩類,我們就可以透過迴歸分析的幫助,找出一條線來最適當的把這兩類的點切分開來,這條線如果是直線,那就是一個一元一次方程式,如果是曲線那當然就是二次三次或更高。   而該怎麼找出最佳的一個切割面?SVM 的提議也很簡單,他假設如果存在一個 hyperplane,這個 hyperplane 距離所有 A/B 類節點的距離總和最長,那他就是一個最佳的分割面,這裡的距離的概念也就是 margin。簡單理解就是,處在兩個點的「最中間」位置的那條線就是這兩點的最佳切割線。  ...