질문 : Git 및 Mercurial-비교 및 대조
잠시 동안 저는 개인 프로젝트에 Subversion을 사용하고 있습니다.
점점 더 Git과 Mercurial, DVCS에 대한 훌륭한 이야기를 계속 듣고 있습니다.
DVCS 전체에 소용돌이를주고 싶지만 어느 옵션에도 익숙하지 않습니다.
Mercurial과 Git의 차이점은 무엇입니까?
참고 : 나는 어떤 것이 "최고"인지, 어떤 것부터 시작해야하는지 알아 내려는 것이 아닙니다. 구현과 철학이 어떻게 다른지 알고 싶기 때문에 주로 유사하고 다른 주요 영역을 찾고 있습니다.
답변
면책 조항 : 저는 Git을 사용하고, git 메일 링리스트에서 Git 개발을 따르고, 심지어 Git (주로 gitweb)에 약간 기여합니다. 문서에서 Mercurial을 알고 있고 FreeNode의 #revctrl IRC 채널에 대한 토론에서 일부를 알고 있습니다.
이 글을 작성하는 동안 Mercurial에 대한 도움을 주신 #mercurial IRC 채널의 모든 분들께 감사드립니다.
요약
여기에 PHPMarkdown / MultiMarkdown / Markdown의 Maruku 확장과 같은 테이블 구문이 있으면 좋을 것입니다.
- 리포지토리 구조 : Mercurial은 문어 병합 (두 개 이상의 부모 포함)을 허용하지 않으며 커밋되지 않은 개체에 태그를 지정하지도 않습니다.
- 태그 : Mercurial은 리포지토리 별 태그에 대한 특수 규칙과 함께 버전이 지정된
.hgtags
.hg/localtags
로컬 태그도 지원합니다. Git 태그는 refsrefs/tags/
네임 스페이스에있는 참조이며 기본적으로 가져 오기시 자동 추적되며 명시 적 푸시가 필요합니다. - 브랜치 : Mercurial에서 기본 워크 플로는 익명의 헤드를 기반으로합니다. Git은 경량의 명명 된 브랜치를 사용하며 원격 저장소의 브랜치를 따르는 특별한 종류의 브랜치 (원격 추적 브랜치)를 사용합니다.
- 개정 이름 지정 및 범위 : Mercurial은 개정 번호 를 저장소에 로컬로 제공하고이 로컬 번호에 대한 상대적 개정 (팁, 즉 현재 분기에서 계산) 및 개정 범위를 기반으로합니다. Git은 브랜치 팁과 관련하여 개정을 참조하는 방법을 제공하며 개정 범위는 토폴로지 (개정 그래프 기반)입니다.
- Mercurial은 이름 변경 추적을 사용하고 Git은 이름 변경 감지 를 사용하여 파일 이름 변경을 처리합니다.
- 네트워크 : Mercurial은 SSH 및 HTTP "스마트"프로토콜과 정적 HTTP 프로토콜을 지원합니다. 최신 Git은 SSH, HTTP 및 GIT "스마트"프로토콜과 HTTP (S) "dumb"프로토콜을 지원합니다. 둘 다 오프라인 전송을위한 번들 파일을 지원합니다.
- Mercurial은 확장 (플러그인) 및 확립 된 API를 사용합니다. Git에는 스크립팅 가능성 과 확립 된 형식이 있습니다.
리포지토리 구조, 리비전 저장
Mercurial과 Git이 다른 점이 몇 가지 있지만 유사하게 만드는 다른 점이 있습니다. 두 프로젝트 모두 서로의 아이디어를 빌립니다. 예를 들어 Mercurial (이전의 bisect extension )의 hg bisect
명령은 Git의 git bisect
명령에서 영감을 얻었으며 git bundle
hg bundle
에서 영감을 받았습니다.
Git에는 개체 데이터베이스에 4 가지 유형의 개체가 있습니다. 파일 내용을 포함하는 Blob 개체, 파일 이름 및 파일 권한의 관련 부분을 포함하는 디렉터리 구조를 저장하는 계층 적 트리 개체 (파일에 대한 실행 권한, 심볼릭 링크) , 저작자 정보를 포함하는 커밋 객체, 커밋 (프로젝트 최상위 디렉토리의 트리 객체를 통해)으로 표시되는 개정시 저장소 상태의 스냅 샷에 대한 포인터 및 0 개 이상의 상위 커밋에 대한 참조, 다른 객체를 참조하고 할 수있는 태그 객체 PGP / GPG를 사용하여 서명해야합니다.
Git은 객체를 저장하는 두 가지 방법을 사용합니다. 각 객체가 별도의 파일에 저장되는 느슨한 형식 (해당 파일은 한 번 작성되고 수정되지 않음)과 많은 객체가 하나의 파일에 델타 압축 된 상태로 저장되는 패킹 된 형식입니다. 작업의 원자 성은 객체를 작성한 후 새 객체에 대한 참조가 작성된다는 사실에 의해 제공됩니다 (원자 적으로 create + rename 트릭 사용).
git gc
(디스크 공간을 줄이고 성능을 개선하기 위해)를 사용하여 주기적으로 유지 관리해야하지만, 요즘에는 Git이 자동으로 수행합니다. (이 방법은 더 나은 저장소 압축을 제공합니다.)
Mercurial (내가 이해하는 한)은 파일 로그에 파일의 히스토리를 저장합니다 (제 생각에는 이름 변경 추적과 같은 추가 메타 데이터 및 일부 도우미 정보와 함께). 매니페스트 라는 평면 구조를 사용하여 디렉터리 구조 를 저장하고 변경 집합 (개정)에 대한 정보를 저장하는 변경 로그 라는 구조를 사용하여 커밋 메시지와 0 개, 하나 또는 두 개의 부모를 포함합니다.
Mercurial은 트랜잭션 저널 을 사용하여 작업의 원 자성을 제공하고 파일을 잘라내어 작업이 실패하거나 중단 된 후 정리합니다. Revlog는 추가 전용입니다.
Git과 Mercurial의 리포지토리 구조를 살펴보면 Git은 객체 데이터베이스 (또는 콘텐츠 주소 지정 파일 시스템)와 비슷하고 Mercurial은 기존의 고정 필드 관계형 데이터베이스와 더 비슷하다는 것을 알 수 있습니다.
차이점 :
Git에서 트리 개체는 계층 구조를 형성합니다. Mercurial 매니페스트 파일은 평면 구조입니다. Git blob 객체는 파일 내용의 한 버전을 저장합니다. Mercurial 파일 로그 에는 단일 파일의 전체 기록이 저장됩니다 (여기서 이름 변경과 관련된 모든 문제를 고려하지 않은 경우). 즉, Git이 Mercurial보다 빠를 수있는 다양한 작업 영역, 동일한 것으로 간주되는 다른 모든 것 (예 : 병합 또는 프로젝트 기록 표시), Mercurial이 Git보다 빠를 수있는 영역 (예 : 패치 적용 또는 표시)이 있습니다. 단일 파일의 기록). 이 문제는 최종 사용자에게 중요하지 않을 수 있습니다.
Mercurial의 변경 로그 구조의 고정 레코드 구조로 인해 Mercurial의 커밋에는 최대 두 개의 상위 항목 만있을 수 있습니다. Git의 커밋은 둘 이상의 부모를 가질 수 있습니다 ( "octopus merge"라고 함). (이론적으로) 문어 병합을 일련의 두 부모 병합으로 대체 할 수 있지만 Mercurial과 Git 저장소간에 변환 할 때 복잡한 문제가 발생할 수 있습니다.
내가 아는 한 Mercurial에는 Git의 주석이 달린 태그 (태그 객체)와 동등한 기능이 없습니다. 주석이 달린 태그의 특별한 경우는 서명 된 태그 (PGP / GPG 서명 포함)입니다. Mercurial과 함께 배포되는 GpgExtension을 사용하여 Mercurial과 동등한 작업을 수행 할 수 있습니다. Git 에서처럼 Mercurial에서 커밋되지 않은 객체 에 태그를 지정할 수는 없지만 그다지 중요하지 않다고 생각합니다 (일부 git 저장소는 태그가 지정된 blob을 사용하여 서명 된 태그를 확인하는 데 사용할 공개 PGP 키를 배포합니다).
참조: 분기 및 태그
Git에서 참조 (분기, 원격 추적 분기 및 태그)는 커밋의 DAG 외부에 있습니다 (필요한 경우). refs/heads/
네임 스페이스 ( 로컬 브랜치 )의 참조는 커밋을 가리키며 일반적으로 "git commit"에 의해 업데이트됩니다. 그들은 가지의 끝 (머리)을 가리 키기 때문에 그러한 이름이 있습니다. refs/remotes/<remotename>/
네임 스페이스 ( 원격 추적 분기 )의 참조는 커밋을 가리키고 원격 저장소 <remotename>
분기를 따르며 "git fetch"또는 이와 동등한 방법으로 업데이트됩니다. refs/tags/
네임 스페이스 ( tags )의 참조는 일반적으로 커밋 (경량 태그) 또는 태그 객체 (주석 및 서명 된 태그)를 가리키며 변경할 수 없습니다.
태그
Mercurial에서는 태그를 사용하여 개정판에 영구 이름을 지정할 수 있습니다. 태그는 무시 패턴과 유사하게 저장됩니다. 이는 전역 적으로 표시되는 태그가 리포지토리의 .hgtags
이는 두 가지 결과를 가져옵니다. 첫째, Mercurial은 모든 태그의 현재 목록을 가져오고 해당 파일을 업데이트하기 위해이 파일에 대한 특수 규칙을 사용해야합니다 (예 : 현재 체크 아웃 된 버전이 아닌 파일의 가장 최근에 커밋 된 개정판을 읽습니다). 둘째, 다른 사용자 / 다른 저장소에 새 태그가 표시되도록이 파일에 대한 변경 사항을 커밋해야합니다 (내가 이해하는 한).
hg/localtags
저장된 로컬 태그 도 지원합니다.이 태그는 다른 사람이 볼 수 없으며 양도 할 수 없습니다.
refs/tags/
네임 스페이스에 저장된 다른 객체 (일반적으로 태그 객체, 즉 커밋을 가리키는 객체)에 대한 고정 (상수) 이름 참조입니다. 기본적으로 리비전 세트를 가져 오거나 푸시 할 때 git은 가져 오거나 푸시되는 리비전을 가리키는 태그를 자동으로 가져 오거나 푸시합니다. 그럼에도 불구하고 어떤 태그를 가져 오거나 푸시할지 어느 정도 제어 할 수 있습니다.
Git은 경량 태그 (커밋을 직접 가리킴)와 주석이 달린 태그 (태그 객체를 가리키며, 선택적으로 PGP 서명을 포함하는 태그 메시지를 포함하고 차례로 커밋을 가리킴)를 약간 다르게 처리합니다. 예를 들어 기본적으로 설명 할 때 주석이 달린 태그 만 고려합니다. "git describe"를 사용하여 커밋합니다.
Git에는 Mercurial의 로컬 태그와 동일한 기능이 없습니다. 그럼에도 불구하고 git 모범 사례는 준비된 변경 사항을 푸시하고 다른 사람들이 복제하고 가져 오는 별도의 공개 베어 저장소를 설정하는 것이 좋습니다. 즉, 푸시하지 않는 태그 (및 브랜치)는 리포지토리의 비공개입니다. heads
, remotes
또는 tags
이외의 네임 스페이스를 사용할 수도 있습니다 (예 : local-tags
대한 local-tags).
개인적인 의견 : 내 의견으로는 태그가 개정 그래프 외부에 있으므로 개정 그래프 외부에 있어야합니다 (개정 그래프에 대한 포인터입니다). 태그는 버전이 지정되지 않아야하지만 양도 가능해야합니다. Mercurial이 파일을 무시하는 것과 유사한 메커니즘을 사용한다는 것은 .hgtags
특별히 처리하거나 (트리 내의 파일은 전송할 수 있지만 일반적으로 버전이 지정됨) 로컬 전용 태그 ( .hg/localtags
localtags는 버전이 없지만 .hg/localtags
브랜치
Git에서 로컬 브랜치 (브랜치 팁 또는 브랜치 헤드)는 커밋에 대한 명명 된 참조이며 새 커밋을 늘릴 수 있습니다. 분기는 또한 활성 개발 라인을 의미 할 수 있습니다. 즉, 분기 팁에서 도달 할 수있는 모든 커밋을 의미합니다. 로컬 브랜치는 refs/heads/
네임 스페이스에 있으므로 예를 들어 'master'브랜치의 정규화 된 이름은 'refs / heads / master'입니다.
Git의 현재 분기 (체크 아웃 된 분기 및 새 커밋이 이동할 분기를 의미)는 HEAD 참조가 참조하는 분기입니다. 기호 참조가 아닌 커밋을 직접 가리키는 HEAD를 가질 수 있습니다. 익명의 명명되지 않은 분기에있는 이러한 상황을 분리 된 HEAD 라고합니다 ( "git branch"는 사용자가 '(no branch)'에 있음을 나타냅니다).
Mercurial에는 익명의 브랜치 (지점 헤드)가 있으며 북마크 확장을 통해 북마크를 사용할 수 있습니다. 이러한 책갈피 분기 는 순전히 로컬이며 해당 이름은 Mercurial을 사용하여 양도 할 수 없습니다 (버전 1.6까지). rsync 또는 scp를 사용하여 .hg/bookmarks
파일을 원격 저장소에 복사 할 수 있습니다. hg id -r <bookmark> <url>
을 사용하여 현재 책갈피 팁의 개정 ID를 가져올 수도 있습니다.
1.6부터 북마크를 밀거나 당길 수 있습니다. BookmarksExtension 페이지에는 Working With Remote Repositories 에 대한 섹션이 있습니다. Mercurial 북마크 이름은 전역 이라는 차이점이 있지만 Git의 'remote'정의는 원격 저장소의 이름에서 로컬 원격 추적 분기의 이름 으로 분기 이름 매핑을 설명합니다. 예를 들어 refs/heads/*:refs/remotes/origin/*
매핑은 'origin / master'원격 추적의 원격 저장소에서 'master'브랜치 ( 'refs / heads / master')의 상태를 찾을 수 있음을 의미합니다. 브랜치 ( 'refs / remotes / origin / master').
Mercurial은 브랜치 이름이 커밋 (변경 세트)에 포함 되는 소위 브랜치라고도합니다. 이러한 이름은 전역 (페치시 전송 됨)입니다. 이러한 브랜치 이름은 변경 세트 메타 데이터의 일부로 영구적으로 기록됩니다. 최신 Mercurial을 사용하면 "이름이 지정된 지점"을 닫고 지점 이름 기록을 중지 할 수 있습니다. 이 메커니즘에서 가지의 팁은 즉석에서 계산됩니다.
Mercurial의 "명명 된 브랜치"는 제 생각에 커밋 레이블 이라고해야합니다. "명명 된 분기"가 여러 팁 (여러 개의 자식없는 커밋)을 가질 수 있고 개정 그래프의 여러 분리 된 부분으로 구성 될 수도있는 상황이 있습니다.
Git에는 Mercurial "임베디드 브랜치"에 해당하는 것이 없습니다. 또한 Git의 철학은 브랜치가 일부 커밋을 포함한다고 말할 수 있지만 커밋이 일부 브랜치에 속한다는 것을 의미하지는 않습니다.
의욕 문서는 여전히 일명 복제에 의해 분기, 수명이 긴 지점에 대한 최소한 별도의 클론 (별도의 저장소) (저장소 워크 플로우 당 단일 지점)를 사용하도록 제안합니다.
Branches in pushing
Mercurial은 기본적으로 모든 머리를 밀어냅니다. 단일 브랜치 ( 단일 헤드 )를 푸시하려면 푸시하려는 브랜치의 팁 개정을 지정해야합니다. 개정 번호 (저장소에 로컬), 개정 식별자, 책갈피 이름 (저장소에 로컬, 전송되지 않음) 또는 포함 된 분기 이름 (지명 된 분기)으로 분기 팁을 지정할 수 있습니다.
내가 이해하는 한, Mercurial 용어로 "명명 된 브랜치"에있는 것으로 표시된 커밋을 포함하는 다양한 개정판을 푸시하면이 "명명 된 브랜치"가 사용자가 푸시하는 저장소에있을 것입니다. 이것은 그러한 임베디드 브랜치 ( "명명 된 브랜치")의 이름이 전역 적 (주어진 저장소 / 프로젝트의 복제본과 관련하여)임을 의미합니다.
기본적으로 ( push.default
구성 변수에 따라) "git push"또는 "git push < remote >"Git은 일치하는 브랜치를 푸시합니다. 즉, 사용자가 푸시하는 원격 저장소에 해당하는 로컬 브랜치 만 이미 있습니다. --all
옵션을 사용하여 git-push ( "git push --all")를 사용하여 모든 분기 를 푸시하고 "git push < remote > < branch >"를 사용하여 주어진 단일 분기 를 푸시 할 수 있으며 " git push < remote > HEAD "to push current branch .
remote.<remotename>.push
구성 변수를 통해 푸시 할 분기를 Git이 구성하지 않았다고 가정합니다.
Branches in fetching
참고 : 여기서는 "가져 오기"란 변경 사항을 로컬 작업과 통합 하지 않고 원격 저장소에서 다운로드하는 것을 의미하는 Git 용어를 사용합니다. 이것이 " git fetch
"와 " hg pull
"이하는 일입니다.
내가 올바르게 이해한다면 기본적으로 Mercurial 은 원격 저장소에서 모든 헤드 hg pull --rev <rev> <url>
"또는 " hg pull <url>#<rev>
"를 통해 가져올 분기를 지정할 수 있습니다. 단일 분기를 가져옵니다. 개정 식별자, "명명 된 분기"이름 (변경 로그에 포함 된 분기) 또는 책갈피 이름을 사용하여 <rev>를 지정할 수 있습니다. 그러나 (적어도 현재) 북마크 이름은 전송되지 않습니다. 모든 "명명 된 브랜치"개정판은 이전에 속합니다. "hg pull"은 익명의 이름없는 헤드로 가져온 브랜치의 팁을 저장합니다.
기본적으로 Git에서 ( "git clone"에 의해 생성 된 'origin'원격 및 "git remote add"를 사용하여 생성 된 원격의 경우) " git fetch
"(또는 " git fetch <remote>
")는 원격 저장소에서 모든 분기를 가져옵니다. refs/heads/
네임 스페이스), refs/remotes/
네임 스페이스에 저장합니다. 이는 예를 들어 원격 'origin'의 'master'(전체 이름 : 'refs / heads / master') 라는 분기가 'origin / master'원격 추적 분기 (전체 이름 : 'refs /)로 저장 (저장)된다는 것을 의미합니다. remotes / origin / master ').
git fetch <remote> <branch>
를 사용하여 Git에서 단일 브랜치 를 가져올 수 있습니다.-Git은 Mercurial unnamed heads와 비슷한 FETCH_HEAD에 요청 된 브랜치를 저장합니다.
이들은 강력한 refspec Git 구문의 기본 사례에 불과합니다. refspec을 사용하면 가져올 브랜치 및 저장할 위치를 지정 및 / 또는 구성 할 수 있습니다. 예를 들어 기본 "모든 분기 가져 오기"케이스는 '+ refs / heads / * : refs / remotes / origin / *'와일드 카드 참조 사양으로 표시되고 "fetch single branch"는 'refs / heads / <branch> :'의 약칭입니다. . Refspec은 원격 저장소의 브랜치 (ref) 이름을 로컬 참조 이름에 매핑하는 데 사용됩니다. 그러나 Git에서 효과적으로 작업 할 수 있도록 refspec에 대해 (많은) 알 필요가 없습니다 (주로 "git remote"명령 덕분에).
개인적 의견 : 개인적으로 Mercurial의 "이름이 지정된 분기"(변경 집합 메타 데이터에 포함 된 분기 이름 포함)는 특히 분산 버전 제어 시스템의 경우 전역 네임 스페이스가있는 잘못된 디자인이라고 생각합니다. 예를 들어 Alice와 Bob이 공통점이없는 브랜치 인 'for-joe'라는 이름의 "이름이 지정된 브랜치"가 저장소에있는 경우를 가정 해 보겠습니다. 그러나 Joe의 저장소에서이 두 분기는 단일 분기로 잘못 취급됩니다. 그래서 당신은 어떻게 든 지사 이름 충돌을 방지하는 규칙을 생각해 냈습니다. 이것은 Git에서는 문제가되지 않습니다. Joe의 저장소에서 Alice의 'for-joe'브랜치는 'alice / for-joe'이고 Bob에서는 'bob / for-joe'입니다. Mercurial wiki에서 제기 된 브랜치 ID 문제에서 브랜치 이름 분리를 참조하십시오.
Mercurial의 "북마크 브랜치"에는 현재 인 코어 배포 메커니즘이 없습니다.
차이점 :
이 영역은 james woodyatt 와 Steve Losh 가 답변에서 말했듯이 Mercurial과 Git의 주요 차이점 중 하나입니다. Mercurial은 기본적으로 익명의 경량 코드 라인을 사용하며 용어로는 "헤드"라고합니다. Git은 원격 리포지토리의 분기 이름을 원격 추적 분기의 이름에 매핑하는 인젝 티브 매핑과 함께 경량의 명명 된 분기를 사용합니다. Git은 브랜치의 이름을 "강제"하지만 (이름이 지정되지 않은 단일 브랜치를 제외하고 분리 된 HEAD라고하는 상황) 이것은 단일 저장소 패러다임에서 여러 브랜치를 의미하는 토픽 브랜치 워크 플로와 같은 브랜치가 많은 워크 플로에서 더 잘 작동한다고 생각합니다.
Naming revisions
Git에는 개정의 이름을 지정하는 여러 가지 방법이 있습니다 (예 : git rev-parse 맨 페이지에 설명 됨).
- 전체 SHA1 개체 이름 (40 바이트 16 진수 문자열) 또는 저장소 내에서 고유 한 하위 문자열
- 심볼릭 참조 이름, 예를 들어 'master'( 'master'브랜치 참조), 'v1.5.0'(태그 참조) 또는 'origin / next'(원격 추적 브랜치 참조)
- 접미사
^
to revision 매개 변수는 커밋 객체의 첫 번째 부모를 의미하고^n
은 병합 커밋의 n 번째 부모를 의미합니다. 개정 매개 변수에 대한 접미사~n
은 첫 번째 상위 행에있는 커밋의 n 번째 조상을 의미합니다. 이러한 접미사를 결합하여 기호 참조의 경로를 따르는 개정 지정자를 형성 할 수 있습니다 (예 : 'pu ~ 3 ^ 2 ~ 3'). - "git describe"의 출력 (예 : 'v1.6.5.1-75-'의 출력, 즉 가장 가까운 태그, 선택적으로 뒤에 대시 및 여러 커밋, 대시, 'g'및 축약 된 개체 이름이옵니다.) g5bf8097 '.
여기에 언급되지 않은 reflog와 관련된 개정 지정자도 있습니다. Git에서 각 개체는 커밋, 태그, 트리 또는 Blob에 SHA-1 식별자가 있습니다. 지정된 개정판에서 트리 (디렉토리) 또는 blob (파일 내용)을 참조하는 'next : Documentation'또는 'next : README'와 같은 특수 구문이 있습니다.
Mercurial에는 변경 세트 이름을 지정하는 다양한 방법이 있습니다 (예 : hg 맨 페이지에 설명 됨).
- 일반 정수는 개정 번호로 처리됩니다. 개정 번호는 주어진 저장소에 로컬 이라는 것을 기억해야합니다. 다른 저장소에서는 다를 수 있습니다.
- 음의 정수는 팁에서 순차적 오프셋으로 처리되며 -1은 팁을 나타내고 -2는 팁 이전의 개정을 나타냅니다. 또한 저장소에 로컬입니다.
- 고유 한 개정 식별자 (40 자리 16 진수 문자열) 또는 고유 접두사.
- 태그 이름 (주어진 개정과 연관된 기호 이름) 또는 책갈피 이름 (확장자 포함 : 지정된 헤드와 연관된 기호 이름, 저장소에 로컬) 또는 "명명 된 분기"(커밋 레이블, "명명 된 분기"에 의해 제공된 개정)은 다음과 같습니다. 주어진 커밋 레이블이있는 모든 커밋의 팁 (자녀없는 커밋), 그러한 팁이 둘 이상인 경우 가장 큰 개정 번호)
- 예약 된 이름 "tip"은 항상 최신 개정판을 식별하는 특수 태그입니다.
- 예약 된 이름 "null"은 null 개정을 나타냅니다.
- 예약 된 이름 "." 작업 디렉토리 상위를 나타냅니다.
차이점
위의 목록을 비교해 볼 수 있듯이 Mercurial은 리포지토리에 로컬로 수정 번호를 제공하지만 Git은 제공하지 않습니다. 반면 Mercurial은 저장소 (적어도 ParentrevspecExtension 없이)에 로컬 인 'tip'(현재 분기)에서만 상대 오프셋을 제공하는 반면 Git은 모든 팁에서 따르는 커밋을 지정할 수 있습니다.
가장 최근 개정판은 Git에서는 HEAD, Mercurial에서는 "tip"입니다. Git에는 null 개정이 없습니다. Mercurial과 Git은 둘 다 많은 루트를 가질 수 있습니다 (하나 이상의 부모없는 커밋을 가질 수 있습니다. 이것은 일반적으로 이전에 분리 된 프로젝트가 참여한 결과입니다).
참조 : Elijah 's Blog (newren 's)에있는 다양한 종류의 개정 지정자 기사.
개인적 의견 : 개정 번호 가 과대 평가되었다고 생각합니다 (적어도 분산 된 개발 및 / 또는 비선형 / 분기 기록). 첫째, 분산 버전 제어 시스템의 경우 저장소에 로컬이거나 특정 방식으로 중앙 번호 지정 기관으로 일부 저장소를 처리해야합니다. 둘째, 더 긴 역사를 가진 대규모 프로젝트는 5 자리 범위의 수정 횟수를 가질 수 있으므로 6-7 자 수정 식별자로 단축 된 것보다 약간의 이점 만 제공하고 수정이 부분적으로 만 주문되는 동안 엄격한 순서를 암시합니다 (여기서는 개정 n과 n + 1은 부모와 자식 일 필요가 없습니다).
Revision ranges
Git에서 수정 범위는 토폴로지 입니다. 일반적으로 볼 수있는 A..B
구문은 선형 기록의 경우 A에서 시작하여 (A 제외) 수정 범위를 의미하며 B에서 끝나는 (즉, 범위는 아래에서 열림 ^AB
축약 형 ( "구문 설탕")입니다. 히스토리 순회 명령은 A에서 도달 할 수있는 것을 제외하고 B에서 도달 할 수있는 모든 커밋을 의미합니다. 이는 A..B
범위의 동작이 완전히 예측 가능하고 매우 유용하다는 것을 의미합니다 A..B
그런 다음 A와 B의 공통 조상 (기본 병합)에서 개정 B까지의 개정 범위.
Mercurial에서 개정 범위는 개정 번호 범위를 기반으로합니다. A:B
구문을 사용하여 지정되며 Git 범위와는 반대로 닫힌 간격으로 작동합니다. 또한 범위 B : A는 역순으로 범위 A : B이며 Git에서는 그렇지 않습니다 (그러나 A...B
구문에 대한 아래 참고 사항 참조). 그러나 이러한 단순성은 대가를 동반합니다. 수정 범위 A : B는 A가 B의 조상이거나 그 반대 인 경우에만 의미가 있습니다. 그렇지 않으면 (내 생각에) 범위는 예측할 수 없으며 결과는 저장소에 로컬입니다 (수정 번호는 저장소에 로컬이기 때문에).
이 문제는 새로운 토폴로지 개정 범위 가있는 Mercurial 1.6에서 수정되었습니다. 여기서 'A..B'(또는 'A :: B')는 X의 후손이자 Y의 조상 인 변경 집합 집합으로 이해됩니다. , 나는 Git의 '--ancestry-path A..B'와 동일하다고 생각합니다.
Git은 또한 개정판의 대칭 적 차이에 대한 A...B
AB --not $(git merge-base AB)
아니라 AB를 의미합니다. 즉, A 또는 B에서 도달 할 수있는 모든 커밋을 의미하지만 둘 다에서 도달 할 수있는 모든 커밋을 제외합니다 (공통 조상에서 도달 할 수 있음).
Renames
Mercurial은 이름 변경 추적 을 사용하여 파일 이름 변경을 처리합니다. 이것은 파일의 이름이 변경되었다는 사실에 대한 정보가 커밋시 저장됨을 의미합니다. Mercurial에서이 정보는 filelog (파일 revlog) 메타 데이터의 "enhanced diff"형식으로 저장됩니다. 그 결과 hg rename
/ hg mv
...를 사용해야하거나 유사성 기반 이름 바꾸기 감지를 수행 hg addremove
Git은 파일 이름 변경을 처리 하기 위해 이름 변경 감지 를 사용한다는 점에서 버전 제어 시스템 중에서 고유합니다. 즉, 파일의 이름이 변경되었다는 사실은 병합을 수행 할 때 또는 diff를 표시 할 때 (요청 / 구성된 경우) 필요할 때 감지됩니다. 이는 이름 변경 감지 알고리즘을 개선 할 수 있고 커밋시 고정되지 않는 장점이 있습니다.
Git과 Mercurial 모두 단일 파일의 기록을 표시 할 때 이름을 변경하려면 --follow
git blame
/ hg annotate
에서 파일의 행별 기록을 표시 할 때 이름을 바꿀 수 있습니다.
Git에서 git blame
명령은 코드 이동을 추적 할 수 있으며 코드 이동이 건전한 파일 이름 변경의 일부가 아니더라도 한 파일에서 다른 파일로 코드를 이동 (또는 복사) 할 수 있습니다. 내가 아는 한이 기능은 Git에 고유합니다 (작성 당시, 2009 년 10 월).
Network protocols
Mercurial과 Git 모두 동일한 파일 시스템의 저장소에서 가져 오기 및 푸시를 지원합니다. 여기서 저장소 URL은 저장소에 대한 파일 시스템 경로 일뿐입니다. 둘 다 번들 파일 에서 가져 오기를 지원합니다.
Mercurial은 SSH 및 HTTP 프로토콜을 통한 가져 오기 및 푸시를 지원합니다. SSH의 경우 대상 컴퓨터에 액세스 할 수있는 셸 계정과 설치 / 사용 가능한 hg 사본이 필요합니다. HTTP 액세스를 위해서는 hg-serve
또는 Mercurial CGI 스크립트 실행이 필요하며 Mercurial은 서버 시스템에 설치되어야합니다.
Git은 원격 저장소에 액세스하는 데 사용되는 두 가지 종류의 프로토콜을 지원합니다.
- SSH 및 사용자 정의 git : // 프로토콜 (
git-daemon
)을 통한 액세스를 포함하는 "스마트"프로토콜 은 서버에 git을 설치해야합니다. 이러한 프로토콜의 교환은 클라이언트와 서버가 공통된 개체에 대해 협상 한 다음 팩 파일을 생성 및 전송하는 것으로 구성됩니다. Modern Git은 "스마트"HTTP 프로토콜에 대한 지원을 포함합니다. - HTTP 및 FTP (가져 오기 전용) 및 HTTPS (WebDAV를 통한 푸시 용)를 포함하는 "dumb"프로토콜
git update-server-info
(보통 후크에서 실행). 교환은 커밋 체인을 걷는 클라이언트와 필요에 따라 느슨한 개체 및 팩 파일을 다운로드하는 것으로 구성됩니다. 단점은 엄격하게 요구되는 것보다 더 많이 다운로드한다는 것입니다 (예 : 하나의 packfile 만있는 코너의 경우에는 몇 개의 수정본 만 가져 오더라도 전체 다운로드를 받게됩니다). 그리고 완료하려면 많은 연결이 필요할 수 있습니다.
확장: 스크립트 가능성과 확장(플러그인) 비교
Mercurial은 성능을 위해 C로 작성된 일부 핵심 코드와 함께 Python 으로 구현됩니다. 추가 기능을 추가하는 방법으로 확장 (플러그인) 작성을위한 API를 제공합니다. "북마크 브랜치"또는 서명 개정판과 같은 일부 기능은 Mercurial과 함께 배포 된 확장 기능으로 제공되며이를 켜야합니다.
Git은 C , Perl 및 쉘 스크립트 로 구현됩니다. Git은 스크립트에서 사용하기에 적합한 많은 저수준 명령 ( plumbing)을 제공합니다. 새로운 기능을 도입하는 일반적인 방법은 Perl 또는 쉘 스크립트로 작성하고 사용자 인터페이스가 안정화되면 성능, 이식성 및 코너 케이스를 피하는 쉘 스크립트의 경우 C로 다시 작성하는 것입니다 (이 절차를 builtinification 이라고 함).
Git은 [repository] 형식과 [network] 프로토콜에 의존하고 구축됩니다. 언어 바인딩 대신 다른 언어로 Git을 (일부 또는 전체) 재 구현 합니다 (일부는 부분적으로 재 구현되고 git 명령을 부분적으로 래퍼합니다) : JGit (Java, EGit, Eclipse Git Plugin에서 사용), Grit (Ruby) , Dulwich (Python), git # (C #).
TL, DR
출처 : https://stackoverflow.com/questions/1598759/git-and-mercurial-compare-and-contrast
'개발관련 > Git' 카테고리의 다른 글
git pull --rebase의 사용시기 (0) | 2021.09.14 |
---|---|
'git merge'와 'git rebase'의 차이점 (0) | 2021.09.14 |
경고 : push.default is unset; its implicit value is changing in Git 2.0 (0) | 2021.09.10 |
Gemfile.lock이 .gitignore에 포함 되어야 하는가? (0) | 2021.09.08 |
Git에서 처음 두 커밋을 Squash 하는 방법 (0) | 2021.09.07 |