[Git] 12장 고급기능

참조

refs

  • 깃은 커밋으로 코드 이력을 관리합니다.
  • 커밋은 고유의 SHA1 해시 값을 가지고 있으며, 이 해시값은 여러 기능에서 참조합니다.
  • 깃에서는 참조하는 해시 값을 refs 목록 으로 가지고 있습니다.

해시

  • 깃에서 해시 값은 매우 중요합니다.
  • 깃은 SHA1 알고리즘을 사용하여 해시 값을 생성합니다.
  • 해시 값은 깃의 동작을 구분하며, 중복되지 않는 유일한 값입니다.
  • 깃의 모든 작업은 SHA1 해시 값을 참조합니다.
  • 깃 내부적으로 동작하는 작업들은 SHA1 해시 값으로 연결 고리를 생성합니다.
  • 따라서 깃의 동작을 정확히 이해하려면 해사 값을 자세히 알아볼 필요가 있습니다.
  • 생성된 모든 해시 값은 show 명령어로 확인할 수 있습니다.
$ git show 해시값

역조회

  • show 명령어는 해시 값을 사용하여 커밋 정보를 확인합니다.
  • 반대로 rev-parse 명령어로 포인터의 해시 값을 알 수 있습니다.
  • 예를 들어 브랜치는 커밋 해시 값을 가리키는 포인터입니다.
  • 따라서 브랜치 이름을 사용하여 참조하는 해시 값을 조회할 수 있습니다.
  • master 브랜치의 해시값을 확인해 봅시다.
$ git rev-parse master

참조 목록

  • 깃은 SHA1 해시 값을 생성하고, 커밋은 생성된 해시 값을 간접적으로 사용합니다.
  • 또 깃에서는 생성된 해시 값을 쉽게 참조할 수 있도록 refs 목록을 생성합니다.
  • 깃의 모든 refs 목록은 저장소의 숨긴 영역인 .git/refs 폴더 안에 저장됩니다.
  • 또 복잡한 SHA1 해시 값을 쉽게 찾아 사용할 수 있도록 별칭도 쓸 수 있습니다.
  • 별칭은 .git/refs 폴더 안에서 생성 및 관리할 수 있습니다.
  • 즉, refs 정보는 깃의 기능들을 구현하는 내부 메커니즘입니다.
  • 처음 저장소를 생성하면 .git/refs 폴더에는 heads 와 tags 폴더만 있습니다.
  • 새로운 브랜치를 만들 때마다 해시 값을 가지는 refs 파일들을 생성합니다.
$ ls .git/refs -all ---------저장소 refs 파일 목록

reflog

  • 깃은 안정적인 작업을 유지하려고 참조된 모든 refs 를 기록합니다.
  • 그리고 내부적으로 작업한 모든 HEAD 와 브랜치 포인터를 기록합니다.
  • 이때 사용된 포인터들의 기록을 reflog 라고 합니다.

참조 기록

  • reflog 기록은 reflog 명령어를 사용하여 확인할 수 있습니다.
  • reflog 는 한 번이라도 사용했던 HEAD 와 브랜치를 기록합니다.
  • 하지만 모든 기록을 영구적으로 가지고 있지는 않습니다.
  • reflog 는 시스템에서 정의한 며칠 간의 기록만 보관합니다.
  • 그 이전의 작업들은 모두 삭제 합니다.

$ git reflog

기록 확인

  • reflog 의 기록들은 HEAD@{숫자} 형태입니다.
  • 각 숫자는 작업을 수행한 해시 값을 가리킵니다.
  • 따라서 reflog에 기록된 HEAD@{숫자} 포인터를 이용하여 커밋 정보를 확인할 수 있습니다.
  • 마지막에 참조한 refs 로그의 해시 값 정보를 확인해 봅시다.

기간 확인

  • 커밋의 로그 기록이 많은 경우 필터링할 수 있습니다.
  • 필터링은 특정한 날짜, 시간 등 기준을 적용합니다.
  • 다음 예처럼 어제 작업한 내역말 출력할 수 있습니다.
  • 깃에서 참조하는 refs의 로그는 reflog 명령어 또는 git log -g 옵션을 사용하여 확인할 수 있습니다.
$ git show master@{yesterday}

기록 유지

  • refs는 현재 로컬 저장소에서 작업한 모든 로그의 참조 기록입니다.
  • 깃은 커밋, 브랜치 등 내부적으로 사용한 모든 객체의 로그를 기록합니다.
  • 하지만 refs는 로컬 저장소에만 기록하므로 원격 저장소나 복제, 복사 등으로는 refs 기록을 옮길 수 없습니다.

파일 애너테이션

  • 개발하다 보면 코드를 잘못 작성해서 오류가 발생하곤 합니다.
  • 하지만 여러 개발자와 협업하다 보면 잘못된 코드를 찾는 것이 쉽지 않습니다.

blame

  • 잘못된 코드가 어디서부터 시작되었는지 찾기 어렵습니다.
  • 잘못된 내용을 찾으려면 모든 커밋이력을 살펴보아야 하는데, 생각보다 시간이 오래 걸립니다.
  • 깃은 이러한 코드를 쉽게 찾을 수 있게 파일의 수정 이력을 분석합니다.
  • 그리고 blame 기능은 커밋의 메타 정보를 코드 라인별로 같이 결합하여 출력합니다.
  • 코드를 수정한 사람이 누구인지, 언제 수정한지를 쉽게 판별할 수 있으며, 메타 정보를 바당으로 문제를 좀 더 쉽게 파악할 수 있습니다.

blame 기능 실습

1. master 브랜치에서 index.html 내용 수정 후 커밋

  • master 브랜치로 checkout 합니다.
  • 그리고 index.html 내용을 수정 후, 커밋합니다.

2. blame 명령어로 확인

  • blame 명령어는 개별 파일에서만 동작하며, 명령어 인자 값으로 개별 파일을 전달합니다.
$ git blame 파일이름

옵션 활용

  • 소스 코드의 용량이 클 때는 이력 정보도 많이 출력됩니다.
  • 이때는 -L 옵션을 사용하여 파일의 특정 영역만 지정할 수 있습니다.
$ git blame -L 시작줄, 마지막줄 파일이름

replace

  • 기본적으로 한 번 생성된 객체는 변경할 수 없습니다.
  • 커밋을 변경하려면 깃의 원리를 응용해야 합니다.
  • 커밋이 참조하는 해시 값을 속여서 다른 커밋으로 변경하는 것입니다.
  • replace는 기존 커밋을 다른 커밋인 것처럼 변경하는 기능입니다.
  • 깃 저장소에서 오랫돈안 작업하면 수많은 커밋 기록이 쌓입니다.
  • 커밋이 많아지면 저장소 크기도 증가합니다.
  • 저장소를 분리할 수 있는 서브모듈이 있지만, 오래된 저장소와는 다릅니다.
  • 이때 replace 를 사용하면 커밋이 많은 오래된 저장소를 분리할 수 있습니다.

가비지 콜렉트(garbage collect)

  • 깃은 저장소를 효율적으로 유지 관리하려고 가비지 콜렉트(garbage collect)를 지원합니다.
  • 또 깃은 가비지를 효과적으로 관리할 수 있는 별도의 명령어를 제공합니다.

가비지

  • 가비지(garbage) 는 동적인 언어에서 메모리를 관리하려고 생성한 개념입니다.
  • 깃은 이력을 추적할 때 객체의 생성과 변경을 반복합니다.
  • 여러 번 처리 동작을 반복하면서 시간이 지남에 따라 연결 고리가 없는 고립된 객체들이 생겨 비효율적인 자원 상태가 됩니다.
  • 고립된 객체들은 대표적으로 리셋(reset) 또는 리베이스(rebase) 등을 자주 할 때 발생합니다.
  • 연결 고리가 없는 객체들은 불필요하며, 용량만 차지하므로 정리해주는 작업이 필요합니다.

가비지 생기는 이유

  • 깃의 커밋은 생성된 객체의 연결 고리를 설정하는 동작입니다.
  • 커밋을 변경하면 새로운 객체로 연결 고리를 재설정합니다.
  • 하지만 기존에 연결된 객체들은 삭제하지 않고 유지합니다.
  • 이러한 과정에서 불필요한 가비지가 생성됩니다.
  • 이처럼 객체는 향후 수동으로 커밋을 복구할 때 사용할 수 있기 때문에 유지합니다.

압축 관리

  • 깃의 내부 원리는 SHA1 해시와 객체의 응용입니다.
  • 파일 변경, 트리 구조, 커밋 등 대부분의 내부 작업은 객체를 생성하고 연결하는 동작들입니다.
  • 커밋들이 실행될 때 이러한 내부 동작으로 연결 고리가 없는 객체가 수없이 생성됩니다.
  • 깃 내부에 이러한 객체가 많아지면 저장소 용량도 커지고, 객체도 빠르게 관리하기 어렵습니다.
  • 깃은 이러한 객체를 줄이려고 생성된 객체를 압축합니다.
  • 즉, 깃은 연결 고리가 없는 객체들을 pack 파일 형태로 압축하여 저장합니다.

실행

  • 깃은 가비지를 정리하려고 별도의 gc 명령어를 제공합니다.
  • gc 는 저수준 명령어로 garbage collect 의 약어입니다.
  • 깃에서 내부적으로 가비지 정리가 필요하다고 생각할 때, gc 명령어를 자동으로 실행합니다.
  • gc 명령어가 실행되면 오래된 객체들은 삭제하고 저장소 용량도 정리합니다.
  • 자동 실행 외에 사용자가 직접 gc 명령어를 실행할 수도 있습니다.
  • gc 명령어를 실행할 때는 prune, repack, pack, rerere 등 하위 명령어와 같이 사용합니다.
  • gc 명령어가 실행되면 객체를 압축하고 pack 파일 형태로 저장하거나 제거합니다.
$ git gc -auto

압축

  • gc 명령어는 객체의 압축과 refs 를 같이 처리합니다.
  • refs 를 같이 압축하면 압축하기 전의 파일들은 삭제됩니다.
  • 그리고 새로운 .git/packed-refs 파일을 생성합니다.
  • 이후 refs 가 추가로 변경되면 압축한 packed-refs 값을 수정하지 않고, 새로운 refs 파일을 생성합니다.
  • 즉, 압축한 이후에는 refs 파일이 2개가 되며, refs 파일이 여러 개 있으면 기본적으로 refs 안에 있는 파일을 먼저 찾습니다.
  • 그리고 이후에 압축된 packed-refs 내용을 검색합니다.

환경 설정

  • gc 명령어의 동작은 환경 설정으로 제어할 수 있습니다.
  • gc 명령어의 동작은 gc.auto 항목으로 자동 설정을 허용하지 않을 수도 있고, gc.autopacklimit 를 사용하여 최대 압축 숫자를 제어할 수도 있습니다.
gc.reflogExpire: reflog가 보존되는 기간을 설정합니다. 기본값은 90일입니다.
gc.reflogExpireUnreachable: 기본값은 30일입니다.
gc.aggressiveWindow: 창의 크기를 정합니다. 기본값은 250입니다.
gc.aggressiveDepth: 압축에 사용되는 매개변수이며, 기본값은 50입니다.
gc.pruneExpire: 저장소에 쓰는 다른 프로세스와 동시에 실행될 때 손상을 방지합니다.
gc.worktreePruneExpire: 유예 기간을 설정할 수 있습니다.

prune

  • 깃의 가비지를 정리하는 gc 명령어의 몇 가지 명령어와 함께 사용합니다.
  • 그중 prune 명령어는 고립된 객체를 정리하는 내부 유틸리티입니다.

고립된 객체

  • 깃은 객체를 이용하여 이력을 추적하고 변경된 내역을 기록합니다.
  • 새로운 변경과 커밋이 있을 때 기존 객체를 다른 객체로 연결합니다.
  • 즉, 앞에서도 이야기 했듯이 객체들은 SHA1 해시 값을 사용하여 서로 연결되어 있습니다.
  • 어떤 객체를 새로운 커밋으로 재설정하면 참조 링크가 해제되는데, 이것은 해당 객체가 더 이상 필요하지 않다는 것입니다.
  • 즉, 불필요한 객체가 생기는 것입니다.
  • 고립된 객체는 객체 간 연결 고리가 끊겨 명령어를 사용해도 해당 객체에 접근할 수 없는 객체를 의미합니다.
  • 고립된 객체는 불필요하므로 정리해야 합니다.
  • 깃 자체적으로 완벽하게 정리하기 어려우므로 수동 으로 정리해야 합니다.

객체 정리

  • 리셋으로 제거된 객체는 삭제되지 않고 고립된 객체가 됩니다.

  • 리셋으로 객체까지 완벽하게 삭제하려면 prune 명령어를 사용합니다.

  • prune 명령어를 실행할 때는 다음 두 옵션을 같이 사용합니다.

    • -dry-run : 실행하지 않고 작업할 내역만 출력합니다.
    • -verbos : 작업한 결과를 출력합니다.
  • prune 명령어는 실행 후의 작업 결과를 출력합니다.

  • 하지만 어떤 메시지도 출력하지 않았습니다.

  • 이처럼 작업 메시지가 없는 것은 아직 깃 내부에 고립된 객체가 없어 prune 동작 자체가 실행되지 않은 것입니다.

  • 리셋은 단지 커밋의 참고 연결 고리만 해제하는 것입니다.

  • 사실 깃은 생성된 객체를 쉽게 삭제하는 것을 허용하지 않습니다.

  • 정확하게 말하자면 참고 연결 고리만 해제한 객체와 실제 고립된 객체는 서로 다릅니다.

원격 작업

  • 보통 prune 명령어는 로컬 환경에서 실행합니다.
  • 하지만 고립된 객체는 원격 저장소에도 있을수 있습니다.
  • remote/fetch 명령어를 같이 조합하면 원격 저장소에도 prune 명령어를 실행할 수 있습니다.
  • 원격 저장소의 브랜치를 병합한 후 삭제합니다.
  • fetch -prune 명령어는 오래된 브랜치를 정리합니다.
  • 원격 저장소에 연결한 후 제거하기 전에 최신 상태를 먼저 가져옵니다.
$ git remote prune
# git fetch -prune

rerere

  • 불특정 다수의 사람과 협업하다 보면 여러 가지 문제가 발생합니다.
  • 깃은 협업 작업을 할 때 발생한 문제들을 충돌이라는 형태로 알려 줍니다.

동일한 충돌

  • 협업 중 발생하는 여러 문제 중에서 일부는 같은 유형의 문제입니다.
  • 비슷한 충돌 패턴을 반복적으로 수동으로 해결하곤 합니다.
  • 깃은 이렇게 반복되는 같은 유형의 충돌 문제를 자동으로 해결할 수 없을까 하는 의문점에서 등장한 기능입니다.
  • rerere는 reuse recorded resolution 의 약어로, 어떤 문제로 충돌이 발생할 때 이를 기록합니다.
  • rerere 기능을 활성화하면, 깃은 충돌을 해결할 때마ㅏ 해결한 문제의 유형을 기록합니다.
  • 기록한 유형의 문제와 비슷한 문제가 향후 다시 발생한다면, 미리 기록한 해결 정보를 바탕으로 자동으로 적용합니다.

활성화

  • 기본적으로 rerere 기능은 활성화되어 있지 않습니다.
  • 이 기능을 사용하려면 다음 명령어로 활성화해야 합니다.
  • 로컬의 전체 저장소에 모두 적용하고자 한다면 -global 옵션을 같이 사용합니다.
  • 단일 저장소에서만 rerere 기능을 확성화할 때는 글로벌 옵션을 제외합니다.
  • 환경 설정으로 활성화하면 저장소에서 rerere 기능을 사용할 수 있습니다.
$ git config rerere.enable true

정리

  • 일반적인 사용자는 깃의 고급 기능까지 알 필요가 없습니다.
  • 하지만 자신이 프로젝트에서 중요한 자리를 맡았거나 저장소를 운영한다면 좀 더 많은 기능을 알고 있는 것이 좋습니다.
  • 깃의 고급기능을 이용하면 협업할 때 발생하는 저장소 문제들을 복구하고 해결할 수 있습니다.
728x90

'버전관리' 카테고리의 다른 글

[맥북]맥OS M1 git 설치하기  (2) 2023.01.11
[Git] 11장 서브모듈  (0) 2022.06.25
[Git] 10장 배포 관리와 태그  (0) 2022.06.24
[Git] 9장 복귀  (0) 2022.06.20
[Git] 8장 병합과 충돌  (0) 2022.06.20

이 글을 공유하기

댓글

Designed by JB FACTORY