Git
資訊系必備的版本管理工具
- 基本介紹
- 基礎操作
- 進階技巧
大綱
基本介紹
what is Git
版本控制?
- 不用每次修正都開一個新的檔案
- 有雷隊友把 code 整個刪掉了
- 大專案出 bug 了,想找出是什麼時候開始出了問題
為什麼需要版本控制?
Git 是版本控制系統
Git 還能做什麼?
Cloud
You
Cloud
Push
You
Repository
Cloud
Push
You
Repository
Cloud
You
Repository
Friend
Clone
Cloud
You
Repository
Friend
Push
Cloud
You
Repository
Friend
Pull
GitHub
You
關於「Git」和「Github」
Git
Git
關於「Git」和「Github」
基本概念
區域劃分
基本流程
- 修改 / 新增檔案
- 移到手推車 add
- 打包進倉庫 commit
雲端合作
- 把檔案推到雲端 push
- 把檔案拉回本地 pull
誰說資訊系不用GUI?
終端機
終端機
Sublime Merge
- 簡潔易懂
- 指令操作
- 功能齊全
- 顯示分支樹
- 免費
下載安裝
官網:https://www.sublimemerge.com
介面
操作
指令
Ctrl + p
Git 基礎
關於自己做事
基礎指令
初始設定
創建倉庫 / 環境設定
創建倉庫
- repository(repo)
- 以 資料夾 為單位
- 讓 git 知道這些內容要被管理
- 初始化 git
儲存位置
Sublime Merge
Sublime Merge
選單 > File > New Repository
環境設定
- 記錄作者資訊
- 調整記錄資訊
- file mode
- 調整環境設置
- 編輯器
e.g. 作者資訊
儲存位置
- Global
~/.gitconfig
-
影響所有 repo
- Local
-
<repo>/.git/config
-
目前所在 repo
-
儲存位置 - Global
儲存位置 - Local
Sublime Merge
Sublime Merge
創建 commit
放上卡車 / 檢查卡車 / 送進倉庫
放上卡車
git add
用途?
- 掌控每個 commit 大小
- 避免把某些資訊丟進倉庫
- 為工作設定斷點
Sublime Merge
檢查卡車
內容資訊
Sublime Merge
modified
untracked
staged
紀錄「改變」
modified
staged
送進倉庫
git commit
Sublime Merge
commit message
成功!
Demo & Lab
- 新增一個 repo
- 做好環境設定
- 新增檔案
a.txt
並放入卡車 - 把卡車送進倉庫
指令 ver.
git init / config / add / status / commit
Review
- 新增倉庫
git init
- 環境設定
git config
- 放上卡車
git add
- 檢查卡車
git status
- 送進倉庫
git commit
創建倉庫
$ git init
$ git init <repo_name>
環境設定
-
$ git config [--global] user.name "<your_name>"
-
$ git config [--global] user.email "<your_email>"
放上卡車
$ git add a.txt
$ git add *.c
$ git add .
$ git add --all
檢查卡車
$ git status
modified
untracked
staged
送進倉庫
$ git commit
$ git commit -m "<message>"
組合技
$ git status
$ git commit
請養成 commit 前檢查 status 的好習慣
Demo
$
指令 time
復原操作
git restore / rm
unstaged
- 剛剛
git add
某些檔案 - 發現這個東西還不能丟到倉庫
- 從 staging area 移除
Sublime Merge
unmodified
- 剛剛修改了一些檔案
- 測試時發現整陀爛光光
- 只能回復到上一個 commit 的狀態
Sublime Merge
Sublime Merge
Demo & Lab
Task: 創建並讓 b.txt
在 staged 跟 modified 間反覆橫跳移動
指令 ver.
git restore / rm
unstage
$ git restore --staged a.txt
$ git restore --staged *.c
第一個 staged
- 新的 repo
- 新增
a.txt
並放入 staging area - 從 staging area 移除
指令
哭阿
git restore
-
git restore
是以 commit 為基底進行復原 - 沒有半個 commit 當然會出事情
怎麼辦
- 下載 Sublime Merge
- 重開一個 repo
- 不管了直接 commit
- ...?
指令
指令
$ git rm --cached a.txt
$ git rm --cached *.c
git rm
- 把檔案從 tracked 直接刪除
-
--cached:
改成變為 untracked
unmodified
$ git restore a.txt
複習
$ git restore [--staged] <file>
$ git rm [--cached] <file>
太多好難記?
Demo
$
指令 time
其他指令
操作歷史
查看歷史 / 刪除歷史 / 暫存桌面 / 回到過去
查看歷史
- commit 順序
- 作者與內容
- 搜尋功能
- 作者
- 時間
- 檔案
Sublime Merge
Sublime Merge
進階搜尋
刪除歷史
- 不小心手殘把錯的東西
git add
- 不小心手殘把錯的東西
git commit
- 完蛋zzz
刪除歷史
- 創造一個新的 commit 修改
-
git log
長很醜 - 強迫症發作
- 做事沒效率
- 專案沒進度
人生沒希望
Sublime Merge
刪除 commit
-
soft
:改成 staged -
mixed
:改成 modified -
hard
:直接刪除
mixed
結果
WARNING !
git reset --hard
是一個十分危險的指令,一旦使用所有進度都會直接消失。請勿搭配酒精使用,並確認無誤再下達。
回到過去
- 做了一堆 commit
- 突然想回到過去看看以前的狀態
Sublime Merge
結果
桌面不乾淨
暫存桌面
- 現在的檔案不足以 commit
- 但又需要清空以放下其他東西
- 暫時存檔之後取出
Sublime Merge
只會儲存 staged
- 先
stage all
- 再
stash
結果
取回暫存 - pop
回歸正題
- 原本想要回到過去被卡住
-
stash
清空桌面 -
pop
之後取回
回到過去
結果
回到現在
修改歷史?
- 先回到過去
- 在歷史紀錄上做不同的修改
- 甚至做出 commit...
結果
Detached HEAD
- 目前 HEAD 所在的位置不屬於任何 branch
- 離開後不容易找到這些 commits
- 謹慎使用
先不要碰
Demo
把桌面清空 回到第一個 commit 再回來並回復桌面
Review
- 查看歷史
git log
- 刪除歷史
git reset
- 暫存桌面
git stash
- 回到過去
git checkout
查看歷史
$ git log
刪除歷史
$ git reset <commit_ID>
$ git reset --soft <commit_ID>
- $ git reset --hard <commit_ID>
回到過去 - 未清空
暫存桌面
$ git add --all
$ git stash
只會暫存 staging area
取回暫存
$ git stash pop
回到過去
$ git checkout <commit_ID>
回到現在
$ git checkout master
git checkout
- git checkout 其實更常用於切換分枝(branch)
- 其中 master 就是分支名稱
- 有時視情況會是 main / 其他名稱
- 下半堂會更詳細介紹
Demo
$
指令 time
隱藏資訊
.gitignore
隱藏資訊
- 機密檔:
secret_key.txt
- 執行檔:
a.exe a.out
- 系統檔:
.vscode/ .DS_Store
- 暫存檔:
a.txt~ b.txt.swp
小心再小心
- 每次都不要 add 到那些檔案
- 不能使用
git add --all
-
git status
很醜 - 強迫症發作
- ...
.gitignore
- 名稱就叫 "
.gitignore"
- 告訴 git 哪些檔案 / 資料夾底下不要紀錄
- 不會被
git status / add
看到
.gitignore
secret_key.txt
*.exe
*.out
.vscode/
.DS_Store
*~
*.swp
Sublime Merge
指令
$ vim .gitignore
結果
影響範圍
-
.gitignore
可以每個資料夾底下都放一個 - 影響範圍會因為位置而有所不同
- 若撰寫前該檔案已 tracked 則不受影響
- 可透過
git rm --cached <file>
變回 untracked
Demo & Lab
Task: 撰寫 .gitignore
使 git 無法追蹤 secret_key.txt
Q & A
$ git checkout break_time !
Git 進階
關於分工合作
分工 = branch
合作 = merge
A
B
版本
+修改
=版本
b
A
B
E
C
D
F
G
branch
merge
g \(\approx\) e + f
b
e
c
f
d
g
branch:分化出不同的分支完成不同的部分
merge:將不同分支的內容整合在一起
branch
some rules:
- 一個 branch 指向一個 commit
- 同個 commit 可以有很多分支,但 Git 同時只會關注(checkout)一個分支
- 每次 commit ,當前的 branch 也會跟著往後移動
A
git checkout main
B
main
A
git checkout main
git commit -m "C"
B
C
main
A
git checkout main
git commit -m "C"
git commit -m "D"
B
C
main
D
A
git checkout main
git commit -m "C"
git commit -m "D"
git checkout <commit ID of B>
B
C
HEAD
main
D
A
git checkout main
git commit -m "C"
git commit -m "D"
git checkout <commit ID of B>
git checkout -b dev
B
C
dev
main
D
A
git checkout main
git commit -m "C"
git commit -m "D"
git checkout <commit ID of B>
git checkout -b dev
git commit -m "E"
B
C
dev
main
D
E
A
git checkout main
git commit -m "C"
git commit -m "D"
git checkout <commit ID of B>
git checkout -b dev
git commit -m "E"
git checkout main
B
C
dev
main
D
E
A
git checkout main
git commit -m "C"
git commit -m "D"
git checkout <commit ID of B>
git checkout -b dev
git commit -m "E"
git checkout main
git commit -m "F"
B
C
dev
main
D
E
F
A
git checkout main
git commit -m "C"
git commit -m "D"
git checkout <commit ID of B>
git checkout -b dev
git commit -m "E"
git checkout main
git commit -m "F"
git checkout dev
B
C
dev
main
D
E
F
A
git checkout main
git commit -m "C"
git commit -m "D"
git checkout <commit ID of B>
git checkout -b dev
git commit -m "E"
git checkout main
git commit -m "F"
git checkout dev
git commit -m "G"
B
C
dev
main
D
E
F
G
merge
some rules:
- A 併進 B ≠ B 併進 A
- A 併進 B:將 branch A 的資訊帶回 B
- 「合併」就可能發生「衝突」(兩邊的意見不合)
main
main
dev
main
dev
main
dev
merge!
int stack[100], top = 0;
void push(int x){
}
int pop(int x){
}
int stack[100], top = 0;
void push(int x){
stack[top++] = x;
}
int pop(){
}
int stack[100], top = 0;
void push(int x){
}
int pop(){
return stack[--top];
}
int stack[100], top = 0;
void push(int x){
stack[top++] = x;
}
int pop(){
return stack[--top];
}
A
git checkout main
B
C
dev
main
D
E
F
D
F
A
git checkout main
git merge dev
B
C
dev
main
D
E
F
G
G
Merge this?
main
dev
main
dev
?
conflict!
conflict
-
兩邊對相同的地方做不同的修改
-
Git 無法自動判斷哪邊該留下
-
需要手動合併
int gcd(int a, int b){
<<<<<<< HEAD
while(b != 0){
int tmp = b;
b = a % b;
a = tmp;
}
return a;
=======
return a % b == 0
? b
: gcd(b, a % b);
>>>>>>> rec
}
int gcd(int a, int b){
}
int gcd(int a, int b){
while(b != 0){
int tmp = b;
b = a % b;
a = tmp;
}
return a;
}
int gcd(int a, int b){
return a % b == 0
? b
: gcd(b, a % b);
}
int gcd(int a, int b){
return a % b == 0
? b
: gcd(b, a % b);
}
<<<<<<< HEAD
合併前的檔案內容
=======
合併進來的分支的檔案內容
>>>>>>> 合併進來的分支名稱
Git 進階
關於異地同步
Sync strategy
push & pull
pull
push
「嘿,現在的版本樹長怎樣」
「嘿,我做了某某某修改」
git pull
for synchronization
A
B
dev
main
C
D
E
F
A
B
dev
main
C
D
E
A
B
dev
main
C
D
E
F
git fetch
A
B
dev
main
C
D
E
F
A
B
dev
main
C
D
E
A
B
origin/dev
origin/main
C
D
E
F
git fetch
git merge
A
B
dev
main
C
D
E
F
A
B
dev
main
C
D
E
A
B
origin/dev
origin/main
C
D
E
F
= git pull
git pull
some rules:
- (fetch)會將所有遠端的 commit 都抓下來
- (merge)只會 merge 當前的 branch
- 有 merge 就可能有 conflict 發生
A
B
dev
main
C
D
E
F
A
B
dev
main
C
D
E
G
origin/dev
A
B
main
dev
C
D
E
F
main
C
dev
G
A
B
origin/main
C
D
E
F
A
B
main
C
D
E
F
main
C
G
A
B
origin/main
C
D
E
F
H
dev
dev
origin/dev
git push
for contribution
A
B
dev
main
C
D
E
A
B
dev
main
C
D
E
F
A
B
dev
main
C
D
E
F
A
B
main
C
D
E
A
B
dev
main
C
D
E
F
git push origin dev
A
B
dev
main
C
D
E
F
git push <remote> <branch>
some rules:
- 將本地的 branch 併進遠端的 branch
- 只會動到指定的 branch
- 必須是 fast-forward,否則會被 reject
- remote 永遠是對的
git push origin dev
A
B
main
C
D
E
F
dev
A
B
main
dev
C
D
E
G
To github.com:username/reponame.git
! [rejected] dev -> dev (fetch first)
error: failed to push some refs to 'github.com:username/reponame.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
E
F
origin/dev
G
dev
H
E
F
origin/dev
G
dev
E
F
dev
pull
merge
push
E
G
dev
E
F
G
dev
H
> git push origin dev
To github.com:username/reponame.git
! [rejected] dev -> dev (fetch first)
error: failed to push some refs to 'github.com:username/reponame.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
> git pull
...
Auto-merging <filename>
CONFLICT (content): Merge conflict in <filename>
Automatic merge failed; fix conflicts and then commit the result.
> git add .
> git commit -m "merged"
> git push
Demo & Lab
Task:合作開發 1A2B 猜數字遊戲
background
-
三個人一組
-
透過 Git / GitHub 協作開發猜數字遊戲
For each group...
A
B
C
A
B
C
A
B
C
3.
新增協作者
Repo 主頁
> Settings
> Collaborators
> Add people
A
B
C
local
ssh-keygen
cat ~/.ssh/id_rsa.pub
github
4.
把 repo clone 到本機上
github.com/username/repo-name
A
B
C
git@github.com:username/repo-name.git
cd /path/to/any/directory
git clone <repo_url>
cd repo-name
5.
A 先打個基本設定
A
B
C
*.exe
.gitignore
#include<stdio.h>
#include<stdlib.h>
int main()
{
}
main.c
git add .
git commit -m "template"
git push origin main
5
main
6.
BC 把 A 的修改 pull 下來
A
B
C
git pull
5
main
7.
B 強迫症發作,改了 A 的 coding style
A
B
C
#include<stdio.h>
#include<stdlib.h>
int main(){
}
main.c
git add .
git commit -m "coding style!!"
git push origin main
5
main
7
main
8.
C 寫了一些遊戲基本流程
A
B
C
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#define len 4
void generate(char *s){
// TODO
}
void input(char *s){
// TODO
}
void response(char *s, char *a){
// TODO
}
int main()
{
char ans[10000], str[10000];
generate(ans);
while(true){
input(str);
response(str, ans);
}
printf("You won!\n");
}
main.c
git add .
git commit -m "game flow"
git push origin main
5
7
main
To github.com:username/repo-name.git
! [rejected] main -> main (fetch first)
error: failed to push some refs to 'github.com:username/repo-name.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
void response(char *s, char *a){
// TODO
}
<<<<<<< HEAD
int main()
{
char ans[10000], str[10000];
generate(ans);
=======
int main(){
>>>>>>> <commit ID>
while(true){
input(str);
response(str, ans);
git pull
void response(char *s, char *a){
// TODO
}
int main(){
char ans[10000], str[10000];
generate(ans);
while(true){
input(str);
response(str, ans);
git add .
git commit -m "merge!"
git push origin main
C
5
7
origin/main
8
main
8
5
7
origin/main
8
main
5
7
main
5
7
8
8
main
pull
merge
push
5
8
main
9.
分工:
A 維護 main
B 處理 input
C 處理 output
A
B
C
5
7
8
8
main
input
output
git pull
git checkout -b "input"
B
C
git pull
git checkout -b "output"
10.
A 簡單加上了分隔線
A
B
C
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#define len 4
void generate(char *s){
// TODO
}
void input(char *s){
// TODO
}
void response(char *s, char *a){
// TODO
}
int main(){
char ans[10000], str[10000];
generate(ans);
while(true){
printf("==========\n");
input(str);
response(str, ans);
}
printf("You won!\n");
}
main.c
git add .
git commit -m "add separator"
git push origin main
5
7
8
8
main
input
output
10
main
11.
B 完成了數字生成和使用者輸入
A
B
C
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<time.h>
#include<string.h>
#define len 4
bool valid(char *s){
if(strlen(s) != len) return false;
for(int i = 0; i < len; i++){
for(int j = 0; j < len; j++){
if(i != j && s[i] == s[j]){
return false;
}
}
}
return true;
}
void generate(char *s){
printf("Set the answer (0 for auto generation): ");
scanf("%s", s);
if(s[0] == '0' && strlen(s) == 1){
srand(time(0));
while(true){
for(int i = 0; i < len; i++){
s[i] = '0' + rand() % 10;
}
s[len] = '\0';
if(valid(s)){
break;
}
}
}else{
if(!valid(s)){
printf("Invalid input\n");
generate(s);
}else{
printf("\033[1A\x1b[2K");
}
}
}
void input(char *s){
printf("Guess a 4-digit number: ");
scanf("%s", s);
}
void response(char *s, char *a){
// TODO
}
int main(){
char ans[10000], str[10000];
generate(ans);
while(true){
input(str);
if(!valid(str)){
printf("Invalid input\n");
}else{
response(str, ans);
}
}
printf("You won!\n");
}
main.c
git add .
git commit -m "finish input"
git push origin input
5
7
8
8
input
output
10
main
11
input
12.
C 完成回覆函數和勝利判斷
A
B
C
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<string.h>
#define len 4
void generate(char *s){
// TODO
}
void input(char *s){
// TODO
}
bool win(char *s, char *a){
return strcmp(s, a) == 0;
}
void response(char *s, char *a){
int cnt_a = 0, cnt_b = 0;
for(int i = 0; i < len; i++){
if(s[i] == a[i]){
cnt_a++;
}else{
for(int j = 0; j < len; j++){
if(s[i] == a[j]){
cnt_b++;
break;
}
}
}
}
if(cnt_a) printf("%dA", cnt_a);
if(cnt_b) printf("%dB", cnt_b);
printf("\n");
}
int main(){
char ans[10000], str[10000];
generate(ans);
while(true){
input(str);
if(win(str, ans)){
break;
}else{
response(str, ans);
}
}
printf("You won!\n");
}
main.c
git add .
git commit -m "finish output"
git push origin output
5
7
8
8
10
main
11
input
12
output
output
13.
身為 main branch 的維護者, A 要負責將 feature branches 合併進 main
A
B
C
git pull
git merge origin/input
5
7
8
8
10
main
11
input
12
output
13
main
14.
身為 main branch 的維護者, A 要負責將 feature branches 合併進 main
A
B
C
git merge origin/output
5
7
8
8
10
11
input
12
output
13
main
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
<<<<<<< HEAD
#include<time.h>
=======
>>>>>>> output
#include<string.h>
#define len 4
bool valid(char *s){
if(strlen(s) != len) return false;
for(int i = 0; i < len; i++){
for(int j = 0; j < len; j++){
if(i != j && s[i] == s[j]){
return false;
}
}
}
return true;
}
void generate(char *s){
printf("Set the answer (0 for auto generation): ");
scanf("%s", s);
if(s[0] == '0' && strlen(s) == 1){
srand(time(0));
while(true){
for(int i = 0; i < len; i++){
s[i] = '0' + rand() % 10;
}
s[len] = '\0';
if(valid(s)){
break;
}
}
}else{
if(!valid(s)){
printf("Invalid input\n");
generate(s);
}else{
printf("\033[1A\x1b[2K");
}
}
}
void input(char *s){
printf("Guess a 4-digit number: ");
scanf("%s", s);
}
bool win(char *s, char *a){
return strcmp(s, a) == 0;
}
void response(char *s, char *a){
int cnt_a = 0, cnt_b = 0;
for(int i = 0; i < len; i++){
if(s[i] == a[i]){
cnt_a++;
}else{
for(int j = 0; j < len; j++){
if(s[i] == a[j]){
cnt_b++;
break;
}
}
}
}
if(cnt_a) printf("%dA", cnt_a);
if(cnt_b) printf("%dB", cnt_b);
printf("\n");
}
int main(){
char ans[10000], str[10000];
generate(ans);
while(true){
printf("==========\n");
input(str);
<<<<<<< HEAD
if(!valid(str)){
printf("Invalid input\n");
=======
if(win(str, ans)){
break;
>>>>>>> output
}else{
response(str, ans);
}
}
printf("You won!\n");
}
14.
身為 main branch 的維護者, A 要負責將 feature branches 合併進 main
A
B
C
git add .
git commit -m "merge output"
5
7
8
8
10
11
input
12
output
13
main
14
main
5
7
8
8
10
11
input
12
output
13
14
main
git push origin main
gcc game.c -o game.exe
./game.exe
-
更系統化的分工合作
-
一個團隊的紀律
-
avoid conflicts!!
5
7
8
8
10
11
input
12
output
13
14
main
大家都想在 main branch 上做事
\(\Rightarrow\) 衝突多、merge 多、歷史記錄亂
每個人在自己的 branch 上做事
彼此不互相干擾
- 同一個 branch 盡量少人操作
- main 作為正式版本
- merge 進主要分支前要 code review
Feature Branching
後話
git rebase
A
B
C
A
B
C
A
B
C
B
C
A
B
C
git merge
git rebase
A
B
C
A
B
C
A
B
C
B
D
A
B
C
git merge
git rebase
git merge
git rebase
A
B
C
B
D
A
B
C
- commit D 幾乎沒有任何意義
- merge 會產生多餘的 commit
- merge 會造成非線性的歷史紀錄
- rebase 就是將 C 的修改重新作用在 B 上面
- rebase 兩邊的 commit 鏈越長,就容易產生越多的 conflict
- rebase 對版本樹結構影像較大
Playground
Init
git init
git config [--global] user.name <bob>
git config [--global] user.email <bob@csie.ntu.edu.tw>
commit!
// function.h
#ifndef FUNCTION_H
#define FUNCTION_H
#include <stdio.h>
#include <math.h>
#include <string.h>
void clear_screen() {
printf("clear_screen\n");
}
void print_screen() {
printf("print_screen\n");
}
#endif
// donut.c
#include <unistd.h>
#include "function.h"
int main() {
clear_screen() ;
for(;;) {
print_screen() ;
}
}
# Makefile
run:
gcc donut.c -lm -Wall -Wextra
initial
// function.h
#ifndef FUNCTION_H
#define FUNCTION_H
#include <stdio.h>
#include <math.h>
#include <string.h>
void clear_screen() {
printf("\x1b[2J");
}
void print_screen() {
printf("print_screen\n");
}
void hide_cursor() {
printf("\033[?25l") ;
}
void reset_cursor() {
printf("\x1b[H");
}
#endif
// donut.c
#include <unistd.h>
#include "function.h"
int main() {
clear_screen() ;
hide_cursor() ;
for(;;) {
reset_cursor() ;
print_screen() ;
usleep(30000) ;
}
}
handle frames
git add <filename>
git status
git commit -m "<message>"
git restore [--staged] <filename>
git rm [--cache] <filename>
Time Travel!
// function.h
#ifndef FUNCTION_H
#define FUNCTION_H
#include <stdio.h>
#include <math.h>
#include <string.h>
void clear_screen() {
printf("\x1b[2J");
}
void print_screen(float A, float B) {
printf("print_screen %f %f\n", A, B);
}
void hide_cursor() {
printf("\033[?25l") ;
}
void reset_cursor() {
printf("\x1b[H");
}
#endif
// donut.c
#include <unistd.h>
#include "donut.h"
#include "function.h"
int main() {
float A = 0 ;
float B = 0 ;
clear_screen() ;
hide_cursor() ;
for(;;) {
reset_cursor() ;
print_screen(A, B) ;
A += omega_A ;
B += omega_B ;
usleep(30000) ;
}
}
add momentum
// donut.h
#ifndef DONUT_H
#define DONUT_H
const float omega_A = 0.07;
const float omega_B = 0.04;
#endif
git log
git reset [--soft|--hard] <commit>
git stash
git stash pop
git checkout <hash>
GitHub
A
B
C
A
B
C
3.
新增協作者
Repo 主頁
> Settings
> Collaborators
> Add people
A
B
C
local
ssh-keygen
cat ~/.ssh/id_rsa.pub
github
4.
把 repo clone 到本機上
github.com/username/repo-name
A
B
C
git@github.com:username/repo-name.git
cd /path/to/any/directory
git clone <repo_url>
cd repo-name
git remote add origin git@github.com:Ccucumber12/myrepo.git
git branch -M main
git push -u origin main
git clone git@github.com:Ccucumber12/myrepo.git
Push n' Pull
// function.h
#ifndef FUNCTION_H
#define FUNCTION_H
#include <stdio.h>
#include <math.h>
#include <string.h>
void clear_screen() {
printf("\x1b[2J");
}
void print_screen(float A, float B) {
printf("print_screen %f %f\n", A, B);
}
void hide_cursor() {
printf("\033[?25l") ;
}
void reset_cursor() {
printf("\x1b[H");
}
coordinate get_projection(float theta, float phi, float A, float B) {
return (coordinate){ .x=0, .y=0, .z=0 };
}
float get_luminance(float theta, float phi, float A, float B) {
return 0;
}
void get_output(float L, coordinate pos) {
}
#endif
// donut.c
#include <unistd.h>
#include "donut.h"
#include "function.h"
int main() {
float A = 0 ;
float B = 0 ;
clear_screen() ;
hide_cursor() ;
for(;;) {
for (float theta=0; theta < 2*pi; theta += dtheta) {
for(float phi=0; phi < 2*pi; phi += dphi) {
coordinate pos = get_projection(theta, phi, A, B) ;
float L = get_luminance(theta, phi, A, B) ;
get_output(L, pos) ;
}
}
reset_cursor() ;
print_screen(A, B) ;
A += omega_A ;
B += omega_B ;
usleep(30000) ;
}
}
A: more structures
// donut.h
#ifndef DONUT_H
#define DONUT_H
const float pi = 3.1415926 ;
const float dtheta = 0.07 ;
const float dphi = 0.02 ;
const float omega_A = 0.07;
const float omega_B = 0.04;
typedef struct Tcoordinate {
int x ;
int y ;
float z ;
} coordinate ;
#endif
// function.h
#ifndef FUNCTION_H
#define FUNCTION_H
#include <stdio.h>
#include <math.h>
#include <string.h>
void clear_screen() {
printf("\x1b[2J");
}
void print_screen(float A, float B) {
printf("print_screen %f %f\n", A, B);
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++)
putchar(output[j][i]);
putchar('\n');
}
}
void hide_cursor() {
printf("\033[?25l") ;
}
void reset_cursor() {
printf("\x1b[H");
}
void reset_values() {
memset(output, (int)' ', sizeof(output)) ;
memset(bestOOZ, 0, sizeof(bestOOZ)) ;
}
coordinate get_projection(float theta, float phi, float A, float B) {
return (coordinate){ .x=0, .y=0, .z=0 };
}
float get_luminance(float theta, float phi, float A, float B) {
return 0;
}
void get_output(float L, coordinate pos) {
}
#endif
// donut.c
#include <unistd.h>
#include "donut.h"
#include "function.h"
int main() {
float A = 0 ;
float B = 0 ;
clear_screen() ;
hide_cursor() ;
for(;;) {
reset_values() ;
for (float theta=0; theta < 2*pi; theta += dtheta) {
for(float phi=0; phi < 2*pi; phi += dphi) {
coordinate pos = get_projection(theta, phi, A, B) ;
float L = get_luminance(theta, phi, A, B) ;
get_output(L, pos) ;
}
}
reset_cursor() ;
print_screen(A, B) ;
A += omega_A ;
B += omega_B ;
usleep(30000) ;
}
}
B: screen size & array
// donut.h
#ifndef DONUT_H
#define DONUT_H
#define width 30
#define height 30
const float pi = 3.1415926 ;
const float dtheta = 0.07 ;
const float dphi = 0.02 ;
const float omega_A = 0.07;
const float omega_B = 0.04;
typedef struct Tcoordinate {
int x ;
int y ;
float z ;
} coordinate ;
char output[height][width] ;
float bestOOZ[height][width] ;
#endif
git pull --ff-only
git push
Rejected
// function.h
#ifndef FUNCTION_H
#define FUNCTION_H
#include <stdio.h>
#include <math.h>
#include <string.h>
void clear_screen() {
printf("\x1b[2J");
}
void print_screen(float A, float B) {
printf("print_screen %f %f\n", A, B);
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++)
putchar(output[j][i]);
putchar('\n');
}
}
void hide_cursor() {
printf("\033[?25l") ;
}
void reset_cursor() {
printf("\x1b[H");
}
void reset_values() {
memset(output, (int)' ', sizeof(output)) ;
memset(bestOOZ, 0, sizeof(bestOOZ)) ;
}
coordinate get_projection(float theta, float phi, float A, float B) {
float costheta = cos(theta), sintheta = sin(theta);
float cosphi = cos(phi), sinphi = sin(phi);
float cosA = cos(A), sinA = sin(A) ;
float cosB = cos(B), sinB = sin(B) ;
// the x,y coordinate of the circle before revolving
float circlex = R2 + R1*costheta;
float circley = R1*sintheta;
// final 3D (x,y,z) coordinate after rotations
coordinate pos ;
float x = circlex*(cosB*cosphi + sinA*sinB*sinphi) - circley*cosA*sinB ;
float y = circlex*(sinB*cosphi - sinA*cosB*sinphi) + circley*cosA*cosB ;
float z = donut_distance + cosA*circlex*sinphi + circley*sinA ;
// (x,y,z) after projection
pos.x = (int) (width/2 + render_distance*x/z);
pos.y = (int) (height/2 - render_distance*y/z);
pos.z = z ;
return pos ;
}
float get_luminance(float theta, float phi, float A, float B) {
// calculate luminance, range from -sqrt(2) to +sqrt(2).
return cos(phi)*cos(theta)*sin(B) - cos(A)*cos(theta)*sin(phi) - sin(A)*sin(theta) + cos(B)*(cos(A)*sin(theta) - cos(theta)*sin(A)*sin(phi));
}
void get_output(float L, coordinate pos) {
}
#endif
A: get_projection + get_luminance
// donut.h
#ifndef DONUT_H
#define DONUT_H
#define width 30
#define height 30
const float pi = 3.1415926 ;
const float dtheta = 0.07 ;
const float dphi = 0.02 ;
const float omega_A = 0.07;
const float omega_B = 0.04;
const float R1 = 1 ;
const float R2 = 2 ;
const float donut_distance = 5 ;
const float render_distance = width*donut_distance*3/(8*(R1+R2));
typedef struct Tcoordinate {
int x ;
int y ;
float z ;
} coordinate ;
char output[height][width] ;
float bestOOZ[height][width] ;
#endif
// function.h
#ifndef FUNCTION_H
#define FUNCTION_H
#include <stdio.h>
#include <math.h>
#include <string.h>
void clear_screen() {
printf("\x1b[2J");
}
void print_screen(float A, float B) {
printf("print_screen %f %f\n", A, B);
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++)
putchar(output[j][i]);
putchar('\n');
}
}
void hide_cursor() {
printf("\033[?25l") ;
}
void reset_cursor() {
printf("\x1b[H");
}
void reset_values() {
memset(output, (int)' ', sizeof(output)) ;
memset(bestOOZ, 0, sizeof(bestOOZ)) ;
}
coordinate get_projection(float theta, float phi, float A, float B) {
return (coordinate){ .x=0, .y=0, .z=0 };
}
float get_luminance(float theta, float phi, float A, float B) {
return 0;
}
void get_output(float L, coordinate pos) {
// larger ooz means closer to viewer, reset output for closer z
float ooz = 1 / pos.z ; // one over z
if(ooz > bestOOZ[pos.y][pos.x]) {
bestOOZ[pos.y][pos.x] = ooz ;
output[pos.y][pos.x] = L > 5 ? '@' : '.';
}
}
#endif
B: get_output
git push # reject
git pull --ff-only
git merge origin/main
git push
Branching
// function.h
#ifndef FUNCTION_H
#define FUNCTION_H
#include <stdio.h>
#include <math.h>
#include <string.h>
void clear_screen() {
printf("\x1b[2J");
}
void print_screen(float A, float B) {
printf("print_screen %f %f\n", A, B);
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++)
putchar(output[j][i]);
putchar('\n');
}
}
void hide_cursor() {
printf("\033[?25l") ;
}
void reset_cursor() {
printf("\x1b[H");
}
void reset_values() {
memset(output, (int)' ', sizeof(output)) ;
memset(bestOOZ, 0, sizeof(bestOOZ)) ;
}
coordinate get_projection(float theta, float phi, float A, float B) {
float costheta = cos(theta), sintheta = sin(theta);
float cosphi = cos(phi), sinphi = sin(phi);
float cosA = cos(A), sinA = sin(A) ;
float cosB = cos(B), sinB = sin(B) ;
// the x,y coordinate of the circle before revolving
float circlex = R2 + R1*costheta;
float circley = R1*sintheta;
// final 3D (x,y,z) coordinate after rotations
coordinate pos ;
float x = circlex*(cosB*cosphi + sinA*sinB*sinphi) - circley*cosA*sinB ;
float y = circlex*(sinB*cosphi - sinA*cosB*sinphi) + circley*cosA*cosB ;
float z = donut_distance + cosA*circlex*sinphi + circley*sinA ;
// (x,y,z) after projection
pos.x = (int) (width/2 + render_distance*x/z);
pos.y = (int) (height/2 - render_distance*y/z);
pos.z = z ;
return pos ;
}
float get_luminance(float theta, float phi, float A, float B) {
// calculate luminance, range from -sqrt(2) to +sqrt(2).
return cos(phi)*cos(theta)*sin(B) - cos(A)*cos(theta)*sin(phi) - sin(A)*sin(theta) + cos(B)*(cos(A)*sin(theta) - cos(theta)*sin(A)*sin(phi));
}
void get_output(float L, coordinate pos) {
}
#endif
A: get_projection + get_luminance
// donut.h
#ifndef DONUT_H
#define DONUT_H
#define width 30
#define height 30
const float pi = 3.1415926 ;
const float dtheta = 0.07 ;
const float dphi = 0.02 ;
const float omega_A = 0.07;
const float omega_B = 0.04;
const float R1 = 1 ;
const float R2 = 2 ;
const float donut_distance = 5 ;
const float render_distance = width*donut_distance*3/(8*(R1+R2));
typedef struct Tcoordinate {
int x ;
int y ;
float z ;
} coordinate ;
char output[height][width] ;
float bestOOZ[height][width] ;
#endif
// function.h
#ifndef FUNCTION_H
#define FUNCTION_H
#include <stdio.h>
#include <math.h>
#include <string.h>
void clear_screen() {
printf("\x1b[2J");
}
void print_screen(float A, float B) {
printf("print_screen %f %f\n", A, B);
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++)
putchar(output[j][i]);
putchar('\n');
}
}
void hide_cursor() {
printf("\033[?25l") ;
}
void reset_cursor() {
printf("\x1b[H");
}
void reset_values() {
memset(output, (int)' ', sizeof(output)) ;
memset(bestOOZ, 0, sizeof(bestOOZ)) ;
}
coordinate get_projection(float theta, float phi, float A, float B) {
return (coordinate){ .x=0, .y=0, .z=0 };
}
float get_luminance(float theta, float phi, float A, float B) {
return 0;
}
void get_output(float L, coordinate pos) {
// larger ooz means closer to viewer, reset output for closer z
float ooz = 1 / pos.z ; // one over z
if(ooz > bestOOZ[pos.y][pos.x]) {
bestOOZ[pos.y][pos.x] = ooz ;
output[pos.y][pos.x] = L > 5 ? '@' : '.';
}
}
#endif
B: get_output
# create local branch
git branch <bob>
git checkout <bob>
git push -u origin <bob>
# merge and push
git checkout main
git pull
git merge <bob>
# pull alice branch and checkout
git pull
git switch <alice>
git checkout -b <alice> origin/<alice>
git branch -d <bob> # delete local branch
git push origin -d <bob> # delete remote branch
git pull --prune # remove remote deleted branch
CONFLICT
// function.h
#ifndef FUNCTION_H
#define FUNCTION_H
#include <stdio.h>
#include <math.h>
#include <string.h>
void clear_screen() {
printf("\x1b[2J");
}
void print_screen(float A, float B) {
printf("print_screen %f %f\n", A, B);
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++)
putchar(output[j][i]);
putchar('\n');
}
}
void hide_cursor() {
printf("\033[?25l") ;
}
void reset_cursor() {
printf("\x1b[H");
}
void reset_values() {
memset(output, (int)' ', sizeof(output)) ;
memset(bestOOZ, 0, sizeof(bestOOZ)) ;
}
coordinate get_projection(float theta, float phi, float A, float B) {
float costheta = cos(theta), sintheta = sin(theta);
float cosphi = cos(phi), sinphi = sin(phi);
float cosA = cos(A), sinA = sin(A) ;
float cosB = cos(B), sinB = sin(B) ;
// the x,y coordinate of the circle before revolving
float circlex = R2 + R1*costheta;
float circley = R1*sintheta;
// final 3D (x,y,z) coordinate after rotations
coordinate pos ;
float x = circlex*(cosB*cosphi + sinA*sinB*sinphi) - circley*cosA*sinB ;
float y = circlex*(sinB*cosphi - sinA*cosB*sinphi) + circley*cosA*cosB ;
float z = donut_distance + cosA*circlex*sinphi + circley*sinA ;
// (x,y,z) after projection
pos.x = (int) (width/2 + render_distance*x/z);
pos.y = (int) (height/2 - render_distance*y/z);
pos.z = z ;
return pos ;
}
float get_luminance(float theta, float phi, float A, float B) {
// calculate luminance, range from -sqrt(2) to +sqrt(2).
return cos(phi)*cos(theta)*sin(B) - cos(A)*cos(theta)*sin(phi) - sin(A)*sin(theta) + cos(B)*(cos(A)*sin(theta) - cos(theta)*sin(A)*sin(phi));
}
void get_output(float L, coordinate pos) {
if (pos.x < 0 || pos.x >= width || pos.y < 0 || pos.y >= height)
return ;
// larger ooz means closer to viewer, reset output for closer z
float ooz = 1 / pos.z ; // one over z
if(ooz > bestOOZ[pos.y][pos.x]) {
bestOOZ[pos.y][pos.x] = ooz ;
output[pos.y][pos.x] = L > 5 ? '@' : '.';
}
}
#endif
A: fix size error
// function.h
#ifndef FUNCTION_H
#define FUNCTION_H
#include <stdio.h>
#include <math.h>
#include <string.h>
void clear_screen() {
printf("\x1b[2J");
}
void print_screen(float A, float B) {
printf("print_screen %f %f\n", A, B);
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++)
putchar(output[j][i]);
putchar('\n');
}
}
void hide_cursor() {
printf("\033[?25l") ;
}
void reset_cursor() {
printf("\x1b[H");
}
void reset_values() {
memset(output, (int)' ', sizeof(output)) ;
memset(bestOOZ, 0, sizeof(bestOOZ)) ;
}
coordinate get_projection(float theta, float phi, float A, float B) {
float costheta = cos(theta), sintheta = sin(theta);
float cosphi = cos(phi), sinphi = sin(phi);
float cosA = cos(A), sinA = sin(A) ;
float cosB = cos(B), sinB = sin(B) ;
// the x,y coordinate of the circle before revolving
float circlex = R2 + R1*costheta;
float circley = R1*sintheta;
// final 3D (x,y,z) coordinate after rotations
coordinate pos ;
float x = circlex*(cosB*cosphi + sinA*sinB*sinphi) - circley*cosA*sinB ;
float y = circlex*(sinB*cosphi - sinA*cosB*sinphi) + circley*cosA*cosB ;
float z = donut_distance + cosA*circlex*sinphi + circley*sinA ;
// (x,y,z) after projection
pos.x = (int) (width/2 + render_distance*x/z);
pos.y = (int) (height/2 - render_distance*y/z);
pos.z = z ;
return pos ;
}
float get_luminance(float theta, float phi, float A, float B) {
// calculate luminance, range from -sqrt(2) to +sqrt(2).
return cos(phi)*cos(theta)*sin(B) - cos(A)*cos(theta)*sin(phi) - sin(A)*sin(theta) + cos(B)*(cos(A)*sin(theta) - cos(theta)*sin(A)*sin(phi));
}
char get_pattern(float L) {
// L is in range of -sqrt(2) to +sqrt(2)
// luminance_index is now in the range 0..11 (8*sqrt(2) = 11.3)
int luminance_index = L * 8 ;
return pattern[luminance_index] ;
}
void get_output(float L, coordinate pos) {
// check if out of range
if (L < 0)
return ;
// larger ooz means closer to viewer, reset output for closer z
float ooz = 1 / pos.z ; // one over z
if(ooz > bestOOZ[pos.y][pos.x]) {
bestOOZ[pos.y][pos.x] = ooz ;
output[pos.y][pos.x] = get_pattern(L) ;
}
}
#endif
B: better display
// donut.h
#ifndef DONUT_H
#define DONUT_H
#define width 30
#define height 30
const float pi = 3.1415926 ;
const float dtheta = 0.07 ;
const float dphi = 0.02 ;
const float omega_A = 0.07;
const float omega_B = 0.04;
const float R1 = 1 ;
const float R2 = 2 ;
const float donut_distance = 5 ;
const float render_distance = width*donut_distance*3/(8*(R1+R2));
const char pattern[] = ".,-~:;=!*#$@" ;
typedef struct Tcoordinate {
int x ;
int y ;
float z ;
} coordinate ;
char output[height][width] ;
float bestOOZ[height][width] ;
#endif
git merge <bob> conflict !
vim <file>
git add <file>
git commit -m "message"
git push
Resources
- https://git-scm.com/doc
- https://docs.github.com/en
- https://www.atlassian.com/git/tutorials/learn-git-with-bitbucket-cloud
Git2.0
By thomaswang2003
Git2.0
- 264