Git Step-3 : Branch

1. Branch란?

의미적으로는 작업 유형에 따라 만든 분리된 영역을 Branch라고 합니다. 실제 Branch는 어떤 영역의 공간을 할당하는 것이 아니라 특정 커밋 객체를 가르키는 포인터입니다.

커밋 객체는 git commit 명령이 실행될 때 Staging Area에 있는 체크섬, 저자나 메시지 같은 메타 데이터, 이전 커밋의 대한 체크섬 등을 포함해서 저장되는 객체입니다.

image-20210302054304515

image-20210302054328453

Git의 branch는 커밋 포인터를 가볍게 이동할 수 있고, 처음 커밋하면 이 master branch가 생성된 커밋을 가리킵니다. 이후 커밋을 만들면 master branch는 자동으로 마지막 커밋을 가리킵니다.

image-20210302060500409

master branch는 git init 명령으로 초기화할 때 자동으로 만들어지는 default branch이고 HEAD는 현재 git이 가르키고 있는 branch입니다.

image-20210302061126845

2. Branch 기초

  • branch 생성

    git branch <branch name> 명령으로 branch를 생성할 수 있습니다.

    $git branch testing
    

    새로 만든 testing branch는 현재 작업중인 마지막 커밋을 가리키고, head는 아직 master branch를 가르키고 있는 상태가 됩니다.

    image-20210302063157860

    image-20210302063241132

    git log --decorate 명령으로 branch가 어떤 커밋을 가리키는지 확인할 수 있습니다.

    $git log --decorate
    commit 59e1dffacbda1bcdea5d0005baa2dbe76ab1dc88 (HEAD -> master, testing)
    Author: donghyeok <donghyeok.jh@gmail.com>
    Date:   Tue Mar 2 06:16:28 2021 +0900
      
        check branch
    

    또는 git branch -v 명령으로 branch 별 커밋의 hash값과 메시지를 확인할 수 있습니다.

    $git branch -v
    * master f8ce154 azz
      test   5030dd1 Add file in test branch
    
  • branch 이동

    git checkout <branch name> 명령으로 다른 branch로 이동하며 HEAD는 이동한 branch를 가르킵니다.

    $git checkout testing
    Switched to branch 'testing'
    

    image-20210302064242399

    testing branch에서 파일을 하나 생성하고 commit 후 로그를 확인하면 HEAD가 testing branch를 가르키고 있는 것을 확인할 수 있습니다.

    $git commit -m "testing branch commit"
    [testing 096aca5] testing branch commit
     1 file changed, 1 insertion(+)
     create mode 100644 testing_newfile.txt
       
    $git log --decorate
    commit 096aca5ae84fe44b0b3d17652b1fd3464d26ca5b (HEAD -> testing)
    Author: donghyeok <donghyeok.jh@gmail.com>
    

    testing branch에서 새로운 커밋을 하나 만들었기 때문에 testing branch는 새로 만든 커밋을 가르키고, master branch는 그전 커밋을 가르키는 상태가 됩니다.

    image-20210302065107143

    이 상태에서 master branch로 checkout하면 HEAD는 master branch로 변경되지만 testing branch에서 작업한 파일은 보이지 않습니다. 즉 working directory가 master branch가 가르키고 있는 커밋 기준으로 변경됩니다.

    $git checkout master
    Switched to branch 'master'
    

    image-20210302065422574

    image-20210302065752848

    HEAD branch가 가리키고 있는 커밋이 무엇이고 branch들의 history를 ASCII graph로 확인할려면 git log --oneline --decorate --graph --all 명령어를 실행합니다.

    $git log --oneline --decorate --graph --all
    

    image-20210302072912776

    image-20210302074446040

    git checkout -b <branch name> 명령으로 branch 생성과 checkout을 동시에 할 수 있습니다.

    $git checkout -b testing3
    Switched to a new branch 'testing3'
    

    checkout 명령으로 branch를 이동할려고 할 때 checkout할 branch와 head branch에 commit하지 않은 파일이 충돌나면 branch를 변경할 수 없습니다.

    Git의 branch는 어떤 한 커밋을 가리키는 40자의 SHA-1 체크섬 파일에 불과합니다. 그래서 만들기도 쉽고 지우기도 쉽습니다. 새로 branch를 하나 만드는 것은 41btye(40btye의 체크섬과 1byte 줄바꿈 문자)에 불과합니다. branch를 생성할 때 프로젝트를 통째로 복사해야하는 다른 VCS와의 차이는 극명합니다.

  • branch 보기

    git branch 명령은 현재 생성된 branch 및 head branch 정보를 알 수 있습니다.

    $git branch
      master
      testing
      testing2
    * testing3
    

    git branch -v 명령은 branch 마다 마지막 커밋 메시지도 함께 보여줍니다.

    $git branch -v
      conflictTestBranch cc2fb517 modify conflict branch
    * master             88d73a25 modify conflict file
      testing2           59e1dffa check branch
    
  • Branch Merge

    Branch Merge는 head branch에서 git merge <target-branch> 명령으로 merge 합니다. 이 외 rebase 명령으로 하는 방법도 있는데 rebase는 주의할 점이 있어 git에 많이 익숙해진 다음 사용하는 것이 좋습니다.

    image-20210302235633127

    merge할 때 메시지를 보면 Fast-forward라는 단어가 나옵니다. 이것은 testing3 branch가 가리키고 있는 커밋이 master branch가 가리키고 있는 커밋을 참조하고 있기 때문에 master branch의 참조 포인터를 testing3 branch가 가르키고 있는 커밋으로 옮겨주기만 하는 방식입니다.

    image-20210303000910403

    merge할 testing branch가 가르키고 있는 커밋이 master branch가 가리키고 있는 커밋과 다른 방향으로 진행 중인 상태 즉 testing branch 생성 이후 커밋을 2번 이상한 경우에는 각 branch가 가르키고 있는 공통 커밋을 하나 만들어 3-way Merge 커밋(=Merge 커밋)을 하게 됩니다.

    image-20210304214209022

    image-20210303004900249

  • branch 삭제

    용도를 다 한 branch는 git branch -d <branch name> 명령으로 삭제합니다.

    $git branch -d testing3
    Deleted branch testing3 (was 3f98b9c).
      
    $git log --oneline 
    삭제전: 3f98b9c (HEAD -> master, testing3) testing3 commit-3
    삭제후: 3f98b9c (HEAD -> master) testing3 commit-3
    

    merge가 완료되었거나 변경내역이 없는 branch의 경우에는 git branch -d <branch name>명령으로 삭제가 가능하지만 변경내역이 존재하는데 merge가 안된 경우에는 branch 삭제 시에 오류가 발생합니다. 만약 강제로 삭제하려면 git branch -D <branch name> 명령을 사용해야 합니다.

    $git branch -d newbranch
    Deleted branch newbranch (was 88d73a25).
      
    $git checkout -b newbranch2
    Switched to a new branch 'newbranch2'
    $vim new.txt
    $git add .
    $git commit -m "modify"
    [newbranch2 d114a60d] modify
     1 file changed, 1 insertion(+), 1 deletion(-)
    $git checkout master
    Switched to branch 'master'
      
    $git branch -d newbranch2
    error: The branch 'newbranch2' is not fully merged.
    If you are sure you want to delete it, run 'git branch -D newbranch2'.
      
    $git branch -D newbranch2
    Deleted branch newbranch2 (was d114a60d).
    

3. Conflict

  • Conflict(충돌)은 두 개의 branch에서 동일한 파일을 수정한 상태에서 Merge를 하려고 하면 Conflict가 발생하고 개발자가 이를 해결해야 Merge가 가능합니다.

    Conflict가 발생할 때 git status 명령으로 확인하면 Unmerged 상태의 파일을 확인할 수 있습니다.

    $git merge conflictTestBranch
    Auto-merging new.txt
    CONFLICT (content): Merge conflict in new.txt
    Automatic merge failed; fix conflicts and then commit the result.
      
    $git status
    On branch master
    You have unmerged paths.
      (fix conflicts and run "git commit")
      (use "git merge --abort" to abort the merge)
      
    Unmerged paths:
      (use "git add <file>..." to mark resolution)
            both modified:   new.txt
      
    no changes added to commit (use "git add" and/or "git commit -a")
    

    충돌난 파일을 열어보면 충돌이 난 부분을 표준 형식에 따라 표시 해줍니다.

    1aa111
    222aa
    bbbbbbbbbbbbbbbb
    <<<<<<< HEAD
    master branch
    =======
    conflict branch
    >>>>>>> conflictTestBranch
    

    둘 중 하나를 선택하고 나머지는 삭제합니다.

    1aa111
    222aa
    bbbbbbbbbbbbbbbb
    conflict branch
    

    git commit 후 git log를 확인하면 정상적으로 Merge된 걸 알 수 있습니다.

    $git commit -a -m "modify conflict file"
      
    $git log --oneline --graph --all
    *   88d73a25 (HEAD -> master) modify conflict file
    |\
    | * cc2fb517 (conflictTestBranch) modify conflict branch
    * | b0fefe61 modify master branch
    |/
    * 4fe02393 modify B
    

    conflict 후 commit message는 어떻게 충돌을 해결했고 좀 더 확인해야 하는 부분은 무엇이고 왜 그렇게 해결했는지에 대해서 자세하게 기록하는 것이 나중에 이 Merge 커밋을 이해하는데 도움을 줍니다.

4. Branch Workflow

Long-Running Branch은 Stable(배포)용 Master, 개발용 develop, 오류 수정용 hotfix, 릴리리즈용 release 등 topic branch로 관리, 통합하여 진행합니다. Short-Running Branch는 프로젝트의 환경에 따라 내부적으로 조율해서 진행해야 됩니다.

image-20210306212123577

5. Remote Branch

remote Refs는 remote 저장소에 있는 branch, tag 등을 가르키는 포인터 레퍼런스 입니다. 그리고 remote Refs보다 많이 사용되는 remote traking branch가 있습니다. remote tracking branch<remote>/<branch> 형식의 이름을 가지며 remote branch를 추적합니다. origin remote의 master branch를 추적하는 remote tracking branch는 origin/master입니다.

origin이라는 이름은 git clone 시 자동으로 만들어 주는 이름이며 git clone -o <rename> 명령으로 이름을 변경할 수 있습니다. 예를들어 git clone -o good 이라고 옵션을 주고 실행하면 good/master라는 remote tracking branch가 생성됩니다.

git ls-remote <remote> 명령으로 모든 remote Refs를 조회할 수 있습니다.

$git ls-remote ts
4cdb5fe1a74dc6d2cac2143dcc8d9929fc84ea82        HEAD
92d673b5ae282df80213f9543c7373c333a91ef1        refs/heads/flexbox-sticky-footer
99c60f5957eca32b8612ddae0d87e72b94c9bac0        refs/heads/gh-pages-2.2.1
1f1aa440b171b711f4eae360d1d4b82fd2cae4c7        refs/heads/gh-pages-3.1.6
4cdb5fe1a74dc6d2cac2143dcc8d9929fc84ea82        refs/heads/master
075482c3832bde4f00908d976c2177b9f7beea02        refs/heads/navbar-menu
...

git remote show <remote> 명령은 모든 remote branch와 그 정보를 보여줍니다.

$git remote show ts
* remote ts
  Fetch URL: https://github.com/donghyeok-dev/donghyeok-dev.github.io.git
  Push  URL: https://github.com/donghyeok-dev/donghyeok-dev.github.io.git
  HEAD branch: master
  Remote branches:
    flexbox-sticky-footer tracked
    gh-pages-2.2.1        tracked
    gh-pages-3.1.6        tracked
    master                tracked
    navbar-menu           tracked
  Local ref configured for 'git push':
    master pushes to master (local out of date)

remote tracking branch는 임의로 변경할 수 없으며 git fetch, pull, push 명령으로 서버 정보를 받아 올 때 자동으로 갱신됩니다.

만약 A개발자가 remote master branch에 commit을 push하면 B개발자의 git repository의 origin/master는 갱신되지 않기 때문에 이전 상태의 커밋을 가르키고 있습니다. 이 상태에서 B개발자가 별도의 작업을 한 후 commit을 한 상태라면 remote와 local의 master의 커밋 히스토리는 분기 됩니다. B개발자가 git fetch 또는 git pull을 하게 되면 remote master의 내용을 받아와서 remote tracking branch를 갱신합니다. (git pull을 하게 되면 local master와 Merge하게 됩니다.)

image-20210304232651658

git fetch 또는 git pull (origin/master와 local master 간 Merge 까지 진행됨.)

image-20210304232734852

local branch를 서버로 전송할 때는 git push <remote> <remote-branch> 명령을 사용합니다. push 명령은 remote 저장소에 쓰기 권한이 있어야 사용할 수 있습니다. 만약 다른 이름을 사용하려면 git push <remote> <local-branch>:<remote-branch> 명령을 사용할 수 있습니다.

만약 다른 개발자가 remote server에 apple이라는 branch를 생성하여 push하고 이것을 내려 받기 위해서는 다음과 같이 명령을 수행 해야 합니다.

  1. git fetch origin apple 명령을 실행.
  2. git merge origin/apple 명령으로 Merge 또는 git checkout -b <branch> <remote>/<remote-branch> 명령으로 새로운 branch 생성.
$git checkout -b apple origin/apple
Branch apple set up to track remote branch apple from origin.
Switched to a new branch 'apple'

tracking branch의 설정 로그를 보려면 git log --vv 명령을 실행합니다.

$ git branch -vv
* master aa527242 [origin/master: ahead 3] Merge local master branch and origin/master branch
demo f0651d3 [origin/apple: ahead 3, behind 1] this should do it

ahead 3 : 로컬 branch가 remote branch보다 커밋이 3개 앞서 있음.

behind 1: 로컬 branch가 remote branch보다 커밋이 1개 뒤쳐져 있음.

remote branch 삭제는 git push origin --delete <branch name> 명령을 사용합니다.

6. Branch Rebase

git merge 처럼 두개 branch를 합치는 방법 중 하나이고, merge와 다른점은 커밋 히스토리를 통합 시키는 것입니다.

아래와 같은 상황일 때 rebase merge 하는 과정을 살펴 보겠습니다.

image-20210305065826104

target-branch인 rebaseTest branch로 checkout 합니다.

$git checkout rebaseTest
Switched to branch 'rebaseTest'

$git log --oneline --graph --all
* 9c16439b (HEAD -> rebaseTest) Modify sub file in rebaseTest branch
* 37b69f55 Create new file in rebaseTest branch
| * b80b8674 (master) Modify main file in master branch
|/
* 8cd4011a Create new file in master

image-20210305071529933

rebase 명령이 실행되면 내부적으로 변경된 사항을 Patch로 만들고 master branch에 Patch를 적용 합니다. Patch는 기존 rebaseTest의 커밋을 master branch 커밋 히스토리에 잘라내기-붙여넣기 하는 것입니다. 다만 복사된 커밋들의 hash 값들은 변경 됩니다. Patch가 완료되면 rebaseTest branch는 마지막 커밋을 가르킵니다.

$git rebase master
First, rewinding head to replay your work on top of it...
Applying: Create new file in rebaseTest branch
Applying: Modify sub file in rebaseTest branch

$git log --oneline --graph --all 
* 9553268e (HEAD -> rebaseTest) Modify sub file in rebaseTest branch
* 2265755a Create new file in rebaseTest branch
* b80b8674 (master) Modify main file in master branch
* 8cd4011a Create new file in master

image-20210305073839162

마지막으로 master branch에 checkout 한 다음 Fast-Forword Merge 시키면 됩니다.

$git checkout master
Switched to branch 'master'

$git merge rebaseTest
Updating b80b8674..9553268e
Fast-forward
 rebase_sub1.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 rebase_sub1.txt

$git log --oneline --graph --all
* 9553268e (HEAD -> master, rebaseTest) Modify sub file in rebaseTest branch
* 2265755a Create new file in rebaseTest branch
* b80b8674 Modify main file in master branch
* 8cd4011a Create new file in master

image-20210305074813089

Rebase를 할 때 target branch에 checkout 후 rebase 명령을 내렸습니다. 아래와 같이 명령을 실행하면 한번에 두 과정이 실행됩니다.

$git rebase <source branch> <target branch>

rebase가 유용하게 쓰일 때가 있습니다. 바로 master - Server branch - Clinet branch 형태로 진행중에 Server branch의 기능이 구현 중이고 Server branch에서 생성된 Clinet branch의 기능이 완료되어 master branch에 적용해야되는 상황입니다.

image-20210305081514180

$git log --oneline --graph --all
* c1c32178 (client) Modify file in client branch
* 83d17b8b Add file in cleint branch
| * 08e39b8f (server) Modify test file in server branch
| * 14c4a6ad Add test tile in server branch
|/
* 786e1b64 Modify file in Server branch
* 860f109c Add file in Server branch
| * 3784cd3b (HEAD -> master) Add file in Master branch
|/
* 9553268e (rebaseTest) Modify sub file in rebaseTest branch

image-20210305082836745

이 상태에서 git rebase --onto master server client 명령을 실행하면 master와 client만 합쳐진게 보입니다.

$git rebase --onto master server client
First, rewinding head to replay your work on top of it...
Applying: Add file in cleint branch
Applying: Modify file in client branch

$git log --oneline --graph --all
* 95c6b9d6 (HEAD -> client) Modify file in client branch
* 56b71737 Add file in cleint branch
* 3784cd3b (master) Add file in Master branch
| * 08e39b8f (server) Modify test file in server branch
| * 14c4a6ad Add test tile in server branch
| * 786e1b64 Modify file in Server branch
| * 860f109c Add file in Server branch
|/
* 9553268e (rebaseTest) Modify sub file in rebaseTest branch

image-20210305083055271

하지만 master의 커밋 포인터가 최신이 아니므로 Fast-Forward 시킵니다.

$git checkout master
$git merge client
Updating 3784cd3b..95c6b9d6
Fast-forward
 client.java | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 client.java
 
$git log --oneline --graph --all
* 95c6b9d6 (HEAD -> master, client) Modify file in client branch
* 56b71737 Add file in cleint branch
* 3784cd3b Add file in Master branch
| * 08e39b8f (server) Modify test file in server branch
| * 14c4a6ad Add test tile in server branch
| * 786e1b64 Modify file in Server branch
| * 860f109c Add file in Server branch
|/
* 9553268e (rebaseTest) Modify sub file in rebaseTest branch

*Rebase는 이미 공개 저장소에 Push 한 커밋을 Rebase하면 안됩니다. Rebase는 기존 커밋을 그대로 사용하는 것이 아니라 내용은 같지만 새로운 커밋을 만들기 때문에 협업 중 변경 전 커밋을 pull 해서 작업하고 있는 개발자와 꼬일 수 있기 때문입니다.

7. Stash

작업 중인 branch에서 commit을 하지 않은 상태로 다른 branch로 이동할 경우 working directory와 staging area는 공유되어 버립니다.

develop$ git status -s
M  a_module.java
 M b_module.java
 
develop$ git checkout stable
M       a_module.java
M       b_module.java
Switched to branch 'stable'
Your branch is up to date with 'origin/stable'.

stable$ git status -s
M  a_module.java
 M b_module.java

작업 중인 branch에서 다른 branch로 이동하기전에 임시로 저장한 뒤 branch를 변경하고, 추후에 다시 작업중이었던 branch로 돌아와서 임시 저장한 내용들을 불러오는 기능이 stash입니다.

usage: git stash list [<options>]
   or: git stash show [<options>] [<stash>]
   or: git stash drop [-q|--quiet] [<stash>]
   or: git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]
   or: git stash branch <branchname> [<stash>]
   or: git stash clear
   or: git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
          [-u|--include-untracked] [-a|--all] [-m|--message <message>]
          [--] [<pathspec>...]]
   or: git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
          [-u|--include-untracked] [-a|--all] [<message>]

사용 예

develop$ git stash save
develop$ git stash list
stash@{0}: WIP on develop: 0e3c111 modify 2
develop$ git checkout stable

stable$ git status -s
//변경한 branch에서 작업...
stable$ git checkout develop

develop$ git stash list
stash@{0}: WIP on develop: 0e3c111 modify 2

//임시 저장한 내용 불러옴.
develop$ git stash apply
On branch develop
Your branch is ahead of 'origin/develop' by 2 commits.
  (use "git push" to publish your local commits)

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   a_module.java
        modified:   b_module.java

no changes added to commit (use "git add" and/or "git commit -a")

develop$ git stash list
stash@{0}: WIP on develop: 0e3c111 modify 2

//a_module.java는 staging area에 있었는데 working directory로 변경됨.
develop$ git status -s
 M a_module.java
 M b_module.java

//임시 저장 내용 삭제.
develop$ git stash drop
Dropped refs/stash@{0} (9ed06287bbff2fc7e0286d6157ce60555eb2956b)


//불러옴과 동시에 임시 저장 삭제는 pop명령을 이용하면 됨.
develop$ git stash save
Saved working directory and index state WIP on develop: 0e3c111 modify 2

develop$ git stash pop
On branch develop
Your branch is ahead of 'origin/develop' by 2 commits.
  (use "git push" to publish your local commits)

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   a_module.java
        modified:   b_module.java

no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (34d56d07c321ba3520b77c85e7e7bb27467c7930)

develop$ git stash list

8. Branch를 이용한 협업 시뮬레이션

github에 repository 하나를 만듭니다.

remote에 통합 branch “stable”를 생성합니다.

$touch README.md
$git add README.md
$git commit -m "first commit"
$git branch -M stable
$git remote add origin https://github.com/donghyeok-dev/gitTest.git
$git push -u origin stable

git branch -M <branch name> 명령은 현재 branch의 이름을 변경하는데 존재하면 덮어쓰기 합니다.

git branch -m <source branch> <rename branch> 명령으로 branch 이름도 변경할 수 있습니다.

git remote add <remote name> <remote url> 명령으로 remote Repository를 추가할 수 있습니다.

git push -u <remote name> <branch name> 명령으로 remote branch에 커밋을 push합니다.

-u옵션은 최초 한번만 remote와 branch 인자를 작성하여 사용하면 다음 git push 할 때부터는 remote와 branch 인자를 생략하고 git push만 사용할 수 있습니다.

$git log --online
8d5fed7 (HEAD -> stable, origin/stable) first commit
$git ls-remote origin
ff44a5761920ae459c420d449955def12e38bad1        HEAD
ff44a5761920ae459c420d449955def12e38bad1        refs/heads/stable
$git remote show origin
* remote origin
  Fetch URL: https://github.com/donghyeok-dev/gitTest.git
  Push  URL: https://github.com/donghyeok-dev/gitTest.git
  HEAD branch: stable
  Remote branch:
    stable tracked
  Local branch configured for 'git pull':
    stable merges with remote stable
  Local ref configured for 'git push':
    stable pushes to stable (up to date)

개발자A와 개발자B가 협업하는 과정을 테스트 하기 위해 디렉토리를 각각 만들고 –local 옵션을 사용해 user.name과 user.email을 설정 했습니다.

#gitTest1 directory
$git config --local user.name "developerA"
$git config --local user.email "developerA@example.com"

#gitTest2 directory
$git config --local user.name "developerB"
$git config --local user.email "developerB@example.com"

몇가지 상황을 나열하고 분석해 봅니다.

A개발자: 조금 전에 remote에 repository(origin/stable)를 만들면서 commit하나를 push한 상황.

B개발자: A개발자와 협업하기 위해 origin의 stable branch를 clone 합니다.

$git clone https://github.com/donghyeok-dev/gitTest.git gitTest2

B개발자: stable branch의 README.md파일을 수정해서 origin stable에 push합니다.

$git add README.md
$git commit -m "Developer B modify the file"
$git push origin stable

A개발자: a.txt 파일을 새로 생성하고 commit합니다.

$git add a.txt
$git commit -m "Add file a.txt from A developer"

A개발자: remote에 push하지 않고, remote에 변경사항을 fetch-merge합니다.

$git fetch origin
     remote: Enumerating objects: 5, done.
     remote: Counting objects: 100% (5/5), done.
     remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
     Unpacking objects: 100% (3/3), 235 bytes | 58.00 KiB/s, done.
     From https://github.com/donghyeok-dev/gitTest
     8d5fed7..ff44a57  stable     -> origin/stable
 $git merge origin/stable
 	Merge made by the 'recursive' strategy.
     README.md | 1 +
     1 file changed, 1 insertion(+)

A개발자의 로그를 확인해 봅니다.

$git log --oneline --graph --all

9d6b6a6 (HEAD -> stable) Merge remote-tracking branch 'origin/stable' into stable
|\
| * ff44a57 (origin/stable) Developer B modify the file

| 487be31 Add file a.txt from A developer
|/

8d5fed7 first commit

B개발자의 로그를 확인해 봅니다.

$git log --oneline --graph --all
ff44a57 (HEAD -> stable, origin/stable, origin/HEAD) Developer B modify the file
8d5fed7 first commit

현재 각 개발자의 커밋 히스토리와 tracking branch의 상태

image-20210306203158116

지금부터는 workflow를 좀 나눠서 진행 해보겠습니다.

그 전에 현재 최신 커밋을 tag도 작성하고 remote에 반영하도록 하겠습니다.

$git tag -a v0.1 -m "Initial version" 9d6b6a6
$git push origin v0.1
$git push origin stable
$git log --oneline --graph --all
*   9d6b6a6 (HEAD -> stable, tag: v0.1, origin/stable) Merge remote-tracking branch 'origin/stable' into stable
|\
| * ff44a57 Developer B modify the file
* | 487be31 Add file a.txt from A developer
|/
* 8d5fed7 first commit

개발용 develop branch를 만들고 새로운 커밋을 하나 만듭니다.

$git checkout -b develop
$touch a_develop.txt
$git add .
$git commit -m "Add file a.develop.txt from A developer"

remote에서 stable과 develop branch를 확인 해봅니다.

image-20210306213802164

image-20210306213856465

B개발자도 새롭게 만든 branch에서 작업하기 위해 remote에서 내려받습니다.

$ git fetch origin
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 7 (delta 1), reused 6 (delta 0), pack-reused 0
Unpacking objects: 100% (7/7), 834 bytes | 104.00 KiB/s, done.
From https://github.com/donghyeok-dev/gitTest
   ff44a57..9d6b6a6  stable     -> origin/stable
 * [new branch]      develop    -> origin/develop
 * [new tag]         v0.1       -> v0.1
 
$git log --oneline --graph --all
* 971d61d (origin/develop) Add file a.develop.txt from A developer
*   9d6b6a6 (tag: v0.1, origin/stable, origin/HEAD) Merge remote-tracking branch 'origin/stable' into stable
|\
| * ff44a57 (HEAD -> stable) Developer B modify the file
* | 487be31 Add file a.txt from A developer
|/
* 8d5fed7 first commit

$git merge origin/stable
Updating ff44a57..9d6b6a6
Fast-forward
 a.txt | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a.txt
 
$git log --oneline --graph --all
* 971d61d (origin/develop) Add file a.develop.txt from A developer
*   9d6b6a6 (HEAD -> stable, tag: v0.1, origin/stable, origin/HEAD) Merge remote-tracking branch 'origin/stable' into stable
|\
| * ff44a57 Developer B modify the file
* | 487be31 Add file a.txt from A developer
|/
* 8d5fed7 first commit

$git checkout develop
Branch 'develop' set up to track remote branch 'develop' from 'origin'.
Switched to a new branch 'develop'

여기까지 진행했다면 모든 저장소가 통일되게 됩니다.

image-20210307002635400

B개발자가 신규 기능 개발을 위해 module이라는 branch를 생성해서 작업 한 후 develop branch로 merge하고 서버에 push하는 과정을 진행합니다.

$git checkout -b module
Switched to a new branch 'module'

$git log --oneline --graph --all
* 971d61d (HEAD -> module, origin/develop, develop) Add file a.develop.txt from A developer
*   9d6b6a6 (tag: v0.1, origin/stable, origin/HEAD, stable) Merge remote-tracking branch 'origin/stable' into stable
|\
| * ff44a57 Developer B modify the file
* | 487be31 Add file a.txt from A developer
|/
* 8d5fed7 first commit

$touch b_moudle.java
$git add .
$git commit -m "Add moudle from B developer"
$code b_moudle.java
$git add .
$git commit -m "Modify mobule form B developer"

$git checkout develop
Switched to branch 'develop'
Your branch is up to date with 'origin/develop'.

$git merge module
Updating 971d61d..3941cf7
Fast-forward
 b_moudle.java | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 b_moudle.java
 
$git log --oneline --graph --all
* 3941cf7 (HEAD -> develop, module) Modify mobule form B developer
* 9a1b5d2 Add moudle from B developer
* 971d61d (origin/develop) Add file a.develop.txt from A developer
*   9d6b6a6 (tag: v0.1, origin/stable, origin/HEAD, stable) Merge remote-tracking branch 'origin/stable' into stable
|\
| * ff44a57 Developer B modify the file
* | 487be31 Add file a.txt from A developer
|/
* 8d5fed7 first commit

$git push origin develop

$git log --oneline --graph --all
* 3941cf7 (HEAD -> develop, origin/develop, module) Modify mobule form B developer
* 9a1b5d2 Add moudle from B developer
* 971d61d Add file a.develop.txt from A developer
*   9d6b6a6 (tag: v0.1, origin/stable, origin/HEAD, stable) Merge remote-tracking branch 'origin/stable' into stable
|\
| * ff44a57 Developer B modify the file
* | 487be31 Add file a.txt from A developer
|/
* 8d5fed7 first commit

image-20210307005237273

A개발자도 develop branch에 어떤 모듈을 개발해서 merge 합니다.

(단, B개발자의 push는 모른채 개발합니다.)

$touch a_module.java
$git add .
$git commit -m "Add module from A developer"
$code a_module.java
$git add .
$git commit -m "Modify module from A developer"
$git log --oneline --graph --all
* 2f91a08 (HEAD -> module) Modify module from A developer
* 7b6a0b0 Add module from A developer
* 971d61d (origin/develop, develop) Add file a.develop.txt from A developer
*   9d6b6a6 (tag: v0.1, origin/stable, stable) Merge remote-tracking branch 'origin/stable' into stable
|\
| * ff44a57 Developer B modify the file
* | 487be31 Add file a.txt from A developer
|/
* 8d5fed7 first commit

$git checkout develop
Switched to branch 'develop'

$git merge module
Updating 971d61d..2f91a08
Fast-forward
 a_module.java | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 a_module.java
 
$git log --oneline --graph --all
* 2f91a08 (HEAD -> develop, module) Modify module from A developer
* 7b6a0b0 Add module from A developer
* 971d61d (origin/develop) Add file a.develop.txt from A developer
*   9d6b6a6 (tag: v0.1, origin/stable, stable) Merge remote-tracking branch 'origin/stable' into stable
|\
| * ff44a57 Developer B modify the file
* | 487be31 Add file a.txt from A developer
|/
* 8d5fed7 first commit

$ git push origin develop
To https://github.com/donghyeok-dev/gitTest.git
 ! [rejected]        develop -> develop (fetch first)
error: failed to push some refs to 'https://github.com/donghyeok-dev/gitTest.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

push가 rejected 당합니다. 먼저 fetch 를 하라고 합니다.

$git fetch origin
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 5 (delta 2), reused 5 (delta 2), pack-reused 0
Unpacking objects: 100% (5/5), 461 bytes | 76.00 KiB/s, done.
From https://github.com/donghyeok-dev/gitTest
   971d61d..3941cf7  develop    -> origin/develop
  
$git log --oneline --graph --all
* 2f91a08 (HEAD -> develop, module) Modify module from A developer
* 7b6a0b0 Add module from A developer
| * 3941cf7 (origin/develop) Modify mobule form B developer
| * 9a1b5d2 Add moudle from B developer
|/
* 971d61d Add file a.develop.txt from A developer
*   9d6b6a6 (tag: v0.1, origin/stable, stable) Merge remote-tracking branch 'origin/stable' into stable
|\
| * ff44a57 Developer B modify the file
* | 487be31 Add file a.txt from A developer
|/
* 8d5fed7 first commit

$ls
README.md  a.txt  a_develop.txt  a_module.java

A개발자의 develop branch와 fetch를 받아온 B개발자의 branch 부분이 분기 되어 있고, 실제 ls로 보아도 b 개발자의 b_module.java 파일이 없습니다. 로그에 보이는 origin/develop과 merge를 하고 push 합니다.

$git merge origin/develop
Merge made by the 'recursive' strategy.
 b_moudle.java | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 b_moudle.java
 
$git log --oneline --graph --all
*   cf35c79 (HEAD -> develop) Merge remote-tracking branch 'origin/develop' into develop
|\
| * 3941cf7 (origin/develop) Modify mobule form B developer
| * 9a1b5d2 Add moudle from B developer
* | 2f91a08 (module) Modify module from A developer
* | 7b6a0b0 Add module from A developer
|/
* 971d61d Add file a.develop.txt from A developer
*   9d6b6a6 (tag: v0.1, origin/stable, stable) Merge remote-tracking branch 'origin/stable' into stable
|\
| * ff44a57 Developer B modify the file
* | 487be31 Add file a.txt from A developer
|/
* 8d5fed7 first commit

$ls
README.md  a.txt  a_develop.txt  a_module.java  b_moudle.java

$git push origin develop
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 12 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (7/7), 686 bytes | 686.00 KiB/s, done.
Total 7 (delta 3), reused 0 (delta 0)
remote: Resolving deltas: 100% (3/3), completed with 1 local object.
To https://github.com/donghyeok-dev/gitTest.git
   3941cf7..cf35c79  develop -> develop
   
$git log --oneline --graph --all
*   cf35c79 (HEAD -> develop, origin/develop) Merge remote-tracking branch 'origin/develop' into develop
|\
| * 3941cf7 Modify mobule form B developer
| * 9a1b5d2 Add moudle from B developer
* | 2f91a08 (module) Modify module from A developer
* | 7b6a0b0 Add module from A developer
|/
* 971d61d Add file a.develop.txt from A developer
*   9d6b6a6 (tag: v0.1, origin/stable, stable) Merge remote-tracking branch 'origin/stable' into stable
|\
| * ff44a57 Developer B modify the file
* | 487be31 Add file a.txt from A developer
|/
* 8d5fed7 first commit

image-20210307012936301

develop branch의 현재 내용을 relase branch로 옮겨서 테스트 후 stable branch로 merge 합니다.

$git push origin release

$git log --oneline --graph --all
* 783dd60 (HEAD -> release, tag: v1.0.0-release, origin/release) Fix bug in a_module.java
*   cf35c79 (origin/develop, develop) Merge remote-tracking branch 'origin/develop' into develop
|\
| * 3941cf7 Modify mobule form B developer
| * 9a1b5d2 Add moudle from B developer
* | 2f91a08 (module) Modify module from A developer
* | 7b6a0b0 Add module from A developer
|/
* 971d61d Add file a.develop.txt from A developer
*   9d6b6a6 (tag: v0.1, origin/stable, stable) Merge remote-tracking branch 'origin/stable' into stable
|\
| * ff44a57 Developer B modify the file
* | 487be31 Add file a.txt from A developer
|/
* 8d5fed7 first commit

$git checkout stable
$git merge release
Updating 9d6b6a6..783dd60
Fast-forward
 a_develop.txt | 0
 a_module.java | 2 ++
 b_moudle.java | 1 +
 3 files changed, 3 insertions(+)
 create mode 100644 a_develop.txt
 create mode 100644 a_module.java
 create mode 100644 b_moudle.java

$git tag -a v1.0.0 -m "v1.0.0 version" 783dd60
$git push origin v1.0.0
$git push origin stable
* 783dd60 (HEAD -> stable, tag: v1.0.0-release, tag: v1.0.0, origin/stable, origin/release, release) Fix bug in a_module.java
*   cf35c79 (origin/develop, develop) Merge remote-tracking branch 'origin/develop' into develop
|\
| * 3941cf7 Modify mobule form B developer
| * 9a1b5d2 Add moudle from B developer
* | 2f91a08 (module) Modify module from A developer
* | 7b6a0b0 Add module from A developer
|/
* 971d61d Add file a.develop.txt from A developer
*   9d6b6a6 (tag: v0.1) Merge remote-tracking branch 'origin/stable' into stable
|\
| * ff44a57 Developer B modify the file
* | 487be31 Add file a.txt from A developer
|/
* 8d5fed7 first commit

![image-20210307015435753](https://cdn.jsdelivr.net/gh/donghyeok-dev/donghyeok-dev.github.io@master/assets/images/posts/image-20210307015435753.png

댓글남기기