10の一般的なGitの問題とその修正方法

私はもともと2014年10月にCodementor向けにこの記事を書きました。かなり新しいgitユーザーから経験豊富な開発者まで、すべての人にとって何かが必要です。

1.ローカルファイルの変更を破棄する

問題を感じる最良の方法は、コードに飛び込んで遊んでみることです。残念ながら、プロセスで行われた変更は最適でない場合があります。その場合、ファイルを元の状態に戻すのが最も速くて簡単な解決策です。

git checkout-Gemfile#指定されたパスをリセット
git checkout-lib bin#は複数の引数でも動作します

ご参考までに、二重ダッシュ(-)は、コマンドラインユーティリティがコマンドオプションの終了を示す一般的な方法です。

2.ローカルコミットを元に戻す

残念ながら、間違った方向に進んでいることに気付くまでに少し時間がかかることがあります。その時点で、1つ以上の変更が既にローカルにコミットされている可能性があります。これはgit resetが便利なときです:

git reset HEAD〜2#最後の2つのコミットを取り消し、変更を保持
git reset --hard HEAD〜2#最後の2つのコミットを取り消し、変更を破棄します

--hardオプションに注意してください!作業ツリーとインデックスがリセットされるため、変更はすべて失われます。

3.ファイルシステムから削除せずにgitからファイルを削除する

git add中に注意しないと、コミットしたくないファイルを追加してしまう可能性があります。ただし、git rmは、ステージング領域とファイルシステムの両方からそれを削除します。その場合は、ステージングバージョンのみを削除し、ファイルを.gitignoreに追加して、同じ間違いを2回繰り返さないようにします。

git reset filename#またはgit rm --cached filename
echo filename >> .gitignore#それを.gitignoreに追加して、再度追加しないようにします

4.コミットメッセージを編集する

タイプミスが発生しますが、幸いなことにコミットメッセージの場合、それらを修正するのは非常に簡単です:

git commit --amend#$ EDITORを開始してメッセージを編集します
git commit --amend -m "New message"#新しいメッセージを直接設定

しかし、git-amendでできることはそれだけではありません。ファイルを追加するのを忘れましたか?それを追加して、前のコミットを修正するだけです!

git add forgotten_file git commit --amend

--amendは実際には以前のコミットを置き換える新しいコミットを作成するため、既に中央リポジトリにプッシュされているコミットの変更には使用しないでください。他の開発者が以前のバージョンを既にチェックアウトしていないこと、および自分の作業に基づいていることが確実にわかっている場合、このルールの例外を作成できます。その場合、強制プッシュ(git push --force)でも問題ありません。ツリーの履歴がローカルで変更されたため、ここでは--forceオプションが必要です。つまり、早送りマージができないため、プッシュはリモートサーバーによって拒否されます。

5.プッシュする前にローカルコミットをクリーンアップする

--amendは非常に便利ですが、言い直したいコミットが最後のものではない場合は役に立ちません。その場合、インタラクティブなリベースが便利です:

git rebase --interactive
#このブランチの追跡情報を指定しなかった場合
#アップストリームおよびリモートブランチ情報を追加する必要があります。
git rebase-インタラクティブなオリジンブランチ

これにより、構成済みのエディターが開き、次のメニューが表示されます。

8a20121を選択してRubyバージョンを2.1.3にアップグレードします
22dcc45を選択する
#fcb7d7c..22dcc45をfcb7d7cにリベースします
#
#コマンド:#p、pick = use commit
#r、reword =コミットを使用するが、コミットメッセージを編集する
#e、編集=コミットを使用、ただし修正は停止
#s、squash =コミットを使用しますが、前のコミットにマージします
#f、fixup = "squash"に似ていますが、このコミットのログメッセージを破棄します
#x、exec =シェルを使用してコマンド(行の残り)を実行
#
#これらの行は並べ替えることができます。それらは上から下に実行されます。
#
#ここで行を削除すると、コミットが失われます。
#
#ただし、すべてを削除すると、リベースは中止されます。
#
#空のコミットはコメント化されていることに注意してください

上部にローカルコミットのリストが表示され、その後に使用可能なコマンドの説明が表示されます。更新するコミットを選択し、選択を言い換えに変更(または略してr)すると、メッセージを編集できる新しいビューが表示されます。

ただし、上記のリストからわかるように、インタラクティブなリベースは、単純なコミットメッセージの編集以上のものを提供します。リストからコミットを削除するだけでなく、コミットを完全に削除することもできます。スカッシュを使用すると、複数のコミットを1つにマージできます。これは、機能ブランチでリモートにプッシュする前に行うことが好きなことです。永遠に記録された「忘れられたファイルを追加」と「タイプミスを修正」のコミットはもうありません!

6.プッシュされたコミットを元に戻す

前のヒントで示した修正にもかかわらず、不完全なコミットが中央リポジトリにたまに侵入することがあります。 gitは単一または複数のコミットを元に戻す簡単な方法を提供するため、これは絶望する理由にはなりません。

git revert c761f5c#指定したIDのコミットを元に戻します
git revert HEAD ^#最後から2番目のコミットを元に戻す
git revert development〜4..develop〜2#コミットの全範囲を元に戻します

追加のリバートコミットを作成せず、作業ツリーに必要な変更のみを適用する場合は、-no-commit / -nオプションを使用できます。

#最後のコミットを元に戻すが、元に戻すコミットは作成しない
git revert -n HEAD

man 1のgit-revertのマニュアルページには、さらにオプションがリストされており、いくつかの追加例が提供されています。

7.繰り返されるマージ競合を避ける

すべての開発者が知っているように、マージの競合を修正するのは退屈かもしれませんが、まったく同じ競合を繰り返し解決することは(たとえば、実行中の機能ブランチで)まったく面倒です。過去にこれに悩まされていた場合は、十分に活用されていない再利用記録された解像度機能について学ぶことができます。グローバル設定に追加して、すべてのプロジェクトで有効にします。

git config --global rerere.enabled true

あるいは、ディレクトリ.git / rr-cacheを手動で作成することにより、プロジェクトごとに有効にすることができます。

これは万人向けの機能ではありませんが、それを必要とする人にとっては、時間の節約になります。あなたのチームが同時にさまざまな機能ブランチに取り組んでいると想像してください。次に、それらすべてを1つのテスト可能なプレリリースブランチにマージします。予想どおり、いくつかのマージ競合があり、それらを解決します。残念ながら、ブランチの1つがまだそこにないことが判明したため、再度マージを解除することにしました。数日後(または数週間後)ブランチの最終準備が整うと、再びマージしますが、記録された解像度のおかげで、同じマージの競合を再度解決する必要はありません。

マニュアルページ(man git-rerere)には、さらなるユースケースとコマンド(git rerere status、git rerere diffなど)に関する詳細情報があります。

8.マージ後に何かを壊したコミットを見つける

大きなマージの後にバグを導入したコミットを追跡するには、かなり時間がかかる可能性があります。幸いなことに、gitはgit-bisectの形式で優れたバイナリ検索機能を提供します。最初に、初期セットアップを実行する必要があります。

git bisect start#bisectingセッションを開始します
git bisect bad#現在のリビジョンを不良としてマーク
git bisectの良いリビジョン#最後の既知の良いリビジョンをマークします

この後、gitは既知の「良い」バージョンと「悪い」バージョンの中間のリビジョンを自動的にチェックアウトします。スペックを再度実行し、それに応じてコミットを「良い」または「悪い」としてマークできます。

git bisect good#またはgit bisec bad

このプロセスは、バグを導入したコミットに到達するまで続きます。

9. gitフックでよくある間違いを避ける

いくつかの間違いは繰り返し発生しますが、gitワークフローの定義された段階で特定のチェックまたはクリーンアップタスクを実行することにより、簡単に回避できます。これはまさに、フックが設計されたシナリオです。新しいフックを作成するには、実行可能ファイルを.git / hooksに追加します。スクリプトの名前は、利用可能なフックのいずれかに対応する必要があります。その完全なリストは、マニュアルページ(man githooks)で利用できます。新しいリポジトリを初期化するときにgitが使用するテンプレートディレクトリを作成することで、すべてのプロジェクトで使用するグローバルフックを定義することもできます(詳細については、man git-initを参照してください)。 〜/ .gitconfigの関連エントリとテンプレートディレクトリの例は次のとおりです。

[初期化]
    templatedir =〜/ .git_template
  
→ツリー.git_template
  .git_template
  └──フック
      └──事前コミット

新しいリポジトリを初期化すると、テンプレートディレクトリ内のファイルがプロジェクトの.gitディレクトリ内の対応する場所にコピーされます。

次に、少し工夫されたcommit-msgフックの例を示します。これにより、すべてのコミットメッセージが「#123」のようなチケット番号を参照するようになります。

#!/ usr / bin / env ruby
メッセージ= File.read(ARGV [0])

メッセージ=〜/ \ s *#\ d + /
  「[POLICY]メッセージはチケットを参照しませんでした。」
  1番出口
終わり

10.他のすべてが失敗したとき

これまで、gitを使用する際の一般的なエラーを修正する方法について、かなり多くの根拠を説明しました。それらのほとんどは十分に簡単な解決策を持っていますが、大きな銃を取り出してブランチ全体の履歴を書き直さなければならない場合があります。これの一般的な使用例の1つは、パブリックリポジトリにコミットされた機密データ(実稼働システムのログイン資格情報など)の削除です。

git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch secrets.txt' \
  --prune-empty --tag-name-filter cat---all

これにより、すべてのブランチとタグからファイルsecrets.txtが削除されます。また、上記の操作の結果として空になるコミットも削除されます。これによりプロジェクトの履歴全体が書き換えられることに注意してください。これは、分散ワークフローでは非常に混乱を招く可能性があります。また、問題のファイルは削除されましたが、そこに含まれる資格情報はまだ侵害されていると見なされる必要があります!

GitHubには、機密データの削除に関する非常に優れたチュートリアルがあり、man git-filter-branchには、使用可能なさまざまなフィルターとそのオプションに関するすべての詳細があります。

もともとgist.github.comで公開されました。