git reset --softの実用的な使い方は?


136

私はgitを1か月以上使用しています。実際、私は昨日初めて初めてリセットを使用しましたが、ソフトリセットはまだ私にはあまり意味がありません。

私は、ソフトリセットを使用して、インデックスや作業ディレクトリを変更せずにコミットを編集できることを理解していgit commit --amendます。

これら2つのコマンドは本当に同じ(reset --softvs commit --amend)ですか?実用的にどちらか一方を使用する理由はありますか?さらに重要なことに、reset --softコミットの修正以外に他の用途はありますか?

回答:


110

git resetはすべて移動に関するものHEADあり、一般的にはブランチ参照です。
質問:作業ツリーとインデックスはどうですか?
で使用する場合--soft移動しHEAD、最も頻繁に分岐REFを更新し、そしてのみHEAD
これは以下とは異なりますcommit --amend

  • 新しいコミットは作成されません。
  • 実際にHEADを任意のコミットに移動できます(現在のコミットのやり直しを許可しながら、HEADを移動しないことcommit --amendについてのみ)

ちょうどこの組み合わせの例を見つけました:

  • 古典的なマージ
  • サブツリーのマージ

すべてを1つにまとめます(タコ、2つ以上のブランチがマージされているため)コミットマージ。

Tomas "wereHamster" Carneckyは彼の"Subtree Octopus merge"記事で次のように説明しています

  • サブツリーマージ戦略は、あるプロジェクトを別のプロジェクトのサブディレクトリにマージし、その後サブプロジェクトを最新の状態に保つ場合に使用できます。gitサブモジュールの代替です。
  • タコのマージ戦略は、3つ以上のブランチをマージするために使用できます。通常の戦略では2つのブランチしかマージできません。それ以上マージしようとすると、gitは自動的にタコ戦略にフォールバックします。

問題は、1つの戦略しか選択できないことです。しかし、リポジトリ全体がアトミックに新しいバージョンに更新されるクリーンな履歴を取得するために、2つを組み合わせたかったのです。

私はスーパーprojectAプロジェクトを持っています、それをと呼びましょう、そしてサブプロジェクトをprojectBサブディレクトリにマージしましたprojectA

(それがサブツリーのマージ部分です)

また、ローカルコミットもいくつか維持しています。
ProjectAは定期的に更新され、projectB数日または数週間ごとに新しいバージョンがあり、通常はの特定のバージョンに依存しprojectAます。

私は両方のプロジェクトを更新することを決定したとき、私は単にから引っ張っていないprojectAと、projectB そのようプロジェクト全体の原子更新がどうあるべきかには2つのコミットを作成します
代わりにとローカルコミットを組み合わせた単一のマージコミットを作成しますprojectAprojectB
ここでトリッキーな部分は、これはタコのマージ(3つのヘッド)ですprojectB、サブツリー戦略とマージする必要があるということです。これが私がすることです:

# Merge projectA with the default strategy:
git merge projectA/master

# Merge projectB with the subtree strategy:
git merge -s subtree projectB/master

ここでは、著者が使用reset --hardして、read-tree最初の二つのマージが作業ツリーとインデックスに行っていたものを復元するために、それはどこでreset --soft助けることができる:
私はこれら二つのマージやり直しにどのように取り組んできました、私の作業ツリーとインデックス、すなわちしています結構ですが、これらの2つのコミットを記録する必要はありませんか?

# Move the HEAD, and just the HEAD, two commits back!
git reset --soft HEAD@{2}

これで、Tomasのソリューションを再開できます。

# Pretend that we just did an octopus merge with three heads:
echo $(git rev-parse projectA/master) > .git/MERGE_HEAD
echo $(git rev-parse projectB/master) >> .git/MERGE_HEAD

# And finally do the commit:
git commit

したがって、毎回:

  • (作業ツリーとインデックスに関して)最終的に何に満足するか
  • そこにたどり着くまでのすべてのコミットに満足していません

git reset --soft 答えです。


8
自己への注意:を押し潰すの簡単な例git reset --softstackoverflow.com/questions/6869705/...
VonC

3
また、間違ったブランチにコミットした場合にも役立ちます。すべての変更はステージングエリアに戻り、正しいブランチをチェックアウトしている間、一緒に移動します。
smoebody 2015年

44

使用例-一連のローカルコミットを組み合わせる

「エラー。これら3つのコミットは1つだけの可能性があります。」

そのため、最後の3つ(または何でも)のコミットを元に戻します(インデックスや作業ディレクトリには影響しません)。次に、すべての変更を1つとしてコミットします。

例えば

> git add -A; git commit -m "Start here."
> git add -A; git commit -m "One"
> git add -A; git commit -m "Two"
> git add -A' git commit -m "Three"
> git log --oneline --graph -4 --decorate

> * da883dc (HEAD, master) Three
> * 92d3eb7 Two
> * c6e82d3 One
> * e1e8042 Start here.

> git reset --soft HEAD~3
> git log --oneline --graph -1 --decorate

> * e1e8042 Start here.

これで、すべての変更が保存され、1つとしてコミットする準備が整いました。

あなたの質問に対する短い回答

これら2つのコマンドは本当に同じ(reset --softvs commit --amend)ですか?

  • 番号。

実用的にどちらか一方を使用する理由はありますか?

  • commit --amend 最後のコミットからファイルを追加/ rmするか、そのメッセージを変更します。
  • reset --soft <commit> 複数の順次コミットを組み合わせて新しいコミットにします。

さらに重要なことに、reset --softコミットの修正以外に他の用途はありますか?

  • 他の答えを見てください:)

2
reset --softコミットの修正以外に他の用途はありますか-いいえ」の他の回答を参照してください
アントニーハッチキンズ

最後のコメントへの回答-一部は同意しましたが、単一のコミットの修正は具体的すぎると思います。たとえば、機能が記述されたら、すべてをつぶして、レビュー担当者にとってより便利な新しいコミットを作成することができます。ソフトリセット(プレーンを含むgit reset)は、(a)履歴を書き換えたい場合、(b)古いコミットを気にしない場合(インタラクティブなリベースの煩わしさを回避できるため)、および(c)変更へのコミットが複数ある(そうでない場合commit --amendはより簡単です)。
johncip

18

私はそれを使って、最後のコミット以上のものを修正します。

コミットAでミスをしてからコミットBをしたとしましょう。今はBしか修正できないので、 git reset --soft HEAD^^ため、Aを修正して再コミットし、次にBを再コミットします。

もちろん、大規模なコミットにはあまり便利ではありません…しかし、とにかく大規模なコミットを行うべきではありません;-)


3
git commit --fixup HEAD^^ git rebase --autosquash HEAD~Xうまくいきます。
Niklas、2011年

3
git rebase --interactive HEAD^^ここでは、コミットAとBの両方を編集することを選択します。これにより、AとBのコミットメッセージが保持されます。必要に応じて、これらも変更できます。
Paul Pladijs

2
Aにリセットした後、Bを再コミットするにはどうすればよいですか?
northben 2013年

1
おそらくあまり仕事だもう一つの方法は、:Bのハッシュをコミット取得、あなたのgitのログを確認し、その後git reset A、作成し、変更を加え、git commit --amendgit cherry-pick <B-commit-hash>
naught101 '19 / 06/19

15

別の潜在的な用途は、隠蔽の代替としてです(一部の人々はこれを好まない場合があります。たとえば、https://codingkilledthecat.wordpress.com/2012/04/27/git-stash-pop-considered-harmful/を参照してください)。)。

たとえば、私がブランチで作業していて、マスターで何かを緊急に修正する必要がある場合、私はただ行うことができます:

git commit -am "In progress."

次に、マスターをチェックアウトして修正を行います。終わったら、ブランチに戻って

git reset --soft HEAD~1

中断したところから作業を続けます。


3
更新(今ではgitの方が理解しやすくなっています):--soft変更をすぐにステージングすることに本当に関心がない限り、ここでは実際には不要です。私は今git reset HEAD~これを行うときに使用します。私はいくつかの変更は、私は枝を切り替える必要があるとき上演、そして道というそれを維持したい場合、私はないgit commit -m "staged changes"、そしてgit commit -am "unstaged changes"、その後git reset HEAD~に続いてgit reset --soft HEAD~、完全に作業状態を復元します。正直に言うと、私はこれらの両方のことを私が知っている今ではそれほど多くはしていませんgit-worktree:)
deltacrux

7

を使用git reset --softして、インデックスと作業ツリーにある変更の親として保持するバージョンを変更できます。これが役立つケースはまれです。ときどき、作業ツリーに加えた変更を別のブランチに属するように決定する場合があります。または、これをいくつかのコミットを1つに集約する簡単な方法として使用できます(スカッシュ/フォールドと同様)。

実用的な例については、VonCによるこの回答を参照してください。Gitで 最初の2つのコミットをスカッシュしますか?


これは、私が以前に見つけたことのない良い質問です。しかし、reset softを使用して別のブランチに変更を加える方法を確認できるかわかりません。それで私はブランチをチェックアウトしていて、そこで使用git reset --soft anotherBranchしてコミットしますか?しかし、あなたは本当にckeckoutブランチを変更しないので、あなたは、にコミットします支店またはにanotherBranch
AJJ 2011年

これを行うときは、ツリーを変更するため、git checkoutを使用しないことが重要です。git reset --softは、HEADが指すリビジョンを操作するだけの方法と考えてください。
ヨハネスルドルフ

7

考えられる使用法の1つは、別のマシンで作業を続けたい場合です。これは次のように機能します。

  1. stashのような名前の新しいブランチをチェックアウトします。

    git checkout -b <branchname>_stash
    
  2. stashブランチを押し上げ、

    git push -u origin <branchname>_stash
    
  3. 他のマシンに切り替えます。

  4. あなたの隠し場所と既存のブランチの両方を引き下げ、

    git checkout <branchname>_stash; git checkout <branchname>
    
  5. これで、既存のブランチにいるはずです。stashブランチからの変更をマージし、

    git merge <branchname>_stash
    
  6. マージする前に、既存のブランチを1にソフトリセットします。

    git reset --soft HEAD^
    
  7. stashブランチを削除し、

    git branch -d <branchname>_stash
    
  8. また、stashブランチを元の場所から削除し、

    git push origin :<branchname>_stash
    
  9. 変更を通常の方法で隠したかのように、変更を続けます。

将来的には、GitHubと共同で考えています。この「リモートスタッシュ」機能をより少ないステップで提供する必要があります。


2
最初のマシンでの最初のスタッシュとポップは完全に不要です。ダーティな作業コピーから直接新しいブランチを作成し、コミットして、変更をリモートにプッシュすることができます。

7

実用的な使い方の1つは、すでにローカルリポジトリにコミットしている場合(つまり、git commit -m)、git reset --soft HEAD〜1を実行して、最後のコミットを元に戻すことができます。

また、参考までに、すでに変更をステージングしている場合(つまりgit addを使用している場合)は、git reset --mixed HEADを実行してステージングを逆にすることができますまでに、git reset

最後に、git reset --hardは、ローカルの変更を含むすべてを消去します。頭の後にある〜は、上から行くべきコミットの数を示します。


1
git reset --soft HEAD ~1私が与えるfatal: Cannot do soft reset with paths.私たちは、それは次のようになりますので、HEADの後にスペースを削除する必要があると思うgit reset --soft HEAD~1
アンドリュー・ロアー

6

git reset --soft <sha1>」を使用する大きな理由は、移動することですHEADは、ベアリポジトリです。

--mixedまたはを使用しようとした場合--hardオプションしようとすると、存在しないツリーやインデックスを変更して作業しようとしているため、エラーが発生します。

注:これは、ベアリポジトリから直接行う必要があります。

注意:ベアリポジトリでリセットするブランチがアクティブなブランチであることを確認する必要があります。そうでない場合は、リポジトリに直接アクセスできるときに、ベアリポジトリのアクティブブランチを更新する方法に関するVonCの回答に従ってください。


1
以前の回答(stackoverflow.com/questions/4624881/…)で述べたように、これは、ベアリポジトリ(ここで言及)に直接アクセスできる場合に当てはまります。+1。
VonC、

@VonCはい、絶対に正しいとメモを追加していただきありがとうございます!リセットはリポジトリから直接行われると想定しているので、これを忘れてしまいます。また、ユーザーがベアリポジトリでリセットしたいブランチがアクティブなブランチであると想定しています。ブランチがアクティブなブランチではない場合、ベアリポジトリのアクティブなブランチを更新する方法について、回答(stackoverflow.com/questions/3301956/…)に従って更新する必要があります。回答もアクティブなブランチ情報で更新します。再度、感謝します!!!
ハゾク

2

SourceTreeは、必要なビットだけをステージングするための非常に便利なインターフェースを備えたgit GUIです。適切なリビジョンを修正するためにリモートで類似したものはありません。

したがってgit reset --soft HEAD~1、よりもはるかに便利ですcommit --amend、このシナリオ。コミットを元に戻し、すべての変更をステージング領域に戻し、SourceTreeを使用してステージングされたビットの微調整を再開できます。

実際にcommit --amendは、2つのコマンドの方が冗長なコマンドであるように見えますが、gitはgitであり、わずかに異なる機能を実行する同様のコマンドを避けません。


1

私はこのスレッドの答えが本当に好きですが、私は git reset --softは少し異なっていますが、それでも非常に実用的なシナリオしています。

私は、開発用のIDEを使用します。これには、最後のコミット後の変更(ステージングおよびアンステージング)を表示するための優れたdiffツールがあります。今、私の仕事のほとんどは複数のコミットを伴います。たとえば、特定のタスクを完了するために5回コミットするとします。1〜5の増分コミットごとにIDEでdiffツールを使用して、最後のコミットからの変更を確認します。コミットする前に変更を確認するのに非常に役立つ方法だと思います。

しかし、私のタスクの最後に、すべての変更をまとめて(最初のコミットの前から)確認したい場合、プルリクエストを行う前にセルフコードレビューを行うと、前回のコミット(コミット後)からの変更のみが表示されます4)現在のタスクのすべてのコミットからの変更ではありません。

だから私はgit reset --soft HEAD~44つのコミットを戻すために使用します。これにより、すべての変更をまとめて確認できます。自分の変更に自信がある場合は、自信をgit reset HEAD@{1}持ってリモートにプッシュできます。


1
...その後git add --patchgit commit繰り返し実行して、一連のコミットシリーズを構築します。最初のコミットは、机の上のメモやメモの最初のドラフトのようなものであり、公開のためではなく、思考を整理するためにそこにあります。
jthill

ねえ、私はあなたが提案しているものを完全に理解していませんでした。
Swanky Coder 2018

1
ただ、代わりにやってのことgit reset @{1}、あなたの最初のドラフトシリーズを復元するには、代わりのために、出版シリーズを構築することができますgit add -pし、git commit
jthill 2018

はい正解。もう1つの方法は、(私が通常従う方法)アップストリーム/マスターに基づいてリベースし、コミットを1つにつぶすことです。
Swanky Coder

1

もう1つの使用例は、プルリクエストで他のブランチを自分のものに置き換える場合です。たとえば、開発中の機能A、B、Cを持つソフトウェアがあるとします。

あなたは次のバージョンで開発していて、あなたは:

  • 機能Bを削除

  • 機能Dを追加

その過程で、機能Bに追加されたばかりのホットフィックスを開発します。

あなたは次の開発にマージすることができますが、それは時々面倒になる可能性がありますがgit reset --soft origin/develop、変更を使用してコミットを作成し、ブランチが競合することなくマージ可能であり、変更を維持することもできます。

これはgit reset --soft便利なコマンドです。私は個人的に、「WIP」のような「完了した作業」を持たないコミットをスカッシュするために多く使用するため、プルリクエストを開くと、すべてのコミットが理解できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.