Git에서 'error: Your local changes would be overwritten by merge' 오류 해결하기

GitBeginner
지금 연습하기

소개

Git은 개발자가 코드를 관리하고 효과적으로 협업할 수 있도록 돕는 강력한 버전 관리 시스템입니다. 하지만 Git을 처음 접할 때 혼란스러울 수 있는 오류 메시지를 마주할 때가 있습니다. 그중 흔한 오류가 바로 "Your local changes would be overwritten by merge"입니다. 이 오류는 커밋되지 않은 로컬 변경 사항이 병합 과정에서 덮어쓰여질 위험이 있어 Git이 안전하게 병합을 수행할 수 없을 때 발생합니다.

이번 실습에서는 이 오류가 발생하는 원인을 파악하고, 이를 해결하는 방법과 향후 Git 워크플로우에서 이러한 오류를 방지하기 위한 전략을 구현하는 방법을 배웁니다.

Git 워크플로우 이해하기

이 단계에서는 기본적인 Git 워크플로우를 복습하고, 오류를 재현하기 위해 준비된 저장소를 확인합니다.

Git이란 무엇인가

Git은 여러 개발자가 서로의 작업에 방해받지 않고 동일한 코드베이스에서 작업할 수 있도록 지원하는 분산 버전 관리 시스템입니다. 파일의 변경 이력을 추적하므로 필요할 때 이전 버전으로 되돌릴 수 있습니다.

기본적인 Git 워크플로우

일반적인 Git 워크플로우는 다음과 같은 단계로 구성됩니다:

  • 작업 디렉토리에서 파일 수정
  • git add를 사용하여 변경 사항 스테이징
  • git commit을 사용하여 변경 사항 커밋
  • git push를 사용하여 원격 저장소로 변경 사항 푸시
  • git pull을 사용하여 원격 저장소에서 변경 사항 가져오기

시작하기

먼저 프로젝트 디렉토리로 이동합니다:

cd /home/labex/project/git-merge-demo

저장소의 상태를 확인합니다:

git status

다음과 유사한 출력이 나타나야 합니다:

On branch main
nothing to commit, working tree clean

이제 어떤 브랜치가 있는지 확인합니다:

git branch

다음과 같이 표시됩니다:

  development
* main

별표(*)는 현재 main 브랜치에 있음을 나타냅니다. 저장소의 파일을 살펴보겠습니다:

ls -la

다음과 같이 표시됩니다:

total 20
drwxr-xr-x 3 labex labex 4096 ... .
drwxr-xr-x 3 labex labex 4096 ... ..
drwxr-xr-x 8 labex labex 4096 ... .git
-rw-r--r-- 1 labex labex   24 ... README.md
-rw-r--r-- 1 labex labex   27 ... script.js
-rw-r--r-- 1 labex labex   25 ... styles.css

파일 내용을 확인합니다:

cat README.md
cat script.js
cat styles.css

좋습니다! 이제 저장소의 구조를 이해했습니다. 다음 단계에서는 "local changes would be overwritten by merge" 오류를 발생시키는 시나리오를 만들어 보겠습니다.

병합 충돌 시나리오 만들기

이 단계에서는 "Your local changes would be overwritten by merge" 오류가 발생하는 시나리오를 만듭니다. 이를 통해 오류가 발생하는 이유를 이해할 수 있습니다.

development 브랜치에서 변경 사항 만들기

먼저 development 브랜치로 전환하고 변경 사항을 만듭니다:

git checkout development

다음과 같이 표시됩니다:

Switched to branch 'development'

이제 script.js 파일을 수정합니다:

echo "console.log('Hello from development branch!');" > script.js

이 변경 사항을 커밋합니다:

git add script.js
git commit -m "Update script.js on development branch"

다음과 같이 표시됩니다:

[development xxxxxxx] Update script.js on development branch
 1 file changed, 1 insertion(+), 1 deletion(-)

main 브랜치에서 커밋되지 않은 변경 사항 만들기

이제 main 브랜치로 다시 전환합니다:

git checkout main

main 브랜치에 있는지 확인합니다:

git branch

다음과 같이 표시됩니다:

  development
* main

이제 main 브랜치에서 동일한 파일(script.js)을 수정하되, 이번에는 변경 사항을 커밋하지 않습니다:

echo "console.log('Hello from main branch!');" > script.js

병합 충돌 오류 트리거하기

작업 디렉토리에 커밋되지 않은 변경 사항이 있는 상태에서 development 브랜치를 main 브랜치로 병합해 봅니다:

git merge development

다음과 유사한 오류 메시지가 나타납니다:

error: Your local changes to the following files would be overwritten by merge:
        script.js
Please commit your changes or stash them before you merge.
Aborting

축하합니다! "Your local changes would be overwritten by merge" 오류를 발생시키는 시나리오를 성공적으로 만들었습니다.

이 오류가 발생하는 이유는 다음과 같습니다:

  • development 브랜치에서 script.js를 수정하고 커밋했습니다.
  • main 브랜치에서도 script.js를 수정했지만 커밋하지 않았습니다.
  • 병합을 시도할 때, Git은 병합 작업으로 인해 커밋되지 않은 변경 사항이 덮어쓰여질 것임을 감지했습니다.

다음 단계에서는 이 오류를 해결하는 방법을 배웁니다.

Git Stash를 사용하여 병합 충돌 해결하기

이 단계에서는 git stash를 사용하여 "Your local changes would be overwritten by merge" 오류를 해결합니다. 이 명령은 커밋되지 않은 변경 사항을 임시로 저장하여 나중에 다시 적용할 수 있게 해줍니다.

Git Stash란 무엇인가

git stash는 커밋되지 않은 변경 사항(스테이징된 것과 스테이징되지 않은 것 모두)을 가져와 나중에 사용할 수 있도록 저장하고, 작업 복사본의 변경 사항을 되돌리는 Git 명령입니다.

Git Stash를 사용하여 오류 해결하기

git stash를 사용하여 커밋되지 않은 변경 사항을 저장합니다:

git stash

다음과 유사한 출력이 나타납니다:

Saved working directory and index state WIP on main: xxxxxxx Initial commit

이 메시지는 변경 사항이 저장되었음을 확인해 줍니다. 이제 작업 디렉토리가 깨끗한지 확인합니다:

git status

다음과 같이 표시됩니다:

On branch main
nothing to commit, working tree clean

이제 script.js의 내용을 확인합니다:

cat script.js

원래 내용이 표시됩니다:

console.log('Hello, Git!');

development 브랜치 병합하기

작업 디렉토리가 깨끗해졌으므로 이제 안전하게 development 브랜치를 병합할 수 있습니다:

git merge development

다음과 같이 표시됩니다:

Updating xxxxxxx..xxxxxxx
Fast-forward
 script.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

script.js의 내용을 다시 확인합니다:

cat script.js

이제 다음과 같이 표시됩니다:

console.log('Hello from development branch!');

스태시된 변경 사항 가져오기

development 브랜치를 성공적으로 병합했으므로, 이제 스태시해 두었던 변경 사항을 가져옵니다:

git stash list

다음과 같이 표시됩니다:

stash@{0}: WIP on main: xxxxxxx Initial commit

하나의 스태시가 저장되어 있음을 알 수 있습니다. 이를 적용합니다:

git stash apply

충돌 메시지가 나타날 수 있습니다:

Auto-merging script.js
CONFLICT (content): Merge conflict in script.js

Git이 병합된 파일 위에 스태시된 변경 사항을 적용하려고 시도하기 때문에 발생하는 정상적인 현상입니다. script.js의 내용을 확인합니다:

cat script.js

다음과 같은 내용이 보일 것입니다:

<<<<<<< Updated upstream
console.log('Hello from development branch!');
=======
console.log('Hello from main branch!');
>>>>>>> Stashed changes

이는 수동으로 해결해야 하는 병합 충돌입니다. nano를 사용하여 파일을 엽니다:

nano script.js

파일을 편집하여 두 변경 사항을 모두 유지하거나 하나를 선택합니다:

// 두 변경 사항 모두 유지
console.log("Hello from development branch!");
console.log("Hello from main branch!");

Ctrl+O를 누른 후 Enter를 눌러 파일을 저장하고, Ctrl+X를 눌러 nano를 종료합니다.

이제 해결된 충돌을 스테이징하고 커밋합니다:

git add script.js
git commit -m "Merge development branch and resolve conflict"

축하합니다! git stash를 사용하여 "Your local changes would be overwritten by merge" 오류를 성공적으로 해결했습니다.

병합 오류를 해결하는 다른 방법들

이전 단계에서는 git stash를 사용하여 오류를 해결했습니다. 이 단계에서는 이 상황을 처리하는 다른 접근 방식들을 살펴봅니다.

방법 1: 병합 전 변경 사항 커밋하기

"Your local changes would be overwritten by merge" 오류를 피하는 가장 간단한 방법 중 하나는 병합 작업을 수행하기 전에 변경 사항을 커밋하는 것입니다.

이를 보여주기 위해 다른 시나리오를 만들어 보겠습니다. 먼저 styles.css 파일을 수정합니다:

echo "body { background-color: #f0f0f0; }" > styles.css

이제 변경 사항을 스태시하는 대신 커밋합니다:

git add styles.css
git commit -m "Update styles.css on main branch"

다음과 같이 표시됩니다:

[main xxxxxxx] Update styles.css on main branch
 1 file changed, 1 insertion(+), 1 deletion(-)

이제 development 브랜치로 전환하여 같은 파일을 수정하고 커밋합니다:

git checkout development
echo "body { background-color: #e0e0e0; }" > styles.css
git add styles.css
git commit -m "Update styles.css on development branch"

이제 main 브랜치로 다시 전환합니다:

git checkout main

이제 병합을 시도하면 변경 사항을 이미 커밋했기 때문에 "local changes would be overwritten" 오류가 발생하지 않습니다. 하지만 병합 충돌이 발생할 수는 있습니다:

git merge development

다음과 같이 표시될 수 있습니다:

Auto-merging styles.css
CONFLICT (content): Merge conflict in styles.css
Automatic merge failed; fix conflicts and then commit the result.

이는 두 브랜치 모두에서 파일의 동일한 부분을 커밋했을 때 발생하는 다른 종류의 충돌입니다. 이 충돌을 해결해 보겠습니다:

cat styles.css

다음과 같이 표시됩니다:

<<<<<<< HEAD
body { background-color: #f0f0f0; }
=======
body { background-color: #e0e0e0; }
>>>>>>> development

파일을 편집하여 충돌을 해결합니다:

nano styles.css

내용을 다음과 같이 변경합니다:

body {
  background-color: #f5f5f5;
}

파일을 저장하고 해결된 충돌을 커밋합니다:

git add styles.css
git commit -m "Resolve merge conflict in styles.css"

방법 2: Git Checkout을 사용하여 로컬 변경 사항 폐기하기

또 다른 접근 방식은 더 이상 필요하지 않은 로컬 변경 사항을 단순히 폐기하는 것입니다:

## README.md에 변경 사항 만들기
echo "## Updated README" > README.md

## 상태 확인
git status

다음과 같이 표시됩니다:

On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")

이 변경 사항이 필요 없다고 판단되면 폐기할 수 있습니다:

git checkout -- README.md

상태를 다시 확인합니다:

git status

다음과 같이 표시됩니다:

On branch main
nothing to commit, working tree clean

README.md의 내용이 원래 상태로 복원되었습니다:

cat README.md

이러한 대안 방법들은 특정 상황과 필요에 따라 "Your local changes would be overwritten by merge" 오류를 처리할 수 있는 다양한 옵션을 제공합니다.

병합 충돌 방지를 위한 모범 사례

이 단계에서는 "Your local changes would be overwritten by merge" 오류를 방지하는 데 도움이 되는 습관을 연습합니다.

1. 변경 사항을 자주 커밋하기

작고 잦은 커밋은 병합 충돌 가능성을 줄이는 데 도움이 됩니다:

## 작은 변경 사항 만들기
echo "// Add a comment" >> script.js

## 변경 사항 커밋
git add script.js
git commit -m "Add a comment to script.js"

2. 푸시하기 전에 풀(Pull)하기

변경 사항을 푸시하기 전에 항상 원격 저장소에서 변경 사항을 가져오세요:

## 실제 시나리오에서는 다음과 같이 합니다:
## git pull origin main
## 로컬에서 작업 중이므로 development로 전환했다가 다시 돌아오는 것으로 시뮬레이션합니다
git checkout development
git checkout main

3. 기능 브랜치(Feature Branch) 사용하기

각 기능이나 버그 수정마다 새로운 브랜치를 만드세요:

## 새로운 기능 브랜치 만들기
git checkout -b feature-navbar

## 변경 사항 만들기
echo "/* Navigation bar styles */" >> styles.css

## 변경 사항 커밋
git add styles.css
git commit -m "Add navigation bar styles"

main 브랜치로 돌아갑니다:

git checkout main

4. 정기적으로 main 브랜치와 병합 또는 리베이스(Rebase)하기

기능 브랜치를 main 브랜치와 최신 상태로 유지하세요:

## 기능 브랜치로 전환
git checkout feature-navbar

## 실제 시나리오에서는 main과 병합하거나 리베이스합니다:
## git merge main
## 또는
## git rebase main

## 데모를 위해 시뮬레이션합니다
echo "/* More styles */" >> styles.css
git add styles.css
git commit -m "Add more styles"

## main으로 돌아가기
git checkout main

5. Git Status를 자주 사용하기

병합이나 풀과 같은 작업을 수행하기 전에 항상 저장소의 상태를 확인하세요:

git status

다음과 같이 표시됩니다:

On branch main
nothing to commit, working tree clean

6. 팀과 소통하기

실습에서 직접 보여줄 수는 없지만, 소통은 충돌을 피하는 핵심입니다. 팀원들이 어떤 파일을 작업 중인지 공유하여 동일한 파일에 대한 동시 수정을 방지하세요.

7. 복잡한 병합에는 GUI 도구 사용하기

복잡한 병합 충돌의 경우 GUI 도구 사용을 고려하세요:

## 사용 가능한 GUI 병합 도구 목록 확인 (실제 환경에서)
## git mergetool --tool-help

## 여기서는 일반적으로 사용 가능한 병합 도구를 표시합니다
echo "Common merge tools: meld, kdiff3, vimdiff, vscode"

이러한 모범 사례를 따르면 Git 워크플로우에서 병합 충돌과 "Your local changes would be overwritten by merge" 오류 발생을 크게 줄일 수 있습니다.

요약

이번 실습에서는 Git에서 "Your local changes would be overwritten by merge" 오류를 해결하는 방법을 배웠습니다. 이는 작업 디렉토리에 커밋되지 않은 수정 사항이 있는 상태에서 변경 사항을 병합하거나 가져오려 할 때 발생하는 흔한 문제입니다.

이번 실습에서 다룬 핵심 내용은 다음과 같습니다:

  1. 서로 다른 브랜치에서 동일한 파일을 수정하여 "Your local changes would be overwritten by merge" 오류를 유발하는 시나리오를 만들었습니다.

  2. 이 오류를 해결하는 여러 방법을 배웠습니다:

    • git stash를 사용하여 변경 사항을 임시로 저장
    • 병합 전 변경 사항 커밋
    • git checkout을 사용하여 원치 않는 로컬 변경 사항 폐기
  3. 병합 충돌을 방지하기 위한 모범 사례를 살펴보았습니다:

    • 작고 잦은 커밋 수행
    • 푸시 전 풀(Pull) 수행
    • 기능 브랜치 사용
    • 정기적으로 main 브랜치와 병합 또는 리베이스
    • git status를 자주 사용
    • 팀과 소통
    • 복잡한 병합에는 GUI 도구 사용

이러한 개념과 기술을 이해함으로써 Git 워크플로우를 더 효율적으로 관리하고 병합 충돌로 이어지는 일반적인 실수를 피할 수 있습니다.

Git은 버전 관리와 협업을 위한 강력한 도구이지만, 충돌을 피하기 위해서는 변경 사항을 신중하게 관리해야 한다는 점을 기억하세요. 이번 실습에서 배운 기술들은 다른 사람들과 효과적으로 협업하면서 코드베이스의 무결성을 유지하는 데 도움이 될 것입니다.