ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Git] 특정 브랜치에서 main(master) 브랜치 merge(push) 막기
    저장소/git 2022. 7. 3. 15:45

    특정 브랜치에서 main 또는 master 브랜치에 merge 또는 push되는 것을 막는 방법을 설명합니다.

    • 아래 설명에서는 develop → main이 되지 않도록 예시를 듭니다.

    github branch protection rule 추가

    repository에서 Settings → Branches에 들어간 다음 Branch protection rules 에서 Add rule 을 클릭합니다.

    그 다음, Branch name pattern에 아래의 룰을 적용할 패턴을 추가하고 (여기서는 main 브랜치명을 그대로 적습니다.) Require a pull request before merging 을 체크합니다.

    체크하면 하위 리스트가 나오게 되는데 Require approvals 를 체크해제 하지 않고 몇명의 approval을 받아야만 PR을 머지할 수 있는지 세팅해 줍니다.

    여기서는 1명 이상이 approval을 해야만 머지가 되도록 설정했습니다.

    이 방법의 문제는 해당 repository의 administrator 라면 강제로 PR을 할 수 있다는 점입니다.

    github action 추가

    다음 방법으로는 gitgub action을 추가하여 PR이 등록되었을 때, 알림을 주는 방식입니다. 해당 방식 또한 실패했다고 해서 PR을 막을 순 없습니다. (강제성X)

    main 브랜치에서 아래의 코드를 .github/workflows/{파일명}.yml 에 추가합니다.

    name: Restrict merge from develop to main
    
    on:
      pull_request:
        branches: [ main ]
    
    jobs:
      check-compare-branch:
        runs-on: ubuntu-latest
        steps:
          - name: Check compare branch
            run: |
              # PR 소스 브랜치가 develop일 경우, 실패 그 외의 브랜치면 통과
              if [[ "$GITHUB_HEAD_REF" == 'develop' ]]; then exit 1; else exit 0; fi;

    서버에 반영을 한 다음, 해당 github repository의 Actions를 들어가면 name으로 지정한 workflow가 생성된 것을 볼 수 있습니다.

    develop 브랜치에서 main으로 PR을 등록하면 테스팅이 실패한 것을 볼 수 있습니다.

    위에서 설명한 것 처럼 실패해도 Merge pull request 버튼을 눌러 main에 머지를 할 수 있습니다.

    로컬 git hook 추가하기

    githook을 사용하여 main(master) 브랜치에 특정 브랜치가 merge가 되는 상황을 방지할 수 있습니다.

    1. 적용할 repository의 디렉토리에서 .git/hooks/ 로 이동한 다음 prepare-commit-msg 파일을 생성합니다.
    2. 아래의 코드를 추가하고 저장한 다음, 편집기를 종료합니다.
    #!/usr/bin/env ruby
    
    # This git hook will prevent merging specific branches into master
    # Put this file in your local repo, in the .git/hooks folder
    # and make sure it is executable.
    # The name of the file *must* be "prepare-commit-msg" for Git to pick it up.
    
    FORBIDDEN_BRANCHES = ["develop"]
    
    def merge?
      ARGV[1] == "merge"
    end
    
    def into_master?
      current_branch = `git branch | grep '*' | sed 's/* //'`.chop
      current_branch == "master"
    end
    
    def merge_msg
      @msg ||= `cat .git/MERGE_MSG`
    end
    
    def from_branch
      @from_branch = merge_msg.match(/Merge branch '(.*?)'/)[1]
    end
    
    def from_forbidden_branch?
      FORBIDDEN_BRANCHES.include?(from_branch)
    end
    
    if merge? && into_master? && from_forbidden_branch?
      out = `git reset --merge`
      puts
      puts " STOP THE PRESSES!"
      puts " You are trying to merge #{from_branch} into the *master* branch."
      puts " Surely you don't mean that?"
      puts
      puts " run the following command now to discard your working tree changes:"
      puts
      puts " git reset --merge"
      puts
      exit 1
    end
    1. 해당 파일의 권한을 다음과 같이 설정합니다. chmod +x .git/hooks/prepare-commit-msg
    2. merge option을 수정했다면 다음의 명령어를 실행합니다. git config branch.master.mergeoptions "--no-ff"

    위의 단계를 전부 적용했다면 develop 브랜치를 master 브랜치에 머지를 하려고 할 때, 실패가 발생하게 됩니다. 실패가 됐다면 여전히 merge 상태이므로 git reset --merge 를 입력하여 되돌려줘야 합니다.

    해당 방법의 문제는 로컬에서만 막는 것이므로 github에서는 여전히 강제로 PR 후 머지가 가능합니다.

    결론

    github을 사용하고 관리자 권한이 있다면 사실상 완벽하게 막는 방법은 없다

    레퍼런스

    https://github.community/t/improvement-suggestion-settings-branch-pattern-that-can-only-merge-to-target-branch-pattern/171870

    https://bl.ocks.org/slattery/5eea0d6ca64687ecba6b

    댓글