Linus Torvalds는 Git이 절대로 파일을 추적하지 않는다고 할 때 무엇을 의미합니까? 역사는 전체 프로젝트의 역사를 바탕으로합니다… (

Linus Torvalds 는 2007 년 Google의 Tech Talk에서 Git이 처리 할 수있는 파일 수를 물었을 때 인용했습니다 (43:09).

… Git은 컨텐츠를 추적합니다. 절대 단일 파일을 추적하지 않습니다. Git에서 파일을 추적 할 수 없습니다. 당신이 할 수있는 일은 단일 파일이있는 프로젝트를 추적 할 수 있지만 프로젝트에 단일 파일이있는 경우 반드시 수행하고 수행 할 수 있지만 10,000 개의 파일을 추적하면 Git은 해당 파일을 개별 파일로 보지 않습니다. Git은 모든 것을 전체 내용으로 생각합니다. Git의 모든 역사는 전체 프로젝트의 역사를 바탕으로합니다…

( 여기서 사본 .)

그러나 Git 서적에 들어가면 Git 의 파일을 추적 하거나 추적 할 수 없다는 것이 가장 먼저 알려 집니다. 또한, 전체 Git 경험이 파일 버전 관리에 맞춰진 것처럼 보입니다. 사용 git diff하거나 git status출력 할 때 파일별로 표시됩니다. 사용 git add하면 파일 단위로 선택할 수도 있습니다. 파일 단위로 기록을 검토 할 수 있으며 매우 빠릅니다.

이 진술을 어떻게 해석해야합니까? 파일 추적 측면에서 Git은 CVS와 같은 다른 소스 제어 시스템과 어떻게 다릅니 까?



답변

CVS에서는 기록이 파일별로 추적되었습니다. 브랜치는 각각 고유 한 버전 번호가있는 다양한 버전의 고유 한 파일로 구성 될 수 있습니다. CVS는 유사한 방식으로 개별 파일을 추적하는 RCS ( Revision Control System )를 기반 으로했습니다.

반면에 Git은 전체 프로젝트 상태의 스냅 샷을 만듭니다. 파일은 독립적으로 추적 및 버전 관리되지 않습니다. 저장소의 개정은 하나의 파일이 아니라 전체 프로젝트의 상태를 나타냅니다.

Git은 파일 추적을 언급 할 때 단순히 프로젝트 기록에 포함된다는 것을 의미합니다. Linus의 이야기는 Git 컨텍스트에서 파일 추적을 언급하는 것이 아니라 CVS 및 RCS 모델과 Git에서 사용되는 스냅 샷 기반 모델을 대조하는 것이 었습니다.


답변

나는 Brian M에 동의합니다 . carlson의 답변 : Linus는 파일 지향과 커밋 지향 버전 제어 시스템을 적어도 부분적으로 구별하고 있습니다. 그러나 나는 그것보다 더 많은 것이 있다고 생각합니다.

에서 내 책 정체되고 완료 만나지 수도, 나는 마련하려고 분류 버전 제어 시스템. 내 분류법에서 우리가 여기서 관심을 갖는 용어 는 버전 제어 시스템 의 원 자성 입니다. 현재 22 페이지를 참조하십시오. VCS에 파일 레벨 원자가있는 경우 실제로 각 파일에 대한 히스토리가 있습니다. VCS는 파일 이름과 각 시점에서 발생한 사항을 기억해야합니다.

힘내 그렇게하지 않습니다. Git에는 커밋 히스토리 만 있습니다. 커밋은 원자 단위이며 히스토리 저장소의 커밋 세트입니다. 커밋이 기억하는 것은 데이터-파일 이름으로 가득 찬 전체 트리와 각 파일과 함께 제공되는 내용-그리고 커밋을 한 사람, 언제, 왜, 내부 Git 해시 ID와 같은 메타 데이터입니다. 커밋의 부모 커밋. (그것은이 부모, 그리고 모든 커밋과 부모를 읽고에 의해 형성된 감독 acycling 그래프 입니다 저장소의 역사.)

VCS는 커미트 지향적이지만 여전히 파일별로 데이터를 저장할 수 있습니다. 그것은 때로는 중요한 세부 사항이지만 구현 세부 사항이며 Git도 그렇게하지 않습니다. 대신, 각 커밋 은 트리 객체 인코딩 파일 이름 , 모드 (즉,이 파일이 실행 가능한지 아닌지) 및 실제 파일 내용에 대한 포인터 와 함께 트리를 기록합니다 . 내용 자체는 blob 객체 에 독립적으로 저장됩니다 . 커밋 객체와 마찬가지로 Blob은 내용에 고유 한 해시 ID를 가져옵니다. 단 한 번만 나타날 수있는 커밋과 달리 Blob은 많은 커밋에 나타날 수 있습니다. 따라서 Git의 기본 파일 내용은 Blob으로 직접 저장되고 간접적으로 저장됩니다 커밋 객체에 (직접 또는 간접적으로) 해시 ID가 기록 된 트리 객체.

Git에게 다음을 사용하여 파일 기록을 보여달라고 요청할 때 :

git log [--follow] [starting-point] [--] path/to/file

어떤 망할 놈이 정말로하고있는 것은 걷기입니다 커밋 힘내있는 유일한 역사 역사를, 그러나 보여주는 당신이 커밋의 하지 않는 한을 :

  • 커밋은 병합되지 않은 커밋이며
  • 해당 커밋의 부모도 파일을 가지고 있지만 부모의 내용이 다르거 나 커밋의 부모가 파일을 전혀 가지고 있지 않습니다.

(그러나 이러한 조건 중 일부는 추가 git log옵션을 통해 수정 될 수 있으며 , Git이 히스토리에서 커밋을 완전히 생략하도록하는 히스토리 단순화라는 부작용을 설명하기가 매우 어렵습니다). 여기에 표시되는 파일 기록은 어떤 의미에서 리포지토리에 정확하게 존재하지 않습니다. 대신 실제 기록의 합성 하위 집합입니다. 다른 git log옵션 을 사용하면 다른 “파일 기록”을 얻게됩니다 !


답변

혼란스러운 부분은 다음과 같습니다.

Git은 그 파일들을 개별 파일로 본 적이 없습니다. Git은 모든 것을 전체 내용으로 생각합니다.

Git은 종종 자체 저장소에 객체 대신 160 비트 해시를 사용합니다. 파일 트리는 기본적으로 각 컨텐트 (메타 데이터)와 관련된 이름 및 해시 목록입니다.

그러나 160 비트 해시는 콘텐츠를 고유하게 식별합니다 (git 데이터베이스의 유니버스 내에서). 따라서 컨텐츠로 해시가있는 트리 에는 해당 상태 의 컨텐츠포함됩니다 .

파일 내용의 상태를 변경하면 해시가 변경됩니다. 그러나 해시가 변경되면 파일 이름 내용과 관련된 해시도 변경됩니다. “디렉토리 트리”의 해시가 변경됩니다.

git 데이터베이스가 디렉토리 트리를 저장할 때, 그 디렉토리 트리는 모든 서브 디렉토리의 모든 내용과 그 안에있는 모든 파일을 포함하고 포함합니다 .

Blob 또는 다른 트리에 대한 (불변의, 재사용 가능한) 포인터로 트리 구조로 구성되지만 논리적으로 전체 트리의 전체 내용에 대한 단일 스냅 샷입니다. git 데이터베이스 의 표현 은 플랫 데이터 내용이 아니지만 논리적으로 모든 데이터이며 다른 것은 아닙니다.

트리를 파일 시스템으로 직렬화하고 모든 .git 폴더를 삭제하고 git에게 트리를 다시 데이터베이스에 추가하도록 지시하면 데이터베이스에 아무것도 추가하지 않아도됩니다. 요소는 이미 존재했을 것입니다.

git의 해시를 불변 데이터에 대한 참조 카운트 포인터로 생각하면 도움이 될 수 있습니다.

그 주위에 응용 프로그램을 빌드 한 경우 문서는 여러 페이지로 구성되며 레이어가 있고 그룹이 있고 개체가 있습니다.

객체를 변경하려면 완전히 새로운 그룹을 만들어야합니다. 그룹을 변경하려면 새 문서가 필요한 새 페이지가 필요한 새 레이어를 만들어야합니다.

단일 객체를 변경할 때마다 새 문서가 생성됩니다. 오래된 문서는 계속 존재합니다. 새 문서와 기존 문서는 대부분의 컨텐츠를 공유합니다. 동일한 페이지를 가지고 있습니다 (1 제외). 한 페이지에는 같은 레이어가 있습니다 (1 제외). 해당 계층은 동일한 그룹을 갖습니다 (1 제외). 해당 그룹은 동일한 객체를 갖습니다 (1 제외).

그리고 마찬가지로 논리적으로 사본을 의미하지만 구현 측면에서 볼 때 동일한 불변 객체에 대한 또 다른 참조 카운트 포인터입니다.

git repo는 그렇게 비슷합니다.

이것은 주어진 git changeset이 commit 메시지 (해시 코드)를 포함하고, 작업 트리를 포함하고, 부모 변경을 포함한다는 것을 의미합니다.

이러한 상위 변경 사항에는 상위 변경 사항이 포함됩니다.

히스토리 를 포함하는 git repo의 일부는 변경 체인입니다. “디렉토리”트리에서 “디렉토리”트리 의 레벨로 변경 체인을 변경하면 변경 세트와 변경 체인에 고유하게 접근 할 수 없습니다.

파일에 어떤 일이 발생하는지 확인하려면 변경 세트에서 해당 파일로 시작하십시오. 그 변경 세트에는 역사가 있습니다. 종종 그 역사에는 동일한 내용을 가진 동일한 이름의 파일이 존재합니다. 내용이 동일하면 파일이 변경되지 않은 것입니다. 다른 경우 변경 사항이 있으므로 정확히 무엇을 해결하기 위해 작업을 수행해야합니다.

때때로 파일이 사라졌습니다. 그러나 “디렉토리”트리에는 동일한 내용의 동일한 파일 (동일한 해시 코드)이있을 수 있으므로 해당 방식으로 추적 할 수 있습니다 (참고 : 커밋 대상과 별도로 파일을 이동하려는 커밋을 원하는 이유입니다) -편집하다). 또는 동일한 파일 이름이며 파일을 확인한 후 유사합니다.

git은 “파일 히스토리”를 함께 패치 워크 할 수 있습니다.

그러나이 파일 히스토리는 파일의 한 버전에서 다른 버전으로의 링크가 아닌 “전체 변경 세트”의 효율적인 구문 분석에서 비롯됩니다.


답변

“git은 파일을 추적하지 않습니다”는 기본적으로 git의 커밋은 트리의 경로를 “blob”에 연결하는 파일 트리 스냅 샷과 commit 기록을 추적하는 커밋 그래프로 구성됨을 의미 합니다. 그 밖의 모든 것은 “git log”및 “git blame”과 같은 명령에 의해 즉석에서 재구성됩니다. 이 재구성은 다양한 옵션을 통해 파일 기반 변경 사항을 찾는 것이 얼마나 어려운지 알 수 있습니다. 기본 휴리스틱은 변경없이 파일 트리에서 BLOB 위치가 변경되는 시점 또는 파일이 이전과 다른 Blob과 연관된시기를 결정할 수 있습니다. Git이 사용하는 압축 메커니즘은 블롭 / 파일 경계에 대해 크게 신경 쓰지 않습니다. 컨텐츠가 이미 어딘가에 있으면 다양한 블롭을 연관시키지 않고 저장소 증가를 작게 유지할 수 있습니다.

이제는 저장소입니다. Git에는 작업 트리가 있으며이 작업 트리에는 추적 및 추적되지 않은 파일이 있습니다. 추적 된 파일 만 색인 (스테이징 영역? 캐시?)에 기록되며 추적 된 파일 만 저장소에 저장됩니다.

색인은 파일 지향적이며이를 조작하기위한 일부 파일 지향 명령이 있습니다. 그러나 저장소에서 끝나는 것은 파일 트리 스냅 샷과 관련 Blob 데이터 및 커밋의 조상의 형태로 커밋하는 것입니다.

Git은 파일 히스토리와 이름 변경을 추적하지 않으며 그 효율성은 그에 의존하지 않기 때문에 때때로 Git이 사소한 히스토리에 관심이있는 히스토리 / diffs / blames를 생성 할 때까지 다른 옵션으로 몇 번 시도해야합니다.

역사를 재구성 하는 대신 기록 하는 Subversion과 같은 시스템에서는 다릅니다 . 기록이 없으면 정보를들을 수 없습니다.

실제로 릴리스 트리를 Git으로 체크인 한 다음 그 효과를 복제하는 스크립트를 생성하여 한 번에 차등 설치 프로그램을 빌드했습니다. 때때로 전체 트리가 이동 되었기 때문에 생성 된 모든 항목을 덮어 쓰거나 삭제하는 것보다 훨씬 작은 차등 설치 프로그램이 생성되었습니다.


답변

Git은 파일을 직접 추적하지 않지만 저장소의 스냅 샷을 추적하며 이러한 스냅 샷은 파일로 구성됩니다.

그것을 보는 방법이 있습니다.

다른 버전 제어 시스템 (SVN, Rational ClearCase)에서는 파일을 마우스 오른쪽 단추로 클릭하고 변경 히스토리를 얻을 수 있습니다 .

Git에는이를 수행하는 직접적인 명령이 없습니다. 이 질문을 참조하십시오 . 당신은 얼마나 많은 다른 답변이 있는지에 놀랄 것입니다. Git은 SVN이나 ClearCase 가하는 방식 이 아니라 단순히 파일을 추적하지 않기 때문에 간단한 대답 이 없습니다 .


답변

우연히 “콘텐츠”를 추적하면 빈 디렉토리가 추적되지 않습니다.
따라서 폴더의 마지막 파일을 git rm 하면 폴더 자체가 삭제됩니다 .

항상 그런 것은 아니며, Git 1.4 (2006 년 5 월) 만이 commit 443f833을 사용 하여 “콘텐츠 추적”정책을 시행했습니다 .

자식 상태 : 빈 디렉토리를 건너 뛰고 -u를 추가하여 추적되지 않은 모든 파일을 표시하십시오.

기본적으로, 우리 --others --directory는 내용없이 (사용자의 관심을 끌기 위해) 관심없는 디렉토리를 표시하고 (정렬되지 않은 출력을 위해) 사용합니다.
빈 디렉토리를 표시하는 것은 의미가 없으므로 그렇게 할 --no-empty-directory때 전달하십시오 .

제공 -u(또는 --untracked)하면이 정리가 해제되어 사용자가 추적되지 않은 파일을 모두 얻을 수 있습니다.

이것은 2011 년 1 월에 커밋 8fe533 , Git v1.7.4 와 함께 에코 되었습니다.

이것은 일반적인 UI 철학과 일치합니다 .git은 빈 디렉토리가 아닌 컨텐츠를 추적합니다.

그 동안 Git 1.4.3 (2006 년 9 월)에서 Git은 커밋 되지 않은 컨텐츠를 비어 있지 않은 폴더로 제한하기 시작합니다 . commit 2074cb0 :

완전히 추적되지 않은 디렉토리의 내용을 나열하지 말고 해당 디렉토리의 이름 (및 후행 ‘ /‘) 만 나열해야합니다 .

콘텐츠 추적은 git blame이 초기에 (Git 1.4.4, Oct. 2006, commit cee7f24 ) 더 성능이 좋았던 것입니다.

더 중요한 것은 내부 구조는 동일한 커밋에서 둘 이상의 경로를 가져올 수 있도록하여 컨텐츠 이동 (일명 잘라 내기 및 붙여 넣기)을보다 쉽게 지원하도록 설계되었습니다 .

그 내용 (추적 내용)은 Git 1.5.0 (Git 1.5.0)과 함께 Git API에 git add를 넣는 것입니다 (2006 년 12 월, 366bfcb 커밋 )

‘git add’를 인덱스에 일류 사용자 친화적 인 인터페이스로 설정

이것은 지수에 대해 전혀 이야기하지 않고 적절한 정신 모델을 사용하여 지수의 힘을 미리 가져옵니다.
예를 들어 git-add 매뉴얼 페이지에서 모든 기술 토론이 철수 된 방법을 참조하십시오.

커밋 할 컨텐츠는 모두 함께 추가해야합니다.
해당 컨텐츠가 새 파일에서 제공되는지 또는 수정 된 파일에서 제공되는지는 중요하지 않습니다.
git-add를 사용하거나 git-commit을 제공함으로써 -a(물론 이미 알려진 파일에 대해서만) “추가”하면 됩니다.

만든 것입니다 그 git add --interactive같은 힘내 1.5.0으로 가능 ( 5cde71d 커밋 )

선택한 후에는 빈 줄로 응답 하여 색인에서 선택한 경로에 대한 작업 트리 파일 의 내용 을 준비하십시오 .

따라서 디렉토리에서 모든 내용을 재귀 적으로 제거 -r하려면 디렉토리 이름뿐만 아니라 옵션 을 전달해야 합니다 <path>(여전히 Git 1.5.0, commit 9f95069 ).

파일 자체 대신 파일 내용을 보는 것이 commit 1de70db (Git v2.18.0-rc0, 2018 년 4 월)에 설명 된 것과 같은 병합 시나리오를 허용합니다.

이름 바꾸기 / 추가 충돌과 다음 병합을 고려하십시오.

  • A면 : 수정 foo, 관련 없음 추가bar
  • B면 : 이름 바꾸기 foo->bar(그러나 모드 또는 내용을 수정하지 마십시오)

이 경우 원래 foo, A ‘s foo 및 B가 3 방향으로 병합되면 A 와 동일한 모드 / 콘텐츠 bar로 원하는 경로 이름 bar이 생성됩니다 foo.
따라서 A에는 파일에 대한 올바른 모드와 내용이 있으며 올바른 경로 이름이 있습니다 (즉, bar).

커밋 37b65ce , Git v2.21.0-rc0, 2018 년 12 월, 최근 충돌 충돌 해결이 개선되었습니다.
그리고 bbafc9c의 커밋 firther은 파일 고려의 중요성 설명 내용을 이름 바꾸기 / 이름 바꾸기 (2to1) 충돌에 대한 처리를 개선하여를 :

  • collide_path~HEAD및에 파일을 저장하는 대신 collide_path~MERGE파일이 양방향으로 병합되고에 기록됩니다 collide_path.
  • 이름을 바꾸지 않고 히스토리 측면에서 파일에 대한 변경 사항을 무시하고 색인에서 이름이 바뀐 측에 존재하는 이름이 바뀐 파일의 버전을 기록하는 대신에 이름이 바뀐 컨텐츠에 대해 3 방향 컨텐츠 병합을 수행합니다. 경로를 지정한 다음 2 단계 또는 3 단계에서 저장하십시오.
  • 각 이름 변경에 대한 내용 병합에 충돌이있을 수 있으므로 이름이 바뀐 두 파일을 병합해야하기 때문에 중첩 된 충돌 마커가 생길 수 있습니다.

답변