[Git] 在 Mac 上使用 Git 入門

[Git] 在 Mac 上使用 Git 入門

很久以前曾經在 Windows 上用 Git Gui,不過其實對 Git 的使用相當不熟,

剛好最近公司的比賽會用到 Gitlab,決定還是用 Git 命令列來開始慢慢熟悉 Git,

這樣也比直接使用 GUI 版本的 Git 要更能理解 Git 的一些底層運作方式~

 

1. 安裝 Git

可以直接去 Git 官網下載給 Mac 用的安裝檔

或是用 Homebrew 來安裝:

brew install git

 

2. 初始化 Git

進到要被 Git 管理的目錄,執行 git init,

就會在這個目錄下建立隱藏的 .git 目錄:

testuser@localhost ~/Dropbox/MyProjects $ git init
Initialized empty Git repository in /Users/testuser/Dropbox/MyProjects/.git/

 

或者是從遠端的 GitHub/Gitlab 伺服器複製 Git repository 下來的話,

可以用 git clone,就會將遠端的 Git 目錄複製下來 (同樣包含 .git 目錄):

git clone git@gitlab.test.com:machine-learning/machine-learning.git

  

接下來可以作一些設定,最常見的就是設定使用者名稱和 email 帳號:

git config --local user.name "testuser"
git config --local user.email "testuser@test.com"

 

這個例子裡指定了 –local,表示這兩個設定只屬於這個 Git repository 目錄, 

另外還可以下 –global 表示這個使用者,或是 –system 表示整個系統都用這個設定~

設定好之後,執行 git config –local -l 就可以看目前 repository 的設定:

testuser@localhost ~ $ git config --local -l
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
core.precomposeunicode=true
remote.origin.url=git@gitlab.test.com:machine-learning-sig/machine-learning.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
branch.master.remote=origin
branch.master.merge=refs/heads/master
user.name=testuser
user.email=testuser@test.com

 

如果省略了 –local (或 –global, –system) 選項的話,會由 system, global, local 依序顯示出來,

雖然全部都顯示出來,但同名的設定是會由下面的去覆蓋上面的~

例如我在 –global 那邊設定使用者名稱是 globaluser,–local 設定使用者名稱是 testuser,

那麼在此 repository 用到的使用者名稱會是 testuser:

testuser@localhost ~ $ git config -l
user.name=globaluser
user.email=globaluser@gmail.com
......
user.name=testuser
user.email=testuser@test.com

 

3. 將檔案加入 Git 索引

執行 git status 可以看到有哪些檔案尚未被加入索引 (Untracked files),

或是哪些檔案有被修改過,但尚未加入索引~

例如下面的 convert_mnist_data.py, round1.py, visualize_digit_image.py 這幾個檔案,

和 test 與 train 兩個目錄,都尚未加入索引:

testuser@localhost ~ $ 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)
Users/testuser/convert_mnist_data.py
Users/testuser/round1.py
Users/testuser/test/
Users/testuser/train/
Users/testuser/visualize_digit_image.py

 

執行 git add 就可以將檔案加入 Git 索引,

而 git add . 可以將目前目錄下所有的檔案/目錄都加到索引裡,

為了避免加入不必要的檔案,可以加上 -n 選項,先讓 git 列出來要加入哪些檔案:

testuser@localhost ~ $ git add -n .
add 'Users/testuser/convert_mnist_data.py'
add 'Users/testuser/round1.py'
add 'Users/testuser/test/00000000'
add 'Users/testuser/test/00000001'
add 'Users/testuser/test/00000002'
add 'Users/testuser/test/00000003'
......

 

一執行就發現 test 目錄下有一堆檔案,並不是我想加入 git 的…

因為我才剛開始使用,不太確定加入一大堆小檔案對 Git 的效率是否會有影響,

但考慮到其他人如果從 Gitlab 抓一堆檔案下去也很耗時,

決定還是不要用 git add . 的方式加檔案,而是一個個加進去~

雖然是一個個加,但可以放在同一個 git add 命令裡面,多次 git add 也可以:

git add convert_mnist_data.py round1.py visualize_digit_image.py
git add train_files.tgz

 

這時執行 git status,就可以看到我們分次加入的檔案,都被列在將要 commit 的區域:

testuser@localhost ~ $ 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:   convert_mnist_data.py
new file:   round1.py
new file:   train_files.tgz
new file:   visualize_digit_image.py
Untracked files:
(use "git add <file>..." to include in what will be committed)
test/
train/

 

執行 git commit 可以將已經加入索引的檔案 commit 進 Git,用 -m 選項加入 check in 的敘述,例如:

testuser@localhost ~ $ git commit -m "First check in"
[master 37deb96] First check in
4 files changed, 106 insertions(+)
create mode 100755 convert_mnist_data.py
create mode 100755 round1.py
create mode 100644 train_files.tgz
create mode 100755 visualize_digit_image.py

 

commit 完之後,再執行 git status,可以看到已經沒有被加到索引,但尚未 commit 的檔案了~

但 git status 還是會持續提醒那些尚未被加入索引的檔案/目錄 (本例中的 test 和 train 目錄):

testuser@localhost ~ $ git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
Untracked files:
(use "git add <file>..." to include in what will be committed)
test/
train/
nothing added to commit but untracked files present (use "git add" to track)

 

如果這時候修改了已經加入過 Git 索引的檔案,git status 就會將它列出來,

例如我修改了 round1.py,就可以看到它被列在 modified 區域:

testuser@localhost ~ $ 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:   round1.py
Untracked files:
(use "git add <file>..." to include in what will be committed)
sample.csv
test/
train/

 

假設想看看這個檔案跟已經 commit 的最終版本的差異的話,

可以執行 git diff HEAD <file name>:

testuser@localhost ~ $ git diff HEAD round1.py
diff --git a/round1.py b/round1.py
index 7b4f33c..59e8a86 100755
--- a/round1.py
+++ b/round1.py
@@ -99,7 +99,7 @@ def main():
sess.run(init)
for i in range(1000):
-        # print "Training batch", i
+        print "Training batch", i
batch_xs, batch_ys = mnist.train.next_batch(100)
sess.run(train, feed_dict={x: batch_xs, y_: batch_ys})

  

4. 檢視 commit 過的記錄 

執行 git show 可以看到最近一次的 commit 記錄,還可以看到 diff 的結果:

testuser@localhost ~ $ git show
commit 66615141c09b44bc1df555334453b1ae52bfbb3f
Author: testuser <testuser@test.com>
Date:   Sun Apr 10 16:01:27 2016 +0800
Use universal newline mode to open label csv files
diff --git a/round1.py b/round1.py
index d788ac4..7b4f33c 100755
--- a/round1.py
+++ b/round1.py
@@ -169,7 +169,7 @@ def extract_labels(file_path):
"""Extract the labels into a 1D uint8 numpy array [index]."""
print 'Extracting labels from', file_path
-    with open(file_path, 'rb') as csv_file:
+    with open(file_path, 'rU') as csv_file:
csv_reader = csv.reader(csv_file)
labels = [int(row[1]) for row in csv_reader]
labels = numpy.array(labels)

 

但若是想看更早之前的 commit 記錄,可以用 git log 指令,

這個指令後面也可以接一個檔案路徑,代表只看那個檔案的修改記錄:

testuser@localhost ~ $ git log
commit 66615141c09b44bc1df555334453b1ae52bfbb3f
Author: testuser <testuser@test.com>
Date:   Sun Apr 10 16:01:27 2016 +0800
Use universal newline mode to open label csv files
commit a4b5263dbbb5f949afa2a6e34ae9eacdfd0a15f6
Author: testuser <testuser@test.com>
Date:   Sun Apr 10 15:55:15 2016 +0800
Output sample.csv with Unix line-ending

 

從 git log 命令的輸出結果裡,可以看到每個 commit 記錄都帶了一個長長的 ID,

像是 66615141c09b44bc1df555334453b1ae52bfbb3f,a4b5263dbbb5f949afa2a6e34ae9eacdfd0a15f6 等等~

git diff 可以加上一個 commit ID,來顯示出那個 commit 和最新版本間的差異,

也可以加上兩個 commit ID,來顯示這兩個 commit 間的差異,例如: 

testuser@localhost ~ $ git diff a4b5263dbbb5f949afa2a6e34ae9eacdfd0a15f6 e8f055b853f69cf9a2d9d8bb4f60f348416d2e09
diff --git a/round1.py b/round1.py
index d788ac4..212f6c3 100755
--- a/round1.py
+++ b/round1.py
@@ -110,7 +110,7 @@ def main():
with open(OUTPUT_SAMPLE_PROBABILITY_CSV, "wb") as csvfile:
print "Writing probabilities to", OUTPUT_SAMPLE_PROBABILITY_CSV
-        csvwriter = csv.writer(csvfile, lineterminator='\n')
+        csvwriter = csv.writer(csvfile)
probabilities_all = y.eval(feed_dict={x: mnist.test.images}, session=sess)
for (i, probabilities_for_i) in enumerate(probabilities_all):

 

5. 將本地端的修改與遠端的 Gitlab 同步

因為我之前是用 git clone 從遠端 Gitlab 複製 repository 下來的,

因此接下來在本地端的修改,可以用 git push 上傳到伺服器上~

在這之前,通常要先將遠端的 Git repository 設定成名字 origin,

這樣之後 git push/pull 可以省略這個名稱:

git remote add origin git@gitlab.test.com:machine-learning/machine-learning.git

 

接著就可以執行 git push 指令,將檔案上傳至伺服器上:

testuser@localhost ~ $ git push
warning: push.default is unset; its implicit value has changed in
Git 2.0 from 'matching' to 'simple'. To squelch this message
and maintain the traditional behavior, use:
git config --global push.default matching
To squelch this message and adopt the new behavior now, use:
git config --global push.default simple
When push.default is set to 'matching', git will push local branches
to the remote branches that already exist with the same name.
Since Git 2.0, Git defaults to the more conservative 'simple'
behavior, which only pushes the current branch to the corresponding
remote branch that 'git pull' uses to update the current branch.
See 'git help config' and search for 'push.default' for further information.
(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode
'current' instead of 'simple' if you sometimes use older versions of Git)
Counting objects: 8, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (7/7), done.
Writing objects: 100% (8/8), 13.36 MiB | 2.01 MiB/s, done.
Total 8 (delta 1), reused 0 (delta 0)
To git@gitlab.test.com:machine-learning/machine-learning.git
5aeb8ff..37deb96  master -> master

 

上面的 git push 指令前面吐出了一些警告訊息,

大意是說 git push 在 2.0 的時候作了一些行為的改變,

如果沒有特殊需求的話,應該就是執行 git config –global push.default simple,

之後就不會再有警告訊息了~

 

執行 git pull 的話,則是將伺服器上的變更記錄傳到本地端,

這個動作有可能會導致衝突需要解決,但目前我還沒有遇到:

testuser@localhost ~ $ git pull
Already up-to-date.

 

這篇文章簡單地將我目前使用到的 git 指令都列出來,

不過一些進階的指令像是 branch, merge, rebase 等等都還沒學到,

希望儘快熟悉 Git 後可以學到囉~^^

 

參考資料:

連猴子都能懂的 Git 入門指南

30 天精通 Git 版本控管

 

(本頁面已被瀏覽過 1,060 次)

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料