it-swarm-ko.tech

작업 흐름 및 rebase 대 병합 질문

나는 Git을 다른 개발자와 함께 프로젝트에서 몇 달간 사용 해왔다. 나는 몇 년 동안의 경험 SVN 을 가지고 있으므로 관계에 많은 짐을 가져다 줄 것입니다.

Git이 분기 및 병합에 우수하다는 소식을 들었습니다. 지금까지는 그것을 볼 수 없었습니다. 물론, 분기는 간단하지만, 병합을 시도하면 모든 것이 지옥으로 빠져 든다. 이제는 SVN에서 익숙해졌지만 다른 서브 버전을위한 하나의 서브 파 버전 시스템을 교환 한 것 같습니다.

제 파트너는 제 문제가 제 인생에서 병합하려는 나의 욕구에서 비롯된 것이며 많은 상황에서 병합 대신 리베이스를 사용해야한다고 말해줍니다. 예를 들어, 그가 내려 놓은 워크 플로는 다음과 같습니다.

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature
git checkout master
git merge my_new_feature

기본적으로 기능 분기를 만들고 항상 마스터에서 분기로 리베이스하고 분기에서 마스터로 병합합니다. 중요한 점은 지점은 항상 로컬로 유지된다는 것입니다.

다음은 내가 시작한 워크 플로입니다.

clone remote repository
create my_new_feature branch on remote repository
git checkout -b --track my_new_feature Origin/my_new_feature
..work, commit, Push to Origin/my_new_feature
git merge master (to get some changes that my partner added)
..work, commit, Push to Origin/my_new_feature
git merge master
..finish my_new_feature, Push to Origin/my_new_feature
git checkout master
git merge my_new_feature
delete remote branch
delete local branch

필자는 rebase 대신 항상 병합을 사용하고, 필자의 feature 브랜치 (및 필자의 feature branch 커밋)를 원격 리포지토리에 푸시합니다.

원격 지사에 대한 나의 추론은 내가 일하는 동안 내 일이 백업되기를 바란다는 것이다. 우리의 저장소는 자동으로 백업되며, 문제가 발생하면 복원 할 수 있습니다. 내 노트북은 그렇지 않거나 철저하지 않습니다. 따라서 다른 곳에 미러링되지 않은 코드가 내 랩톱에 없기를 바랍니다.

리베이스 대신 병합을 수행하는 이유는 병합이 표준 인 것처럼 보이고 리베이스가 고급 기능인 것처럼 보입니다. 내 직감은 내가하려는 것은 고급 설정이 아니므로 리베이스가 불필요해야한다는 것입니다. 나는 Git에서 새로운 Pragmatic Programming book을 꼼꼼히 살펴 보았고, 광범위하게 병합하고 rebase에 대해서는 거의 언급하지 않았다.

어쨌든, 최근 브랜치에서 작업 흐름을보고 있었고 마스터로 다시 병합하려고하면 모든 것이 지옥에갔습니다. 중요하지 않아야 할 일들에 수 많은 갈등이있었습니다. 갈등은 나에게 의미가 없었습니다. 로컬 마스터가 모든 충돌이 해결 되었기 때문에 원격 마스터에게 강제로 밀어 넣기로 끝내기까지 하루가 걸렸습니다.하지만 원격 호스트는 여전히 만족스럽지 않았습니다.

이와 같은 "올바른"워크 플로우는 무엇입니까? 힘내는 분기를 만들고 슈퍼 쉽게 병합하기로되어 있고, 나는 그것을 보지 않고있다.

2011-04-15업데이트

이것은 매우 인기있는 질문 인 것 같습니다. 그래서 나는 처음 물었던 이래로 2 년 동안의 경험으로 업데이트 할 것이라고 생각했습니다.

최소한 우리의 경우 원래의 워크 플로가 정확하다는 것이 밝혀졌습니다. 즉, 이것이 우리가하는 일이며 작동합니다 :

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge my_new_feature

사실, 우리의 작업 흐름은 약간 다르다. 원시 병합 대신에squash 병합을하는 경향이 있기 때문이다. ( 참고 : 이것은 논쟁의 여지가 있습니다. 아래를보십시오 .) 이것은 우리가 전체 기능 브랜치를 마스터에서 단일 커밋으로 바꿀 수있게 해줍니다. 그런 다음 지사 지점을 삭제합니다. 이렇게하면 우리 지부에서 약간 지저분한 것이라 할지라도 커밋을 마스터에 논리적으로 구성 할 수 있습니다. 그래서, 이것이 우리가하는 일입니다.

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge --squash my_new_feature
git commit -m "added my_new_feature"
git branch -D my_new_feature

스쿼시 병합 논쟁- 여러 명의 주석가가 지적했듯이, 스쿼시 병합은 지사의 모든 기록을 삭제합니다. 이름에서 알 수 있듯이 모든 커밋은 하나의 단일 커브로 축소됩니다. 작은 기능의 경우이 기능을 단일 패키지로 압축 할 때 의미가 있습니다. 큰 기능의 경우, 특히 개별 커밋이 이미 원자적일 경우 특히 좋은 생각이 아닙니다. 그것은 정말로 개인적 선호에 달려 있습니다.

Github and Bitbucket (기타?) 요청 풀기- 병합/리베이스가 풀 요청과 어떻게 관련되어 있는지 궁금한 경우, 위의 모든 단계를 수행 할 때까지 마스터에 다시 병합하십시오. git을 수동으로 병합하는 대신 PR 만 받아들입니다. 이것은 스쿼시 병합 (적어도 기본적으로는 아님)을 수행하지는 않지만, 스쿼시가 아닌 비 순방향 전달은 풀 요청 커뮤니티에서 승인 된 병합 규칙입니다 (알고있는 한). 특히 다음과 같이 작동합니다.

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git Push # May need to force Push
...submit PR, wait for a review, make any changes requested for the PR
git rebase master
git Push # Will probably need to force Push (-f), due to previous rebases from master
...accept the PR, most likely also deleting the feature branch in the process
git checkout master
git branch -d my_new_feature
git remote Prune Origin

나는 힘내를 사랑하기 위해 왔고 결코 SVN으로 돌아 가기를 원하지 않는다. 어려움을 겪고 있다면 그냥 붙어서 터널 끝에 빛을 보게 될 것입니다.

929
Micah

"충돌"은 "동일한 내용의 병행 진화"를 의미합니다. 그래서 병합하는 동안 "지옥으로가는 것"이라면, 같은 파일 세트에서 엄청난 발전을 이룩한 것입니다.

리베이스가 병합보다 나은 이유는 다음과 같습니다.

  • 당신은 마스터 중 하나와 로컬 커밋 내역을 다시 작성하고 (그리고 나서 당신의 작업을 다시 적용하여 충돌을 해결하십시오)
  • 마스터의 모든 커밋 내역과 다시 적용 할 변경 사항 만 포함하므로 최종 병합은 확실히 "빨리 감기"됩니다.

이 경우 정확한 작업 흐름 (일반적인 파일 세트에 대한 발전)은 rebase로 먼저 확인한 다음를 병합합니다.

그러나 로컬 브랜치 (백업 이유)를 푸시하는 경우 다른 브랜치에 의해 그 브랜치를 끌어서는 안됩니다 (또는 연속적 rebase로 커밋 히스토리가 재 작성되기 때문에).


randyfay.com 에서 두 가지 흥미로운 게시물에 언급 : barraponto 언급에 그 주제 (rebase 다음 워크 플로우를 병합)

  • Git 의 Rebase Workflow : 먼저 rebase를 가져 오도록 상기시켜줍니다 :

이 기법을 사용하면 현재 HEAD으로 최신 패치 인 공용 브랜치의 맨 위에 작업을 수행 할 수 있습니다.

(비슷한 기술 Bazaar 존재)

360
VonC

TL; DR

Git rebase 워크 플로는 충돌 해결에 나쁜 사람이나 SVN 워크 플로에 익숙한 사람들로부터 Git Disasters 피하기 : Gory Story 에서 제안한 것처럼 사용자를 보호하지 않습니다. 충돌 해결을 더 지루하게 만들고 잘못된 충돌 해결을 복구하기가 더 어려워집니다. 대신 diff3을 사용하면 처음에는 그렇게 어렵지 않습니다.


Rebase 워크 플로우는 충돌 해결에 좋지 않습니다!

나는 역사를 정리하기 위해 매우 친근하다. 그러나 내가 충돌을 일으킨 경우, 즉시 리베이스를 중단하고 대신 병합을 수행하십시오! 사람들이 충돌 해결을위한 병합 워크 플로우에 대한 더 나은 대안으로 리베이스 워크 플로우를 추천하고 있다는 사실이 실제로 저를 죽입니다. 이 질문에 관한 것입니다).

병합하는 동안 "지옥으로"가면 리베이스 중에 "지옥으로"가되며 잠재적으로 훨씬 더 많은 지옥이됩니다! 이유는 다음과 같습니다.

이유 # 1 : 각 커밋마다 한 번이 아니라 한 번 충돌 해결

병합 대신 리베이스 할 때, 동일한 충돌에 대해 리베이스하려는 커밋 횟수만큼 충돌 해결을 수행해야합니다!

실제 시나리오

나는 지점에서 복잡한 방법을 리팩토링하기 위해 마스터에서 분기합니다. 리팩토링 작업은 리팩토링하고 코드 검토를 받기 위해 총 15 개의 커밋으로 구성됩니다. 리팩토링의 일부는 이전에 마스터에 있던 혼합 탭과 공백을 수정하는 것입니다. 이것은 필요하지만 불행히도 마스터 에서이 방법으로 변경 한 내용과 충돌합니다. 물론이 방법으로 작업하는 동안 누군가가 마스터 브랜치에서 내 변경 사항과 병합 해야하는 동일한 방법을 간단하고 합법적으로 변경합니다.

지점과 마스터를 다시 병합 할 때 두 가지 옵션이 있습니다.

git merge : 충돌이 발생합니다. 그들이 마스터하고 변경하여 내 지점 (최종 제품)에 적용한 변경 사항을 확인했습니다. 끝난.

git rebase : first 커밋과 충돌합니다. 충돌을 해결하고 리베이스를 계속합니다. 내 second 커밋과 충돌합니다. 충돌을 해결하고 리베이스를 계속합니다. 내 third 커밋과 충돌합니다. 충돌을 해결하고 리베이스를 계속합니다. 내 fourth commit과 충돌합니다. 충돌을 해결하고 리베이스를 계속합니다. 내 fifth 커밋과 충돌합니다. 충돌을 해결하고 리베이스를 계속합니다. 내 sixth 커밋과 충돌합니다. 충돌을 해결하고 리베이스를 계속합니다. 내 seventh 커밋과 충돌합니다. 충돌을 해결하고 리베이스를 계속합니다. 내 eighth 커밋과 충돌합니다. 충돌을 해결하고 리베이스를 계속합니다. 내 ninth 커밋과 충돌합니다. 충돌을 해결하고 리베이스를 계속합니다. 내 tenth 커밋과 충돌합니다. 충돌을 해결하고 리베이스를 계속합니다. 내 eleventh 커밋과 충돌합니다. 충돌을 해결하고 리베이스를 계속합니다. 내 twelfth 커밋과 충돌합니다. 충돌을 해결하고 리베이스를 계속합니다. 내 thirteenth commit과 충돌합니다. 충돌을 해결하고 리베이스를 계속합니다. 내 fourteenth commit과 충돌합니다. 충돌을 해결하고 리베이스를 계속합니다. 내 fifteenth 커밋과 충돌합니다. 충돌을 해결하고 리베이스를 계속합니다.

this 이 선호하는 워크 플로 인 경우 농담해야합니다. 마스터에서 한 번의 변경으로 충돌하는 공백 수정 만 있으면됩니다. 모든 커밋은 충돌하므로 해결해야합니다. 그리고 이것은 공백 만있는 simple 시나리오입니다. 그러나 파일 간 주요 코드 변경과 관련하여 실제 충돌이 발생하고 that 여러 번 해결해야 함을 금지했습니다.

추가 충돌 해결이 필요하면 실수 할 것 일 가능성이 높아집니다. 그러나 취소 할 수 있기 때문에 실수는 괜찮습니다. 물론 ...

이유 # 2 : 리베이스를 사용하면 실행 취소가 없습니다!

우리 모두는 갈등 해결이 어려울 수 있으며 일부 사람들은 그 문제에 매우 나쁘다는 것에 모두 동의 할 수 있다고 생각합니다. 실수가 발생하기 쉽기 때문에 git을 사용하면 실행 취소하기가 쉽습니다.

병합 할 때 분기, git은 충돌 해결이 제대로 수행되지 않으면 버려지거나 수정 될 수있는 병합 커밋을 만듭니다. 잘못된 병합 커밋을 공개/정식 리포지토리로 이미 푸시 한 경우에도 git revert를 사용하여 병합에서 발생한 변경 사항을 취소하고 새 병합 커밋에서 병합을 올바르게 다시 실행할 수 있습니다.

리베이스 할 때 브랜치, 충돌 해결이 잘못되었을 가능성이 높으면 망가졌습니다. 모든 커밋에 잘못된 병합이 포함되어 있으므로 리베이스를 다시 실행할 수 없습니다 *. 기껏해야, 돌아가서 영향을받는 각 커밋을 수정해야합니다. 재미 없어.

리베이스 후에는 커밋의 원래 부분과 잘못된 충돌 해결의 결과로 무엇을 도입했는지 확인할 수 없습니다.

* git의 내부 로그에서 이전 참조를 파기하거나 rebasing 전에 마지막 커밋을 가리키는 세 번째 분기를 만드는 경우 rebase를 취소 할 수 있습니다.

충돌 해결에서 지옥을 꺼내십시오 : diff3 사용

이 충돌을 예로 들어 보겠습니다.

<<<<<<< HEAD
TextMessage.send(:include_timestamp => true)
=======
EmailMessage.send(:include_timestamp => false)
>>>>>>> feature-branch

갈등을 살펴보면 각 지점이 무엇을 변경했는지 또는 의도가 무엇인지 알 수 없습니다. 이것이 갈등 해결이 혼란스럽고 어려운 이유라고 생각합니다.

구조에 diff3!

git config --global merge.conflictstyle diff3

Diff3을 사용하면 새로운 충돌마다 병합 된 공통 조상 인 세 번째 섹션이 있습니다.

<<<<<<< HEAD
TextMessage.send(:include_timestamp => true)
||||||| merged common ancestor
EmailMessage.send(:include_timestamp => true)
=======
EmailMessage.send(:include_timestamp => false)
>>>>>>> feature-branch

먼저 병합 된 공통 조상을 검사하십시오. 그런 다음 각면을 비교하여 각 가지의 의도를 결정하십시오. HEAD이 (가) EmailMessage를 TextMessage로 변경했음을 알 수 있습니다. 그 목적은 TextMessage에 사용되는 클래스를 변경하여 동일한 매개 변수를 전달하는 것입니다. 또한 feature-branch의 의도가 : include_timestamp 옵션에 대해 true 대신 false를 전달 함을 알 수 있습니다. 이러한 변경 사항을 병합하려면 두 가지 의도를 결합하십시오.

TextMessage.send(:include_timestamp => false)

일반적으로 :

  1. 공통 조상과 각 분기를 비교하고 가장 간단한 변경을 갖는 분기를 결정하십시오.
  2. 이 간단한 변경 사항을 다른 지점의 코드 버전에 적용하면 더 간단하고 복잡한 변경 사항이 모두 포함됩니다.
  3. 변경 사항을 함께 병합 한 섹션 이외의 충돌 코드 섹션을 모두 제거하십시오.

대안 : 지점의 변경 사항을 수동으로 적용하여 해결

마지막으로 diff3에서도 일부 충돌을 이해하기가 끔찍합니다. 이것은 diff가 의미 상 공통적이지 않은 공통 라인을 발견 할 때 특히 발생합니다 (예 : 두 지점이 같은 곳에 빈 줄이 생겼습니다!). 예를 들어, 하나의 브랜치가 클래스 본문의 들여 쓰기를 변경하거나 유사한 메소드를 재정렬합니다. 이 경우 더 나은 해결 방법은 병합의 양쪽에서 변경 사항을 검사하고 diff를 다른 파일에 수동으로 적용하는 것입니다.

Origin/feature1 여기서 lib/message.rb 병합이 충돌하는 시나리오에서 충돌을 해결하는 방법을 살펴 보겠습니다.

  1. 현재 체크 아웃 한 지점 (HEAD 또는 --ours) 또는 병합중인 지점 (Origin/feature1 또는 --theirs)이 적용하기에 더 간단한 변경인지 결정하십시오. 트리플 도트 (git diff a...b)와 함께 diff를 사용하면 b에서 마지막 분기 된 이후 a에서 발생한 변경 사항이 표시됩니다. 즉, a와 b의 공통 조상을 b와 비교합니다.

    git diff HEAD...Origin/feature1 -- lib/message.rb # show the change in feature1
    git diff Origin/feature1...HEAD -- lib/message.rb # show the change in our branch
    
  2. 더 복잡한 버전의 파일을 확인하십시오. 이렇게하면 모든 충돌 마커가 제거되고 선택한면이 사용됩니다.

    git checkout --ours -- lib/message.rb   # if our branch's change is more complicated
    git checkout --theirs -- lib/message.rb # if Origin/feature1's change is more complicated
    
  3. 복잡한 변경 사항을 체크 아웃 한 상태에서 간단한 변경 사항의 차이점을 확인하십시오 (1 단계 참조). 이 diff에서 충돌하는 파일에 각 변경 사항을 적용하십시오.

374
Edward Anderson

내 작업 흐름에서는 가능한 한 리베이스 (rebase)하고 (자주 시도합니다. 불일치가 많이 발생하지 않도록함으로써 분기 간 충돌의 양과 심각도를 줄입니다).

그러나 대부분의 rebase 기반 워크 플로우에서도 병합을위한 장소가 있습니다.

병합은 실제로 두 개의 부모가있는 노드를 만듭니다. 이제 다음과 같은 상황을 생각해보십시오. 두 개의 독립적 인 기능 브랜치 A와 B가 있으며 이제 A와 B에 의존하는 기능 브랜치 C를 개발하려고합니다. A와 B는 검토 중입니다.

내가하는 일은 다음과 같습니다.

  1. 지점 C를 A 위에 만듭니다 (그리고 체크 아웃).
  2. B와 병합

이제 C 지점에는 A와 B의 변경 사항이 포함되어 있으며 계속 개발할 수 있습니다. A를 변경하면 다음과 같은 방식으로 분기 그래프가 재구성됩니다.

  1. a의 새로운 상단에 가지 T를 만듭니다.
  2. t와 B를 병합하다.
  3. c를 T에 rebase
  4. 분지 T 삭제

이 방법을 사용하면 실제로 임의의 그래프를 유지할 수 있지만 부모가 변경 될 때 리베이스를 수행 할 수있는 자동 도구가 없으므로 위에서 설명한 상황보다 더 복잡한 작업을 수행하는 것은 이미 너무 복잡합니다.

31
Alex Gontmakher

git을 사용하지 마십시오. 원산지 - 모든 상황에서 거울.

이 작업을 수행 할 것인지 묻는 메시지는 표시되지 않으며 로컬 상자에없는 원격 지점을 모두 지울 수 있기 때문에 확실해야합니다.

http://Twitter.com/dysinger/status/1273652486

21
Scott Brown

나는 당신의 설명을 읽은 후에 하나의 질문을 가지고있다.

git checkout master
git pull Origin
git checkout my_new_feature

당신의 지사에서 'git rebase/merge master'를하기 전에?

왜냐하면 당신의 master 브랜치는 친구 저장소에서 자동으로 업데이트되지 않기 때문입니다. 당신은 git pull Origin로 그렇게해야합니다. 나. 어쩌면 항상 변하지 않는 로컬 마스터 브랜치에서 리베이스 할 수 있습니까? 그리고 Push time, 당신은 보지 못했던 커밋을 가지고있는 로컬 저장소를 밀고 있습니다.

14
knweiss

당신의 상황에서는 당신의 파트너가 맞다고 생각합니다. 재배치에 대해 니스는 외부인에게 변경 사항이 모든 것이 깨끗한 순서대로 진행되는 것입니다. 이것은

  • 변경 사항을 매우 쉽게 검토 할 수 있습니다.
  • 니스, 작은 커밋을 계속 만들 수 있습니다. 그런데 한 번에 모든 커밋을 공개 (마스터에 병합하여) 할 수 있습니다.
  • 공용 마스터 브랜치를 살펴보면 다른 개발자가 다른 기능을 사용하는 커밋 시리즈를 볼 수 있지만 모든 기능이 혼합되지는 않습니다

계속해서 개인 개발 브랜치를 백업을 위해 원격 저장소로 푸시 할 수 있지만 재 게시 할 것이므로 "공개"브랜치로 취급해서는 안됩니다. BTW, 이것을하기위한 쉬운 명령은 git Push --mirror Origin입니다.

기사 Git 을 사용하여 소프트웨어를 패키징하는 것은 합병 대 리버스 (rebase)의 트레이드 오프를 설명하는 꽤 좋은 작업입니다. 조금 다른 컨텍스트이지만 원칙은 동일합니다. 기본적으로 지점이 공개 또는 비공개인지 여부와 주류에 통합 할 방법을 결정합니다.

13
Pat Notz

어쨌든, 최근 브랜치에서 작업 흐름을보고 있었고 마스터로 다시 병합하려고하면 모든 것이 지옥에갔습니다. 중요하지 않아야 할 일들에 수 많은 갈등이있었습니다. 갈등은 나에게 의미가 없었습니다. 로컬 마스터가 모든 충돌이 해결 되었기 때문에 원격 마스터에게 강제로 밀어 넣기로 끝내기까지 하루가 걸렸습니다.하지만 원격 호스트는 여전히 만족스럽지 않았습니다.

파트너 나 권장 워크 플로우 모두에서 이해가되지 않는 갈등을 경험해야합니다. 비록 당신이 가지고 있었다면, 당신이 제안 된 워크 플로우를 따르고 있다면 해결 후 '강제적 인'푸시가 요구되어서는 안됩니다. 실제로 푸시하는 지점을 병합하지는 않았지만 원격 팁의 하위가 아닌 지점을 푸시해야한다는 의미입니다.

무슨 일이 있었는지 조심스럽게 봐야 할 것 같아. 다른 사람이 로컬 브랜치를 만든 후 원격 브랜치를 로컬 브랜치에 다시 병합하려고 시도한 시점에서 원격 마스터 브랜치를 (의도적으로 또는 비공개로) 되 감을 수 있습니까?

다른 버전 관리 시스템과 비교할 때 Git을 사용하면 도구에 대한 대처가 줄어들고 소스 스트림의 근본적인 문제를 해결할 수 있습니다. 힘내는 마법을 수행하지 않으므로 충돌하는 변화가 충돌을 일으키지 만 커밋 태생을 추적하여 쓰기 작업을 쉽게 수행해야합니다.

12
CB Bailey

내가 관찰 한 것부터 git merge는 병합 후에도 분기를 분리하는 경향이있는 반면, rebase는 병합하여 하나의 단일 분기로 결합합니다. 후자는 훨씬 더 깨끗하게 나오는 반면, 전자에서는 병합 후에도 어느 분기에 어떤 커밋이 속하는지를 쉽게 알 수 있습니다.

7
Pepe

"브랜치가 몇 개인 단 하나의 개발자라도 리베이스를 사용하고 적절하게 병합하는 습관을 얻는 것이 중요합니다. 기본적인 작업 패턴은 다음과 같습니다 :

  • 기존 지점 A에서 새 지점 B 만들기

  • B 지점에서 변경 사항 추가/커밋

  • 지점 A에서 업데이트를 다시받습니다.

  • 지점 B에서 지점 A로 변경 사항 병합 "

https://www.atlassian.com/git/tutorials/merging-vs-rebasing/

6
Rakka Rage

Git에는 "정확한"워크 플로우가 없다. 보트를 뜨는 것을 사용하십시오. 그러나 분기를 병합 할 때 끊임없이 갈등을 겪는다면 동료 개발자와 더 잘 협조해야합니다. 두 사람이 같은 파일을 계속 편집하는 것처럼 들리 네요. 또한 공백 및 Subversion 키워드 (예 : '$ Id $'등)에주의하십시오.

2
Bombe