ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Git, GitHub 명령어 사용 꿀팁
    IT 이야기 2023. 12. 14. 10:59

    본 글은 그렙(Grepp)에서 프론트엔드 개발을 하고 있는 조민철 님의 글을 일부 재편집하였습니다.
    오늘은 Git, GitHub의 명령어 사용법을 준비하였는데요. 작업의 효율성과 편의성을 기르고 싶은 분들이라면 끝까지 읽어보시는 것을 추천드립니다.
    작성자: 조민철(Grepp. 프론트엔드 개발자)
    편집자: Tami


    팀 단위로 소프트웨어 개발을 할 때 필수적으로 사용하는 도구 중 하나가 버전 관리 시스템 Git입니다. Git은 add, commit, log, stash 등 버전 관리를 위한 다양한 명령어를 제공합니다. 혹시 자주 쓰는 Git 명령어를 조합하거나 직접 수정해서 사용하고 싶은 적 없으셨나요?

    예를 들어, 흔히 사용하는 git pull 명령어는 git fetch && git merge와 같이 두 가지 명령어를 조합한 명령어입니다.

    # 원격 저장소의 내용을 다운로드합니다.
    $ git fetch
    
    # 로컬 저장소에 내용을 병합합니다.
    $ git merge
    
    # 원격 저장소의 내용을 다운로드해서 로컬 저장소에 병합합니다.
    $ git pull

    pull 명령어 한 번이면 두 단계의 명령어를 한 번에 실행할 수 있어서 사용자 편의성이 더 높아지고 작업 흐름이 단순화됩니다. 이러한 원리를 활용하여 Git 명령어를 직접 조합해서 사용하면 작업자에 입맛에 맞게 Git을 더 능숙하게 사용할 수 있습니다.

    이 글에서는 Git의 별칭(alias) 기능을 활용하여 사용자 고유의 명령어를 만드는 방법을 알아봅니다. git pull과 같이 여러 명령어를 한 명령어로 조합하는 것뿐만 아니라 하나의 명령어에서 여러 옵션을 간소화하여 사용하는 방법도 함께 살펴보겠습니다.

    참고:

    • 본 글에서는 Git을 CLI(Command Line Interface) 환경에서 사용하는 방법을 다룹니다.
    • 본 글에서 만드는 별칭 명령어가 수행하는 기능은 Visual Studio Code 같은 에디터나 GitKraken 같은 유명한 GUI(Graphical User Interface) 프로그램에서 이미 제공되고 있을 수 있습니다.

    준비

    연습용 저장소를 준비합니다. 본인이 작업하고 있는 저장소도 좋고요, 커밋 히스토리가 적당히 있으면 됩니다.
    저는 정적 사이트를 만들어주는 오픈 소스 프로젝트 Nextra를 가져오겠습니다.

    $ git clone https://github.com/shuding/nextra.git
    $ cd nextra

    커밋 로그 그래프를 한눈에 확인하기

    요약:

    • 언제 사용하나요? 커밋 로그 그래프를 한눈에 확인하고 싶을 때 사용합니다.
    • 어떻게 만드나요? git log 명령어에서 제공하는 옵션을 미리 지정하여 별칭을 만듭니다.

    Git에서 중요한 내용은 대부분 커밋(commit)에 기록되어 있습니다. 프로젝트에서 어떤 일이 진행되었는지, 어떤 코드가 언제 추가되었거나 삭제되었는지, 누가 무엇을 변경했는지 알고 싶다면 커밋 기록을 살펴보면 됩니다. 커밋 기록을 살펴보는 강력한 명령어가 git log인데요. git log에서는 제공하는 옵션을 활용하여 커밋 기록을 빠르게 살펴볼 수 있는 alias를 만들겠습니다.

    git log에서 제공되는 다음과 같은 옵션을 적용하면 기본적인 형태의 커밋 로그 그래프를 확인할 수 있습니다.

    $ git log --oneline --decorate --graph

    하지만 커밋이 언제 작성되었는지, 누가 작성했는지는 나타나지 않네요. 다음과 같이 커밋의 작성 날짜와 작성자를 볼 수 있다면 더 좋겠습니다.

    커밋에 대한 정보가 한눈에 더 잘 들어오지 않나요?
    위와 같은 그래프를 보려면 다음과 같이 명령어를 입력해야 합니다.

    $ git log \
          --color --graph --decorate \
          --date=format:'%Y-%m-%d' \
          --abbrev-commit \
          --pretty=format:'%C(red)%h%C(auto)%d %s %C(green)(%cr)%C(bold blue) %an'

    너무 길죠? 이 명령어를 간단하고 쉽게 사용할 수 있도록 별칭을 만들겠습니다.

    별칭 기능을 사용하려면 Git의 설정 파일을 수정하면 됩니다.

    # 로컬 프로젝트 범위에 적용되는 설정 파일을 변경합니다.
    $ git config --edit --local
    
    # 또는 원하는 에디터로 직접 열어서 수정해도 됩니다.
    $ vi .git/config       # vim으로 설정 파일 열기
    $ code .git/config     # vscode로 설정 파일 열기

    아래와 같이 alias 항목을 추가합니다.

    [alias]
        l = "log \
          --color --graph --decorate \
          --date=format:'%Y-%m-%d' \
          --abbrev-commit \
          --pretty=format:'%C(red)%h%C(auto)%d %s %C(magenta)(%cr)%C(bold green) %an'"

    이제 직접 만든 커스텀 명령어를 실행해보세요.

    $ git l

    위에서 봤던 커밋 해시, 브랜치, 커밋 이름, 날짜, 작성자 이름 같은 주요 항목들이 모두 출력되고 각 항목을 색상별로 확인할 수 있습니다.

    참고:

    여러 개의 커밋을 하나로 합치기

    요약:

    • 언제 사용하나요? 여러 커밋을 하나로 합치고 싶을 때 사용합니다.
    • 어떻게 만드나요? log, reset, commit 명령어를 조합해서 별칭을 만듭니다.

    작업을 하다 보면 비슷한 작업을 했던 여러 커밋을 하나로 합치고 싶을 때가 있습니다. 명령어 한 번으로 여러 개의 커밋을 하나로 합치는 방법을 알아보겠습니다.

    아래 커밋 그래프를 확인해주세요. 각 커밋을 보면 작업자가 다음과 같이 코드를 변경했습니다:

    • b6c57a24: 사이드바 UI에 버그가 있어서 고치는 작업을 했습니다.
    • e3dbbf4f: 그런데 해당 버그와 관련된 수정할 부분이 추가로 발견되어 한 번 더 작업을 했습니다.
    • 46015e38: 웬걸, 해당 버그와 관련된 부분이 또 있었습니다.

    동일한 버그를 수정한 작업이라서 하나의 커밋으로 합치고 싶습니다. 어떻게 해야 할까요?
    일반적으로 git rebase -i 명령어를 사용해서 인터랙티브 모드에 진입하여 해결합니다.

    $ git rebase -i b6c57a24

    인터랙티브 모드에 진입하여 추가로 작업한 두 개의 커밋 앞에 s(=squash) 커맨드를 입력하면, 두 개의 추가 커밋이 첫번째 작업 커밋으로 합쳐집니다.

    인터랙티브 모드로도 커밋을 합칠 수도 있지만, 여러 단계를 밟지 않고 명령어 한 번으로 수행할 수 있으면 어떨까요?
    위에서 했던 것처럼 git 설정 파일을 열어서 squash라는 이름으로 alias를 추가합니다.

    [alias]
        l = "log \
          --color --graph --decorate \
          --date=format:'%Y-%m-%d' \
          --abbrev-commit \
          --pretty=format:'%C(red)%h%C(auto)%d %s %C(magenta)(%cr)%C(bold green) %an'"
    
        squash = "!f() { \
            messages=$(git log --format=%B --reverse ${1}~..HEAD); \
             git reset --soft ${1} && \
             echo \"${messages}\" | git commit --amend --allow-empty -F -; \
             unset -f f; \
        }; f"

    명령어에 대한 설명은 뒤에서 하기로 하고 사용법부터 살펴보면 다음과 같습니다.

    # git squash <합쳐질 부모 커밋>
    $ git squash b6c57a24

    직접 실행해서 보여드리겠습니다. 현재 커밋 로그는 다음과 같습니다. 커밋 메시지를 자세히 보기 위해 git log 명령어를 실행했습니다. 이제 아래와 같은 커밋 로그가 어떻게 변하는지 확인해보세요.

    위 명령어를 실행했을 때 기대하는 결과는 커밋 46015e38, e3dbbf4f, b6c57a24하나로 합쳐지는 것(squash) 입니다.
    이때 커밋에 포함된 변경사항만 합치는 것이 아니라 커밋에 기록된 커밋 메시지도 함께 보존하겠습니다.

    $ git squash b6c57a24

    3개의 커밋이 하나로 합쳐지면서 커밋에 변경사항이 생겼기 때문에 b6c57a24는 새로운 해시 d6ffced2으로 변경되었고, 기존 커밋 메시지가 보존된 채로 변경 사항이 하나로 합쳐졌습니다.

    우리가 만든 git l 명령어를 실행해보면 커밋 로그가 더 깔끔해진 것을 확인할 수 있습니다.

    그런데 코드를 더 나은 방식으로 작성할 수 있습니다. 예를 들어, git squash 뒤에 파라미터를 전달하지 않으면 다음과 같이 경고 메시지를 출력하고 싶습니다.

    $ git squash
    Please provide the commit hash of the parent commit where other commits will be squashed into.

    아래와 같이 수정하면 됩니다.

    [alias]
        l = "log \
            --color --graph --decorate \
            --date=format:'%Y-%m-%d' \
            --abbrev-commit \
            --pretty=format:'%C(red)%h%C(auto)%d %s %C(magenta)(%cr)%C(bold green) %an'"
    
        squash = "!f() { \
            if [ -z \"$1\" ]; then \
                echo 'Please provide the commit hash of the parent commit where other commits will be squashed into.'; \
            else \
                messages=$(git log --format=%B --reverse ${1}~..HEAD); \
                git reset --soft ${1} && \
                echo \"${messages}\" | git commit --amend --allow-empty -F -; \
            fi; \
            unset -f f; \
        }; f"

    이제 주석을 추가하여 위 코드가 하는 일을 간략하게 설명하겠습니다.

    [alias]
        l = "log \
            --color --graph --decorate \
            --date=format:'%Y-%m-%d' \
            --abbrev-commit \
            --pretty=format:'%C(red)%h%C(auto)%d %s %C(magenta)(%cr)%C(bold green) %an'"
    
        squash = "!f() { \
            # 인자($1)를 전달하지 않으면 경고 메시지를 출력합니다.;\n \
            if [ -z \"$1\" ]; then \
                echo 'Please provide the commit hash of the parent commit where other commits will be squashed into.'; \
            else \
                # 인자($1)로 전달한 부모 커밋부터 현재 브랜치(HEAD)의 모든 커밋 메시지를 가져옵니다.;\n \
                messages=$(git log --format=%B --reverse ${1}~..HEAD); \
                # 현재 브랜치를 인자($1)로 전달한 부모 커밋으로 되돌립니다. 이때 부모 커밋 이후의 모든 변경사항을 staging area에 추가합니다.;\n \
                git reset --soft ${1} && \
                # 가져온 커밋 메시지를 사용해서 현재 커밋에 덮어씌웁니다.;\n \
                echo \"${messages}\" | git commit --amend --allow-empty -F -; \
            fi; \
            unset -f f; \
        }; f"

    참고:

    기타

    제가 자주 사용하는 alias를 몇 가지 간략하게 공유하면서 글을 마치겠습니다.

    # 기존 명령어 간소화 또는 일부 변경
    s = status
    
    sw = switch  
    swc = switch -c
    
    ac = "!git add --all && git commit -v"  
    amend = commit -a --amend --no-edit  
    precommit = commit --allow-empty -n
    
    pushthis = push origin HEAD
    
    # 추적하지 않는 파일도 포함해서 stash 하기
    stash = stash --include-untracked
    
    # n번째 stash의 내용 확인하기
    stash-show = "!f() { git stash show -p --include-untracked \"${1:-0}\"; }; f"
    
    # 현재 가리키고 있는 커밋 해시 출력
    hash = "!git rev-parse --short HEAD"
    
    # 현재 체크아웃된 브랜치의 이름 출력
    head = !git rev-parse --abbrev-ref HEAD
    
    # 현재 브랜치의 백업 브랜치 만들기
    bak = "!git switch -c \"bak/$(git head)\""
    
    # 백업 브랜치 일괄 삭제하기
    bak-clean = "!git for-each-ref refs/heads/bak --format='%(refname)' | while read ref ; do branch=${ref#refs/heads/} ; git branch -D $branch ; done"
    
    # 동일한 이름의 로컬 브랜치를 원격 저장소와 동기화하기
    synchard = "!f() { git fetch --all && git checkout ${1:-main} && git reset --hard origin/${1:-main} && git switch - ; }; f"

    본 글에서 만든 alias에 대한 전체 코드는 링크에서 확인할 수 있습니다. 감사합니다.

    함께 읽으면 좋은 글

     

     

    댓글

Programmers