23.06.15
DEVOTEE를 활성화 시키면
지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.
버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.
| 컨텐츠 유형 | 제목 | 저장일 | 삭제 |
|---|
본인인증 로그인에 실패하였습니다.
회원이 아니시거나 본인인증 등록이
완료되지 않은 사용자입니다.
소프트웨어 개발 환경이 급속도로 진화하면서, 효율적인 협업과 버전 관리의 중요성이 그 어느 때보다 부각되고 있습니다.
현대의 복잡하고 대규모화된 프로젝트들은 개발자 간의 원활한 소통과 체계적인 코드 관리를 필수적으로 요구하고 있으며,이러한 요구를 충족시키는 핵심 도구로 Git이 주목받고 있습니다.
소프트웨어 개발 분야에서 Git은 단순한 도구를 넘어 프로젝트 성공의 핵심 요소로 자리잡았습니다.
특히 Git Flow는 효율적인 브랜치 관리와 협업을 위한 강력한 워크플로우 모델로 널리 사용되고 있습니다.
그러나, Git Flow의 진정한 가치는 이론적 이해를 넘어 실제 개발 현장에서 마주치는 다양한 상황에 적절히 대응할 수 있는 능력에서 나타납니다.
이 블로그에서는 Git Flow의 이해도를 높이고, 실제 문제 해결 능력을 향상시키기 위해 다양한 시나리오별 케이스를 제공하고 분석합니다.
각 시나리오는 실제 개발 현장에서 자주 마주치는 상황을 바탕으로 구성되었으며, 문제 해결을 위한 단계별 가이드와 함께 그 배경에 있는 Git Flow의 원리를 설명합니다.
우리는 feature-login 브랜치에서 로그인 기능을 구현하고 있습니다.
작업이 완료되면 feature-login 브랜치를 develop 브랜치에 병합할 것입니다.
병합이 완료되면, 원격 develop 브랜치와 로컬 develop 브랜치를 일치시키고 로컬의 feature-login 브랜치는 삭제할 것입니다.
feature 브랜치 생성 및 작업: 새로운 기능을 개발하기 위해 feature-login 브랜치를 생성하고 작업을 진행.
feature 브랜치 푸시: 작업이 완료되면 feature-login 브랜치를 원격에 푸시.
develop 브랜치로 병합: develop 브랜치로 돌아가 feature-login 브랜치를 병합.
로컬 develop 브랜치 동기화: 병합 후 원격 develop 브랜치를 로컬 develop 브랜치와 동기화.
feature 브랜치 삭제: 더 이상 필요 없는 feature-login 브랜치를 로컬과 원격에서 삭제.
원격 develop 브랜치에서 새로운 featue-login 브랜치를 생성하고 작업을 시작합니다.
# 원격 develop 브랜치에서 새로운 feature 브랜치 생성
git checkout develop
git pull origin develop # 로컬 develop 브랜치를 최신화
git checkout -b feature-login # feature-login 브랜치 생성 및 체크아웃
# 작업 진행: 예를 들어 로그인 기능 구현
# 작업 파일: app/login.py, 수정 후 커밋
git add app/login.py
git commit -m "Add login functionality"feature-login 브랜치에서 작업이 완료되면, 원격 리포지토리에 푸시합니다.
# 원격 feature-login 브랜치로 푸시
git push origin feature-login작업이 완료되면 feature-login 브랜치를 develop 브랜치에 병합합니다.
# develop 브랜치로 돌아감
git checkout develop
git pull origin develop # 병합 전에 develop 브랜치를 최신 상태로 유지
# feature-login 브랜치를 develop 브랜치에 병합
git merge feature-login
# 충돌 발생 시 수동으로 해결 후
# git add <conflicted-file>
# git merge --continue
# 병합 완료 후 원격 develop 브랜치에 푸시
git push origin develop병합 후 원격의 develop 브랜치가 최신 상태로 업데이트되었으므로, 로컬 develop 브랜치도 최신 상태로 동기화합니다.
# 원격 develop 브랜치와 로컬 develop 브랜치 동기화
git pull origin developfeature-login 브랜치의 작업이 완료되었으므로 로컬과 원격의 feature-login 브랜치를 삭제합니다.
# 로컬 feature-login 브랜치 삭제
git branch -d feature-login
# 원격 feature-login 브랜치 삭제
git push origin --delete feature-login시나리오 케이스1) 실수로 develop 브랜치에서 신규 기능 작업
시나리오 케이스2) 피처 작업 중 다른 브랜치 모듈 필요 상황
시나리오 케이스3) push 실패를 rebase로 해결하는 케이스
시나리오 케이스4) rebase 원리를 잘 설명하는 시나리오
시나리오 케이스5) stash의 원리를 잘 설명하는 시나리오
시나리오 케이스6) fast-forward 병합과 병합 커밋
실수로 develop 브랜치에서 새로운 기능을 작업하고 있었습니다. 이 작업은 feature-login 브랜치에서 해야 하는데, 뒤늦게 이를 깨닫게 됩니다.
현재 상태에서는 develop 브랜치에 잘못된 커밋이 포함되어 있으므로 이를 feature-login 브랜치로 옮기고, develop 브랜치를 원래 상태로 되돌려야 합니다.
Git은 현재의 워크 디렉토리 상태(즉, 수정된 파일, 스테이징된 파일 등)를 그대로 유지하면서 브랜치를 생성하거나 이동할 수 있게 설계되었습니다.
그래서 git checkout -b <새로운 브랜치>를 실행할 때, 해당 작업 상태가 자동으로 새로운 브랜치로 복사됩니다.
커밋하지 않은 변경 사항도 새로운 브랜치로 복사되므로, 새로운 브랜치에서 작업을 이어서 진행할 수 있습니다.
이미 커밋된 내용은 원래 브랜치와 새 브랜치 모두에 존재하게 됩니다.
develop 브랜치에서 실수로 작업을 시작: 실수로 develop 브랜치에서 작업을 진행.
작업을 새로운 feature 브랜치로 옮기기: git checkout -b feature-login 명령으로 작업을 새로운 feature 브랜치로 이동.
develop 브랜치 되돌리기: git reset --hard origin/develop 명령으로 develop 브랜치를 원격과 동일한 상태로 되돌림.
feature 브랜치에서 작업 계속 진행: featue-login 브랜치에서 작업을 완료하고 푸시.
develop 브랜치에 병합: 작업 완료 후 develop 브랜치로 병합.
feature 브랜치 삭제: 필요 없는 featue-login 브랜치를 로컬과 원격에서 삭제.
실수로 develop 브랜치에서 작업을 한 상태입니다. 현재 어떤 작업을 했는지 확인하고, 이를 다른 브랜치로 옮길 준비를 합니다.
# 현재 브랜치 확인
git branch
# 로그를 통해 최근 커밋 확인
git log --oneline지금까지 develop 브랜치에서 진행한 작업을 새로운 feature-login 브랜치로 옮깁니다.
# 현재 상태를 그대로 두고, feature-login 브랜치 생성
git checkout -b feature-login
# 이제 feature-login 브랜치에서 작업이 계속됨이 과정에서는 develop 브랜치에 있던 작업이 그대로 새로운 브랜치(feature-login)로 옮겨집니다.
develop 브랜치를 원래 상태로 되돌려야 합니다. 이를 위해 develop 브랜치로 돌아가서, 잘못된 커밋을 되돌리거나 리셋합니다.
# develop 브랜치로 돌아감
git checkout develop
# 원격 develop 브랜치와 동일하게 로컬 브랜치를 되돌림
git reset --hard origin/developgit reset --hard 명령은 현재 로컬 develop 브랜치를 원격 상태와 동일하게 되돌려줍니다. 이 명령을 실행하면 develop 브랜치에서 했던 잘못된 작업이 사라집니다.
이제 featue-login 브랜치에서 계속 작업을 진행하고, 필요할 경우 원격 리포지토리에 푸시합니다.
# feature-login 브랜치에서 작업 진행 후 푸시
git add <files>
git commit -m "Continue login feature development"
git push origin feature-login작업이 완료되면 feature-login 브랜치를 develop 브랜치에 병합합니다.
# develop 브랜치로 돌아가 최신 상태로 만듦
git checkout develop
git pull origin develop
# 체크아웃된 브랜치인 develop에 feature-login 브랜치를 병합
git merge feature-login # 현재 상태가 develop브랜치 상태이므로 명시적으로 develop브랜치를 표시하지 않았지만 feature-login은 develop에 병합이 됨
# 병합 후 푸시
git push origin developfeature-login 브랜치의 작업이 완료되었으므로 로컬과 원격에서 브랜치를 삭제합니다.
bash코드 복사# 로컬 feature-login 브랜치 삭제
git branch -d feature-login
# 원격 feature-login 브랜치 삭제
git push origin --delete feature-loginfeatue-login 브랜치에서 로그인 기능을 개발하고 있습니다.
다른 팀원이 feature-profile 브랜치에서 프로필 관련 모듈을 작업하여 develop 브랜치에 병합했습니다.
feature-login 브랜치에서 이 프로필 모듈이 필요하게 되어, 최신 develop 브랜치의 변경 사항을 feature-login 브랜치로 가져와야 합니다.
브랜치 간 병합의 유연함:
Git은 서로 다른 브랜치에서 작업하던 내용이 필요한 경우, 쉽게 다른 브랜치의 최신 변경 사항을 가져올 수 있습니다.
이 과정에서 rebase와 merge는 서로 다른 방식으로 히스토리를 관리하므로, 상황에 따라 적절한 방법을 선택할 수 있습니다.
rebase vs merge:
rebase는 히스토리를 깔끔하게 유지하며, 브랜치 간의 병합을 직관적으로 이해할 수 있게 해 줍니다.
반면, merge는 병합 커밋을 통해 두 브랜치 간의 변경 사항을 명확하게 기록하며, 병합 히스토리를 추적하기 쉽습니다.
충돌 관리:
rebase 또는 merge 시 충돌이 발생할 수 있으며, 충돌 해결 과정에서 Git의 유연함과 충돌 해결 방식에 대한 이해가 필요합니다.
feature-login 브랜치에서 작업 중:
로그인 기능을 개발하던 중, develop 브랜치에 병합된 새로운 프로필 모듈이 필요하다는 것을 인지.
develop 브랜치에서 최신 변경 사항 확인:
git log를 사용해 develop 브랜치에 병합된 새로운 모듈을 확인.
feature-login 브랜치로 develop 브랜치의 변경 사항 병합:
두 가지 방법 중 하나 선택:
rebase: git rebase origin/develop – 커밋 히스토리를 깔끔하게 유지.
merge: git merge origin/develop – 병합 커밋을 남겨 히스토리 기록.
작업 계속 진행:
develop 브랜치에서 가져온 프로필 모듈을 활용해 로그인 기능 개발을 계속 진행.
작업 완료 후 develop 브랜치에 병합:
featue-login 브랜치를 다시 develop 브랜치로 병합하고 푸시.
featue-login 브랜치 삭제:
작업이 끝난 featue-login 브랜치를 로컬과 원격에서 삭제.
featue-login 브랜치에서 로그인 기능을 개발하는 중입니다. 그런데 개발 도중, 새로 병합된 프로필 모듈이 필요하다는 것을 알게 되었습니다.
git checkout -b feature-login
# 로그인 관련 작업 진행 중
echo "def login(): pass" > app/login.py
git add app/login.py
git commit -m "Implement login function"다른 팀원이 feature-profile 브랜치에서 작업한 프로필 모듈이 develop 브랜치에 병합되었는지 확인합니다.
git fetch origin
git log origin/develop --oneline로그를 통해 develop 브랜치에 feature-profile 브랜치의 변경 사항이 병합된 것을 확인합니다.
develop 브랜치의 최신 변경 사항을 featue-login 브랜치로 병합하여 프로필 모듈을 가져옵니다.
여기서 rebase 또는 merge 두 가지 방법을 사용할 수 있습니다. 각 방법에 따라 히스토리가 다르게 관리됩니다.
# feature-login 브랜치에서 rebase
git checkout feature-login
git fetch origin
git rebase origin/develop이 방법을 사용하면, featue-login 브랜치의 커밋들이 develop 브랜치의 최신 커밋들 뒤에 정렬됩니다.
충돌이 발생할 경우, 충돌 파일을 수정한 후 다음 명령으로 계속 진행합니다:
git add <conflicted-file>
git rebase --continue
# feature-login 브랜치에서 merge
git checkout feature-login
git fetch origin
git merge origin/develop이 방법은 별도의 병합 커밋이 생성되며, develop 브랜치의 변경 사항을 그대로 반영합니다.
충돌이 발생하면 수동으로 해결하고 다음 명령으로 병합을 완료합니다:
git add <conflicted-file>
git commit # 병합 커밋 완료
develop 브랜치에서 가져온 최신 모듈을 활용하여 로그인 기능 개발을 이어갑니다.
# 프로필 모듈을 활용한 로그인 작업
echo "import profile" >> app/login.py
git add app/login.py
git commit -m "Use profile module in login"작업이 완료되면 featue-login 브랜치를 다시 develop 브랜치에 병합합니다.
git checkout develop
git pull origin develop
git merge feature-login
git push origin develop작업이 완료되었으므로 featue-login 브랜치를 삭제합니다.
git branch -d feature-login
git push origin --delete feature-loginmanifest용 repo라 main 브랜치만을 사용해서 git 작업이 이루어지는 상황입니다.
Alice와 Bob이 동시에 main 브랜치에서 작업하고 있습니다.
Alice가 먼저 main 브랜치에 변경 사항을 push하였습니다.
Bob은 Alice가 이미 작업을 push한 사실을 모른 채, main 브랜치에서 자신만의 작업을 하고 git add와 git commit을 수행한 후 git push origin main을 시도합니다.
Bob의 push가 실패합니다. 이는 Alice의 변경 사항이 원격에 먼저 적용되었기 때문에 발생합니다. 이 경우, Git은 충돌을 피하기 위해 Bob의 push를 거부합니다.
분산 버전 관리 시스템: 로컬과 원격 간 히스토리 불일치 시 push를 거부하여 충돌을 방지하고 동기화 상태 유지.
rebase를 통한 히스토리 정리: 병합 커밋 없이 히스토리를 깔끔하게 유지하여 순차적 히스토리 구성.
충돌 방지 및 해결 유도: pull --rebase 시 충돌이 발생하면 사용자가 직접 해결하도록 하여 협업의 명확성을 유지.
강제 푸시(--force-with-lease) 사용: 협업 시 다른 팀원의 변경 사항을 보호하며 강제 푸시를 허용.
브랜치 히스토리의 일관성 유지: rebase를 통해 변경 사항이 순차적으로 기록되도록 하여 명확한 히스토리 흐름 제공.
이 모든 설계 원리는 Git이 협업 환경에서의 안정성과 일관성을 유지하며 효율적인 버전 관리를 할 수 있도록 돕습니다.
Alice의 작업 후 main 브랜치에 push:
Alice는 main 브랜치에서 새로운 기능을 추가하고, 이를 원격 main 브랜치에 push합니다.
Bob의 작업 진행 및 push 시도:
Bob은 Alice의 작업이 반영된 줄 모르고, 자신의 작업을 main 브랜치에 추가하고 커밋한 후 git push origin main을 시도합니다.
push 실패: 원격의 main 브랜치가 더 앞서 있기 때문에, Git은 Bob의 push를 거부합니다.
git pull --rebase origin main을 사용해 Alice의 커밋을 가져오고 자신의 커밋 재배치:
Bob은 git pull --rebase origin main 명령을 사용하여 Alice의 커밋을 가져온 후 자신의 커밋을 최신 커밋 뒤로 재배치합니다.
이로 인해 Alice의 작업 이후에 Bob의 작업이 이어지는 형태로 히스토리가 재구성됩니다.
rebase 완료 후 push:
rebase가 완료된 후 git push origin main을 다시 시도하면, 이번에는 성공적으로 원격 main 브랜치에 Bob의 작업이 반영됩니다.
Alice는 main 브랜치에서 변경 사항을 커밋하고 원격 리포지토리에 push합니다.
git checkout main
# Alice가 새로운 기능 추가
echo "def new_feature(): pass" >> app/feature.py
git add app/feature.py
git commit -m "Add new feature"
git push origin main현재 main 브랜치 상태 (원격):
bash코드 복사* a1b2c3d - Add new feature (Alice's commit)
* e1c8f2b - Initial commitBob은 Alice의 push가 이루어진 사실을 모른 채 main 브랜치에서 다른 작업을 진행하고 커밋 후 push합니다.
git checkout main
# Bob이 새로운 기능 추가
echo "def product_list(): pass" >> app/product.py
git add app/product.py
git commit -m "Add product list feature"
git push origin mainpush 실패: Git은 원격의 main 브랜치가 로컬보다 앞서 있다고 판단하여 Bob의 push를 거부합니다.
git pull --rebase 사용Bob은 원격에 있는 Alice의 변경 사항을 가져오고, 자신의 커밋을 그 이후로 재배치하기 위해 git pull --rebase origin main을 실행합니다.
git pull --rebase origin mainrebase 후의 커밋 히스토리:
Git은 Alice의 커밋(a1b2c3d)을 먼저 적용하고, Bob의 커밋(7f4b3d9)을 그 뒤에 재배치합니다.
# rebase 후 main 브랜치 커밋 로그 (로컬)
* 7f4b3d9 - Add product list feature (Bob's commit)
* a1b2c3d - Add new feature (Alice's commit)
* e1c8f2b - Initial commitAlice의 커밋 뒤에 Bob의 커밋이 정렬되어, Bob의 변경 사항이 마치 Alice의 작업을 기반으로 작업된 것처럼 히스토리가 재배치됩니다.
rebase가 완료된 후, Bob은 다시 push 명령을 실행합니다.
git push origin main이번에는 성공적으로 push가 이루어집니다. 원격의 main 브랜치는 Bob의 변경 사항이 포함된 최신 상태로 업데이트됩니다.
현재 main 브랜치 상태 (원격):
* 7f4b3d9 - Add product list feature (Bob's commit)
* a1b2c3d - Add new feature (Alice's commit)
* e1c8f2b - Initial commit두 명의 개발자, Alice와 Bob이 각각 feature-alice 와 feature-bob 브랜치에서 작업하고 있습니다.
feature-alice 브랜치에서 Alice가 먼저 기능을 구현한 후, develop 브랜치에 병합하였습니다.
Bob은 여전히 자신의 브랜치에서 작업 중이며, 이제 Alice의 기능을 자신의 작업에 반영하고 싶어 rebase 를 사용하려고 합니다.
커밋 히스토리 재배치:
rebase는 커밋을 다른 브랜치의 최신 커밋 뒤로 재배치하여, 히스토리가 깔끔하고 직관적이게 만듭니다.
Bob의 브랜치 커밋이 Alice의 develop 브랜치 변경 사항 뒤에 위치하게 되어, 마치 Bob이 최신 상태에서 작업한 것처럼 히스토리를 재구성합니다.
히스토리 깔끔하게 유지:
merge를 사용하는 경우 병합 커밋이 생성되어 히스토리가 복잡해질 수 있습니다.
반면, rebase는 병합 커밋을 생성하지 않고 브랜치의 히스토리를 최신 상태로 정렬하여 직관적인 히스토리를 유지합니다.
충돌 해결의 필요성:
rebase 중 기존 커밋들 간에 충돌이 발생할 수 있습니다. 사용자는 이를 수동으로 해결해야 하며, git rebase --continue를 사용해 rebase를 계속 진행할 수 있습니다.
강제 푸시 필요성:
rebase는 기존 커밋 히스토리를 변경하기 때문에, rebase 완료 후 강제로 푸시(--force-with-lease)가 필요합니다. 이를 통해 원격 리포지토리의 히스토리와 일치시킵니다.
Alice는 feature-alice 브랜치에서 로그인 기능을 개발하고 develop에 병합했습니다.
Bob은 feature-bob 브랜치에서 상품 관리 기능을 개발 중이며, Alice의 변경 사항을 develop에서 가져오기 위해 rebase를 사용했습니다.
rebase 후 Bob의 커밋은 Alice의 커밋 뒤에 정렬되었으며, 이를 통해 Bob의 브랜치가 최신 변경 사항을 기반으로 작업한 것처럼 히스토리가 재구성되었습니다.
이 시나리오는 rebase의 원리인 커밋 재배치와 히스토리 정리를 명확히 보여줍니다.
이를 통해 협업 시 깔끔한 히스토리를 유지하면서도 다른 브랜치의 변경 사항을 반영할 수 있는 방법을 이해할 수 있습니다.
develop 브랜치에는 초기 프로젝트 설정만 있습니다.
Alice는 로그인 기능을 추가했고 이를 feature-alice 브랜치에서 develop 브랜치로 병합했습니다.
Bob은 상품 관리 기능을 추가하고 있으며, 아직 develop에 병합하지 않았습니다.
현재 커밋 히스토리:
# develop 브랜치 커밋 로그
* c5d8a2e - Add login feature (from Alice's branch)
* e1c8f2b - Initial commit
# feature-bob 브랜치 커밋 로그 (Alice가 작업한 커밋 이전 상태에서 시작됨)
* 7f4b3d9 - Add product list feature
* e1c8f2b - Initial commitAlice가 작업한 로그인 기능(c5d8a2e)이 develop 브랜치에 병합되었습니다.
Bob은 여전히 자신의 feature-bob 브랜치에서 작업 중입니다. 이 브랜치는 Alice가 병합하기 전의 develop을 기반으로 하고 있습니다.
Bob은 자신의 브랜치에 Alice가 추가한 로그인 기능을 반영하고 싶습니다. 이를 위해 rebase를 사용합니다.
git checkout feature-bob
git fetch origin
git rebase origin/developrebase 명령어를 사용하면, feature-bob 브랜치의 커밋(7f4b3d9)이 develop 브랜치의 최신 커밋(c5d8a2e) 뒤에 재배치됩니다.
즉, Bob의 변경 사항이 Alice의 작업 뒤에 추가되는 형태로 히스토리가 재구성됩니다.
rebase 후의 커밋 히스토리:
# feature-bob 브랜치 커밋 로그 (rebase 후)
* 7f4b3d9 - Add product list feature (from Bob's branch)
* c5d8a2e - Add login feature (from Alice's branch)
* e1c8f2b - Initial commitBob의 상품 관리 기능(7f4b3d9)이 Alice의 로그인 기능(c5d8a2e) 뒤에 위치하게 되며, 마치 feature-bob 브랜치가 Alice의 기능을 기반으로 작업한 것처럼 보이게 됩니다.
만약 Bob의 작업이 Alice의 로그인 기능과 충돌하는 부분이 있다면, rebase 중에 충돌이 발생할 수 있습니다.
Git은 충돌을 감지하고 Bob에게 충돌을 해결하도록 요구합니다.
# 충돌이 발생하면 충돌 파일을 수정한 후
git add <conflicted-file>
git rebase --continue모든 충돌을 해결한 후 git rebase --continue 명령어로 계속 진행하면 rebase가 완료됩니다.
rebase를 완료한 후, feature-bob 브랜치의 내용을 원격에 푸시합니다.
rebase는 기존 커밋의 히스토리를 변경하기 때문에, 강제 푸시가 필요합니다.
git push origin feature-bob --force-with-leaseAlice는 feature-login 브랜치에서 로그인 기능을 개발하고 있습니다.
개발 도중, 긴급한 버그가 발견되어 main 브랜치로 돌아가 버그를 수정해야 합니다.
현재 Alice는 로그인 기능을 작업 중이지만, 아직 작업을 완료하지 않았고 커밋도 하지 않았습니다.
Alice는 이 작업을 **임시로 저장(stash)**하고, 나중에 다시 이어서 작업하고 싶어합니다.
작업 상태의 임시 저장 및 복원
stash는 현재 브랜치에서 커밋하지 않은 작업 상태를 임시로 저장하고 워킹 디렉토리를 초기화할 수 있게 합니다.
이를 통해 개발 도중 다른 작업을 우선순위에 두어야 할 때, 현재 진행 중이던 작업을 잃지 않고 나중에 이어서 진행할 수 있습니다.
워킹 디렉토리 초기화
git stash를 실행하면, 워킹 디렉토리가 깨끗한 상태로 돌아갑니다.
즉, 변경 사항이 모두 stash라는 임시 저장소에 보관되고, 브랜치를 변경하거나 새로운 작업을 시작할 수 있는 깨끗한 환경이 제공됩니다.
필요 시 다시 복원
git stash pop 명령을 통해 임시로 저장된 변경 사항을 다시 가져올 수 있으며, 그 상태에서 작업을 이어나갈 수 있습니다. 이 과정에서 stash는 자동으로 삭제됩니다.
git stash apply 명령을 사용할 수도 있으며, 이 경우 stash는 그대로 유지되기 때문에, 여러 번 복원해야 하는 상황에 유리합니다.
복수의 stash 관리
Git은 여러 개의 stash를 관리할 수 있습니다.
Alice가 여러 작업을 stash에 임시 저장하면, git stash list 명령을 통해 모든 stash 목록을 확인할 수 있고, 특정 stash를 선택하여 복원할 수 있습니다.
예를 들어, git stash apply stash@{1}을 사용하면 두 번째로 저장된 stash를 복원할 수 있습니다.
Alice는 feature-login 브랜치에서 작업 중, 긴급한 버그 수정 요청을 받아 stash를 사용해 작업을 임시로 저장했습니다.
이후 main 브랜치로 돌아가 버그를 수정한 뒤, 다시 feature-login 브랜치로 돌아와 stash에 저장된 변경 사항을 복원하여 로그인 기능 작업을 이어갔습니다.
이 시나리오는 stash의 원리인 작업 상태의 임시 저장과 복원, 브랜치 간 자유로운 이동을 잘 설명해줍니다.
Alice는 로그인 기능을 개발하면서 몇몇 파일을 수정하고 있었지만, 아직 변경 사항을 커밋하지 않은 상태입니다.
git checkout -b feature-login
# 로그인 관련 코드 작성
echo "def login(): pass" >> app/login.py현재 app/login.py 파일이 수정되었지만, 커밋하지 않은 상태입니다.
git status
# 출력:
# Modified: app/login.py이때 긴급한 버그를 수정하라는 요청을 받았습니다.
이를 위해 Alice는 featue-login 브랜치를 중단하고, main 브랜치로 돌아가 버그 수정 작업을 해야 합니다.
하지만 로그인 기능 작업을 중단하지 않고 나중에 이어서 진행하고 싶기 때문에, 현재 작업 상태를 stash를 통해 임시로 저장할 계획입니다.
Alice는 변경된 내용을 stash에 임시로 저장합니다.
git stash결과: Alice의 app/login.py 파일의 변경 내용이 임시로 저장되고, 워킹 디렉토리는 깨끗한 상태가 됩니다.
git status
# 출력:
# Your branch is up to date with 'origin/feature-login'.
# nothing to commit, working tree cleanAlice는 main 브랜치로 돌아가 버그 수정 작업을 합니다.
git checkout main
# 긴급한 버그 수정 작업
echo "def fix_critical_bug(): pass" >> app/fix.py
git add app/fix.py
git commit -m "Fix critical bug"
git push origin main버그 수정을 완료한 후, Alice는 로그인 기능을 계속 개발하기 위해 feature-login 브랜치로 돌아갑니다.
그리고 stash에 저장된 작업을 복원합니다.
git checkout feature-login
git stash pop결과: stash에 임시 저장된 변경 사항이 다시 워킹 디렉토리에 적용되며, Alice는 로그인 기능 개발을 계속할 수 있게 됩니다.
git status
# 출력:
# Modified: app/login.pyAlice는 이어서 로그인 기능을 추가로 개발하고 커밋합니다.
echo "def validate_login(): pass" >> app/login.py
git add app/login.py
git commit -m "Add login validation feature"
git push origin feature-login사용자는 featue-login 브랜치에서 새로운 로그인 기능을 개발하고 있으며, 작업을 마친 후 이를 develop 브랜치에 병합하려고 합니다.
하지만, develop 브랜치에서 어떤 변경 사항도 발생하지 않았고, Git은 이를 fast-forward 방식으로 병합하려고 합니다.
처음 사용자라면 Git이 병합 커밋을 생성하지 않고 단순히 브랜치를 빠르게 병합해버리는 동작에 혼란을 겪을 수 있습니다.
또, 사용자가 명시적으로 병합 커밋을 만들고 싶을 경우 어떻게 해야 하는지 알지 못할 수 있습니다.
fast-forward 병합:
Git은 두 브랜치가 별도의 커밋 없이 동일한 커밋을 공유하는 경우 fast-forward 방식으로 병합합니다.
이 방식은 단순히 브랜치의 헤드를 다른 브랜치로 이동시키는 것으로, 병합 커밋이 생성되지 않고 히스토리도 깔끔하게 유지됩니다.
병합 커밋 생성 (non fast-forward merge):
사용자는 히스토리를 명확하게 남기고 싶을 때 --no-ff 옵션을 사용하여 병합 커밋을 생성할 수 있습니다.
이는 브랜치 간의 병합이 명시적으로 기록되며, 나중에 히스토리를 추적하기 용이합니다.
사용자 혼란:
처음 사용자들은 병합할 때 Git이 자동으로 fast-forward를 수행한다는 점을 모르고, 병합 커밋이 생성되지 않으면 이를 의아하게 여길 수 있습니다.
이 점을 잘 이해하고 있다면 브랜치 관리 및 병합 전략을 더 유연하게 설계할 수 있습니다.
feature-login 브랜치 생성 후 작업: 로그인 기능을 구현하고, 이를 develop 브랜치에 병합.
Git의 기본 동작: fast-forward 병합: develop 브랜치가 변경되지 않았으므로 Git은 fast-forward 병합을 수행하여 별도의 병합 커밋을 만들지 않음.
병합 커밋을 명시적으로 생성: --no-ff 옵션을 사용해 병합 커밋을 생성하여 히스토리를 명확하게 기록.
fast-forward 병합의 이해: fast-forward 병합은 별도의 병합 커밋을 생성하지 않고, 히스토리를 단순화하는 기본 Git 동작임을 이해.
develop 브랜치에서 새로운 기능을 개발하기 위해 featue-login 브랜치를 생성하고 로그인 기능을 구현합니다.
git checkout -b feature-login
echo "def login(): pass" > app/login.py
git add app/login.py
git commit -m "Add login functionality"이제 작업이 완료되었으므로 develop 브랜치로 돌아가서 featue-login 브랜치를 병합하려고 합니다.
하지만 develop 브랜치에서 다른 작업이 없었기 때문에 Git은 fast-forward 병합을 수행합니다.
git checkout develop
git merge feature-login결과: Git은 develop 브랜치가 변경되지 않았으므로 fast-forward 병합을 수행합니다.
이는 develop 브랜치가 featue-login 브랜치의 커밋을 단순히 가리키게 만드는 방식입니다.
git log --oneline
# 출력:
# <feature-login의 커밋들이 develop의 최신 커밋으로 표시됨, 병합 커밋 없음>사용자가 혼란스러워할 수 있는 점: Git이 병합 커밋을 생성하지 않고, 단순히 feature-login 브랜치의 커밋을 develop 브랜치 위에 추가해버린다는 점.
즉, fast-forward 방식은 별도의 병합 커밋을 만들지 않으며, 이로 인해 병합 히스토리가 남지 않습니다.
사용자는 병합 히스토리가 명확하게 남기를 원할 수 있습니다. 이럴 경우, Git에게 fast-forward 병합을 하지 않고 병합 커밋을 생성하라는 명령을 내릴 수 있습니다.
git checkout develop
git merge --no-ff feature-login결과: Git은 병합 커밋을 생성하여, featue-login 브랜치가 develop 브랜치로 병합된 히스토리를 남깁니다.
git log --oneline
# 출력:
# <병합 커밋이 추가되고, feature-login의 작업 내용이 병합된 것이 보임>
# Merge branch 'feature-login'사용자가 알아야 할 점: --no-ff 옵션을 사용하면 Git은 반드시 병합 커밋을 생성하며, fast-forward 병합을 방지합니다. 이로 인해 두 브랜치의 병합 히스토리가 명확하게 남습니다.
병합하려는 브랜치가 한 번도 다른 브랜치와 병합되지 않은 경우, Git은 기본적으로 fast-forward 방식으로 병합을 처리합니다.
fast-forward 병합은 브랜치 간에 충돌이 없을 때 주로 발생하며, Git은 별도의 병합 커밋 없이 브랜치의 헤드를 옮깁니다.
소프트웨어 개발 분야에서 협업은 이미 필수적인 요소로 자리 잡았습니다.
대규모 프로젝트의 복잡성과 시간 제약으로 인해 개인이 전체 개발을 담당하는 것은 현실적으로 불가능하며, 팀 단위의 협력이 프로젝트 완성의 핵심이 되었습니다.
Git은 개발자 협업의 표준 도구로 자리매김하여 일상적으로 사용되고 있습니다.
이 버전 관리 시스템은 코드 변경 사항을 추적하고 여러 개발자의 작업을 효과적으로 통합하는 데 필수적입니다.
일상적인 Git 사용 과정에서 발생할 수 있는 다양한 상황과 문제에 대한 이해는 매우 중요합니다.
이러한 시나리오별 케이스를 숙지하고 적절히 대응할 수 있는 능력은 현대 개발 환경에서 핵심 역량으로 간주됩니다.
개발 현장에서 자주 발생하는 Git 관련 시나리오를 사전에 학습하고 이해하는 것은 프로젝트의 효율성과 생산성을 크게 향상시킬 수 있습니다.
이는 문제 발생 시 신속한 대응을 가능케 하며, 팀 전체의 작업 흐름을 원활하게 만듭니다.
이러한 준비를 통해 개발자들은 기술적 문제 해결에 더 집중할 수 있게 되어, 궁극적으로 프로젝트의 성공 가능성을 높이는 데 기여할 것입니다.
케이스: 특정 파일을 실수로 커밋했는데, 이를 수정하고 싶음.
해결방안:
git reset --soft HEAD~1 # 마지막 커밋을 되돌림 (변경 내용은 남겨둠)
git restore <filename> # 잘못 커밋된 파일 복원
git commit --amend # 커밋 수정
케이스: 작업을 잘못된 브랜치에서 수행했음.
해결방안:
git checkout -b new-branch # 새로운 브랜치를 생성
git checkout original-branch
git reset --hard origin/original-branch # 원래 브랜치 상태로 복원
케이스: 로컬에서 커밋한 후, pull을 시도했더니 충돌이 발생.
해결방안:
git fetch origin
git rebase origin/main # 원격 브랜치를 로컬 커밋 위에 적용
# 충돌 해결 후
git rebase --continue
git push origin main
케이스: 실수로 git push --force로 인해 원격 리포지토리의 기록이 잘못됨.
해결방안:
다른 팀원과 협력해 강제로 푸시된 이전 커밋을 복구합니다. 이전 커밋 해시를 찾아 그 상태로 되돌립니다:
git reflog # 로컬에서 커밋 해시 찾기
git reset --hard <commit-hash>
git push --force
케이스: 브랜치를 병합하려는데 충돌이 발생.
해결방안:
git merge <branch-name>
# 충돌 발생 시 충돌 파일을 수정한 후
git add <conflicted-files>
git commit # 충돌 해결 후 병합 완료
케이스: 실수로 로컬 브랜치를 삭제했으나 복구하고 싶음.
해결방안:
git reflog # 삭제된 브랜치의 HEAD를 찾음
git checkout -b <branch-name> <commit-hash>
케이스: 커밋 메시지를 잘못 입력했거나 수정이 필요함.
해결방안:
git commit --amend -m "새로운 커밋 메시지"
케이스: 푸시하지 않은 상태에서 잘못된 커밋을 되돌리고 싶음.
해결방안:
git reset --soft HEAD~1 # 마지막 커밋을 되돌림 (변경 내용은 남아 있음)
케이스: 원격에 푸시한 커밋을 되돌리고 싶음.
해결방안:
git revert <commit-hash> # 특정 커밋을 취소하는 새로운 커밋 생성
git push origin main
케이스: 현재 작업을 임시로 저장하고 다른 작업을 해야 할 경우.
해결방안:
git stash # 변경사항 임시 저장
git stash pop # 저장된 변경 사항 다시 적용
케이스: git rebase 중 충돌이 발생한 경우.
해결방안:
충돌된 파일을 수정한 후:
git add <conflicted-files>
git rebase --continue # rebase 계속 진행
케이스: 원격 브랜치를 삭제하고 싶음.
해결방안:
git push origin --delete <branch-name>
케이스: 로컬 브랜치를 삭제했으나 원격에는 남아 있는 경우.
해결방안:
git checkout -b <branch-name> origin/<branch-name>
케이스: 특정 커밋만 다른 브랜치에 적용하고 싶음.
해결방안:
git cherry-pick <commit-hash>
케이스: 리포지토리의 원격 URL이 변경되어 새로운 URL로 바꾸고 싶음.
해결방안:
git remote set-url origin <new-url>
DEVOTEE를 활성화 시키면
지금 작성한 댓글에 AI가 댓글을 달아줍니다.