gitで段階的な変更のみを隠しておくことは可能ですか?


366

段階的な変更だけを隠す方法はありますか?私が問題を抱えているシナリオは、ある時点でいくつかのバグに取り組み、いくつかの段階的な変更がない場合です。これらのファイルを個別にステージングし、.patchファイルを作成し、コードが承認されるまで隠しておくことができるようにしたいと考えています。このようにして、承認されたら、(現在の)セッション全体を隠しておき、そのバグをポップしてコードをプッシュすることができます。

私はこれを間違った方法で行っていますか?プロセスを簡略化するためにgitが他の方法でどのように機能するかを誤解していますか?


はい、おそらくあなたはこの状況に陥るために間違ったことをしています。まだ有用な質問です。あなたは本当に次の修正を始める前に隠しておくか分岐させるべきです。接線方向の回答であるstackoverflow.com/a/50692885は、おそらくgitでこれを処理するためのより良い方法です。上流からコミットをプルした場合、スタッシュをいじってみると、作業領域に奇妙なことがよくあります。
SamuelÅslund19年

回答:


472

はい、DOUBLE STASHで可能です

  1. 隠しておく必要のあるすべてのファイルをステージングします。
  2. を実行しますgit stash --keep-index。このコマンドは、すべての変更(ステージングされたものとステージングされていないもの)を含むstashを作成しますが、ステージングされた変更は作業ディレクトリー(ステージングされた状態のまま)に残します。
  3. 走る git stash push -m "good stash"
  4. 今、あなたが"good stash"ファイルのみを上演しました

stashの前にステージングされていないファイルが必要な場合は、最初のstash(で作成されたもの--keep-index)を適用するだけで、stashしたファイルを削除できます"good stash"

楽しい


ステージングされていなくても、サブモジュールの変更を隠しておきます。これを回避する方法はありますか?
rluks

1
これはどういうわけかすべての新しいファイルを(ステージングされていても)除外しました。
Aurimas 2017年

8
@Aurimas、新しいファイルを隠しておくには、-uスイッチを使用する必要があります。
ジャイロマイト2017

2
最初のスタッシュを再適用してすべての変更を元に戻し、ステージングの変更の使用git stash apply --indexオプションのみに関心がある場合。これにより、(ステージングされていない)状態が維持されます。作業ツリーから不要な変更を簡単に削除できるようになりました。
otomo

私はこの答えが言ったことを正確に行う必要はありませんでしたが、--keep-indexフラグについて知ることは非常に役に立ちました。
アーロンクラウス

128

最新のgitでは--patchオプションを使用できます

git stash push --patch  

git stash save --patch   # for older git versions

そして、gitはstashに追加するかしないかをファイルに変更するたびに尋ねます。
あなたはただ答えるyn

DOUBLE STASHの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
その結果、ステージングされたファイルはstashに保存されます

ステージングされたファイルを保持せず、それらをstashに移動する場合。次に、別のエイリアスを追加して実行できます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 2017

6
同意します、これは大丈夫ですが、ここでの質問の考えは、私が何かをしたい変更をステージングするこの作業のすべてをすでに行ったということです(表面的には最初はコミットするが、今は隠したい)。もう一度繰り返します。
Steven Lu

4
新しく作成されたファイルでは機能しません(変更されたファイルでのみ機能します)
Derek Liang

@DerekLiang:新しく作成されたファイルはまったく追跡されません。おそらく次の-u|--include-untrackedオプションを確認する必要がありますgit-stash
Eugen Konkov '20

2
docsから:「保存:このオプションはgit stash pushのために非推奨になりました。pathspecをとることができず、オプション以外の引数がメッセージを形成するという点で 'stash push'とは異なります。」
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}

7
スクリプトをgitコマンドに変換するには、discoblog.com / blog / 2014/03/29 / custom
Petr Bela

3
これは素晴らしい!コマンドラインでstashの説明を入力しない場合、ユーザーにstashの説明を入力するように調整しました:gist.github.com/brookinc/e2589a8c5ca33f804e4868f6bfc18282
brookinc

1
これはgit 2.23.0ではまったく機能しません。
thnee

おかげで、私は賛成してここでエイリアスに変えました:stackoverflow.com/a/60875067/430128
ラマン

34

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.1およびv2.21.0.windows.1でテスト済み

制限:

  • ステージングされたファイルがない場合、これはすべてのものを隠しておくことに注意してください。
  • また、一部しかステージングされていないファイルがある場合(つまり、一部の変更された行のみがステージングされ、他の一部の変更された行はステージングされていない場合)、ファイル全体がスタッシュされます(ステージングされていない行を含む)。

6
これは、説明されている状況での最良の選択肢だと思います。理解しやすく、黒魔術は必要ありません。
ルイス

1
これはかなりきれいです。それからエイリアスを作成してしまいました!
カルペシュパンチャル

EMは😉やって来る得票」キープ
ソモS.

@KalpeshPanchalエイリアスを共有できますか?どうやってそれをエスケープするのかわからないので、正しく解釈されていません。
Igor Nadj

2
@IgorNadjはい!ここでは、次のとおりです。 github.com/panchalkalpesh/git-aliases/commit/...
Kalpesh Panchal

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Åslund19年

1
この回答が示すように、特定の手法で達成する方法ではなく、達成したいことを直接尋ねた方がよい場合があります。一時的な枝やチェリーピックは、複雑な状況で便利です。
Guney Ozsan

ファイルを部分的にステージングした場合、元のブランチに戻って再度ポップする前に、変更を隠しておく必要があります
jan-glx

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^^

これは、2つのファイルを作成します。0001-fix-bug-123.patch0002-fix-bug-321.patch

または、バグごとに個別のブランチを作成して、バグ修正を個別にマージまたはリベースしたり、問題が解決しない場合は削除したりすることもできます。


2

git stash --keep-index 良い解決策です...ただし、Git 2.23(2019年第3四半期)で修正された、削除されたパスで正しく機能しなかった点を除きます

Thomas Gummerer()によるcommit b932f6a(2019年7月16日)を参照してください。(合併によりJunio C浜野- -f8aee85コミット 2019年7月25日)tgummerer
gitster

stash:で削除されたファイルの処理を修正 --keep-index

git stash push --keep-index インデックスとディスクの両方で、インデックスに追加されたすべての変更を保持することになっています。

現在、これはファイルがインデックスから削除されたときに正しく動作しません。
ディスク上で削除されたままにする代わりに、**-keep-indexは現在ファイルを復元します。**

git checkoutインデックスと作業ツリーを忠実に復元できる非オーバーレイモードで' ' を使用して、この動作を修正します。
これにより、コードも簡素化されます。

追跡されていないファイルが、インデックスで削除されたファイルと同じ名前である場合、追跡されていないファイルが上書きされることに注意してください。


2

Gitでインデックス(段階的な変更)だけを隠しておくことは、本来あるべきことよりも困難です。私は@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"

段階的変更と非段階的変更の両方を一時的な隠し場所にプッシュ、段階的変更はそのままにします。次に、ステージングされた変更をstashにプッシュします。これは、保持したいstashです。などのエイリアスに渡される引数は、--message "whatever"このstashコマンドに追加されます。最後に、一時スタッシュをポップして元の状態を復元し、一時スタッシュを削除してから、リバースパッチアプリケーションを使用して、作業ディレクトリからスタッシュされた変更を「削除」します。

ステージングされていない変更(エイリアスstash-working)だけを隠しておくという反対の問題については、この回答を参照してください。


1

一度にいくつかのバグに取り組むことが絶対に必要ですか?「同時に」とは、「複数のバグのためにファイルを同時に編集すること」を意味します。あなたが絶対にそれを必要としない限り、私はあなたの環境で一度に1つのバグだけに取り組むでしょう。そうすれば、ローカルのブランチとリベースを使用できます。これは、複雑なスタッシュ/ステージを管理するよりもはるかに簡単です。

マスターがコミットBにいるとします。ここで、バグ#1に取り組みます。

git checkout -b bug1

今、あなたはブランチのバグ1にいます。変更を加えて、コミットし、コードのレビューを待ちます。これはローカルであるため、他の人に影響を与えることはなく、git diffからパッチを作成するのは簡単です。

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ブランチをプッシュして削除すると、すぐに使用できます。ワークスペースでは一度に1つのバグですが、ローカルブランチを使用すると、リポジトリで複数のバグを処理できます。これにより、複雑なステージ/スタッシュダンスが回避されます。

コメントでctoteの質問に答えてください:

さて、各バグの隠し場所に戻って、一度に1つのバグのみを処理できます。少なくとも、ステージングの問題を回避できます。しかし、これを試してみて、私は個人的にそれが面倒だと思います。Stashesはgit logグラフでは少し乱雑です。さらに重要なことに、何かを台無しにすると元に戻すことはできません。ダーティな作業ディレクトリがあり、スタッシュをポップした場合、そのポップを「取り消す」ことはできません。既存のコミットを台無しにすることははるかに困難です。

ですからgit rebase -i

あるブランチを別のブランチにリベースする場合、インタラクティブに行うことができます(-iフラグ)。これを行うと、コミットごとに実行する処理を選択するオプションがあります。Pro GitはすばらしいHTML形式の本で、リベースとスカッシュについての素晴らしいセクションがあります。

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

便宜上、彼らの例を逐語的に盗みます。次のコミット履歴があり、バグ1をマスターにリベースしてスカッシュしたいとします。

    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.
#

ブランチのすべてのコミットを1つのコミットに押しつぶすには、最初のコミットを「ピック」として保持し、後続のすべての「ピック」エントリを「スカッシュ」または単に「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
詳細な投稿ありがとうございます!これにより、多くの問題が確実に解決されます。唯一の問題は、現在のチームがすべての配信を1つのコミットに留めるように要求したことです。:(
MrDuk 2013

1
プロダクションリポジトリでの作業履歴が不要または必要ない場合は、問題ありません。ブランチをマージするのではなく、差分を適用することにより、追跡マスターの履歴をなくします。実際にマージされた履歴を持つ装飾されたマスターブランチを維持し、そこから実際の作業を行う方法を考え出すことができるので、正しいdiffの生成を自動化するのが簡単になります。
jthill 2013

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
これは元の質問に答えないため、反対票を投じました。私はブランチで作業していて、統合ブランチに個別にコミットする必要があると思う変更を加えました。私がやりたいのは、現在の「進行中の作業」ブランチの代わりに、別のブランチに切り替えて個別にコミットできるように、変更してステージングするステージです。(警告、先にgitが怒鳴っています。)これを行うのが非常に難しいのは不合理です。これはよくあることだと思います。(1つのブランチで作業していて、最初に変更する必要のある素早い変更を見つけ、それを忘れている。)
jpmc26

0

Mike Monkiewiczへのコメントのうち、より単純なモデルを使用することをお勧めします。通常の開発ブランチを使用しますが、マージのsquashオプションを使用して、マスターブランチで単一のコミットを取得します。

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ワークフローを使用できることです。


この答えがどのように役立つかはわかりません。元の質問とは関係ありません。
1

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

つまり、がらくたを隠しておき、隠し場所と一緒に捨てます。

gitバージョン2.17.1でテスト済み


コメントのない反対投票は私にとっても次の読者にとっても役に立ちません... zaenks不機嫌そうなanon。このワンライナーの1つの問題を想像できますが、必要なすべての変更をインデックスに追加することを忘れないように非常に注意する必要があります。そうしないと、重要な変更も削除されます。しかし、繰り返しになりますが、cliツールの不注意な使用は、最悪の場合、貴重な時間と仕事にとって非常に危険です。
wmax
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.