[git] 체리노도 따라할 수 있는 협업을 위한 git

YumeIroVillain·2024년 3월 10일
0

개발노트

목록 보기
28/30
  • git 강의안

    • 시간: 약 20분 + QnA 10분
    • 전달요소: git 개념설명 + Paradigm + 실용 심화명령어 CheatSheet
    • Why - 왜 해당 세미나가 필요한가?

      • 현재 여러 인원이서, 동일 branch에서 작업을 하고 있음.
      • compile되지 않는 코드를 로컬에 보유한 상태로, 잠깐 branch를 이동해야하거나, git pull을 하여 HEAD를 fetch한 후 이동해야할 때, 난감했던 경험이 있었을 것임.
        • Naive한 방법으로는, compile 되는 버전까지 작성한 뒤, push 하고 pull하는 방법이 있겠지만, 타인의 코드를 잠깐 보기 위한 pull을 위해 push를 하는 것은 비효율임.
        • 또는, 여러명이서 master 브랜치에서 작업을 할 때, 누가 commit을 잘못한다면 이것을 어떻게 처리하는지에 대한 개념이 정립되지 않았다면, Naive한 방법으로는 push를 다시 한번 더 하는 방법이 있겠지만 이것은 모든 commit object가 compilable 해야한다는 Rule에 어긋남.
        • 사실상 현재 우리는 Branching을 거의 사용하지 않고 있음.
        • etc..
      • 따라서, git 개념을 잡고, cheatSheet를 공유하고, 협업방식을 제안하고자 한다.
    • How - 개념설명

      • 기본개념

        • git != GitHub
          • git: 레포지토리 - 프로젝트와 관련된 파일뭉치
          • GitHub: 리모트 - git의 repository를 보관해주는 remote 저장소
            • GitHub말고 여러 서비스가 있음. 직접 만들수도 있고, GitLab, Bitbucket ... 등등이 있다.
        • CheatSheet

          • # file을 Staged 상태로 변경
            git add <filename>
          • # file을 Commit 상태로 변경
            git commit -m "message"
            # file을 변경사항을 확인하고, Commit Message를 적은 뒤 Commit
            git commit -v
          • # remote repository 로 push
            git push
            # origin이라는 remote repository에 master를 생성하고, local repository의 master의 내용을 remote repository의 master 브랜치에 보내라.
            git push origin master
            # --set-upstream 의 줄임말. local의 master 브랜치가 remote의 master를 항상 추적하도록 설정. 이 이후로, remote master 브랜치는 local master 브랜치의 upstream branch가 된다.
            git push -u origin master
            # --set-upstream이 안된 상태에서 push하기. 첫번째 master는 src의 master(로컬), 두번째 master는 dst의 master(리모트)를 의미함.
            git push origin master:master
          • # remote repository의 가장 최근 상태를 local로 가져온 후, 나의 local branch를 가장 최신코드와 합친다.
            # 아래 두 명령어의 순차적 실행과 완전히 동일하다(fetch와 merge는 나중에 설명).
            # git fetch
            # git merge
            git pull
          • # 현재 git의 상태를 알려준다.
            git status
          • # !!!주의!!! local 코드를 변경시킴.
            # add의 반대버전. restore는 commit object의 file 상태로 local file을 원상복귀한다.
            git restore <filename>
          • # 로컬과, Staged(add된 버전)를 비교
            git diff
            # Staged(add된 버전)과, CommitObject를 비교
            git diff --cached
          • # Commit Hash마다 Commit Message와 년월일 확인
            git log
      • 중급개념 - Branch란

        • 사례001: 뒤늦게 깨달은 버그

          • 갑: A.py을 first commit - hash 0001
          • 갑: A.py에 새로운 a 기능 추가하고 commit - hash 0002
          • 갑: B.py을 first commit - hash 0003
          • 갑: B.py의 새로운 기능 b를 추가하던 도중..
          • 뒤늦게, A.py의 hash 0002 버전에서 b 기능 관련하여 버그가 있는걸 발견한 상황.
          • 해결안
            • if, Branch 사용법을 잘 모른다.
              • B.py의 d기능을 마저 완성시키고 master에 커밋 - hash 0004
                logseq.order-list-type:: number
              • A.py의 b기능을 디버깅하고 master에 커밋 - hash 0005
                logseq.order-list-type:: number
              • 문제점
                • 이럴거면 git 쓰는 의미가 없다. 오히려 불편하기만 해질 뿐.
                • Git을 쓰는 목적: Code 히스토리를 저장/관리하기 위해.
                  • 이렇게 단순히 쌓아나간다면, 히스토리 관리가 무의미하다.
                  • feature a, feature b를 분리하여 history를 관리해야, 추후에도 더 명확하게 관리가 된다.
            • else if, Branch 사용법을 안다.
              • 갑: A.py을 first commit - hash 0001
              • 갑: A.py에 새로운 a 기능 추가하고 commit - hash 0002
              • 갑: git branch feat2
              • 갑: git checkout feat2 -> HEAD가 master에서 feat2로 바뀜.
              • 갑: feat2 브랜치에서, b기능 추가 - hash 0003
              • 갑: git checkout을 통해, hash 0002로 돌아가 디버그 후 commit - hash 0004
              • 갑: feat2 의 hash 0002와, master의 hash 0004를 merge
        • 협업을 위한 명령어 정리

          • CheatSheet

            • # branch 생성
              git branch <branch_name>
            • # 현재 HEAD를 <branch_name> 커밋위치로 옮기기
              git switch <branch_name>
              # 동일
              git checkout <branch_name>
            • # <branch_name> 이름의 branch를 현재 내 branch에 합치기
              git merge <branch_name>
            • # file_name의 file을 ____의 버전으로 변경
              git checkout <file_names>
              # dir_name 이하의 file들 모두를 ____의 버전으로 변경
              git checkout <dir_name>
            • # 만약 강제 옵션을 넣는다면, local 변경될 수 있음.
              # 특정 commit 버전으로 돌아가기
              git checkout <hash_id>
            • # 브랜치 생성 + 그 브랜치로 이동
              # 아래 2개 명령어를 순차적으로 수행한 것과 같음.
              # git branch <branch_name>
              # git checkout <branch_name>
              git checkout -b <branch_name>
            • # git clone을 했다면, origin이 자동으로 등록되므로 쓸 일이 없다.
              # 특정 이유로 origin이 등록이 안되었다면, 수동으로 등록해줄 수 있다.
              git remote add origin https://github.com/John/gitTest
              # 다른 remote를 등록할 수도 있다.
              git remote add origin2 https://github.com/Peter/gitTest
              # remote 현황 파악
              git remote -v
            • # remote에 있는 <branch_name> 브랜치를 로컬에 가져오기
              git checkout -t origin/<branch_name>
        • Merge Conflict 해결: merge 하려는 두 브랜치 간에서, 동일 file에서 차이가 있을 때.

          • CheatSheet

            • # remote repository의 가장 최근상태를 내 local로 가져온다.
              # 다른 사람들이 어떤 부분을 수정했고 push했는지를 확인만 하고싶을 때 사용.
              # 내 local에는 영향을 미치지 않는다. 
              # merge말고 rebase를 쓰고싶을 때 쓰기도 한다.
              git fetch
            • # 
              git merge
      • 고급개념

        • cherry-pick - 특정 commit의 delta만 내 branch에 반영하겠다.

          • # git status를 성실히 안했거나
            # 코드 dependency 때문에 다른 사람의 코드 위에서 작업해야 하는 경우
            # ex) 다른 사람이 버그를 고쳐줘서 push해줬고, 내가 그 버그를 고친 상태에서 다음 무언가를 구현해야 할 때.
            git cherry-pick <hash_id>
            # 참고) hash_id는 7자리만 입력해도 됨.
        • reset - 취소하기

          • # add 취소하기
            # mixed reset: 전체 add 취소. add된 local file들은 Modified 된채로 그대로 있다.
            git reset HEAD
            # mixed reset: 특정 filename만 add 취소. add된 local file은 Modified 된채로 그대로 있다.
            git reset HEAD <filename>
            # !!!주의!!! 아래 명령어는 local file 변경가능성있음
            # hard reset: add가 취소되면서, add된 local file도 지워버리거나 이전버전으로 덮어씌워버린다.
            git reset HEAD <filename> --hard
          • # commit 취소하기
            # soft reset: commit은 취소되고, add된 단계로 돌아간다.
            git reset HEAD^ --soft
            # mixed reset: commit은 취소되고, commit된 내용물은 add 이전단계로 돌아간다(unstaged)
            # 직역: HEAD^ 상태로 돌아가겠다.
            git reset HEAD^
            # hard reset: commit은 취소되고, commit된 내용물을 전부 삭제함.
            git reset HEAD^ --hard
            • HEAD란?
              • ^는, 커밋 부모를 뜻함. ~1과 동일함.
              • ^^는 조부모를 뜻함. ~2와 동일함.
              • ^^^는 조조부모를 뜻함. ~3과 동일함.
        • stash - 임시저장하기

          • 사례002: 갑자기 걸려온 급한 디버그 요청

            • 갑: feature/login 브랜치에서 작업중이었다.
            • 을: 갑님이 commit하신 feature/logout 브랜치에 버그가 있으니, 오늘내로 고쳐주세요.
            • 갑: '아, 지금 login.py 덜 완성되어서 commit 할 수도 없고...그렇다고 작업중이던, add된 login.py 및 아직 add가 안된 Modified 파일(login_data.py)들을 날릴수는 없어.'
              • -> 브랜치가 cleanup되지 않아서, 브랜치변경이 불가한상태.
              • 방법 1 - stash

                # Last Commit 이후의 변경사항이 전부 stash 스택에 저장된다. 저장되는 파일은 add되지 않은 Modified 상태의 file도 포함한다.
                # 주의) Untracked 상태의 파일은 당연히 저장되지 않음(git에서 아예 인지하지 못한 파일이므로). => 다만, 이 file은 conflict낼 일도 없기에(git에서 아예 인지하지 못한 file이므로), 문제없음.
                git stash
                # 저장한 stash 목록을 확인할수있다.
                git stash list
                git checkout feature/logout
                작업완료후
                git checkout feature/login
                # 아래 두 명령어의 순차적 실행과 완전히 같다.
                # git stash apply
                # git stash drop
                git stash pop
              • 방법 99(비추천) - Ctrl C + Ctrl V로 수동백업

                cp login.py login(1).py
                cp login_data.py login_data(1).py
                ...(싹다 cp로 수동백업)
                git reset HARD --hard
                git checkout feature/logout
                작업완료후
                git checkout feature/login
                cp login(1).py login.py
                cp login_data(1).py login_data.py
                ...(싹다 cp로 수동복원)
            • # 새로 filename을 add한 뒤, Latest Commit을 "바꾸기(덮어쓰기)"
              git add <filename>
              git commit --amend
              
              # Latest Commit의 커밋메시지를 바꾸기
              git commit --amend -m "NewCommitMessage"
            • # merge 안쓰고 branch 합치는 법
              # 내가 feature1 브랜치에서 작업하고있고, master 브랜치의 Latest Commit 이후에 feature1 브랜치의 Commit들을 쌓아올리고 싶을 때.
              # 장점: 불필요한 merge commit을 생성하지 않을 수 있음.
              # 단점: 기존 feature 1의 Commit Hash가 0001이라고 쳤을 때, rebase 이후의 동일 Commit Object의 Hash는 새로운 값을 가지게 됨.
              git rebase master
            • # interactive 하게 rebase 하겠다.
              # Commit History의 변경/삭제를 하고싶을 때 사용.
              git rebase -i
            • # rebase 이후, 달라진 commit Hash 때문에 push 거부가 발생할 때.
              # !!!주의!!! remote의 Commit가 덮어씌워져서, 복구불가능하게 될 수 있음. History가 남지 않음.
              # remote branch의 Commit Hash를 덮어씌워버릴 수 있고, 이렇게되면 다른 모든 사람이 push할때 ERROR가 발생하게 됨. 따라서, 미리 말을 해주자.
              git push --force
            • # ~/.gitconfig
              [alias]
              ll = log -U -n 1
              co = checkout
              # 아래와 같다.
              # ~/.bashrc
              alias gb = "echo 'git branch' && git branch"
      • issue생성과 PR생성, PR리뷰까지

      • 대표적인 협업방식

        • GitFlow

          • 가장 유명한 전략.
          • 크게 5개의 브랜치를 운영하게 됨.
            • main
            • develop
            • feature
            • release
            • hotfix
          • 장점: 안정적인 버전관리
          • 단점: CI/CD쪽에서 안좋아함.
        • Github Flow

        • Trunk-based

          • main 브랜치만 쓴다.
          • 다만, feature가 필요하면, main에서 파생시킨 뒤 Merge하낟.
          • 장점
            • 소스코드가 한 곳에만 있으니까 편함. main에 있는게 전부임.
          • 단점
            • main이 release 브랜치를 거치지않고 User에게 바로 노출됨.
            • feature 또한 최소한의 UnitTest만 거친 뒤 main에 병합됨.
              • => 따라서, main의 무결성을 보장할 수 없음. feature 1과, feature 2 각각의 UnitTest는 통과하는데, feature 1 와 feature 2 합칠 때 발생하는 버그가 있다면 main에서 터지게 됨.
                • 물론, feature 2를 짜는 사람이 성실하게 매시간 git pull을 해서 항상 Latest 버전의 feature 1을 가져간다면 이론상 완벽함.
              • 그러므로, main에 merge request를 날린다면 admin이 매우 신중해야 하고, Developer가 직접 push하는 형태라면, push를 신중하게 해야 함.
              • 싫으면, main의 무결성을 포기하면 모든게 해결된다(어?).
        • Gitlab Flow

          • Trunk-based와 유사.
      • 기타 꿀팁

        • # WORKING_DIRECTORY/.git/config
          # ...
          [alias]
                  s = status -s
                  co = checkout
                  ci = commit
                  br = branch
                  l = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%C(bold blue)<%an>%Creset' --abbrev-commit  
        • # 현재 config들 확인가능.
          git config --list
    • What - 그 결과, 어떤 Working flow를 만들 것인가?

      • Commit은 완성된 코드만(적어도 compile 되는 코드만) 하자.
      • git push --force를 쓸 때는 반드시 모든 인원에게 푸시포스를 한다고 얘기하자.
      • git reset은 commit을 지우는 식으로 동작하고, git revert는 commit을 덮어씌우는 식으로 동작한다. 따라서, 공유브랜치(master)에서는 revert를 사용하자.
    • References

profile
HW SW 둘다 공부하는 혼종의 넋두리 블로그 / SKKU SSE 17 / SWM 11th

0개의 댓글