개요
- git push 전 git db를 최신화 시키고 이를 rebase 하여 local에서 conflict 처리를 하고자 한다.
- 이를 위해 해당 기능의 명령어를 shell script로 작성하여 해결하였다.
발단(형상관리 방법의 변화)
- 메인페이지의 빈번한 수정이 요청됨에 따라 배포 주기가 엄청 짧아졌다.
- 이에 대응하기 위해 형상관리 방법을 아래와 같이 간결하게 변경하였다.
- 기존 방식
- (팀장) 공용 remote github repo 생성
- (팀장) 실서버 배포용으로 <main> 브런치, 개발용 브런치로 <dev> 브런치 생성
- (개발자) remote github repo를 fork 뜸(앞으로 forked repo라 명명)
- (개발자) local에 forked repo를 클론하고 <dev> 브런치 생성
- (개발자) local <dev>에서 수정 후 commit하고 forked repo <dev>로 push
- (개발자) forked repo <dev>에서 변경사항을 remote github repo <dev>로 pull request
- (팀장) pull request 확인 후 merge 할지 결정
- 변경된 방식
- (팀장) 공용 remote github repo 생성
- (팀장) 실서버 배포용으로 main 브런치, 개발용 브런치로 dev 브런치 생성
- (개발자) local에 remote github repo를 클론하고 <dev> 브런치 생성
- (개발자) local <dev>에서 수정 후 commit하고 remote github repo <dev>로 push
- 기존 방식
- 발생 가능한 문제
- 여러 개발자가 하나의 repo에 직접 push하기 때문에 conflict이 날 가능성이 높다.
- 개발물 적용 전 (팀장)의 확인 과정이 없어 불완전한 코드가 merge 될 가능성이 존재한다.
이 부분은 실서버 반영전 테스트 서버에 우선 배포해 충분 테스트를 하여 어느 정도 해소한다.
- 발생 가능한 문제 1.의 해결
- push 후 conflict이 나면 github에서 어떤 것을 반영할지 정하고 수정해서 merge 해야한다.
- 이는 local에서 conflict을 처리하는 것보다 매우 불편함으로 되도록
git push 전 git db를 최신화 시키고 이를 rebase 하여 local에서
conflict 처리를 하고자 한다. - 이를 위한 shell script를 작성하여 더 편하게 push를 하도록하였다.
local에서 conflict 처리
- remote github repo의 최신 git db를 local git에 반영하고 현재 commit에 rebase
1 2 3 4
## git pull --rebase [remote repo nickname in local] [local branch name] # remote repo nickname in local을 여기선 'upstream' 이라고 가정 # 우리는 현재 dev 브런치만 사용한다고 가정 git pull --rebase upstream dev
- 이 과정에서 conflict이 발생할 수 있다.
- conflict이 발생하면 적절히 판단하여 조치 후 merge commit을 하면 된다.
- local의 지금까지 변경사항을 remote github repo에 push
1 2 3 4
## git push [remote repo nickname in local] [local branch name] # remote repo nickname in local을 여기선 'upstream' 이라고 가정 # 우리는 현재 dev 브런치만 사용한다고 가정 git push upstream dev
- local의 지금까지 변경사항을 forked repo에 push
1 2 3 4
## git push [forked repo nickname in local] [local branch name] # forked repo nickname in local을 여기선 'origin' 이라고 가정 # 우리는 현재 dev 브런치만 사용한다고 가정 git push origin dev
- 현재는 forked repo를 쓰지 않지만 그래도 최신화 해줌
- 1~3과정을 순차적으로 실행하되 하나라도 실패하면
그 다음 과정을 진행하지 않고 멈춰야한다.
Shell script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
#!/usr/bin/env bash TRUE=1 FALSE=0 function is_succeeded_git_command() { local message=$1 if [[ ${message} == *error* ]]; then return ${FALSE} fi return ${TRUE} } function sleep_with_msg() { local sleep_sec=$1 echo "+-- sleep ${sleep_sec} sec" sleep ${sleep_sec} } function pull_and_push() { echo "+-- pull --rebase from upstream dev" result=1 # Catch stderr of this command result=`git pull --rebase upstream dev 2>&1` sleep_with_msg 0.5 is_succeeded_git_command "${result}" is_succeeded=$? if [[ is_succeeded -eq ${FALSE} ]]; then echo "${result}" echo "+-- failed" return 9001 fi echo "echo result :: ${result}" # Catch stderr of this command echo "+-- push from local dev to upstream dev" result=`git push upstream dev 2>&1` sleep_with_msg 0.5 is_succeeded_git_command "${result}" is_succeeded=$? if [[ is_succeeded -eq ${FALSE} ]]; then echo "${result}" echo "+-- failed" return 9001 fi echo "echo result :: ${result}" # Catch stderr of this command echo "+-- push from local dev to origin dev" result=`git push origin dev 2>&1` sleep_with_msg 0.5 is_succeeded_git_command "${result}" is_succeeded=$? if [[ is_succeeded -eq ${FALSE} ]]; then echo "${result}" echo "+-- failed" return 9001 fi echo "echo result :: ${result}" echo "+-- done" } pull_and_push
주요 shell 문법
- 함수
- 선언
1 2 3
function 함수이름() { return 리턴값 }
- 실행
1 2 3
# 함수이름만 적으면 실행된다. # 인자는 공백으로 구분한다. 함수이름 인자1 인자2
- 받은 인자의 처리
1 2 3 4 5 6 7 8 9 10 11
# 받은 인자는 순서대로 $순서 변수에 담긴다. # ex. 함수이름 인자1 인자2이면 인자1은 $1, 인자2는 $2에 담긴다. # shell에서 그냥 변수 선언 및 할당하면 무조건 global이다. # 함수 안에서만 동작하는 변수를 쓰고 싶다면 local 명령어를 써야한다. function 함수이름() { local 인자1=$1 #인자1에 9가 담김 local 인자2=$2 #인자2에 a가 담김 return 리턴값 } 함수이름 9 "a"
- 함수 리턴값 받기
1 2 3 4 5 6 7
# $?라는 특수 변수는 마지막으로 실행된 명령어, 함수 등의 종료값을 받는다. function 함수이름() { return 9 } 함수이름 echo "$?" # 이 스크립트를 실행하면 9가 출력된다.
- 선언
is_succeeded_git_command 함수 설명
- 깃 명령어 실행결과 문자열을 받아 성공 여부를 체크하는 함수이다.
- bash shell에서는 boolean type으로 true, false을 사용하나
나의 경우 boolean type 테스트가 매우 헷갈려서 그냥 숫자로 할당하여 처리하였다.
shell boolean type - bash shell에서는 equal test도 여러개이다(shell equality operators).
- [[ ${a} = ${b} ]]는 POSIX shell 기반으로 문자열 변수 동일여부 체크 시 사용한다.
이 방법은 정규 표현식 처리가 불가능하다. - [[ ${a} == ${b} ]]는 bash shell 기반으로 문자열 변수 동일여부 체크 시 사용한다.
이 방법은 정규 표현식 처리가 가능하다. - [[ ${a} -eq ${b} ]]는 bash shell 기반으로 정수형 변수 동일여부 체크 시 사용한다.
- [[ ${a} = ${b} ]]는 POSIX shell 기반으로 문자열 변수 동일여부 체크 시 사용한다.
- git 명령어 실패 시 에러메세지 앞에는 error라는 문구가 항상 포함됨으로
[[ ${message} == *error* ]] 테스트를 통해 git 명령어 에러 여부를 판단한다
(표준에러와 표준출력이 섞여서 있어서 좀 모호하긴 함 ^^;;)
pull_and_push 함수 설명
- 주요 로직이 포함된 함수이다.
- git pull –rebase upstream dev 2>&1
- 2>&1 이해하기를 먼저 읽으면 좋다.
- 2는 파일디스트럭터 중 하나로 표준에러를 의미한다.
- 1은 파일디스트럭터 중 하나로 표준출력을 의미한다.
- 2>&1을 쓰면 표준에러를 표준출력으로 보내겠다는 뜻이다.
- 2>&1이 없으면 에러가 표준출력으로 변환되지 않기 때문에 ‘|’ 등의 문법을 쓸 수 없다.
- 명령어 표준출력을 변수에 할당
- 변수이름=`명령`
- 따라서 result=`git pull –rebase upstream dev 2>&1`는
git pull –rebase 명령 실행 결과(표준출력, 표준에러 포함)가 result 변수에 저장된다.