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 여성게
:
Tools/Git&GitHub 2020. 3. 27. 20:12

 

 

이번 포스팅은 내용설명은 거의 없이 진행할 것이다. 포스팅 내용은 Git에서 자주 사용되거나 유용한 Git 명령들이다.

 

  • git commit --amend : 완료된 커밋을 수정하고 싶을때나, 변경된 내용을 이미 커밋된 내용에 다시 포함시키고 싶을 때 사용하는 명령이다. "A"라는 커밋을 찍었는데, push 전에 내용이 변경되어 staging에 변경사항이 있는데, 이 변경된 staging 내용을 "A"라는 커밋에 포함시키고 싶을 때 사용한다. 즉, staging 변경사항이 없으면 "A"라는 커밋 내용과 동일하고 커밋메시지만 수정한다.
  • git reset HEAD <fileName> : 이미 staging에 올라간 파일을 unstaging으로 바꾸고 싶을때 사용한다. 이 말은 git add한 파일을 되돌리는 것이다. fileName을 넣으면 특정 파일만, 그렇지 않으면 staging에 올라간 모든 파일을 unstaging으로 만든다.
  • git checkout -- <fileName> : 변경된 unstaging 파일중에 최근 커밋 내용상태로 돌아가고 싶을때 사용하는 명령이다. 이 명령은 unstaging 일 경우에만 유효한 명령이다.
  • git fetch & git pull의 차이점 : git fetch는 리모트에 있는 저장소의 데이터를 모두 가져온다. 하지만 로컬 브랜치에 자동으로 머지하지는 않는다. git pull은 리모트 저장소의 모든 데이터를 가져옴과 동시에 현재 머물고 있는 브랜치에 머지까지 자동으로 한다.
  • git tag -l 'v1.8.5*' : v1.8.5로 시작하는 모든 태그를 보여준다.
  • git tag -a 0.1.0 -m 'tag message' : git에는 Lightweight & Annotated 태그 두 가지가 존재하는데, Lightweight 태그는 브랜치와 비슷한데 브랜치처럼 가리키는 지점을 최신 커밋으로 이동시키지 않는다. 단순히 특정 커밋에 대한 포인터일 뿐이다. 한편 Annotated 태그는 Git 데이터베이스에 태그를 만든 사람의 이름, 이메일 태그를 만든 날짜, 그리고 태그 메시지도 저장한다. 해당 명령은 Annotated 태그를 만드는 명령이다("-a 옵션을 붙임으로써")
  • git tag 0.1.0 : Lightweight 태그를 만들어준다. 기본적으로 파일에 커밋 체크섬을 저장하는 것뿐이다.
  • git push <remote명> [태그이름] & git push <remote명> --tags : git push 명령은 자동으로 리모트 서버에 태그를 전송하지 않는다. 태그를 만들었으면 서버에 별도로 push해야한다. 이 명령은 파일의 변경사항에 대한 푸시가 아니고 태그에 대한 푸시이다.
  • git config --global alias.unstageall 'reset HEAD' : 자주 쓰는 명령어를 alias 지정가능하다. git unstageall를 입력하면 git reset HEAD와 동일한 명령어 역할을 하게 된다.
  • git branch --merged : 현재 checkout한 브랜치를 기준으로 merge된 브랜치인지 보여준다. (현재 브랜치가 master이면 master로 이미 merge한 브랜치를 보여준다.) 이 말은 목록으로 나온 브랜치는(현재 브랜치 제외) 삭제해도 정보가 손실되지 않는다. 그 이유는 이미 현재 브랜치로 머지된 브랜치이기 때문에.
  • git branch --unmerged : 현재 checkout한 브랜치를 기준으로 merge하지 않는 브랜치를 보여준다. 그 말은 현재 브랜치를 기준으로 branch를 생성했고, 해당 브랜치를 현재 브랜치로 머지하지 않은 경우 목록으로 나온다. 이 경우에는 git branch -d로 삭제 할 수 없고git branch -D 옵션으로 삭제해야한다.
  • git reset --soft, --mixed, --hard : git reset 명령은 이전 커밋으로 되돌리거나, 커밋을 되돌리고 staging에서 unstaging로 만들거나 혹은 아예 working directory에서 삭제까지 할 수 있는 명령이다. 간단히 이미지로 보자.

 

> git reset --soft HEAD~ : 최근 커밋에서 바로 이전(부모)커밋으로 HEAD를 옮긴다.

 

 

이미지를 보면 커밋만 되돌리고 staging에는 그대로 올라가있다. 그 다음은 git reset --mixed 이다.

 

 

보면 c.txt 파일이 untracked된 것을 볼 수 있다. 이 말은 바로 이전 커밋으로 돌아감과 동시에 해당 파일을 staging에서 unstaging으로 만들었다. 마지막으로 git reset --hard이다.

 

 

가장 조심해서 써야할 옵션이 --hard이다. 커밋도 돌아가고 staging을 unstaging으로 만듬과 동시에 working 디렉토리에서 c.txt를 삭제까지 하였다. 만약 특정 커밋으로 되돌아 가고 싶다면 git reset [option] <commit해시값>으로 되돌아갈 수 있다.

 

 

  • git merge --abort : merge시 충돌이 났을 경우 merge 이전으로 돌려준다. 만약 merge 이전에 working 디렉토리에서 stash하지 않았거나 커밋하지 않은 파일이 존재하고 있을 때는 git merge --abort로 merge전으로 돌아가지 못할 수 있다. 반드시 merge전에는 작업중인 것을 stash하거나 commit해주자.
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 여성게
:
Tools/Git&GitHub 2019. 10. 15. 17:34

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

 

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

 

 

posted by 여성게
:
Tools/Git&GitHub 2019. 9. 24. 23:25

현상

github에서 저장소 생성 후 저장소 주소를 remote에 입력(git remote add origin https://github..)했고, 로컬에서도 정상적으로 초기화(git init)했는데도 git pull 또는 git merge 명령이 동작하지 않고 git push origin master시 [rejected] master -> master (non-fast-forward)이런 에러가 발생하는 경우

원인

깃허브에 생성된 원격 저장소와 로컬에 생성된 저장소 간 공통분모가 없는 상태에서 병합하려는 시도로 인해 발생. 기본적으로 관련 없는 두 저장소를 병합하는 것은 안되도록 설정되어 있음.

해결방법

아래와 같이 git pull 시에 –allow-unrelated-histories 옵션 추가하여 관련 없었던 두 저장소를 병합하도록 허용

 

posted by 여성게
:
Tools/Git&GitHub 2019. 9. 24. 23:24

remote: Permission to 403

remote: Permission to ~ denied to id(xxx).
fatal: unable to access 'https://github.com/~': The requested URL returned error: 403

 

 

a라는 github 아이디로 '최초' 글로벌 유저를 등록 후 b라는 github 아이디로 글로벌유저를 등록 후 git push를 하게 되면 기존에 최초 등록한 a아이디를 바라보고 있기에 에러를 발생시키는 것이었습니다.

 

  1. spolight 검색을 통해 keychain Access.app 또는 키체인 접근을 실행합니다.
  2. 오른쪽 상단에 검색창에 github.com 을 검색합니다.
  3. 리스트에 보이는 github.com 더블클릭 후 계정 암호 현재 사용할 깃허브의 계정 암호로 입력합니다.
  4. 변경사항 저장을 누르고 창닫기

 

에러메세지에서 익숙했던 xxx(id)는 최초 등록했던 id였고 현재 작업하고 있는 mac에서 키체인 자격을 업데이트를 하여 해결할 수 있었습니다.

posted by 여성게
: