Tools/Git&GitHub 2020. 8. 2. 20:17

 

오늘 다루어볼 내용은 git의 cherry-pick(체리픽)이다. 보통 체리픽을 사용하는 이유는 다른 브랜치의 커밋의 일부만 가져올때 많이 사용한다. 간단하게 예제를 다루어보자.

 

> gb
* [1] feature/add-function
  [2] feature/update-function

 

위처럼 두개의 브랜치가 존재하고 각 브랜치의 커밋로그는 아래와 같다.

 

> feature/add-function
* 13e321a - (HEAD -> feature/add-function) add c.txt (9 minutes ago) <levi.yoon>
* f894b25 - add b.txt (9 minutes ago) <levi.yoon>
* 35402a1 - add a.txt (9 minutes ago) <levi.yoon>

> feature/update-function
* cb6a172 - (HEAD -> feature/update-function) add d.txt (7 minutes ago) <levi.yoon>
* d516ac8 - update a.txt (7 minutes ago) <levi.yoon>
* 13e321a - (feature/add-function) add c.txt (9 minutes ago) <levi.yoon>
* f894b25 - add b.txt (10 minutes ago) <levi.yoon>
* 35402a1 - add a.txt (10 minutes ago) <levi.yoon>

 

여기서 feature/update-function 브랜치에 있는 "d516ac8 - update a.txt" 이 커밋내용만 가져와 feature/add-function 브랜치에 넣고 싶을 때 cherry-pick을 이용한다.

 

> git checkout feature/add-function
> git cherry-pick d516ac8
> git log
* 09edc77 - (HEAD -> feature/add-function) update a.txt (46 seconds ago) <levi.yoon>
* 13e321a - add c.txt (12 minutes ago) <levi.yoon>
* f894b25 - add b.txt (12 minutes ago) <levi.yoon>
* 35402a1 - add a.txt (12 minutes ago) <levi.yoon>

 

지금 한 예제는 같은 레포지토리의 다른 브랜치에서 커밋을 가져왔지만, 다른 원격 레포지토리의 특정 브랜치에 있는 커밋내용도 내 레포지토리 브랜치에 넣을 수 있다.

 

posted by 여성게
:
Tools/Git&GitHub 2020. 6. 6. 15:40

 

오늘 다루어볼 내용은 평소에 조금 헷갈렸던 Git reset과 revert이다. 깃에서 되돌리기 위한 방법은 크게 2가지가 있다. 바로 reset과 revert이다. 그렇다면 둘의 차이점은 무엇일까?

 

  • reset : 시계를 마치 과거로 돌리는 듯한 행위
  • revert : 특정 사건을 없었던 일로 만드는 행위
git reset <--option> "돌아가고 싶은 커밋 hash"

먼저 reset을 알아보자. reset은 특정 커밋으로 돌아가는 행위다. 그말은 특정 커밋 이후의 커밋이력 모두 없어지게 되는 것이다.(물론 옵션마다 상태가 다르긴하다) reset에는 옵션이 3가지가 있다. hard, soft, mixed 각 옵션에 대한 설명은 아래와 같다.

 

  • hard : 돌아가려는 커밋 이후의 모든 내용을 다 지워버린다.
  • soft : 돌아가려는 커밋 이력으로 되돌아 갔고, 이후의 내용은 stage 상태로 남아있는다.(git add 상태) 즉, 커밋을 다시 할 수 있는 상태가 되는 것이다.
  • mixed : 돌아가려는 커밋 이력으로 되돌아 갔고, 이후 내용은 남아있지만, unstage된 상태로 남아있다.(git add 이전 상태, tracked file list)

이제 각 옵션에 대해 실습해보자. 우선 쓰레기 코드가 들어간 커밋이 마지막에 찍혀있는 것을 보자.

 

 

각 reset 옵션 적용 후 상태를 확인하자.

 

<hard>

 

이전 커밋이력으로 돌아갔다. 그리고 이전에 추가했던 trash_code.txt도 감쪽 같이 사라졌다.

 

 

<soft>

 

쓰레기코드가 추가된 커밋이 사라졌다. 하지만 Uncommitted changes 상태가 되었다.(git add)

 

 

<mixed>

 

이전 커밋으로 잘 돌아갔고, 파일은 untracked된 상태이다.

 

 

만약 현재 커밋으로부터 몇개 이전으로 커밋을 되돌리고 싶다면 아래 문법으로도 reset이 가능하다.

 

#현재 커밋 기준 6번째 전 커밋으로 되돌린다.
git reset HEAD~6

 

여기까지 reset에 대해 다루어보았다. 다음은 revert에 대해 다루어보자.

 

git revert "지우고 싶은 커밋 hash"

revert는 특정 커밋을 아예 날려버리는 행위이다. 하지만 reset과는 조금 다른 이력이 남는다. 바로 실습내용을 확인해보자.

 

 

나는 가장 마지막 커밋을 revert 할것이다. 과연 이력은 어떻게 남을 걸까?

 

 

reset과는 다르게 내가 특정 커밋을 revert했다라는 이력이 남아있다.(물론 trash_code.txt가 추가됬다는 것도 남아있다.) 이것이 reset과의 차이점이며, 또 하나는 reset은 특정 커밋 이후의 이력을 모두 지우지만, revert는 중간에 껴있는 특정 커밋 이력을 날려버릴 수 있다.

 

그렇다면 revert와 reset은 어떠한 상황에서 사용해야 할까?

보통 reset은 원격 레포지토리로 푸시하기 이전에만 사용한다. 만약 이미 원격 레포지토리에 푸시한 이후에 reset을 하고, push를 하면 아래와 같은 메시지가 보일 것이다. 그 이유는 로컬의 커밋이 원격 커밋보다 뒤에 있기 때문에.. 물론 git push --force 옵션을 사용하여 push할 수 있지만, 이미 다른 사람들이 작업한 커밋이 푸시되어있다면? 윽 생각도 하기 싫다. 

 

 

reset은 혼자만 사용하는 브랜치일 때와 다른 사람들이 해당 브랜치를 pull한 적이 없을 때 정도만 사용가능할 듯하다. 그 이유는 로컬에서만 작업한 내용에서 실수로 다른 커밋을 찍었고 아직 push하기 이전에 reset을 한다면 커밋이력이 훨씬 깔금해지기 때문이다. 하지만 push 이후에는 왠만하면 revert 옵션을 사용하자.

 

push 이후에 revert를 사용하면 내가 어떠한 커밋에 대해 revert를 했는지가 커밋 로그에 남기때문에 훨씬 이력관리에 좋고, 다른 사람이 제가 이력을 지운 커밋이후에 작업을 했더라고, 그 커밋들을 건드리지 않기때문에 크게 문제 없다. 여기까지 git reset, revert에 대해 다루어보았다.

 

<참고>

 

개발바보들 1화 - git "Back to the Future"

  이 내용에 대한 자세한 기술적인 설명이 듣고 싶나요? 연속되는 다음글을 참조하세요    

www.devpools.kr

posted by 여성게
:
Tools/Git&GitHub 2020. 3. 27. 21:41

 

Git에서 한 브랜치에서 다른 브랜치로 합치는 방법은 두 가지다. 하나는 이전 포스팅에서 다룬 Merge이고 다른 하나는 Rebase이다. Rebase 상황을 이전 포스팅에서 다루어봤던 merge 상황이랑 비교해보자.

 

 

Git - fast-forward merge 란? fast-forward & 3-way Merge의 차이점

1. fast-forward merge 아래와 같은 상황이 있다고 가정하자. issue 처리를 위해 master 브랜치에서 브랜치를 따서 작업을 하고 있었다. 갑자기 픽스해서 빠르게 릴리즈해야할 버그 사항이 있어 hotfix 브랜치를..

coding-start.tistory.com

 

아래와 같은 상황이 있다.

 

 

이 상황에서 3-way-merge를 하면 그래프상태는 아래와 같은 상태가 된다.

 

#current branch master

> git merge issue-1

 

 

그런데 Rebase를 하면 어떻게 될까?

 

#current branch issue-1

> git rebase master
> git checkout master
> git merge issue-1

 

 

실제로 일어나는 일은 일단 두 브랜치가 나뉘기 전인 공통 커밋(C2)으로 이동하고 나서 그 커밋으로부터 지금 Checkout(issue-1)한 브랜치가 가르키는 커밋까지 diff를 차례로 만들어 어딘가에 임시로 저장해 놓고(새로운 커밋을 만든다.), Rebase할 브랜치(master) 커밋 뒤에 차례로 붙여버린다. 그리고 master에서 issue-1 브랜치를 머지하면 fast-forward merge가 된다.

 

결론적으로 보면 합친다는 관점에서 같고 최종 내용 또한 같다. 하지만 Rebase가 좀 더 깨끗한 히스토리를 만든다. Rebase한 브랜치의 Log를 살펴보면 히스토리가 위 그림과 같이 선형으로 되어 있다. 

 

하지만 Rebase를 사용할 때 주의사항이 있다. git rebase master를 해서 master branch 커밋에 issue-1 커밋을 차례로 붙여버렸는데, 이 커밋은 새로 생성된 커밋이다. Rebase전의 C3 != Rebase후의 C3 인 것이다. 그러면 이 상황이 어떻게 주의를 해야하는 상황이 되어버린 것일까? 그 상황은 바로 이미 리모트 저장소에 Push한 커밋을 Rebase하는 상황이다. Rebase는 새로운 커밋을 만들어 뒤에 차례로 붙여버린다고 이야기 했는데, 이미 Push한 커밋을 Rebase하고 Push하면 나말고 다른 팀원이 보고 있는 커밋이 내용은 같이만 다른 커밋이 되어버린다. 이런 경우에는 똑같은 커밋이 두개가 생겨버리는 것이다. 엄청 혼란스러운 상황이다. 이런 상황은 서로 모든 팀원이 공유가 된 상태에서 "git pull --rebase(git fetch & git rebase)" 로 문제를 미리 방지할 수 있다.

 

뭐가 더 좋다고 할 수는 없지만 상황에 따라 잘 사용해야한다. 

posted by 여성게
:
카테고리 없음 2020. 3. 27. 20:38

 

1. fast-forward merge

 

아래와 같은 상황이 있다고 가정하자.

 

  1. issue 처리를 위해 master 브랜치에서 브랜치를 따서 작업을 하고 있었다.
  2. 갑자기 픽스해서 빠르게 릴리즈해야할 버그 사항이 있어 hotfix 브랜치를 master 기준으로 따서 작업을 했다.

 

버그를 픽스하고 테스트를 통과해 이제 릴리스 해야하는 상황이다. 이제 master에 hotfix를 머지해야되는 상황이다. 그런데 hotfix는 master를 베이스로 하고 있는 브랜치, 즉 master의 upstream 브랜치이다. 이제 머지를 하면 fast-forward 머지가 되는 것인데, 이것은 단순히 master가 바라보고 있는 커밋을 앞으로 이동하는 것(hotfix 브랜치가 가르키는 커밋을 master가 바라본다.) 뿐이다.

 

2. 3-way-merge

 

그렇다면 3-way-merge는 무엇일까? 위 상황과 차이점이 있다면 현재 브랜치가 가르키는 커밋이 Merge할 브랜치의 조상이 아니라는 점이다. 이 경우에 Git은 각 브랜치이 가르키는 커밋과 두 개 커밋의 공통 조상 커밋을 사용하여 머지하는 3-way-merge를 하게 된다.

 

우선 3-way-merge가 발생하는 커밋 플로우를 보자.

 

 

master 브랜치가 가르키고 있는 C4 커밋이 현재 브랜치가 가르키고 있는 C5의 조상이 아니다. 이유는 이전에 hotfix 브랜치를 머지하여 fast-forward merge가 발생했기 때문이다. 이때 3-way-merge가 발생하면 최적의 공통 조상을 자동으로 찾아서 머지한다. 다른 버전 관리 시스템은 개발자가 직접 공통 조상을 찾아서 merge해야 하는데 이점에서 보면 Git은 merge가 아주 쉽다.

 

 

fast-forward와는 조금 다른 방식임을 직접 그림으로 보면 명확하다. 새로운 커밋이 생겼기 때문이다. 또한 fast-forward merge와는 다르게 충돌(conflict)이 날 수 있다. 그 상황은 처음 상황을 되살려서 hotfix가 수정한 파일과 issue-1 브랜치에서 수정한 파일이 같은 부분을 수행했을 때 발생한다. 이때는 Git은 충돌이 났기 때문에 자동으로 새 커밋을 생성하지 않는다. 충돌이 나서 unmerged된 파일을 수정하고 수동으로 커밋을 해야한다.

posted by 여성게
:
Tools/Git&GitHub 2020. 3. 26. 19:06

 

이번 포스팅은 굉장히 짧은 포스팅이 될것이다. 포스팅 내용은 ".gitignore" 파일이 먹지않을 때, 해결 방법이다. 분명 .gitignore에 파일 확장자 등을 추가하였는데 계속해서 changes에 나온다면 git의 캐시가 문제가 되는지 확인 해볼 필요가 있다. 아래 명령을 통해 캐시를 삭제해보자.

 

> git rm -r --cached
> git add .
> git commit -m "clean untracked files"

 

아마 캐시 되었었던 파일들이 쫙 deleted된걸로 나오고 일부 파일들은 untracked로 나올 것이다. 최종적으로 한번 커밋해주자 ! 

posted by 여성게
:
인프라/Jenkins 2020. 3. 21. 20:11

 

이전 포스팅에 이어 Jenkins&GitHub을 이용한 CI 실습을 다루어볼 것이다. 이번 포스팅에서는 본격적으로 실습을 진행하기 전에 아주 간단한 echo를 찍어주는 pipeline 하나를 만들어 볼 것이다. 사실 이것을 설명하는 이유는 멀티브랜치 파이프라인에 대해 조금 이해해보기 위함이다.

 

1. Multibranch Pipeline 생성

사실 우리가 개발할때, 버전관리 시스템에서 브랜치를 하나만 사용하여 개발하지 않는다. 각 회사마다의 브랜치 전략이 있겠지만 가장 대중적인 브랜치 전략은 아래와 같다.

 

  • Feature branch
  • Integration branch
  • Master branch(production branch)

이렇게 여러개의 브랜치를 가지고 개발을 진행하고 이러한 멀티 브랜치 전략에 대해 Jenkins를 이용하여 CI를 구현할때는 Multibranch Pipeline을 이용한다. 자세한 브랜치 전략은 구글링해서 참고하자 ㅎㅎ.

 

새로운 Item 메뉴를 클릭하면 다음과 같은 페이지가 보일 것이다.

 

 

item 이름을 넣고 Multibranch Pipeline을 생성해준다. 하나 설명하면 Multibranch Pipeline은 직접 스크립트를 Jenkins에서 작성하지 않고 소스코드가 있는 Github project root에 Jenkinsfile로 정의된다. Jenkinsfile은 쉽게 말하면 파이프라인 스크립트가 정의된 파일로 보면 된다.

 

Branch Sources 탭을 클릭하여 필요한 정보를 기입할 것이다. 지금은 자세한 설명보다는 파이프라인이 동작하는 것을 집중적으로 볼 것이기 때문에 정말 필요한 정보만 기입한다.

 

Repository HTTPS URL에 생성한 GitHub 레포지토리 주소를 기입한다. Discover branchesStrategyAll branches로 바꿔준다. Add 버튼을 클릭하고 Filter by name (with wildcards)를 추가한다. 그리고 Includemaster develop을 입력한다. 여기까지 작성하고 save해준다.

 

다음으로 간단하게 springboot project를 생성하고 해당 프로젝트 root에 Jenkinsfile을 생성해준다. 소스코드는 아래 깃헙 정보를 참고하자

 

 

yoonyeoseong/jenkins_sample

Contribute to yoonyeoseong/jenkins_sample development by creating an account on GitHub.

github.com

 

2. Jenkinsfile 작성

간단하게 Jenkinsfile을 작성해보자. 샘플은 위 깃헙에 있으니 내려받아도 무방하다.

 

pipeline {
    agent none

    stages {
        stage('example build') {
            steps {
                echo 'jenkins sample build ! ${AUTHOR}'
            }
        }
    }
    post {
        always {
            echo 'post process !'
        }
    }
}

 

여기까지 작성한 후 master branch를 기준으로 develop branch를 하나 딴 후에 간단히 수정 후 커밋&푸시 해보자 ! 우리는 이전 포스팅에서 "GitHub의 master와 develop 브랜치의 PR, push를 인식시킨다." 라는 설정을 넣었기 때문에 Jenkins pipeline이 돌기를 기대해야한다. 과연 파이프라인이 작동하였을까?

 

오?! 뭔가 돌았다?(필자는 테스트로 한번 더 돌려서 2개가 보인다.) #1을 클릭한 후에 정말 잘 돌았는지 Console Output을 보자 !

 

 

오호 우리가 정의한 파이프라인이 작동했다 ! 정말 신기방기하다. 정말 간단한 것이지만 여기까지 진행했다면 Jenkins&GitHub을 이용한 CI의 느낌을 어느정도 느꼈을 것이다. 그리고 조금더 감각있는 사람은 앞으로 어떠한 실습을 진행할지 예상도 될것이다. 여기까지 간단하게 멀티브랜치 파이프라인 실습을 마친다. 다음 포스팅부터는 본격적으로 실무적인 레벨의 실습을 다루어본다.

 

3. 지금까지 다룬것

 

  1. CentOS 환경에서 Docker로 Jenkins 설치
  2. Jenkins와 GitHub 연동
  3. develop branch로 수정한 후 커밋&푸시를 통해 간단한 멀티브랜치 파이프라인 동작확인
posted by 여성게
:
인프라/Jenkins 2020. 3. 21. 19:42

 

오늘 다루어볼 내용은 CI의 핵심 젠킨스(Jenkins)를 다루어볼 것이다. 사실 이번 포스팅을 시작으로 젠킨스(Jenkins) 관련 몇개의 포스팅이 더 있을 듯하다. 오늘은 간단히 CentOS환경에 docker로 Jenkins를 설치해볼 것이다. 바로 예제로 넘어간다.

 

필자는 미리 CentOS VM을 준비하였다.(사실 회사에서 장비를 맘껏 받아쓸 수 있어서 ㅎㅎ 실습용으로 생성했다.) 이번 포스팅은 CentOS가 준비되어 있다라는 가정이다.

 

1. CentOS에 Docker Engine 설치

첫번째로 Docker Engine을 설치해보자.

 

> sudo yum -y update
> sudo yum -y install docker docker-registry
> sudo systemctl start docker
> sudo docker ps
> sudo systemctl enable docker
> sudo systemctl status docker

 

위와 같은 순서로 도커엔진(docker engine)을 설치한다. 잘 설치가 되지 않거나, 혹은 다른 OS에서 실습을 진행중이라면 구글링해보자.

 

2. Docker Jenkins install

==========jenkins 공식이미지를 이용하여 jenkins 실행==========
> sudo docker run -d --name jenkins -p 8080:8080 -p 50000:50000 -v /home/deploy/jenkins_v:/var/jenkins_home jenkins/jenkins:lts

==========jenkins 설치 후 초기 비밀번호 조회==========
> sudo docker exec -it jenkins cat /var/jenkins_home/secrets/initialAdminPassword

 

도커를 이용하여 젠킨스를 띄웠고 초기 비밀번호까지 획득했다면 8080포트로 접속해보자.

 

 

위와 같이 초기 비밀번호를 입력하는 창이 나온다. 아까 받아놓은 초기 비밀번호를 입력한다.

 

 

아마 사내인프라인 경우 위와 같이 offline이라고 뜰것이다. 젠킨스는 다양한 플러그인 설치 때문에 꼭 외부와 통신할 필요가 있다. 사내에 존재하는 proxy server를 이용하여 proxy 설정을 하자 !

 

 

Install suggested plugins를 눌러 필요한 플러그인을 모두 설치해준다. 플러그인이 모두 설치되었다면 이제 접속을 위한 인증정보를 작성해야한다.

 

 

모든 정보를 기입한 후 로그인해서 젠킨스 대시보드에 접속해보자 !

 

 

필자가 미리 만들어 놓은 Job을 제외하고는 거의 비슷한 화면이 떴을 것이다. 여기까지 젠킨스 설치가 완료되었다. 도커의 위대함을 다시 한번 느끼게 되는 실습이다. 만약 도커가 아니었다면 젠킨스 설치에 더 복잡한 프로세스가 있었을 것이다. 

 

3. Jenkins&Github 연동

이 포스팅의 최대 목표는 Jenkins를 이용한 CI 구성이다. 대부분 버전관리시스템은 git을 많이 이용하기 때문에 Jenkins&Github 연동을 통해 Jenkins pipeline을 구성할 것이다. 여기서 주의할 점은 Github에서 webhook을 걸기 위해 꼭 Jenkins는 외부에서 접근가능한 형태의 Domain 혹은 IP를 가지고 있어야한다. 만약 여기까지 문제없다면 바로 다음 실습을 진행한다.

 

첫번째로 Github 계정 정보를 넣어준다.

 

Credintials > System > Global credentials (unrestricted) click > Add Credentials 까지 들어온다.

 

kind는 Username with password로 하고 Username, Password 등 정보를 Github 로그인정보와 동일하게 기입해준다. 다음으로 Jenkins 설정 > 시스템 설정 메뉴로 들어와서 Github tab으로 이동한다.

 

 

고급 탭을 누른다. 고급탭을 누르면 Additional actions 탭이 생겼을 것이고 Manage additional Github actions를 눌러 Convert login and password to token을 눌러 우리가 넣었던 Github 인증 정보를 선택한다.

 

 

그런 다음 Create token credentials를 눌러 토큰을 생성해준다.

 

 

이제 Github에 접속하여 Generate된 토큰을 확인하자.

 

Settings > Developer settings > Personal access tokens 메뉴로 들어가면 우리가 Jenkins에서 생성한 token 정보가 보일 것이다.

 

 

다시 Jenkins설정 > 시스템 설정 > GitHub 탭으로 돌아가서 GitHub Server 정보를 입력하자. Name은 편한이름을 넣으면 되고, API URLhttps://api.github.com을 입력하자. 여기서 주의할 것은 혹시나 사내에서 GitHub 엔터프라이즈 버전을 사용한다면 API URL을 자체적으로 가지고 있으니 그것을 입력한다. 마지막으로 Credentials는 방금 생성한 토큰정보로 입력해준다.

 

모든 입력이 끝났다면 Test connection 버튼을 눌러보자!

 

 

여기까지 완료되었다면 Jenkins&Github 연동이 완료되었다. 조금 긴 글이었기 때문에 여기서 한번 끊고 다음 포스팅에서 이어서 Jenkins와 Github을 이용한 CI 실습을 이어나가겠다.

posted by 여성게
:
Tools/Git&GitHub 2019. 10. 15. 17:34

아래 명령어는 특정 브랜치만 clone하는 방법이다.

 

git clone -b {branch_name} --single-branch {git_repository_host}

 

 

posted by 여성게
: