Git에서 하위 디렉토리를 어떻게 병합합니까?


84

로컬 Git 브랜치에서 원격 Git 브랜치로 하위 디렉토리의 변경 사항 만 병합 할 수 있습니까? 아니면 "모두 또는 아예"입니까?

예를 들면 다음과 같습니다.

branch-a
 - content-1
 - dir-1
   - content-2

branch-b
 - content-1
 - dir-1
   - `content-2

branch-a dir-1의 내용과 branch-b dir-1의 내용 만 병합하고 싶습니다.


답변:


81

SO 질문 " 선택 파일을 git-merge와 어떻게 병합합니까? "에 대한 대안으로 방금 git read-tree를 기반으로 전체 하위 디렉토리를 병합하는 데 더 적합 할 수있는 이 GitHub 스레드 를 찾았 습니다 .

  • 내 저장소 => cookbooks
    내 저장소 대상 디렉토리 =>cookbooks/cassandra
  • 원격 저장소 => infochimps
    병합하려는 원격 저장소 소스 cookbooks/cassandra=>infochimps/cookbooks/cassandra

병합하는 데 사용한 명령은 다음과 같습니다.

  • 저장소를 추가하고 가져옵니다.
git remote add -f infochimps git : //github.com/infochimps/cluster_chef.git
  • 병합 수행
git merge --allow-unrelated-histories -s ours --no-commit infochimps / master

( 'ours'전략 ( -s ours) 을 사용하여 병합을 수행 하여 소스 분기의 변경 사항을 삭제합니다. 이는 infochimps/master대상 분기의 파일을 실제로 수정하지 않고 병합 된 사실을 기록합니다. )

  • 만 병합 infochimps/cookbooks/cassandra으로cassandra
git read-tree --prefix = cassandra / -u infochimps / master : cookbooks / cassandra

이것은 소스 저장소 cookbooks/cassandra의 업스트림 브랜치 에서 필요한 소스 하위 디렉토리 즉 , 트리 만 읽습니다 .

점을 유의 대상 하위 디렉토리 이름도해야한다 cookbooks/cassandra, 또는 당신은 볼 것이다 :

fatal: Not a valid object name
  • 변경 커밋
 git commit -m 'merging in infochimps cassandra'

추가

기괴합니다. [편집]read-tree단계는 다음과 같이 실패 할 수 있습니다.

error: Entry 'infochimps/cookbooks/cassandra/README' overlaps with 'cookbooks/cassandra/README'. Cannot bind.

... 두 파일이 동일한 경우에도 . 도움이 될 수 있습니다.

git rm -r cassandra
git read-tree --prefix=cassandra/ -u infochimps/master:cookbooks/cassandra

그러나 물론, 이것이 원하는 것을 수동으로 확인하십시오.


3
@Martin git-scm.com/docs/git-rev-parse#_specifying_revisions는 찾아 <rev>:<path>예를 들어, HEAD:README, :README,master:./README
VonC

6
git read-tree단계는 나를 위해 실패error: Entry 'foo/bar/baz.php' overlaps with 'bar/baz.php'. Cannot bind.
웨스턴 Ruter에게

1
@VonC하지만 아니, 나는 역사가 필요합니다. 이 답변은 트리 내의 파일에 대한 로컬 수정이있는 경우를 설명하지 않습니다. 그래서 병합해야합니다.
Weston Ruter

저에게는 overlaps with위에서 언급 한 오류가 동일한 파일 에서도 나타납니다 . 그게 얼마나 이상해?
ulidtko

1
@ChrisHalcrow infochimps/master병합 된 사실을 기록 하지만 실제로 대상 분기의 파일을 수정하지 않습니다. 다음 단계 git read-tree --prefix=cassandra에서 수정이 이루어 지기 때문 입니다. 최종 커밋은 실제 "병합"내용을 기록합니다.
VonC

29

내 예를 들어, 자신의 업스트림 버전을 반영하고 최신 코드로 가져 오는 분기 '소스'와 분기 '대상'이 있다고 가정합니다. 'source'브랜치에만 존재하는 newFeature라는 저장소의 하위 디렉토리를 원한다고 가정 해 보겠습니다.

git checkout destination
git checkout source newFeature/
git commit -am "Merged the new feature from source to destination branch."
git pull --rebase
git push

내가 본 다른 모든 것보다 훨씬 덜 복잡하며 여기 에서 찾을 수 있습니다 .

이것은 '실제 병합'이 아니므로 대상 브랜치에 newFeature에 대한 커밋 정보가없고 해당 하위 디렉터리의 파일에 대한 수정 만 있습니다. 그러나 아마도 나중에 전체 분기를 병합하거나 폐기 할 것이기 때문에 문제가되지 않을 수 있습니다.


2
역사를 보존합니까?
Bibrak

2
@Bibrak이 접근 방식은 역사를 보존하지 않습니다. 현재 명령과는 전혀 다릅니다.
Ravi Gidwani

6

Eclipse 의 포럼 스레드 에서 이것을 얻었 으며 매력처럼 작동했습니다.

git checkout source-branch
git checkout target-branch <directories-or-files-you-do-**NOT**-want> 
git commit
git checkout target-branch
git merge source-branch

1
나는 이것을 시도하고 내가 원하지 않는 타겟 브랜치의 소스 브랜치의 디렉토리로 끝났습니다. dirs 세트로 지정 했음에도 불구하고 두 번째 명령에서 원하지 않았습니다.
마라톤

2
이것은 병합되지 않고 폴더를 소스 분기의 폴더로 바꿉니다.
Gp2mv3

@ Gp2mv3 나는 그것이 견고 해 보인다고 생각한다. checkout폴더를 동일하게 만듭니다 => 차이점은 checkout폴더 해제 => merge차이점입니다. 유효한 전략입니다.
Caveman

5

두 개의 분기가 있지만 분기 a 에서 분기 bdir-1 의 기록 만 병합하려는 OP의 시나리오가 주어지면 다음과 같습니다.

# Make sure you are in the branch with the changes you want
git checkout branch-a

# Split the desired folder into its own temporary branch
# This replays all commits, so it could take a while
git subtree split -P dir-1 -b temp-branch

# Enter the branch where you want to merge the desired changes into
git checkout branch-b

# Merge the changes from the temporary branch
git subtree merge -P dir-1 temp-branch

# Handle any conflicts
git mergetool

# Commit
git commit -am "Merged dir-1 changes from branch-a"

# Delete temp-branch
git branch -d temp-branch

0

branch-a와 branch-b를 모두 포함하는 Git 리포지토리를 만듭니다.

git checkout branch-a
git diff branch-b dir-1 > a.diff
patch -R -p1 < a.diff

14
이 답변에는 더 많은 정보가 필요합니다. 실제 코드와 주석은 무엇입니까?
qodeninja 2013-10-04

2
요청자가 병합을 원합니다. 패치를 사용하여 변경 사항을 전달하면 모든 커밋이 자동으로 하나의 패치로 압축되고 기록이 손실됩니다.
에릭

0

git cherry-pick원하는 커밋을 선택하고 이러한 커밋 만 병합하는 데 사용 합니다. 여기서 핵심 트릭은 이러한 커밋을 쉬운 방법으로 가져 오는 것입니다 (그러므로 수동으로 Git 로그를 확인하고 직접 입력하여 확인할 필요가 없습니다). 방법은 다음과 같습니다. 다음과 git log같이 커밋의 SHA-1 ID 를 인쇄하는 데 사용 합니다 .

git log ^<commit-a> <commit-b> --pretty=format:"%h" --reverse -- <subdir>

'commit-a'는 병합 할 브랜치의 시작점 직전 커밋이고 'commit-b'는 병합 할 브랜치의 마지막 커밋입니다. '--reverse'는 이러한 커밋을 나중에 체리 피킹을 위해 역순으로 인쇄합니다.

그런 다음 다음과 같이하십시오.

git cherry-pick $(git log ^<commit-a> <commit-b> --pretty=format:"%h" --reverse -- <subdir>)

간단하고 안정적인 두 단계입니다!


다른 지점의 단일 디렉토리를 병합하고 역사를 보존하는 훌륭하고 유일한 방법입니다. 하지만 ... ... 하나의 커밋 만 원하는 경우 첫 번째 명령의 모든 커밋에서 분기를 만든 다음 새 분기를 하나의 스쿼시 병합으로 병합합니다 ....
tom

1
이것은 발견 된 커밋이 해당 디렉토리에만 영향을 미친다고 가정합니다. 커밋이 병합하려는 디렉토리와 그렇지 않은 디렉토리 모두에 영향을 미치면 너무 많이 선택됩니다.
Scott

Two steps, simple and stable!전혀 간단 하지 않습니다 :)
Rafa

그렇다면 어떤 식으로 간단하다고 생각하십니까? @Rafa
로버트

@Robert 나는 당신의 대답이 간단하지 않다는 것을 암시하려는 것이 아닙니다. git끔찍한 인터페이스와 수수께끼와 임의의 이름과 의미로 가득 찬 끔찍한 정신 모델을 가진 결함은 모두 켜져 있습니다.
Rafa
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.