2017. 11. 30. 21:07ㆍGit
Centralized Workflow
1. Centralized Workflow는 기존의 Subversion(SVN)으로 협업할 때와 크게 다를 바 없다.
SVN에 비하면 Git은 다음 장점이 있다.
첫째, 모든 팀 구성원이 로컬 저장소를 이용해서 개발한다는 점이다.
로컬 저장소는 중앙 저장소로 부터 완벽히 격리된 상태이므로, 다른 팀 구성원 및 중앙 저장소의 변경 내용을 신경 쓰지 않고 자신의 작업에만 집중할 수 있다.
둘째, Git의 브랜치와 병합 기능의 이점을 들 수 있다. Git 브랜치를 이용하면 안전하게 코드를 변경하고 다른 브랜치에 통합할 수 있다.
1.1 작동 원리
Centralized Workflow는 프로젝트의 변경 내용을 추적하기 위해 단일 중앙 저장소를 이용한다.
Subversion의 trunk 대신, master란 브랜치를 사용하고, 모든 변경 내용은 이 브랜치에 커밋(commit)한다.
이 워크플로우에서는 master 브랜치 하나만 사용한다.
팀 구성원은 중앙 저장소를 복제하여 로컬 저장소를 만든 후, 로컬 저장소에서 파일을 수정하고 변경 내용을 커밋한다(SVN과 달리 변경 내용은 로컬 저장소에 기록된다).
로컬 저장소는 원하는 때 언제든 중앙 저장소와 동기화할 수 있다.
로컬 master 브랜치의 변경 내용을 프로젝트의 중앙 저장소에 올리고자할 때는 ‘push’ 명령을 이용한다.
svn commit과 비슷하지만, 로컬 저장소의 커밋 이력을 중앙 저장소에 그대로 보관한다는 점은 다르다.
1.2. 충돌 처리
항상 중앙 저장소의 커밋이 기준이다.
만약에 로컬 저장소의 변경 내용을 중앙 저장소에 푸시(push)할 때, 푸시하려는 커밋 이력과 중앙 저장소의 커밋 이력이 서로 충돌한다면 Git은 중앙 저장소의 커밋을 보호하기 위해 푸시를 받지 않고 거부한다.
이 때는 중앙 저장소의 변경 내용을 먼저 로컬 저장소로 가져 와서(fetch), 자신의 변경 내용을 재배열(rebase)해야 한다.
사진 출처 : https://www.atlassian.com/git/tutorials/comparing-workflows#centralized-workflow
다른 팀원이 이미 변경한 내용에 자신의 변경 내용을 덧 붙이는 것이다.
리베이스 도중에 중앙 저장소의 변경 내용과 자신의 변경 내용이 충돌한다면, Git은 리베이스를 멈추고, 수작업으로 충돌을 해결하라고 한다.
충돌을 잡고 난 후, 평상시 처럼 git status나 git add 명령으로 충돌 해결 과정을 마치고 리베이스를 계속할 수 있다.
만약 리베이스 중에 문제가 발생하면 언제든 리베이스를 취소하고 처음부터 다시 할 수 있다.
1.3. 연습
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.Toast; public class MainActivity extends AppCompatActivity { Button messiButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); messiButton = (Button)findViewById(R.id.messiButton); messiButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(getApplicationContext(), "FC Barcelona", Toast.LENGTH_SHORT).show(); } }); } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.jaehyukshin.gitcollaboratingworkflows.MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/messiButton" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Lionel Messi" /> </android.support.constraint.ConstraintLayout> | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.Toast; public class MainActivity extends AppCompatActivity { Button ronaldoButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ronaldoButton = (Button)findViewById(R.id.ronaldoButton); ronaldoButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(getApplicationContext(), "Real Madrid", Toast.LENGTH_SHORT).show(); } }); } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.jaehyukshin.gitcollaboratingworkflows.MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/ronaldoButton" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Cristiano Ronaldo" /> </android.support.constraint.ConstraintLayout> | cs |
# 충돌처리
$ git pull --rebase origin master
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | jaehyukshin$ git pull --rebase origin master From https://github.com/ninetyfivejae/GitCollaboratingWorkflows * branch master -> FETCH_HEAD First, rewinding head to replay your work on top of it... Applying: Cristiano Ronaldo's work added Using index info to reconstruct a base tree... M GitCollaboratingWorkflows/app/src/main/java/com/example/jaehyukshin/gitcollaboratingworkflows/MainActivity.java M GitCollaboratingWorkflows/app/src/main/res/layout/activity_main.xml M README.md Falling back to patching base and 3-way merge... Auto-merging README.md CONFLICT (content): Merge conflict in README.md Auto-merging GitCollaboratingWorkflows/app/src/main/res/layout/activity_main.xml CONFLICT (content): Merge conflict in GitCollaboratingWorkflows/app/src/main/res/layout/activity_main.xml Auto-merging GitCollaboratingWorkflows/app/src/main/java/com/example/jaehyukshin/gitcollaboratingworkflows/MainActivity.java CONFLICT (content): Merge conflict in GitCollaboratingWorkflows/app/src/main/java/com/example/jaehyukshin/gitcollaboratingworkflows/MainActivity.java error: Failed to merge in the changes. Patch failed at 0001 Cristiano Ronaldo's work added The copy of the patch that failed is found in: .git/rebase-apply/patch When you have resolved this problem, run "git rebase --continue". If you prefer to skip this patch, run "git rebase --skip" instead. To check out the original branch and stop rebasing, run "git rebase --abort". | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | jaehyukshin$ git status rebase in progress; onto 65d1ca4 You are currently rebasing. (fix conflicts and then run "git rebase --continue") (use "git rebase --skip" to skip this patch) (use "git rebase --abort" to check out the original branch) Unmerged paths: (use "git reset HEAD <file>..." to unstage) (use "git add <file>..." to mark resolution) both modified: GitCollaboratingWorkflows/app/src/main/java/com/example/jaehyukshin/gitcollaboratingworkflows/MainActivity.java both modified: GitCollaboratingWorkflows/app/src/main/res/layout/activity_main.xml both modified: README.md no changes added to commit (use "git add" and/or "git commit -a") | cs |
수작업으로 충돌 해결
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.Toast; public class MainActivity extends AppCompatActivity { Button messiButton; Button ronaldoButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); messiButton = (Button) findViewById(R.id.messiButton); messiButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(getApplicationContext(), "FC Barcelona", Toast.LENGTH_SHORT).show(); } }); ronaldoButton = (Button) findViewById(R.id.ronaldoButton); ronaldoButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(getApplicationContext(), "Real Madrid", Toast.LENGTH_SHORT).show(); } }); } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.jaehyukshin.gitcollaboratingworkflows.MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/messiButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Lionel Messi" /> <Button android:id="@+id/ronaldoButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Cristiano Ronaldo" /> </LinearLayout> | cs |
충돌 해결 후 rebase 재개
$ git add <file>
$git rebase --continue
원격 저장소에 올리기
$git push origin master
여기서 HEAD에 올라간 작업내용을 올리고 싶으면 브랜치를 따로 만들어서 작업하면 된다
git branch <new-branch-name> ---
지금까지 봤듯이 몇 개의 Git 명령어만으로도 Subversion의 작업 흐름을 그대로 재현할 수 있다.
Centralized Workflow는 Git의 특장점인 분산 버전 관리의 이점은 누리지 못한다.
그럼에도 불구하고 SVN 개발 환경을 Git으로 전환할 수 있는 좋은 시작점이 될 수 있다.
참고로, Github 원격 저장소에 올라간 바로 이전의 commit을 지우고 싶으면
git reset HEAD^
명령을 사용하면 된다.
원격 저장소에 있는 정보가 손실 될 수 있는 작업이라서 reject 시킨다.
하지만 이런 데이터를 날려버리는 것이기 때문에 덮어씌우는
git push origin +master
# 참고 사이트)
https://www.atlassian.com/git/tutorials/comparing-workflows#centralized-workflow
http://blog.appkr.kr/learn-n-think/comparing-workflows/
'Git' 카테고리의 다른 글
[Git Collaborating Workflows / Git 협업 흐름] Feature Branch Workflow (0) | 2017.12.01 |
---|---|
How to use Markdown / 마크다운 작성법 (0) | 2017.12.01 |
git add commit push with Terminal or gitbash (1) | 2017.10.15 |
git add commit push with SourceTree GUI 소스트리 사용한 깃 add commit push (1) | 2017.10.15 |
About Git (0) | 2017.07.23 |