[Git] Git 노트

Published: by Creative Commons Licence

트리 해시 Tree Hash

트래 해시는 특정 커밋 시점에서의 프로젝트 디렉터리 구조(트리)를 나타내는 해시값이다. 커밋된 파일들과 디렉터리 구조를 바탕으로 계산한 해시값이며, 파일의 내용이나 파일 시스템의 구조가 바뀌면 같이 변경된다.

커밋 해시와 비교했을 때, 특정 커밋의 전체 상태를 나타내는 커밋 해시와 다르게, 트리 해시는 해당 커밋에서의 파일과 디렉터리 구조만을 나타낸다. 그래서 작성자나 날짜가 변경되더라도 트리 해시는 영향을 받지 않는다. 그리고 커밋 해시는 Git 저장소에서 고유한 값을 갖지만, 트리 해시는 파일의 내용과 구조가 동일하다면 여러 커밋에서 같은 값이 존재할 수 있다.

detected dubious ownership in repository at …

fatal: detected dubious ownership in repository at '//wsl.localhost/Ubuntu/home/fixalot/repo/bun-testbed'
To add an exception for this directory, call:

  git config --global --add safe.directory '%(prefix)///wsl.localhost/Ubuntu/home/fixalot/repo/bun-testbed'

WSL에 있는 로컬 저장소를 호스트의 git으로 접근하려 할 때 이런 메시지와 오류가 발생한다. git이 알려주는대로 safe.directory 설정을 추가하면 바로 해결된다.

Git Credential Helper

git-credential-manager-core.exe: No such file or directory

만약 C:/Program Files/Git/mingw64/libexec/git-core/git-credential-manager-core.exe: No such file or directory 라는 메시지의 오류가 발생한다면, 원인은 git 설정 중 credential.helper의 이름이 잘못됐을 가능성이 크다.

우선 아래처럼 설정을 지워보고:

git config --global --unset-all credential.helper

해결이 안되면 credential.helper를 직접 지정해보자:

git config --global credential.helper "C:\Program Files\Git\mingw64\libexec\git-core\git-credential-wincred.exe"

참고로 credential helper를 확인하려면 아래처럼 입력한다:

git config -l | grep credential
# 출력 -> credential.helper=/mnt/c/Program\ Files/Git/mingw64/libexec/git-core/git-credential-manager-core.exe

git config --get-all --show-origin credential.helper
# 출력 -> file:/home/fixalot/.gitconfig   /mnt/c/Program\ Files/Git/mingw64/libexec/git-core/git-credential-manager-core.exe

WSL 환경의 git credential.helper

WSL에서는 credential.helper를 지정하지 않으면, 매번 비밀번호(혹은 토큰)를 입력해야 한다. 아래처럼 직접 지정해 줄 것:

# WSL에서
git config --global credential.helper "/mnt/c/Program\\ Files/Git/mingw64/libexec/git-core/git-credential-wincred.exe"

자격 증명 관리자 Credential Manager

윈도우에선 보통 git을 설치할 때 'Git Credential Manager for Windows'를 같이 설치하는데, 요걸로 권한이 필요한 저장소에 접속할 때 필요한 자격증명을 관리한다.

문제는 다른 계정으로 바꾸는 방법을 모르겠다는 건데… 임시 방편으로 윈도우 설정 메뉴인 '자격 증명 관리자'를 열어서 지워버리거나 직접 수정하는 방법이 있다.

GitHub CLI

공식 도움말: https://cli.github.com/manual/

# 설치
winget install --id GitHub.cli

path에 자동으로 추가됨. 터미널 재실행 후:

gh auth login

이후 로그인 진행한 뒤, 원하는 저장소로 이동해서 필요한 명령 실행하면 된다.

# job 목록 보기
gh run list --limit 5

# 상세 보기
gh run view 1590799398 --verbose

# job 재실행
gh run rerun 1591056086

# job 취소
gh run cancel 1591056086

… have diverged

공개된 브랜치와 같은 이름의 브랜치를 강제 푸시 등으로 업로드하면, 강제 푸시 전에 해당 브랜치를 로컬로 내려받은 사람은 리모트 브랜치와 로컬 브랜치의 히스토리가 갈라진 상태가 됨. Git에선 이걸 diverged라고 부른다.

revert는 작업 브랜치에서

master 브랜치에 feature 브랜치를 머지하는 상황이라 가정했을 때, 머지한 내용을 되돌리고 싶으면 master 브랜치를 리버트 하는게 아니라 feature 브랜치를 리버트하고 그 다음 다시 머지하는게 낫다. master를 리버트한 뒤에 feature에서 같은 라인을 수정 후 다시 머지하려고 하면 충돌이 발생하거나 변경 사항이 없다며 머지가 아예 안될 수도 있기 때문.

사실 master를 리버트 했다 해도, 리버트 후의 master에서 새로 만든 브랜치에 feature를 체리픽 하거나, revert 커밋을 feature로 역머지 후 작업하는 등 방법은 얼마든지 있긴하지만… 작업 브랜치가 아닌 메인 혹은 중간 브랜치에서 리버트를 자제한다 정도의 규칙이라고 생각하는게 맞겠다.

만약 feature 브랜치의 커밋이 잘게 쪼개져있어서 모두 리버트하기 번거로운 상황이라면 feature를 스쿼시 리베이스 후 리버트하면 된다.

좋은 git commit 메시지를 위한 영어 사전

https://blog.ull.im/engineering/2019/03/10/logs-on-git.html

대소문자 문제

특정 브랜치가 대소문자로 분리되어 중복 생성되는 경우가 발생할 수 있다. 이 경우 설정을 다음처럼:

git config core.ignorecase false

대소문자를 무시하지 말라고 해주면 됨.

가령 A가 대소문자를 무시한 상태로 커밋을 올렸더니 feature 대신 Feature 브랜치가 생겼다고 하자. 대소문자를 무시하지 않는 설정을 하고 있던 B는 pull 후에 Feature 브랜치를 발견한다.

이 현상을 해소하기 위해 A는 대소문자를 무시하지 않도록 설정을 변경하고 다시 push한다. B는 .git\refs\remotes\origin\Feature 디렉터리를 .git\refs\remotes\origin\feature로 강제변경한 뒤 git fetch --prune 명령을 실행해서 Feature 브랜치가 삭제되도록 한다.

오토 패킹이 자주 발동하면

https://stackoverflow.com/questions/8633981/what-does-auto-packing-the-repository-for-optimum-performance-mean/16233094

dangling object가 많으면 이런 현상이 있다고 함. git fsck로 확인했을 때, dangling commit이 너무 많다고 판단되면 git gc --prune=now로 날리는 방법이 있다고 함.

혹은 아예:

git config gc.auto 0

설정에서 Garbage Collection 자동실행 주기를 0으로 만들어버리는 방법도 있다.

충돌이 발생하지 않는 조건

  • 머지해오는 브랜치가 현재 브랜치의 자손일 때(Fast-forward 머지일 때)
  • 다른 파일을 변경했을 때
  • 같은 파일을 서로 다른 브랜치에서 변경했지만 변경 내용이 완전히 같을 때

Git Bash for Windows의 시작 위치 변경

바로 가기의 속성에서 시작 위치를 원하는 곳(예: C:\dev\git)으로 변경하면 되나, 속성에서 대상으로 지정된 실행파일의 뒤에 --cd-to-home 옵션을 지우지 않으면 효과가 없다.

config의 user

user.nameuser.email은 작성자와 커미터로 사용되긴 하지만, 저장소 접근 권한과 연관된 것은 아니다. 만약 pull/push 등의 명령이 거부되었다면 문제는 user 설정이 아니므로 다른곳을 찾아봐야 함. 예를 들어 'Git Credential Manager for Windows(윈도우용 깃 자격 증명 관리자)'를 사용한다면 같은 저장소에 여러 권한을 설정할 수가 없는데, SSH 설정 등으로 해결할 수 있다 하나 귀찮으므로 깃허브의 콜라보레이터 추가로 해결해부렀다.

참고로 자격 증명 관리자는 Atom, git bash, fork 등에서 사용하며, eclipse는 자체 관리하기 때문에 윈도우의 자격 증명 관리자를 사용하지 않는다.

author와 committer의 차이

  • author: 코드를 실제로 작성한 자(커밋을 처음 만든 사람)
  • committer: 실제 작성자를 대신하여 코드 작성자로 간주되는 자(커밋을 마지막으로 리베이스한 사람)

패치(patch)를 적용한다고 했을 때 패치로 가져오는 변경 사항을 실제 작성한 사람이 author, 패치를 브렌치에 적용한 사람은 committer다.

이 정보는 리베이스 등으로 커밋 아이디가 변경됐을 때 아래처럼 입력해 알 수 있다:

git log -1 --pretty=fuller

git으로 파일내용이나 커밋로그 검색하기

https://blog.outsider.ne.kr/849

merge, rebase, cherry-pick의 차이

http://dogfeet.github.io/articles/2012/git-delta.html#-merge-rebase-cherry-pick-

기본 에디터 변경하기

git config --global core.editor "'C:/Program Files/Sublime Text 3/sublime_text.exe' -w"

pull 명령에 자동 rebase 설정

git config --global pull.rebase true

help

git help config
git config --help

되돌리기

modified 되돌리기: git checkout -- . staged 되돌리기: git reset HEAD

checkout: 특정 브랜치나 태그, 체크섬으로 이동

git checkout master # master 브렌치로 이동
git checkout -- . # 현재 경로의 모든 파일 되돌리기
git checkout HEAD . # 현재 경로의 모든 파일 되돌리기

reset

git reset HEAD # 모든 파일의 스테이징 취소
git reset HEAD~1 # 이전 커밋으로 되돌림

revert

reset과 차이점은 HEAD가 원래의 커밋을 가리키는게 아니라 새로운 커밋을 생성하여 변경한 내용을 기록한다.

git revert HEAD # 이전 커밋으로 되돌림

rebase

merge의 경우 두 브랜치의 최종결과만을 기준으로 병합한다면 rebase는 브랜치의 변경 사항을 순서대로 다른 브랜치에 적용하며 병합한다. 저장소의 커밋 로그와 이력을 한 줄로 정리해주기 때문에 보통 완료된 브랜치를 마스터에 병합할 때 사용한다.

cherry-pick

커밋하나만 rebase

fetch

fetch는 데이터를 모두 가져오지만 머지는 생략한다. (pull = fetch + merge)

git fetch # origin 저장소에서 받아온다.

patch

컴퓨터 프로그램에 추가하여 오류를 제거하거나 오류를 제거하기위한 소프트웨어

fast-forward merge

현재 브랜치가 머지할 브랜치의 부모 커밋일 경우 발생. (어느 한 쪽이 다른 한 쪽으로 단순히 '앞으로 가기'만 하기 때문에 이런 이름이 붙음)

3-way merge

머지할 브랜치 둘과 공통 부모 커밋 셋의 커밋을 기준으로 머지하는 것

branch

특정 커밋(의 최신버전)을 가리키는 포인터

HEAD

현재 작업중인 브랜치를 가리키는 포인터. checkout 명령은 이 포인터를 변경하는 것이다.

remote의 HEAD

원격 저장소의 default branch를 의미한다.