상황설명 

연차가 많이 쌓이긴 개발자분들 git repository를 보면 커밋로그의 작성은 정말 체계적이다. 업무별로 딱딱 정리가 되고 적당한 타이밍에 맞춰서 커밋기록이 찍혀있다. 이것도 사실 컨벤션이라는 것이 존재하지만 git 사용 경험이 적은 분들 커밋로그 기록을 보면 뭔가의 기준이 없고 커밋을 했는데도 내용이 별로 없는 경우도 있다. 특정 업무 단위로 커밋이 작성되면 좋지만 실제로 작업하다보면 회사에서 중간에 내용을 끊고 집에 가서 작업을 해야 하는 경우도 있을 것이다. 회사에서 A기능 구현중인데 퇴근을 하고 집에서도 구현해야하는 상황에선 github로 커밋을 하고 집 PC에서 받아서 구현을 해야 한다. 그리고 집에서 기능을 작업을 하고 그것을 커밋하고 다음날 회사가서 출근하여 기능을 마무리한다. 이런 경우 커밋기록은 다음과 같이 작성될 수 있다. 

  • A기능 회사에서 작업
  • A기능 집에서 작업
  • A기능 회사에서 결국 완성

그런데 이걸 보고 팀장님이 커밋기록이 지저분하다며 하나로 합쳐달라고 하실 수 있다. 사실 팀이나 회사마다 commit 로그 작성도 기준이 있을 것이다. 이런 기준에 맞아야 하는데 상황에 따라서는 위처럼 커밋로그를 작성할 수 밖에 없다. 

git rebase 

이럴 때 commit 을 재설정하는 명령어인 git rebase를 사용해보자

아래의 화면은 하나의 커밋당 하나의 파일을 추가해서 커밋을 총 4번한 상황이다. 이 상황에서 rebase를 써보려고 한다. 

목적은 이 최근 3개의 커밋을 하나로 합치고 하나의 커밋로그로 만들려는 것이다. 그래서 결과적으로는 3개의 커밋작업의 파일은 유지하고 로그만 하나로 만들어서 "A기능 구현" 이라는 커밋로그 하나만 남길 것이다.

 

이때 사용하는 명령은 git rebase -i HEAD~3 이렇게 입력하면 된다. i 옵션은 여러개의 커밋 목록을 묶을 때 사용하고 HEAD 지점에서 3개의 커밋에 대한 rebase를 한다는 것이다. (커밋을 합치는것 뿐만 아니라 분리도 가능하다고 한다.)

 

Git - git-rebase Documentation

In interactive mode, you can mark commits with the action "edit". However, this does not necessarily mean that git rebase expects the result of this edit to be exactly one commit. Indeed, you can undo the commit, or you can add other commits. This can be u

git-scm.com

  • git rebase -i HEAD~3 
    • 이 상황에서 총 커밋이 4개므로 git rebase -i HEAD~4를 입력하면 invalid upstream 에러가 뜬다.
    • 이 때는 git rebase -i --root 를 입력해야 초기 상태부터 하는 rebase가 가능하다.

  • rebase 위한 창은 아래와 같다. 각 커밋에서 pick, reword, edit, squash, fixup 등의 설정을 할 수 있다.
    • pick은 커밋로그를 그대로 쓰겠다는 것이고 
    • reword를 사용하면 commit 메세지를 편집할 수 있다. ( 근데 이게 써봤는데 딱히 쓸 의미가 없음 )
    • squash는 찌그려트린다는 말로 다른 커밋과 합친다는 말이다. ( 이전 상태로 합친다는 것 주의 )
    • 그리고 drop이라는 것도 있어서 중간에 커밋을 삭제할 수 있다. (커밋을 삭제하면 그 커밋에 생성했던 파일이 날라가니까 주의하기) 

  • 이때 커밋로그 배치가 최근 것이 아래쪽으로 배치되는 것을 확인할 수 있다. 이제 i를 눌러서 입력모드로 진입하고 3개의 커밋중 가장 나중에 된 것 기준으로 pick을 기록하고 나머지는 s(squash)처리를 한다. ( squash가 이전 상태와 합치기 때문에 가장 나중 것을 pick으로 잡고 최근 것을 s로 지정해야 한다. )
    • 이러면 3개의 커밋을 가장 오래전에 한 commit으로 합치겠다는 말이다.
    • 이 상태에서 esc와 클론을 누르고 wq로 저장하면 다음단계로 진입한다.

  • 이상태는 최종 커밋메세지를 변경할 수 있는 상태이다.

  • 커밋 메세지 3개 중에 2개를 지우고 마지막 커밋 메세지를 알맞게 수정한다.(3개중 아무거나 하나 남기면 됨) 그리고 wq로 저장한다. 

커밋메세지 수정

  • 결과적으로 아래와 같이 커밋이 하나로 합쳐진 것을 볼 수 있다. 그리고 파일은 모두 그대로 남아있다.

파일 그대로지?

추가적인 것 

  • 만약에 rebase를 잘못 사용한 경우 git reflog로 되돌리고 아래처럼 git rebase --abort로 rebase를 제거한다.

 github로 작업을 할 때 주의할 점은 필요 없거나 보안측면에서 민감한 파일이 github에 올라가지 못하게 하는 것이다. 예를 들어서 팀프로젝트를 하던 중에 팀원이 이메일 인증 구현 과정에서 네이버나 구글 아이디, 비밀번호를 적어 놓은 .properties 파일을 github에 올린 경우가 있었다. 팀원은 별로 중요한 계정이 아니라고는 하지만 이것이 누군가에 의해 탈취가 되어 악용이 되는 사례가 있을 수 있다는 것을 잊어서는 안된다. 또한 누군가가 aws의 key값을 실수로 github에다가 업로드하여 그것이 탈취되어 과금폭탄을 받은 사례들이 있다. 이러한 것들을 조심해야 한다. 이런 비밀번호나 api키 값들이 main branch에 올라오게 되면 github 이메일 계정으로 이메일이 날라오는데 git gardian인가 하는데서 날라오는 것인데 이것에 대한 처리는 아직 해본적이 없긴하다. 

 

그래서 초기에 팀원들끼리 말을 맞춰서 노출되어서 안되는 정보를 담은 config 파일들을 따로 관리를 하도록 해야 한다. 팀원들 끼리만 접근할 수 있는 구글 클라우드에 따로 저장을 해서 관리를 하던가해서 각자 작업환경에만 저장해두고 원격에는 올리면 안되는 것이다. 그리고 gitignore 셋팅은 프로젝트 초기에 바로 하는 것이 좋다. 이걸 프로젝트 진행 중간에 해버리면 이미 tracked된 파일들에 대해선 ignore 처리가 안되게 된다. 팀원들 마다의 커밋 상태가 다르기에 누군가가 중간에 ignore에 추가하더라도 merge 과정 중간에서 갑자기 노출되어서는 안되는 파일이 등장해버린다. 

 

 이러한 gitignore 파일은 이클립스로 git 작업을 하면 자동으로 만들어주는 경우가 있긴한데 그렇다하더라도 gitignore 파일 작성법은 알아야 한다. 내 config 파일이 어디에 저장할지 알고, 그리고 어떤 파일명으로 하고 어떤것을 gitignore에 추가할 줄 알고 자동화된 gitignore만 믿을 것인가? 내가 생각하는 gitignore 파일 작성 절차를 얘기해보려 한다. 

 

.gitignore 작성절차

  • 키워드만 입력하면 gitignore 내용을 자동으로 생성해주는 사이트를 1차적으로 이용한다. 
  • https://www.toptal.com/developers/gitignore
  • 원하는 키워드를 입력하고 생성하면 자동으로 내용이 만들어진다.

  • 이후에 gitignore 작성법에 따라 노출되어서는 안되는 파일을 gitignore 파일에 추가한다. 

.gitignore 작성법

  • #은 주석을 작성할 때 쓴다. 
  • / 로 끝나는 것은 폴더로 인식한다. 
    • /를 앞에다가 붙일 수 있고 끝에다가 붙일 수 있는데 이 차이를 알아야 한다.
    • 앞에다가 붙이면 .gitignore가 있는 위치 기준으로 현재 위치에서만 찾는다. 
    • 뒤에다가 붙이면 하위 폴더에서도 해당 폴더를 찾아서 ignore 한다. 
    • 폴더를 ignore하기 때문에 폴더 안에 있는 파일도 모조리 ignore한다.
  • *.exe 와 같이 와일드 카드 옵션을 붙여서 exe라고 하는 확장자의 파일을 무시할 수 있다.
  • !를 붙이면 무시되지 않는 파일을 지정할 수 있다. 
  • *의 사용
    • doc/*.txt
      • 의미설명 : 해당 폴더 바로 밑에 있는 txt 확장자 파일을 ignore
    • doc/**/*.txt
      • 의미설명 : 해당 폴더 사이에 어떤 폴더가 있더라도 txt 확장자 파일을 ignore (없어도 된다.) 

 

ignore된 파일을 확인하는 명령어

  • git status --ignored

예제로 알아보기

  • 디렉토리 내의 파일구성은 다음과 같다.
.gitignore
ABC
┕ aa.txt
┕ Hello
   ┕ aaa.txt
   ┕ lib.jar
Hello
┕ a.txt
doc
┕ report
   ┕ paper2.pdf
   ┕ test2.txt
┕ paper1.pdf
┕ test1.txt
  • 편의를 위해서 vscode로 진행한다.

구조

  • / 를 앞에다 붙이는 경우
    • 앞에다가 작성하는 경우에는 현재 디렉토리에서 해당 파일을 찾게 된다.
    • 따라서 하위 폴더에 있는 Hello는 ignore하지 못하여 .gitignore 파일이 있는 위치의 Hello만 ignore한다.

  • / 를 뒤에다가 붙이는 경우
    • 해당 폴더를 하위 디렉토리까지 찾아서 ignore 해준다.
    • 아래 그림에서는 현제 디렉토리에 있는 Hello폴더 ABC 폴더에 있는 Hello 폴더 등 모조리 찾는다.

  • 특정 확장자 파일을 ignore 하고 싶다면? 
    • *.txt > 이런식으로 작성한다. 
    • 와일드 카드 표시는 하위 디렉토리에 있는 파일도 모조리 포함시킨다. 

  • 이 상황에서 특정 파일만 ignore에서 제외를 시키고 싶다면?
    • ! 라는 키워드를 쓴다.
    • 분명히 * 로 다 ignore 했는데 특정 파일만 !로 가르키므로써 ignore에서 제외된다.

!가 이렇게 작동함

  • 단, ! 키워드는 폴더 채로 ignore 하고 그 안에 내용물에 대해서 ! 키워드를 작성하는 경우엔 동작하지 않는다. 

  • 특정 폴더 안에 특정 확장자 파일을 ignore
  • 폴더에서 depth라는 것을 고려해야 한다. 바로 밑에 있는 파일은 ignore 되지만 더 아래에 있는 doc/report/paper2.pdf는 ignore가 되지 않았다.

 

  • 특정 폴더 안에 또 폴더가 있고 특정확장자 파일을 ignore
  • doc/**/*.pdf 와 같이 작성하면 된다. *를 두번 적으면 중간 디렉토리에 대해서 와일드 카드를 걸고 폴더가 있든 없든간에 다 잡아준다. 

중간와일드

어떠한 파일을 ignore 하는 것이 좋을까?

 일반적으로는 빌드되어 생성되는 target폴더나 IDE마다 프로젝트 내에서 생성되는 .setting폴더, .idea 폴더, .metadata 등의 폴더등을 ignore한다. 그리고 jar 파일도 ignore해서 작업시에 팀원들끼리 따로 공유하는 것이 좋다. 웹프로그래밍시에는 Nodejs 코드 작성시엔 env 파일을, Spring 코드 작성시에는 .properites 등의 파일을 ignore하여 관리하는 것이 좋다. 

내가 작성한 소스 코드를 github 원격 저장소에 업로드 하고 싶은 상황이 온다. 처음에 git 공부를 하다보면 디테일한 상황을 고려하지 않게 되는데 이게 실전에 다가오면 원격 저장소 상태나, 로컬 저장소 상태 등에 따라서 상황이 구분이 되고 막상 업로드 하려고 하면 딱히 방법이 떠오르지 않는다. 그래서 3가지 경우의 수 정리해보았다.

 

1. 로컬에서 작업을 주욱 하다가~ Readme.md 파일을 생성하지 않은 원격 저장소에 소스코드를 업로드 하는 경우
2. 이미 만들어진 https 주소를 복사하여 clone 후 로컬에서 작성중인 소스코드를 원격 저장소에 업로드 하는 경우
3. 이전에 생성한 원격 저장소에 이미 Readme.md 파일이 있는 경우 내가 작업했던 로컬 작업영역 내용을 원격에 업로드 하는 경우

 

1. 로컬에서 작업을 주욱 하다가~ Readme.md 파일을 생성하지 않은 원격 저장소에 소스코드를 업로드하는 상황

로컬에 작업을 진행하고 커밋

 

일단 로컬에서 저장소를 만들어서 다음과 같이 파일을 만들고 add 후 commit하여 작업이 끝났다고 가정하자.

Add a README file을 체크하지 않고 git repository를 생성

 

미리 만들어 놓은 git repository가 떠올랐고 여기엔 readme.md 파일이 없다.

> $ git remote add origin "생성한 git repository의 https 주소" : 원격 저장소의 주소를 지정

git remote add 명령어 실행화면

> $ git remote -v : 등록된 원격 저장소 주소 확인

 

등록된 원격 저장소 주소

 

> $ git push -u origin main : -u라는 옵션은 --set-upstream 옵션과 동일하다. 특정 브렌치를 지정해서 업로드를 위한 길을 지정하는 작업이라고 보면 된다. 길을 지정하면 그 브렌치로 소스 코드가 업로드 된다. ( 이 옵션 지정 없이 push 명령어를 입력하게 되면 git push --set\-upstream origin main이라는 에러 문구가 뜬다. )

 

                                   

 

 

원격 저장소에 소스 코드가 업로드 되었다.

소스 코드가 업로드된 상황

2. 이미 만들어진 https 주소를 복사하여 clone 후 로컬에서 작성중인 소스코드를 원격 저장소에 업로드 하는 경우 (이 때는 Readme.md 파일 생성여부 상관없음)

 

HTTPS 주소

 

이미 만들어 놓은 git repository의 원격 저장소의 https 주소를 복사

clone 명령어 실행

> git clone "git repository의 https주소" : clone은 원격의 저장소를 로컬에다 복제를 하는 것이라 보면 된다.

  결과적으로 현재 디렉토리에 원격 저장소 폴더가 생기고 그 안으로 이동한다.

clone 이후 상황

해당 폴더에 들어가니 branch문구가 찍히고 현재 디렉토리에 원격의 파일이 들어왔다.

git remote -v 실행결과

git clone을 하는 경우에는 remote 주소가 자동으로 지정된다.

그리고 이상황에서 파일을 새로 추가 혹은 변경 등의 작업이 일어난다고 가정하자.

작업을 한 상황

> git push origin main : clone을 한 상황에서는 set-upstream을 따로 지정하지 않아도 된다.

바로 git push origin main으로 소스코드 업로드 가능

 

원격 저장소에 업로드
업로드된 결과

 

3. 이전에 생성한 원격 저장소에 이미 Readme.md 파일이 있는 경우 내가 평소에 작업했던 로컬 작업영역 내용을 원격에 업로드 하는 경우

 

새 저장소 생성

이 경우에는 이전에 원격 저장소를 만들어놨는데 README.md 파일이 있는 경우이다.

로컬에서 작업을 완료함

그리고 로컬에서 다음과 같이 작업을 완료했다고 가정하자

이 때 위의 1번의 방법과 같이 업로드 하려하는 경우 다음과 같이 된다.

에러

error : failed to push some refs to "주소~" 라는 에러가 난다. 이러한 이유는 원격 저장소에 README.md 파일이 존재해서 그렇다. README.md 파일이 없는 경우 로컬에 있는 소스코드만 원격으로 들이밀면 되나, README.md 파일이 있기에 이것으로 인해 로컬 저장소가 예상한 것과 원격 저장소의 상태의 불일치가 발생한다.

그래서 로컬 저장소는 "이거 혹시 원격 저장소에 누가 push 한거 아니야?" 라고도 생각할 수 있는 것이다.

 

해결 방법은 두가지다.

1) git push -f origin main ( -f 옵션을 이용한 강제 푸시방법 )

2) git pull origin main --allow-unrelated-histories

1번 방법은 뭔가의 불일치 느낌이 있어도 억지로 밀어 넣겠다라는 뜻이고

2번 방법은 --allow-unrelated-histories 옵션 설정으로 pull하여 원격 저장소의 변경사항을 가져오고 로컬 저장소에 merge도 시키겠다는 말( pull은 merge 과정이 포함한다. 주의!!)

 

1번의 방법

먼저 1번의 방법으로 강제로 업로드 시켜본다. 결과는 README.md 파일이 없어진다. 만약에 README.md에 뭔가 중요한 내용이 있었더라면 이거 큰 일이다...

Readme.md 파일 어디감??

음.. 이번엔 시간을 과거로 되돌려서 이제 2번 방법으로 해보자 ( 앞 과정 똑같이 셋팅 )

원격 저장소 등록

> git remote add origin "원격 저장소 주소"

> git pull origin main --allow-unrelated-histories

앞의 명령어를 실행시킨 결과 Merge 메세지를 작성하는 창이 나오고

결국엔 Merge가 된다. 원격 저장소의 Readme.md 파일이 로컬 저장소로 들어와 merge된 것이다.

readme.md 어서오고

 

readme.md 어서오고

이제 다시 원격 저장소로 push 명령어 실행

 

readme.md파일이 원격 저장소에?

readme.md파일이 사라지지 않고 원격 저장소에 들어온다.

이는 readme.md 파일을 로컬로 가져오고 상태를 맞춰주고 push를 했기 때문에 그렇다.

강제 push 방법인 1번 방법도 많이 쓰는 방법이기도 하지만 Readme.md 파일에 뭔가 써놓은게 많아서 날라가는 것을 원치 않는 경우에는 2번 방법을 써주자.

'Git & Github' 카테고리의 다른 글

커밋로그를 바꿀 수 있는 git rebase  (0) 2023.07.17
프로젝트의 시작은 .gitignore부터..  (0) 2023.07.17

+ Recent posts