git에서 단계적 변경 사항 만 저장하면 가능합니까?


364

단계적 변경 사항 만 숨길 수있는 방법이 있습니까? 내가 겪고있는 시나리오는 주어진 시간에 몇 가지 버그에 대해 작업했으며 여러 단계되지 않은 변경 사항이있는 경우입니다. 이 파일들을 개별적으로 준비하고 .patch 파일을 만든 다음 코드가 승인 될 때까지 숨기고 싶습니다. 이렇게하면 승인되면 전체 (현재) 세션을 숨기고 해당 버그를 팝하고 코드를 푸시 할 수 있습니다.

나는 이것에 대해 잘못 가고 있습니까? 프로세스를 단순화하기 위해 git이 다른 방식으로 작동하는 방식을 이해하고 있습니까?


그렇습니다. 아마도이 상황에 들어가기 위해 잘못된 일을하고있을 것입니다. 여전히 유용한 질문입니다. 다음 수정을 시작하기 전에 숨기거나 분기해야합니다. 접선 형 대답 stackoverflow.com/a/50692885 는 아마도 git에서 이것을 처리하는 더 좋은 방법 일 것입니다. 업스트림에서 커밋을 가져온 경우 스 태쉬를 가지고 노는 것은 종종 내 작업 영역에 이상한 일을합니다.
Samuel Åslund 8

답변:


470

예, DOUBLE STASH로 가능합니다

  1. 숨겨야 할 모든 파일을 준비하십시오.
  2. 를 실행하십시오 git stash --keep-index. 이 명령은 모든 변경 사항 ( 스테이지 및 스테이지 되지 않은) 으로 숨김을 작성 하지만 작업 디렉토리에는 스테이징 된 변경 사항이 남아 있습니다 (여전히 스테이징 됨).
  3. 운영 git stash push -m "good stash"
  4. 이제 당신은 "good stash"했다 만 파일을 개최 .

이제 숨김 전에 준비되지 않은 파일이 필요한 경우 첫 번째 숨김 (로 만든 것--keep-index )을 적용하면 숨김 파일을 제거 할 수 있습니다 "good stash".

즐겨


하위 모듈이 변경되지 않은 경우에도 변경 사항을 보관합니다. 이 주위에 방법이 있습니까?
rluks

1
이것은 어떻게 든 모든 새 파일 (스테이지조차도)을 버렸습니다.
Aurimas

8
@Aurimas, 새 파일을 숨기려면 -u스위치 를 사용해야합니다 .
Gyromite

2
첫 번째 숨김을 다시 적용하고 모든 변경 사항을 다시 가져 오면 스테이지없는 변경 사용 git stash apply --index옵션 에만 관심이있을 수 있습니다 . 이것은 (단계적) 상태를 유지하려고 시도합니다. 작업 트리에서 원하지 않는 변경 사항을 제거하는 것이 더 쉽습니다.
otomo

이 답변이 말한 것을 정확하게 수행 할 필요는 없지만 --keep-index 플래그를 아는 것이 매우 도움이되었습니다.
Aaron Krauss

128

최신 자식으로 --patch옵션을 사용할 수 있습니다

git stash push --patch  

git stash save --patch   # for older git versions

그리고 git은 파일의 각 변경 사항을 숨김에 추가하거나 묻지 않도록 요청합니다.
당신은 그냥 대답 y하거나n

이중 저장소의 UPD
별칭 :

git config --global alias.stash-staged '!bash -c "git stash --keep-index; git stash push -m "staged" --keep-index; git stash pop stash@{1}"'

이제 파일을 준비한 다음 실행할 수 git stash-staged있습니다.
결과적으로 준비된 파일은 숨김으로 저장됩니다 .

준비된 파일을 유지하지 않고 파일을 숨김 상태로 옮기려면 그런 다음 다른 별칭을 추가하고 다음을 실행할 수 있습니다 git move-staged.

git config --global alias.move-staged '!bash -c "git stash-staged;git commit -m "temp"; git stash; git reset --hard HEAD^; git stash pop"'

17
기술적으로 질문에 대답하지는 않지만 선택적 스 태싱을 달성하는 정말 좋은 기술입니다.
alexreardon

6
동의합니다. 그러나 여기 질문에 대한 아이디어는 내가 할 일을하고 싶었던 변경 사항을 스테이징하는 작업을 이미 완료했다는 것입니다 (원래는 커밋하지만 원래는 숨기고 싶습니다). 다시 한 번 해봐
Steven Lu

4
(만 수정 된 파일에 작품) 새로 생성 된 파일을 작동하지 않습니다
데릭 리앙

@DerekLiang : 새로 만든 파일은 전혀 추적되지 않습니다. -u|--include-untrackedgit-stash
Eugen Konkov

2
로부터 문서 : " 저장 :이 옵션은 찬성되지 않습니다 자식 숨김 푸시 . 그것은 pathspecs을 할 수 없으며, 비 - 옵션 인수는 메시지를 형성한다는 점에서 '숨겨 놓은 푸시'는 다르다."
Borjovsky

53

현재 준비된 것만 숨기고 다른 모든 것을 남기는 스크립트를 만들었습니다. 관련이없는 변경을 너무 많이 시작하면 정말 좋습니다. 원하는 커밋과 관련이없는 것을 스테이징하고 그냥 숨기십시오.

(시작점 Bartłomiej에게 감사합니다)

#!/bin/bash

#Stash everything temporarily.  Keep staged files, discard everything else after stashing.
git stash --keep-index

#Stash everything that remains (only the staged files should remain)  This is the stash we want to keep, so give it a name.
git stash save "$1"

#Apply the original stash to get us back to where we started.
git stash apply stash@{1}

#Create a temporary patch to reverse the originally staged changes and apply it
git stash show -p | git apply -R

#Delete the temporary stash
git stash drop stash@{1}


3
대단해! 나는 그들이 명령 행에 하나를 입력하지 않으면 숨겨 놓은 설명은 사용자에게 메시지를 표시하는 데 쥐게했습니다 gist.github.com/brookinc/e2589a8c5ca33f804e4868f6bfc18282
brookinc을

1
git 2.23.0에서는 전혀 작동하지 않습니다.
thnee

고마워, 나는 그것을 upoverted하고 여기에 별칭으로 바꿨다 : stackoverflow.com/a/60875067/430128 .
라만

33

TL; DR-- $(git diff --staged --name-only) git <pathspec>매개 변수에 추가 하십시오.

간단한 원 라이너는 다음과 같습니다.

git stash -- $(git diff --staged --name-only)

그리고 간단하게 메시지를 추가하려면 :

git stash push -m "My work in progress" -- $(git diff --staged --name-only)

v2.17.1v2.21.0.windows.1 에서 테스트

한계 :

  • 파일이 준비되지 않은 경우 모든 것이 숨겨집니다.
  • 또한 부분적으로 만 스테이징 된 파일이있는 경우 (즉, 일부 변경된 라인 만 스테이지되고 다른 변경된 라인은 스테이징되지 않은 경우) 전체 파일이 스테이징되지 않습니다 (스테이지되지 않은 라인 포함).

6
나는 이것이 설명 된 상황에서 가장 좋은 옵션이라고 생각합니다 : 이해하기 쉽고 검은 마술이 없습니다!
루이스

1
이것은 매우 깔끔합니다. 나는 별명을 만들었습니다!
Kalpesh Panchal

계속 투표해라 😉
Somo S.

@KalpeshPanchal 별명을 공유 할 수 있습니까? 탈출하는 방법을 잘 모르겠으므로 올바르게 해석하지 못합니다.
Igor Nadj


15

같은 것을 달성하기 위해 ...

  1. 작업하려는 파일 만 준비하십시오.
  2. git commit -m 'temp'
  3. git add .
  4. git stash
  5. git reset HEAD~1

팔. 원하지 않는 파일은 숨겨져 있습니다. 원하는 파일이 모두 준비되었습니다.


3
이 기억하기 쉬운 쉽게 가장 좋은 대답입니다
케빈

9

이 시나리오에서는 각 이슈마다 새로운 브랜치를 생성하는 것을 선호합니다. 접두사 temp /를 사용하므로 나중에이 분기를 삭제할 수 있음을 알고 있습니다.

git checkout -b temp/bug1

bug1을 수정 한 파일을 준비하고 커밋하십시오.

git checkout -b temp/bug2

그런 다음 필요에 따라 각 분기에서 커밋을 체리 선택하고 풀 요청을 제출할 수 있습니다.


2
멋진 스 태싱 사운드를 아는 것이 좋지만 실제로는 이것이 질식 할 가능성이 적은 접근법처럼 보입니다.
ryanjdillon

1
커밋없이 변경 사항을 가져 오려면 "git cherry-pick tmpCommit"을 사용하여 병합 커밋 또는 "git merge tmpCommit"+ "git reset HEAD ^"로 임시 커밋을 다시 가져 오십시오.
Samuel Åslund

1
이 답변이 때때로 보여 주듯이 주어진 기술로 달성하는 방법 대신 달성하고자하는 것을 직접 묻는 것이 좋습니다. 임시 지점과 체리 픽은 복잡한 상황에서 편리합니다.
구니 오즈 산

파일을 부분적으로 준비한 경우 원래 브랜치로 돌아가서 다시 팝업하기 전에 변경 사항을
숨겨야

6

왜 특정 버그에 대한 변경 사항을 커밋하고 해당 커밋과 그 이전 버전에서 패치를 작성하지 않습니까?

# hackhackhack, fix two unrelated bugs
git add -p                   # add hunks of first bug
git commit -m 'fix bug #123' # create commit #1
git add -p                   # add hunks of second bug
git commit -m 'fix bug #321' # create commit #2

그런 다음 적절한 패치를 작성하려면 다음을 사용하십시오 git format-patch.

git format-patch HEAD^^

이 두 개의 파일을 생성합니다 : 0001-fix-bug-123.patch0002-fix-bug-321.patch

또는 각 버그에 대해 별도의 분기를 만들 수 있으므로 버그 수정을 개별적으로 병합하거나 리베이스 할 수 있으며 문제가 해결되지 않으면 삭제할 수도 있습니다.


2

git stash --keep-index 좋은 해결책입니다 ... Git 2.23 (Q3 2019)에서 수정 된 제거 된 경로에서 올바르게 작동하지 않는 것을 제외하고

Thomas Gummerer ( )의 commit b932f6a (2019 년 7 월 16 일)를 참조하십시오 . ( Junio ​​C Hamano의해 병합 - 커밋 f8aee85 , 2019 년 7 월 25 일)tgummerer
gitster

stash: 제거 된 파일 처리 문제 해결 --keep-index

git stash push --keep-index 색인에 추가 된 모든 변경 사항을 색인과 디스크 모두에 보관해야합니다.

현재 파일을 인덱스에서 제거하면 올바르게 작동하지 않습니다.
**-keep-index는 디스크 에서 파일을 삭제하지 않고 현재 파일을 복원합니다. **

git checkout오버레이가없는 모드에서 ' '를 사용 하여 인덱스와 작업 트리를 충실하게 복원 하여 해당 동작을 수정하십시오 .
이것은 또한 코드를 단순화합니다.

추적되지 않은 파일이 색인에서 삭제 된 파일과 이름이 같은 경우 추적되지 않은 파일을 덮어 씁니다.


2

힘내에서 인덱스 (단계적 변경 사항) 만 숨기는 것이보다 어렵습니다. @Joe의 대답 이 잘 작동한다는 것을 알았 으며 약간의 변형을이 별칭으로 바꿨습니다.

stash-index = "!f() { \
  git stash push --quiet --keep-index -m \"temp for stash-index\" && \
  git stash push \"$@\" && \
  git stash pop --quiet stash@{1} && \
  git stash show -p | git apply -R; }; f"

그것은 밀어 은 혼자 변화를 개최 떠나는 개최하고 임시 은닉에 unstaged 변경됩니다. 그런 다음 단계적 변경 사항을 숨김으로 푸시합니다. 이는 숨김 상태입니다. --message "whatever"이 숨김 명령에 추가되는 것과 같이 별명에 전달 된 인수 마지막으로 임시 숨김을 팝하여 원래 상태를 복원하고 임시 숨김을 제거한 다음 최종 패치 애플리케이션을 통해 작업 디렉토리에서 숨김 변경 사항을 "제거"합니다.

비 스테이징 된 변경 (별칭 stash-working) 만 숨기는 문제의 반대 는 이 답변을 참조하십시오 .


1

한 번에 여러 버그를 해결해야합니까? "한 번에"라는 말은 "여러 개의 버그에 대해 동시에 파일을 편집 한 파일"을 의미합니다. 당신이 절대적으로 그것을 필요로하지 않는 한, 나는 당신의 환경에서 한 번에 하나의 버그에 대해서만 노력할 것입니다. 이렇게하면 복잡한 숨김 / 단계를 관리하는 것보다 훨씬 쉬운 로컬 분기 및 리베이스를 사용할 수 있습니다.

마스터가 커밋 B에 있다고 가정 해 봅시다. 이제 버그 # 1에서 작업하십시오.

git checkout -b bug1

이제 지점 bug1에 있습니다. 코드를 변경하고 커밋하고 코드 검토를 기다립니다. 이것은 로컬이므로 다른 사람에게 영향을 미치지 않으며 git diffs에서 패치를 작성하기에 충분히 쉬워야합니다.

A-B < master
   \
    C < bug1

지금 당신은 bug2에서 일하고 있습니다. 로 다시 마스터로 돌아 갑니다git checkout master . 새 지점을 만드십시오 git checkout -b bug2. 변경, 커밋, 코드 검토 대기

    D < bug2
   /
A-B < master
   \
    C < bug1

검토를 기다리는 동안 다른 사람이 마스터에서 E & F를 커밋한다고 가정 해 봅시다.

    D < bug2
   /
A-B-E-F < master
   \
    C < bug1

코드가 승인되면 다음 단계를 통해 마스터하도록 코드를 리베이스 할 수 있습니다.

git checkout bug1
git rebase master
git checkout master
git merge bug1

결과는 다음과 같습니다.

    D < bug2
   /
A-B-E-F-C' < master, bug1

그런 다음 로컬 bug1 브랜치를 푸시하고 삭제할 수 있습니다. 작업 공간에서 한 번에 하나의 버그가 발생하지만 로컬 브랜치를 사용하면 저장소에서 여러 버그를 처리 할 수 ​​있습니다. 그리고 이것은 복잡한 무대 / 무대 댄스를 피합니다.

주석에서 ctote의 질문에 대한 답변 :

글쎄, 당신은 각 버그에 대한 숨김으로 돌아가서 한 번에 하나의 버그로만 작업 할 수 있습니다. 준비 문제를 해결하는 Atleast. 그러나 이것을 시도한 결과 개인적으로 문제가 있습니다. 숨김은 자식 로그 그래프에서 약간 지저분합니다. 더 중요한 것은 무언가를 망치면 되돌릴 수 없습니다. 더러운 작업 디렉토리가 있고 숨김을 표시하면 해당 팝업을 "실행 취소"할 수 없습니다. 기존 커밋을 망치는 것이 훨씬 어렵습니다.

그래서 git rebase -i.

한 분기를 다른 분기로 리베이스 할 때 대화식으로 수행 할 수 있습니다 (-i 플래그). 이 작업을 수행 할 때 각 커밋으로 수행 할 작업을 선택할 수 있습니다. Pro Git은 HTML 형식의 온라인 설명서이며 rebasing & squashing에 대한 멋진 섹션이 있습니다.

http://git-scm.com/book/ch6-4.html

편의상 그들의 예를 그대로 훔치겠습니다. 다음 커밋 히스토리를 가지고 있고, bug1을 마스터로 리베이스 및 스쿼시하려고합니다.

    F < bug2
   /
A-B-G-H < master
   \
    C-D-E < bug1

입력 할 때 표시되는 내용은 다음과 같습니다. git rebase -i master bug1

pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

분기의 모든 커밋을 단일 커밋으로 스쿼시하려면 첫 번째 커밋을 "pick"으로 유지하고 이후의 모든 "pick"항목을 "squash"또는 "s"로 바꿉니다. 커밋 메시지도 변경할 수 있습니다.

pick f7f3f6d changed my name a bit
s 310154e updated README formatting and added blame
s a5f4a0d added cat-file
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit

예, 스 쿼싱은 약간의 고통이지만 여전히 스 태시를 많이 사용하는 것이 좋습니다.


1
자세한 게시물 감사합니다! 이것은 확실히 많은 문제를 해결합니다. 제가 아는 유일한 문제는 현재 팀이 모든 배달을 단일 커밋으로 유지하도록 요청한 것입니다. :(
MrDuk

1
프로덕션 리포지토리에서 작업 기록이 필요하지 않거나 원하는 경우 괜찮습니다. 브랜치를 병합하는 대신 diff를 적용하여 추적 마스터 기록을 없앱니다. 실제로 병합 된 히스토리가있는 데코 레이팅 된 마스터 브랜치를 유지하는 방법을 알아 내고 그로부터 실제 작업을 수행하여 올바른 diff 생성을 쉽게 자동화 할 수 있습니다.
jthill

2
참고 git checkout master; git checkout -b bug2로 단축 할 수있다 git checkout -b bug2 master. 동일하게 적용 git checkout bug1; git rebase master; git checkout master; git merge bug1과 동일하다, git rebase master bug1; git push . bug1:master(부여 된 push트릭 명확하지 않다)
knittl가

1
멋진 답변을 사용할 수 있도록 위의 답변에서 위의 내용을 살펴 보았습니다.
Mike Monkiewicz

6
이것이 원래의 질문에 답하지 않기 때문에 하향 투표했습니다. 나는 어떤 일을하고있는 지점에 있고 통합 지점에 별도로 헌신해야한다고 생각한 것을 변경했습니다. 내가하고 싶은 것은 현재 "작업 진행 중"브랜치 대신 다른 브랜치로 전환하여 별도로 커밋 할 수 있도록 변경하고 숨기는 단계입니다. (경고, 앞으로 뛰어 들다.) 이것이 너무 어렵다는 것은 터무니없는 일이다. 나는 이것이 흔한 일 이라고 상상해야한다 . (한 지점에서 작업하고 빠른 변경을해야하며 먼저 전환하는 것을 잊어 버립니다.)
jpmc26

0

Mike Monkiewicz 답변에 대한 귀하의 의견 중 더 간단한 모델을 사용하는 것이 좋습니다. 정기 개발 브랜치를 사용하지만 병합의 스쿼시 옵션을 사용하여 마스터 브랜치에서 단일 커밋을 얻으십시오.

git checkout -b bug1    # create the development branch
* hack hack hack *      # do some work
git commit
* hack hack hack *
git commit
* hack hack hack *
git commit
* hack hack hack *
git commit
git checkout master     # go back to the master branch
git merge --squash bug1 # merge the work back
git commit              # commit the merge (don't forget
                        #    to change the default commit message)
git branch -D bug1      # remove the development branch

이 절차의 장점은 일반 git 작업 흐름을 사용할 수 있다는 것입니다.


이 답변이 어떻게 도움이 될 수 있는지 알 수 없습니다. 원래 질문과 관련이 없습니다.
frapen

0

TL; DR ;git stash-staged

별명을 작성한 후 :

git config --global alias.stash-staged '!bash -c "git stash -- \$(git diff --staged --name-only)"'

여기서는 파일 git diff목록을 반환 한 다음이 목록 을 쉼표 로 전달합니다 .--staged--name-only
pathspecgit stash

보낸 사람 man git stash:

git stash [--] [<pathspec>...]

<pathspec>...
   The new stash entry records the modified states only for the files
   that match the pathspec. The index entries and working tree
   files are then rolled back to the state in HEAD only for these
   files, too, leaving files that do not match the pathspec intact.


-1

실수로 변경, 특히 여러 파일을 삭제하려면 다음을 수행하십시오.

git add <stuff to keep> && git stash --keep-index && git stash drop

다시 말해 쓰레기를 숨기고 그 쓰레기를 완전히 버립니다.

자식 버전 2.17.1에서 테스트


코멘트가없는 공감대는 나에게도 도움이되지 않으며 다음 독자에게도 도움이되지 않습니다. zaenks grumpy anon. 이 one-liner에서 한 가지 문제를 상상할 수는 있지만 원하는 모든 변경 사항을 색인에 추가하는 것을 잊지 않아야합니다. 그렇지 않으면 중요한 변경 사항도 삭제됩니다. 그러나 클리 도구를 부주의하게 사용하는 것은 최악의 경우 소중한 시간과 직업에 매우 위험 할 수 있습니다.
wmax
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.