WTC - Git

김진성·2024년 10월 29일

WTC

목록 보기
2/6

기본적인 Git 명령어를 숙지한다

Git은 개발자 간의 협업을 위한 가장 기본적인 프로그램으로, 컴퓨터 파일의 변경 사항을 추적하고 여러 사용자 간의 해당 파일에 대한 작업을 조정한다. 지금은 add, commit, push 등의 간단한 명령어만 배워도 충분하지만, Git에 대해 미리 알아두어도 나쁠 것은 없다.


Git으로 관리할 자원을 고려한다

Java 코드만 있으면 .class 파일을 생성할 수 있다. 따라서 Git을 통해 .class 파일을 관리할 필요가 없다. IntelliJ IDEA의 .idea 폴더와 Eclipse의 .metadata 폴더도 IDE에서 자동으로 생성하는 폴더이므로 Git으로 관리할 필요가 없다. Git에 코드를 추가할 때는 Git을 통해 형상 관리해야 하는 코드인지 고려하는 것이 좋다. 또한 .gitignore에 대해서도 알아본다.

.gitignore는 Git에서 특정 파일이나 디렉토리를 버전 관리에서 제외하기 위해 사용하는 설정 파일입니다. 프로젝트에서 생성된 임시 파일, 빌드 결과물, 환경 설정 파일 등 Git으로 관리할 필요가 없는 파일을 .gitignore에 추가하면 Git이 해당 파일들을 추적하지 않습니다. .gitignore 파일은 프로젝트의 루트 디렉토리에 위치하며, 텍스트 형식으로 작성됩니다.

1. .gitignore 기본 작성법

  • 특정 파일 무시하기 : 파일명 형태로 작성하여 특정 파일을 무시합니다.
    secret.txt 
  • 특정 디렉토리 무시하기 : 디렉토리명/ 형태로 작성하여 특정 폴더를 무시할 수 있습니다.
    temp/
  • 와일드카드 사용 : *와 같은 와일드카드를 이용하여 특정 패턴에 맞는 파일을 무시할 수 있습니다.
    *.log       // 모든 .log 파일 무시
    *.temp      // 모든 .temp 파일 무시
  • 하위 디렉토리 파일 무시 : 하위 폴더에서도 특정 파일을 무시하려면 **/파일명 형식으로 작성합니다.
    **/*.bak    // 모든 디렉토리 내 .bak 파일 무시
  • 예외 설정하기 : !를 사용하여 .gitignore에 이미 포함된 규칙 중에서 예외적으로 추적할 파일을 지정할 수 있습니다.
    *.log       // 모든 .log 파일 무시
    !important.log  // important.log 파일만 추적

2. .gitignore 파일 작성 시 주의할 점

  • .gitignore를 프로젝트 시작 시 작성하는 것이 좋습니다. 이미 Git에서 추적 중인 파일은 .gitignore에 추가해도 무시되지 않기 때문에, 추적을 중단하려면 해당 파일을 Git에서 제거해야 합니다.
  • .gitignore에 추가할 규칙은 팀원들과 협의하여 작성하는 것이 좋습니다.

3. IntelliJ, Gradle을 사용하는 경우

  1. 컴파일된 파일 무시 (.class, .jar, .war 등)

    .class 파일이나 build/ 폴더에 생성되는 컴파일된 결과물은 버전 관리에 포함하지 않는 것이 좋습니다. 이는 Gradle 빌드 시 자동으로 생성되며, 소스 코드만 있으면 언제든 재생성할 수 있기 때문입니다.

  2. IDE 설정 파일 무시

    IntelliJ에서는 .idea/ 폴더와 *.iml 파일, Eclipse는 .metadata/ 폴더 등은 IDE 환경에 맞게 자동으로 생성되므로, 팀 전체가 일관된 설정을 원하지 않는다면 Git에서 제외하는 것이 좋습니다.

  3. 사용자 환경에 의존적인 파일 무시 (.env, config 파일 등)

    환경 설정 파일(.env 등)은 민감한 정보(API 키, 비밀번호 등)나 사용자 고유의 설정을 포함할 수 있으므로, Git으로 관리하지 않고 .gitignore에 포함하는 것이 좋습니다.

  4. 빌드 도구 관련 파일 무시 (Gradle 캐시 등)

    Gradle 프로젝트에서는 /.gradle/ 디렉토리 내의 캐시 파일을 무시하도록 설정합니다. 이는 빌드 속도 향상에 필요한 파일을 포함하지만, 다른 환경에서는 자동으로 생성되므로 Git 관리에서 제외해도 됩니다.

4. 일반적인 .gitignore 예시

보통 많은 프로젝트에서 공통으로 사용하는 .gitignore 규칙은 다음과 같습니다:

// IntelliJ 관련 파일
.idea/
*.iml

// Gradle 관련 파일
.gradle/
build/

// Java 컴파일 파일
*.class

// 환경 변수 파일
.env

// 로그 파일
*.log

// 기타 OS별 파일
.DS_Store   // macOS
Thumbs.db   // Windows

커밋 메시지를 의미 있게 작성한다

해당 커밋에서 수행된 작업을 이해할 수 있도록 커밋 메시지를 작성한다. 또한 팀의 좋은 코드 리뷰 문화는 작은 커밋을 작성하는 것에서 비롯된다.

git 커밋 메시지를 잘 쓰려고 노력해야 하는 이유

왜 커밋 메시지를 잘 쓰기 위해 노력해야 할까요? 이유는 서로 다를 수 있지만, 잘 쓰인 커밋 메시지가 더 유익하다는 사실에는 많은 프로그래머들이 동의할 것으로 여겨집니다. 그 중 대표적인 3가지를 꼽아보면 다음과 같습니다.

  1. 더 좋은 커밋 로그 가독성
  2. 더 나은 협업과 리뷰 프로세스
  3. 더 쉬운 코드 유지보수

좋은 git 커밋 메시지를 작성하기 위한 7가지 약속

이하 약속은 커밋 메시지를 English로 작성하는 경우에 최적화되어 있습니다. 한글 커밋 메시지를 작성하는 경우에는 더 유연하게 적용하셔도 좋을 것 같네요.

  1. 제목과 본문을 한 줄 띄워 분리하기
  2. 제목은 영문 기준 50자 이내로
  3. 제목 첫글자를 대문자로
  4. 제목 끝에 . 금지
  5. 제목은 명령조
  6. 본문은 영문 기준 72자마다 줄 바꾸기
  7. 본문은 어떻게보다 무엇을에 맞춰 작성하기

1. 제목과 본문을 한 줄 띄워 분리하기

시작부터 가장 핵심적인 꿀팁 한 가지를 알려드리겠습니다. 결론부터 말씀드리자면 제목과 본문 사이에 공백 한 줄을 추가하는 것은 생각보다 상당히 효과적인 테크닉입니다. 우선 git commit의 man 페이지를 참조하면 다음과 같은 문장이 나옵니다.

Though not required, it's a good idea to begin the commit message with a single short (less than 50 character) line summarizing the change, followed by a blank line and then a more thorough description. The text up to the first blank line in a commit message is treated as the commit title, and that title is used throughout Git. For example, git-format-patch(1) turns a commit into email, and it uses the title on the Subject line and the rest of the commit in the body.

요약하자면 커밋 메시지는 50자 이내의 요약문장과 빈 줄 하나, 그리고 설명문으로 구성하면 좋다는 내용입니다.

이 내용이 강제사항은 아니며 모든 커밋 메시지를 제목과 본문으로 구성할 필요는 없습니다. 때에 따라서는 아래의 예와 같이 변경사항을 한 줄로 요약한 커밋 메시지가 더 효율적입니다.

아 직전 커밋 중 `README.md`에 들어간 오타 한글자 고침 아 (부끄)
쌍따옴표 한 개 빼먹었다..--; 수정

하지만 충분히 내용이 있고 잘 갖춰진 커밋 메시지를 작성할 땐 git의 제안을 따라보세요.

제목과 본문 사이에 한 줄 공백을 두면, 어떤 마법이 일어나는지 보여드리겠습니다. 다음과 같이 제안을 따른 커밋 메시지가 있습니다. 제목, 공백 한 줄, 본문으로 구성되어 있습니다.

Derezz the master control program

MCP turned out to be evil and had become intent on world domination.
This commit throws Tron's disc into MCP (causing its deresolution)
and turns it back into a chess game.

git log는 이 커밋 메시지를 다음과 같이 보여줍니다.

$ git log
commit 42e769bdf4894310333942ffc5a15151222a87be
Author: Kevin Flynn <kevin@flynnsarcade.com>
Date:   Fri Jan 01 00:00:00 1982 -0200

 Derezz the master control program

 MCP turned out to be evil and had become intent on world domination.
 This commit throws Tron's disc into MCP (causing its deresolution)
 and turns it back into a chess game.

별다를게 없네요. 여기서 git log --oneline 옵션을 사용해 봅니다.

$ git log --oneline
42e769 Derezz the master control program

짠! 제목만 보여줍니다. 한 줄 공백으로 분리하지 않았다면 어떻게 보였을까요?

$ git log --oneline
42e769 Derezz the master control program MCP turned out to be evil and had become intent on world domination. This commit throws Tron's disc into MCP (causing its deresolution) and turns it back into a chess game.

더 뚱뚱한 본문을 가진 커밋 메시지였더라면 어떻게 보였을지 예상이 되시죠?

git shortlog라는 명령어도 있습니다. 이 명령어를 사용해서 로그를 볼 때, 제목과 본문이 한 줄 공백으로 나뉘어진 커밋들은 이렇게 보여집니다.

$ git shortlog
Kevin Flynn (1):
    Derezz the master control program

Alan Bradley (1):
    Introduce security program "Tron"

Ed Dillinger (3):
    Rename chess program to "MCP"
    Modify chess program
    Upgrade chess program

Walter Gibbs (1):
    Introduce protoype chess program

2. 제목은 영문 기준 50자 이내로

'50자 이내'라는 규칙은 지키기 그리 어려운 제약사항이 아닙니다. 이 작은 강제로 커밋 메시지 작성자는 더 읽기 좋은 커밋 제목을 만들 수 있고, 가장 간결히 요약된 제목을 작성할 수 있습니다. 또는 그렇게 만들 방법을 고민하는 습관을 들이는데 도움을 주죠.

Git은 50자를 넘으면 경고를, 72자를 넘는 모든 제목 줄은 줄임표로 잘라낸다. 50자를 목표로 하되, 72자가 제한이라고 생각하자.

3. 제목 첫글자를 대문자로

이건 거의 영문법의 문제네요. 첫글자는 대문자, 아시죠? 영어를 모국어로 사용하는 사람들이 의외로 이 문법에 민감하다고 합니다.

  • accelerate to 88 miles per hour (X)
  • Accelerate to 88 miles per hour (O)

4. 제목 끝에 . 금지

  • Open the pod bay doors. (X)
  • Open the pod bay doors (O)

5. 제목은 명령조

아마도 이 글에서 제일 낯선, 이전에는 모르셨을 법한, 그리고 가장 중요한 내용이 아닐까 생각합니다. 제목을 작성하거나 한 줄 메시지로 커밋을 할 때, 즉 커밋 메시지 가장 첫 문장의 영문법은 명령조로 합시다. 이는 첫 단어를 동사원형으로 쓰라는 의미입니다. 처음 보신 분은 이게 왜 그래야 할까 싶으실 겁니다. 사실 우리는 우리말로 커밋 메시지를 읽을 때, 누군가에게 안 일어난 일을 하도록 "지시"하는 명령문보다는 일어난 일을 "보고"하는 설명문을 더 편안히 받아들입니다. 어쨌든 커밋 메시지는 과거의 기록이니까요. 예를 들면 이런 메시지를 봤다고 가정해 보겠습니다.

예를 들면 이런 메시지를 봤다고 가정해 보겠습니다.

인증 메소드 고쳐라

* CABVerification.java: 15번째 줄 인증 메소드의 인자를 현 정책에 맞게 고쳤다.
* JSONFormat.java: 이 커밋은 이 파일에 적절한 로깅 메소드를 추가한다.
* MainView.java: 약간의 리팩토링.

이 메시지 제목을 이렇게 고치고 싶으실 겁니다.

인증 메소드 고쳤다

우리말로 제목을 요약하는 경우라면 때로는 '문장'보다 '구문'을 더 선호할 때도 있습니다.

인증 메소드 수정

그럼 왜 영문 커밋 메시지의 제목을 명령문으로 써야 하는지 알려드리겠습니다.

위에서 말씀드린 것처럼 평소에는 명령문이 사람이 읽기에 다소 무례하거나 로그 메시지라는 상황에 어울리지 않는 것처럼 느껴질 수 있습니다. 외국인도 우리와 똑같이 생각하고 있습니다. 그런데 명령문은 git 커밋의 제목으로는 아주 딱입니다. 그 한가지 좋은 이유는 git 스스로가 자동 커밋을 작성할 때 명령문을 사용하고 있습니다. 예를 들어, 기억을 되돌아보면 git merge를 실행했을 때 커밋 메시지 기본값이 이렇습니다.

Merge branch 'myfeature'

git revert 명령어를 실행하면 어떤 메시지가 기본값으로 들어갈까요?

Revert "Add the thing with the stuff"

This reverts commit cc87791524aedd593cff5a74532befe7ab69ce9d.

Github에서 풀 리퀘스트의 "Merge" 버튼을 클릭하면 자동으로 채워지는 메시지가 어땠나요?

Merge pull request #123 from someuser/somebranch

위에서 말씀드린 것처럼 평소에는 명령문이 이 사람이 읽기에 다소 무례하거나 로그 메시지라는 상황에 어울리지 않는 것처럼 느껴질 수 있습니다. 외국인도 우리와 똑같이 생각하고 있습니다. 그런데 명령문은 git 커밋의 제목으로는 아주 딱입니다. 그 한가지 좋은 이유는 git 스스로가 자동 커밋을 작성할 때 명령문을 사용하고 있습니다. 예를 들어, 기억을 되돌아 보면 깃 머지를 실행했을 때 커밋 메시지 기본값이 이렇다.한줄 커밋 메시지의 이야기이고요. 커밋 메시지의 본문을 명령조로 시작할 필요는 없다. 자연스럽게 과거형이나 현재형 시제를 사용하여 변경점을 서술하면 된다.

이처럼 커밋 메시지를 명령문으로 작성한다는 것은, git의 빌트-인 컨벤션(Built-in Convention)을 그대로 따른다는 것을 의미합니다. 때문에 커밋 제목을 명령문으로 작성하면, 자동 메시지로 채워진 커밋 사이에 자연스레 녹아듭니다. 앞선 항목에서 설명드린 git shortlog와 같은 타 명령어에도 연계되어 잘 어울리죠.

물론 이것은 제목이나 한 줄 커밋 메시지의 이야기이고요, 커밋 메시지의 본문을 명령조로 시작할 필요는 없습니다. 자연스럽게 과거형이나 현재형 시제를 사용하여 변경점을 서술하면 됩니다.

혼란스러운 점을 말끔히 해소할 수 있는 아주 간단한 팁을 한가지 알려드리겠습니다. 제목을 작성할 때 어떤 문법으로 작성해야 할지 잘 모르시겠다면, 다음 문장 뒤에 제목을 배치해서 문법이 잘 어울리나 확인해 보세요.

  • If applied, this commit will {제목}

예문을 보여드리겠습니다.

  • (If applied, this commit will) Refactor subsystem X for readability
    • 이 커밋이 적용되면 가독성을 위해 하위 시스템 X가 리팩토링 됩니다.
  • (If applied, this commit will) Update getting started documentation
    • 이 커밋이 적용되면 시작 설명서가 업데이트 됩니다.
  • (If applied, this commit will) Remove deprecated methods
    • 이 커밋이 적용되면 더 이상 사용되지 않는 메서드가 제거됩니다.
  • (If applied, this commit will) Release version 1.0.0
    • 이 커밋이 적용되면 버전 1.0.0이 릴리스 됩니다.
  • (If applied, this commit will) Merge pull request #123 from user/branch
    • 이 커밋이 적용되면 사용자/브랜치의 풀 요청 #123이 병합 됩니다.

안어울리는 문장을 쓰면 어떻게 되는지 볼까요?

  • (If applied, this commit will) Fixed bug with Y
    • 이 커밋이 적용되면 Y의 버그가 수정 됩니다.
  • (If applied, this commit will) Changing behavior of X
    • 이 커밋이 적용되면 X의 동작이 변경 됩니다.
  • (If applied, this commit will) More fixes for broken stuff
    • 이 커밋이 적용되면 손상된 부분에 대한 수정이 더 많아 집니다.
  • (If applied, this commit will) Sweet new API methods
    • 이 커밋이 적용되면 새로운 API 메서드가 추가 됩니다.

이 규칙은 오로지 "제목"에만 적용되고, "본문"을 작성할 때는 평서문으로 작성한다는 것을 다시 한 번 기억해 주세요.

6. 본문은 영문 기준 72자마다 줄 바꾸기

git은 자동으로 커밋 메시지 줄바꿈을 하지 않습니다. git log에 스타일을 지정해서 자동 줄바꿈이 적용된 로그 출력을 만들 수는 있지만요. 단순한 git log 명령어 입력만으로도 보기 좋은 메시지를 만들고자 한다면 적당한 위치에서 엔터키를 눌러 줄을 바꿔주세요. 그 적당한 위치로 72자를 추천드립니다.

꿀팁: git log에 스타일을 지정하는 방법을 모르셨다면, git이 설치된 시스템의 커맨드라인에 다음 명령어를 입력해서 사용해 보세요. 커스텀 스타일 옵션이 적용된 git log를 실행시켜주는 "alias"를 만들어주는 설정입니다. 한 번 설정한 이후 로그 확인은 git log 대신 git lg을 간단하게 입력하시면 됩니다. 자동 줄바꿈이 적용됨은 물론 Magically! 예뻐진 git 로그를 감상해 보세요. 제가 직접 편집해서 사용하고 있는 명령어입니다. 물론 기존의 git log도 원래대로 동작합니다. 이 설정을 ~/.gitconfig 파일에 작성해두면 매번 입력할 필요없이 다음 git 실행부터 항상 적용됩니다.

$ git config --global alias.lg "log --graph --abbrev-commit --decorate --date=relative --format=format:'%C(bold red)%h%C(reset) : %C(bold green)(%ar)%C(reset) - %C(cyan)<%an>%C(reset)%C(bold yellow)%d%C(reset)%n%n%w(90,1,2)%C(white)%B%C(reset)%n'"

[그림1] git lg 실행 결과

[그림2] git lg --oneline 실행 결과

7. 본문은 어떻게보다 무엇을에 맞춰 작성하기

제목이 내용을 충분히 전달하는 것 같습니다. 좋은 사례로 Bitcoin Core 프로젝트에 실제로 사용된 커밋 메시지를 보여드리겠습니다.

commit eb0b56b19017ab5c16c745e6da39c53126924ed6
Author: Pieter Wuille <pieter.wuille@gmail.com>
Date:   Fri Aug 1 22:57:55 2014 +0200

   Simplify serialize.h's exception handling

   Remove the 'state' and 'exceptmask' from serialize.h's stream
   implementations, as well as related methods.

   As exceptmask always included 'failbit', and setstate was always
   called with bits = failbit, all it did was immediately raise an
   exception. Get rid of those variables, and replace the setstate
   with direct exception throwing (which also removes some dead
   code).

   As a result, good() is never reached after a failure (there are
   only 2 calls, one of which is in tests), and can just be replaced
   by !eof().

   fail(), clear(n) and exceptions() are just never called. Delete
   them.

PLUS TIP! 커밋 메시지로 Github 이슈(issue)를 자동 종료시키기

만약 커밋 메시지를 영문으로 작성하신다면 좋은 팁 하나 더 소개해 드릴게요. Github에는 커밋 메시지에 특정한 단어를 사용해 자동으로 이슈를 종료시키는 편리한 기능이 탑재되어 있습니다. 이 예약어는 커밋 메시지 안의 어느 위치에서나 사용 가능합니다. 이제 커밋 메시지로 Github 이슈를 닫아보세요! 방법은 간단합니다.

키워드 #이슈번호

Github가 이슈 종료로 인식하는 키워드는 다음과 같습니다.

  • close
  • closes
  • closed
  • fix
  • fixes
  • fixed
  • resolve
  • resolves
  • resolved

각 키워드마다 기능에 차이는 전혀 없고요, 문법이나 맥락에 맞게 사용하면 됩니다. 다만 close 계열은 일반 개발 이슈, fix 계열은 버그 픽스나 핫 픽스 이슈, resolve 계열은 문의나 요청 사항에 대응한 이슈에 사용하면 적당하다는 관례는 있습니다. 그럼 예문을 보여드릴게요.

# 제목에 이슈 한 개 닫기를 적용한 사례
Close #31 - refactoring wrap-up

* This is wrap-up of refactoring main code.
* main.c
  * removed old comments
  * fixed rest indentations
  * method extraction at line no. 35

# 본문에 이슈 여러 개 닫기를 적용한 사례
Update policy 16/04/02

* This closes #128 - cab policy, closes #129 - new hostname, and fixes #78 - bug on logging.
* cablist.txt: changed ACL due to policy update delivered via email on 16/04/02, @mr.parkyou
* hostname.properties: cab hostname is updated
* BeautifulDeveloper.java: logging problem on line no. 78 is fixed. The `if` statement is never happening. This deletes the `if` block.

이렇게 커밋을 작성하여 푸시하면, 푸시한 브랜치에 따라 이슈가 자동으로 닫힙니다. 예를 들어 Github 저장소의 default branch를 master로 설정해뒀을 경우, master 브랜치에 커밋 후 푸시하면 즉시 해당 번호의 이슈가 닫힙니다. develop 브랜치에 푸시했다면, 이슈는 닫히지 않고 있다가 나중에 develop -> master Merge가 되었을 때 알아서 닫힙니다.


커밋 메시지에 이슈 또는 풀 리퀘스트 번호를 포함하지 않는다

일부 프로젝트에서는 작업을 이슈 또는 풀 리퀘스트와 연결하기 위해 커밋 메시지에 이슈 또는 풀 리퀘스트 번호를 포함하기도 한다. 그러나 이 접근 방식은 원본 저장소의 관련 없는 이슈 또는 풀 리퀘스트에 영향을 미칠 수 있다. 따라서 커밋 메시지에 이슈 또는 풀 리퀘스트 번호를 포함하지 않는다.


풀 리퀘스트를 만든 후에는 닫지 말고 추가 커밋을 한다

이미 풀 리퀘스트를 생성하면 변경을 위해 새 풀 리퀘스트를 만들 필요가 없다. 변경이 필요한 경우 추가 커밋을 하면 자동으로 반영된다.

profile
minimalist

0개의 댓글