Hello, Git

使用者設定

設定使用者的信箱和名稱

// 先查看目前設定
$ git config --list 

// 設定信箱,名稱
$ git config --global user.name "pengpon"
$ git config --global user.email "xxxx@gmail.com"

直接更改 家目錄底下.gitconfig也可以!

新建Repository

// 建立新目錄
$ mkdir git-demo 
$ cd git-demo

// 初始化, 對這個目錄進行版控
$ git init
D:\IT\practice\git-demo>git init
Initialized empty Git repository in D:/IT/practice/git-demo/.git/

// 目錄下會新增.git這個目錄

git 工作目錄

圖取自為你自己學git

在Git中分成Working Directory Staging Area Repository三個區塊

  • 利用git add將檔案放入Staging Area
  • 利用git commit將Staging Area檔案放入Repo

讓Git控管

在專案目錄下新增一index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>hellp git</title>
</head>
<body>
  
</body>
</html>

查看目前專案狀態

$ git status
On branch master

No commits yet

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

        index.html

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

告知目前在master分支上, 還沒有任何commit

有一個檔案index.html為Untracked-> 尚未被加入版控被追蹤

交付這個檔案給Git, 使用git add

$ git add index.html
$ git status
On branch master

No commits yet

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

        new file:   index.html

此時, index.html變成new file的狀態, 表示這個檔案已被放在Staging Area中

比較git add .git add --all

git add .會將目前目錄下的所有子目錄, 子子目錄都加到Staging Area

git add --all 是將專案所有異動都加入Staging Area

提交這次異動到 Repo, 使用git commit

$ git commit -m "init commit"
[master (root-commit) 54bad65] init commit
 1 file changed, 11 insertions(+)
 create mode 100644 index.html

練習方便, 要製造空的commit, 可使用git commit --allow-empty -m "空的"

檢視紀錄

檢視commit紀錄

$ git log
commit 4a28350f1ec538b833d44feb2efd0133b18128ea (HEAD -> master)
Author: pengpon <pengpon214@gmail.com>
Date:   Sun Mar 21 14:06:10 2021 +0800

    空的

commit 76d880eae294f30e7fe6f9a8b26454c81fd5c7ca
Author: pengpon <pengpon214@gmail.com>
Date:   Sun Mar 21 14:06:08 2021 +0800

    空的

commit 196ce5e96fd50a0d6c6881022dde1f5abf3d96a2
Author: pengpon <pengpon214@gmail.com>
Date:   Sun Mar 21 14:05:54 2021 +0800

    空的

commit 54bad65aa0211a96d55798eca2f9fbd24cfb001e
Author: pengpon <pengpon214@gmail.com>
Date:   Sun Mar 21 14:04:03 2021 +0800

    init commit
    
    

可以查看每一次的commit作者, 什麼時候commit, commit訊息

其他git log不同的輸出格式

$ git log --oneline
4a28350 (HEAD -> master) 空的
76d880e 空的
196ce5e 空的
54bad65 init commit

$ git log --oneline --graph
* 4a28350 (HEAD -> master) 空的
* 76d880e 空的
* 196ce5e 空的
* 54bad65 init commit

部分Commit

只想要commit檔案的某一個區塊

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>hellp git</title>
</head>
<body>
  <h1>我是標題</h1>
  <p>123123</p>
  <header>我是header</header>
  <div>我是內容</div>
  <footer>我是footer</footer>
</body>
</html>

index.html增加三行

  <header>我是header</header>
  <div>我是內容</div>
  <footer>我是footer</footer>

使用git add加上-p參數, 會跳出詢問, 是否要把hunk區塊加入暫存

y->加入整個檔案

e->編輯要加入的內容

$ git add -p index.html
diff --git a/index.html b/index.html
index 68e0558..ccf8e23 100644
--- a/index.html
+++ b/index.html
@@ -8,5 +8,8 @@
 <body>
   <h1>我是標題</h1>
   <p>123123</p>
+  <header>我是header</header>
+  <div>我是內容</div>
+  <footer>我是footer</footer>
 </body>
 </html>
\ No newline at end of file
Stage this hunk [y,n,q,a,d,e,?]?
// 選擇e 

跳出一編輯器, 顯示

# Manual hunk edit mode -- see bottom for a quick guide.
@@ -8,5 +8,8 @@
 <body>
   <h1>我是標題</h1>
   <p>123123</p>
+  <header>我是header</header>
+  <div>我是內容</div>
+  <footer>我是footer</footer>
 </body>
 </html>
\ No newline at end of file
# ---
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.
# 
# If the patch applies cleanly, the edited hunk will immediately be
# marked for staging.
# If it does not apply cleanly, you will be given an opportunity to
# edit again.  If all lines of the hunk are removed, then the edit is
# aborted and the hunk is left unchanged.

留下要加入暫存區的內容, 存檔離開

修改 Commit紀錄

方法:

  • 使用git rebase
  • git reset拆掉commit, 再重新commit
  • 使用--amend 來修改最後一次commit (只有最後一次)

實作amend-修改最後一次commit

$ git log --oneline // 先檢視目前log
0263340 (HEAD -> master) waaaaaaa
4a28350 空的
76d880e 空的
196ce5e 空的
54bad65 init commit

$ git commit --amend -m "add title"
[master 2e9b57c] add tittle
 Date: Sun Mar 21 14:20:20 2021 +0800
 1 file changed, 1 insertion(+), 1 deletion(-)
 
$ git log --oneline
2e9b57c (HEAD -> master) add tittle
4a28350 空的
76d880e 空的
196ce5e 空的
54bad65 init commit

追加異動到最後一次commit中, 也可使用–amend

// 增加檔案style.css, 異動index.html

$ 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:   index.html

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

        style.css

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

$ git add --all

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   index.html
        new file:   style.css

// 把檔案或是異動併入最後一次commit
$ git commit --amend --no-edit
[master f8fe91b] add tittle
 Date: Sun Mar 21 14:20:20 2021 +0800
 2 files changed, 2 insertions(+), 1 deletion(-)
 create mode 100644 style.css
 

實作修改歷史訊息-rebase

$ git log --oneline
4449f50 (HEAD -> master) Merge branch 'feature4'
439adbf (feature4) add text
a130319 feature4 function
5979365 conflict fixed
d0f8be1 (feature2) more function
ae6b919 add line
f8fe91b add tittle
4a28350 (feature1) 空的
76d880e 空的
196ce5e 空的
54bad65 init commit

// 使用rebase整理commit 
$ git rebase - 5979365

-i代表要進入互動模式, 後面接 從哪個commit開始

會跳出編輯器

pick a130319 feature4 function
pick 439adbf add text

# Rebase 5979365..439adbf onto 5979365 (2 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
~
~
~
~

p:保留commit

r:修改commit 訊息

pick a130319 feature4 function
r 439adbf add text

esc+:wq存檔離開

再跳出另一個編輯器

add text

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Sun Mar 21 22:13:40 2021 +0800
#
# interactive rebase in progress; onto 5979365
# Last commands done (2 commands done):
#    pick a130319 feature4 function
#    reword 439adbf add text
# No commands remaining.
# You are currently editing a commit while rebasing branch 'master' on '5979365'.
#
# Changes to be committed:
#       modified:   index.html

輸入新的commit 訊息, add text 改成 modify text, 存檔離開

$ git rebase -i 5979365
[detached HEAD e8bf6eb] modify text
 Date: Sun Mar 21 22:13:40 2021 +0800
 1 file changed, 1 insertion(+)
Successfully rebased and updated refs/heads/master.


$ git log --oneline
e8bf6eb (HEAD -> master) modify text
a130319 feature4 function
5979365 conflict fixed
d0f8be1 (feature2) more function
ae6b919 add line
f8fe91b add tittle
4a28350 (feature1) 空的
76d880e 空的
196ce5e 空的
54bad65 init commit

退回到某一個commit

$ git log --oneline
f8fe91b (HEAD -> master) add tittle
4a28350 空的
76d880e 空的
196ce5e 空的
54bad65 init commit

$ git reset 4a28350
Unstaged changes after reset:
M       index.html

// 工作目錄留有f8fe91b這個commit異動的檔案

git rest有三種模式:

  • mixed
    • 預設參數
    • 將Staging Area檔案丟掉
    • 不動Working Directory的檔案
    • commit拆出來的檔案留在Working Directory
  • soft
    • Working Directory和Staging Area都留著
    • commit拆出來的檔案留在Staging Area
  • hard
    • Working Directory和Staging Area都丟

簡言之 commit拆分出來的檔案:

  • mixed: 丟回工作目錄
  • soft: 丟回暫存
  • hard:直接掰掰

指向目前所在的分支, 切換分支時.git/HEAD內容也會更著改變

// 開新分支 feature1 feature2
$ git branch feature1
$ git branch feature2

// 切到分支feature2
$ git checkout feature2
Switched to branch 'feature2'
M       index.html

// 查看.git/HEAD
$ cat .git/HEAD
ref: refs/heads/feature2

// 切至master
$ git checkout master
$ cat .git/HEAD
ref: refs/heads/master

使用分支

// 列出目前專案有哪些分支
$ git branch
  feature1
  feature2
* master

// 新增分支
$ git branch feature4

// 分支改名 feature4改為feature3
$ git branch -m feature4 feature3

// 刪除分支
$ git branch -d feature3
Deleted branch feature3 (was f8fe91b).

// 切換分支
$ git checkout feature2

分支想像成貼紙, 貼在某個commit上

每一次的commit都是一個物件, 指向某個tree物件

合併分支

情境feature2開發告一段落, 準備併回master

// 切回master
$ git checkout master

// 合併分支
$ git merge feature2
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

提醒: index.html發生衝突

// index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>hellp git</title>
</head>
<body>
<<<<<<< HEAD
  <h1>我是標題</h1>
  <p>123123</p>
  <header>我是header</header>
  <div>我是內容</div>
  <footer>我是footer</footer>
  
=======
  我是feature2
  好多功能
>>>>>>> feature2
</body>
</html>

master和feature2的index.html不一致

Git將有衝突的段落標記出來, 上半部是HEAD (master分支), 下半部是feature2的內容

決定取用誰的,並把標記修掉

留下feature2的內容

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>hellp git</title>
</head>
<body>
  我是feature2
  好多功能
</body>
</html>
// 修改完後, 繼續將檔案加入暫存
$ git add index.html

$ git commit -m "confilct fixed"

sourcetree圖

從master再開一個分支feature4, 修改index.html開發功能

開發完成後, 併回master

$ git checkout master
$ git merge feature4
Updating 5979365..a130319
Fast-forward
 index.html | 1 +
 1 file changed, 1 insertion(+)

Fast-forward快轉模式, 直接master貼紙指到feature4的commit上, 如下圖

sourcetree-merge-ff

如果需要保留線圖, 使用--no-ff

// 切回feature4 異動index.html內容
$ git checkout feature4
$ git add index.html
$ git commit -m "add text"

// 切至master 合併feature4
$ git checkout master
$ git merge feature4 --no-ff
Merge made by the 'recursive' strategy.
 index.html | 1 +
 1 file changed, 1 insertion(+)

結果會額外做出一個commit,

sourcetree --no-ff

Revert?

新功能推上線有問題, 用Revert!

$ git log --oneline
e8bf6eb (HEAD -> master) modify text
a130319 feature4 function
5979365 conflict fixed
d0f8be1 (feature2) more function
ae6b919 add line
f8fe91b add tittle
4a28350 (feature1) 空的
76d880e 空的
196ce5e 空的
54bad65 init commit

打算取消最後的commit (e8bf6eb)

$ git revert HEAD --no-edit
[master 9c8f87b] Revert "modify text"
 Date: Sun Mar 21 22:46:42 2021 +0800
 1 file changed, 1 deletion(-)
 
 $ git log --oneline
9c8f87b (HEAD -> master) Revert "modify text"
e8bf6eb modify text
a130319 feature4 function
5979365 conflict fixed
d0f8be1 (feature2) more function
ae6b919 add line
f8fe91b add tittle
4a28350 (feature1) 空的
76d880e 空的
196ce5e 空的
54bad65 init commit

Revert會增加一個commit去取消不要的commit

適用在已經推出去的commit!

參考書籍

為你自己學Git