Development environment

[Git] Git 정리: 개념, 필수 명령어, 협업 흐름, 트러블슈팅 총정리

hjjummy 2025. 9. 19. 11:34

오늘은 Git을 이용한 실제 작업 흐름 중, 기존에 내 로컬 폴더에서 작업하던 내용을 깃에 올리는 상황을 다뤄보려고 한다.
즉, 프로젝트를 처음부터 git clone으로 받아서 시작한 게 아니라, 중간에 이미 진행하던 로컬 작업물을 깃 저장소 구조로 옮겨야 할 때 유용한 Git 사용 흐름이다.

📌 Process
clone → cp → add → commit → push → PR/MR

 

여기에 더해, 현업에서 자주 마주치는 에러나 헷갈리는 포인트를
증상 → 원인 → 해결 명령어 형태로 깔끔하게 정리해두려 한다.

 

💥왜 이제서야 정리하게 되었나?

Git을 사용한 지 꽤 되었는데… 사실 오늘 진짜 식겁한 경험을 했다.ㅎㅎ  내 로컬 폴더 내용을 잘못 커밋해서 원격 저장소의 내용이 내 로컬 내용으로 덮히면서 싹 날아가 버렸당. 😱 😱 😱

순간 머리가 하얘졌는데, 다행히도 Git에는 되돌리기 기능이 있어서 복구할 수 있었다.

물론 복구 기능이 있다는 건 알았지만, 회사 Git이다 보니 순간 손이 덜덜 떨렸다… 😭

 

그때 느낀 게 “ 이 기회에 Git을 제대로 이해하고 있어야겠다 ” 라고 생각이 들었고

그래서 이번 글에서는
내가 겪은 실수와 해결 과정을 토대로 Git의 기본 개념 , 필수 명령어 , 협업 흐름까지 한 번 정리해보고자 한다.

 

문서 끝에는 되돌리기 전략, 브랜치 전략, 커밋 컨벤션, .gitignore/.gitattributes, LFS, 고급 명령어(rebase/cherry-pick/stash/bisect) 등까지 정리해두었다.


1)🔎Git이란?

Git은 단순히 코드를 저장하는 툴이 아니다.
👉 분산형 버전 관리 시스템(Distributed Version Control System, DVCS) 으로,
프로젝트의 변경 이력을 스냅샷(snapshot) 단위로 관리하며, 분기(branch) 와 병합(merge) 을 통해 협업을 지원한다.

 

즉, Git은 단순한 “파일 업로드/다운로드” 도구가 아니라,
분산된 환경에서 안정적으로 이력 추적, 변경 관리, 협업을 가능하게 하는 소프트웨어 구성 관리(SCM: Software Configuration Management) 시스템이다.

 

 

  • Git은 “엔진(버전 관리 도구)”.
  • GitHub은 “그 엔진을 활용하는 세계적인 오픈소스 광장”.
  • GitLab은 “엔진 + 협업 + 자동화까지 올인원으로 제공하는 기업용 도구”.

 

2) 🔎핵심 개념

 

  • 레포지토리(Repository): 프로젝트 저장소. .git/ 폴더가 있으면 Git이 추적 중인 디렉토리.
  • 스테이징(Staging): 다음 커밋에 포함할 변경을 선택하는 단계 (git add).
  • 커밋(Commit): 시점의 스냅샷. “무엇을 왜 바꿨는지” 메시지와 함께 기록 (git commit -m).
  • 브랜치(Branch): 독립된 작업선. 예: feat/…, fix/…, chore/….
  • 원격(Remote): 중앙 저장소(GitHub, GitLab). 보통 이름은 origin.
  • PR/MR: 브랜치를 main에 합치기 위한 리뷰 요청 (Pull Request / Merge Request).
  • Rebase vs Merge
    • rebase ➡️ 이력을 직선으로 깔끔하게 유지 (추천).
    • merge ➡️ 머지 커밋이 생겨도 안전하고 단순.

 

3) 🛠️ 실제 작업 흐름 (표준 프로세스)

예시: /workspace/chloe/미래에셋/file02 → source/c02_dc_new_contract_doc/ 반영

# 0) 클론 및 최신화
cd /workspace/chloe
[ -d miraeassetbmt_poc2025 ] || git clone https://git.sparklingsoda.ai:8443/consulting/miraeassetbmt_poc2025.git
cd miraeassetbmt_poc2025
git pull origin main

# 1) 중첩 리포 방지 (.git 제거)
rm -rf /workspace/chloe/미래에셋/file02/.git 2>/dev/null || true

# 2) 숨김 포함 복사 (마침표 중요!)
mkdir -p source/c02_dc_new_contract_doc
cp -r "/workspace/chloe/미래에셋/file02/." "source/c02_dc_new_contract_doc/"

# 3) 스테이징 + 커밋
git config core.quotepath false   # (옵션) 한글 경로 가독성
git add -A source/c02_dc_new_contract_doc
git commit -m "chore(c02): sync file02 → c02_dc_new_contract_doc"

# 4) 푸시
git push origin main              # 보호 브랜치 아니면 OK
# 보호 브랜치라면 브랜치 생성 후 MR:
# git switch -c chore/add-c02-sync
# git push -u origin chore/add-c02-sync

 


4) ❗ 자주 겪는 에러 모음 (증상 → 원인 → 해결)

증상 원인 해결 (명령어)
fatal: not a git repository ... 현재 폴더가 레포가 아님 (.git/ 없음) git init → git remote add origin <URL> (원격이 있으면 애초에 git clone <URL>)
git: 'add.' is not a git command add와 . 사이 공백 누락 git add .
커밋 시 Author identity unknown user.name/email 미설정 git config --global user.name "..." / git config --global user.email "..." (혹은 리포 한정으로 git config user.name ...)
remote origin already exists origin이 이미 있음 git remote -v 확인 → git remote set-url origin <URL> 또는 git remote remove origin && git remote add origin <URL>
! [rejected] main -> main (fetch first) / non-fast-forward 거부 원격 main과 로컬 이력 불일치 git pull --rebase origin main → git push -u origin main
fatal: refusing to merge unrelated histories 서로 다른 git init 이력 병합 시도 git pull --rebase origin main --allow-unrelated-histories (머지 방식이면 --no-rebase)
잘못 푸시 후 되돌리고 싶음(보호 브랜치) 강제 푸시 불가 정석: git revert --no-edit <이전해시>..<현재해시> → git push origin main
하위 폴더가 갑자기 “리포처럼” 동작 하위 폴더에 .git/ 생겨 중첩 리포 rm -rf <subdir>/.git로 제거 후 부모 레포에서 정상 커밋
“폴더만 특정 경로에 업로드” 하고 싶음 Git은 리포 단위로 동작 리포 루트에서 경로 생성 후 파일만 복사 → git add -A <경로> → git commit → git push
rsync 없음 환경에 rsync 미설치 숨김 포함 복사: cp -r <원본>/. <대상>/
한글/특수문자 경로가 깨져 보임 콘솔 표시 이슈 git config core.quotepath false
추가/수정/삭제 섞였는데 일부만 스테이징 git add .는 삭제 추적 미포함일 수 있음 git add -A <경로>
보호 브랜치 강제 푸시 실패 정책상 금지 작업 브랜치 생성 → git push -u origin <branch> → MR/PR로 병합

5) ✨ 협업의 표준 흐름

1. 작업 전 최신화

git switch main                       # 메인 브랜치로 이동
git pull --rebase origin main         # 원격(main) 최신 이력을 로컬에 가져와서 내 브랜치와 깔끔하게 맞춤(rebase 방식)

 

2. 브랜치 생성(의미 있는 이름)

git switch -c feat/my-task             # 새 브랜치(feat/my-task)를 만들고 동시에 이동

 

3. 작업 → 스테이징/커밋

git status                       # 현재 상태 확인
git add -A                       # 변경된 파일(추가/수정/삭제 포함)을 모두 스테이징 영역에 올림
git commit -m "feat: implement my task" # 스테이징한 변경을 커밋으로 기록, 메시지 남김

 

4. 원격 푸시( + 업스트림 설정)

git push -u origin feat/my-task # 원격(origin)에 브랜치(혹은 메인)를 푸시하고 업스트림 설정(-u)

 

👉  -u (혹은 --set-upstream) 옵션

    : 이 옵션은 “내 로컬 브랜치(feat/my-task)와 원격 브랜치(origin/feat/my-task)를 연결해 달라”는 뜻

      이렇게 연결해 두면 그다음부터는 git push나 git pull만 입력해도 Git이 자동으로 원격 브랜치를 알아서 찾아감.

      연결하지 않으면 매번 git push origin feat/my-task처럼 원격 이름과 브랜치 이름을 다 써줘야 함.

       즉, -u는 “이 브랜치의 기본 원격 브랜치를 설정(upstream branch 설정)”하는 기능임.

 

5. 이후 GitHub/GitLab에서  PR/MR 생성 → 리뷰/승인 → main에 머지

 

  • git push -u origin feat/my-task를 하면 원격 저장소(GitHub, GitLab)에 새로운 브랜치 feat/my-task가 생김.
  • 협업 환경에서는 이 브랜치를 main에 바로 머지하지 않고, 리뷰 과정을 거쳐야 함.
  • 그래서 웹 UI(GitHub/GitLab)에서 “이 브랜치를 main에 합치자”라는 요청을 만드는 게 바로 Pull Request(PR) 혹은 Merge Request(MR)임.

 

6. 머지 이후 로컬 최신화 & 브랜치 정리

git switch main       # 내가 작업하던 브랜치 → main으로 돌아옴
git pull --rebase origin main  # 원격(origin)의 main 최신 커밋을 내 로컬 main에 반영.
git branch -d chore/sync-c02-doc   # 선택 : 더 이상 필요 없는 작업 브랜치(chore/sync-c02-doc)를 로컬에서 삭제.

PR/MR을 통해 main에 머지된 뒤, 내 로컬 환경을 최신 상태로 정리하는 단계


6) 🔙 되돌리기 전략 — 언제 revert, 언제 reset, 언제 restore인가

  • revert (기록 유지, 협업 안전)
    보호 브랜치에서 과거 변경을 “반대로 적용하는 새 커밋”을 만든다.
git revert <커밋>              # 단일
git revert --no-edit A..B      # 범위

 

  • reset --hard (기록 재작성, 신중)
    내 브랜치의 HEAD를 특정 커밋로 “이동”시키며 워킹 디렉토리도 강제로 맞춤.
    공유 브랜치에서 쓰려면 팀 합의 + 보호 해제 필요
git reset --hard HEAD~1
git push --force-with-lease     # 보호 브랜치면 불가

 

  • restore (파일 레벨 복구)
    스테이징/워킹 디렉토리의 파일 단위 변경 취소.
git restore <파일>             # 워킹 디렉토리 변경 버림
git restore --staged <파일>    # 스테이징 해제

원칙: 공유/보호 브랜치에서는 revert 우선, reset --hard는 로컬이나 개인 브랜치에서만.

 


참고로, 내가 이번에 되돌릴때 사용한 명령어는 아래와 같다. 

git revert --no-edit <이전해시>..<현재해시>
git push origin main

 

👉  revert를 사용한 이유는 

  • git reset --hard는 과거 커밋 기록을 지워버려서, 협업 중인 원격 브랜치(main)를 Protected Branch로 설정해두면 사용 불가.
  • git revert는 기존 커밋을 그대로 두고, 반대로 적용하는 새 커밋을 만들 안전하게 되돌려줌.

따라서 이력은 보존되고, 협업자들도 “어떤 변경이 있었고, 어떻게 되돌렸는지”를 투명하게 확인할 수 있다.

협업에서는 기록이 중요한데, 내가 되돌렸다는 흔적까지 포함해서 남겨두는 것이 추후 디버깅, 리뷰, 히스토리 관리에 도움이 된다.혼자 하는 프로젝트라면 reset으로 간단히 되돌려도 되지만, 팀 협업 환경에서는 revert를 추천한다.

 


7) ⚔️ 충돌 처리 (rebase 흐름)

git pull --rebase origin main   # 충돌 발생
# 충돌 표시된 파일을 수동으로 수정
git add <해결된_파일들>
git rebase --continue
git push

 


8) 📝 커밋 메시지 컨벤션

  • feat: 기능 추가
  • fix: 버그 수정
  • chore: 빌드/배포/정리 등
  • docs: 문서
  • refactor: 리팩터링
  • test: 테스트 코드
  • perf: 성능 개선

예: chore(c02): sync /workspace/chloe/미래에셋/file02 → source/c02_dc_new_contract_doc


9) 🚫 .gitignore / .gitattributes 

.gitignore

👉 “Git이 추적하지 않을 파일/폴더를 지정”하는 설정 파일

  • Git은 기본적으로 디렉토리 안의 모든 파일을 추적하려고 한다.
  • 하지만 로그, 캐시, 빌드 산출물, 개인 환경설정 같은 건 굳이 버전 관리할 필요 없.
  • 이런 것들을 .gitignore에 적어두면 git add 해도 무시된다.

 

예시

__pycache__/   # 파이썬 실행 시 자동 생성되는 캐시 폴더
*.pyc          # 파이썬 바이트코드 파일
.venv/         # 가상환경 폴더
env/           # 또 다른 이름의 가상환경
*.log          # 로그 파일
.DS_Store      # Mac OS에서 자동 생성되는 파일
.idea/         # JetBrains IDE 프로젝트 설정
.vscode/       # VSCode 설정 폴더

이렇게 해두면 협업 시 쓸데없는 파일 혹은 보안에 취약한 파일들이 올라가지 않아 저장소가 깔끔해짐.

 

.gitattributes 

👉 “Git이 파일을 어떻게 다뤄야 하는지 속성을 지정”하는 설정 파일

  • 줄 끝(EOL) 통일: OS마다 줄바꿈(LF, CRLF)이 달라 충돌이 나기 쉬움 → .gitattributes에서 통일 가능.
  • 머지 전략 제어: 특정 파일은 자동 머지하지 않고 무조건 충돌로 표시.
  • 바이너리 파일 처리: 이미지/동영상처럼 텍스트 diff가 무의미한 파일은 “바이너리”로 지정.

예시

# PNG, JPG 파일은 텍스트 diff 대신 바이너리로 취급
*.png binary
*.jpg binary

 

정리

오늘의 교훈:

  • Git은 단순 업로드 툴이 아니라 이력 관리 시스템이다.
  • 협업에서는 항상 브랜치 → PR/MR → 머지 흐름을 따르자.
  • 되돌리기 전략(revert/reset/restore)만 알아도 공포심이 확 줄어든다.