{"id":16527018,"url":"https://github.com/jaem1n207/advanced-git","last_synced_at":"2025-10-25T09:09:41.906Z","repository":{"id":217431997,"uuid":"743376655","full_name":"jaem1n207/advanced-git","owner":"jaem1n207","description":"생산성을 향상하기 위해 Git의 다양한 기능을 다루는 문서","archived":false,"fork":false,"pushed_at":"2024-01-17T16:24:08.000Z","size":1234,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-13T16:21:39.697Z","etag":null,"topics":["git"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jaem1n207.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-15T05:13:26.000Z","updated_at":"2024-01-17T06:14:20.000Z","dependencies_parsed_at":null,"dependency_job_id":"0a52386f-a40d-48f5-b1a7-5a4773eaf4b7","html_url":"https://github.com/jaem1n207/advanced-git","commit_stats":null,"previous_names":["jaem1n207/advanced-git"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaem1n207%2Fadvanced-git","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaem1n207%2Fadvanced-git/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaem1n207%2Fadvanced-git/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaem1n207%2Fadvanced-git/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jaem1n207","download_url":"https://codeload.github.com/jaem1n207/advanced-git/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241610992,"owners_count":19990507,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["git"],"created_at":"2024-10-11T17:31:58.004Z","updated_at":"2025-10-25T09:09:41.802Z","avatar_url":"https://github.com/jaem1n207.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Git 활용 잘해보기\n\n[![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fjaem1n207%2Fadvanced-git\u0026count_bg=%233182F6\u0026title_bg=%23555555\u0026icon=github.svg\u0026icon_color=%23E7E7E7\u0026title=hits\u0026edge_flat=false)](https://hits.seeyoufarm.com)\n\n**Q: 이 문서는 왜 작성되었나요?**\n\n**A:** 프로젝트를 할 때 항상 변수는 존재하기에 작업 도중 History를 수정해야 하는 상황은 자주 발생합니다. 이런 상황에서 사용할 수 있는 전략을 설명합니다. 궁극적으로는 실무에서 유용한 Git의 다양한 기능을 다뤄 보고 더 **효율적으로 작업**할 수 있도록 하기 위함입니다.\n\n**Q: 왜 \"Git History\"를 관리해야 하나요?**\n\n**A:** Git History는 프로젝트의 **성장과 진행 과정**을 나타냅니다. 그렇기에 협업하는 사람이 많아질수록 Git History를 관리하는 것은 중요해집니다.\n\n\u003e [!WARNING]\n\u003e 실무에서 자주 마주치는 상황에서 Git History를 관리하는 방법, Git을 통해 더 효율적으로 작업할 수 있도록 돕는 여러 기능을 다룹니다. 따라서 개념은 가볍게 다루고 넘어가므로 개념이나 원리에 대해 자세히 알고 싶다면 [Git 공식 문서](https://git-scm.com/doc)를 참고해 주세요.\n\n## 목차\n\n\u003c!-- AUTO-GENERATED-CONTENT:START (TOC:collapse=true\u0026collapseText=자세히 보려면 클릭하세요\u0026excludeText=목차) --\u003e\n\u003cdetails\u003e\n\u003csummary\u003e자세히 보려면 클릭하세요\u003c/summary\u003e\n\n- [Git 활용 잘해보기](#git-활용-잘해보기)\n  - [목차](#목차)\n  - [요약](#요약)\n    - [커밋을 수정하는 방법](#커밋을-수정하는-방법)\n    - [Git Stash](#git-stash)\n    - [Git Reset](#git-reset)\n    - [Git Bisect](#git-bisect)\n    - [Git Merge](#git-merge)\n    - [Git Rebase](#git-rebase)\n    - [Git Reflog](#git-reflog)\n  - [Git Stash](#git-stash-1)\n  - [Git Reset](#git-reset-1)\n    - [--soft 옵션](#--soft-옵션)\n    - [--hard 옵션](#--hard-옵션)\n    - [--mixed 옵션](#--mixed-옵션)\n    - [실무에서의 사용 예시](#실무에서의-사용-예시)\n  - [Git Bisect](#git-bisect-1)\n  - [Git Merge](#git-merge-1)\n    - [기본 사용법](#기본-사용법)\n    - [충돌 해결](#충돌-해결)\n    - [Fast-Forward 병합](#fast-forward-병합)\n    - [No-Fast-Forward 병합](#no-fast-forward-병합)\n    - [Squash 병합](#squash-병합)\n    - [실무에서의 사용 예시](#실무에서의-사용-예시-1)\n  - [Git Rebase](#git-rebase-1)\n  - [Git Squash](#git-squash)\n  - [Git Reflog](#git-reflog-1)\n  - [마지막 커밋을 수정하는 방법](#마지막-커밋을-수정하는-방법)\n  - [여러 개의 커밋 메시지를 수정하고 싶은 경우](#여러-개의-커밋-메시지를-수정하고-싶은-경우)\n  - [상황에 맞게 사용하는 Merge, Rebase, Squash](#상황에-맞게-사용하는-merge-rebase-squash)\n  - [마무리](#마무리)\n  - [참고](#참고)\n\n\u003c/details\u003e\n\u003c!-- CUSTOM-AUTO-GENERATED-CONTENT:END --\u003e\n\n## 요약\n\n\u003cdetails\u003e\n\u003csummary\u003e자세히 보려면 클릭하세요\u003c/summary\u003e\n\n### 커밋을 수정하는 방법\n\n- `git commit --amend`를 사용하여 최신 로그 메시지를 변경할 수 있습니다.\n- `git commit --amend`를 사용하여 가장 최근 커밋을 수정할 수 있습니다.\n- `git rebase`를 사용하여 커밋을 합치고 로그를 수정할 수 있습니다.\n- `git rebase -i`를 사용하면 로그 수정 시 표준 `git rebase`보다 훨씬 정밀한 제어가 가능할 수 있습니다.\n\n### Git Stash\n\n- `git stash`를 사용하여 작업을 저장하고 나중에 불러올 수 있습니다.\n- 주로 브랜치를 변경해야 하는데 아직 커밋하고 싶지 않은 경우에 유용합니다.\n- `git stash apply`로 저장한 작업을 불러올 수 있으며, 여러 번 사용한 경우 최근 stash를 선택하여 적용할 수 있습니다.\n- 저장된 작업을 새로운 브랜치에 적용하려면 `git stash branch \u003c브랜치 이름\u003e`을 사용합니다.\n- 불필요한 stash는 `git drop`이나 `git stash clear`로 제거할 수 있습니다.\n\n[Git Stash에 대해 자세히 알아보기](#git-stash-1)\n\n### Git Reset\n\n- `git reset`을 사용하여 커밋을 초기화할 수 있습니다.\n- 주로 실수로 커밋한 지저분한 코드를 수정할 때 유용합니다.\n- `git reset --soft HEAD~1`을 사용하여 최근 커밋을 초기화하면서 변경 사항을 유지할 수 있습니다.\n\n[Git Reset에 대해 자세히 알아보기](#git-reset-1)\n\n### Git Bisect\n\n`git bisect`를 사용하면 복잡한 코드베이스에서 문제가 발생한 커밋을 효과적으로 찾아낼 수 있습니다.\n\n1. 버그가 없었던 마지막 커밋을 찾습니다.\n2. 버그가 있는 현재 상태의 커밋을 확인합니다.\n3. `git bisect`를 시작하여 좋은 커밋과 나쁜 커밋을 지정합니다.\n4. 자동으로 `checkout` 된 커밋에서 테스트 스크립트를 실행하거나 수동으로 버그를 확인합니다.\n5. 테스트 결과에 따라 해당 커밋을 좋은 상태 또는 나쁜 상태로 표시합니다.\n6. 버그를 도입한 커밋을 찾으면 `git bisect reset`을 실행하여 `bisect` 세션을 종료합니다.\n\n[Git Bisect에 대해 자세히 알아보기](#git-bisect-1)\n\n### Git Merge\n\n`git merge` 명령어는 두 개의 브랜치를 하나로 합치는 데 사용됩니다. 기본적인 사용법은 다음과 같습니다:\n\n```bash\ngit checkout main\ngit merge feature\n```\n\n`git merge`는 fast-forward, no-fast-forward, squash 등 다양한 옵션을 제공하며, 실무에서는 주로 기능 개발의 완료나 배포 전략에 활용됩니다.\n\n[Git Merge에 대해 자세히 알아보기](#git-merge-1)\n\n### Git Rebase\n\n`git rebase` 명령어는 주로 커밋 로그를 다루기 위해 사용되며, 기능 브랜치를 메인 코드 베이스에 병합하기 전 로그를 정리할 수 있습니다. 기본적인 사용법은 다음과 같습니다:\n\n```bash\ngit checkout feature_branch\ngit rebase main_branch\n```\n\n충돌이 발생하면 충돌을 해결하고 계속 진행합니다. `rebase` 후에는 fast-forward 병합을 통해 선형 로그를 유지할 수 있습니다.\n\n[Git Rebase에 대해 자세히 알아보기](#git-rebase-1)\n\n### Git Reflog\n\n`git reflog` 명령어는 Git의 내부 로그로 HEAD와 브랜치의 변경 이력을 확인할 수 있습니다. 주로 **실수로 삭제한 커밋을 복구**하거나, `rebase` 등으로 인한 문제를 해결하는 데 활용됩니다. 커밋을 복구하려면 `git reset --hard HEAD@{n}` 명령어를 사용하면 됩니다. 주의: `git reflog`는 로컬 저장소에만 존재하므로 복구 후에는 강제 푸시가 필요할 수 있습니다.\n\n[Git Reflog에 대해 자세히 알아보기](#git-reflog-1)\n\n\u003c/details\u003e\n\n## Git Stash\n\n`git stash`를 실행하면 **커밋하지 않고도 작업을 저장**할 수 있습니다. 보통 아래와 같은 상황일 때 유용합니다.\n\n`feat/1` 브랜치에서 1번 기능을 작업하고 있습니다. 그러다 급하게 수정해야 할 사항이 생겨 `hotfix/1` 브랜치로 전환해야 하는 경우가 종종 있습니다. 하지만 아직 `feat/1` 브랜치에 커밋되지 않은 코드가 꽤 지저분(디버깅 코드가 포함되어 있는 등)하다면 아직 커밋하고 싶지 않을 겁니다.\n\n이때 `git stash`를 실행하면 됩니다. `stash`는 브랜치에 임시 로컬 커밋을 저장하는 것과 같다고 생각하면 됩니다. 또한 `stash`는 원격 레포로 푸시할 수 없으므로 개인적으로 용도로 실행하면 됩니다.\n\n이제 기존 브랜치의 커밋 로그를 더럽히지 않고 안전하게 브랜치를 변경할 수 있게 되었습니다. 작업을 끝내고 다시 `feat/1` 브랜치로 돌아와서 **저장해 둔 코드를 불러오고 싶다면** `git stash apply`를 실행하면 저장된 코드를 쉽게 불러올 수 있습니다.\n\n`stash`를 여러 번 실행했었고 그중 **가장 최근** stash를 적용하고 싶다면 아래처럼 하면 됩니다:\n\n```bash\ngit stash list\n\u003e stash@{0}: WIP on test/stash: 1fe8781 test: ff-only2\n\u003e stash@{1}: WIP on test/stash: 1fe8781 test: ff-only2\n\u003e stash@{2}: WIP on test/stash: 1fe8781 test: ff-only2\n\ngit stash apply stash@{0}\n```\n\nstash로 저장된 커밋을 **새로운 브랜치에 적용**하고 싶다면 아래처럼 하면 됩니다:\n\n```bash\ngit stash branch ${브랜치 이름}\n\u003e 새로 만든 ${브랜치 이름} 브랜치로 전환합니다\n\u003e 현재 브랜치 ${브랜치 이름}\n```\n\n만약 저장되어 있는 일부 stash를 **제거**하고 싶다면 `git drop`을 실행하여 stash를 개별적으로 제거하거나, `git stash clear`를 사용하여 모든 stash를 제거할 수 있습니다.\n\n## Git Reset\n\n`git reset`을 실행하면 **커밋을 초기화**하거나 변경 사항을 다시 스테이징할 수 있습니다. 보통 실수로 지저분한 코드를 커밋한 상황일 때 유용하게 사용할 수 있습니다.\n\n\u003e [!NOTE]\n\u003e 단순히 마지막 커밋 메시지를 수정하거나 다른 커밋 안에 밀어 넣는 작업을 하고 싶다면 [마지막 커밋을 수정하는 방법](#%EB%A7%88%EC%A7%80%EB%A7%89-%EC%BB%A4%EB%B0%8B%EC%9D%84-%EC%88%98%EC%A0%95%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95) 섹션을 참고해 주세요.\n\u003e 최근 커밋이 아니라 여러 개의 예전 커밋을 수정하려면 [여러 개의 커밋 메시지를 수정하고 싶은 경우](#%EC%97%AC%EB%9F%AC-%EA%B0%9C%EC%9D%98-%EC%BB%A4%EB%B0%8B-%EB%A9%94%EC%8B%9C%EC%A7%80%EB%A5%BC-%EC%88%98%EC%A0%95%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%9D%80-%EA%B2%BD%EC%9A%B0) 섹션을 참고해 주세요.\n\n실수로 지저분한 코드까지 포함되어 커밋된 상황이라고 예를 들어보겠습니다. 이때 `git reset` 명령어를 사용하면 코드가 아직 커밋되지 않은 것처럼 처리할 수 있습니다. 이렇게 하려면 `git reset --soft HEAD~1`을 실행합니다. 여기서 `HEAD~1`은 가장 최근 커밋을 나타냅니다. 커밋 해시를 기준으로 입력할 수도 있습니다.\n\n```bash\n# HEAD\ngit reset --soft HEAD~2\n# 커밋 해시\ngit reset --soft 12a34b5\n```\n\n`git reset`은 `--soft`, `--hard`, `--mixed`라는 세 가지 주요 옵션과 함께 사용될 수 있으며, 각 옵션은 Git의 세 가지 주요 영역인 작업 디렉토리(Working Directory), 스테이징 영역(Staging Area), 그리고 저장소(Repository)에 다르게 영향을 미칩니다. `git reset`의 역할을 정확히 이해하기 위해 이 옵션 간의 차이를 알아보겠습니다.\n\u003csmall\u003e 그림으로 표현할 재주가 없어 글로 설명하겠습니다 \u003c/small\u003e\n\n### --soft 옵션\n\n`git reset --soft` 옵션은 커밋 메시지를 수정하거나 추가 변경 사항을 커밋에 포함시키고 싶을 때 유용합니다.. 이 옵션을 사용하면 지정된 커밋으로 HEAD는 이동하지만, **작업 디렉토리는 영향을 받지 않고 스테이징 영역의 상태는 리셋 직전 상태로 유지**됩니다.\n\n```bash\ngit reset --soft HEAD~1\n```\n\n위 명령은 가장 최근 커밋을 취소하고, 그 커밋의 변경 사항들을 스테이징 영역에 남겨둡니다.\n\n### --hard 옵션\n\n`git reset --hard` 옵션은 실수로 잘못된 파일을 커밋했거나, 작업을 처음부터 다시 시작하고 싶을 때 유용합니다. 이 옵션을 사용하면 HEAD는 지정된 커밋으로 이동하고, **스테이징 영역과 작업 디렉토리 모두 리셋 직전 상태로 되돌려**집니다.\n\n```bash\ngit reset --hard HEAD~1\n```\n\n위 명령은 가장 최근 커밋을 취소하고, 그 커밋의 변경 사항들을 작업 디렉토리에서 완전히 제거합니다. 주의해야 할 점은, `--hard` 옵션은 변경 사항을 영구적으로 삭제하므로 사용에 주의가 필요합니다.\n\n### --mixed 옵션\n\n`git reset --mixed` 옵션은 커밋을 재구성하고 싶거나 스테이징된 변경 사항을 다시 검토하고 싶을 때 유용합니다. 이 옵션을 사용하면 HEAD는 지정된 커밋으로 이동하고, **스테이징 영역의 상태는 리셋되지만, 작업 디렉토리의 파일들은 그대로 유지**됩니다.\n\n```bash\ngit reset --mixed HEAD~1\n```\n\n위 명령은 가장 최근 커밋을 취소하고, 그 커밋의 변경 사항들을 스테이징 영역에서 제거합니다. 그러나 작업 디렉토리의 파일들은 변경 사항이 그대로 남아 있어서 다시 스테이징하거나 수정할 수 있습니다.\n\n\u003csmall\u003e 옵션의 작동 원리 등 자세한 내용은 [Git 공식 문서](https://git-scm.com/book/ko/v2/Git-%EB%8F%84%EA%B5%AC-Reset-%EB%AA%85%ED%99%95%ED%9E%88-%EC%95%8C%EA%B3%A0-%EA%B0%80%EA%B8%B0)에서 확인해 주세요. \u003c/small\u003e\n\n### 실무에서의 사용 예시\n\n**오타가 있어 커밋 메시지를 수정하고 싶을 때**는 `--soft` 옵션을 사용하면 됩니다.\n\n```bash\n# 실수로 오타를 낸 초기 커밋\ngit commit -m \"오타 난 커밋 메시지\"\n# --soft 옵션을 사용하여 리셋 후 다시 커밋\ngit reset --soft HEAD~1\ngit commit -m \"정확하게 작성한 커밋 메시지\"\n```\n\n위는 마지막 커밋을 취소하고 **변경 사항을 스테이징 상태로 유지**하여 새로운 커밋 메시지로 다시 커밋한 방식입니다.\n\u003cbr /\u003e\n\n**잘못된 파일을 커밋해서 이 변경 사항을 완전히 제거하고 싶을 때**는 `--hard` 옵션을 사용하면 됩니다.\n\n```bash\n# 잘못된 파일을 커밋\ngit commit -m \"잘못된 파일 추가\"\n# 이 변경 사항을 완전히 제거\ngit reset --hard HEAD~1\n```\n\n위는 마지막 커밋을 취소하고 **작업 디렉토리와 스테이징 영역에서 모든 변경 사항을 제거**합니다.\n\u003cbr /\u003e\n\n**여러 파일을 커밋했지만 일부 파일의 커밋을 취소하고 싶을 때**는 `--mixed` 옵션을 사용하면 됩니다.\n\n```bash\n# 실수로 모든 파일을 커밋\ngit add .\ngit commit -m \"모든 파일 추가\"\n# 일부 파일의 커밋을 취소하고 싶을 때\ngit reset --mixed HEAD~1\n# 이제 선택적으로 파일을 다시 스테이징할 수 있습니다.\ngit add file1.txt file2.txt\ngit commit -m \"특정 파일 추가\"\n```\n\n위는 마지막 커밋을 취소하고 **스테이징된 변경 사항을 해제하지만, 작업 디렉토리의 파일들은 그대로 유지**하여 선택적으로 다시 스테이징하고 커밋할 수 있게 합니다.\n\u003cbr /\u003e\n\n`git reset` 명령어와 각 옵션들은 상황에 따라 매우 유용하게 사용될 수 있습니다. 그러나 특히 `--hard` 옵션은 데이터를 영구적으로 삭제할 수 있으므로 신중히 사용해야 합니다.\n\n## Git Bisect\n\n협업을 하다보면 제품에 버그가 발견되었지만, 해당 **버그가 언제 코드에 도입되었는지 명확하지 않은 경우**가 있습니다. 이럴 때 `git bisect`를 사용하면 버그를 효율적으로 찾아낼 수 있습니다.\n\n`git bisect` 명령어는 이진 검색 알고리즘을 사용하여 버그가 도입된 커밋을 찾아내는 데 사용합니다. 특히 대규모 프로젝트에서 버그가 언제 처음 발생했는지를 파악할 때 매우 유용합니다.\n\n매우 간단한 예시 상황을 들어보겠습니다. `box`라는 클래스 명을 가진 요소의 배경색은 원래 파란색이어야 하지만 현재 빨간색입니다. 그러나 언제 어떻게 빨간색이 되었는지 알 수 없습니다. 이 의도치 않게 빨간색으로 변경한 커밋을 찾아보도록 하겠습니다.\n\n`git bisect`를 시작하기 전에, 버그가 없는 '좋은' 상태의 커밋과 버그가 있는 '나쁜' 상태의 커밋을 알고 있어야 합니다.\n\n이 상황에선 언제 이 '나쁜' 커밋이 추가되었는지 알 수 없으므로 발견한 현재 HEAD를 '나쁜' 커밋으로 지정하겠습니다. 좋은 커밋은 이 파일이 생성된 시점으로 해두겠습니다. 먼저 `git log`를 실행해 이 파일이 생성된 시점의 커밋 해시를 복사하겠습니다.\n\n```bash\n$ git log\n\n* f9965b5 (HEAD -\u003e main) 그냥 채워넣는 커밋2\n* a26733d 그냥 채워넣는 커밋\n* 6dc333a :memo: Update docs\n* 667aa60 (origin/main, origin/HEAD) Increase width of box to 200px\n* fefbd00 Remove animation from red box\n* cb09d2a 의도치 않는 버그 코드 삽입\n* 79381ec Add animation to the blue box\n* e81d361 :sparkles: 테스트용 html 생성\n...\n```\n\n이제 `bisect`를 시작하고 git에 나쁜 커밋과 좋은 커밋을 알리도록 하겠습니다.\n\n```bash\n$ git bisect start\n$ git bisect bad          # 현재 HEAD가 버그가 있는 상태로 가정\n$ git bisect good e81d361 # 버그가 없었던 마지막 알려진 커밋\n\n이등분: (대략 2 단계) 뒤에 시험할 리비전이 3개 남았습니다\n[667aa609bf57864efa1118c63cc6b9260f5e2adf] Increase width of box to 200px\n```\n\n이후 `git bisect`는 자동으로 중간 지점의 커밋으로 `checkout`하고, 해당 상태에서 버그를 테스트하도록 요청합니다. 버그가 재현되면 '나쁜' 상태로, 그렇지 않으면 '좋은' 상태로 표시합니다. `git bisect`는 이 정보를 바탕으로 다음 테스트할 커밋을 선택하고, 이 과정을 반복하여 버그를 도입한 커밋을 찾아냅니다.\n\n```bash\ngit bisect bad  # 현재 커밋에서 버그가 발견되면\ngit bisect good # 현재 커밋에서 버그가 없으면\n```\n\n이 상황에선 `box` 요소의 배경 색상을 웹 페이지에서 확인하며 커밋 상태를 깃에게 알리면 되겠죠.\n\n웹 페이지를 새로고침하고 문제가 사라졌는지 확인합니다.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg width='480' src=\"./images/bisect/bug-box.webp\"\u003e\n\u003c/p\u003e\n\n문제가 여전히 존재하므로 `git bisect bad`를 실행하여 git에 여전히 문제가 있는 커밋임을 알립니다. 배경색이 빨간색이 아니었던 커밋을 찾을 때까지 이 과정을 반복합니다.\n\n```bash\n$ git bisect bad\n\n# git이 올바른 커밋을 찾기 전에 검색해야 하는 커밋 수를 알려줍니다.\n이등분: (대략 1 단계) 뒤에 시험할 리비전이 1개 남았습니다\n[cb09d2a1b41c4e74f7a8f717eb4439e205f85fa4] 의도치 않는 버그 코드 삽입\n...\n$ git bisect bad\n\n이등분: (대략 0 단계) 뒤에 시험할 리비전이 0개 남았습니다\n[79381ec656cfc7b3b1fc60fa8c468c2f0eaa9299] Add animation to the blue box\n```\n\n... 그리고 웹 페이지 새로고침...\n\n\u003cp align=\"center\"\u003e\n  \u003cimg width='480' src=\"./images/bisect/good-box.webp\"\u003e\n\u003c/p\u003e\n\n박스 배경 색상이 더 이상 빨간색이 아니므로 좋은 커밋입니다. `git bisect good`을 실행합니다:\n\n```bash\n$ git bisect good\n\ncb09d2a1b41c4e74f7a8f717eb4439e205f85fa4 is the first bad commit\ncommit cb09d2a1b41c4e74f7a8f717eb4439e205f85fa4\nAuthor: jaem1n207 \u003croy.jm.lee@gmail.com\u003e\n\n    의도치 않는 버그 코드 삽입\n\n index.html | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n```\n\n이제 `git show`를 사용하여 커밋을 확인하고 문제를 파악할 수 있습니다:\n\n```bash\n$ git show cb09d2a1b41c4e74f7a8f717eb4439e205f85fa4\n```\n\n\u003cp align=\"center\"\u003e\n  \u003cimg width='480' src=\"./images/bisect/bad-commit.webp\"\u003e\n\u003c/p\u003e\n\n작업이 끝나면 `git bisect reset`을 실행해 브랜치를 원래 상태로 되돌립니다:\n\n```bash\ngit bisect reset # bisect 섹션 종료\n```\n\n매우 간단한 예시라 두 커밋 사이의 간격이 크지 않습니다. 실제 상황에선 훨씬 간격이 큰 경우가 많을 겁니다. 간격이 큰 경우 번거롭긴 하겠지만, 대부분의 경우 문제를 발견할 수 있습니다.\n\n이 과정을 통해 문제가 되는 커밋을 빠르게 찾아 문제를 해결하고, 해당 버그가 어떻게 코드에 포함되었는지 알 수 있습니다. 이는 팀 내에서 코드 리뷰를 진행하거나, 향후 비슷한 문제를 방지하기 위해 활용될 수 있습니다.\n\u003csmall\u003e물론 가장 좋은 것은 이 기능을 사용하는 일이 없는 것입니다.\u003c/small\u003e\n\n## Git Merge\n\n`git merge` 명령어는 **두 개의 브랜치를 하나로 합치는 데 사용**됩니다. 이는 브랜치 간의 작업을 병합할 때 주로 사용되며, 기능 개발이 완료되어 메인 코드 베이스에 병합해야 할 때 유용합니다.\n\u003csmall\u003e 병합과 관련해서 처음 다루는 섹션이니 여기서만 다른 섹션에 비해 조금 더 자세히 분리해서 설명하도록 하겠습니다. \u003c/small\u003e\n\n### 기본 사용법\n\n`main` 브랜치에 `feature` 브랜치를 병합하는 기본적인 과정은 다음과 같습니다:\n\n```bash\ngit checkout main\ngit merge feature\n```\n\n이 명령은 `feature` 브랜치의 변경 사항을 `main` 브랜치에 병합합니다.\n\n### 충돌 해결\n\n병합 과정에서 두 브랜치에서 같은 파일의 같은 부분을 수정했다면 충돌이 발생할 수 있습니다. Git은 자동으로 이를 해결할 수 없을 때 사용자에게 충돌을 해결하도록 요청합니다.\n\n```bash\n# 충돌이 발생한 경우\ngit merge feature\n# 충돌을 해결한 후\ngit add .\ngit commit -m ${커밋 메시지}\n```\n\n충돌을 해결하고 나면, 변경 사항을 스테이징하고 새로운 커밋을 만들어 충돌을 해결한 상태를 기록합니다.\n\n### Fast-Forward 병합\n\n`feature` 브랜치가 `main` 브랜치로부터 분기된 이후 `main` 브랜치에 새로운 커밋이 없다면, fast-forward 병합이 가능합니다. 이 경우 `feature` 브랜치의 커밋들이 `main` 브랜치의 **커밋 이력에 그대로** 추가될 수 있습니다.\n\n```bash\ngit checkout main\ngit merge feature --ff-only\n```\n\n`--ff-only` 옵션은 fast-forward 병합이 가능할 때만 병합을 수행하고, 그렇지 않으면 병합하지 않습니다.\n\n### No-Fast-Forward 병합\n\n프로젝트의 이력을 명확하게 유지하기 위해 병합 커밋을 만들고 싶을 때는 no-fast-forward 병합을 사용할 수 있습니다.\n\n```bash\ngit checkout main\ngit merge feature --no-ff\n```\n\n이 옵션을 사용하면 병합 커밋이 항상 생성되며, 브랜치의 이력을 보존할 수 있습니다.\n\n### Squash 병합\n\n여러 개의 커밋을 하나의 커밋으로 합치고 싶을 때는 `squash` 병합을 사용할 수 있습니다. 이는 `feature` 브랜치의 커밋들을 단일 커밋으로 `main` 브랜치에 병합하고자 할 때 유용합니다.\n\n```bash\ngit checkout main\ngit merge feature --squash\n# squash 후에는 커밋을 수동으로 완료해야 합니다.\ngit commit -m ${커밋 메시지}\n```\n\n`squash` 옵션을 사용하면 `feature` 브랜치의 모든 변경 사항이 스테이징 상태로 남게 되며, 사용자가 새로운 커밋 메시지와 함께 커밋을 완료해야 합니다.\n\n### 실무에서의 사용 예시\n\n실무에서는 `git merge`를 사용하여 기능 개발이 완료된 브랜치를 `main` 브랜치에 통합하거나, `release` 브랜치를 생성하여 배포 준비를 합니다. 예를 들어, `release` 브랜치를 `main` 브랜치로부터 분기하여 버전을 관리하고, 최종적으로 `main` 브랜치에 병합하여 배포할 수 있습니다.\n\n```bash\ngit checkout main\ngit merge release --no-ff -m \"Merge release 1.0.0 into main\"\n```\n\n이렇게 하면 `main` 브랜치에 명확한 릴리즈 지점이 생성되며, 나중에 버전별로 코드 상태를 쉽게 확인할 수 있습니다.\n\n또한, 핫픽스가 필요한 경우 `hotfix` 브랜치를 생성하여 긴급한 수정을 진행한 후 `main` 브랜치와 `release` 브랜치에 병합할 수 있습니다.\n\n```bash\ngit checkout -b hotfix\n# ...핫픽스 작업...\ngit checkout main\ngit merge hotfix\ngit checkout release\ngit merge hotfix\n```\n\n이러한 방식으로 `git merge`는 다양한 브랜치 관리 전략에 따라 유연하게 사용될 수 있으며, 프로젝트의 안정성과 로그의 명확성을 동시에 관리할 수 있습니다.\n\n## Git Rebase\n\n제가 가장 유용하게 사용하고 있는 명령어입니다. `git rebase` 명령어는 **커밋 로그를 완벽하게 관리**할 수 있으며 주로 선형 로그를 유지하기 위해 사용됩니다.\n\n대부분의 작업은 기능 브랜치를 만들고 메인 코드 베이스에 병합할 텐데요. 이때 병합하기 전에 `rebase`를 사용하여 커밋 로그를 정리할 수 있습니다. 중요하지 않은 커밋을 `squash`하고, 사용하지 않는 커밋을 삭제하며, 작업 순서에 맞도록 커밋 로그 순서를 재정렬할 수 있습니다.\n\n`feat/1` 브랜치를 `main` 브랜치에 병합할 때 선형 로그를 유지하려면 다음과 같이 하면 됩니다:\n\n`main` 브랜치를 **최신 상태로 업데이트**합니다.\n\n```bash\ngit checkout main\ngit pull origin main\n```\n\n`feat/1` 브랜치로 전환하고 `feat/1` 브랜치를 `main` 브랜치에 `rebase`합니다.\n\n```bash\ngit checkout feat/1\ngit rebase main\n```\n\n이렇게 하면 `feat/1` 브랜치의 변경 사항이 `main` 브랜치의 최신 커밋 위에 적용됩니다.\n\n`rebase` 과정에서 **충돌이 발생**하면, 충돌을 해결하고 `rebase`를 계속 진행하면 됩니다.\n\n```bash\n# 충돌 해결 후\ngit add .\ngit rebase --continue\n```\n\n충돌이 모두 해결되고 `rebase`가 완료되면, `feat/1` 브랜치의 커밋들이 `main` 브랜치의 커밋들 뒤에 선형적으로 위치하게 됩니다.\n\n이제 `feat/1` 브랜치를 `main` 브랜치에 병합할 수 있습니다. 병합 시 `--ff-only` 옵션을 사용해 **fast-forward 병합을 강제**할 수 있습니다.\n\n```bash\ngit checkout main\ngit merge feat/1 --ff-only\n```\n\n이렇게 하면 별도의 병합 커밋 없이 **선형 로그를 유지**할 수 있습니다.\n마지막으로 `main` 브랜치를 원격 저장소에 푸시하면 됩니다.\n\n```bash\ngit push origin main\n```\n\n이렇게 `feat/1` 브랜치의 변경 사항을 `main` 브랜치에 선형적으로 병합하여 커밋 로그를 깔끔하게 유지할 수 있습니다.\n\n또한 `rebase`에는 `standard`와 `interactive`로 두 개의 모드가 있습니다.\n\n```bash\n# standard mode\ngit rebase \u003cbase\u003e\n# interactive mode\ngit rebase -i \u003cbase\u003e\n```\n\n`standard` 모드의 `git rebase`는 현재 작업 중인 브랜치의 커밋을 자동으로 가져와서 전달된 브랜치의 Head에 적용합니다.\n\n`-i` 플래그를 사용해 `git rebase`를 실행하면 편집기를 이용해 프로세스의 커밋을 개별적으로 변경할 수 있습니다. 이렇게 하면 기존 커밋을 제거, 분할 및 변경하는 등 로그를 정리할 수 있습니다.\n\n`rebase`는 기존의 커밋을 재사용하는 것이 아니라, 내용이 같은 커밋을 새로 만들기 때문에 강제 푸시할 때 항상 주의해야 합니다. 물론 이후 설명할 [git reflog](#git-reflog)를 사용하면 커밋을 복원하고 전체 rebase를 실행 취소할 수 있습니다.\n\n## Git Squash\n\n`git squash`는 PR의 모든 커밋을 **합쳐서 하나의 커밋**으로 만들어 줍니다. `squash`로 브랜치를 병합하면 하나의 커밋으로 합쳐지므로 PR 하나가 하나의 커밋이 됩니다. 이렇게 되면 메인 코드 베이스에서 모든 커밋이 빌드될 수 있게 됩니다. 그러면 문제가 생겼을 때 어느 커밋으로도 되돌아갈 수 있어 잘 쪼개거나 합치는 것은 중요합니다.\n\n예를 들어, 캐러셀을 구현하는 브랜치는 아래처럼 커밋 로그가 쌓이겠죠.\n\n- 캐러셀 슬라이더 구현\n- 캐러셀 스타일 정의\n- 캐러셀 버튼 추가\n- 캐러셀 크로스 브라우징 해결\n- 캐러셀 여백 조정\n\n5개의 커밋을 만들었지만 모두 하나의 기능과 관련이 있습니다. 각 커밋들도 이 새로운 기능에 관한 것이기에 서로 분리하기에는 애매합니다.\n\n`git merge --squash`를 사용할 수도 있지만, `cherry-pick`을 사용해 커밋을 가져올 때 커밋 설명이 더 단순하고 시각적으로 설정하기 쉬운 `git rebase -i`를 사용하는 것이 편하다고 생각합니다.\n\n```bash\n# git merge를 사용한 방식\ngit merge --squash \u003c브랜치명\u003e\n# git rebase -i를 사용한 방식\ngit rebase -i HEAD~3\n\npick 123abcd 캐러셀 구현\nsquash 234bcde 캐러셀 슬라이더 구현\nsquash 345cdef 캐러셀 스타일 정의\n```\n\n어떤 방식으로 하던 git 로그를 살펴보면 비교적 작은 차이의 여러 커밋이 하나의 목표를 지향하는 하나의 커밋이 된 것을 확인할 수 있습니다. 덕분에 변경 사항을 쉽게 추적할 수 있게 되었고 문제가 생겼을 때 과감하게 `revert`할 수 있습니다. 덤으로 모든 커밋이 이렇게 된다면 `git bisect`를 더 유용하게 사용할 수 있게 되겠죠.\n\n## Git Reflog\n\n`git reflog` 명령어는 **Git의 내부 로그인 reflog를 확인**할 수 있게 해주며, 이는 모든 HEAD의 변경사항과 브랜치의 업데이트를 기록합니다. 이 명령어는 실수로 커밋을 잃어버렸거나 `rebase`와 같은 작업으로 인해 커밋이 사라진 것처럼 보일 때 이를 복구하는 데 유용하게 사용할 수 있습니다.\n\n예를 들어, `rebase`를 실행했는데 중요한 커밋을 잘못 삭제했다고 가정해 봅시다. 이런 경우 `git reflog`를 사용하여 삭제된 커밋을 찾아 복구할 수 있습니다.\n\n먼저 `git reflog`를 실행하여 최근에 변경된 HEAD의 목록을 확인합니다.\n\n```bash\ngit reflog\n```\n\n출력 결과는 다음과 같을 수 있습니다:\n\n```bash\n1c002dd (HEAD -\u003e main, origin/main, origin/HEAD) HEAD@{0}: merge feat/1: Fast-forward\nab1c2ef HEAD@{1}: commit: Fix typo in README\n...\nd34db33 HEAD@{5}: rebase (start): checkout main\nc0ffee1 HEAD@{6}: commit: Add feature 1\n...\n```\n\n여기서 `HEAD@{6}`은 `Add feature 1`라는 메시지를 가진 커밋을 나타냅니다. 이 커밋을 잘못 삭제했다면, 해당 커밋으로 되돌아갈 수 있습니다.\n`HEAD@{6}` 형식에 대해 조금 더 자세히 설명해 보자면, `HEAD@{6}`은 \"여섯 번 이동하기 전에 HEAD가 있던 곳\", `master@{one.week.ago}`는 \"이 로컬 레포에서 일주일 전에 master가 가리키던 곳\" 등을 의미합니다. 자세한 건 [git revisions](https://git-scm.com/docs/gitrevisions)를 참고해 주세요.\n\n```bash\ngit rebase --abort\n```\n\n위 명령어로 `rebase`를 취소할 수 없는 경우에는 `git reflog`를 사용하여 `rebase` 이전 상태로 되돌아갈 수 있습니다.\n\n```bash\ngit reset --hard HEAD@{n}\n```\n\n여기서 `n`은 `rebase`를 시작하기 전의 `HEAD` 위치입니다.\n\n`git reflog`는 기본적으로 **로컬 저장소에만 존재**하며, 원격 저장소에는 반영되지 않습니다. 따라서 로컬에서 실수로 커밋을 잃어버렸을 때 복구하는 데 유용합니다. 하지만 `git reflog`를 사용하여 복구한 후에는 원격 저장소에 강제 푸시(`git push --force`)를 해야 할 수도 있으니, 마찬가지로 주의가 필요합니다.\n\n## 마지막 커밋을 수정하는 방법\n\n보통 커밋 로그를 관리하는 일 중 **마지막 커밋을 수정**하는 상황이 가장 잦습니다.(저만 그런 걸 수도 있고요) 크게 두 가지로 나눌 수 있습니다:\n\n1. 커밋 메시지만 수정\n2. 나중에 수정한 파일을 다른 커밋 안에 밀어 넣는 작업\n\n**커밋 메시지를 수정**하는 방법은 아래 명령어를 실행하면 됩니다:\n\n```bash\ngit commit --amend\n```\n\n이 명령은 아래 이미지처럼 텍스트 편집기를 실행시켜서 마지막 커밋 메시지를 열어줍니다.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg width='480' src=\"./images/amend/modify-last-commit-msg.webp\"\u003e\n\u003c/p\u003e\n\n여기에 메시지를 바꾸고 편집기를 닫으면 편집기는 바뀐 메시지로 마지막 커밋을 수정합니다.\n\u003csmall\u003e`vi` 편집기에 익숙하지 않다면 [VI 에디터 명령어 정리](https://emptyreset.tistory.com/15)를 참고해 주세요. 간단히 설명하자면 편집기에서 `ESC`를 눌러 모드 전환을, `i`, `a`, `o`, `s`를 눌러 편집 모드로 전환을, `:`를 누르고 `q` 또는 `wq`를 눌러 나가거나 저장할 수 있습니다.\u003c/small\u003e\n\n커밋하고 난 후 새로 만든 파일이나 수정한 파일을 **가장 최근 커밋에 밀어 넣고 싶은 경우**엔 아래처럼 하면 됩니다:\n\n```bash\ngit add ${파일명}\ngit commit --amend\n```\n\n`git add` 명령어로 원하는 파일을 Staging Area에 넣고 `git commit --amend` 명령어를 입력해 커밋하면 커밋 자체가 수정되면서 추가로 수정사항을 밀어 넣을 수 있습니다.\n이때 **SHA-1 값이 바뀌기** 때문에 과거의 커밋을 변경할 때 주의해야 합니다. `Rebase`와 같이 이미 푸시한 커밋은 수정하면 안 됩니다.\n\n만약 커밋 메시지를 수정할 필요가 없이 코드의 오타를 살짝 고치는 정도의 사소한 수정을 할 경우엔 **편집기를 열지 않고** 싶을 수도 있습니다. 이 경우엔 아래와 같이 `--no-edit` 옵션을 사용하면 편집기 실행 없이 수정할 수 있습니다.\n\n```bash\ngit commit --amend --no-edit\n```\n\n## 여러 개의 커밋 메시지를 수정하고 싶은 경우\n\n`git rebase -i` 를 실행하면 최근 커밋이 아니라 **여러 개의 예전 커밋을 수정**할 수 있습니다. 이는 커밋 로그를 정리하는 데 매우 유용하게 사용할 수 있습니다. 예를 들어보겠습니다:\n\n`A 변수명 수정` - `A 로직 수정` - `파일명 수정` 순으로 작업을 하다가 A 변수에 더 어울리는 이름이 생각 나 커밋을 추가했습니다. 그럼 `A 변수명 수정` - `A 로직 수정` - `파일명 수정` - `A 변수명 수정` 형태로 커밋 로그가 복잡해집니다. 이때 `git rebase -i` 를 사용해 순서를 바꾸고 커밋을 합쳐 `A 변수명 수정` - `A 로직 수정` - `파일명 수정`처럼 만들 수 있습니다.\n\n어떻게 사용하는지 살펴보겠습니다. 아래 이미지는 수정 전 커밋 로그입니다. 여기서 `A 변수명 다시 수정` 커밋을 `A 변수명 수정` 커밋과 합쳐보겠습니다\n\n\u003cp align=\"center\"\u003e\n  \u003cimg width='480' src=\"./images/rebase/duplicate-msg.webp\"\u003e\n\u003c/p\u003e\n\n```bash\ngit rebase -i ${수정할 커밋의 이전 커밋}\n```\n\n`git rebase -i` 명령어를 사용하면 대화형 모드로 들어갑니다. 이때 내가 수정하고 싶은 커밋의 이전 커밋을 기준으로 실행해야 합니다. 예를 들어, `A` 커밋이 `HEAD~3`라면 `HEAD~4`를 사용합니다. 커밋 해시를 기준으로 입력할 수도 있습니다.\n\n입력하고 나면 아래 이미지처럼 출력되는 `vi` 편집기를 볼 수 있습니다.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg width='480' src=\"./images/rebase/rebase-vi-editor.webp\"\u003e\n\u003c/p\u003e\n\n각 라인은 `[명령어] [커밋 해시] [커밋 메시지]` 순서대로 구성되어 있습니다. 아래 주석에선 각 커밋에 사용할 수 있는 명령어들의 목록과 역할을 확인할 수 있습니다.\n\n여기서 `squash`와 `fixup`이 **특정 커밋을 이전 커밋과 합치는** 명령어입니다. `squash`는 각 커밋들의 메시지가 합쳐지는 반면, `fixup` 은 이전의 커밋 메시지만 남기는 차이점이 있습니다.\n\n이전 커밋 메시지만 남기면 되므로 `fixup`을 사용해 보겠습니다.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg width='480' src=\"./images/rebase/fixup-command.webp\"\u003e\n\u003c/p\u003e\n\n합쳐지길 원하는 커밋을 찾아 `pick`을 `fixup`으로 변경했습니다. 그리고 `A 변수명 다시 수정` 커밋을 **`A 변수명 수정` 커밋 바로 아래로 이동**시켰습니다. 이후 변경사항을 저장하고 편집기를 종료합니다.\n\n똑같은 라인의 변수명을 수정했으므로 `rebase` 중에 **충돌이 발생**했습니다. 해결 후 `git rebase --continue` 명령어를 입력해 계속 진행해 줍니다.\n\n이때 주의할 점은, **이미 원격 레포에 푸시된 브랜치의 커밋을 로컬에서 `rebase`해서 푸시하는 경우**는 없어야 합니다. `rebase`는 기존의 커밋을 재사용하는 것이 아니라, 내용이 같은 커밋을 새로 만들기 때문입니다. 즉, 원격 브랜치를 베이스로 작업하고 있던 동료의 커밋 로그를 깨뜨리거나 작업 본을 날려 먹을 수도 있으니 항상 주의해야 합니다.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg width='480' src=\"./images/rebase/rebase-continue.webp\"\u003e\n\u003c/p\u003e\n\n두 커밋이 합쳐져 커밋 로그가 깨끗하게 정리된 것을 볼 수 있습니다.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg width='480' src=\"./images/rebase/rebase-finish.webp\"\u003e\n\u003c/p\u003e\n\n## 상황에 맞게 사용하는 Merge, Rebase, Squash\n\n해당 섹션은 **[HashiCorp](https://www.hashicorp.com/)의 공동창업자인 [Mitchell Hashimoto](https://gist.github.com/mitchellh)가 얼마 전 작성한 [Merge vs. Rebase vs. Squash](https://gist.github.com/mitchellh/319019b1b8aac9110fcfb1862e0c97fb) 글을 번역**한 것입니다. 너무 좋은 글이라 여기에 정리해봅니다. Mitchell Hashimoto가 작성한 `Merge vs. Rebase vs. Squash` 내용을 기반으로 Outsider님이 작성하신 [글](https://blog.outsider.ne.kr/1704)도 정말 유용하니 꼭 한 번 보시길 바랍니다.\n\n\u003e 병합하는 방법은 여러 종류가 있습니다. 각각의 장점이 있으며 상황에 따라 유연하게 사용하면 됩니다.\n\u003e\n\u003e Merge 커밋을 만드는 것이 로그를 가장 잘 표현한다고 생각하기 때문에 Merge를 선호합니다. 병합 지점을 쉽게 볼 수 있고, 개발자가 수행한 모든 WIP 커밋을 볼 수 있습니다. 즉, 전체 병합을 쉽게 되돌릴 수 있습니다(git revert -mN).\n\u003e\n\u003e 또한 모든 커밋이 빌드된다면 커밋이 많을수록 git bisect가 좋아진다고 생각합니다. 이상적으로 +50/-50에 가까운 커밋을 유지하는 것이 좋습니다. 그러면 리뷰 단계에서 버그의 존재를 파악할 수 있습니다.\n\u003e\n\u003e 단, 이 전략은 개발자가 모든 커밋을 잘 관리해야만 성공할 수 있습니다. 일반적으로 함께 일하는 개발자가 모든 커밋을 빌드 가능한 상태로 유지하기를 기대했습니다.\n\u003e\n\u003e PR에 수많은 \"WIP\" 커밋이 있지만 실제로는 비교적 작은 차이로 하나의 목표를 지향하는 경우에는 Squash를 합니다. Git과 Github에서 기본적으로 제공하는 커밋 메시지는 좋지 않아 Squash를 할 때 커밋 메시지를 새로 작성합니다.\n\u003e\n\u003e PR에 \"WIP\"가 많은데 각 커밋의 차이가 크다면 인터랙티브 Rebase로 커밋을 Squash하고 순서를 변경합니다. 개발자들이 커밋 관리에 신경 쓰기를 기대하지만, 안타깝게도 많은 개발자가 GIt에 익숙하지 않습니다.\n\u003e\n\u003e 마지막으로, 대규모 인터랙티브 Rebase 작업 시 Git GUI 클라이언트를 사용하는 편입니다. GIT CLI에 매우 익숙하더라도, 변경된 라인이 많은 대규모 PR(ex: 50개 이상의 커밋)을 인터랙티브하게 Rebase할 때는 GUI를 사용하는 것이 유용합니다. macOS를 사용해서 Tower를 사용하고 있다. 하지만 이 상황을 제외하곤 GUI를 사용하지 않습니다.\n\n## 마무리\n\n단순히 커밋, 푸시, 풀 명령어만 사용하지 않고 커밋 로그를 넘나들 수 있도록 Git에 익숙해진다면 커밋 로그를 깨끗하게 관리할 수 있고 효율적인 작업이 가능해집니다.\n\n하지만 협업할 때 모든 개발자가 Git에 익숙한 것은 아니기에 커밋 로그가 이렇게 잘 정리되는 것은 사실상 불가능에 가깝습니다. 그럼에도 이러한 기능을 손에 익혀두면 유용하게 잘 쓰이는 상황이 반드시 있습니다.\n\n## 참고\n\n- [Git Docs](https://git-scm.com/doc)\n- ['Outsider'님의 Merge vs Rebase vs Squash](https://blog.outsider.ne.kr/1704?category=0)\n- ['재그지그'님의 Git rebase with interactive](https://wormwlrm.github.io/2020/09/03/Git-rebase-with-interactive-option.html#squash-fixup)\n- [고급 깃 가이드](https://www.toptal.com/git/the-advanced-git-guide)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaem1n207%2Fadvanced-git","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjaem1n207%2Fadvanced-git","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaem1n207%2Fadvanced-git/lists"}