24 분 소요

1. Git 기초와 로컬 작업 흐름

1. Git 초기 설정

커밋 작성자(author) 설정

  • 최초 1회 설정
  • 만약 설정을 하지 않고 진행하면 commit 메시지를 남기는 상황에서 아래와 같은 에러 발생
$ git commit -m "Initial commit"
Author identity unknown # 이거 누가 쓴지 모르겠다는 의미 

*** Please tell me who you are. # 님이 누군지 좀 알려줘..!

Run # 아래 이거 그냥 따라하셈
 
  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"

to set your account's default identity.
Omit --global to set the identity only in this repository.

fatal: unable to auto-detect email address (got 'poeun@DESKTOP-21K4HL.(none)')
  • 이 설정은 인증(로그인) 정보와는 전혀 상관없음

author 설정

$ git config --global user.email "rjsdudans@naver.com"
$ git config --global user.name "ingu627"

설정 확인

$ git config --global --list # -l(소문자)

filter.lfs.smudge=git-lfs smudge -- %f
filter.lfs.process=git-lfs filter-process
filter.lfs.required=true
filter.lfs.clean=git-lfs clean -- %f
user.name=ingu627
user.email=rjsdudans@naver.com
init.defaultbranch=master

(추가 설정) 커밋 편집기 변경

  • 해당 명령어는 반드시 vscode가 설치되어 있어야 함

  • 기본 텍스트 편집기를 vim에서 vscode 로 변경하는 것

$ git config --global core.editor "code --wait"

로컬 저장소의 git 히스토리 삭제

$ rm -rf .git

2. 파일 생성

폴더 생성

$ mkdir 폴더명

파일 생성

$ touch 파일명

파일 열기

$ vi 파일명

3. Git Basic

로컬 저장소 설정

# git 초기화 -> master 표시가 뜰 것!
# 결과적으로 해당 폴더에 숨기 폴더로 .git/이라는 폴더가 생김
$ git init
Initialized empty Git repository in F:/test/.git/

$ touch a.txt # a.txt 파일 생성

$ touch b.txt # b.txt 파일 생성 

로컬 저장소의 git 삭제할 때

$ rm -rf .git

확인

$ ls -a

./  ../  .git/  a.txt  b.txt
  • a.txt와 b.txt를 touch로 만들어 놨다.

주의 사항!!

.git 폴더가 또 다른 폴더 내부에 있으면 안됨! (git 속 git은 절대 금지!!!)

image-20210705144804835

status

현재 git이 관리하는 폴더의 파일과 폴더 상태를 알려주는 명령어(working directory & staging area를 확인하는 명령어)

$ git status # WD & SA를 확인하는 명령어!! 정말 정말 중요하기 때문에 습관적으로 사용해야 한다.
On branch master

No commits yet

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

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

add

WD -> SA로 올리는 과정

$ git add a.txt

$ git status
On branch master

No commits yet

Changes to be committed: # 커밋 되어질 변경 사항들 
  (use "git rm --cached <file>..." to unstage)
        new file:   a.txt

Untracked files: # git이 아직 변경 사항을 추적하고 있지 않은 친구들
  (use "git add <file>..." to include in what will be committed)
        b.txt

image-20210705150947301

# 기본 -> commit을 위한 폴더 & 파일을 추가!
$ git add 파일/폴더이름

$ git add a.txt # 특정 파일을 WD -> SA 
$ git add . # 해당 디렉토리(하위 디렉토리 모두 포함)의 모든 폴더 & 파일을 SA 
$ git add my-folder/ # 특정 디렉토리를 WD -> SA

윈도우 git 에서 한글 파일이 깨져 나올 때 해결 방법

$ git config core.quotepath false

터미널 입력 시 파일명에 공백이 있을 경우 문제

  • 터미널에서 명렁어 입력 시 파일명, 폴더명에 공백이 있으면 제대로 인식이 안된다. 이럴 때는 공백 앞에 역슬래시()를 넣으면 해결된다.

에러 - 공백일 있을 경우 에러가 난다.

$ git add useful_code/한글 파일 깨짐 방지 코드.ipynb
fatal: pathspec 'useful_code/한글' did not match any files

해결법 - 역슬래시를 붙이면 제대로 된다.

$ git add useful_code/한글\ 파일\ 깨짐\ 방지\ 코드.ipynb

수정되거나 삭제된 파일 반영

git add -u

또한 git commit 에서 -a옵션을 붙여주게 되면 수정되거나 삭제된 파일만 commit하게 해준다.

git commit -a -m'message'

=> push까지 하면 github에 제대로 반영이 된다.

commit

  • commit을 통해서 하나의 버전으로 기록됨
  • commit 메시지는 현재의 변경 사항을 잘 나타내도록 ‘잘’ 써야함
  • commit 내역은 $ git log라는 명령어로 확인할 수 있음

commit 메시지의 기본 구조

# -m(message)
$ git commit -m "남기고 싶은 메시지"
$ git commit -m "Initial commit"
[master (root-commit) acf00f0] Initial commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a.txt

commit 내역 확인

#
$ git log

commit acf00f0792da06e949f21997a4f4226dc64a3ea7 (HEAD -> master)
Author: ingu627 <rjsdudans@naver.com>
Date:   Sun Nov 21 14:36:03 2021 +0900

    Initial commit

commit 이력을 더 짧게 보기

$ git log --oneline
acf00f0 (HEAD -> master) Initial commit

git 로그 정지 탈출 방법

: Q를 누른다.

퀴즈! 만약 status를 여기서 찍으면 어떤 결과가 나올까?

  • 현재 b.txt가 WD에 위치해있기 때문에 빨간색으로 표시된다.
  • a.txt 는 commit 명령어를 통해 하나의 버전으로 기록됨
$ git status

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

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

image-20210705153111460

2. 원격 저장소와 협업

기본 설정

  1. 내 로컬 컴퓨터의 폴더를 git이 관리하도록 설정한다.
# git으로 초기화
$ git init
Initialized empty Git repository in F:/TIL/.git/


# add & commit 
$ git status

On branch master

No commits yet

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

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

$ git add .
$ git status

On branch master

No commits yet

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


$ git commit -m "Initial commit"

[master (root-commit) d2c6be1] Initial commit
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a.txt
 create mode 100644 b.txt

  1. Github Repository 생성

image

  • public은 다 볼 수 있는 저장소이다.
  • private은 나 또는 이 권한이 있는 유저만 볼 수 있는 저장소이다.

원격 저장소 등록 & 업로드 명령어

원격 저장소 추가

  • 최초 1회만 설정하면 됨
# 원격 저장소 추가
# git아 원격 저장소 좀 등록해줘(add) origin이라는 이름(별명)으로 원격 저장소URL을
$ git remote add origin 원격저장소URL
  • 원격저장소 url은 복사 붙여넣기 한다.

image

# 예시
$ git remote add origin https://github.com/IT3AI1/TIL.git

등록된 원격 저장소 확인

$ git remote -v
origin  https://github.com/IT3AI1/TIL.git (fetch)
origin  https://github.com/IT3AI1/TIL.git (push)

원격 저장소가 잘못 등록되어 삭제해야 하는 경우

$ git remote rm origin
$ git remote -v

원격 저장소에 나의 소스 코드 업로드

  • add, commit 이후에 원격 저장소에 나의 로컬 버전 기록을 업로드 하고 싶으면 push 진행

  • 2.23 버전의 로그인 이슈

    • 아래 명령어를 입력하고 다시 push 작업 진행
    $ git config --global credential.git.github.com.provider generic
    
    • 혹은 vscode에 git bash를 연결해서 push하면 문제 해결 가능
$ git push -u origin master # 첫 git push를 하면 아래와 같이 인증을 요구하는 화면이 나옴
Select an authentication method for 'https://github.com/':
  1. Web browser (default)  
  2. Personal access token  
option (enter for default):  # 바로 enter를 눌러서 진행

# 조금 기다린 뒤에 github 사이트로 들어가서 새로고침하면 로컬 저장소에 있는 모든 내역이 업로드 된 것을 확인할 수 있음

# 이후에는 add & commit을 진행하고 push를 하면 새로운 버전을 원격 저장소(github)에 업로드 할 수 있다.
$ git push -u origin master

Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 214 bytes | 214.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/IT3AI1/TIL.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

image

기본 루틴

$ git add .
$ git commit -m "커밋 메시지"
$ git push origin master # git아 버전 이력을 push 해줘 어디로?! origin(우리가 붙인 별명)으로 master 브랜치를!

image-20210705165108737

Git에서 push는 로컬 저장소에서 관리한 버전 이력을 원격 저장소로 업로드하는 명령어이고, pull은 원격 저장소의 변경 사항을 받아오며, clone은 원격 저장소 전체를 복제한다. 이 글은 push·pull·clone의 사용법과 .gitignore, 그리고 push 에러 해결 방법을 정리한다.

push

  • 로컬 저장소에서 관리한 버전 이력을 원격 저장소(github)으로 업로드 하는 명령어

      $ git push origin master
    
  • 참고

    $ git push -u origin master # 여기서 -u는 최초 1회만 붙이면 됩니다. (최소 1회 push 할 때만 붙이자!)
    

pull

  • 원격 저장소의 변경 사항을 받아옴(업데이트)
  • commit 내역을 기반으로 변경 사항을 새로 갱신하는 행위
$ git pull origin master # git아 pull(업데이트 해줘) origin 이라는 원격 저장소로부터 master 브랜치를!
$ git pull origin master
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 739 bytes | 67.00 KiB/s, done.
From https://github.com/IT3AI1/TIL
 * branch            master     -> FETCH_HEAD
   d2c6be1..29e2c44  master     -> origin/master
Updating d2c6be1..29e2c44
Fast-forward
 README.md | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 README.md

  • 원격저장소에 read.me를 추가한 후 pull을 한 결과이다.

clone

  • 원격 저장소 전체를 복제
  • 최초 1회만 수행

  • 클론 받은 프로젝트는 자동으로 .git이 설정되어 있음 (+ remote도 등록되어 있음)
    • 복제한거니까!
$ git clone 복제할저장소URL

image

만약 원격 저장소의 이름이 아닌 다른 이름으로 폴더를 clone 받고 싶다면?

$ git clone 원격저장소URL clone받을폴더이름

# 예시
$ git remote add origin https://github.com/IT3AI1/TIL.git TIL-test

push & pull(+clone) 시나리오

최초 1회 clone 이후 add, commit, push & pull, add, commit, push

image-20210705173745218

gitignore

git이 어떠한 폴더 / 파일의 버전 관리를 무시할 수 있도록 적어두는 문서 -> 여기에 적힌 내용은 git이 버전 관리를 하지 않습니다.

  • 일반적으로 개인정보 및 특정 사람에게만 적용되는 개발 환경과 같은 설정이 포함됨
    • 개발할 때 사용하는 secret key, (보안상의 이유로) github과 같은 원격저장소에 업로드되면 안되는 정보 등은 git이 버전 관리를 하면 안됨
  • 그리고 버전 관리가 필요없다고 생각되는 폴더 혹은 파일도 포함 시킬 수 있음
  • .gitignore 파일을 만들어서 관리한다.
  • 일반적으로 git init 을 진행하기 전에 만든다.
practice # 특정한 폴더를 git이 관리하지 않게함
a.txt # 특정한 파일을 git이 관리하지 않게함
*.txt # 특정한 확장자를 가진 파일을 git이 관리하지 않게함

image

gitignore.io

https://gitignore.io/

  • .vscode / .DS_Store -> 보안상의 이유가 아니더라도 버전 관리의 필요성이 없는 친구들이 존재..!

  • 미리 특정한 언어, 프레임워크, 운영 체제 등에서 git으로 버전 관리를 할 필요가 없는 요소를 누군가! 미리 만들어 놓았음
  • 언어, 프레임워크, 운영체제 등을 검색하고 ‘생성’ 버튼을 누르면 자동으로 필요한 내용을 만들어 준다.

다시 정리!

  • gitignore는 어느 시점에 만드는 것이 좋을까요?
    • git init 전/후
  • gitignore 파일이 위치하는 곳은 어디일까요?
    • 일반적으로 .git 폴더가 존재하는 곳에 생성

push error

$ git push origin main
To https://github.com/IT3AI1/practice.git
 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to 'https://github.com/IT3AI1/practice.git'   # 거절됨...
hint: Updates were rejected because the tip of your current branch is behind  
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.     

해석

# 원격 저장소로 push하는 것을 실패했음..
error: failed to push some refs to 'https://github.com/IT3AI1/practice.git' 

# update가 reject되었는데 왜냐면... remote에 local 없는 어떤 변경 사항이 있음...
hint: Updates were rejected because the remote contains work that you do

# 이러한 상황은 커밋이 다른 경우에 발생하는데.. remote 저장소와 local 저장소를 일치 시켜야 할 것 같음...
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

# 어.. git pull 한번 해볼래?? 언제?? 다시 push 하기 전에...
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
  1. 로컬 저장소(내 컴퓨터)와 원격 저장소(github)의 commit 이력을 비교해본다!
    • 이 문제는 로컬 저장소와 원격 저장소의 commit 이력이 다르기 때문에 발생하는 문제
    • 주로 집에서 push를 하고 강의장에 가서 pull하지 않은 채로 작업하고 add, commit 이후에 push를 하면 보게 되는 메시지
  2. 로컬 저장소에서 $ git pull origin master를 진행한다.
  3. vs code 창이 나오면 text 파일을 x 버튼을 눌러서 종료 한다.
    • 이때 vs code 창이 열리지 않는 경우도 있기 때문에 당황하지 말 것!

3. 브랜치 전략과 병합(Merge)

참고: 이 포스트의 실습 예제는 master 브랜치를 사용합니다. 새로운 프로젝트에서는 main 브랜치 사용을 권장합니다.

branch command

$ git init
Initialized empty Git repository in F:/branch_practice/.git/

$ touch a.txt
$ git add .
$ git commit -m 'Finish a.txt'
[master (root-commit) 31d9b7b] Finish a.txt
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a.txt

브랜치 생성

$ git branch 브랜치이름

# 예시
$ git branch feature

브랜치 목록 확인

  • * 가 찍혀있는 것은 현재 위치한 브랜치를 의미함
$ git branch
  feature
* master

브랜치 이동

$ git checkout 브랜치이름

# 예시
$ git checkout feature
Switched to branch 'feature'

$ git branch
* feature
  master

브랜치 생성 + 이동

$ git checkout -b 브랜치명

# 예시
$ git checkout -b feature2
Switched to a new branch 'feature2'

$ git branch
  feature
* feature2
  master

브랜치 병합 (merge)

  • HEAD : 현재 브랜치의 최신 커밋
# feature2 브랜치에서 b.txt 파일 생성 후 add, commit
$ touch b.txt
$ git add .
$ git commit -m "Finish b.txt in feature2 branch"
[feature2 b4c6264] Finish b.txt in feature2 branch
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 b.txt
  • feature2에서 b.txt를 생성했다. 이를 다시 master로 가서 feature2와 merge를 할 것이다.
# master 브랜치로 이동
$ git checkout master
Switched to branch 'master'

# merge
$ git merge 머지할브랜치이름

# 예시
$ git merge feature2 
Updating 31d9b7b..b4c6264
Fast-forward
 b.txt | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 b.txt

# 확인
$ git log
commit b4c6264a5c45aacb80d58851e6800848723c1122 (HEAD -> master, feature2)
Author: ingu627 <rjsdudans@naver.com>
Date:   Sun Nov 21 16:11:05 2021 +0900

    Finish b.txt in feature2 branch

commit 31d9b7b69c3441f83c1f05295c74ac519c797d87 (feature)
Author: ingu627 <rjsdudans@naver.com>
Date:   Sun Nov 21 16:06:23 2021 +0900

    Finish a.txt

브랜치 삭제

$ git branch -d 브랜치이름

# 예시
$ git branch -d feature2
Deleted branch feature2 (was b4c6264).

# 확인
$ git branch
  feature
* master

branch 명령어

1. branch 생성

git branch 브랜치명

2. branch 이동

git checkout 브랜치명

3. branch 생성 & 이동

git checkout -b 브랜치명

4. branch 상태 확인 (local)

git branch

5. branch 상태 확인 (remote)

git branch -r

6. branch 상태 확인 (local + remote)

git branch -a

7. remote branch 가져오기

git checkout -t 닉네임/브랜치명

git checkout --track 닉네임/브랜치명

8. branch 병합

  • 다른 브랜치를 현재 Checkout된 브랜치에 Merge 하는 명령
git merge 브랜치명

9. branch 삭제 (local)

git branch -d 브랜치명

10. branch 강제 삭제 (local)

git branch -D 브랜치명 

11. branch 원격 저장소 삭제 (remote)

git push origin -d 브랜치명

git push origin --delete 브랜치명

12. 유효하지 않은 branch 업데이트 (청소) (remote내에서)

  • 리모트 브랜치의 더 이상 유효하지 않은 참조를 깨끗이 지우는 명령어
git remote prune origin

git remote update --prune

13. branch 로컬 업데이트

  • 로컬 저장소를 최신 정보로 갱신(리모트 저장소와 동기화)하며 자동적으로 더이상 유효하지 않은 참조를 제거한다.
git fetch -p

14. commit 기록 그래프화

git log --all --oneline --graph

15. branch 업로드

git push origin 브랜치명 

16. branch명 변경

git branch -m 브랜치명 새로운 브랜치명

17. merge여부 branch 보여줌

  • 현재 Checkout한 브랜치에 Merge한 브랜치를 살펴본다.
  • 현재 Checkout한 브랜치에 Merge하지 않은 브랜치를 살펴본다.
git branch --merged

git branch --no-merged

branch scenario

1. branch 사용법

중요: 브랜치를 조작할 때는 반드시 master 브랜치에 최소 1개의 커밋이 있어야 합니다!

실습 준비

  • 따라해 본다.
# git 초기화 및 commit 남겨놓기

$ git init
$ touch a.txt 
# a.txt에 text 1이라는 글을 작성한다.

$ git add .
$ git commit -m'text2'
[master 67f01f6] text2
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 b.txt
# 3개의 커밋을 더 남긴다.
$ git log --oneline
b9aaaf5 (HEAD -> master) test3
67f01f6 text2
acf00f0 Initial commit

ex. 이제 우리는 로그인 기능을 개발해본다고 가정

$ git branch feature/login # 브랜치 생성만
$ git branch
  feature/login
* master

master 브랜치에서 마지막 줄 추가

text 1
text 2
text 3
master에서 작성한 text 4 # 추가

# add, commit
$ touch d.txt # master에서 d.txt를 생성했다.
$ git add .
$ git commit -m'master text 4'
[master 121a503] master text 4
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 d.txt

 
 # log
$ git log --oneline
121a503 (HEAD -> master) master text 4
b9aaaf5 (feature/login) test3
67f01f6 text2
acf00f0 Initial commit

브랜치 이동을 하고 a.txt를 확인해보자

$ git checkout feature/login 
Switched to branch 'feature/login'

# master text 4가 사라짐

# log 확인
$ git log --oneline
b9aaaf5 (HEAD -> feature/login) test3
67f01f6 text2
acf00f0 Initial commit
# 현재 브랜치는 feature/login

text 1
text 2
text 3
login text 4 # 추가

브랜치를 다룰 때는 현재 내가 ‘어떤’ 브랜치에 있는지를 반드시 알아야 합니다!

# login.txt

login text 4 # 추가
$ touch login.txt

$ git status
On branch feature/login
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        login.txt # WD + commit 이력 없음(git이 지금까지 한번도 추적한적 없음)

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


# add, commit
$ git add .
$ git commit -m'login text4'
[feature/login f6dda89] login text4
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 login.txt

# 현재 브랜치는? feature/login
$ git log --oneline
f6dda89 (HEAD -> feature/login) login text4
b9aaaf5 test3
67f01f6 text2
acf00f0 Initial commit


# 다른 브랜치의 그래프까지 보는 방법
$ git log --all --graph --oneline
* f6dda89 (HEAD -> feature/login) login text4
| * 121a503 (master) master text 4
|/
* b9aaaf5 test3
* 67f01f6 text2
* acf00f0 Initial commit

2. branch merge scenario

  • 드라이브에 git-merge 폴더 생성 후 해당 폴더에서 VS Code를 열자! image

  • Ctrl + 백틱(1왼쪽에 있는)을 누른 후 Git Bash 클릭 후 작업

image

$ git init
Initialized empty Git repository in F:/git-merge/.git/

$ touch test.txt
  • test.txt에 master test 1이라고 채워넣기 (저장 꼭!!) image
$ git status
On branch master

No commits yet

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

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

$ git add .
$ git commit -m'master test 1'
[master (root-commit) 9fe5f83] master test 1
 1 file changed, 1 insertion(+)
 create mode 100644 test.txt
 
# log
$ git log
commit 9fe5f839afe5de4e366dc981f62f9692cce859ee (HEAD -> master)
Author: ingu627 <rjsdudans@naver.com>
Date:   Sun Nov 21 19:41:06 2021 +0900

    master test 1

3가지 병합 상황

1. fast-forward

image-20210706133955957

주의사항: 반드시 master 브랜치에는 1개 이상의 커밋이 존재해야 합니다!

“다른 브랜치가 생성된 이후에 master 브랜치의 변경 사항이 없는 상황 => 단순하게 master 브랜치의 포인터를 최신 commit으로 이동시킨다.”

master 브랜치에서 feature/login 브랜치를 병합(merge) 할 때 feature/login 브랜치가 master 브랜치 이후의 commit을 가리키고 있다면 master 브랜치는 feature/login 브랜치가 가리키고 있는 최신 commit으로 포인터(HEAD)를 이동시키면 된다.

1. feature/login 브랜치 생성 후 이동

$ git checkout -b feature/login
Switched to a new branch 'feature/login'

2. 특정한 작업을 완료하고 commit 진행

$ touch login.txt
$ git add .
$ git commit -m'login test 1'
[feature/login 6302c25] login test 1
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 login.txt

# log
$ git log --oneline
# master 브랜치에서 feature/login 브랜치 생성 -> feature/login 브랜치에서 새로운 commit 생성
6302c25 (HEAD -> feature/login) login test 1
9fe5f83 (master) master test 1

image

3. master 브랜치로 이동

  • 브랜치를 이동하는 순간 login.txt 파일이 사라진다.
  • 왜냐하면 login.txt 파일은 master 브랜치가 아니라 feature/login 브랜치에서 commit(버전)을 했기 때문임
 $ git checkout master
 Switched to branch 'master'

image

4. master 브랜치에 feature/login 브랜치에서 작업한 결과물 병합

  • 병합 이후에 login.txt 파일이 생김

병합 규칙: 병합하고자 하는 브랜치로 이동한 후 작업합니다.

$ git merge feature/login
Updating 9fe5f83..6302c25
Fast-forward
login.txt | 0
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 login.txt

image

5. 로그 확인

  • HEAD -> master가 위로 올라왔음!
# 병합 전
$ git log --oneline
# master 브랜치에서 feature/login 브랜치 생성 -> feature/login 브랜치에서 새로운 commit 생성
6302c25 (HEAD -> feature/login) login test 1
9fe5f83 (master) master test 1

# 병합 후 
$ git log --oneline
6302c25 (HEAD -> master, feature/login) login test 1
9fe5f83 master test 1

6. 브랜치 삭제

$ git branch -d feature/login
Deleted branch feature/login (was 6302c25).

# 확인
$ git branch
* master

$ git log --oneline
6302c25 (HEAD -> master) login test 1
9fe5f83 master test 1

2. Merge commit -> conflict x

image-20210706135659337

다른 브랜치(feature/signup)가 생성된 이후에 master 브랜치에 변경 사항이 있고 feature/signup 브랜치에도 변경 사항이 존재하는 경우 -> 이때 충돌 상황이 발생하지 않는 경우

  • 다른 파일의 변경 사항을 작성(수정) commit한 경우

  • Merge commit 이 발생함
    • 이 commit 내역은 우리가 직접 작성한 commit이 아닌 git이 자동으로 작성해준 commit message
  • 충돌이 발생하지 않는 이유는 다른 파일 (같은 파일이라도 다른 라인을 수정한 경우 포함)이기 때문에 단순하게 합치기만 하면 되기 때문!

1. signup 브랜치 생성 & 이동

  • git checkout -b 브랜치이름 : 이름명으로 생성하고 그 브랜치로 이동
$ git checkout -b feature/signup
Switched to a new branch 'feature/signup'

2. 특정한 작업을 진행하고 commit

$ touch signup.txt

# add, commit
$ git add .
$ git commit -m'signup test 1'
[feature/signup 586d298] signup test 1
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 signup.txt
   
# log
$ git log --oneline
586d298 (HEAD -> feature/signup) signup test 1  # feature/signup 브랜치의 최신 커밋 + 현재 내가 위치한 브랜치
6302c25 (master) login test 1 # master 브랜치의 최신 커밋
9fe5f83 master test 1

image

3. master로 이동

  • 이동하는 순간 signup.txt 파일이 사라짐
  • signup 브랜치에서 파일을 생성하고 commit 했기 때문!
$ git checkout master
Switched to branch 'master'

$ git log --oneline
6302c25 (HEAD -> master) login test 1
9fe5f83 master test 1

image

4. master 브랜치에서 추가 작업을 진행한 다음 commit

$ touch master.txt

# add, commit
$ git add .
$ git commit -m'master test 1'
[master 2c9f3c0] master test 1
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 master.txt
   
# log 
$ git log --oneline
2c9f3c0 (HEAD -> master) master test 1
6302c25 login test 1
9fe5f83 master test 1

# 브랜치 포함 모든 log를 확인할 때 
$ git log --all --oneline
2c9f3c0 (HEAD -> master) master test 1
586d298 (feature/signup) signup test 1
6302c25 login test 1
9fe5f83 master test 1

5. master 브랜치에 병합

  • merge를 진행하고나서 확인해보니 signup.txt가 생기는 걸 확인할 수 있음
$ git merge feature/signup
Merge made by the 'recursive' strategy.
 signup.txt | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 signup.txt

# log
$ git log --oneline
6e469f3 (HEAD -> master) Merge branch 'feature/signup' # 우리가 직접 입력한 commit x -> Merge commit
2c9f3c0 master test 1
586d298 (feature/signup) signup test 1
6302c25 login test 1
9fe5f83 master test 1

6. log

  • graph로 보고자 할 때
$ git log --all --graph --oneline
*   6e469f3 (HEAD -> master) Merge branch 'feature/signup'
|\
| * 586d298 (feature/signup) signup test 1
* | 2c9f3c0 master test 1
|/
* 6302c25 login test 1
* 9fe5f83 master test 1

7. branch 삭제

$ git branch -d feature/signup
Deleted branch feature/signup (was 586d298).

$ git log --all --graph --oneline
*   6e469f3 (HEAD -> master) Merge branch 'feature/signup'
|\
| * 586d298 signup test 1
* | 2c9f3c0 master test 1
|/
* 6302c25 login test 1
* 9fe5f83 master test 1

3. Merge commit -> conflict o

다른 브랜치(feature/signup)가 생성된 이후에 master 브랜치에 변경 사항이 있고 feature/signup 브랜치에도 변경 사항이 존재하는 경우 -> 이때 충돌 상황이 발생하는 경우

  • 같은 파일의 같은 부분을 작성(수정)하여 commit한 경우

  • Merge commit 이 발생함
    • 이 commit 내역은 우리가 직접 작성한 commit이 아닌 git이 자동으로 작성해준 commit message
  • 충돌이 발생하는 이유는 같은 파일의 같은 부분을 수정했기 때문에 어떤 버전으로 만들지 git은 알 수 없기 때문!
  • 그래서 직접 우리가(==사람이) 수정해줘야 한다.

1. hotfix 브랜치 생성 후 이동

$ git checkout -b hotfix
Switched to a new branch 'hotfix'

2. 특정 작업 완료 후 & commit 진행

# hotfix branch의 test.txt
master test 1
이건 hotfix 브랜치에서 # 추가
수정했습니다.

image

$ git add .
$ git commit -m'hotfix test 1'
[hotfix 03ba33b] hotfix test 1
 1 file changed, 3 insertions(+), 1 deletion(-)
   
$ git log --oneline
03ba33b (HEAD -> hotfix) hotfix test 1
6e469f3 (master) Merge branch 'feature/signup'
2c9f3c0 master test 1
586d298 signup test 1
6302c25 login test 1
9fe5f83 master test 1

3. master로 이동

$ git checkout master
Switched to branch 'master'

4. 특정 작업 후 commit

  • hotfix 브랜치에서 작업한 부분과 동일한 부분을 수정
# master branch의 test.txt
master test 1
이건 master 브랜치에서 # 추가
수정한 내용입니다.

image

$ git add .
$ git commit -m'master test 1'
[master b74a431] master test 1
 1 file changed, 3 insertions(+), 1 deletion(-)

# log
$ git log --oneline
b74a431 (HEAD -> master) master test 1
6e469f3 Merge branch 'feature/signup'
2c9f3c0 master test 1
586d298 signup test 1
6302c25 login test 1
9fe5f83 master test 1

5. Merge

$ git merge hotfix 
Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt # 충돌.. -> merge 하는 과정에서 충돌이 발생함 -> test.txt

# 자동으로 병합하는 것 실패! -> 충돌을 해결하고 난 다음에 그 결과를 commit 하세요
Automatic merge failed; fix conflicts and then commit the result.

image

  • 같은 부분(라인)을 다른 브랜치에서 각각 수정하고 commit 했기 때문에 병합하는 과정에서 git은 어떤 부분을 합쳐야 하는지 알 수 없음 -> 그래서 우리가 직접 합치고 나서 merge commit을 발생시켜야 한다.

     master test 1
     <<<<<<< HEAD
     이건 master 브랜치에서 
     수정한 내용입니다.
     =======
     이건 hotfix 브랜치에서
     수정했습니다.
     >>>>>>> hotfix
    

image

# 수정
master test 1
이건 master 브랜치에서 
수정한 내용입니다.
이건 hotfix 브랜치에서
수정했습니다.
# master | MERGING -> Merge commit을 남겨야 함

image

$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   test.txt # both modified

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

# add로 올리고 commit으로 Merge commit을 직접 남긴다.
$ git add .
$ git commit -m "충돌 해결!"
[master 03c4743] 충돌 해결!

6. log 확인

  • 충돌이 발생하지 않은 경우에는 6e469f3 (HEAD -> master) Merge branch 'feature/signup' 이러한 commit을 git이 자동으로 만들어주지만 충돌을 직접 해결하는 경우 우리가 충돌을 해결했다는 의미의 커밋을 직접 남길 수 있다.
$ git log --oneline
03c4743 (HEAD -> master) 충돌 해결!
b74a431 master test 1
03ba33b (hotfix) hotfix test 1
6e469f3 Merge branch 'feature/signup'
2c9f3c0 master test 1
586d298 signup test 1
6302c25 login test 1
9fe5f83 master test 1

7. branch 삭제

$ git branch -d hotfix
Deleted branch hotfix (was 03ba33b).

# log
$ git log --all --oneline --graph
*   03c4743 (HEAD -> master) 충돌 해결!
|\
| * 03ba33b hotfix test 1
* | b74a431 master test 1
|/
*   6e469f3 Merge branch 'feature/signup'
|\
| * 586d298 signup test 1
* | 2c9f3c0 master test 1
|/
* 6302c25 login test 1
* 9fe5f83 master test 1

4. 변경 사항 되돌리기(Undoing)

undoing은 어떤 행위를 취소할 때 사용하는 명령어다. 이 글은 파일을 Unstage로 되돌리는 git rm --cached와 git restore --staged, Modified 파일을 되돌리는 git restore, 완료된 커밋을 수정하는 git commit --amend, 그리고 reset과 revert의 차이를 정리한다.

undoing

  • 어떤 행위를 취소할 때 사용하는 명령어

  • 사전 준비

# git 초기화 & a.txt README.md 파일 생성

image

1. 파일 상태를 Unstage로 변경하기

Staging Area(INDEX)와 Working Directory(WA)를 넘나드는 방법

첫 번째 - rm --cached

  • 따로 따로 커밋하려고 했지만 실수로 모두 $ git add . 를 한 상황 (처음으로 add를 하는 상황이라고 가정)
$ git status
On branch master

No commits yet

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

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

$ git add .

$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage) # rm --cached를 써 ... 무대에서 내리고 싶으면..!
        new file:   README.md
        new file:   a.txt    

a.txt를 add하기 전으로 돌린다.

$ git rm --cached a.txt
rm 'a.txt'

$ git status
On branch master

No commits yet

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

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        a.txt # 무대에서 내려옴!!

image-20210706143516204

commit 남겨보자!

$ git add .
$ git commit -m'first commit'
[master (root-commit) a46391e] first commit
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 README.md
 create mode 100644 a.txt

두 번째 - restore

  • 두 개의 파일을 모두 수정하고 따로따로 커밋하려고 했지만, 실수로 $ git add . 라고 해버린 상황

  • a.txt, README.md 파일에 각각 메시지를 남겨보자

image

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory) # WD에 있음 + commit이 한번이라고 발생 했던 친구? 넵! -> modified!       
        modified:   README.md
        modified:   a.txt

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

$ git add .

# status 
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   README.md
        modified:   a.txt
$ git restore --staged a.txt

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   README.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)       
        modified:   a.txt

image-20210706143940012

첫 번째와 두 번째 뭐가 다를까?

bash로 볼 때

$ touch b.txt

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   README.md  # SA + commit이 한번이라도있었던 -> restore --staged

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in 
working directory)
        modified:   a.txt # WD + commit이 한번이라도 있었던 

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        b.txt # WD + commit이 한번도 안된 친구

다시 정리하면

  1. git rm --cached <file>
    • 기존에 커밋이 없는 경우 SA -> WD로 내릴 때 사용
  2. git restore --staged <file>
    • 기존에 커밋이 있는 경우 SA -> WD로 내릴 때 사용

2. Modified된 파일 되돌리는 방법

  • add가 되어있지 않은(WD에 있는) + 수정된(modified) a.txt를 다시 돌려보자
  • 일단 commit은 적어도 한번 있었고 수정되었음
  • 하지만 SA에 올라가지 않은 상태

주의!!!!

  • 원래 파일로 돌아갔기 때문에 ‘절대로’ 다시 되돌릴 수 없음
  • 수정한 내용이 마음에 들지 않을 때만 사용해야 함(정말 마음에 안들때만 써야함)
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   README.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in 
working directory)
        modified:   a.txt # 이녀석을 수정 전 상태로 돌릴 예정

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        b.txt
# 기존에 a.txt에 작성된 내용이 모두 사라짐
$ git restore a.txt

# status -> 애초에 commit으로 남기지 않았기 때문에 돌릴 수 없음
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   README.md

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

3. 완료된 커밋 수정

$ git commit --amend
  1. 커밋 메시지를 잘못 적은 경우 수정!!
    • 가장 최신의 commit만 수정 가능함..!
    • 역사를 바꾸려고 하면 안됨..!
  2. 너무 일찍 커밋을 한 경우(무언가 빼먹고 commit을 진행한 경우)

[주의 사항] :커밋 메시지를 바꾸면 커밋 해시값이 변하기 때문에 원격 저장소에 업로드한 경우 커밋 메시지는 절대로 수정하지 말 것! amend 는 로컬에서만 사용한다.

3.1 커밋 메시지 수정

  • 수정을 진행하고 창을 닫아주면 됩니다.
$ git add .
$ git commit -m'amend text file' # 오타가 났다.
[master 2cc67ed] amend text file 
 2 files changed, 3 insertions(+)
 create mode 100644 b.txt   
 
# 수정 진행
# amend text file -> 이 부분 수정

$ git commit --amend -m 'text file'
[master d984105] text file
 Date: Sun Nov 21 22:11:31 2021 +0900
 2 files changed, 3 insertions(+)    
 create mode 100644 b.txt

image

3-2. 어떠한 파일을 빼먹고 commit을 한 경우

다시 커밋을 하고 싶으면 수정 작업을 하고 SA에 추가한 다음 --amend 옵션을 사용하여 커밋 재작성

$ touch foo.txt bar.txt
$ git add foo.txt
# 상태 확인
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   foo.txt # SA + new file -> commit이 한번도 없었던 상태

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        bar.txt # WD
# 실수로 bar.txt를 빼먹고 커밋을 진행함
$ git commit -m'foo & bar'
[master 48f0541] foo & bar
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 foo.txt

# log
$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        bar.txt # bar는 WD에 남아있음!

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

해결하기

$ git add bar.txt 
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   bar.txt
$ git add bar.txt

$ git commit --amend -m'foo & bar'
[master d6175dd] foo & bar
 Date: Sun Nov 21 22:22:41 2021 +0900
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 bar.txt
 create mode 100644 foo.txt
# 상태 확인
$ git status
On branch master
nothing to commit, working tree clean

# log 확인
$ git log --oneline
d6175dd (HEAD -> master) foo & bar # 새로운 커밋이 생긴게 아니라 기존 커밋에 bar.txt의 변경 사항만 추가됨
d984105 text file
a46391e first commit

reset vs revert

reset

  • https://git-scm.com/docs/git-reset
  • “시계를 마치 과거로 돌리는 듯한 행위”
  • 특정 커밋으로 되돌아가며 되돌아간 특정 커밋 이후의 커밋들은 모두 사라지며, 파일 상태는 옵션을 통해 결정

3가지 옵션

1. --soft

  • reset하기 전까지 했던 SA, WD 작업은 남겨둠
  • 돌아가려는 커밋으로 되돌아가고,
  • 이후의 commit된 파일들을 staging area로 돌려놓음 (commit 하기 전 상태)
  • 즉, 바로 다시 커밋할 수 있는 상태가 됨

2. --mixed

  • (기본) SA reset, WD작업은 남겨둠
  • 돌아가려는 커밋으로 되돌아가고,
  • 이후의 commit된 파일들을 working directory로 돌려놓음 (add 하기 전 상태)
  • 즉, unstaged 된 상태로 남아있음
  • 기본값

3. --hard

  • reset하기 전 SA, WD 모든 작업 리셋
  • 돌아가려는 커밋으로 되돌아가고,
  • 이후의 commit된 파일들(tracked 파일들)은 모두 working directory에서 삭제
  • 단, Untracked 파일은 Untracked로 남음

08-2

# undoing 폴더에서 했던 내용 이어서 진행

# --hard 예시
$ git log --oneline
d6175dd (HEAD -> master) foo & bar
d984105 text file
a46391e first commit
$ git reset --hard d984105
HEAD is now at d984105 text file
$ git log --oneline
d984105 (HEAD -> master) text file
a46391e first commit

$ git status
On branch master
nothing to commit, working tree clean

reset 특징들

  • reset은 과거로 돌아가게 되면 돌아간 커밋 이후의 커밋은 모두 히스토리에서 사라짐
  • 커밋 히스토리가 바뀌기 때문에 다른 사람과 공유하는 브랜치에서 사용 시 충돌이 발생
  • 공유하는 브랜치에서 이전 커밋을 수정하고 싶을 때는 git revert 사용

revert

  • https://git-scm.com/docs/git-revert

  • “특정 사건을 없었던 일로 만드는 행위”
  • 이전 커밋 내역을 그대로 남겨둔 채 새로운 커밋(==없었던 일입니다!를 의미하는 commit)을 생성
  • 커밋 히스토리 변경 없이 해당 커밋 내용만을 삭제한 상태의 새로운 커밋을 생성

08-1

추가 commit 2개만 더 남기자

# undoing에서 이어서 진행
$ touch c.txt d.txt
$ git add c.txt
$ git commit -m "Add c.txt"
[master d9c38f7] Add c.txt
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 c.txt

$ git add d.txt
$ git commit -m "Add d.txt"
[master aaf2db9] Add d.txt
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 d.txt
$ git log --oneline
aaf2db9 (HEAD -> master) Add d.txt
d9c38f7 Add c.txt
d984105 text file
a46391e first commit

revert commit 편집기 실행

  • 다른 사람과 공유하는 브랜치에서 이전 커밋을 수정하고 싶을 때 사용
  • 커밋 히스토리가 바뀌지 않기 때문에 충돌이 발생하지 않음
$ git revert d984105
Removing b.txt
hint: Waiting for your editor to close the file... error: There was a problem 
with the editor 'vi'.
                     Please supply the message using either -m or -F option.

$ git log --oneline
# 기존 commit 이력이 그대로 남아있기 때문에 해당 하는 시점으로 언제든 
aaf2db9 (HEAD -> master) Add d.txt
d9c38f7 Add c.txt
d984105 text file
a46391e first commit

정리

08-3

image

그외 방법

$ git reflog
aaf2db9 (HEAD -> master) HEAD@{0}: reset: moving to aaf2db9
d47f656 HEAD@{1}: reset: moving to d47f656
d47f656 HEAD@{2}: commit: text file amend
aaf2db9 (HEAD -> master) HEAD@{3}: commit: Add d.txt
d9c38f7 HEAD@{4}: commit: Add c.txt
d984105 HEAD@{5}: reset: moving to d984105
d6175dd HEAD@{6}: commit (amend): foo & bar
48f0541 HEAD@{7}: commit: foo & bar
d984105 HEAD@{8}: commit (amend): text file
2cc67ed HEAD@{9}: commit: amend text file
a46391e HEAD@{10}: commit (initial): first commit
  • reflog는 이런식으로 이전까지했던 작업들 reflog를 확인해 몇번째 HEAD로 이동할지 확인한다.

  • 만약 HEAD@{7}로 이동할꺼라면

$ git reset --hard HEAD@{7}

5. 핵심 요약 치트시트 & References

5.1 Git 핵심 명령어 치트시트

분류 명령어 설명
설정 git config --global user.name "이름" 커밋 작성자 이름 설정
  git config --global user.email "이메일" 커밋 작성자 이메일 설정
  git config --global core.editor "code --wait" 기본 커밋 에디터를 VS Code로 변경
기초 git init 현재 디렉토리를 로컬 Git 저장소로 초기화
  git status 워킹 디렉토리와 스테이징 영역의 파일 상태 확인
  git add <파일명> (git add .) 변경된 파일을 Staging Area에 추가
  git commit -m "메시지" 스테이지된 변경 사항을 버전으로 기록
  git log --oneline --graph --all 커밋 히스토리를 한눈에 그래프로 확인
원격 git remote add origin <URL> 원격 저장소 등록
  git push -u origin <브랜치명> 로컬 커밋 이력을 원격 저장소로 업로드 (최초 -u)
  git pull origin <브랜치명> 원격 저장소의 최신 커밋을 가져와 병합
  git clone <URL> 원격 저장소를 로컬로 복제 (.git 자동 설정)
브랜치 git branch <이름> 새로운 브랜치 생성
  git checkout <이름> / git switch <이름> 지정 브랜치로 이동
  git checkout -b <이름> / git switch -c <이름> 브랜치 생성과 동시에 이동
  git merge <브랜치명> 현재 브랜치에 대상 브랜치 병합
  git branch -d <이름> 병합 완료된 브랜치 삭제 (-D: 강제 삭제)
되돌리기 git restore --staged <파일> Staging Area에서 워킹 디렉토리로 내리기 (Unstage)
  git restore <파일> 워킹 디렉토리의 수정 사항 취소 (Modified 취소)
  git commit --amend 직전 커밋 메시지 수정 또는 파일 누락 반영
  git reset --soft <커밋ID> 커밋 취소 + Staged 상태 유지
  git reset --mixed <커밋ID> 기본값: 커밋 및 스테이지 취소 (WD 수정 유지)
  git reset --hard <커밋ID> 커밋, 스테이지, 워킹 디렉토리 변경 완전 폐기
  git revert <커밋ID> 기존 커밋을 취소하는 새로운 반대 커밋 생성
  git reflog HEAD의 모든 이동 이력 조회 (유실된 커밋 복구)

5.2 References

첫 번째 글입니다 가장 최근 글입니다

댓글남기기