git reset --hard를 서브 디렉토리로 만드는 방법?


197

업데이트 : Git 2.23 (2019 년 8 월) git restore에는 이것을 수행하는 새로운 명령 이 있습니다. 허용 된 답변을 참조하십시오 .

업데이트 : 이것은 Git 1.8.3에서보다 직관적으로 작동 합니다. 내 대답을 참조하십시오 .

다음 유스 케이스를 상상해보십시오 .Git 작업 트리의 특정 하위 디렉토리에서 모든 변경 사항을 제거하고 다른 모든 하위 디렉토리는 그대로 유지하려고합니다.

이 작업에 적합한 Git 명령은 무엇입니까?

아래 스크립트는 문제를 보여줍니다. How to make files주석 아래에 적절한 명령을 삽입하십시오. 현재 명령은 a/c/ac스파 스 체크 아웃에서 제외되어야 하는 파일 을 복원합니다 . 내가주의 하지 않는 명시 적으로 복원 할 a/a하고 a/b, 난 단지 "알고" a아래 모든 것을 복원 할. 편집 : 그리고 나는 또한 "알지 못 b하거나 " 와 같은 레벨에 다른 디렉토리가 있습니다 a.

#!/bin/sh

rm -rf repo; git init repo; cd repo
for f in a b; do
  for g in a b c; do
    mkdir -p $f/$g
    touch $f/$g/$f$g
    git add $f/$g
    git commit -m "added $f/$g"
  done
done
git config core.sparsecheckout true
echo a/a > .git/info/sparse-checkout
echo a/b >> .git/info/sparse-checkout
echo b/a >> .git/info/sparse-checkout
git read-tree -m -u HEAD
echo "After read-tree:"
find * -type f

rm a/a/aa
rm a/b/ab
echo >> b/a/ba
echo "After modifying:"
find * -type f
git status

# How to make files a/* reappear without changing b and without recreating a/c?
git checkout -- a

echo "After checkout:"
git status
find * -type f

3
어떤 약 git stash && git stash drop?
CharlesB

1
무엇에 대해 git checkout -- /path/to/subdir/?
iberbeu

3
@CharlesB : git stash경로 인수를 받아들이지 않습니다 ...
krlmlr

@iberbeu : 아뇨. 스파 스 체크 아웃에서 제외 된 파일도 추가합니다.
krlmlr

1
@CharlesBailey : 그렇다면 왜 "신뢰할 수있는 공식 출처에서 답을 찾는 중입니다."라는 라디오 버튼이 있습니까? 현상금 대화 상자에서? 직접 입력하지 않았습니다! 또한 "git reset subdirectory"(따옴표없이) 인터넷 검색을 시도하고 처음 세 위치에 무엇이 있는지 확인하십시오. kernel.org 메일 링리스트에 대한 메시지를 찾기가 더 어려울 것입니다. -또한이 동작이 버그인지 또는 기능인지는 아직 명확하지 않습니다.
krlmlr

답변:


164

Git 2.23 (2019 년 8 월)에서는 새로운 명령이 있습니다.git restore

git restore --source=HEAD --staged --worktree -- aDirectory
# or, shorter
git restore -s@ -SW  -- aDirectory

그렇게하면 인덱스와 작업 트리가 모두 HEAD내용과 같은 내용으로 대체 reset --hard되지만 특정 경로가됩니다.


원래 답변 (2013)

( Dan Fabulich에 의해 논평 된) 참고 사항 :

  • git checkout -- <path> 강제 재설정을 수행하지 않습니다. 작업중인 트리 내용을 준비된 내용으로 바꿉니다.
  • git checkout HEAD -- <path>경로에 대한 하드 리셋을 수행하여 인덱스와 작업 트리를 모두 HEAD커밋 의 버전으로 바꿉니다 .

대답 으로 Ajedi32 , 모두 체크 아웃 양식 대상 개정에서 삭제 된 파일을 제거하지 않습니다 .
작업 트리에 HEAD에 존재하지 않는 추가 파일이 있으면 파일을 git checkout HEAD -- <path>제거하지 않습니다.

참고 : git checkout --overlay HEAD -- <path>(Git 2.22, Q1 2019) 를 사용하면 인덱스와 작업 트리에 나타나지만 포함되지 않은 파일은 정확하게 <tree-ish>일치하도록 제거됩니다 <tree-ish>.

그러나 체크 아웃은 존중 git update-index --skip-worktree"에서 언급 한 바와 같이, (당신이 무시하고자하는 디렉토리에 대해) 왜 제외 않는 파일 내 자식 스파 스 체크 아웃?에 다시 출현 계속 ".


1
명확히하십시오. 이후 git checkout HEAD -- .에 스파 스 체크 아웃에서 제외 된 파일이 다시 나타납니다. 무엇입니까 git update-index --skip-worktree어떻게해야?
krlmlr

@krlmlr skip-worktree 또는 unchanged는 git에 대해 색인에 "보이지 않는"항목을 작성하는 두 가지 방법입니다 : fallengamer.livejournal.com/93321.html , stackoverflow.com/q/13630849/6309stackoverflow. com / a / 6139470 / 6309
VonC

@krlmlr 해당 링크는 'skipped-worktree'로 표시되면 체크 아웃이 해당 항목을 여전히 복원하는지 확인하기위한 포인터입니다.
VonC

죄송합니다. 현재 작업이 너무 복잡합니다. 독점이 아닌 포괄적 인 재설정을 원합니다. Git에서 이것을 할 수있는 좋은 방법이 있습니까?
krlmlr

@krlmlr no :를 수행하는 것이 가장 좋습니다. git checkout HEAD -- <path>그런 다음 복원 된 디렉토리 (그러나 스파 스 체크 아웃에서 여전히 선언 된 디렉토리)를 삭제하십시오.
VonC

126

친절하게 기능과 호환성 스위치를 구현 한 Git 개발자 Duy Nguyen에 따르면 Git 1.8.3 에서 다음과 같이 작동합니다 .

git checkout -- a

( a하드 리셋하려는 디렉토리가 있습니다). 원래 동작은 다음을 통해 액세스 할 수 있습니다.

git checkout --ignore-skip-worktree-bits -- a

5
Git 개발팀과 후속 작업을 수행해 주셔서 감사합니다.
Dan Cruz

13
이 경우 "a"는 되돌리려는 디렉토리를 의미하므로 되돌리려는 디렉토리에 있으면 명령은 현재 디렉토리를 의미하는 git checkout -- .위치에 있어야합니다 ..
TheWestIsThe ...

5
내 입장에서 한 가지 의견은 폴더를 먼저 스테이지 해제해야한다는 것입니다 git reset -- a(여기서 a는 재설정하려는 디렉토리입니다)
Boyan

git reset --hard전체 리포지토리 를 원할 때도 그렇지 않습니까?
krlmlr

해당 디렉토리에 새 파일을 추가 한 경우 rm -rf a이전에 수행하십시오 .
Tobias Feil

30

바꿔보십시오

git checkout -- a

git checkout -- `git ls-files -m -- a`

버전 1.7.0부터 Git 은 skip-worktree 플래그를 사용 합니다.ls-files

테스트 스크립트 실행 (약간의 약간의 조정이 git commit...에서 git commit -qand git status로 변경됨 git status --short) 출력 :

Initialized empty Git repository in /home/user/repo/.git/
After read-tree:
a/a/aa
a/b/ab
b/a/ba
After modifying:
b/a/ba
 D a/a/aa
 D a/b/ab
 M b/a/ba
After checkout:
 M b/a/ba
a/a/aa
a/c/ac
a/b/ab
b/a/ba

제안 된 checkout변경 출력으로 테스트 스크립트를 실행합니다 .

Initialized empty Git repository in /home/user/repo/.git/
After read-tree:
a/a/aa
a/b/ab
b/a/ba
After modifying:
b/a/ba
 D a/a/aa
 D a/b/ab
 M b/a/ba
After checkout:
 M b/a/ba
a/a/aa
a/b/ab
b/a/ba

좋은 데요 그러나 우선 git checkout"skip-worktree"비트를 존중 해서는 안 됩니까?
krlmlr

skip-worktree 플래그가 사용되었음을 빠르게 검토 checkout.c하고 tree.c공개하지 않습니다.
Dan Cruz

이 명령에 대해 bash 별명을 설정해야 할지라도 실제로 유용 할 정도로 간단합니다. Duy Nguyen은 내 메시지에 Git 메일 링리스트에 답장 을 보냈다 .보다 사용자 친화적 인 대안이 곧 나올지 살펴 보자.
krlmlr

18

단순히 변경 사항을 버리는 경우 다른 답변에서 제안한 git checkout -- path/또는 git checkout HEAD -- path/명령이 효과적입니다. 그러나 디렉토리를 HEAD 이외의 수정본으로 재설정하려는 경우 해당 솔루션에 중요한 문제가 있습니다. 대상 수정본에서 삭제 된 파일을 제거하지 않습니다.

대신 다음 명령을 사용하기 시작했습니다.

git diff --cached commit -- subdir | git apply -R --index

이것은 대상 커밋과 인덱스 사이의 차이점을 찾은 다음 해당 디렉토리를 작업 디렉토리와 색인에 반대로 적용하여 작동합니다. 기본적으로 이는 색인의 내용이 지정한 개정의 내용과 일치 함을 의미합니다. 사실 git diff경로 인수를 사용하면 특정 파일이나 디렉토리에이 효과를 제한 할 수 있습니다.

이 명령은 상당히 길고 자주 사용하려고 계획하기 때문에 이름이 지정된 별칭을 설정했습니다 reset-checkout.

git config --global alias.reset-checkout '!f() { git diff --cached "$@" | git apply -R --index; }; f'

다음과 같이 사용할 수 있습니다.

git reset-checkout 451a9a4 -- path/to/directory

아니면 그냥 :

git reset-checkout 451a9a4

나는 당신의 의견을 어제 보았고 오늘 그것을 실험했습니다. 당신의 별명이 도움이됩니다. +1
VonC

이 옵션 git checkout --overlay HEAD -- <path>은 @VonC가 그의 답변에서 언급 한 명령 과 어떻게 비교 됩니까?
Ehtesh Choudhury

1
@EhteshChoudhury git checkout --overlay HEAD -- <path>아직 공개되지 않은 메모 (Git 2.22는 2019 년 2 분기에 출시 될 예정)
VonC

5

나는 제외하고 자식과 함께 아무것도 할 방법에 대해 아무 생각이 없기 때문에, 여기에 끔찍한 옵션을 제공거야 add commit하고 push, 여기에 내가 하위 디렉토리를 "복귀"방법은 다음과 같습니다

로컬 PC에서 새 저장소를 시작하고 코드를 복사하려는 커밋으로 모든 것을 되 돌린 다음 해당 파일을 내 작업 디렉토리, 즉 add commit pushvoila 로 복사했습니다 . 플레이어를 미워하지 말고, 토발즈 씨가 우리보다 똑똑하다는 것을 미워하십시오.


4

재설정은 일반적으로 모든 것을 변경하지만 git stash유지하려는 항목을 선택하는 데 사용할 수 있습니다 . 언급했듯이 stash경로를 직접 수락하지 않지만 --keep-index플래그 와 함께 특정 경로를 유지하는 데 계속 사용할 수 있습니다 . 귀하의 예에서는 b 디렉토리를 숨기고 다른 모든 것을 재설정합니다.

# How to make files a/* reappear without changing b and without recreating a/c?
git add b               #add the directory you want to keep
git stash --keep-index  #stash anything that isn't added
git reset               #unstage the b directory
git stash drop          #clean up the stash (optional)

그러면 스크립트의 마지막 부분에서 다음과 같은 결과가 출력됩니다.

After checkout:
# On branch master
# Changes not staged for commit:
#
#   modified:   b/a/ba
#
no changes added to commit (use "git add" and/or "git commit -a")
a/a/aa
a/b/ab
b/a/ba

나는 이것이 목표 결과라고 생각합니다 (b는 수정 된 상태로 유지되고 a / * 파일은 다시 반환되며 a / c는 다시 작성되지 않습니다).

이 접근 방식은 매우 유연하다는 이점이 있습니다. 디렉토리에 특정 파일 만 추가하고 다른 파일은 추가하지 않고 세분화 할 수 있습니다.


멋지지만을 git add제외한 모든 것이 필요합니다 a. 실제로 어려운 것 같습니다.
krlmlr

1
@krlmlr 그렇지 않습니다. git add .그런 git reset a다음를 제외한 모든 것을 추가 할 수 있습니다 a.
Jonathan Wren

1
@krlmlr 또한 git add삭제 된 파일을 추가하지 않는다는 점에 주목할 가치가 있습니다. 따라서 삭제 된 파일 만 복구 git add .하는 경우 수정 된 파일은 모두 추가하지만 삭제 된 파일은 추가하지 않습니다.
Jonathan Wren

3

서브 디렉토리의 크기가 특별히 크지 않고 CLI에서 벗어나 려면 서브 디렉토리 를 수동으로 재설정 하는 빠른 솔루션이 있습니다 .

  1. 마스터 브랜치로 전환하고 재설정 할 서브 디렉토리를 복사하십시오.
  2. 이제 기능 분기로 다시 전환하고 서브 디렉토리를 1 단계에서 방금 작성한 사본으로 바꾸십시오.
  3. 변경 사항을 적용하십시오.

건배. 피처 브랜치의 하위 디렉토리를 마스터 브랜치의 디렉토리와 동일하게 수동으로 재설정하기 만하면됩니다 !!


이 답변에 대한 투표를 보지 못했지만 때로는 이것이 가장 간단한 성공 경로입니다.
Sue Spence

1

Ajedi32대답 은 내가 찾던 것이지만 일부 커밋의 경우이 오류가 발생했습니다.

error: cannot apply binary patch to 'path/to/directory' without full index line

디렉토리의 일부 파일이 이진 파일이기 때문일 수 있습니다. git diff 명령에 '--binary'옵션을 추가하면 수정되었습니다.

git diff --binary --cached commit -- path/to/directory | git apply -R --index

0

이건 어떤가요

subdir=thesubdir
for fn in $(find $subdir); do
  git ls-files --error-unmatch $fn 2>/dev/null >/dev/null;
  if [ "$?" = "1" ]; then
    continue;
  fi
  echo "Restoring $fn";
  git show HEAD:$fn > $fn;
done 
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.