Git의 강점과 특징
저번 포스팅까지 Git과 Github의 사용방법에 대해서 작성해보았다.
이번에는 Git의 특별한 강점과 추가적인 깊은 내용을 포스팅 하려고 한다.
1. Git의 강점
첫번째로 Git은 VCS 중에서도 특별한 강점 두가지가 있는데, 스냅샷과 분산 버전 관리 시스템이이다.


첫 번째 강점인 스냅샷에 대해 설명하기 앞서 델타 버전을 언급해야 하는데
위 사진을 보면 델타 버전은 파일을 하나씩 별도로 보면서 차이점만 따로 관리하고 있다.
반면 스냅샷은 프로젝트 전체를 스냅샷 형태로 관리하되, 변경되지 않은 파일은
따로 저장하는 것이 아닌, 기존에 저장되어 있던 파일에 링크만 걸어준다.
따라서 특정 버전의 파일을 불러올 때, 스냅샷 방식의 속도가 훨씬 빠르게 불러올 수 있다.
예를 들어 Version3 파일을 불러오고 싶을 때 델타 방식은 version2의 filc A, Version1 의 file B
그리고 version 3의 file C 를 각각 계산한 뒤 통합해야 하는 반면에 스냅샷 방식은
Version3 에 있는 A, B, C 의 파일을 모두 불러와서 사용할 수 있어서 속도가 훨씬 빠르다.
두 번째 강점은 중앙 집중식 버전 관리가 아닌, 분산 버전 관리 시스템이라는 점이다.
예를 들어 CBS 나 Subversion 과 같은 VCS 는 원격 서버의 Git 의 Github과 같은 곳에
모든 관리 내역이 저장되고, local에서는 다운로드 받은 파일들로만 작업할 수 있다.
반면에 Git은 원격 서버의 파일을 local환경에 받아서 인터넷 연결 관계 없이 사용하다가
원하는 시점에 Push 와 Pull로 동기화하면서 협업할 수 있기 때문에 원격저장소의 의존성이 낮다.

그 밖에도 다양한 특징들이 있는데 그중 Git의 공간, HEAD, fetch와 pull에 대해 알아보려고 한다.
2. Git의 세가지 공간
Git에서는 파일의 상태를 Work directory, Staging area, Repository 세가지로 분류한다.
먼저 Working directory 는 사용자가 프로젝트의 실제 파일을 생성하고 수정하는 작업 공간으로
Git이 아직 추적하지 않는 untracked 파일과, 추적하고 있는 tracked 파일이 존재한다.
Untracked 케이스는 두가지가 있는데, gitignore에 추가되어 아예 무시된 파일이거나
새로 생성된 파일이라서 git 이 add 를 진행한 적 없는 파일인 경우도 untracked 에 속한다.
Staging area 는 work directory 에서 변경된 파일 중 commit 에 포함할 파일을 선별하여 등록하는
중간 단계의 공간으로서, git add 명령어를 통해 staging 영역에 파일을 추가할 수 있다.
마지막으로 Reposity 는 프로젝트의 모든 변경 이력을 담고 있는 저장소를 의미한다.
staging area 로 이동된 파일들을 git commit 혹은 git push 명령어를 통해 repository로 옮긴다.
Repository에는 local repository 와 remote repository 가 존재하는데 먼저 local repository 는
.git파일에 위치하며, 사용자의 컴퓨터에 저장된 변경 이력과 메타데이터를 뜻한다.
반면에 remote repository 는 git commit + push명령어로 반영하는데
Github나 Gitlab 과 같은 원격 서버에 위치하며 여러 개발자가 공유하고 협업하는 데 사용된다.

위 개념과 관련하여 Git의 다른 기능이 있는데 Git rm 과 Git mv 명령어가 있다.
실무에선 쓰이지 않는 경우도 많지만, 해당 기능이 어떤 역할을 하는지 간단하게 언급하려고 한다.
먼저 work directory에서 파일을 삭제한 상태로 git add . 까지 진행한 상태가 git rm 이다.
즉 git rm "file name" 을 입력하면 work directory에서 파일을 삭제하고 staging area까지 옮긴다.

두번째로 work directory에 있는 파일 이름을 변경한 상태로 git status를 진행하면
기존에 있는 파일은 삭제되고 변경한 이름의 파일이 추가된 것으로 인식되는데,
여기서 git add . 를 진행한 뒤에 git status를 확인하면 그제야 파일 이름이 변경된 것으로 인식한다.

그렇다면 git mv "before file name" "after file name" 을 진행하면 파일명을 변경한 뒤
staging area까지 한번에 등록되는 상태로 변경되어 commit 을 진행할 수 있다.

추가적으로 staging area에 있는 파일을 다시 work directory로 다시 돌릴 수도 있다.
예를 들어 3개의 파일을 수정한 뒤 git add . 명령어를 입력하면 staging area에 들어가는데
여기서 git restore --staged "file name" 을 입력하면 스테이지 상태에 올린 파일을
work directory 공간으로 다시 옮기겠다는 의미로, commit을 분리하고자 할 때 주로 사용된다.

반대로 work directory 파일을 staging area 상태로 되돌리고 싶다면 git restore "file name" 을 사용한다.

하단 포스팅에서 언급했던 git reset 에는 --soft와 --mixed, --hard 3가지 옵션이 있는데
위 세가지 옵션에 따라 work directory와 staging area, repository 공간에 따른 동작 차이가 있다.
Git 을 활용하여 과거 데이터 가져오기.
저번 포스팅에 이어 Git 을 사용하여 변경사항을 추적하고 버전으로 기록하는 과정을 다뤄보려고 한다.이를 통해서 변경 내역을 버전으로 남기고, 잘못된 커밋을 되돌리는 방법까지 알아볼 수
rusharp.tistory.com
git reset 에 옵션을 사용하지 않으면 default는 --mixed 로
변경된 내용을 working directory 에는 남겨두지만 staging area 에서는 제거한다.
두번째로 --soft는 repository에서만 제거하고 staging area 에서는 남겨두고,
--hard는 work direcctory, staging area, repository 까지 수정사항을 완전히 삭제한다.

이와 관련해서 좋은 정리 데이터가 있어서 가져와봤는데, 여기서 HEAD 라는 개념이 있다.
그렇다면 Git HEAD 가 무엇을 의미하는지, checkout 이 무엇인지 알아보고자 한다.

3. HEAD
먼저 Git HEAD 는 쉽게 이야기하자면 "특정 브랜치의 가장 최신 commit" 을 의미한다.
예를 들어 git branch의 상태가 아래와 같다고 봤을 때, 보라색으로 표시된 곳이 HEAD 이다.
git switch 로 branch를 옮기게 되었을 때, 해당 branch의 HAED 위치로 이동하게 된다.

여기에서 히스토리 내역은 변경하지 않고 파일 상태만 바꾸고 싶을 때 git checkout을 사용한다.
git checkout 는 HEAD 를 기준으로 위치를 이동하거나 커밋 해시를 사용하는 방법이 있다.
HAED 를 사용하는 명령어는 git checkout HEAD "^ or ~" 으로, ^ 또는 ~ 개수만큼 이전으로 이동한다.
예를 들어 HEAD 기준으로 5번째 이전의 commit 으로 이동하고 싶다면 git commit HEAD^^^^^
혹은 git checkout HEAD~5 혹은 git checkout HEAD~~~~~ 을 사용해주면 되다.
반대로 최신의 상태로 옮기고 싶을 땐 git checkout - 를 사용해서 이전에 위치했던 HEAD로 옮겨간다.
주의할 점은 git checkout HEAD~2 이후에 git checkout - 를 입력하면 한칸이전으로 돌아가는 것이 아닌,
이전에 위치했던 곳인 2칸 앞으로 넘어가기 때문에, 만약 특정 위치로 다시 옮겨가고 싶은 거라면
git checkout "hash name" 을 사용해서 원하는 곳으로 HAED 위치를 변경할 수 있다.

그리고 여기서 HEAD 란 브랜치의 가장 최신의 commit 이라고 이야기했는데,
HEAD가 동작하는 방식은 익명의 branch를 하나 만들어서 HEAD 위치를 지정한다.
즉, 익명의 브랜치를 만든것과 동일한 상태로, 다른 branch로 이동하기 위해서는 git switch를 한다.
그렇기 때문에 여기서 branch를 분리하면 아래와 같이 새로운 브랜치로 생성할 수 있다.

또한 git HEAD를 활용하여 리셋도 진행할 수 있는데, 현재 branch에서 2step뒤로 reset 하고 싶다면
git log 를 찾아볼 필요 없이 git reset --hard HAED~2 를 입력하면 된다.

4. Git Fetch 와 Pull의 차이
git fetch는 원격 저장소의 최신 커밋을 local로 가져오는 역할을 한다.
정확하게는 remote 환경에서 수정사항이 있는 상태일 때 fetch를 진행하면
내 main head 는 유지되고 가상의 branch가 추가되어 변화를 받아온다.
반대로 pull은 fetch의 과정을 포함한 상태로, 원격에 변경사항이 없는 상태에서
pull을 진행하면 그냥 아무것도 받아오지 않고, 새로운 커밋이 있으면 fetch를 진행한 뒤
어떤 옵션을 줬느냐에 따라서 merge나 rebase를 하는 것이다.
그렇다면 local에 remote 의 변경사항을 적용하기 전에 미리 확인할 수 있는데,
git fetch 이후 git checkout origin/main 을 하면 local에서 remote의 변경사항을 미리 볼 수 있다.
여기서 주의사항이 있는데, git switch origin/main은 진행해도 아래처럼 에러가 발생할 수 있다.
왜냐하면 origin/main은 원격 브랜치라서 로컬 브랜치처럼 switch를 사용할 수 없다.
다만 다시 main branch로 이동할 때에는 git switch main 을 활용해서 main branch로 올 수 있다.

추가로 만약 remote 환경에서 branch가 생성되었을 때는 어떻게 확인할 수 있을까?
먼저 git fetch로 변경사항을 불러온 뒤 git branch -a를 진행하면 new_branch가 있다.
이후 git checkout origin/new_branch 를 진행하면 해당 branch의 변경내용을 확인할 수 있다.

여기서 main branch로 돌아온 뒤 git switch -t -origin/new_branch를 입력하면,
local의 new_branch를 생성한 뒤 origin/new_branch를 trace 하고 commit 을 주고받을 수 있다.
