Git Step-5 : 원복(되돌리기)

1. Reset

현재 branch가 가르키고 있는 커밋을 지정된 커밋으로 변경하는 명령어입니다.

remote repository에 push된 커밋은 reset으로 되돌리면 안됩니다. local repository에서 작업 중인 커밋들에 대해서 reset을 적용하고 push로 remote repository에 공유된 커밋을 되돌리기 할 때는 revert 명령어를 사용해야 합니다.

$git git reset -h
usage: git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]
   or: git reset [-q] [<tree-ish>] [--] <pathspec>...
   or: git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]
   or: git reset --patch [<tree-ish>] [--] [<pathspec>...]

    -q, --quiet           be quiet, only report errors
    --mixed               reset HEAD and index
    --soft                reset only HEAD
    --hard                reset HEAD, index and working tree
    --merge               reset HEAD, index and working tree
    --keep                reset HEAD but keep local changes
    --recurse-submodules[=<reset>]
                          control recursive updating of submodules
    -p, --patch           select hunks interactively
    -N, --intent-to-add   record only the fact that removed paths will be added later
    --pathspec-from-file <file>
                          read pathspec from file
    --pathspec-file-nul   with --pathspec-from-file, pathspec elements are separated with NUL character
$git reset [--soft | --mixed | --hard]  <commit>

reset 옵션 중 자주 사용되는 –soft, –mixed, –hard는 git의 3개의 영역을 옵션에 따라 reset합니다.

current는 지금 작업중인 내용이 유지되고, reset은 지정된 commit의 내용으로 변경됩니다.

image-20210309105414618

reset 명령 및 reset을 되돌리는 방법

$git log --oneline
0e3c111 (HEAD -> develop) modify 2
92fe575 modify

//92fe575 커밋으로 리셋
$git reset --hard 92fe575
$git log --oneline
92fe575 modify

//reflog로 리셋된 이력 확인. HEAD는 92fe575 커밋을 가리키고 있음.
$git reflog
92fe575 (HEAD -> develop) HEAD@{0}: reset: moving to 92fe575

//그래서 0e3c111 커밋으로 돌아가기 위해서는 HEAD로 reset해도 돌아갈 수 없음.
$git reset --hard HEAD
HEAD is now at 92fe575 modify

/*
위에서 reset HEAD로 한번 더 reset하였기 때문에 ORIG_HEAD는 reset전 92fe575를 가리키고 있음
따라서 위에서 reset HEAD 대신 reset ORIG_HEAD로 하였으면 0e3c111로 돌아갈 순 있었지만
지금 상황에서는 ORIG_HEAD가 92fe575을 가리키고 있기 때문에 돌아갈 수 없음.
*/
$git reset --hard ORIG_HEAD
HEAD is now at 92fe575 modify

//reflog로 이전 커밋을 찾아서 reset해야됨.
$git reflog
92fe575 (HEAD -> develop) HEAD@{0}: reset: moving to ORIG_HEAD
92fe575 (HEAD -> develop) HEAD@{1}: reset: moving to HEAD   
92fe575 (HEAD -> develop) HEAD@{2}: reset: moving to 92fe575
0e3c111 HEAD@{3}: reset: moving to ORIG_HEAD
92fe575 (HEAD -> develop) HEAD@{4}: reset: moving to 92fe575  
0e3c111 HEAD@{5}: commit: modify 2

//로그에서 변경하고 싶은 HEAD번호로 reset하면 됨.
$git reset --hard HEAD@{3}
HEAD is now at 0e3c111 modify 2

$git log --oneline
0e3c111 (HEAD -> develop) modify 2
92fe575 modify

2. Revert

변경할 커밋을 지정하면 지정된 커밋을 working directory로 반영하고 revert에 대한 커밋을 생성합니다.

기존 커밋들은 그대로 유지한채 새로운 커밋을 만드는 방식이기 때문에 remote에 push로 공유된 커밋도 변경되지 않아서 안전한 방식입니다.

3개의 커밋을 생성하고 revert를 진행 해보겠습니다.

* a8d689d (HEAD -> feature-revert) hello world! !
* 3bf5737 hello world
* 8df82d2 hello
*   96c1620 (develop) Merge branch 'release' into develop

$cat revert-test.java 
public class Test() {
    public void main() {
        System.out.println("hello world!!!");
    }
}

만약 3bf5737 커밋인 hello world로 돌아가고 싶다면 revert 대상 커밋을 3bf5737로 지정하는 것이 아니라 변경 지점인 a8d689d 커밋을 revert해야됩니다.

$git revert HEAD
[feature-revert c69ff86] Revert "hello world! !"
 1 file changed, 1 insertion(+), 1 deletion(-)

$git log --oneline --graph --all
* c69ff86 (HEAD -> feature-revert) Revert "hello world! !"
* a8d689d hello world! !
* 3bf5737 hello world
* 8df82d2 hello
*   96c1620 (develop) Merge branch 'release' into develop

그럼 만약 실수로 3bf5737 커밋을 지정하면 어떻게 될까요?

$git revert HEAD^
Auto-merging revert-test.java
CONFLICT (content): Merge conflict in revert-test.java
error: could not revert 3bf5737... hello world
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'

CONFLICT가 납니다..

public class Test() {
    public void main() {
<<<<<<< HEAD
        System.out.println("hello world!!!");
=======
        System.out.println("hello");
>>>>>>> parent of 3bf5737... hello world
    }
}

CONFLICT 내용을 수정하고 commit하면 정상적으로 revert 됩니다.

$git commit -am "Conflict revert"
[feature-revert c256d42] Conflict revert
 1 file changed, 4 insertions(+)
 
$git log --oneline --graph --all
* 0a2018c (HEAD -> feature-revert) Conflict revert
* a8d689d hello world! !
* 3bf5737 hello world
* 8df82d2 hello
*   96c1620 (develop) Merge branch 'release' into develop

그럼 만약 a8d689d커밋에서 8df82d2커밋으로 갈려면 어떻게 해야될까요?

revert를 두번 하면 됩니다. git revert a8d689d , git revert 3bf5737

위에서 a8d689d 를 revert 한 상황이니 3bf5737를 revert 해보겠습니다.

$git revert 3bf5737  
[feature-revert dc35f6f] Revert "hello world"
 1 file changed, 1 insertion(+), 1 deletion(-)
 
$cat revert-test.java 
public class Test() {
    public void main() {
        System.out.println("hello");
    }
}

위 상황을 reset으로 처리하면 아래와 같습니다.

* a8d689d (HEAD -> feature-revert) hello world! !
* 3bf5737 hello world
* 8df82d2 hello
*   96c1620 (develop) Merge branch 'release' into develop

$git reset --hard HEAD^         
HEAD is now at 3bf5737 hello world

$git log --oneline --graph --all
* 3bf5737 (HEAD -> feature-revert) hello world
* 8df82d2 hello
*   96c1620 (develop) Merge branch 'release' into develop

최신 커밋이 현재 branch에서 제외됩니다.

댓글남기기