[Git] 조금만 더 알아보자(1)

식빵·2022년 2월 22일
0

git

목록 보기
4/5
post-thumbnail

여태 배웠던 거에서 약간의 부수적인 것들을 더 알아보자.


👏 commit 심화

📌 hunk

의미: 파일 단위가 아닌 더 작은 단위의 변경사항

우리는 여태 커밋을 하나의 파일 단위로 했었다.
그런데 만약에 하나의 파일에서도 라인별로 다른 커밋을 하고 싶을 때가 있다.
이건 기존의 파일 단위 커밋으로는 힘들다.

이럴 때 필요한 개념이 hunk 이다.
실습을 통해 알아보자.

$ touch testFile.txt

$ vim testFile.txt

$ cat testFile.txt
including text for elastic develop

including text for docker config

including text for linux bash config



$ git add .; git commit -m 'commit hunk test file' # 일단 커밋

$ vim testFile.txt # 편집! 모든 문자열 뒤에 "changed" 라는 문자열을 추가해줬다.
$ cat testFile.txt
including text for elastic develop - changed!!!

including text for docker config - changed!!!

including text for linux bash config - changed!!

# 이 상태에서 하나의 파일을 단위로 작업하는게 아니라 라인별로 add, commit 따로 해보자.

$ git add -p # 이렇게 입력하면... 아래처럼 화면이 나온다.

현재 [y, n, q, a, d, s, e, ?] 중 하나를 입력하라고 하는데,
도통 뭔소린지 모르겠다. 이럴 때는 ? 를 입력하고 엔터한다.


도움말을 보니 대충 상황을 알았다.
(1/1)이라는 표시는 현재 hunk 가 그냥 통으로 다 잡혔다는 뜻이다.


나는 각 라인별 변화가 hunk로 잡히길 바라서 s 줬다.
그 이후에 각 hunk 에 대해서 staging 여부를 y/n으로 결정했다.
여기서는 elastic, bash config 와 관련된 라인에 대해 add를 했다.


이 상태에서 git status 명령어를 입력하면 위처럼 나온다.


git diff --staged를 통해서 현재 staging 된 것과 리포지토리 커밋을 비교해보자.


마지막으로 git commit -v를 통해서 커밋을 진행한다. 그러면 위와 같은 커밋 메세지
작성 창이 나온다. 여기서 맨 첫줄에 적절한 메세지를 적어주고 :wq로 나와주자.

커밋 내용을 보면 방금까지 봤던 git diff --staged 와 같은 내용이 있음을 알 수 있다.

지금까지 add 와 commit을 더 세세하게 하는 법을 알아봤다.




📌 git stash

커밋하기 애매한 것들을 잠시 임시공간(stack)에 치워두는 기능

어떤 일을 하다보면 갑자기 선임자가 일을 부탁해서 하던 작업을 중단하고 변경사항을 저장해야 할 때가 있다.

문제는 이걸 commit으로 하기에는 굉장히 애매하다는 것이다.
이럴 때 유용한게 git stash 이다.

git stash를 사용하면 우리가 작업하던 변경 파일들을 모두 다른 곳에 잠시 저장할 수 있다.


$ ls
a1.txt  b1.txt

$ git log --all --decorate --oneline --graph
* 6c0d0b8 (HEAD -> main) First Commit

$ vim b1.txt  # 파일 내용 변경

$ git status # modified:   b1.txt 출력

$ git stash # 이건 git stash save 와 같은 의미다. 
# 참고로 git stash -p 를 하면 hunk 로도 스테시할 수 있다.
Saved working directory and index state WIP on main: 6c0d0b8 First Commit

$ git stash list
stash@{0}: WIP on main: 6c0d0b8 First Commit

$ git stash apply # stash 한 내용 재 적용

$ git stash list # 그런데 재적용해도 stash 기록은 삭제 되지 않는다.
stash@{0}: WIP on main: 6c0d0b8 First Commit

$ git stash drop stash@{0}  # 이렇게 해야 지워진다.

$ # 만약 재적용과 동시에 drop 까지 할려면 pop을 쓰면 된다.
# apply + drop = pop

### 참고로 다른 브랜치에서 switch를 통해 옮기려는 브랜치에 
### 충돌이 날 파일이 있으면 switch가 안된다. 이때야 말로 정말 stash가 빛을 발휘한다.

$ vim a1.txt

$ git commit -am 'main branch change!'

$ git checkout HEAD^

$ git switch -c dev-branch
Switched to a new branch 'dev-branch'

$ git log --all --decorate --oneline --graph
* 7600c34 (main) main branch change!
* df6dd91 (HEAD -> dev-branch) second commit
* 6c0d0b8 First Commit

$ vim a1.txt

$ git status
On branch dev-branch
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:   a1.txt

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

$ git switch main
error: Your local changes to the following files would be overwritten by checkout:
        a1.txt
Please commit your changes or stash them before you switch branches.
Aborting

$ git stash

$ git switch main # 스위칭이 된다!

참고:

  • git stash -u 를 하면 untracked 파일도 stash 해준다.
  • git stash -a 를 하면 untracked + ignore 파일도 stash 해준다.
  • git stash -p 를 하면 hunk 단위로 stash가 가능하다.
  • git stash clear 로 모든 stash 정보를 지운다.




📌 git commit --amend

마지막 커밋 수정

git commit --amend -m '수정할 메세지 작성~'


## tip. 파일 수정 + 커밋 메세지는 변경 X 시에는 아래처럼.
git add changelog.md # 수정한 파일 add
git commit --amend --no-edit # 수정한 파일 aded 한 후에 기존 커밋에 덧붙임.




📌 git rebase -i {커밋 해시}

마지막 커밋뿐만 아니라 더 과거의 커밋 내역도 수정 가능

$ git log --oneline
1fc940e (HEAD -> main) FINAL COMMIT
4da18cd 이 커밋 왜함?	   # 3. 이 커밋은 정말 지우고 싶음
50ab6ab fourth commit  
250ebda third 부분 추가     # 2.third commit 으로 하나로 합치고 싶음
558f8b5 third commit
ae178a6 something...      # 1.이름을 바꾸고 싶음!
2d85548 second commit
7c3d90a FIRST COMMIT

# git rebase -i {커밋 해시 }: 여기서 커밋 해시값은 고치고 싶은 커밋의 바로 앞 커밋!

# 일단 "1. 이름을 바꾸고 싶음" 부터 해결해보자.
git rebase -i 2d85548

git rebase -i 에 의해서 아래 화면이 나온다.

위 그림을 자세히 보면 맨 위의 문단에 pick... pick... 로 표기되어 있는 게 보인다.
이거는 각 커밋에 대해서 어떻게 작업을 할 것인지 지정하는 것이다.

지금처럼 pick(= p) 로 커밋 해시 앞에 적혀 있으면,
해당 커밋은 그냥 기존 커밋이 유지되는 것이다.


하지만 우리가 지금 하려는 건 "커밋 이름 수정"이다.
이때는 위 그림처럼 pick 대신 reword(= r) 로 수정해주면 된다.
그리고 나서 :wq 로 빠져나온다.


빠져나오면 바로 커밋 메세지 작성 화면이 나온다.
이때 커밋 메세지를 수정하면 된다. 나는 BUG FIXED 라고 수정하겠다.


git log로 과거의 커밋이 수정된 걸 확인할 수 있다.

$ git log --oneline
e078397 (HEAD -> main) FINAL COMMIT
6394e3c 이 커밋 왜함?
40a91a8 fourth commit
c199501 third 부분 추가
9fcc6ad third commit
17a92bd BUG FIXED     ## 커밋 메세지 수정 성공!
2d85548 second commit
7c3d90a FIRST COMMIT

이번에는 바로 이어서 "이 커밋 왜함?" 커밋을 없애보겠다.


$ git rebase -i 40a91a8

###################################################
# rebase -i 화면이 나오면 맨 윗 문단을 아래처럼 수정

d 6394e3c 이 커밋 왜함?       # pick => d 로 수정!!
pick e078397 FINAL COMMIT
####################################################

# 참고: 만약에 충돌이 나면 해결한 후, git add .; git rebase --continue!

# "이 커밋 왜함?" 커밋이 삭제된 것을 확인
$ git log --oneline
57cef05 (HEAD -> main) FINAL COMMIT
40a91a8 fourth commit
c199501 third 부분 추가
9fcc6ad third commit
17a92bd BUG FIXED
2d85548 second commit
7c3d90a FIRST COMMIT

##### 이번에는 "third 부분 추가" 커밋을 "third commit" 에 통합시키자.

###################################################
# rebase -i 화면이 나오면 맨 윗 문단을 아래처럼 수정
pick 9fcc6ad third commit
s c199501 third 부분 추가      # pick => s 로 수정!!
pick 40a91a8 fourth commit
pick 57cef05 FINAL COMMIT
###################################################

# 위처럼 하면 바로 앞의 커밋에 합친다(squash)는 것이다.

그러면 아래와 같은 화면이 나오는데, 여기서 상단에 보면 두 개의 커밋 메세지가 보인다.
여기서 우리가 지우고자 하는 것을 삭제하고 :wq로 나가면 끝이다.
참고로 여기서 남기는 하나의 커밋 메세지는 수정도 가능하다!


- 삭제 전


- 삭제 후


마지막으로 확인하면... 정상적으로 합쳐진 것을 확인할 수 있다!

$ git log --oneline
f1933c7 (HEAD -> main) FINAL COMMIT
44009cc fourth commit
4181e05 third commit   # 합쳐졌다!
17a92bd BUG FIXED
2d85548 second commit
7c3d90a FIRST COMMIT

그런데 만약에 하나의 커밋을 둘로 나누고 싶으면 어떨까?
rebase -i 는 이것 조차도 가능하게 해준다.


## 예를 들어서 우리가 마지막 FINAL COMMIT에 외부 팀의 디자인과 javascript 를 
## 통합하는 과정이라고 해보자.

$ vim design.txt # 외부팀의 디자인 통합

$ vim javascript.txt  # 외부팀의 javascript 기능 통합

$ git add .

$ git commit -m 'INTEGRATING'

$ git log --oneline
460760c (HEAD -> main) INTEGRATING
0a67414 FINAL COMMIT
44009cc fourth commit
4181e05 third commit
17a92bd BUG FIXED
2d85548 second commit
7c3d90a FIRST COMMIT

# 그런데 막상하니 design 과 javascript 는 하나의 커밋에 두기 애매하다고 판단된다.
# 그래서 두 개의 커밋으로 나눌 것이다.
# 'INTEGRATING - DESIGN', 'INTEGRATING - JS' 로 나눌 예정이다.

$ git rebase -i 0a67414

###################################################
# rebase -i 화면이 나오면 맨 윗 문단을 아래처럼 수정

e 460760c INTEGRATING   # pick ==> e 로 수정
###################################################

# 여기서 git bash 맨 끝에보면 rebase 중임을 알 수 있다.
# devToroko@DESKTOP MINGW64 /c/study/rebase_i (main|REBASE 1/1) 
$ git reset HEAD^  # 한 단계 전으로 돌아가서...

$ git status
interactive rebase in progress; onto 0a67414
Last command done (1 command done):
   edit 460760c INTEGRATING
No commands remaining.
You are currently editing a commit while rebasing branch 'main' on '0a67414'.
  (use "git commit --amend" to amend the current commit)
  (use "git rebase --continue" once you are satisfied with your changes)

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


##### 여기서부터 커밋을 나누면 된다! #####
$ git add design.txt

$ git commit -m 'INTEGRATING - DESIGN'

$ git add javascript.txt

$ git commit -m 'INTEGRATING - JS'

$ git rebase --continue

$ git log --oneline  # 나뉜 것을 확인!
d26a17d (HEAD -> main) INTEGRATING - JS
59c464b INTEGRATING - DESIGN
0a67414 FINAL COMMIT
44009cc fourth commit
4181e05 third commit
17a92bd BUG FIXED
2d85548 second commit
7c3d90a FIRST COMMIT






👏 HEAD 브랜치 사용하기

HEAD 는 브랜치이다. 그리고 브랜치는 무언가를 가리키는 "포인터"다.
HEAD 는 다른 브랜치를 가리키는 포인터다.

이 말이 이해가 안되면 Pro Git에서 제공하는 개념을 조금 숙지하고 오자.

개념만 알았으면 응용하는 법을 알아보자.


일단 아래처럼 git commit history를 완성시켰다.
세세한 커밋의 내용물은 중요하지 않다.


# 현재 HEAD 포인터는 main 브랜치에 붙어 있다.
# HEAD 를 이동시키면서 파일 상태를 스스로 확인해보자.

$ git switch dev-01 # HEAD 가 dev-01 브랜치로 이동
$ ls -al

$ git checkout HEAD^ 
# HEAD~, HEAD~1 모두 가능, HEAD가 현재 가리키는 커밋의 바로 이전 커밋을 가리키게 된다.
# 위와 같이 입력하면 결과적으로 HEAD는 "dev-01 branch First commit" 가리키게 된다.

$ git log --oneline
58645ab (HEAD) dev-01 branch First commit
f7b4a46 Second commit
c440e26 First Commit

$ git checkout f7b4a46   # 참고로 이렇게 HEAD 기준이 아닌 커밋 해시로 이동도 가능!
Previous HEAD position was 58645ab dev-01 branch First commit
HEAD is now at f7b4a46 Second commit

$ git log --oneline
f7b4a46 (HEAD) Second commit
c440e26 First Commit

# 그런데 이상태에서 새로운 커밋을 생성하려면?
# 간단하다! 새브랜치를 만들고 새로운 커밋을 만들면 그만이다.

$ git branch hotfix 

$ git switch hotfix 

# git branch hotfix  +  git switch hotfix =  git switch -c hotfix

$ vim headTest.txt

$ git commit -am 'hot fix # 001 done'

결과





👏 Tag & Release

📌 git tag

위처럼 HEAD를 옮기고 나서 특정 커밋에 태그도 달 수 있다.
태그와 관련된 명령어는 아래와 같다.

$ git tag elastic-search/v1.0.0  # 태그 생성
$ git tag -d elastic-search/v1.0.0 # 태그 삭제
$ git tag -a elastic-search/v1.0.0 # 상세한 태그 생성
$ git tag elastic-search/v1.0.2 -m '태그 메세지'
$ git tag # 이미 있는 태그 조회
$ git show elastic-search/v1.0.0
$ git tag elastic-search/v1.0.0-milestone {커밋 해시} -m '태그 메세지'
$ git checkout elastic-search/v1.0.0 # 특정 태그로 HEAD 옮기기


### 원격에 태그 업데이트
$ git push origin elastic-search/v1.0.0 # 태그명을 준다
$ git push --delete origin elastic-search/v1.0.0 # 원격 태그 지우기
$ git push --tags # 로컬에 모든 태그 원격에 업데이트

git push origin elastic-search/v1.0.0를 실행한 후의 github의 Tags 상태




📌 github release

릴리즈(release)는 원격저장소의 태그가 달린 특정 커밋의 작업물을
간단하게 다운로드 받을 수 있도록 해준다.

Visual Studio Code 의 release에 가보면 클릭만하면 소스가 다운로드 되는 것을 확인할 수 있다.

우리만의 릴리즈를 한번 만들어보자.


git push origin {tag 명} 실행 후에, github 가면 위 그림처럼
tag 버튼 앞에 숫자가 달라진다. 이 버튼을 클릭해준다.


위 그림처럼 릴리즈로 내놓고 싶은 태그명 옆의 Create release 버튼 클릭


위처럼 태그 제목, 내용을 작성해준다.
추가적인 바이너리 파일는 아래 드래그 & 드롭해준다.
그리고 This is a pre-release 를 체크하면 아직 제품화 단계까지는 아님을 알려준다.
테스트니까 그냥 체크하지 않고 , Publish release 버튼을 클릭한다.


결과






👏 Git 설정

알아두면 유용한 git 설정들을 몇가지 알아두자.

git config  --list # 현재 git 설정들 목록조회, --global 옵션으로 글로벌 설정 목록조회

git config  -e  # git 설정을 에디터로 편집, --global 옵션을 줘서 글로벌 설정 편집

git config --global core.autocrlf true # 윈도우면 "true" , Mac 이면 input 
# 개행 문자 차이 때문에 이런 설정을 해줘야 한다.

git config pull.rebase {false/true}
# git pull 명령어 기본 동작 merge/rebase 설정

git config --global push.default current
# push 시 로컬과 동일한 브랜치 명으로 원격에 push 하도록 설정
# ex) main 브랜치에서 push 하면 기본으로 origin/main 으로 push!

git config --global alias.(단축키) "명령어"
# 새로운 커스텀 명령어 생성, 가장 대표적인 것이 로그 이쁘게 출력하는 명령어다.
# "git log 예쁘게" 라고 검색하면 많이 나온다.

git config --global --unset alias.(단축키)
# 커스텀 명령어 삭제





👏 Untracked 없애기

📌 git clean

git untracked 파일을 제거

$ git status
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        some-directory/
        something01
        something02
        something03
        something04
        something05

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

$ git clean  # i ,n , f 중 옵션 하나를 무조건 줘야 한다.
fatal: clean.requireForce defaults to true and neither -i, -n, nor -f given; refusing to clean

$ git clean -f
Removing something01
Removing something02
Removing something03
Removing something04
Removing something05

$ git status
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        some-directory/

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

$ git clean -df            # 폴더도 지우고 싶다면 -df
Removing some-directory/

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


$ # git clean -dn 을 하면 삭제될 것들이 뭔지 쭉 다 볼 수 있다.





👏 복구 방법

reset, revert 말고도 restore에 대해 알아보자.
그리고 reset 한 것을 되돌리는 것도 알아보자.


📌 git restore

$ cat > restore_test.txt << EOF
> i am gonna change
> EOF

$ git add .; git commit -m 'some commit'

$ echo "more change" >> restore_test.txt;\
git commit -am 'some more commit'

$ echo "more more change" >> restore_test.txt; \
git commit -am 'some more more commit'

$ echo "final change" >> restore_test.txt; git commit -am 'final commit'

$ git log -4 --oneline
0a9df42 (HEAD -> main) final commit
f90a648 some more more commit
5046f4e some more commit
ea22ebd some commit

$ cat restore_test.txt
i am gonna change
more change
more more change
final change

# 여기서  커밋해시 => f90a648 로 복구 restore_test.txt 를 복구 시켜보겠다.

$ git restore --source=f90a648 restore_test.txt

$ cat restore_test.txt
i am gonna change
more change
more more change
# 기존에 있었던 "final change" 라는 문구가 사라졌다.

$ git status # 하지만 특정 커밋의 상태의 내용으로 되돌려놓았을 뿐,
# 커밋자체 바뀌는 건 아니다. 그러니 다시 add + commit을 해서 최종 반영을 해야한다.
# ...로그 일부 생략...
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:   restore_test.txt



### 이번에는 stage 에 있는 것을 working directory 로 다시 되돌려 놔보자.

$ git reset --hard # 테스트를 위해 원복

$ echo 'blabla~~ blabla~~' >> restore_test.txt # 고의적으로 변경

$ git add . # staging!

$ git status -s
M  restore_test.txt

# 이 staging 을 취소하겠다!
$ git restore --staged restore_test.txt

$ git status -s
 M restore_test.txt


### 더 나아가서 working directory 의 변경된 내용도 기존으로 복구 시키고 싶다면?
$ git restore restore_test.txt

$ git status -s

# 끝



📌 git reset 되돌리기

정말 말도 안되지만 실수로 git reset --hard 로 맨 처음 커밋으로 되돌려 버리는 실수를
범했을 때 어떻게 해야할까? 이때는 reflog 와 reset을 같이 쓰면 해결할 수 있다.


$ git log --all --oneline --decorate --graph
# ... 중간 커밋들 모두 생략 ...
* 5fbf5d2 Update kakao.txt
* 74668fb naver.txt changed
* b5d3d86 First Commit  ## 여기로 reset 해보자.

$ git reset --hard b5d3d86

$ git log --oneline
b5d3d86 (HEAD -> main) First Commit

$ git reflog
b5d3d86 (HEAD -> main) HEAD@{0}: reset: moving to b5d3d86
0a9df42 HEAD@{1}: reset: moving to HEAD
0a9df42 HEAD@{2}: commit: final commit
f90a648 HEAD@{3}: commit: some more more commit
5046f4e HEAD@{4}: commit: some more commit
ea22ebd HEAD@{5}: commit: some commit
# ... 나머지 로그는 생략 ...

## reflog의 내용 중 제일 위에 것을 보면 우리가 마지막에 했던 reset 이 보인다.
## 이때 봐야 될 것은 그 reset을 하기 이전의 커밋 해시이다.
## 여기에서는 0a9df42 이다. 이 시점으로 되돌리면 된다!
## 되돌리는 방법은 아래와 같다.

$ git reset --hard 0a9df42

$ git log --all --oneline --decorate --graph
# 모든게 되돌아 온 것을 확인할 수 있다.





🍀 참고

profile
백엔드를 계속 배우고 있는 개발자입니다 😊

0개의 댓글