[Git Collaborating Workflows / Git 협업 흐름] Centralized Workflow / git rebase

2017. 11. 30. 21:07Git

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. 연습

# Lionel Messi's work

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




# Cristiano Ronaldo's work
다시 Init commit으로 돌아가서 Ronaldo의 작업을 시작한다.
commit 완료한 상태에서 Init commit으로 체크아웃하면 된다(소스트리에서 그냥 더블클릭하면 됨)

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


명령을 해주면 원격에 올라간 commit 사항을 폐기할 수 있다.



# 참고 사이트)

https://www.atlassian.com/git/tutorials/comparing-workflows#centralized-workflow


http://blog.appkr.kr/learn-n-think/comparing-workflows/


https://medium.com/@2xel/gitbash%EC%97%90%EC%84%9C-github%EC%97%90-push%EB%90%9C-commit-%EC%82%AD%EC%A0%9C-be1092843fc0