현재 브랜치에 커밋되지 않은 변경 사항이있는 경우 다른 브랜치를 체크 아웃하십시오.


353

기존의 다른 브랜치를 체크 아웃하려고 할 때 대부분 Git은 현재 브랜치에서 커밋되지 않은 변경 사항이 있으면 허용하지 않습니다. 먼저 변경 사항을 커밋하거나 숨겨야합니다.

그러나 때때로 Git은 변경 사항을 커밋하거나 숨기지 않고 다른 지점을 체크 아웃 할 수 있으며 변경 사항을 지점 I 체크 아웃으로 전달합니다.

여기서 규칙은 무엇입니까? 변경 사항이 준비되어 있는지 또는 준비되어 있지 않은지가 중요합니까? 다른 브랜치에 변경 사항을 수행해도 의미가 없습니다 .git이 때때로 허용하는 이유는 무엇입니까? 즉, 어떤 상황에서는 도움이됩니까?

답변:


355

예비 메모

여기서 관찰 한 내용은 작업을 시작한 후에 branch1( branch2먼저 다른 지점으로 전환하는 것이 좋은지 잊어 버리거나 알지 못함 ) 다음을 실행 한다는 것입니다 .

git checkout branch2

때때로 Git은 "OK, 당신은 이제 branch2에 있습니다!"라고 말합니다. 때때로 Git은 "나는 그렇게 할 수 없다. 나는 당신의 변화를 잃을 것이다"라고 말한다.

Git 허용하지 않으면 변경 사항을 커밋하고 영구적으로 저장해야합니다. 그것들을 저장하기 위해 사용할 수도 git stash있습니다. 이것이 설계된 것 중 하나입니다. 하는 것으로 git stash savegit stash push실제로는 의미 "모든 변경 사항을 커밋하지만 전혀 지점에, 나는 지금있는 곳에서 제거합니다." 그러면 전환이 가능해집니다. 이제 진행중인 변경 사항이 없습니다. 그런 다음 git stash apply전환 후 사용할 수 있습니다 .

사이드 바 : git stash save이전 구문입니다. git stash pushGit 버전 2.13에서 도입되어 git stash새로운 옵션에 대한 인수 문제를 해결합니다 . 기본 방식으로 사용될 때 둘 다 동일한 작업을 수행합니다.

원하는 경우 여기에서 읽기를 중단 할 수 있습니다!

망할 놈의 경우 하지 않습니다 당신이 전환 할 수, 당신은 이미 구제가 : 사용 git stash또는 git commit; 또는 변경 사항이 재현하기에 사소한 경우이를 사용하여 변경 git checkout -f하십시오. 이 답변은 약간의 변경을 시작하더라도 Git이 언제 당신을 허락 것인지에 관한 것 git checkout branch2입니다. 왜 다른 시간이 아닌 때때로 작동 합니까?

여기서의 규칙은 한 가지 방법으로 간단하고 다른 방법으로는 복잡하거나 설명하기 어렵습니다.

상기 전환이 그러한 변경을 방해 할 필요가없는 경우에만 작업 트리에서 커밋되지 않은 변경 사항으로 분기를 전환 할 수 있습니다.

즉 — 이것은 여전히 ​​단순화되어 있습니다. 무대 git add, 무대 git rm등 이있는 매우 어려운 코너 케이스 가 있습니다 branch1. A git checkout branch2는 이것을해야합니다 :

  • 모든 파일의 경우 입니다branch1하지branch2, 1 개 파일이 삭제.
  • 모든 파일에 들어 있습니다branch2하지branch1, (적절한 내용) 해당 파일을 만들 수 있습니다.
  • 두 분기에있는 모든 파일에 대해 버전 branch2이 다르면 작업 트리 버전을 업데이트하십시오.

이러한 각 단계는 작업 트리에서 무언가를 방해 할 수 있습니다.

  • 작업 트리의 버전이의 커밋 된 버전과 동일한 경우 파일을 제거하는 것이 "안전"합니다 branch1. 변경 한 경우 "안전하지 않은"것입니다.
  • 파일이 branch2존재하지 않는 경우 파일을 만드는 방법 은 "안전"합니다. 2 현재 존재하지만 "잘못된"내용이있는 경우 "안전하지 않은"것입니다.
  • 물론 작업 트리 버전이 이미 커밋 된 경우 파일의 작업 트리 버전을 다른 버전으로 바꾸는 것이 "안전"합니다 branch1.

새 분기 ( git checkout -b newbranch)를 만드는 것은 항상 "안전한"것으로 간주됩니다.이 프로세스의 일부로 작업 트리에서 파일이 추가, 제거 또는 변경되지 않으며 인덱스 / 스테이징 영역도 변경되지 않습니다. (주의 : 새로운 지점의 시작 지점을 변경하지 않고 새로운 지점을 만들 때 안전하지만, 다른 인수를 추가하면 (예 :) git checkout -b newbranch different-start-point변경해야 할 수도 있습니다 different-start-point. Git은 평소와 같이 체크 아웃 안전 규칙을 적용합니다 .)


1 이것은 우리가 파일이 차례로 단어의 정의가 필요한 지점에 있어야하는 것이 무엇을 의미하는지 정의가 필요 분기를 제대로. (참조 정확하게 우리가 "가지"가 무슨 뜻 이죠? ) 여기서, 정말 뜻이 무엇인지 커밋되는 지점 이름의 결의 : 그 경로에있는 파일 의 경우 해시를 생성합니다. 그 파일이 아닙니다 에 대신 오류 메시지가 있다면. 이 특정 질문에 대답 할 때 색인 또는 작업 트리 에 경로 가 존재 하지 않습니다. 따라서 여기서 비밀은 각각에 대한 결과를 검사하는 것입니다P branch1git rev-parse branch1:Pbranch1Pgit rev-parsebranch-name:path. 파일이 최대 하나의 브랜치에 "in"있기 때문에 실패하거나 두 개의 해시 ID를 제공합니다. 두 개의 해시 ID가 동일한 경우 파일은 두 분기에서 동일합니다. 변경할 필요가 없습니다. 해시 ID가 다른 경우 파일은 두 분기에서 다르며 분기를 전환하도록 변경해야합니다.

여기서 중요한 개념은 커밋의 파일 이 영원히 동결 된다는 것입니다. 편집 할 파일은 고정 되지 않습니다 . 우리는 적어도 처음에는 고정 된 두 커밋 사이의 불일치 만보고 있습니다. 불행하게도, 우리 또는 힘내-또한 파일을 처리 할 필요가 없습니다 당신이 멀리 전환 가고있는 커밋에 있는 (가) 당신이 스위치에거야 커밋. 파일이 인덱스 및 / 또는 작업 트리에 존재할 수 있기 때문에 나머지 복잡한 문제가 발생합니다.

2 "올바른 내용"과 함께 이미 존재하는 경우 "안전 정렬"로 간주 될 수 있으므로 Git이이를 생성 할 필요가 없습니다. 적어도 Git의 일부 버전은 이것을 허용하지만, 테스트 결과 Git 1.8.5.4에서 "안전하지 않은"것으로 간주됩니다. 전환 대상 분기와 일치하도록 수정되는 수정 된 파일에 동일한 인수가 적용됩니다. 다시, 1.8.5.4는 단지 "덮어 쓰기 될 것"이라고 말합니다. 기술 노트의 끝 부분도 참조하십시오 .1.5 버전에서 Git을 처음 사용하기 시작한 이후 읽기 트리 규칙이 변경되지 않았다고 생각하면 메모리가 잘못되었을 수 있습니다.


변경 사항이 준비되어 있는지 또는 준비되어 있지 않은지가 중요합니까?

그렇습니다. 특히 변경을 준비한 다음 작업 트리 파일을 "수정 취소"할 수 있습니다. 여기에 다른의 두 지점에있는 파일,의 branch1와는 branch2:

$ git show branch1:inboth
this file is in both branches
$ git show branch2:inboth
this file is in both branches
but it has more stuff in branch2 now
$ git checkout branch1
Switched to branch 'branch1'
$ echo 'but it has more stuff in branch2 now' >> inboth

이 시점에서 작업 트리 파일 은에 있지만 파일의 파일 inboth과 일치합니다 . 이 변경 사항은 커밋을 위해 준비되지 않았으므로 여기에 표시됩니다.branch2branch1git status --short

$ git status --short
 M inboth

space-then-M은 "수정되었지만 준비되지 않음"을 의미합니다 (보다 정확하게는 작업 트리 복사본이 준비 / 인덱스 복사본과 다릅니다).

$ git checkout branch2
error: Your local changes ...

이제 작업 트리 사본을 준비해 봅시다. 이미 알고있는 사본과 일치합니다 branch2.

$ git add inboth
$ git status --short
M  inboth
$ git checkout branch2
Switched to branch 'branch2'

여기에서 준비 및 작업 사본이 모두와 일치 branch2하므로 결제가 허용되었습니다.

다른 단계를 시도해 봅시다 :

$ git checkout branch1
Switched to branch 'branch1'
$ cat inboth
this file is in both branches

체크 아웃이 스테이징 영역을 통해 쓰기 때문에 이제 스테이징 영역에서 변경 한 내용이 손실됩니다. 이것은 약간의 경우입니다. 변화는 사라지지 않았지만 내가 무대에 놓았다는 사실 사라졌습니다.

분기 복사와는 다른 파일의 세 번째 변형을 준비한 다음 현재 분기 버전과 일치하도록 작업 복사본을 설정합니다.

$ echo 'staged version different from all' > inboth
$ git add inboth
$ git show branch1:inboth > inboth
$ git status --short
MM inboth

M여기에 평균들 :에서 파일 다릅니다 개최 HEAD파일 에서 작업 트리 파일 다르다 파일을 개최. 작업 트리 버전은 branch1(일명 HEAD) 버전 과 일치합니다 .

$ git diff HEAD
$

그러나 git checkout체크 아웃을 허용하지 않습니다.

$ git checkout branch2
error: Your local changes ...

branch2버전을 작업 버전으로 설정하십시오 .

$ git show branch2:inboth > inboth
$ git status --short
MM inboth
$ git diff HEAD
diff --git a/inboth b/inboth
index ecb07f7..aee20fb 100644
--- a/inboth
+++ b/inboth
@@ -1 +1,2 @@
 this file is in both branches
+but it has more stuff in branch2 now
$ git diff branch2 -- inboth
$ git checkout branch2
error: Your local changes ...

현재 작업중인 사본이의 branch2파일 과 일치하더라도 준비된 파일은 일치 하지 않으므로 git checkout해당 사본이 손실되고 git checkout는 거부됩니다.

기술 노트 — 미심쩍은 호기심 만 :-)

이 모든 것에 대한 기본 구현 메커니즘은 Git 's index 입니다. 당신이 구축 곳 또한 "준비 영역"이라고 인덱스는,이다 다음 당신이 체크 아웃 한대로 지금 즉, 현재의 커밋을, 일치 밖으로 시작하고 때마다 당신에게 : 커밋 git add파일, 당신은 대체 인덱스 버전을 작업 트리에있는 모든 것을

기억 작업 트리가 당신이 당신의 파일에 일하는 곳입니다. 여기, 커밋과 인덱스에서와 같이 Git 형식 만 사용하는 것이 아니라 일반적인 형식을 사용합니다. 당신은 파일을 추출 그래서 에서 A가 커밋 을 통해 작업 트리에에서 다음 인덱스합니다. 변경 한 후에 git add는 색인으로 이동합니다. 따라서 각 파일에는 현재 커밋, 인덱스 및 작업 트리의 세 위치가 있습니다.

당신이 실행하면 git checkout branch2, 힘내 커버 아래 않습니다 무엇을 비교하는 것이다 끝이 커밋branch2현재 커밋 지금은 인덱스 모두에서 무엇이든 할 수 있습니다. 현재 존재하는 것과 일치하는 모든 파일은 Git에서 그대로 둘 수 있습니다. 모두 손대지 않았습니다. 두 커밋 모두에서 동일한 파일 인 Git은 단독으로 남겨 둘 수 있으며 분기를 전환 할 수있는 파일입니다.

커밋 전환을 포함한 Git의 대부분은 이 인덱스로 인해 비교적 빠릅니다 . 실제로 색인에있는 것은 각 파일 자체가 아니라 각 파일의 해시 입니다. 파일 자체의 복사본은 Git이 blob 객체 라고 부르는 저장소로 저장소에 저장됩니다. 이것은 파일이 커밋에 저장되는 방식과 비슷합니다. 커밋은 실제로 파일을 포함하지 않으며 , Git을 각 파일의 해시 ID로 연결합니다. 따라서 Git은 해시 ID (현재 160 비트 길이의 문자열)를 비교하여 커밋 XY 의 파일 이 같은지 여부를 결정할 수 있습니다. 그런 다음 해당 해시 ID를 색인의 해시 ID와 비교할 수도 있습니다.

이것이 위의 모든 홀수 볼 코너 사례로 이어집니다. 우리는 둘 다 file을 갖는 XY 를 커밋 하고에 path/to/name.txt대한 색인 항목을 가지고 path/to/name.txt있습니다. 세 개의 해시가 모두 일치 할 수 있습니다. 어쩌면 두 개가 일치하고 하나는 일치하지 않을 수 있습니다. 세 가지 모두 다를 수도 있습니다. 그리고, 우리는 또한있을 수 있습니다 another/file.txt그 단지에의 X 또는에서만 Y 및 또는 현재 인덱스가 아닙니다. 이러한 다양한 경우 각각 별도의 고려 사항이 필요 합니다 . Git 은 커밋에서 인덱스로 파일을 복사하거나 인덱스에서 제거하여 X 에서 Y 로 전환 해야 합니까? 그렇다면, 그것은 또한 에있다파일을 작업 트리에 복사하거나 작업 트리에서 제거하십시오. 만약 '사건이야, 인덱스와 작업 트리 버전이 더 적합 커밋 된 버전 중 적어도 하나를했다; 그렇지 않으면 Git은 일부 데이터를 방해합니다.

(이 모든 것에 대한 완전한 규칙 git checkout은 예상 git read-tree문서가 아니라 "Two Tree Merge"섹션 문서에 설명되어 있습니다.)


3
... 또한 작업 트리 git checkout -m와 색인 변경 사항을 새로운 체크 아웃에 병합하는을 제공합니다.
jthill

1
이 훌륭한 설명에 감사드립니다! 그러나 공식 문서에서 정보를 어디서 찾을 수 있습니까? 아니면 불완전합니까? 그렇다면 git에 대한 권위있는 참조는 무엇입니까?
최대

1
(1) 할 수 없으며 (2) 소스 코드. 주요 문제는 Git이 지속적으로 발전하고 있다는 것입니다. 예를 들어, 지금 당장 SHA-256을 사용하거나 찬성하여 SHA-1을 늘리거나 버리려는 큰 압박이 있습니다. Git의이 특정 부분은 지금까지 오랫동안 안정적이었으며 기본 메커니즘은 간단합니다. Git은 현재 색인을 현재 및 대상 커밋과 비교하고 대상 커밋을 기반으로 변경할 파일 (있는 경우)을 결정합니다. 그런 다음 색인 항목을 교체해야하는 경우 작업 트리 파일의 "정 결성"을 테스트합니다.
torek

6
짧은 대답 : 규칙이 있지만 일반 사용자가 기억하는 것만으로 이해하려는 희망을 갖는 것은 너무 모호합니다. 따라서 도구가 지능적으로 행동하는 대신에 당신이 대신에 체크 아웃하는 규칙에 의존해야합니다. 현재 브랜치는 커밋되고 깨끗합니다. 이것이 어떻게 다른 지점으로 뛰어난 변경 사항을 전달하는 것이 유용한 지에 대한 질문에 어떻게 대답하는지 알지 못하지만 이해하기가 어려워서 놓쳤을 수도 있습니다.
Neutrino

2
@ HawkeyeParker :이 답변은 많은 편집을 거쳤으며 그 중 많은 부분이 많이 개선되지는 않았지만 파일이 "지점에 있음"이 무엇을 의미하는지에 대해 뭔가를 추가하려고 시도합니다. 여기서 "분기"라는 개념이 처음에는 제대로 정의되지 않았지만 또 다른 항목이기 때문에 궁극적으로 이것은 흔들 리게 될 것입니다.
torek

51

두 가지 선택 사항이 있습니다. 변경 사항을 숨기십시오.

git stash

그런 다음 나중에 다시 가져옵니다.

git stash apply

또는 원격 지점을 가져 와서 변경 사항을 병합 할 수 있도록 지점에 변경 사항을 적용하십시오. git의 가장 큰 장점 중 하나입니다. 브랜치를 만들고 커밋 한 다음 다른 브랜치를 변경하십시오.

당신은 그것이 말이되지 않는다고 말하지만, 당신은 그것을하고 있기 때문에 풀을 한 후에 마음대로 합칠 수 있습니다. 분명히 다른 선택은 지점의 사본을 커밋 한 다음 당기는 것입니다. 추정은 당신이 그 일을 원하지 않거나 (이 경우 나는 당신이 가지를 원하지 않는다고 당혹 스럽거나) 갈등을 두려워한다는 것입니다.


1
명령이 정확하지 git stash apply않습니까? 여기 문서들.
Thomas8

1
내가 찾고있는 것, 일시적으로 다른 지점으로 전환하고 무언가를 찾고 작업중 인 지점의 동일한 상태로 돌아갑니다. 고마워 Rob!
Naishta

1
예, 이것이 올바른 방법입니다. 나는 받아 들여진 대답의 세부 사항에 감사하지만, 필요한 것보다 어렵게 만듭니다.
Michael Leonard

5
또한, 숨김을 유지할 필요가 없다면 사용할 수 있으며 git stash pop, 성공적으로 적용되면 목록에서 숨김을 제거합니다.
Michael Leonard

1
git stash pop레포 기록에 보관 기록을
남기지

14

새 분기에 변경된 특정 파일에 대한 현재 분기와 다른 편집 내용이 포함 된 경우 변경 사항이 커밋되거나 숨김 상태가 될 때까지 분기를 전환 할 수 없습니다. 변경된 파일이 두 분기 (즉, 해당 파일의 커밋 된 버전)에서 동일하면 자유롭게 전환 할 수 있습니다.

예:

$ echo 'hello world' > file.txt
$ git add file.txt
$ git commit -m "adding file.txt"

$ git checkout -b experiment
$ echo 'goodbye world' >> file.txt
$ git add file.txt
$ git commit -m "added text"
     # experiment now contains changes that master doesn't have
     # any future changes to this file will keep you from changing branches
     # until the changes are stashed or committed

$ echo "and we're back" >> file.txt  # making additional changes
$ git checkout master
error: Your local changes to the following files would be overwritten by checkout:
    file.txt
Please, commit your changes or stash them before you can switch branches.
Aborting

이것은 추적되지 않은 파일뿐만 아니라 추적 된 파일에도 적용됩니다. 추적되지 않은 파일의 예는 다음과 같습니다.

예:

$ git checkout -b experimental  # creates new branch 'experimental'
$ echo 'hello world' > file.txt
$ git add file.txt
$ git commit -m "added file.txt"

$ git checkout master # master does not have file.txt
$ echo 'goodbye world' > file.txt
$ git checkout experimental
error: The following untracked working tree files would be overwritten by checkout:
    file.txt
Please move or remove them before you can switch branches.
Aborting

마스터에서 실험을 수행하고 커밋하고 싶었지만 아직 마스터하지는 않았지만 변경을하면서 브랜치 사이를 이동 해야하는 좋은 예는 ...

$ echo 'experimental change' >> file.txt # change to existing tracked file
   # I want to save these, but not on master

$ git checkout -b experiment
M       file.txt
Switched to branch 'experiment'
$ git add file.txt
$ git commit -m "possible modification for file.txt"

실제로 나는 아직도 그것을 얻지 못한다. 첫 번째 예에서 "우리가 돌아 왔습니다"를 추가하면 로컬 변경 사항을 덮어 쓰게됩니다. 정확히 어떤 로컬 변경 사항입니까? "우리 돌아 왔어"? git은 왜 마스터에서 파일에 "hello world"와 "and back"을 포함하도록이 변경 사항을 master로 전달하지 않는가
Xufeng

첫 번째 예에서 마스터는 'hello world'만 커밋했습니다. 실험에 'hello world \ ngoodbye world'가 투입되었습니다. 브랜치 변경을 수행하려면 file.txt를 수정해야합니다. 문제는 커밋되지 않은 변경 사항 인 "hello world \ ngoodbye world \ nand we 're back"입니다.
Gordolio

1

정답은

git checkout -m origin/master

오리진 마스터 브랜치의 변경 사항을 커밋되지 않은 로컬 변경 사항과 병합합니다.


0

이 변경 사항을 커밋하지 않으려는 경우 수행하십시오 git reset --hard.

그런 다음 원하는 지점으로 체크 아웃 할 수 있지만 커밋되지 않은 변경 내용은 손실됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.