git에 병합 한 후 git-svn dcommit은 위험합니까?


133

git-svn을 시험해보기위한 나의 동기는 쉬운 병합 및 분기입니다. 그런 다음 남자 git-svn (1)이 말합니다.

커밋하려는 분기에서는 git-merge 또는 git-pull을 실행하지 않는 것이 좋습니다. Subversion은 합리적이거나 유용한 방식으로 병합을 나타내지 않습니다. Subversion을 사용하는 사용자는 병합 한 내용을 볼 수 없습니다. 또한 SVN 분기의 미러 인 자식 분기를 병합하거나 가져 오면 dcommit이 잘못된 분기를 커밋 할 수 있습니다.

이것은 svn / trunk (또는 브랜치)에서 로컬 브랜치를 생성하고 해킹하고 svn / trunk로 다시 병합 한 다음 dcommit 할 수 없다는 것을 의미합니까? svn 사용자는 svn pre 1.5.x에서 병합 한 것과 같은 엉망이 항상 있었음을 알지만 다른 단점이 있습니까? 그 마지막 문장도 저를 걱정합니다. 사람들은 일상적으로 이런 종류의 일을합니까?


14
이것은 단지 잘못 : Subversion은 합리적인 또는 유용한 방식으로 병합을 나타내지 않는; Subversion을 사용하는 사용자는 병합 한 내용을 볼 수 없습니다. 또한 SVN 분기의 미러 인 자식 분기를 병합하거나 가져 오면 dcommit이 잘못된 분기를 커밋 할 수 있습니다. Subversion은 git merge 추적 정보뿐만 아니라 훨씬 더 세분화 된 정보도 나타낼 수 있습니다. 적절한 svn : mergeinfo를 작성하지 못하거나 적절한 분기에 dcommit을 만드는 것은 git-svn입니다.
Alexander Kitaev

2
@AlexanderKitaev : git-svn 문서 는 이후 변경되었으며 추가적으로 --mergeinfo=<mergeinfo>SVN에 병합 정보를 전달할 수 있는 옵션이 있습니다. 어떻게 사용 해야하는지 잘 모르겠습니다.
Mr_and_Mrs_D

답변:


174

사실, --no-ffgit merge 옵션으로 더 나은 방법을 찾았습니다 . 내가 사용한이 스쿼시 기술은 더 이상 필요하지 않습니다.

나의 새로운 워크 플로우는 다음과 같습니다 :

  • 나는에서 dcommit있는 유일한 지점과 그 클론 SVN 저장소 인 "마스터"지점을 가지고 ( -s당신이 저장소의 표준 SVN 레이아웃을 가지고 가정 trunk/, branches/그리고 tags/) :

    git svn clone [-s] <svn-url>
    
  • 로컬 지점 "작업"에서 작업합니다 ( -b"작업"분기 작성).

    git checkout -b work
    
  • "작업"분기에 로컬로 커밋합니다 ( -s커밋 메시지를 사인 오프합니다). 속편에서는 3 번의 로컬 커밋을했다고 가정합니다.

    ...
    (work)$> git commit -s -m "msg 1"
    ...
    (work)$> git commit -s -m "msg 2"
    ...
    (work)$> git commit -s -m "msg 3"
    

이제 SVN 서버에 커밋하려고합니다.

  • [결국] SVN 서버에서 커밋하고 싶지 않은 수정 사항을 숨기십시오 (주로 컴파일을 가속화하고 주어진 기능에 집중하기 위해 메인 파일에서 일부 코드에 주석을 달았습니다)

    (work)$> git stash
    
  • SVN 저장소로 마스터 브랜치 리베이스 (SVN 서버에서 업데이트)

    (work)$> git checkout master
    (master)$> git svn rebase
    
  • 작업 지점으로 돌아가서 마스터와 리베이스

    (master)$> git checkout work
    (work)$> git rebase master
    
  • 예를 들어 다음을 사용하여 모든 것이 올바르게 작동하는지 확인하십시오.

    (work)$> git log --graph --oneline --decorate
    
  • 이제이 훌륭한 --no-ff옵션을 사용하여 "작업"브랜치에서 "마스터"로 세 커밋을 모두 병합해야합니다.

    (work)$> git checkout master
    (master)$> git merge --no-ff work
    
  • 로그 상태를 확인할 수 있습니다.

    (master)$> git log --graph --oneline --decorate
    * 56a779b (work, master) Merge branch 'work'
    |\  
    | * af6f7ae msg 3
    | * 8750643 msg 2
    | * 08464ae msg 1
    |/  
    * 21e20fa (git-svn) last svn commit
    
  • 이제 amendSVN 친구들의 마지막 커밋 을 편집 ( )하고 싶을 수도 있습니다 (그렇지 않으면 "Merge branch 'work'"메시지와 함께 단일 커밋 만 표시됩니다)

    (master)$> git commit --amend
    
  • 마지막으로 SVN 서버에서 커밋

    (master)$> git svn dcommit
    
  • 작업으로 돌아가서 숨김 파일을 복구하십시오.

    (master)$> git checkout work
    (work)$> git stash pop
    

4
이것은이 git n00b가 찾고 있던 작업 흐름입니다. 로컬 브랜치를 생성하여 버그를 수정하고, 무엇이든하고 SINGLE 커밋으로 svn으로 보냅니다. 여기에 가장 높은 등급의 답변을 사용하면 내가하고있는 일이 엉망이되었습니다. 오류없이 svn rebase를 git 할 수 없었습니다.
João Bragança

19
이것은 op에서 인용 한 git-svn 문서가 정확히 경고하지 않습니까? --no-ff옵션 과 함께 merge를 실행 하면 빨리 감기 대신 명시 적으로 병합 커밋 (두 부모와의 커밋)을 만듭니다. svn-tracking 브랜치의 모든 커밋이 단일 부모 커밋인지 확인하려면 모든 병합이 빨리 진행되거나 (이를 --ff-only도울 수 있음) 트렁크가 등 뒤로 변경 된 경우 --squash, 맞습니까?
jemmons

4
이것은 즉시 삭제하는 일회용 브랜치에서 작동하지만 git svn dcommit지정한 git commit을 다시 작성합니다. 이것은 다른 부모를 잃어 버렸고 이제 git repo에 해당 분기를에 병합 한 기록이 없습니다 master. 또한 작업 디렉토리가 일관성이없는 상태로 남을 수도 있습니다.
rescdsk

9
사용하는 git merge --no-ff work -m "commit message"대신 추가로 필요의 git commit --amend단계
tekumara

3
나를 위해 나는 마지막 커밋뿐만 아니라 모든 커밋을 유지하고 싶기 때문에 "git merge --ff-only work"를 사용하고 싶다
Moataz Elmasry

49

git-svn으로 로컬 브랜치를 만들 수 있습니다. 로컬 브랜치를 사용하고 업스트림 svn 브랜치를 병합하기 위해 git을 사용하지 않는 한 괜찮습니다.

svn 서버를 추적하는 데 사용하는 "마스터"분기가 있습니다. 이것은 내가 위임 한 유일한 지점입니다. 내가 일을하고 있다면, 토픽 브랜치를 만들고 해결합니다. 커밋하고 싶을 때 다음을 수행합니다.

  1. 토픽 브랜치에 모든 것을 커밋
  2. 자식 svn rebase (작업과 svn 사이의 충돌을 해결하십시오)
  3. 자식 체크 아웃 마스터
  4. 자식 svn rebase (다음 단계는 빨리 진행되는 병합입니다. 아래의 Aaron 의견 참조)
  5. 자식 병합 topic_branch
  6. 병합 충돌 해결 (이 시점에는 없어야 함)
  7. 자식 svn dcommit

또한 svn으로 푸시해서는 안되는 로컬 변경 사항 (디버깅 용)을 유지 해야하는 또 다른 상황이 있습니다. 이를 위해 위의 마스터 지점뿐만 아니라 일반적으로 작업하는 "work"라는 지점도 있습니다. 주제 분기는 작업에서 분기됩니다. 거기에서 작업을 커밋하려면 master를 체크 아웃하고 cherry-pick을 사용하여 svn에 커밋하려는 작업 분기에서 커밋을 선택하십시오. 세 가지 로컬 변경 커밋을 커밋하지 않기를 원하기 때문입니다. 그런 다음 마스터 브랜치에서 커밋하고 모든 것을 리베이스합니다.

가치있는 달리기 git svn dcommit -n커밋하려는 것을 정확히 커밋하려고 먼저 . 자식과 달리 svn에서 기록을 다시 쓰는 것은 어렵습니다!

cherry-pick을 사용하는 것보다 로컬 변경 커밋을 건너 뛰면서 토픽 브랜치에서 변경 사항을 병합하는 더 좋은 방법이 있어야한다고 생각합니다.


내가 이것을 올바르게 이해한다면, git에서 svn과 git을 병합하는 것은 괜찮지 만 svn과 svn은 맞지 않습니까? 그래서 git에서 svn / branch를 svn / trunk와 병합 할 수 없습니까?
Knut Eldhuset

1
사소한 일을하고 싶다면 SVN에서 기록을 다시 작성하는 것은 어렵습니다. 사소한 경우에는 실제로 불가능합니다.
아리스토텔레스 Pagaltzis

19
Greg Hewgill의 답변은 많은 표를 얻었지만 그 말이 잘못되었다고 생각합니다. topic_branch에서 master로 병합하는 것은 단지 빨리 감기 병합 인 경우에만 안전합니다. 병합 커밋이 필요한 실제 병합 인 경우 dcommit 할 때 병합 커밋이 손실됩니다. 다음에 topic_branch를 마스터로 병합하려고 시도하면 git은 첫 번째 병합이 발생하지 않았다고 생각하고 모든 지옥이 풀립니다. 질문 참조 왜 자식 svn의 dcommit 지역 지점에 대한 병합 커밋의 역사를 잃게합니까?
Aaron

1
@Greg Hewgill, 편집 내용이 명확하지 않습니다. 리베이스는 "다음 커밋을 빨리 감기 병합"이라고 썼습니다. 빨리 감기 병합에는 커밋이 포함되지 않기 때문에 그 의미가 무엇인지 모르겠지만 "다음 병합을 빨리 감기 병합"으로 설정했을 수 있습니다. 그러나 그것이 당신이 의미하는 바라면 정확하지 않습니다. topic_branch에서 마스터로의 병합이 빠른 병합인지의 여부는 지점 이후 마스터에 대한 커밋이 있는지 여부에 따라 다릅니다. 당신이 주인을 리베이스하면 후속 병합이 필요하므로, 즉, 마스터에 새로운 커밋을 생성 하지 빨리 감기 병합.
Aaron

1
@Aaron : 응, 내가 무슨 생각을했는지 모르겠어. 나는 그것을 다시 고쳤습니다. 문제 중 하나는 Git에 작업을 수행하는 방법이 너무 많아 특정 작업 순서 를 설명하는 데 약간의 설명이 필요하다는 것 입니다.
Greg Hewgill

33

간단한 해결책 : 병합 후 '작업'분기 제거

짧은 대답 : 병합을 포함하여 원하는대로 git을 사용할 수 있습니다 (간단한 워크 플로우는 아래 참조). 임시 작업 분기를 삭제 하려면 각 ' git merge work '와 ' git branch -d work '를 따르십시오 .

배경 설명 : 병합 / dcommit 문제는 ​​분기를 'git svn dcommit'할 때마다 해당 분기의 병합 기록이 '평평하게'됩니다 .git은 분기에 들어간 모든 병합 작업을 잊어 버립니다. 파일 내용 만 보존됩니다. 그러나이 내용이 (부분적으로) 특정 다른 지점에서 나온 사실은 손실됩니다. 참조 : 왜 git svn dcommit이 로컬 브랜치에 대한 병합 커밋 기록을 잃습니까?

(참고 : git-svn이 할 수있는 일은 많지 않습니다 : svn은 훨씬 더 강력한 git merge를 이해하지 못합니다. 따라서 svn 저장소 내 에서이 병합 정보는 어떤 식으로도 나타낼 수 없습니다.)

그러나 이것은 전체입니다 문제입니다. '작업'브랜치를 '마스터 브랜치'에 병합 한 후 '작업'브랜치를 삭제하면 git 저장소가 100 % 깨끗하며 svn 저장소와 똑같이 보입니다.

내 워크 플로우 : 물론 원격 svn 저장소를 로컬 자식 저장소에 복제했습니다 (시간이 걸릴 수 있음).

$> git svn clone <svn-repository-url> <local-directory>

그런 다음 모든 작업은 "local-directory"내부에서 발생합니다. 서버에서 'svn update'와 같은 업데이트를 받아야 할 때마다 다음을 수행합니다.

$> git checkout master
$> git svn rebase

나는 모든 개발 작업을 다음과 같이 생성되는 별도의 분기 '작업'에서 수행합니다.

$> git checkout -b work

물론 원하는만큼 작업에 대해 많은 브랜치를 생성하고 원하는대로 병합 및 리베이스 할 수 있습니다 (아래에서 논의한 것처럼 작업을 마치면 삭제). 정상적인 작업에서 나는 매우 자주 커밋합니다.

$> git commit -am '-- finished a little piece of work'

다음 단계 (git rebase -i)는 선택 사항입니다 --- svn에 보관하기 전에 역사를 정리하고 있습니다. 커밋 메시지를 분기하고 정리하십시오 (다른 개발자는 내가 겪은 작은 단계와 실수를 모두 볼 필요가 없습니다-결과 만). 이를 위해 나는

$> git log

svn 저장소에있는 마지막 커밋의 sha-1 해시를 복사하십시오 (git-svn-id로 표시). 그런 다음 전화

$> git rebase -i 74e4068360e34b2ccf0c5869703af458cde0cdcb

내 대신 마지막 svn 커밋의 sha-1 해시를 붙여 넣으십시오. 자세한 내용은 'git help rebase'와 함께 설명서를 읽으십시오. 한마디로 :이 명령은 먼저 커밋을 표시하는 편집기를 엽니 다 ---- 이전 커밋으로 스쿼시하려는 모든 커밋에 대해 'pick'을 'squash'로 변경하십시오. 물론 첫 번째 줄은 '픽'으로 유지해야합니다. 이런 식으로 많은 작은 커밋을 하나 이상의 의미있는 단위로 압축 할 수 있습니다. 편집기를 저장하고 종료하십시오. 커밋 로그 메시지를 다시 작성하도록 요청하는 다른 편집기가 표시됩니다.

간단히 말해서 : '코드 해킹'을 마친 후 다른 프로그래머에게 제시하고 싶을 때까지 (또는 역사를 탐색 할 때 몇 주 안에 작업을보고 싶을 때까지) '작업'분기를 마사지합니다. .

svn 저장소에 변경 사항을 푸시하기 위해 다음을 수행합니다.

$> git checkout master
$> git svn rebase

이제 우리는 svn 저장소에서 그 동안 발생한 모든 변경 사항으로 업데이트 된 이전 '마스터'분기로 돌아 왔습니다 (새로운 변경 사항은 '작업'분기에 숨겨져 있습니다).

새로운 '작업'변경 사항과 충돌 할 수있는 변경 사항이있는 경우 새 작업을 푸시하기 전에 로컬로 변경해야합니다 (자세한 내용은 아래 참조). 그런 다음 변경 사항을 svn으로 푸시 할 수 있습니다.

$> git checkout master
$> git merge work        # (1) merge your 'work' into 'master'
$> git branch -d work    # (2) remove the work branch immediately after merging
$> git svn dcommit       # (3) push your changes to the svn repository

참고 1 : 'git branch -d work'명령은 매우 안전합니다. 더 이상 필요하지 않은 브랜치를 삭제할 수 있습니다 (이미 현재 브랜치에 병합되어 있기 때문에). 'master'분기와 작업을 병합하기 전에 실수로이 명령을 실행하면 오류 메시지가 표시됩니다.

참고 2 : 병합과 dcommit 사이에 'git branch -d work' 를 사용하여 분기를 삭제하십시오. dcommit 후 분기를 삭제하려고하면 오류 메시지가 표시됩니다. 'git svn dcommit'을 수행하면 git은 지점이 '마스터'와 병합되었습니다. 안전 검사를 수행하지 않는 'git branch -D work'로 제거해야합니다.

이제 '마스터'브랜치를 실수로 해킹하는 것을 피하기 위해 새로운 '워크'브랜치를 즉시 만듭니다.

$> git checkout -b work
$> git branch            # show my branches:
  master
* work

SVN의 변경으로 '작업'을 통합 : 다음은 내 '일'지점에 근무하는 동안 그 다른 사람이 SVN 저장소를 변경할 때 '자식 svn의 REBASE'알 내가 할 것입니다 :

$> git checkout master
$> git svn rebase              # 'svn pull' changes
$> git checkout work           # go to my work
$> git checkout -b integration # make a copy of the branch
$> git merge master            # integrate my changes with theirs
$> ... check/fix/debug ...
$> ... rewrite history with rebase -i if needed

$> git checkout master         # try again to push my changes
$> git svn rebase              # hopefully no further changes to merge
$> git merge integration       # (1) merge your work with theirs
$> git branch -d work          # (2) remove branches that are merged
$> git branch -d integration   # (2) remove branches that are merged
$> git svn dcommit             # (3) push your changes to the svn repository

더 강력한 솔루션이 존재합니다 : 제시된 워크 플로우는 단순합니다. 'update / hack / dcommit'의 각 라운드 내에서만 git의 기능을 사용하지만 장기 프로젝트 히스토리는 svn 저장소와 마찬가지로 선형입니다. 레거시 svn 프로젝트에서 작은 첫 단계로 git merge 사용을 시작하려는 경우에도 괜찮습니다.

git merging에 익숙해지면 다른 워크 플로우를 자유롭게 탐색하십시오.하고있는 것을 알고 있다면 svn merge와 git merge를 혼합 할 수 있습니다 ( git-svn (또는 비슷한 것을 사용하여 svn merge)? )


이것은 불필요하게 복잡해 보입니다. 나는 사람들이하는 것을 들었습니다 git merge --squash work. 왜 그렇게하지 않습니까? 하나 이상의 'pick'을 생성하는 경우 병합하기 전에 분기에서 스쿼시 커밋을 수행하는 것을 볼 수 있습니다 (예 : 8 개의 커밋이 있고 4 개의 커밋 각각을 1로 설정하고 2 개의 커밋을 마스터에 병합). 또한 '작업'분기를 업데이트 할 때 rebase분기를 위해 다른 분기를 만들고 병합하는 것보다 간단합니다.
void.pointer

8

그렉 휴길의 답변은 안전하지 않습니다! 두 개의 "git svn rebase"사이의 트렁크에 새로운 커밋이 나타나면 병합이 빨리 진행되지 않습니다.

git-merge에 "--ff-only"플래그를 사용하여 보장 할 수 있지만 일반적으로 분기에서 "git svn rebase"를 실행하지 않고 "git rebase master"만 실행합니다 (로컬 만 가정 할 경우). 분기). 그런 다음 "git merge thebranch"가 빨리 감 깁니다.


6

git에서 svn 브랜치를 병합하는 안전한 방법은 git merge --squash를 사용하는 것입니다. 이렇게하면 단일 커밋이 만들어지고 메시지를 추가 할 수 없게됩니다.

svn-branch라는 주제 svn 분기가 있다고 가정 해 봅시다.

git svn fetch
git checkout remotes/trunk -b big-merge
git merge --squash svn-branch

이 시점에서 svn-branch의 모든 변경 사항이 인덱스에서 대기하는 하나의 커밋으로 스쿼시되었습니다.

git commit

그러나 다른 사람들이 지적했듯이 이것은 커밋의 세분성을 잃습니다.
Kzqai

@Tchalvak : 때때로 당신은 단지 그것을 원합니다. 나는 종종 "이전 커밋에서 고정 엉망"때문에 좋은 평판 :-)의 숨길이 막 다른 골목 난 같은 람 기능 지점에 커밋 한
schoetbi

1
그래, 나는 그것이 줄타기 걷기라고 생각하지만, 스쿼시 할 때마다 나중에 개별 커밋을 분리 할 수있는 능력을 상실하기 때문에. 커밋 표준의 다른 선택의 문제.
Kzqai

1
@ schoetbi 예, 때로는 게시하기 전에 지저분한 역사를 정리해야합니다. 이것은 'git rebase -i <trunk>'가 큰 도움이되는 곳입니다 : 당신은 join / reorder / remove 및 심지어 split (!) 커밋, 선택적으로 msg를 고칠 수 있습니다.
inger

5

로컬 git 브랜치를 마스터 git 브랜치에 리베이스 한 다음 dcommit하면 svn 사람들이 익숙한대로 선형으로 볼 수 있도록 모든 커밋을 순서대로 수행 한 것처럼 보입니다. 토픽이라는 로컬 브랜치가 있다고 가정하면

git rebase master topic

그러면 마스터 커밋을 통해 커밋을 수행하여 커밋 할 수 있습니다.

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