資訊系必備的版本管理工具
關於這是什麼
Cloud
Local
Cloud
Clone
Local
Repository
Cloud
Pull
Push
Local
Cloud
Local 1
Local 2
Merge
誰說資訊系不用GUI?
官網:https://www.sublimemerge.com
Ctrl + p
關於自己做事
創建倉庫 / 環境設定
選單 > File > New Repository
~/.gitconfig
影響所有 repo
<repo>/.git/config
目前所在 repo
放上卡車 / 檢查卡車 / 送進倉庫
git add
modified
untracked
staged
modified
staged
git commit
commit message
a.txt
並放入卡車git init / config / add / status / commit
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 的好習慣
$
指令 time
git restore / rm
git add
某些檔案Task: 創建並讓 b.txt
在 staged 跟 modified 間反覆橫跳移動
git restore / rm
$ git restore --staged a.txt
$ git restore --staged *.c
a.txt
並放入 staging area哭阿
git restore
是以 commit 為基底進行復原$ git rm --cached a.txt
$ git rm --cached *.c
--cached:
改成變為 untracked$ git restore a.txt
$ git restore [--staged] <file>
$ git rm [--cached] <file>
$
指令 time
查看歷史 / 刪除歷史 / 暫存桌面 / 回到過去
git add
git commit
git log
長很醜soft
:改成 stagedmixed
:改成 modifiedhard
:直接刪除mixed
結果git reset --hard
是一個十分危險的指令,一旦使用所有進度都會直接消失。請勿搭配酒精使用,並確認無誤再下達。
stage all
stash
pop
stash
清空桌面pop
之後取回把桌面清空 回到第一個 commit 再回來並回復桌面
git log
git reset
git stash
git checkout
$ git log
$ git reset <commit_ID>
$ git reset --soft <commit_ID>
$ git add --all
$ git stash
只會暫存 staging area
$ git stash pop
$ git checkout <commit_ID>
$ git checkout master
git checkout
$
指令 time
.gitignore
secret_key.txt
a.exe a.out
.vscode/ .DS_Store
a.txt~ b.txt.swp
git add --all
git status
很醜.gitignore
.gitignore"
git status / add
看到.gitignore
secret_key.txt
*.exe
*.out
.vscode/
.DS_Store
*~
*.swp
$ vim .gitignore
.gitignore
可以每個資料夾底下都放一個git rm --cached <file>
變回 untrackedTask: 撰寫 .gitignore
使 git 無法追蹤 secret_key.txt
$ git checkout break_time !
關於分工合作
A
B
b
A
B
E
C
D
F
G
branch
merge
b
e
c
f
d
g
branch:分化出不同的分支完成不同的部分
merge:將不同分支的內容整合在一起
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
main
main
dev
main
dev
main
dev
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
main
dev
main
dev
?
兩邊對相同的地方做不同的修改
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
合併前的檔案內容
=======
合併進來的分支的檔案內容
>>>>>>> 合併進來的分支名稱
關於異地同步
push & pull
pull
push
「嘿,現在的版本樹長怎樣」
「嘿,我做了某某某修改」
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
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
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 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
Task:合作開發 1A2B 猜數字遊戲
三個人一組
透過 Git / GitHub 協作開發猜數字遊戲
A
B
C
A
B
C
A
B
C
新增協作者
Repo 主頁
> Settings
> Collaborators
> Add people
A
B
C
ssh-keygen
cat ~/.ssh/id_rsa.pub
把 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
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
BC 把 A 的修改 pull 下來
A
B
C
git pull
5
main
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
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
分工:
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"
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
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
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
身為 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
身為 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");
}
身為 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 上做事
彼此不互相干擾
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