오늘은 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)만 알아도 공포심이 확 줄어든다.
'Development environment' 카테고리의 다른 글
| [Docker] streamlit+ milvus로 구성한 RAG 서비스 docker로 배포하기 (0) | 2025.10.16 |
|---|---|
| [Docker] Docker 기본 개념 및 명령어 정리 (0) | 2025.10.16 |
| [Poetry] poetry 개발 환경 셋팅하기 : 설치부터 가상환경 구축까지 (1) | 2025.08.22 |
| [FastAPI] FastAPI 제대로 이해하기 : 코드와 개념으로 (0) | 2025.08.20 |
| FastAPI · Docker · EC2로 보는 백엔드 개발 흐름 (1) | 2025.08.20 |