Blog 블로그/기술문서Tech Notes

git 명령어Git commands

커밋 그래프와 포인터 움직임 중심의 Git 명령어 정리Git commands, organized around the commit graph and how pointers move

Contents

Git은 커밋을 만들고, 브랜치를 움직이고, 갈라진 작업을 다시 합치는 도구다. 커밋 그래프에서 포인터가 어디로 움직이는지를 기준으로 보면 훨씬 이해하기 쉬웠다.

1. Git의 기본 단위

커밋은 프로젝트의 특정 시점을 저장한 기록이다. 브랜치는 그 커밋을 가리키는 이름표이고, HEAD는 지금 내가 보고 있는 위치다.

  • 커밋: 변경사항이 저장된 지점
  • 브랜치: 특정 커밋을 가리키는 포인터
  • HEAD: 현재 작업 중인 위치
  • 원격 추적 브랜치: 원격 브랜치의 상태를 로컬에서 기억하는 포인터

2. 기록

변경사항은 먼저 스테이징 영역에 올라간 뒤 커밋으로 기록된다.

git add는 작업 파일의 변경사항을 스테이징 영역으로 올리고, git commit은 스테이징된 내용만 새 커밋으로 저장한다.

  • git add . 현재 디렉터리 아래 변경사항을 스테이징
  • git add <file> 특정 파일만 스테이징
  • git commit -m "message" 스테이징된 변경사항을 커밋으로 저장
  • git commit -am "message" 이미 추적 중인 파일을 add와 commit까지 한 번에 처리
  • git commit --amend 마지막 커밋의 메시지나 내용 수정

--amend는 마지막 커밋을 새 커밋으로 다시 만드는 명령이다. 이미 원격에 올린 커밋에는 신중하게 써야 한다.

3. 이동

브랜치는 작업 흐름을 나누기 위해 만든다. 만든 브랜치로 이동한 뒤 커밋하면, 그 브랜치 포인터가 새 커밋으로 이동한다.

HEAD가 feature에 붙어 있으면 지금 작업 위치는 feature다. 이 상태에서 커밋하면 새 커밋은 feature 뒤에 이어진다.

  • git branch 브랜치 목록 확인
  • git branch <branch> 현재 위치에 새 브랜치 생성
  • git branch -d <branch> 병합된 브랜치 삭제
  • git branch -D <branch> 브랜치 강제 삭제
  • git branch -f <branch> <where> 브랜치를 특정 위치로 강제 이동
  • git checkout <branch> / git switch <branch> 브랜치 이동
  • git checkout -b <branch> / git switch -c <branch> 브랜치 생성 후 이동

상대 참조를 쓰면 커밋 해시를 몰라도 원하는 위치를 가리킬 수 있다.

HEAD^는 현재 커밋의 부모를, HEAD~2는 현재 위치에서 두 단계 위의 커밋을 가리킨다.

  • HEAD^ 현재 커밋의 부모
  • HEAD~3 현재 위치에서 세 단계 위 커밋
  • <branch>^2 merge commit에서 두 번째 부모

4. 병합

브랜치를 합치는 대표적인 방법은 merge와 rebase다. 둘 다 작업을 합치지만 남는 커밋 그래프가 다르다.

merge는 다른 브랜치의 변경사항을 현재 브랜치에 합친다. 단, fast-forward가 가능하면 병합 커밋 없이 브랜치 포인터만 이동하고, --no-ff를 쓰면 병합 커밋을 강제로 남긴다. 위 그림의 merge는 fast-forward가 아닌 경우다.

rebase는 작업 커밋을 새 기준 브랜치 뒤에 다시 쌓아 한 줄처럼 정리한다.

  • git merge <branch> 현재 브랜치에 지정한 브랜치를 병합
  • git merge --continue 충돌 해결 후 merge 계속 진행
  • git merge --abort 진행 중인 merge 취소
  • git merge --no-ff <branch> fast-forward가 가능해도 merge commit 생성
  • git rebase <newbase> 현재 브랜치의 커밋을 새 기준 위로 다시 쌓기
  • git rebase <newbase> <branch> 지정한 브랜치를 새 기준 위로 재배치

이미 공유한 커밋에는 rebase를 조심해서 써야 한다. 커밋 해시가 바뀌기 때문이다.

5. 되돌리기와 커밋 선택

되돌리기 명령어 중 reset은 브랜치 포인터를 과거로 옮기고, revert는 취소 내용을 새 커밋으로 남긴다.

reset은 현재 브랜치가 가리키는 위치를 바꾼다. revert는 기존 커밋을 지우지 않고, 반대 변경사항을 담은 새 커밋을 추가한다.

  • git reset HEAD~1 현재 브랜치를 한 커밋 이전으로 이동
  • git reset --soft HEAD~1 커밋만 취소하고 스테이징 유지
  • git reset --mixed HEAD~1 커밋과 스테이징 취소, 작업 파일은 유지
  • git reset --hard HEAD~1 커밋, 스테이징, 작업 파일 변경까지 되돌림
  • git revert HEAD 변경을 취소하는 새 커밋 생성

아직 공유하지 않은 로컬 커밋은 reset으로 정리할 수 있고, 이미 공유한 커밋은 revert로 되돌리는 편이 안전하다.

cherry-pick은 다른 브랜치에 있는 특정 커밋의 변경사항만 골라 현재 브랜치 뒤에 새 커밋으로 붙인다.

  • git cherry-pick <commit1> <commit2> 특정 커밋만 현재 브랜치에 적용

cherry-pick은 작업 트리가 깨끗한 상태에서 실행하는 편이 안전하다. 충돌이 나면 충돌을 해결한 뒤 계속 진행해야 한다.

rebase -i로는 최근 커밋의 순서, 메시지, 포함 여부를 정리한다.

  • git rebase -i HEAD~3 최근 커밋의 순서, 메시지, 포함 여부 편집

자주 쓰는 선택지는 pick, reword, edit, squash, fixup, drop이다. 커밋을 남길지, 메시지를 바꿀지, 합칠지, 제거할지를 여기서 정한다.

6. 표시와 임시 보관

태그는 중요한 커밋에 붙이는 고정 이름표다. stash는 아직 커밋하기 애매한 변경사항을 잠깐 치워두는 용도다.

  • git tag <tag> 현재 HEAD에 lightweight tag 추가
  • git tag <tag> <commit> 지정한 커밋에 lightweight tag 추가
  • git tag -a <tag> -m "message" annotated tag 생성
  • git push origin <tag> 특정 태그를 원격에 업로드
  • git push origin --tags 로컬의 모든 태그를 원격에 업로드
  • git describe 가까운 annotated tag 기준으로 현재 위치 설명
  • git describe --tags lightweight tag까지 포함해 가까운 태그 기준으로 설명

stash는 작업 중인 변경사항을 임시로 보관했다가, 나중에 다시 꺼내 적용할 수 있게 해준다.

  • git stash 추적 중인 파일의 작업 디렉터리/스테이징 변경사항 임시 저장
  • git stash -u untracked 파일까지 포함해 임시 저장
  • git stash -a ignored 파일까지 포함해 임시 저장
  • git stash list stash 목록 확인
  • git stash pop 저장한 변경사항을 다시 적용하고 stash 목록에서 제거 시도

7. 원격 저장소와 협업

원격 저장소는 내 작업을 공유하고 다른 사람의 작업을 받아오는 공간이다. fetch, pull, push가 각각 무엇을 하는지 구분해야 한다.

fetch는 원격 상태를 원격 추적 브랜치에 가져오기만 한다. pull은 가져온 원격 변경사항을 현재 브랜치에 통합하고, push는 로컬 커밋을 원격 저장소로 올린다.

  • git clone <url> 원격 저장소를 로컬로 복제
  • git fetch 원격 변경사항을 가져오되 현재 브랜치에는 합치지 않음
  • git pull fetch 후 가져온 원격 변경사항을 현재 브랜치에 통합
  • git pull --rebase fetch 후 merge 대신 rebase로 통합
  • git pull --no-rebase fetch 후 merge 방식으로 통합
  • git pull --ff-only fast-forward가 가능할 때만 통합
  • git push 로컬 커밋을 원격에 업로드
  • git push -u <remote> <branch> push하면서 upstream 추적 관계 설정

원격 추적 브랜치는 보통 origin/main처럼 표시된다. 실제 원격 브랜치는 원격 저장소에 있고, 로컬의 origin/main은 마지막 통신 시점의 원격 상태를 기억한다. 학습 환경에서는 o/main처럼 짧게 보이기도 한다.

  • git checkout -b <branch> <remote>/<branch> 원격 브랜치를 기준으로 새 로컬 브랜치 생성 및 추적
  • git branch -u <remote>/<branch> <local-branch> 기존 로컬 브랜치가 원격 브랜치를 추적하게 설정
  • git fetch <remote> <source>:<destination> 원격 source를 로컬 destination으로 가져오기
  • git pull <remote> <source>:<destination> 가져온 뒤 현재 브랜치에 통합
  • git push <remote> <source>:<destination> 로컬 source를 원격 destination으로 업로드
  • git push <remote> :<branch> 원격 브랜치 삭제

8. 자주 쓰는 흐름

새 기능 작업

Terminal
git checkout -b <branch>
git add .
git commit -m "message"
git push -u <remote> <branch>

원격이 앞서 있어서 push가 거부될 때

Terminal
git pull --rebase
git push

또는

Terminal
git fetch
git rebase <remote>/<branch>
git push

실수로 main에 커밋했을 때

아직 원격 main에 push하지 않은 상태이고, 커밋되지 않은 작업 변경이 없다면 다음처럼 새 브랜치를 먼저 만든 뒤 main을 원격 상태로 되돌릴 수 있다. --hard는 작업 디렉터리 변경까지 되돌릴 수 있으므로 먼저 git status를 확인해야 한다.

Terminal
git status
git branch <new-branch>
git reset --hard <remote>/main
git switch <new-branch>
git push -u <remote> <new-branch>

이미 원격 main에 push한 커밋이라면 이 흐름보다 revert를 쓰거나 팀과 합의한 뒤 별도로 처리하는 편이 안전하다.

9. 주의 명령어

  • git reset --hard <where> 실제 파일 변경까지 사라질 수 있음
  • git push <remote> :<branch> 원격 브랜치 삭제
  • git branch -f <branch> <where> 브랜치 포인터 강제 이동
  • git rebase <shared-branch> 공유된 커밋의 해시 변경 가능
  • git commit --amend 마지막 커밋을 새 커밋으로 재작성

Git is a tool for creating commits, moving branches, and merging diverged work back together. I found it much easier to understand when I focused on where pointers move in the commit graph.

1. Git's basic units

A commit is a record that saves a particular point in time of the project. A branch is a label that points to a commit, and HEAD is where I am currently looking.

  • Commit: a point where changes are saved
  • Branch: a pointer to a specific commit
  • HEAD: the location I am currently working at
  • Remote-tracking branch: a pointer that remembers the state of a remote branch locally

2. Recording

Changes first go to the staging area and are then recorded as a commit.

git add stages changes from the working files into the staging area, and git commit saves only the staged content as a new commit.

  • git add . stage changes under the current directory
  • git add <file> stage only a specific file
  • git commit -m "message" save staged changes as a commit
  • git commit -am "message" add and commit already-tracked files in one step
  • git commit --amend edit the message or content of the last commit

--amend rebuilds the last commit as a new commit. Use it carefully on commits already pushed to a remote.

3. Moving

Branches are created to split up work streams. If you move to a branch you created and then commit, that branch pointer moves to the new commit.

If HEAD is attached to feature, your current working location is feature. Committing in this state appends the new commit after feature.

  • git branch list branches
  • git branch <branch> create a new branch at the current location
  • git branch -d <branch> delete a merged branch
  • git branch -D <branch> force-delete a branch
  • git branch -f <branch> <where> force-move a branch to a specific location
  • git checkout <branch> / git switch <branch> switch branches
  • git checkout -b <branch> / git switch -c <branch> create a branch and switch to it

With relative references you can point to a location without knowing the commit hash.

HEAD^ points to the parent of the current commit, and HEAD~2 points to the commit two steps up from the current location.

  • HEAD^ parent of the current commit
  • HEAD~3 the commit three steps up from the current location
  • <branch>^2 the second parent of a merge commit

4. Merging

The main ways to combine branches are merge and rebase. Both combine work but leave different commit graphs.

merge combines another branch's changes into the current branch. If a fast-forward is possible, only the branch pointer moves with no merge commit, and --no-ff forces a merge commit. The merge in the figure above is a non-fast-forward case.

rebase restacks the working commits after a new base branch so they form a single line.

  • git merge <branch> merge the specified branch into the current branch
  • git merge --continue continue a merge after resolving conflicts
  • git merge --abort abort an in-progress merge
  • git merge --no-ff <branch> create a merge commit even when fast-forward is possible
  • git rebase <newbase> restack the current branch's commits onto a new base
  • git rebase <newbase> <branch> relocate the specified branch onto a new base

Use rebase carefully on commits you have already shared, because the commit hashes change.

5. Undoing and selecting commits

Of the undo commands, reset moves the branch pointer into the past, while revert records the undo as a new commit.

reset changes where the current branch points. revert leaves the existing commit in place and adds a new commit with the opposite changes.

  • git reset HEAD~1 move the current branch one commit back
  • git reset --soft HEAD~1 undo only the commit, keep staging
  • git reset --mixed HEAD~1 undo the commit and staging, keep working files
  • git reset --hard HEAD~1 undo the commit, staging, and working file changes
  • git revert HEAD create a new commit that undoes the changes

Local commits you have not yet shared can be cleaned up with reset, while commits already shared are safer to undo with revert.

cherry-pick picks only the changes of a specific commit from another branch and appends it as a new commit after the current branch.

  • git cherry-pick <commit1> <commit2> apply only specific commits to the current branch

cherry-pick is safer to run with a clean working tree. If a conflict occurs, resolve it and then continue.

rebase -i reorganizes the order, messages, and inclusion of recent commits.

  • git rebase -i HEAD~3 edit the order, messages, and inclusion of recent commits

The commonly used options are pick, reword, edit, squash, fixup, and drop. Here you decide whether to keep a commit, change its message, combine it, or remove it.

6. Marking and temporary storage

A tag is a fixed label attached to an important commit. stash is for briefly setting aside changes that are not quite ready to commit.

  • git tag <tag> add a lightweight tag at the current HEAD
  • git tag <tag> <commit> add a lightweight tag at the specified commit
  • git tag -a <tag> -m "message" create an annotated tag
  • git push origin <tag> upload a specific tag to the remote
  • git push origin --tags upload all local tags to the remote
  • git describe describe the current location relative to the nearest annotated tag
  • git describe --tags describe relative to the nearest tag, including lightweight tags

stash lets you temporarily store in-progress changes and reapply them later.

  • git stash temporarily save working directory/staging changes of tracked files
  • git stash -u save including untracked files
  • git stash -a save including ignored files
  • git stash list list stashes
  • git stash pop reapply saved changes and try to remove them from the stash list

7. Remote repositories and collaboration

A remote repository is a place to share my work and receive others' work. You need to know how fetch, pull, and push differ.

fetch only pulls the remote state into the remote-tracking branches. pull integrates the fetched remote changes into the current branch, and push uploads local commits to the remote repository.

  • git clone <url> clone a remote repository locally
  • git fetch fetch remote changes without merging them into the current branch
  • git pull after fetching, integrate the remote changes into the current branch
  • git pull --rebase after fetching, integrate by rebase instead of merge
  • git pull --no-rebase after fetching, integrate by merge
  • git pull --ff-only integrate only when a fast-forward is possible
  • git push upload local commits to the remote
  • git push -u <remote> <branch> push and set up the upstream tracking relationship

Remote-tracking branches are usually shown as origin/main. The actual remote branch lives in the remote repository, and the local origin/main remembers the remote state as of the last communication. In learning environments it may appear shortened as o/main.

  • git checkout -b <branch> <remote>/<branch> create and track a new local branch based on a remote branch
  • git branch -u <remote>/<branch> <local-branch> make an existing local branch track a remote branch
  • git fetch <remote> <source>:<destination> fetch a remote source into a local destination
  • git pull <remote> <source>:<destination> fetch and then integrate into the current branch
  • git push <remote> <source>:<destination> upload a local source to a remote destination
  • git push <remote> :<branch> delete a remote branch

8. Common workflows

Working on a new feature

Terminal
git checkout -b <branch>
git add .
git commit -m "message"
git push -u <remote> <branch>

When the remote is ahead and push is rejected

Terminal
git pull --rebase
git push

or

Terminal
git fetch
git rebase <remote>/<branch>
git push

When you accidentally committed to main

If you have not yet pushed to the remote main and have no uncommitted working changes, you can create a new branch first and then reset main back to the remote state, as below. --hard can also undo working directory changes, so check git status first.

Terminal
git status
git branch <new-branch>
git reset --hard <remote>/main
git switch <new-branch>
git push -u <remote> <new-branch>

If the commit has already been pushed to the remote main, it is safer to use revert instead of this workflow, or to handle it separately after agreeing with the team.

9. Commands to be careful with

  • git reset --hard <where> actual file changes can be lost
  • git push <remote> :<branch> delete a remote branch
  • git branch -f <branch> <where> force-move a branch pointer
  • git rebase <shared-branch> can change the hashes of shared commits
  • git commit --amend rewrite the last commit as a new commit

Esc

Search titles, content and code.