반응형
Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

테크매니아

commit amend, checkout, reset, revert 본문

카테고리 없음

commit amend, checkout, reset, revert

SciomageLAB 2024. 10. 3. 16:45
반응형

commit amend, checkout, reset, revert

개발하다 보면 커밋 메세지를 잘못 썼거나 새 파일을 추가하지 않았거나 실수로 파일을 추가/삭제 하는 경우가 종종 있다. 이런 경우 commit --amend 플래그를 이용하거나, reset/revert 명령어를 이용하여 해결할 수 있는데 각각의 명령어 활용법을 정리하려고 한다.

커밋 수정: --amend 플래그

아래처럼 커밋을 했다고 하자. commit 메세지에 'First comit' 이라고 오타가 생겼다. (또는 미처 add 하지 못한 파일이나 파일을 지워야 하거나)

dsparch@DESKTOP-EJ52IEM:/mnt/c/Users/Minds/source/repos/test-project$ git log
commit 3d58f885061c773e648a6c5bdea72ae028f0a803 (HEAD -> master, origin/master)
Author: jisu.choi <jisu.choi@joyfulturtles.com>
Date:   Thu Feb 24 15:53:59 2022 +0900

    Initial message. First comit

이것을 수정하는 방법은 commit 명령에 --amend 옵션을 주어서 수정을 하는 것이다. 수정을 하고 git log를 살펴보면 내용이 바뀐 커밋이 아직 원격 저장소에 푸시 되기 전이라 HEAD만 amend된 커밋을 가리키고 있는 것을 알 수 있다.

dsparch@DESKTOP-EJ52IEM:/mnt/c/Users/Minds/source/repos/test-project$ git commit --amend
[master fbc0d7b] Initial message. First commit
 Date: Thu Feb 24 15:53:59 2022 +0900
 1 file changed, 1 insertion(+)
 create mode 100644 README.md
dsparch@DESKTOP-EJ52IEM:/mnt/c/Users/Minds/source/repos/test-project$ git log
commit fbc0d7b1c7b5dc4c81e9e93d7158c9aac0b74173 (HEAD -> master)
Author: jisu.choi <jisu.choi@joyfulturtles.com>
Date:   Thu Feb 24 15:53:59 2022 +0900

    Initial message. First commit

이 상태에서 push -f 옵션을 주고 덮어쓰면 아래처럼 깔끔하게 로그가 변해있는 것을 볼 수 있다.

dsparch@DESKTOP-EJ52IEM:/mnt/c/Users/Minds/source/repos/test-project$ git push origin master -f
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 258 bytes | 258.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To ../test-origin/
 + 3d58f88...fbc0d7b master -> master (forced update)
dsparch@DESKTOP-EJ52IEM:/mnt/c/Users/Minds/source/repos/test-project$ git log
commit fbc0d7b1c7b5dc4c81e9e93d7158c9aac0b74173 (HEAD -> master, origin/master)
Author: jisu.choi <jisu.choi@joyfulturtles.com>
Date:   Thu Feb 24 15:53:59 2022 +0900

    Initial message. First commit

만약 이미 원격에 푸시가 되었는데 로컬에서 amend를 하여 -f 옵션으로 다시 쓰는 경우는 문제의 소지가 있다. 이미 푸시된 커밋을 누군가 받아갔다면 받아간 사람은 사라진 커밋을 받은채로 원격저장소에서 수정된 커밋을 다시 받아야 한다. 이 방법은 로컬에서 혼자 실수했을 때 좋은 방법이다. amend도 결국 새로운 커밋 ID를 할당하므로..

그리고 여담으로 되도록이면 --force보다 --force-with-lease를 사용하는게 좋다. --force-with-lease는 원격저장소에 누군가 새로운 커밋을 올려 놓은 경우 원격과 내 로컬 저장소의 상태가 일치 하지 않으므로 fetch를 통해 저장소에 정보를 반영할 것을 권고한다.

파일 복구1 : git checkout

파일을 실수로 삭제했거나 이전 내용으로 복구 시키고 싶다면 checkout 명령을 쓰면 된다.

git checkout <commit-hash> -- <file-path> 
# 또는
git checkout <commit-hash>~<number of commit to go back> -- <file-path>

-- 이 파일 경로 앞에 붙는데 이 처럼 두 개의 하이픈을 연달아 쓰는 것은 커밋 해시와 파일 경로 사이의 구분이다. --은 다른데서도 많이 볼 수 있는 표기인데 보통 '-f' 같이 실수로 옵션처럼 파일명이 만들어져서 명령어 칠 때 파일명으로 못 쓰이는 경우 그냥 이름이 '-f' 인거야 라는 표시로 쓴다. 예를 들면 rm -- -f 명령은 -f 라는 파일명을 가진 파일을 지우라는 것이다.

[Fri Feb 25 14:19:04]  [job]: 0 [dir]: ~/test
dsparch@DESKTOP-EJ52IEM$ git lg
* d58d17f (HEAD -> refs/heads/master, refs/remotes/origin/master) I remove file by mistake
* ca5b46d Second commit
* 91555f0 Something

[Fri Feb 25 14:19:07]  [job]: 0 [dir]: ~/test
dsparch@DESKTOP-EJ52IEM$ git checkout ca5b46d~1 -- This_file_needs_to_be_deleted.txt

[Fri Feb 25 14:19:58]  [job]: 0 [dir]: ~/test
dsparch@DESKTOP-EJ52IEM$ ls
README.md  This_file_needs_to_be_deleted.txt

위의 예시는 가장 최근인 d58d17f 커밋에서 This_file_needs_to_be_deleted.txt 파일을 실수로 지우고 커밋한 경우, 이전 커밋인 ca5b46d로 돌아가서 복구하는 예시이다.

[Fri Feb 25 14:31:48]  [job]: 0 [dir]: ~/test
dsparch@DESKTOP-EJ52IEM$ git lg
* fe068a6 (HEAD -> refs/heads/master, refs/remotes/origin/master) Restore file by using checkout command
* d58d17f I remove file by mistake
* ca5b46d Second commit
* 91555f0 Something

[Fri Feb 25 14:31:50]  [job]: 0 [dir]: ~/test
dsparch@DESKTOP-EJ52IEM$ git rm This_file_needs_to_be_deleted.txt
rm 'This_file_needs_to_be_deleted.txt'

[Fri Feb 25 14:31:57]  [job]: 0 [dir]: ~/test
dsparch@DESKTOP-EJ52IEM$ git status -sb
## master...origin/master
D  This_file_needs_to_be_deleted.txt
[Fri Feb 25 14:42:51]  [job]: 0 [dir]: ~/test
dsparch@DESKTOP-EJ52IEM$ git reset HEAD --hard
HEAD is now at fe068a6 Restore file by using checkout command

[Fri Feb 25 14:42:59]  [job]: 0 [dir]: ~/test
dsparch@DESKTOP-EJ52IEM$ ls
README.md  This_file_needs_to_be_deleted.txt

파일 복구 2 : git reset

checkout말고도 reset으로 바꾸는 방법이 있다. git reset은 HEAD를 특정 상태로 돌리는 명령어 이다. 예를 들면 방금 만든 파일을 git rm으로 지워서 실제 파일도 사라지고 index에서도 사라져서 checkout으로도 복구가 안되는 경우에는 staging 상태를 되돌리면 된다.

reset과 checkout 차이

reset은 HEAD가 가리키는 브랜치가 develop일 경우에 develop 브랜치와 함께 커밋을 옮겨 간다. checkout은 HEAD가 가리키는 브랜치가 바뀐다. 즉 두 명령어의 차이는 HEAD가 가리키는 브랜치도 옮기느냐 마느냐 차이다.

아래 그림 좌측 상황에서 git reset commitA 를 실행하면 HEAD가 가리키는 브랜치가 develop이기 때문에 develop도 commitA를 가리키게 되지만 git checkout commitA를 할 경우 HEAD 가리키는 브랜치나 커밋이 commitA로 가고 develop은 그대로 남게 된다.

reset[그림1] reset과 checkout 차이### --soft, --mixed, --hard 차이점

--soft는 HEAD가 가리키는 브랜치를 옮긴다. 단순히 옮겼기 때문에 commit메세지만 다르게 표시되고 git status 명령어로 볼 수 있는 인덱스 트리나 ls로 명령어로 확인할 수 있는 작업 트리의 파일들은 그대로 남아있게 된다.

reset[그림2] --soft, --mixed, --hard--mixed는 한발짝 더 나아가서 index 트리도 업데이트 한다. 그래서 로컬에 있는 작업트리의 파일 내용들은 변하지 않지만 git status로 staging 내용을 보면 변해 있게된다.

--hard는 한발짝 더더 나아가서 작업트리의 파일 내용들을 실제로 반영해서 바꾼다.

파일복구 더 쉽게 : git restore

git restore --source <commit-hash> -- <file-path>
# Ex. 
git restore --source d526af6 -- This_file_needs_to_be_deleted.txt

커밋 되돌리기: git revert

git reset --mixed와 같은 기능이지만 다른 점은 복원 후 커밋을 하나 더 만드는 점이 다. 그래서 과거 커밋을 되돌릴 때 현재 커밋과 충돌나면 merge 하는 과정이 따라온다. revert는 이미 원격 저장소에 잘못된 코드가 올라가서 되돌린 커밋을 푸시하려고 할 때 쓴다. 이미 엎질러진 커밋을 다들 공유하고 있기 때문에 섣불리 커밋을 엎애거나 수정하는 것보다 새로운 커밋을 생성하는게 안전하기 때문이다.

git revert <commit-hash>

아래처럼 두번째 커밋으로 되돌아가야 할때

dsparch@DESKTOP-EJ52IEM$ git lg
* fd1704e (HEAD -> refs/heads/master, refs/remotes/origin/master) This is wrong code
* 92bf210 Second commit : second patch applied
* 84606f6 First commit : normal patch applied

revert 명령어를 수행해주고 conflict난다면 수정 후 add하면 된다.

git revert 92bf210

# 출력 예시
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
error: could not revert 92bf210... Second commit : second patch applied
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'

git add README.md
git revert --continue
# 출력 예시
[master 0068955] Revert Second commit : second patch applied
 1 file changed, 3 insertions(+)

log를 통해 확인해보면 커밋을 되돌린 뒤 새로운 커밋이 추가된 것을 볼 수 있다.

* 0068955 (HEAD -> refs/heads/master) Revert Second commit : second patch applied
* fd1704e (refs/remotes/origin/master) This is wrong code
* 92bf210 Second commit : second patch applied
* 84606f6 First commit : normal patch applied

참고문헌

Microsoft learn - Git 프로젝트를 만들고 수정하는 방법

[그림1], [그림2]

Force push with care

반응형