git pull에서 나는 충돌은 생각보다 쉽게 해결 가능했습니다. conflict난 파일을 즉시 확인할 수 있었으니까요. 그러나 pull request에서 나는 충돌은 꽤 막막합니다. 대체적으로 아래와 같은 상황입니다.

  • master 브랜치에서 feature_xfeature_y 브랜치를 동시에 생성
  • feature_x는 README.md를 수정하여 master에 merge가 완료됨
  • feature_y가 README.md를 수정하여 master에 pull request하는 순간 conflict 발생

동일한 저장소에서 브랜치 간 conflict

생각보다 간단합니다. Pull request 타겟 브랜치의 변경 사항을, Pull request source 브랜치로 가져오면 됩니다. 예를 들어, feature_y 브랜치에서 master 브랜치에 대해 pull request하는 과정에서 conflict가 났다면, feature_y 브랜치에서 master 브랜치의 변경 사항을 가져오는 것입니다.

> git checkout feature_y
> git pull origin master

그럼 현재 브랜치에서 conflict가 발생되고, 해당 충돌을 해결한 후 다시 commit - push하면 pull request에서 충돌이 발생하지 않습니다.

Fork된 저장소와 upstream 브랜치 간 conflict

이 상황은 해결법을 떠올리기 힘듭니다. 사실 위와 동일하게 해결하는데, 이번에는 git의 remote라는 개념을 활용합니다. 예를 들어, origin의 feature_y 브랜치에서 upstream의 master 브랜치에 대해 pull request하는 과정에서 conflict가 났다면, upstream/master 브랜치의 변경 사항을 feature_y 브랜치로 가져오면 됩니다.

> git checkout feature_y
> git remote add upstream [https://upstream_url.git]
> git pull upstream master

위처럼 현재 브랜치에서 conflict가 발생되고, 위처럼 충돌을 해결하고 나면 pull request에선 충돌이 발생하지 않습니다.

'Git 레거시 글' 카테고리의 다른 글

[Git] Fork와 Pull request  (0) 2018.08.12
[Git] Cherry-pick  (0) 2018.05.30
[Git] 커밋 히스토리 수정하기  (0) 2018.05.29
[Git] 커밋 되돌리기  (0) 2018.05.28
[Git] 다시 커밋하기  (0) 2018.05.27

Github, Bitbucket 등 대부분의 Git 기반 서비스들은 forkpull request라는 기능을 가지고 있습니다. fork특정 원격 저장소의 히스토리를 그대로 다른 저장소에 복사하는 것을 말합니다. 대부분 다른 사람이 owner인, write 권한이 없는 오픈 소스 프로젝트에 기여하기 위해 사용합니다.

Pull request는 브랜치 간 변경 사항의 merge를 요청하는 것을 말합니다. Fork된 저장소라면, 원본 저장소에 변경 사항을 요청하기 위해서도 사용합니다.

  1. 팀 프로젝트를 진행한다면, 공용 저장소에 자신을 위한 브랜치를 만들고, 해당 브랜치에 변경 사항을 만들고, pull request로 원본 브랜치에 merge를 요청합니다.(브랜치 간 merge 요청)
  2. 오픈 소스 프로젝트를 진행한다면, 저장소를 fork해서 내 저장소로 만들고, 자신의 저장소의 특정 브랜치에 변경 사항을 만들고, pull request로 원본 저장소에 merge를 요청합니다.(fork된 저장소와 원본 저장소의 브랜치 간 merge 요청)

Github를 기준으로 설명하도록 하겠습니다.

Fork하기

위에서 말했던 것처럼 대부분의 Git 기반 서비스들은 fork를 지원합니다. 제가 현재 진행하고 있는 DMS라는 프로젝트의 DMS-Backend 저장소를 fork해서 제 저장소로 만들도록 하겠습니다.(Organization name까지 합치면, DSM-DMS/DMS-Backend입니다.) 저장소 화면의 우측 상단에 Fork라는 버튼이 있으며, 이 버튼을 눌러 저장소를 어디로 fork할지 선택할 수 있습니다.

자신 혹은 자신이 속한 Organization에 fork할 수 있으며, 여기서는 제 개인 저장소로 fork하도록 하겠습니다.

위와 같은 로딩 화면을 거치고 나면, 선택한 곳에 저장소가 만들어 집니다.

JoMingyu/DMS-Backend 저장소가 만들어졌습니다. 또한, 원래는 github.com/DSM-DMS/DMS-Backend.git이었던 Clone URL이 github.com/JoMingyu/DMS-Backend.git으로 바뀌었습니다.

Fork된 저장소(여기서는 JoMingyu/DMS-Backend)는 이전에 했던 것처럼 origin이라 부르고, Fork의 대상인 원본 저장소(여기서는 DSM-DMS/DMS-Backend)는 upstream이라고 부릅니다.

Pull request하기

Pull request는 브랜치 간 변경 사항 merge를 위해 사용합니다. origin에서 upstream으로 변경 사항을 merge하기 위해서도 사용합니다. 여기서는 후자를 위해 사용해 보도록 하겠습니다. 저장소의 source tree 위쪽에 New pull request라는 버튼이 존재합니다.

이 버튼을 눌러 pull request를 날릴 수 있게 됩니다.

head fork와 base fork를 비교하는 화면이 나옵니다. 여기서는 origin인 JoMingyu/DMS-Backend의 master 브랜치(head fork)에서 upstream인 DSM-DMS/DMS-Backend의 master 브랜치(base fork)로 merge를 요청합니다. Create pull request 버튼을 눌러 실제로 pull request를 여는 화면으로 이동합니다.

Pull request의 제목과 내용을 입력하고, 다시 Create pull request를 누릅니다.

위와 같이 pull request가 생성되었고, upstream에 권한이 있는 사용자가 Merge pull request를 누르면 실제로 변경 사항이 upstream에 적용됩니다.

'Git 레거시 글' 카테고리의 다른 글

[Git] Pull request에서 발생하는 충돌 해결하기  (1) 2018.08.13
[Git] Cherry-pick  (0) 2018.05.30
[Git] 커밋 히스토리 수정하기  (0) 2018.05.29
[Git] 커밋 되돌리기  (0) 2018.05.28
[Git] 다시 커밋하기  (0) 2018.05.27

브랜치의 일부 커밋만 반영하고 싶을 때가 있습니다. cherry-pick을 사용하며, 단지 커밋 해시만을 가지고 사용할 수 있습니다.

  • 1c889e3 : Something
  • a98ebc7 : Important!
  • f119c34 : ...

이들 중 a98ebc7에 해당하는 커밋을 가져오려고 한다면, 아래와 같이 cherry-pick할 수 있습니다.

$ git cherry-pick a98ebc7

rebase를 사용하는 것과 다르게 다른 브랜치의 변경사항도 가져올 수 있고, 단순히 한두개 정도의 변경 사항만을 가져오기에 편하다는 점이 큰 장점입니다.

'Git 레거시 글' 카테고리의 다른 글

[Git] Pull request에서 발생하는 충돌 해결하기  (1) 2018.08.13
[Git] Fork와 Pull request  (0) 2018.08.12
[Git] 커밋 히스토리 수정하기  (0) 2018.05.29
[Git] 커밋 되돌리기  (0) 2018.05.28
[Git] 다시 커밋하기  (0) 2018.05.27

Git으로 작업하다 보면 어떤 이유로든 커밋 히스토리를 수정해야 할 때가 있습니다. 가장 최근 한 개의 커밋은 commit에 --amend 플래그를 붙이면 되고, 더 예전의 커밋을 수정하려면 rebase 명령을 이용하여 수정할 수 있습니다. rebase는 사용자가 선택한 과거부터 HEAD까지의 커밋을 순서대로 다시 커밋하는 역할을 합니다. 따라서 rebase를 이용해 과거의 커밋 메시지를 수정하거나, 커밋의 순서를 바꾸거나, 여러 개의 커밋을 하나로 합칠 수 있습니다. 단, 과거부터 HEAD 범위에만 적용 가능하며, 7개 전 커밋부터 3개 전 커밋과 같은 형태의 rebase는 불가능합니다.

$ git rebase -i HEAD~3

마지막 3개의 커밋을 수정하게 됩니다. 이를 실행하면 텍스트 편집기가 열리고, 그 안에는 수정하려는 커밋의 목록이 펼쳐집니다.

edit

pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file

# Rebase 710f0f8..a5f4a0d onto 710f0f8
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

특정 커밋에서 실행을 멈추게 하려면 스크립트를 수정해야 합니다. pickedit으로 수정하면 그 커밋에서 멈추게 됩니다. 가장 오래된 커밋인 f7f3f6d의 메시지를 수정하려면, 아래와 같이 편집하면 됩니다.

edit f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file

이 상태에서 저장하고 편집기를 종료(:wq)하면 Git은 목록에 있는 커밋 중에서 가장 오래된 커밋으로 이동하고, 커밋을 순서대로 다시 수행하던 도중 edit을 만나 아래와 같은 메시지를 보여줍니다.

$ git rebase -i HEAD~3
Stopped at 7482e0d... updated the gemspec to hopefully work better
You can amend the commit now, with

       git commit --amend

Once you’re satisfied with your changes, run

       git rebase --continue

뭘 해야 하는지 알려줍니다. git commit --amend를 실행해 커밋 메시지를 수정하고, git rebase --continue를 실행하면 됩니다. 그럼 rebase가 해당 커밋을 적용한 후 다음 커밋으로 이동합니다.

squash

rebase를 이용해 커밋을 합칠 수 있습니다. squash라는 기능을 이용하며, rebase를 실행하면 나오는 가이드에 아래와 같이 squash에 대한 도움말이 적혀 있습니다.

s, squash = use commit, but meld into previous commit

해당 커밋과 바로 이전 커밋을 합친다고 합니다. 3개의 커밋을 하나로 합치려면 스크립트를 아래와 같이 수정합니다.

pick f7f3f6d changed my name a bit
squash 310154e updated README formatting and added blame
squash a5f4a0d added cat-file

저장하고 편집기를 종료(:wq)하면 3개의 커밋 메시지를 merge할 수 있도록 에디터를 즉시 실행해 줍니다.

# This is a combination of 3 commits.
# The first commit's message is:
changed my name a bit

# This is the 2nd commit message:

updated README formatting and added blame

# This is the 3rd commit message:

added cat-file

이 메시지를 저장하면 3개의 커밋이 모두 합쳐진 하나의 커밋만 남습니다. squash를 계속해서 진행한 후, force push하면 원하는 대로 커밋이 합쳐집니다.

'Git 레거시 글' 카테고리의 다른 글

[Git] Fork와 Pull request  (0) 2018.08.12
[Git] Cherry-pick  (0) 2018.05.30
[Git] 커밋 되돌리기  (0) 2018.05.28
[Git] 다시 커밋하기  (0) 2018.05.27
[Git] 파일 상태 Lifecycle 관리하기  (0) 2018.05.26

Git을 사용하다가, 커밋을 되돌려야 하는 상황이 생길 수 있습니다. Working tree가 꼬여 모든 파일이 제거되는 커밋을 작성했거나, 단순히 변경 사항이 필요없는 경우 등이 있을 수 있습니다. 그러나 이력을 되돌려야 하는 상황 중 대부분은 당황스럽고 급박한 상황인 경우가 많아, 커밋을 되돌리는 방법에 대해 제대로 알지 못하고 있으면 이력을 되돌리는 일조차 제대로 하지 못할 수 있습니다.

Git에서 이력을 되돌리는 방법은 여러가지가 있으나, 대표적으로 resetrevert를 사용할 수 있습니다. reset은 과거로 돌아가고, revert는 되돌릴 커밋의 코드만 복원시킵니다.

Reset

Reset은 과거의 이력으로 돌아간다고 생각하면 됩니다. 돌아가려는 커밋으로 저장소가 재설정되고, 해당 커밋 이후의 이력은 사라집니다.

  • 000004 : Update some.txt
  • 000003 : Update some.txt
  • 000002 : Add some.txt
  • 000001 : 초기화

000001번에 해당하는 첫 커밋부터, 000004번에 해당하는 네 번째 커밋까지 진행되어 있다고 칩시다. some.txt를 만들고 나서 열심히 수정했는데, 이 파일을 포함한 모든 변경 이력이 필요가 없어져서 some.txt를 만들기 전(000001)으로 이력을 되돌리기로 결정했다고 합시다. 다음과 같이 사용할 수 있습니다.

$ git reset 000001

위처럼 커밋 해쉬를 통해서 되돌아가려는 커밋을 직접 지정할 수도 있고, HEAD를 이용할 수도 있습니다. 예를 들어 현재를 기준으로 3개 전의 이력으로 되돌아가려면 아래처럼 사용할 수 있습니다.

$ git reset HEAD~3

옵션은 hard, mixed, soft가 있습니다.

--hard

$ git reset --hard 000001

돌아가려는 이력(여기서는 000001) 이후의 모든 내용을 지웁니다.

--soft

$ git reset --soft 000001

000001번 커밋으로 되돌아 가긴 하지만, 해당 내용은 지워지지 않고 Index(Staging Area)에 그대로 남아 있습니다. 다시 커밋할 수 있는 상태로 남아있는 것입니다.

--mixed

$ git reset --soft 000001

여기서도 000001번으로 이력은 되돌아가지만, 인덱스가 초기화되며 해당 변경 이력에 해당하는 파일들은 Modified 상태가 됩니다.

Revert

Revert는 특정 커밋의 변경 사항을 되돌리는 역할을 합니다. 사실 결과물 자체는 reset과 다를 바 없지만, 이력은 같지 않습니다.

$ git revert 000004

이렇게 되면, 커밋이 사라지지 않고 000004번 커밋을 되돌린 커밋이 새로 생성됩니다.

  • 000005 : Revert "Update some.txt"
  • 000004 : Update some.txt
  • 000003 : Update some.txt
  • 000002 : Add some.txt
  • 000001 : 초기화

되돌릴 커밋을 여러 개 설정할 수도 있습니다.

$ git revert 000004..000002

Reset vs. Revert

Reset은 이력을 단순하게 만들어 줍니다. 그러나 reset은 해당 커밋 이후의 이력을 모두 제거하므로 신중하게 사용해야 하며, 커밋을 되돌린 이력도 한 가지의 이력으로 남겨 두는 것이 좋기 때문에 되도록이면 revert를 사용하는 것이 좋습니다.

'Git 레거시 글' 카테고리의 다른 글

[Git] Cherry-pick  (0) 2018.05.30
[Git] 커밋 히스토리 수정하기  (0) 2018.05.29
[Git] 다시 커밋하기  (0) 2018.05.27
[Git] 파일 상태 Lifecycle 관리하기  (0) 2018.05.26
[Git] 브랜칭 기법  (0) 2018.05.25

커밋에서 파일을 빼먹었거나, 커밋 메시지를 잘못 적었을 때 해당 커밋을 다시 수행해야 합니다. git commit에 --amend 옵션을 사용합니다.

$ git commit --amend

기본적으로 이것도 커밋입니다. 단, --amend 옵션을 지정함으로써 Index에 있는 현재 변경 사항들을 가장 최근 커밋에 덮어쓸 수 있습니다. 따라서, 방금 한 커밋에서 forgotten 파일을 빼먹었다면 아래와 같이 커밋에 파일을 추가할 수 있습니다.

$ git add forgotten
$ git commit --amend

단순히 메시지만을 수정하고 싶다면, Index에 아무 것도 등록하지 않은 채로 --amend 옵션을 지정하고 다시 커밋하면 됩니다.

'Git 레거시 글' 카테고리의 다른 글

[Git] 커밋 히스토리 수정하기  (0) 2018.05.29
[Git] 커밋 되돌리기  (0) 2018.05.28
[Git] 파일 상태 Lifecycle 관리하기  (0) 2018.05.26
[Git] 브랜칭 기법  (0) 2018.05.25
[Git] 브랜치와 Merge  (0) 2018.05.24

Git의 파일에는 Lifecycle이 존재합니다. Unmodified를 Modified로 변경하려면 파일을 수정하면 되고, Modified를 Staged로 변경하려면 git add하면 됩니다. 여기선 그 역방향으로 파일의 Lifecycle을 변경해 보도록 합시다.

Staged 파일을 Modified(Unstaged)로 변경하기

Working tree에서 Staging area(index)로 파일의 상태를 기록(staging)하기 위해 add를 사용했습니다. 현재 작업 트리의 상태를 보여주는 git status를 사용하면, 아래와 같이 출력될 것입니다.

> git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   Will_commit.txt
        modified:   Dont_commit.txt

git reset HEAD <file>...이라는 문장을 볼 수 있습니다. 실제로 이 명령어를 이용해 파일을 Unstaged 상태로 변경할 수 있습니다. Dont_commit.txt 파일을 Unstaged 상태로 변경해 보겠습니다.

$ git reset HEAD Dont_commit.txt
Unstaged changes after reset:
M       Dont_commit.txt
$
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   Will_commit.txt

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   Dont_commit.txt

Modified 파일을 Unmodified로 변경하기

수정된 파일을 수정 전으로 되돌릴 수도 있습니다. 여기서도 git status 명령이 친절하게 알려줍니다.

$ git status
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   Dont_modify.txt

git checkout -- <file>...을 사용하라고 합니다. 알려주는 대로 한 번 해 봅시다.

$ git checkout -- Dont_modify.txt
$ git status
nothing to commit (create/copy files and use "git add" to track)

수정 전으로 돌아갔습니다. 그러나 수정했던 내용은 전부 사라지므로, 조심해서 사용해야 합니다.

'Git 레거시 글' 카테고리의 다른 글

[Git] 커밋 되돌리기  (0) 2018.05.28
[Git] 다시 커밋하기  (0) 2018.05.27
[Git] 브랜칭 기법  (0) 2018.05.25
[Git] 브랜치와 Merge  (0) 2018.05.24
[Git] Pull에서 충돌 해결하기  (1) 2018.05.23

Git에서는 브랜치를 아주 자유롭게 만들 수 있어 작업을 분할하기 좋지만, 브랜치를 효과적으로 관리하지 못하면 의미가 없어지게 됩니다. 따라서 프로젝트의 참여자 전체가 브랜치를 다루는 방식을 통일하는 것이 좋습니다. 새로운 브랜치의 생성 조건, 브랜치 네이밍, 통합 조건 등등의 규칙들입니다. 여기서 설명하려는 브랜칭 기법들은 일반적으로 브랜치를 통합 브랜치(Integration Branch)토픽 브랜치(Topic Branch)로 나눕니다.

통합 브랜치(Integration Branch)

통합 브랜치란 언제든지 배포할 수 있는 버전을 만들 수 있어야 하는 브랜치 입니다. 그렇기 때문에 늘 안정적인 상태를 유지하는 것이 중요합니다. 만약 어떤 문제가 발견되어 그 문제(버그)를 수정하거나 새로운 기능을 추가할 때, 토픽 브랜치를 만듭니다. 처음에는 보통 통합 브랜치에서 토픽 브랜치를 만들어 냅니다.

일반적으로 저장소를 처음 만들었을 때에 생기는 master 브랜치를 통합 브랜치로 사용합니다.

토픽 브랜치(Topic Branch)

토픽 브랜치란, 기능 추가나 버그 수정과 같은 단위 작업을 위한 브랜치 입니다. feature_x, feature_y, hotfix_z와 같은 형태의 브랜치들입니다. 여러 개의 작업을 동시에 진행할 때에는, 그 수만큼 토픽 브랜치를 생성하는 방식입니다.

토픽 브랜치는 보통 통합 브랜치로부터 만들어 내며, 토픽 브랜치에서 특정 작업이 완료되면 다시 통합 브랜치에 병합하는 방식으로 진행됩니다.

Git-flow

Git-flow는 Vincent Driessen의 'A successful Git branching model'을 적용하여 코드를 관리하는 전략입니다. Vincent의 브랜칭 모델은 'feature - develop - release - hotfixes - master'의 형태로 브랜치를 나누는데, git-flow도 별반 다르지 않습니다.

git-flow의 주요 브랜치는 masterdevelop입니다. 이 브랜칭 전략에서는 새 버전을 배포하기 위해 master 브랜치에 병합합니다. master 브랜치로 커밋될 때 git hook 스크립트를 걸어서 자동으로 빌드하고 운영 서버로 배포하는 형식을 취합니다.

feature

다음 버전을 위해 개발하는 코드는 develop에서 관리합니다. 그러나 이 브랜치 하나로는 한계가 있기 때문에, 보조 브랜치들을 사용합니다. 기능 개발을 위해 feature 브랜치를 사용합니다. develop에서 시작하고, 병합도 develop을 향합니다. feature 브랜치는 그 기능을 다 완성할 때까지 유지하고 있다가 다 완성되면 develop 브랜치로 병합합니다.

release

release 브랜치는 실제 배포할 상태가 된 경우에 생성하는 브랜치입니다. develop에서 시작하고, 병합은 develop과 master에 합니다. 지금까지 개발한 기능을 묶어 develop 브랜치에서 release 브랜치를 따내고, develop 브랜치에서는 다음번 릴리즈를 위한 준비, release 브랜치에서는 버그픽스에 대한 부분만 커밋하게 되고 릴리즈가 준비되었다고 생각하면 master로 머지를 진행합니다. 이후 tag 명령을 이용하여 릴리즈 버전에 대해 명시합니다.

hotfix

production 과정에서 발생한 버그들은 전부 hotfix로 이동합니다. master에서 시작하고, 병합은 develop과 master에 합니다. 이미 배포한 운영버전에서 발생한 문제를 해결하기 위해 만드는 브랜치입니다. 운영 버전에 생긴 치명적인 버그는 즉시 해결해야 하기 때문에, 마스터 브랜치에 만들어둔 태그로부터 hotfix 브랜치를 생성합니다.

git-flow는 꽤 오래 전에 나온 전략인데, git-flow만의 단점(브랜치가 많아 복잡하고, 안 쓰는 브랜치가 있으며 몇몇 브랜치의 포지션은 애매함)을 극복하기 위해 github flowgitlab flow 전략이 나왔습니다.

Github-flow

Github-flow는 배포가 정기적으로 이루어지는 프로젝트를 위한 브랜치 기반 workflow입니다. 브랜치 전략이 단순하고, Pull request 위주로 각 브랜치들의 변경 사항을 머지하므로 코드 리뷰를 자연스럽게 수행할 수 있습니다. Git flow는 좋지만, Github에서 사용하기에는 복잡하다는 입장에서 만들어졌습니다.

브랜칭

브랜칭은 git의 핵심 개념이며, 전체 Github flow는 이를 기반으로 합니다. Github flow에서는 브랜치에 대해 딱 한가지 규칙만이 있는데, master 브랜치의 모든 항목은 항상 배포 가능한 상태여야 한다는 것입니다. 따라서, 기능 구현이나 수정 작업을 할 때 master에서 브랜치를 생성해야 합니다.

Github-flow에서는 브랜치 네이밍, 브랜치 생성, 삭제에 관한 규칙도 따로 없으며, master 브랜치에 대한 규칙 하나만 정확하면 됩니다.

커밋

브랜치가 생성되고 나면, 변경 사항을 만들어낼 것입니다. 파일을 추가, 편집, 삭제할 때마다 커밋을 추가하게 됩니다. 각 커밋에는 관련된 변경 메시지가 있으며, 이는 해당 변경이 이루어진 이유를 설명합니다. 각 커밋은 별도의 변경 단위로 간주되며, 따라서 특정 커밋으로 돌아가거나, 복구하는 데에 편한 환경을 만들어낼 수 있습니다.

Pull request

Pull request는 Github-flow의 가장 중요한 요소입니다. 브랜치 간의 병합을 위해 단순히 merge만을 사용하지 않고, 한 번의 리뷰를 거치도록 변경 사항을 '요청'하는 일입니다. 따라서 Pull request라는 개념은 특히 오픈 소스 프로젝트의 기여에 자주 사용됩니다. 개발 프로세스 중 언제라도 Pull request를 열 수 있고, 이는 해당 브랜치의 변경 사항에 대한 토론을 시작합니다. Github-flow에서 Pull request는 master 브랜치에 변경 사항을 병합하기 전에 코드를 리뷰하기 위한 용도로 자주 사용됩니다.

'Git 레거시 글' 카테고리의 다른 글

[Git] 다시 커밋하기  (0) 2018.05.27
[Git] 파일 상태 Lifecycle 관리하기  (0) 2018.05.26
[Git] 브랜치와 Merge  (0) 2018.05.24
[Git] Pull에서 충돌 해결하기  (1) 2018.05.23
[Git] 원격 저장소에서 Pull받기  (0) 2018.05.22

여러 개발자가 동시에 작업하다 보면, 동일한 소스코드 위에서 기능을 추가할 때도 있고, 버그를 수정할 때도 있습니다. 따라서 동일한 소스코드를 기반으로 작업을 하더라도 다양한 버전의 코드가 만들어질 수밖에 없습니다. 이럴 때, 각자 독립적인 작업 영역 안에서 작업할 수 있도록 해주는 것이 브랜치(branch)입니다. 각각의 브랜치는 다른 브랜치의 영향을 받지 않기 때문에 여러 작업을 동시에 진행할 수 있습니다. 브랜치는 분기라는 뜻이지만 동사로서 가지를 뻗다라는 뜻도 가지고 있고, 실제로 브랜치는 가지를 뻗는다는 느낌으로 생각하면 됩니다.

브랜치의 생성과 이동

Git에서는 항상 작업할 브랜치를 선택해야 합니다. 처음에는 master 브랜치가 선택되어 있습니다. 다른 브랜치로 이동하기 위해서는 checkout 명령을 사용하고, 존재하지 않는 브랜치라면 이동되지 않기 때문에 branch 명령으로 브랜치를 만들고 나서 checkout해 봅시다.

git branch [branch_name]

$ git branch feature_x

별다른 인자 없이 git branch만을 실행하면 브랜치 목록 전체를 확인할 수 있습니다. * 표시는 현재 선택된 브랜치를 뜻합니다.

$ git branch
 master
 * feature_x

브랜치를 삭제하려면 -d 옵션을 이용하면 됩니다.

$ git branch -d feature_x

git checkout [branch_name]

$ git checkout feature_x

checkout이 성공하면, 브랜치 안에 있는 마지막 커밋 내용이 작업 트리에 펼쳐집니다. 브랜치가 전환되었으므로 이 후에 실행한 커밋은 전환한 브랜치에 적용됩니다. Git의 브랜치에는 HEAD라는 특수한 포인터가 있습니다.

HEAD는 현재 작업하는 로컬 브랜치을 나타내는 이름입니다. 기본적으로는 master 브랜치의 선두 부분을 나타냅니다. HEAD를 이동하면, 사용하는 브랜치가 변경됩니다. 따라서 현재 masterfeature_x 브랜치가 존재하고, feature_x 브랜치로 이동했으므로 HEAD는 feature_x에 있습니다.

checkout에서 -b 옵션을 함께 붙여주면 브랜치 생성과 이동을 한번에 실행할 수 있습니다.

$ git checkout -b feature_y

Merge

git merge [branch_name]

다른 브랜치의 변경사항을 현재 브랜치에 병합하기 위해 사용합니다. 예를 들어, 현재 브랜치가 masterfeautre_x 브랜치의 변경사항을 병합하려면 아래의 명령을 실행합니다.

$ git merge feature_x

여기서도 conflict가 발생할 수 있으며, pull에서 conflict가 발생했던 경우와 동일하게 해결하고 add - commit - push하면 됩니다.

자신의 로컬 저장소에서 진행한 변경 이력을 원격 저장소에 push할 당시에, 로컬 저장소가 최신 버전이 아닌 경우(clone 이후 다른 사람이 remote에 push를 진행했을 경우) 자신의 push 요청이 거절됩니다. 이런 경우 병합(merge) 작업을 진행하여 remote에 반영된 다른 사람의 변경 이력을 로컬 저장소에 갱신해야 합니다. 원격 저장소의 변경 사항을 무시하고 자신의 변경 이력을 덮어쓸 수도 있습니다. 아래는 강제 push의 몇가지 예입니다.

$ git push -f
$ git push --force
$ git push origin +<branch_name>

그러나 강제 push를 할 일을 만드는 것은 정말 좋지 않은 일입니다. remote의 변경 이력을 로컬로 merge하는 것이 가장 좋습니다. 그냥 단순히 pull만 하면 됩니다.

$ git pull origin master

문제될 상황이 없다면 git이 알아서 변경 사항을 통합해 줍니다. 여기서 문제 상황은 충돌(conflict)인데, 예를 들어 로컬 저장소에서 README.md라는 파일을 변경했고, 가장 최근 로컬 저장소의 pull 이후 remote의 변경 이력에 README.md의 수정이 포함되어 있다면 충돌이 발생합니다.

충돌 해결하기

병합 기능은 경우에 따라 자동으로 병합할 수 없는 경우가 있고, 그 경우가 바로 충돌이며, 로컬 저장소의 변경 대상과 remote의 변경 대상이 같을 때 충돌이 발생한다고 했습니다. 두 변경 내용 중 어떤 것을 적용할 것인지 판단할 수 없기 때문입니다. 이 경우 git은 적용할 변경 내용의 판단을 개발자에게 맡깁니다. 따라서 충돌은 수동으로 수정해 주어야 합니다. 아래는 충돌이 난 파일의 예입니다.

<<<<<<< HEAD
Hello
=======
Hello!
>>>>>>> aab6d380aaf237a7c0aae28e00ea4607c8a7eec9

'======='로 구분된 위쪽 부분이 로컬 저장소, 아래쪽 부분이 remote의 변경 내용입니다. 둘 중 어떤 변경 이력을 적용할 지 선택하고, 모든 충돌 부분을 수정한 이후 커밋을 수행하면 됩니다.

'Git 레거시 글' 카테고리의 다른 글

[Git] 브랜칭 기법  (0) 2018.05.25
[Git] 브랜치와 Merge  (0) 2018.05.24
[Git] 원격 저장소에서 Pull받기  (0) 2018.05.22
[Git] 원격 저장소에 Push하기  (3) 2018.05.21
[Git] 원격 저장소 만들기  (0) 2018.05.20

+ Recent posts