AI 코딩 일반

AI 수정이 꼬였을 때, 내 코드까지 날리지 않고 되돌리는 법

DevPilot 2026. 9. 22. 13:48

AI가 고친 배송비 계산은 버리고, 내가 바꾼 화면 제목은 남기고 싶습니다. 그런데 둘 다 같은 파일 안에 있다면요. 파일 전체를 되돌리는 버튼을 누르기 전에, 무엇을 기준으로 복원하는지부터 확인해야 합니다.

먼저 답부터: 이미 섞인 변경은 diff를 읽고 골라 되돌려야 합니다. 다음 작업부터는 내 변경을 커밋해 기준점을 남기세요. Git이 미커밋 변경을 보고 “이 줄은 사람, 저 줄은 AI”라고 구분해 주지는 않습니다.

2026-09-21 작성. 별도 임시 Git 저장소에서 실행한 실습입니다. 사람의 수정과 AI의 수정을 코드로 만들어 비교했으며, 특정 AI 도구의 실제 출력이나 운영 프로젝트의 사고 기록은 아닙니다. 실행 환경: macOS, Git 2.50.1(Apple Git-155), Python 3.12.4.

제목과 배송비를 같은 파일에서 바꿔봤습니다

예제의 출발점은 제목이 ‘주문 요약’이고, 주문액이 50,000원 미만이면 배송비 3,000원을 반환하는 파일입니다. 여기서 제목을 ‘결제 내역’으로 바꾼 것을 내 작업, 배송비를 무조건 0으로 만든 것을 AI 작업이라고 가정하겠습니다.

두 변경 모두 아직 커밋하거나 스테이징하지 않았습니다. 스테이징은 다음 커밋에 담을 내용을 준비하는 단계로, 보통 git add로 진행해요. git diff -- app.py로 보면 다음처럼 두 덩어리가 나옵니다.

diff --git a/app.py b/app.py
index 489e076..f2007d5 100644
--- a/app.py
+++ b/app.py
@@ -1,4 +1,4 @@
-TITLE = "주문 요약"
+TITLE = "결제 내역"
 
 
 def subtotal(items):
@@ -12,7 +12,7 @@ def currency_label():
 def shipping_fee(subtotal_amount):
     if subtotal_amount >= 50000:
         return 0
-    return 3000
+    return 0
 
 
 if __name__ == "__main__":

제목은 남겨야 하고, 마지막 return 0만 return 3000으로 돌아가야 합니다. 이 상태에서 파일 전체를 복원하면 두 변경이 함께 사라져요. Git 입장에서는 둘 다 기준 버전 이후에 달라진 내용이기 때문입니다.

직접 따라 할 예제 만들기 — 새 임시 폴더에서 실행

아래 블록 전체를 macOS나 Linux 셸에 붙여 넣으면 작은 예제 저장소를 만듭니다. 실제 프로젝트 파일을 준비할 필요는 없습니다. Windows에서는 WSL 등 호환 셸이 필요합니다.

# macOS / Linux 셸용. 현재 프로젝트 대신 새 임시 폴더에서 실습합니다.
demo_root=$(mktemp -d "${TMPDIR:-/tmp}/devpilot-git-undo.XXXXXX")
cd "$demo_root" || exit 1
git init -q
git config user.name "DevPilot Example"
git config user.email "example@example.invalid"
cat > app.py <<'PYFILE'
TITLE = "주문 요약"


def subtotal(items):
    return sum(items)


def currency_label():
    return "KRW"


def shipping_fee(subtotal_amount):
    if subtotal_amount >= 50000:
        return 0
    return 3000


if __name__ == "__main__":
    print(TITLE)
    print(f"shipping(10000)={shipping_fee(10000)}")
PYFILE
git add -- app.py
git -c core.hooksPath=/dev/null -c commit.gpgsign=false commit -qm "initial example"
python3 - <<'PYEDIT'
from pathlib import Path
p = Path("app.py")
s = p.read_text().replace("주문 요약", "결제 내역")
s = s.replace("return 3000", "return 0")
p.write_text(s)
PYEDIT
# 여기까지는 실습용으로 두 변경을 함께 만든 것입니다.
git diff -- app.py

이미 섞였다면, 현재 파일부터 남겨둡니다

복원 전에 AI와 자동 저장 등 파일을 바꾸는 작업을 멈추고, 현재 내용을 별도 위치에 복사해 두세요. 이 예제에서는 app.py 하나만 대상이므로 다음처럼 보관합니다. 실제 작업에서 여러 파일을 고쳤다면 그 파일들도 함께 보관해야 합니다.

rescue_dir=$(mktemp -d "${TMPDIR:-/tmp}/devpilot-rescue.XXXXXX")
cp app.py "$rescue_dir/app.py"

git status --short
git diff -- app.py
git diff --cached -- app.py

첫 번째 diff는 스테이징 영역과 현재 파일의 차이, --cached는 마지막 커밋과 스테이징 영역의 차이를 보여줍니다. 둘 중 하나만 보면 이미 준비된 변경을 놓칠 수 있어요. 각각의 비교 기준은 Git diff 공식 문서에서도 확인할 수 있습니다.

이 실습에서는 git status --short에 M app.py가 나오고, git diff --cached는 비어 있습니다. 앞쪽 상태가 비어 있는 것은 스테이징된 변경이 없다는 뜻입니다.

파일 전체 대신, 버릴 변경 덩어리를 고릅니다

현재 예제에서 사용할 명령은 다음과 같습니다. -p는 변경 덩어리(hunk)를 하나씩 보여주고 선택하게 합니다.

git restore -p -- app.py

첫 번째로 제목 변경이 나옵니다. 질문은 이 변경을 버릴지 묻는 것이므로, 남길 내 작업에는 n을 입력합니다. 두 번째 배송비 변경은 버리고 싶으니 y입니다.

(1/2) Discard this hunk from worktree ...? n
(2/2) Discard this hunk from worktree ...? y

위 두 줄은 긴 선택지 목록을 생략하고 입력할 답을 표시한 안내입니다. 실제 화면의 diff를 보고 선택하세요. 다른 파일에서도 무조건 n, y 순서로 누르라는 뜻은 아닙니다.

복원 뒤 git diff -- app.py를 다시 실행하면 제목 변경만 남습니다.

diff --git a/app.py b/app.py
index 489e076..495bbc4 100644
--- a/app.py
+++ b/app.py
@@ -1,4 +1,4 @@
-TITLE = "주문 요약"
+TITLE = "결제 내역"
 
 
 def subtotal(items):

python3 app.py로 확인한 출력입니다. 제목은 ‘결제 내역’으로 유지되고 배송비는 3,000원으로 돌아왔습니다.

결제 내역
shipping(10000)=3000

변경 2개 중 1개만 되돌린 셈입니다. 다만 이것은 줄이 떨어져 있어 선택하기 쉬운 예제예요. AI가 내가 고친 바로 그 줄을 다시 고쳤다면 어떻게 될까요?

같은 줄에 수정이 겹쳤다면 Git의 diff만으로 중간에 내가 적었던 내용을 알아낼 수는 없습니다. 별도 커밋, 에디터의 로컬 기록, 파일 사본 등과 대조해 필요한 내용을 직접 재구성해야 합니다.

서로 다른 줄인데 한 덩어리로 묶였다면 선택지의 s로 나눌 수 있는 경우도 있습니다. 나누기 어렵거나 내용이 확실하지 않다면 q로 멈추고 편집기에서 필요한 줄만 고치세요. q가 앞서 적용한 복원을 취소해 주는 것은 아닙니다.

git add를 했다면, 같은 명령의 결과도 달라집니다

git restore -- app.py는 기본적으로 스테이징 영역의 내용을 현재 파일에 가져옵니다. 무조건 마지막 커밋으로 돌아가는 명령으로 외우면 헷갈려요. 공식 restore 문서에서도 복원할 위치와 기준을 구분합니다.

이번 검증에서는 내 제목 수정을 커밋한 뒤 AI 배송비 변경을 git add해 봤습니다. 이 상태에서 일반 restore를 실행하니 배송비 0이 그대로였어요. 스테이징 영역에도 이미 같은 값이 들어 있었기 때문입니다.

4a. AI edit staged, plain restore: shipping=0 (unchanged)
4b. unstage only: shipping=0 (file unchanged)
4c. restore worktree from checkpoint HEAD: shipping=3000

위 실험은 아래 순서로 진행했습니다. HEAD에 내 작업이 저장돼 있고, 그 뒤에는 버릴 AI 변경만 있다는 조건입니다. 처음의 두 변경이 섞인 예제에 그대로 적용하면 안 됩니다.

# 스테이징에서 제외합니다. 현재 파일 내용은 그대로입니다.
git restore --staged -- app.py

# 내 작업을 저장해 둔 HEAD의 내용으로 현재 파일을 복원합니다.
git restore --source=HEAD --worktree -- app.py

이미 AI 작업까지 커밋했다면 이 조건과 다릅니다. 그때의 HEAD는 AI 수정 후 버전이므로, 어떤 커밋을 되돌릴지 확인하는 작업부터 해야 합니다. 이 글은 커밋 전의 변경 복원에 범위를 한정합니다.

다음부터는 AI에게 맡기기 전에 기준 커밋을 남깁니다

내 제목 수정까지 커밋해 둔 실험에서는 복원이 단순해졌습니다. 그 이후 배송비 변경만 있었으므로, 기준 버전으로 돌아가도 제목은 ‘결제 내역’으로 남았어요. 되돌릴 주소를 미리 적어둔 효과입니다.

# AI가 수정하기 전, 내 작업을 확인하는 단계입니다.
git status --short
git diff -- app.py
git diff --cached

# 필요한 변경만 스테이징하고, 커밋 전체에 들어갈 내용을 확인합니다.
git add -- app.py
git diff --cached
git commit -m "checkpoint: preserve my title change"

여기서 중요한 것은 커밋 메시지의 이름보다 git diff --cached에 담긴 내용입니다. git commit은 이미 스테이징돼 있던 다른 파일도 포함할 수 있으니, 내 작업으로 남길 내용인지 전체를 확인하세요.

수정 범위가 크다면 worktree로 작업 폴더를 나누는 방법도 있습니다. 아래는 현재 커밋을 기준으로 새 브랜치와 폴더를 만드는 예시예요. 지정한 브랜치와 경로가 아직 없는 경우에 사용합니다.

git worktree add -b ai/shipping-check ../ai-shipping-check HEAD

AI 작업은 새로 만든 ../ai-shipping-check 폴더에서 시작합니다. 원래 폴더에서 에이전트를 계속 실행하면 폴더를 나눈 의미가 없겠죠. Git worktree 공식 문서는 이렇게 한 저장소에 여러 작업 트리를 두는 방법을 설명합니다.

실행해 보니 새 worktree에는 기준 커밋의 파일만 생겼고, 원래 폴더의 미커밋 변경은 따라오지 않았습니다. AI에게 필요한 내 수정이 아직 미커밋 상태라면 먼저 기준 커밋에 포함할지 결정해야 해요. 의존성 설치나 로컬 설정 파일 준비도 별도입니다.

되돌린 뒤에는 두 가지를 확인합니다

배송비가 원래대로 돌아왔는지, 그리고 내가 바꾼 제목은 남아 있는지. 하나는 버릴 변경을 확인하고 다른 하나는 지킬 변경을 확인합니다. 이 예제에서는 출력 두 줄로 확인했지만, 실제 앱이라면 관련 테스트와 화면 동작도 확인해야 합니다.

기억할 것은 세 가지입니다. 복원 전에 현재 내용을 보관하고, 파일 전체가 아닌 변경 내용을 읽고, 다음 작업에는 기준 커밋을 남기세요. 새로 만든 미추적 파일은 일반 diff에 나오지 않으므로 git status에서도 확인합니다.

저라면 급할수록 전체 되돌리기부터 누르지는 않겠습니다. 몇 줄을 고르는 수고보다, 남겨야 할 작업을 다시 만드는 수고가 더 크니까요. 맡기기 전의 요청은 방향 주는 지시법처럼 정리하고, 맡긴 뒤의 비교 기준은 커밋으로 남겨두면 됩니다.

자주 묻는 질문

git restore는 AI가 수정한 부분만 되돌리나요?

아닙니다. Git은 미커밋 변경의 작성 의도를 구분하지 않습니다. 보관한 기준과 diff를 확인해 되돌릴 부분을 선택해야 합니다.

git restore --staged를 실행하면 코드도 원래대로 돌아가나요?

아니요. 이 명령은 스테이징 영역을 바꾸며 현재 파일 내용은 유지합니다. 파일을 복원할 기준과 위치는 별도로 확인해야 합니다.

사람과 AI가 같은 줄을 고쳤어도 부분 복원으로 분리할 수 있나요?

항상 가능한 것은 아닙니다. 기준 커밋 이후 같은 줄을 여러 번 고쳤다면 diff만으로 중간 내용을 알 수 없습니다. 커밋, 로컬 기록, 사본 등을 대조해야 합니다.

새 worktree에 현재 수정 중인 파일도 복사되나요?

이 글의 HEAD 기준 worktree 생성 명령은 커밋된 상태에서 출발합니다. 기존 작업 폴더의 미커밋 변경은 자동으로 복사되지 않습니다.