Skip to content

09. git best practice

Dongwook Lee edited this page Jun 3, 2013 · 3 revisions

Git Best Practice

Branch-per-Freature

  • 주요 원칙
    • feature는 가능하면 atomic하게(OCP를 지켜서), 작게 작성한다.
      • feature 브랜치를 ISSUE-023 형태로 이슈 관리자의 ticket과 흔히 연결시킨다.
      • dev master 등에 직접 커밋하지 않는다.
        • git flow에서는 가능하긴하다.
    • integration
      • 최대한 자주 실행

      • git-show-branch

        • 브랜치가 어디서 왔는가? 현재 브랜치의 기원 추적에 편리

            ! [git] /Users/nephilim/.dotfiles/git/bin 정리
             * [master] Merge branch 'shell': md2docx added
              ! [shell] shell: md2docx added
               ! [vim-plugin] vim: rnu(relative number unit) 제거
            ----
             -   [master] Merge branch 'shell': md2docx added
             *+  [shell] shell: md2docx added
             *+  [shell^] taskit: help message typo fixed
             *   [master^] zsh: JAVA_HOME libexec/javahome 을 이용하여 경로 설정
             *+  [shell~2] taskit: weekday(mon, tue, ...) based registration added
             *+  [shell~3] choojerk: push notification (via parse.io) added
             *+  [shell~4] choojerk: web crawling script for Google I/O
             *+  [shell~5] taskit: task list escape added
             *+  [shell~6] taskit: tast select & done menu added
             *+  [shell~7] taskit: tasklist selection&store menu
             *+  [shell~8] google tasks api를 이용한 task adder
             *+  [shell~9] dev command 수정
            +*   [git] /Users/nephilim/.dotfiles/git/bin 정리
            +*+  [shell~10] git: git-track-branch 추가
            ---  [shell~11] Merge branch 'vim-plugin'
            +*++ [vim-plugin] vim: rnu(relative number unit) 제거
          
        • shell branch에서 실행 결과

          ! [git] /Users/nephilim/.dotfiles/git/bin 정리 ! [master] Merge branch 'shell': md2docx added * [shell] shell: md2docx added ! [vim-plugin] vim: rnu(relative number unit) 제거

          • [master] Merge branch 'shell': md2docx added +* [shell] shell: md2docx added +* [shell^] taskit: help message typo fixed
          • [master^] zsh: JAVA_HOME libexec/javahome 을 이용하여 경로 설정 +* [shell2] taskit: weekday(mon, tue, ...) based registration added +* [shell3] choojerk: push notification (via parse.io) added +* [shell4] choojerk: web crawling script for Google I/O +* [shell5] taskit: task list escape added +* [shell6] taskit: tast select & done menu added +* [shell7] taskit: tasklist selection&store menu +* [shell8] google tasks api를 이용한 task adder +* [shell9] dev command 수정 ++ [git] /Users/nephilim/.dotfiles/git/bin 정리 ++* [shell10] git: git-track-branch 추가 --- [shell11] Merge branch 'vim-plugin' ++*+ [vim-plugin] vim: rnu(relative number unit) 제거
      • --- 이전 라인(header)

        • 각 브랜치의 최근 commit 요약
        • *은 HEAD를 의미, 나머지는 !
      • --- 이후 라인

        • 각 브랜치 별 들여쓰기
        • 각 컬럼에는 해당 브랜치가 도달 가능한지 여부가 표시된다.
        • - merge branch
        • + branch (other than head-branch)
        • * current branch

no-ff

  • 두 개의 브랜치를 머지 후
    • 하나를 취소한다
    • no-ff가 아니면 주요 브랜치에 들어와 있어서 제거가 번거롭다
      • 그렇지 않다. feature 헤드가 남아있다면 큰 차이가 있지는 않다

          * e294960 - (HEAD, master, feature-03) feature-03:01 added (36 minutes ago)
          | * ada416f - (feature-01) feature-01:03 added (46 minutes ago)
          | *   a54c138 - Merge branch 'master' into feature-01 (47 minutes ago)
          | |\
          | |/
          |/|
          * |   247ab63 - Merge branch 'feature-01' (50 minutes ago)
        
      • 위 master 브랜치에서 reset --hard HEAD^ 해도 feature-03이 남아있다.

back-merge

  • back-merge를 하지않는다.

    • feature가 충분히 독립적이지 않다는 smell
    • cherry-pick을 고려해본다
    • feature
  • 사례

    • back-merge한 이력이 있는 feature-01을 제거하려면?

        *   27ad9e1 - (HEAD, master) Merge branch 'feature-01' (10 minutes ago)
        |\
        | * ada416f - (feature-01) feature-01:03 added (21 minutes ago)
        | *   a54c138 - Merge branch 'master' into feature-01 (22 minutes ago)
        | |\
        * | | e294960 - (feature-03) feature-03:01 added (11 minutes ago)
        | |/
        |/|
        * |   247ab63 - Merge branch 'feature-01' (25 minutes ago)
        |\ \
        | |/
        | * 08f01db - feature-01:02 added (46 minutes ago)
        | * 40577b5 - feature-01:01 added (47 minutes ago)
        * |   1f3d63c - Merge branch 'feature-02' (35 minutes ago)
        |\ \
        | |/
        |/|
        | * 9140660 - (feature-02) feature-02:01 added (36 minutes ago)
        |/
        * 6f026d1 - initial commit (53 minutes ago)
      
    • merge 취소에 대한 주요 법칙(03의 재확인)

      • 누구가에게 merge 당한 commit object는 쉽게 취소가 되지 않는다.
        • 상대의 내용에 영향을 주었기 때문에 상대 또한 작업을 해야한다.
      • 누군가를 merge한 commit object는 취소할 수 있다.
        • 상대의 내용에 영향을 준 건 아니기 때문에 merge commit을 취소하면 된다.
    • 해보니

      • back-merge후 feature의 내용에 집중하지 않고 master브랜치의 내용을 수정하여 feature에 유지한다면 확실히 제거가 안될 것임
      • 원격에 피해를 줄까?
        • rebase 작업은 back-merge를 하지 않아도 결국 유사
        • TODO: revert로 merge 취소 작업 시도
  • share

    • integration 시 conflict는 너무나도 자연스러운 현상(a fact of life)
    • fetch, pull, push를 hooking하여
    • rerere
      • reuse recorded resolution
      • .git/rr-cache에 저장됨
      • 충돌 시 처리한 (원본) 내용을 기록함

git flow와의 차이

  • git flow의 경우
    1. dev branch에 커밋이 가능함
    2. feature로 back-merge를 허락함

DAG

  • directed acyclic graph

practice

back-merge

  • cherry-pick
    • A reasonable middle-ground is cherry-picking.
    • 이유:
      • 다른 feature가 back-merge시 혼합되어 유입될 수 있다.
      • 유입 후 수정까지 되었다면 더욱 최악

our own process

  1. feature는 독립적으로, dev를 진행 중인 feature에 반영하지 않는다. 특히, back-merge는 하지 않는다. 굳이 필요하다면 dev의 원하는 commit을 cherry-pick 한다. 도식으로 나타내면 다음과 같다. git merge --no-ff 사용에 유의하자.

     d-------------d(dev)
      \           / \    
       f----f----f   f--f(feature)
    
     d-------------d(dev)
      \           /  
       f----f----f---f--f(feature)
    

    참고로 git-flow 모델에서는 다음을 허용한다. 하지만 위의 형태로 유지되는 것이 좋다.

     d---d1---d2---d(dev)
      \            / \ 
       f-----f----f   f--f(feature)
    

    back-merge가 허용되는 경우 다음과 같은 tree가 된다. 이런 식으로 feature가 관리되면, 향후 특정 feature를 제거할 경우 작업이 매우 까다로워 진다. back-merge 후 feature에 dev의 내용이 반영되므로 dev에서 제거를 할 수가 없다.

     d---d1---d2---d(dev)
      \            / \ 
       f-----f----f---f---f(feature)
    

    다른 이들이 feature를 작업하고 있지 않다면 다음과 같은 형태로 rebase도 가능하겠지만, 권고하지 않는다.

        d--d--d--d(1.6, master)
            \
             f--f--f (feature)
    
  2. feature 브랜치 명

    • 관련 issue/ticket이 있다면 해당 id를 [id] commit message의 형태로 기술
    • 공백이 없음(- 으로 대체)
  3. commit message

dangling object

  • 개념

    • dangling blob과 dangling commit
  • 실습: check dangling object

    • TODO: 예제 git commit --amend후 unreachable object를 확인하면 이전 commit 확인 가능

    • TODO: 브랜치 삭제

        * 9e820d4 - (HEAD, master) sample markdown document added (4 seconds ago)
        | * d9fe5bf - (develop) chapter-06 early translate: done (15 minutes ago)
        | * 984957a - sample formmated-markdown document added (71 minutes ago)
        |/
        * 878d836 - .git ignore added (73 minutes ago)
        * 311f263 - (origin/master, origin/HEAD) Initial commit (2 weeks ago)
      
  • git fsck

    • Verifies the connectivity and validity of the objects in the database
    • 그냥 fsck 하면 지워진 브랜치의 commit object가 나오지 않는다.
      • --no-reflogs 내용 참고
    • --no-reflogs
      • reflog에서만 도달할 수 있는 것은 도달가능이라 고려하지 않는다는 의미
        • TODO: 예제 git commit --amend후 unreachable object를 확인하면 이전 commit 확인 가능
    • --unreachable
      • reference 노드(reflog포함)에서 도달할 수 없음을 의미
      • unreachable object의 확인
        • --no-reflogs와 함께 사용하면 사라진 브랜치를 확인할 수 있다

            > git fsck --unreachable --no-reflogs
            Checking object directories: 100% (256/256), done.
            unreachable blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
            unreachable commit 984957a17d27b0984f0b390c8a50f639698f4d7f
            unreachable commit d9fe5bf153fb37e14698c76a83b8c2e93ce2f1cf
          
    • --dangling
      • 만들어지기는 했으나, 직접 사용된(참조된) 적이 없는 object

          > git fsck --dangling --no-reflogs
          Checking object directories: 100% (256/256), done.
          dangling blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
          dangling commit d9fe5bf153fb37e14698c76a83b8c2e93ce2f1cf
        
      • 984957a17d27b0984f0b390c8a50f639698f4d7f의 경우 자식이 있으므로 dangling에서 제외된다

    • --full
      • .git/object 뿐만 아니라 packed object, GIT_ALTERNATE_OBJECT_DIRECTORIES를 모두 검색함
  • git gc

    • housekeeping tasks
      • compressing file revisions
      • removing unreachable objects
        • gc 대상은 dangling object가 아님
    • --aggresive
      • aggresively compress
      • 디스크 공간에 여유가 있어도 압축을 수행
    • --auto
      • gc.auto 개수를 넘는 loose object는 repack
    • git gc --prune=now
      • now 이전의 loose object 삭제
        • defalut: 2주로 설정되어 있음
      • --no-prune: 압축만
    • config
      • gc.pruneexpire
      • gc.reflogexpire
      • gc.reflogexpireunreachable
    • 다른 git command에서 활용함
      • gc.auto 설정
        • git config --global gc.auto 0
  • git prune

    • remove unreachable objects
  • git reflog

    • git reflog expire --expire=now --all
  • exampleA

      git reflog expire --expire=now --all 
      # will now report unreachable 
      git reflog
      # nothing to print out 
      git fsck --unreachable 
      # will now actually delete objects 
      git prune -v 
      # gives "bad object ..." 
      git show $dead_commit 
    

notes