메인 콘텐츠로 건너뛰기
index.js의 인사말을 다른 방식으로 고쳐보고 싶어요. 지금 코드는 main에 남겨두고 새 브랜치를 만들었는데, 폴더도 파일도 늘어나지 않아요. 새 작업 공간은 어디에 생긴 걸까요? 앞편에서는 기록할 파일을 골랐어요. 이번에는 어느 브랜치에 다음 커밋을 쌓을지 골라봐요. 같은 폴더에서 브랜치를 만들고 이동하면서 파일 내용과 이력을 함께 확인해요.

브랜치는 커밋을 가리키는 이름이에요

브랜치(branch)는 커밋을 가리키는 참조예요. 새 브랜치를 만들면 처음에는 기존 브랜치와 같은 커밋을 가리킬 수 있어요. 프로젝트 폴더를 하나 더 복사하는 동작은 아니에요. HEAD는 일반적으로 현재 작업 중인 브랜치를 가리켜요. main에서 커밋하면 main이 새 커밋으로 이동하고, HEAD는 계속 main을 가리켜요. 다른 브랜치의 위치는 그대로예요. 이 연결은 Pro Git의 브랜치 설명을 기준으로 해요. 브랜치와 작업 트리·인덱스를 구분해야 해요. 브랜치마다 미커밋 파일을 따로 보관하는 것은 아니에요. 이 글에서는 하나의 작업 트리에서 두 브랜치를 번갈아 사용해요.

시작 상태를 맞춰요

명령은 Bash 기준이에요. 코드를 실행하지 않으므로 Node.js는 필요하지 않아요. 앞편을 마친 연습 저장소의 루트에서 시작해요. 실습은 별도 hook이나 커밋 서명 설정이 없고, 전역 제외 규칙이 예제 파일명에 영향을 주지 않는 환경을 기준으로 해요. 다른 작업이 섞였거나 이미 feature/greeting 브랜치가 있다면 새 연습 저장소를 준비해요.
Git 설치는 첫 커밋 실습을 참고해요. switch와 branch --show-current는 Git 2.23부터 사용할 수 있어요. 아래 준비 명령의 git init -b main에는 Git 2.28 이상이 필요해요.기존 Git 저장소 밖에서 실행해요. 같은 이름의 폴더가 있어 mkdir가 실패하면 멈추고 새 이름을 골라요.
아래 >는 파일을 덮어써요. 방금 만든 빈 연습 폴더에서만 실행해요. 작성자 설정은 이 저장소에만 적용하고 원격은 연결하지 않아요.준비 과정의 git rm --cached는 연습 메모를 인덱스에서만 제거해요. 작업 파일과 과거 기록은 남고, 다음 커밋에는 해당 경로의 삭제를 기록해요.
여기서 rm --cached는 연습 메모를 인덱스에서만 제거해요. 작업 파일은 남고 다음 커밋에는 삭제를 기록해요. 동작이 낯설면 앞편의 추적 해제 설명을 먼저 읽어봐요.
같은 터미널에서 시작 상태를 확인해요.
현재 브랜치는 main이고 일반 status는 working tree clean이어야 해요. 이력은 다섯 커밋이고, --ignored에는 !! debug.log와 !! local-notes.txt가 나와요. 원격 출력은 없어요. 이력 조회가 별도 화면으로 열리면 q로 나와요.

브랜치를 만들고 변경을 기록해봐요

1

브랜치 이름만 만들기

실행 전에 예상해봐요. 새 브랜치를 만들면 현재 브랜치도 바뀔까요?
현재 브랜치와 마지막 출력은 여전히 main이에요. branch -v의 *도 main 앞에 있어요. rev-parse가 출력하는 두 커밋 ID는 같아요. 이름은 둘이지만 아직 같은 기록을 가리켜요.git branch feature/greeting은 생성만 하고 전환하지 않아요. symbolic-ref --short HEAD는 HEAD가 가리키는 참조 이름을 읽어요. git branch와 git symbolic-ref 공식 문서에서 두 역할을 확인할 수 있어요.
2

파일을 고친 뒤 새 브랜치로 이동하기

아직 main에 있을 때 연습 파일 index.js를 덮어써요. 이번 변경은 새 브랜치에 기록할 거예요.
전환 전후 모두 M index.js가 보여요. 파일에는 console.log("hello branch");가 남아 있고, 현재 브랜치만 feature/greeting으로 바뀌어요. 스테이징은 하지 않았으므로 diff --cached는 비어 있어요.두 브랜치가 같은 커밋을 가리키는 이 상황에서는 미커밋 변경을 유지하며 이동할 수 있어요. main에서 편집을 시작했다고 해서 변경이 main 전용으로 저장된 것은 아니에요.
3

현재 브랜치에 커밋 쌓기

커밋 뒤 feature/greeting만 새 커밋을 가리켜요. 로그의 HEAD -> feature/greeting은 현재 브랜치가 그곳이라는 뜻이에요. main은 여전히 앞편의 마지막 커밋을 가리켜요.두 show 출력은 차례로 hello world, hello branch 인사말이에요. 여기서 브랜치:경로는 그 브랜치가 가리키는 커밋의 파일을 읽는 표현이에요. show로 파일을 조회해도 현재 브랜치나 작업 파일은 바뀌지 않아요.
4

main으로 돌아와 파일 비교하기

미커밋 변경이 없는 상태에서 돌아가요. 실행 전 index.js에 어떤 인사말이 나타날지 예상해봐요.
작업 파일과 인덱스의 파일은 모두 console.log("hello world");예요. git show :index.js는 인덱스의 내용을 읽어요. 깨끗한 상태의 switch는 이동할 브랜치에 맞춰 작업 트리와 인덱스를 갱신해요. 폴더 경로는 그대로예요.기본 log는 현재 HEAD에서 부모를 따라 읽으므로 새 인사말 커밋이 보이지 않아요. --all을 붙인 로그에는 feature/greeting의 커밋도 보여요. 기록을 지운 것이 아니라 조회 출발점이 달라졌어요. 옵션은 git log 공식 문서를 참고해요.

미커밋 변경이 있으면 항상 이동할 수 있나요?

앞에서는 같은 커밋 사이를 이동해서 변경을 유지할 수 있었어요. 이번에는 두 브랜치의 index.js가 달라요. main에서 같은 파일을 다시 고치고 전환을 시도해봐요. 아래 >는 연습 저장소의 index.js만 덮어써요.
마지막 명령은 실패하는 것이 이번 실습의 예상 결과예요. 전환으로 현재 수정 내용을 잃을 수 있어 Git이 거절해요. 영어 메시지 환경에서 확인한 출력은 다음과 같아요. 경로나 언어에 따라 표현은 달라질 수 있어요.
실패 뒤에도 브랜치와 파일은 유지돼요. 다음 블록은 위의 거절을 확인한 다음 실행해요.
출력은 main, M index.js, console.log("hello main"); 순서예요. 스테이징된 변경은 없어요. 전환이 거절됐으므로 feature/greeting에서 작업 중이라고 생각하고 커밋하면 안 돼요.
git switch --discard-changes나 -f는 미커밋 변경을 버릴 수 있어요. 거절 메시지를 없애려고 붙이지 않아요. 먼저 현재 브랜치와 두 diff를 읽고, 변경을 어느 브랜치에 보존할지 정해요.
이 예제의 hello main은 main에 남기려던 작업이에요. 내용을 확인하고 그곳에 커밋한 뒤 이동해요. 실제 작업이 미완성이거나 다른 브랜치용이라면 이 예제를 따라 현재 브랜치에 바로 커밋하지 말고, 보존할 위치와 방법부터 정해요.
이제 전환이 성공하고, 인사말도 hello branch와 hello main으로 바뀌어요. 아래는 커밋 ID를 생략한 설명용 도식이에요. 커밋 사이 화살표는 새 커밋에서 부모 커밋을 향해요. 앞편의 더 오래된 네 커밋은 생략했어요. 두 새 커밋의 부모는 같지만 어느 쪽도 다른 쪽의 새 커밋을 포함하지 않아요. switch는 작업 위치를 바꿨을 뿐, 두 변경을 합치지 않았어요. 미커밋 변경이 있다는 이유만으로 전환이 항상 실패하지는 않아요. 손실 없이 유지할 수 있으면 이동할 수 있고, 덮어쓸 위험이 있으면 기본 동작은 거절이에요. 범위와 강제 옵션은 git switch 공식 문서를 참고해요.

HEAD가 브랜치를 가리키지 않을 때도 있어요

이제 main의 변경은 모두 커밋했어요. 이번에는 브랜치 이름 대신 부모 커밋으로 이동해봐요. HEAD~1은 현재 커밋의 첫 번째 부모예요. 여기서는 두 브랜치가 갈라지기 직전의 커밋이에요.
branch --show-current는 아무것도 출력하지 않지만, rev-parse는 커밋 ID를 출력해요. 파일에는 hello world 인사말이 있어요. HEAD가 브랜치를 거치지 않고 커밋을 직접 가리키는 detached HEAD 상태예요. main과 feature/greeting의 위치는 움직이지 않았어요. 이번에는 조회만 하고 돌아와요.
다시 main과 hello main이 보여요. detached 상태에서도 커밋은 만들 수 있지만 기존 브랜치는 그 커밋을 따라가지 않아요. 그곳에서 계속 작업을 남기려면 먼저 새 브랜치를 만들어 연결하는 편이 좋아요. 이 글에서는 detached 상태에 새 커밋을 만들지 않았어요. 자세한 모델은 git checkout의 detached HEAD 설명을 참고해요.

명령의 역할을 구분해요

아래 표는 이번 실습을 정리한 참고예요. 새 브랜치를 다시 만들라는 실행 순서는 아니에요. switch -c는 앞의 생성과 전환을 한 번에 수행할 때 쓰는 명령이에요. 이 실습에서는 두 동작의 차이를 보려고 나누어 실행했어요. -c와 달리 -C는 기존 브랜치가 있어도 위치를 다시 지정할 수 있으므로 같은 옵션으로 취급하지 않아요.

끝난 상태를 확인해요

  • 현재 브랜치는 main이고 작업 파일의 인사말은 hello main이에요. 일반 status는 clean이에요.
  • feature/greeting의 인사말은 hello branch예요. 브랜치 간 diff에는 두 인사말의 차이가 나와요.
  • 공통 다섯 커밋 뒤에 브랜치마다 하나씩 새 커밋이 있어요. 각 브랜치의 이력은 여섯 커밋이고, 두 브랜치 전체에서 서로 다른 커밋은 일곱 개예요.
  • 작업 트리와 인덱스의 두 미커밋 diff는 비어 있어요. README.md와 .gitignore는 시작할 때와 같아요.
  • 제외된 메모와 로그는 남아 있고 원격은 없어요. 두 브랜치는 아직 병합하지 않았어요.
브랜치를 만들었는데 폴더가 늘지 않은 이유를 이제 설명할 수 있어요. 브랜치는 기록의 위치를 가리키고, HEAD는 현재 작업 위치를 알려줘요. switch를 실행하면 그 위치에 맞춰 같은 작업 폴더의 내용을 바꿔볼 수 있어요.

예상과 다른 결과가 나왔다면

이제 main과 feature/greeting에 각각 다른 변경이 있어요. 어느 한쪽으로 이동하는 것과 두 작업을 합치는 것은 별개의 일이에요. 현재 위치가 헷갈리면 먼저 status와 브랜치 이름을 읽고, 이력과 diff로 실제 차이를 확인해보세요.

두 갈래 작업은 어떻게 합칠까요?

새 연습 저장소에서 fast-forward와 merge commit을 비교해요. 합친 파일과 부모 커밋으로 결과를 확인해봐요.