LABOT 機械学習ブログ

本郷で機械学習の受託開発をしている会社のブログです。CEOの堀田がディープラーニング・データ分析周辺の技術情報を発信します。

【永久保存版】Gitのあらゆるトラブルが解決する神ノウハウ集を翻訳した

git github

堀田(@YoshiHotta)です。この記事はGithubで3万スター⭐以上を集めた人気リポジトリ git-flight-rules の翻訳です。

Git はエンジニアが毎日何十回も使うコマンドであるにも関わらず、難しいツールです。 コマンドを間違えて、元に戻そうとあれこれ試しているうちにもっと悲惨な状況になり、復旧できなくなった経験が誰でも一度はあるのではないでしょうか。

Git でトラブルに見舞われてもこの Git フライトルールがあれば安心です! このガイドで対処策が必ず見つかります。

落ち着いてガイドの手順に従えばトラブルから脱出できます。

毎日Git を使う仕事を何年もしていますが、今でも困ることがよくあります。そういう時は git flight rules をすぐに参照していました。本家に日本語版がなかったので、仕方がなくずっと母語ではない英語のバージョンを読んでいました。塵も積もれば山となるで結構な時間をロスしていたと思います。読者の皆さんには Git で困って欲しくない、母語でこの素晴らしい Git フライトルールを読んで、git の操作ミスで苦労して書いたコードを吹き飛ばして欲しくないという思いから、この長い長いガイドを翻訳しました。

Git 初心者からベテランまでぜひ手元に置いておいて下さい。

🌍原著英語版

※ この記事は Attribution-ShareAlike 4.0 International ライセンス で配布します。


目次

「フライトルール」とは何ですか?

問題が発生した場合の対処方法についての、宇宙飛行士のためのガイドです。今の場合、Git を使用しているプログラマのためのガイドです。

フライトルールは苦労して手に入れた知識体系をリスト化してマニュアルにしたものです。「もし X が発生したら、なぜ、なにをやるか」の手順が書かれています。基本的に、これらは非常に詳細なシナリオ固有の標準的な操作手順です。 [...]

マーキュリー計画の地上チームが1960年代初頭に「学び」を最初に集め始めて以来、NASAはエンジンの故障から潰されたハッチまで、ミス、災害、そして解決策の概要をリスト化してきました。

— Chris Hadfield, An Astronaut's Guide to Life.

文書規約

この文書の表記規則を分かりやすくするために、この文書のすべての例で、現在のブランチとステージングした変更があるかを示すためにカスタマイズしたbashプロンプトを使用しています。ブランチは括弧で囲まれ、ブランチ名の横の*はステージングした変更を示します。すべてのコマンドは少なくともgitのバージョン2.13.0から動くはずです。ローカルのgitのバージョンを更新するにはgitのウェブサイト を見てください。

リポジトリ

ローカルのリポジトリを始めたい

既存のディレクトリをGitリポジトリとして初期化するには:

(my-folder) $ git init

リモートのリポジトリをクローンしたい

リモートリポジトリをクローン(コピー)するには、リポジトリのURLをコピーして、次のコマンドを実行します。

$ git clone [url]

これにより、リモートリポジトリと同じ名前のフォルダに保存されます。 複製元のリモートサーバーに接続していることを確認して下さい(ほとんどの場合、インターネットに接続していることを確認すれば大丈夫です)。

デフォルトのリポジトリ名と異なる名前のフォルダに複製するには、次の手順を実行します。

$ git clone [url] name-of-new-folder

誤ったリモートのリポジトリを設定してしまった

いくつかの問題が考えられます。

間違ったリポジトリを複製した場合は、 git cloneを実行した後に作成されたディレクトリを削除し、正しいリポジトリをクローンしてください。

既存のローカルリポジトリの origin として間違ったリポジトリを設定した場合は、次のコマンドを実行して origin の url を変更します。

$ git remote set-url origin [url of the actual repo]

更に詳しく知りたい場合は この StackOverflow のトピック を参照して下さい。

他人のリポジトリのコードを追加したい

Git は他の誰かのリポジトリにアクセス権なしでコードを追加することを許可していません。Github でも同様です。 Github は Git と全く同じではありませんが、 Git のホストサービスのようなものです。 ただし、パッチを使用してコードを提案することができ、GitHubではフォークしたりプルリクエストを出すことができます。

まず、フォークについて少々説明をしましょう。 フォークはリポジトリのコピーです。 これは git の操作ではありませんが、GitHub、Bitbucket、GitLab を含む Git リポジトリをホストする場所では一般的なアクションです。 ホストされたUIを通してリポジトリをフォークすることができます。

プルリクエストを出してコードを提案したい

リポジトリをフォークした後は、通常リポジトリを自分のマシンにクローンする必要があります。例えばクローンを作成せずにGitHubを少し編集することはできますが、この文書は Github フライトルールではないので、ローカルでこれを行う方法を試してみましょう。

# ssh を使っている場合
$ git clone git@github.com:k88hudson/git-flight-rules.git

# https を使っている場合
$ git clone https://github.com/k88hudson/git-flight-rules.git

作成されるディレクトリに cd して、git remote と入力すると、リモートの一覧が表示されます。 通常は1つのリモート、origin があり、それはk88hudson / git-flight-rulesを指します。 この場合、自分のフォークを指すリモートも欲しいと思います。

まず、Gitの慣例に従い、自分のリポジトリに originというリモート名を使い、フォークしたものにupstreamを使います。 originの名前をupstreamに変更しましょう。

$ git remote rename origin upstream

git remote set-urlを使ってこれを行うこともできますが、時間がかかり、手順が多いです。

次に、自分のプロジェクトを指す新しいリモートを設定します。

$ git remote add origin git@github.com:YourName/git-flight-rules.git

これで2つのリモートができました。

  • originは自分のリポジトリを参照します。
  • upstreamは元のリポジトリを参照します。

origin に対しては read / write することができます。 upstream に対しては read だけができます。

好きなように変更を加え終わったら、変更を(通常はブランチ内から) originという名前のリモートにプッシュします。ブランチにいるのなら、このブランチを使う将来のプッシュでリモートトラッキングブランチを指定するのを避けるために --set-upstreamを使うことができます。 例えば

$ (feature/my-feature) git push --set-upstream origin feature/my-feature

Gitを使用してCLIでプルリクエストを出す方法はありません(ただし、hubのようなツールがあります)。 そのため、プルリクエストを作成する準備が整ったら、GitHub(または他のGitホスト)に移動して新しいプルリクエストを作成します。 Gitホストが自動的に元のリポジトリとフォークしたリポジトリをリンクすることに注意してください。

コードレビューのフィードバックには必ず返信するようにしてください。

元のリポジトリの更新をフォークしたリポジトリに反映したい

しばらくして、 upstreamリポジトリが更新されたかもしれません。これらの更新を自分の origin リポジトリに取り込む必要があります。あなたと同様に他の人々もプロジェクトに貢献していることを忘れないでください。 自分のフィーチャーブランチにいて、元のリポジトリの更新を取り込む必要があるとしましょう。

恐らく元のプロジェクトを指すリモートを設定したでしょう。もし そうでなければ、今して下さい。 一般的にリモートの名前として upstreamを使います。

$ (master) git remote add upstream <link-to-original-repository>
# $ (master) git remote add upstream git@github.com:k88hudson/git-flight-rules.git

これで上流 (upstream) から fetch して最新のアップデートを入手できます。

$ (master) git fetch upstream
$ (master) git merge upstream/master

# 又は、一つのコマンドで行うなら
$ (master) git pull upstream master

コミットの編集

自分が何を今コミットしたか?

何も考えずに git commit -aを使って変更をコミットしただけで、今行ったコミットの実際の内容が何であるかはよく分からないとしましょう。 現在のHEADの指す最新のコミットを表示するには

(master)$ git show

又は

$ git log -n1 -p

とします。

特定のコミットでのファイルを見ることもできます( <commitid>は興味のあるコミットです)。

$ git show <commitid>:filename

コミットメッセージに間違ったことを書いた

間違ったコミットメッセージを書いたがコミットをまだプッシュしていない場合は、コミットの修正を変更せずにコミットメッセージだけを変更することができます。

$ git commit --amend --only

これによりデフォルトのテキストエディタが開き、そこでメッセージを編集できます。 一方、これをすべて1つのコマンドで行うこともできます。

$ git commit --amend --only -m 'xxxxxxx'

既にメッセージをプッシュしている場合は、コミットを修正して force push することができますがお勧めしません。

間違った設定の名前とメールアドレスでコミットしてしまった

それが単一のコミットであれば、次のようにして修正できます。

$ git commit --amend --no-edit --author "New Authorname <authoremail@mydomain.com>"

別の方法としては、 git config --global author。(name | email)で作者設定を正しく設定してから次のコマンドを実行します。

$ git commit --amend --reset-author --no-edit

履歴をすべて変更する必要がある場合は、 git filter-branchのmanページを参照してください。

前のコミットからファイルを削除したい

前のコミットからファイルの変更を削除するには、次の手順に従います。

$ git checkout HEAD^ myfile
$ git add myfile
$ git commit --amend --no-edit

ファイルが新しくコミットに追加され、それを(Gitだけから)削除したい場合は、次のようにします。

$ git rm --cached myfile
$ git commit --amend --no-edit

まだマージされていないパッチがあり、不要なファイルをコミットしていて、リモートでパッチを更新するために force push する必要がある場合、これは特に便利です。 --no-editオプションは既存のコミットメッセージを保持するために使用されます。

直前のコミットを削除したい

プッシュされたコミットを削除する必要があるなら以下を使うことができます。 しかし、履歴を不可逆的に変えてしまい、既にリポジトリから pull した他の人の履歴はめちゃくちゃになります。 つまり、よく分からない場合は絶対にしないでください。

$ git reset HEAD^ --hard
$ git push --force-with-lease [remote] [branch]

まだプッシュしていない場合は、Gitを最後のコミットを行う前の状態にリセットします(ステージングされた変更を維持したまま)。

(my-branch*)$ git reset --soft HEAD@{1}

これはプッシュしていない場合にのみうまくいきます。 プッシュしていた場合、本当に安全なやり方は git revert SHAofBadCommitだけです。 これにより、以前のコミットの変更をすべて元に戻す新しいコミットが作成されます。 あるいは、プッシュしたブランチがリベースに対して安全な場合(つまり、他の開発者がそこから pull することは想定されていない場合)は、単に git push --force-with-leaseを使用できます。 詳しくは上記のセクションを見てください。

任意のコミットを削除する

上記と同じ警告が適用されます。 可能であれば絶対に行わないでください。

$ git rebase --onto SHA1_OF_BAD_COMMIT^ SHA1_OF_BAD_COMMIT
$ git push --force-with-lease [remote] [branch]

またはインタラクティブな rebase を実行して、削除したいコミットに対応する行を削除します。

修正したコミットをリモートにpushしようとしたが、エラーメッセージが出た

To https://github.com/yourusername/repo.git
! [rejected]        mybranch -> mybranch (non-fast-forward)
error: failed to push some refs to 'https://github.com/tanay1337/webmaker.org.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

なお rebase(下記参照)と同様に、amend は古いコミットを新しいものに置き換えるので、amend する前のコミットをリモートに既に push しているときは force push( --force-with-lease)する必要があります。 これを行うときは必ずブランチを必ず指定してください。

(my-branch)$ git push origin mybranch --force-with-lease

一般的に、force pushは避けてください。 修正したコミットを force push するのではなく、新しいコミットを作成してプッシュすることをお勧めします。問題のブランチまたは任意の子ブランチに取り組んだ他の開発者のソース履歴に矛盾が生じるためです。 他の誰か同じブランチに取り組んでいる場合、 --force-with-leaseは失敗します。

誰も同じブランチで作業していないこと、またはブランチの先端を無条件でアップデートしたいことを絶対に確信している場合は、 --force-f)を使用できます。 しかし一般的には避けるべきです。

誤って hard reset をしたので元に戻したい

誤って git reset --hardを実行しても、gitは数日間すべてのログを保存しているので、通常はまだコミットを取り戻すことができます。

注意:これは作業がバックアップされている、すなわちコミットされているか stash されている場合にのみ有効です。 git reset --hardはコミットされていない変更を削除しますので、注意して使ってください。 (より安全なオプションは git reset --keepです。)

(master)$ git reflog

過去のコミットのリストとリセットのためのコミットが表示されます。 戻りたいコミットのSHAを選択して、リセットします。

(master)$ git reset --hard SHA1234

これで大丈夫なはずです。

誤ってマージをコミットして push してしまった

マージする準備が整う前に誤ってフィーチャーブランチをメインの開発ブランチにマージした場合でも、マージを元に戻すことができます。 しかし落とし穴があります。マージコミットには複数の親(通常は2つ)があります。

使うコマンドは以下です。

(feature-branch)$ git revert -m 1 <commit>

ここで -m 1 オプションは、元に戻す親として親の番号1(マージが行われたブランチ)を選択するように指示します。

注:親番号はコミットIDではありません。 むしろ、マージコミットには Merge:8e2ce2d 86ac2e7と書かれます。 親番号はこの行の目的の親の1から始まるインデックス、最初の識別子は番号1、2番目の識別子は番号2というように続きます。

秘密情報を含むファイルをコミットして push してしまった

誤って秘密情報や個人データ(パスワード、キーなど)を含むファイルをプッシュした場合は、以前のコミットを修正できます。 一度コミットをプッシュしたら、それに含まれる全てのデータが危険に晒されていると考えるべきであることを覚えておいてください。 これらのステップは公開リポジトリまたは自分のローカルコピーから秘密情報を削除することができますが、他人の pull したコピーから秘密情報を削除することはできません。 パスワードをコミットした場合は、すぐに変更してください。 キーをコミットした場合は、すぐに再生成してください。 その間に誰かが秘密情報を含む元のコミットを pull した可能性があるので、プッシュされたコミットを修正するだけでは不十分です。

ファイルを編集して機密データを削除した場合は、次のコマンドを実行してください。

(feature-branch)$ git add edited_file
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]

ファイル全体を削除したい(ローカルには保持する)場合は、次のコマンドを実行します。

(feature-branch)$ git rm --cached sensitive_file
echo sensitive_file >> .gitignore
(feature-branch)$ git add .gitignore
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]

あるいは、秘密情報をローカル環境変数に格納してください。

ファイル全体を完全に削除したい(ローカルにも保存したくない)場合は、次のコマンドを実行してください。

(feature-branch)$ git rm sensitive_file
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]

その間に他のコミットを行った場合(つまり、秘密情報が前回のコミットより前にコミットされている場合)、rebase する必要があります。

大きいファイルをリポジトリの全ての履歴から消したい

削除したいファイルが秘密または機密である場合は、代わりに秘密情報を含むファイルをコミットして push してしまったを参照してください。

最近のコミットで大きなファイルや不要なファイルを削除しても git の履歴に残っており、リポジトリの .gitフォルダに残っているので、git cloneは不要なファイルをダウンロードします。

この部分のフライトルールは force push を必要とし、リポジトリの履歴の大部分を書き換えるので、遠隔地にいるコラボレーターと仕事をしているのであれば、彼らがローカルの作業をプッシュしたことを確認して下さい。

履歴の書き換えには二つの選択肢があります。組み込みの git-filter-branchまたはbfg-repo-cleanerです。 bfgはとても綺麗で性能が優れていますが、サードパーティ製品をダウンロードしなければならず、javaを必要とします。両方の方法について説明します。最後のステップは、変更を force push することです。これには、大量のリポジトリの履歴が恒久的に変更されるため、通常の force push 以上に特に気をつける必要があります。

オススメのテク: サードーパーティーの bfg を使う

bfg-repo-cleanerを使用するにはjavaが必要です。 ここ からbfg jarをダウンロードしてください。 私たちの例では bfg.jarを使いますが、ダウンロードしたバイナリにはバージョン番号が付いているかもしれません。例えば bfg-1.13.0.jar のように。

特定のファイルを削除します。

(master)$ git rm path/to/filetoremove
(master)$ git commit -m "Commit removing filetoremove"
(master)$ java -jar ~/Downloads/bfg.jar --delete-files filetoremove

bfgでは、サブディレクトリにある場合でも、そのままのファイル名を使用する必要があります。

ファイルをパターンで削除することもできます。

(master)$ git rm *.jpg
(master)$ git commit -m "Commit removing *.jpg"
(master)$ java -jar ~/Downloads/bfg.jar --delete-files *.jpg

bfgを使用すると、最新のコミットに存在するファイルは影響を受けません。 たとえば、リポジトリに大きな.tgaファイルがいくつかあり、それらの一部を削除した場合、この呼び出しは最新のコミットにあるファイルには影響しません。

コミットでファイル名を変更した場合は注意してください。元々 LargeFileFirstName.mp4で、コミットがそれをLargeFileSecondName.mp4に変更した場合、 java -jar ~/Downloads/bfg.jar --delete-files LargeFileSecondName.mp4を実行しても git の履歴から削除されません。 両方のファイル名で、あるいはパターンマッチで --delete-filesコマンドを実行してください。

組み込みのテク: git-filter-branch を使う

git-filter-branchはもっと面倒で機能も少ないですが、bfgをインストールしたり実行することができない場合は使えます。

以下では、 filepatternの置き換えは特定の名前やパターンになります。 例えば \*.jpg のように。 これは全ての履歴とブランチからパターンにマッチするファイルを削除します。

(master)$ git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch filepattern' --prune-empty --tag-name-filter cat -- --all

何が起こっているかの説明:

--tag-name-filter cat は面倒ですが、cat コマンドを使って元のタグを新しいコミットに付けるシンプルな方法です。

--prune-empty は全ての空のコミットを取り除きます。

最後のステップ: 変更したリポジトリの履歴を push する

目的のファイルを削除したら、リポジトリのものが何も壊れていないかを慎重にテストします。壊れている場合は、リポジトリを複製して最初からやり直すのが最も簡単です。 終了するには、必要に応じてgit ガベージコレクションを使用してローカルの.gitフォルダサイズを最小化してから、force push します。

(master)$ git reflog expire --expire=now --all && git gc --prune=now --aggressive
(master)$ git push origin --force --tags

gitリポジトリの履歴全体を書き換えたので、 git pushの操作は大きすぎるかもしれず、「リモートエンドが突然ハングアップしました」というエラーを返すかもしれません。 このような場合は、git post bufferを増やしてみてください。

(master)$ git config http.postBuffer 524288000
(master)$ git push --force

これでうまくいかない場合は、リポジトリの履歴を手動で分割してコミットする必要があります。 以下のコマンドで、push操作が成功するまで <number>を増やしてみてください。

(master)$ git push -u origin HEAD~<number>:refs/head/master --force

プッシュが最初に成功したら、通常の git pushが成功するまで<number>を徐々に減らします。

最後以外のコミットの内容を変更したい

いくつか(例えば3つ)のコミットを作成した後で、最初のコミットに文脈上属していることをし忘れたことに気づいたと思います。 これらの変更を含む新しいコミットを作成するのであれば、クリーンなコードベースになるはずですが、コミットはアトミックではありませんでした(つまり、お互いに属する変更が同じコミットに含まれていなかったということです)。 そのような状況では、これらの変更が属するコミットを変更し、それらを含め、以降のコミットはそのままにします。 そのような場合、 git rebaseは役に立つかもしれません。

最後に行った3番目のコミットを変更したい状況を考えてください。

(your-branch)$ git rebase -i HEAD~4

対話的なリベースモードに入ります。このモードでは、最後の3つのコミットを編集できます。 テキストエディタがポップアップして、次のようなことを表示します。

pick 9e1d264 The third last commit
pick 4b6e19a The second to last commit
pick f4037ec The last commit

これを以下のように変えます。

edit 9e1d264 The third last commit
pick 4b6e19a The second to last commit
pick f4037ec The last commit

これは、"The third last commit"を編集し、他の2つは変更しないでおくことをrebaseに伝えます。 その後、エディタを保存して閉じます。 Gitはその後リベースを開始します。 変更したいコミットで停止し、そのコミットを編集することができます。 これで、最初にコミットをコミットしたときに適用しなかった変更を適用できます。まず編集し、ステージングします。 その後以下を実行します。

(your-branch)$ git commit --amend

これはGitにコミットを再度作成するように指示しますが、コミットメッセージは未編集のままにします。 これで難しい部分は解決されました。

(your-branch)$ git rebase --continue

これで残りの部分が実行されます。

ステージング

最後のコミットにステージングした変更を加えたい

(my-branch*)$ git commit --amend

コミットメッセージを変更したくないということが分かっている場合は、gitにコミットメッセージを再利用するように指示できます。

(my-branch*)$ git commit --amend -C HEAD

新しいファイルの一部をステージングしたい

通常、ファイルの一部をステージングしたい場合は、次のように実行します。

$ git add --patch filename.x

-pは短く動作します。 これにより対話モードが開きます。 コミットを分割するために sオプションを使うことができるでしょう。しかし、ファイルが新規のものなら、このオプションはありません。 新しいファイルを追加するには、以下を行います。

$ git add -N filename.x

それから、追加する行を手動で選択するために eオプションを使う必要があります。 git diff --cachedを実行するか git diff --stagedはどの行がステージされたかを、ローカルに保存したものと比較して表示します。

一つのファイルの変更2つのコミットに分けたい

git addはファイル全体をコミットに追加します。 git add -pで追加したい変更を対話的に選択できます。

大量の修正をステージングして、複数のコミットに分けたい

git reset -pはパッチモードのリセットダイアログを開きます。 これは git add -pに似ていますが、" yes "を選択すると変更のステージングが解除され、それが今後のコミットから削除される点が異なります。

ステージングしていない修正をステージングして、ステージングした修正をアンステージ (unstage) する

これはトリッキーです。 私が知っている最も良い方法は、ステージングされていない編集をstash することです。 その後、 reset してください。 その後、 stash した編集をポップして追加します。

$ git stash -k
$ git reset --hard
$ git stash pop
$ git add -A

ステージングしていない修正

ステージングしていない修正を新しいブランチに移したい

$ git checkout -b my-branch

ステージングしていない修正を別の既にあるブランチに移したい

$ git stash
$ git checkout my-branch
$ git stash pop

ローカルのコミットしていない変更を破棄したい (ステージングしていても、していなくてもよい

ーカルのステージングされた変更とステージングされていない変更を全て破棄したい場合は、次のようにします。

(my-branch)$ git reset --hard
# 又は
(master)$ git checkout -f

これは git addでステージングした全てのファイルをアンステージします。

$ git reset

これにより、コミットされていないローカルの変更が全て元に戻されます(リポジトリのルートで実行する必要があります)。

$ git checkout .

コミットしていない変更を特定のファイルまたはディレクトリに戻すこともできます。

$ git checkout [some_dir|file.txt]

コミットされていないすべての変更を元に戻すもう1つの方法は(入力は長くなりますが、任意のサブディレクトリから動きます)、

$ git reset --hard HEAD

これは全てのローカルの追跡されていないファイルを削除するので、Gitによって追跡されているファイルだけが残ります。

$ git clean -fd

-x も全ての ignore されたファイルを消去します。

ステージングしていない特定の変更を破棄したい

作業コピーの変更の一部を削除したいが、全ては削除したくない場合です。

望ましくない変更をチェックアウトし、良い変更はそのままにします。

$ git checkout -p
# 削除したいコード全てに y と入力して下さい

他の戦略は stashを使うことです。 すべての良い変更を stash し、作業コピーをリセットし、良い変更を再度適用します。

$ git stash -p
# 保存したいコード全てを選択して下さい
$ git reset --hard
$ git stash pop

あるいは、不要な変更を stash してから、 stash を drop します。

$ git stash -p
# 保存したくないコード全てを選択して下さい
$ git stash drop

ステージングしていない特定のファイルを破棄したい

作業コピーから特定のファイルを削除したい場合。

$ git checkout myFile

あるいは、作業コピー内の複数のファイルを破棄するには、それら全てをリストします。

$ git checkout myFirstFile mySecondFile

ステージングしていないローカルの変更だけ破棄したい

ステージングされていないローカルのコミットされていない変更をすべて取り除きたい場合

$ git checkout .

追跡していない全てのファイルを破棄したい

未追跡のファイルをすべて削除したい場合

$ git clean -f

特定のステージングしたファイルをアンステージ (unstage) したい

誤ってステージングされてしまった1つ以上のファイルがあり、それらのファイルが以前にコミットされていないことがあります。 それらをステージングを解除するには

$ git reset -- <filename>

これにより、ファイルのステージングが解除され、ファイルが追跡されなくなります。

ブランチ

全てのブランチをリストする

ローカルのブランチの一覧を見るには、

$ git branch

リモートのブランチの一覧を見るには、

$ git branch -r

全てのブランチ(ローカルとリモート両方)の一覧を見るには、

$ git branch -a

コミットからブランチを作る

$ git checkout -b <branch> <SHA1_OF_COMMIT>

誤ったブランチから pull してしまった

git reflogを使って HEAD が誤った pull の前にどこを指していたかを見ればよいです。

(master)$ git reflog
ab7555f HEAD@{0}: pull origin wrong-branch: Fast-forward
c5bc55a HEAD@{1}: checkout: checkout message goes here

ブランチを望ましいコミットにリセットします。

$ git reset --hard c5bc55a

これで完了です。

ローカルのコミットを捨てて、ローカルのブランチがリモートと同じになるようにしたい

変更をサーバーにプッシュしていないことを確認してください。

git statusは何個のコミットが origin より進んでいるかを表示します。

(my-branch)$ git status
# On branch my-branch
# Your branch is ahead of 'origin/my-branch' by 2 commits.
#   (use "git push" to publish your local commits)
#

(リモートのものと同じにするために)origin と一致するようにリセットする方法の一つは以下を行うことです。

(master)$ git reset --hard origin/my-branch

新しいブランチではなく master にコミットしてしまった

master にいる間に新しいブランチを作成します。

(master)$ git branch my-branch

master を以前のコミットにリセットします。

(master)$ git reset --hard HEAD^

HEAD^HEAD^1の略です。 これは HEADの最初の親を表し、同様にHEAD^2はコミットの2番目の親を表します(マージは2つの親を持つことができます)。

HEAD^2HEAD~2同じではないことに注意してください(詳細はこのリンクを参照して下さい)。

あるいは、HEAD^を使いたくない場合は、masterブランチに設定したいコミットハッシュを調べてください(git logで分かります)。 それからそのハッシュにリセットします。 git pushでこの変更がリモートに反映します。

例えば、master ブランチがいるべきコミットのハッシュが a13b85eであれば

(master)$ git reset --hard a13b85e
HEAD is now at a13b85e

新しいブランチをチェックアウトして作業を続けます。

(master)$ git checkout my-branch

別のブランチからファイルを取ってきたい

何百もの変更を加えた、ワーキングスパイクがあり(注を参照)、 全てがうまくいっているとします。 さて、その作業を保存するために別のブランチにコミットします。

(solution)$ git add -A && git commit -m "Adding all changes from this spike into one big commit."

それをブランチ(おそらくフィーチャーブランチか develop)に入れたいときは、ファイル全体を保存しておきたいはずです。 大きなコミットを小さなコミットに分割したいのです。

たとえば、

  • スパイクの解決策を含むブランチ solutionsolutiondevelopよりも一つだけ進んでいます。
  • 変更を加えたいブランチdevelop

コンテンツをブランチに持っていくことで解決できます。

(develop)$ git checkout solution -- file1.txt

これでsolutionブランチのファイルの内容をdevelopmentブランチに移せます。

# On branch develop
# Your branch is up-to-date with 'origin/develop'.
# Changes to be committed:
#  (use "git reset HEAD <file>..." to unstage)
#
#        modified:   file1.txt

その後、いつものようにコミットします。

注:スパイクソリューションは、問題を分析または解決するために作ります。 ソリューションは推定に使用され、誰もが問題をはっきりと見えた時点で破棄されます。Wikipedia

別々のブランチするべき複数のコミットを一つのブランチにしてしまった

master ブランチにいるとしましょう。 git logを実行すると、2回コミットしたことがわかります。

(master)$ git log

commit e3851e817c451cc36f2e6f3049db528415e3c114
Author: Alex Lee <alexlee@example.com>
Date:   Tue Jul 22 15:39:27 2014 -0400

    Bug #21 - Added CSRF protection

commit 5ea51731d150f7ddc4a365437931cd8be3bf3131
Author: Alex Lee <alexlee@example.com>
Date:   Tue Jul 22 15:39:12 2014 -0400

    Bug #14 - Fixed spacing on title

commit a13b85e984171c6e2a1729bb061994525f626d14
Author: Aki Rose <akirose@example.com>
Date:   Tue Jul 21 01:12:48 2014 -0400

    First commit

各バグのコミットハッシュに注目しましょう(#21の場合は e3851e8、#14の場合は5ea5173)。

まず、master ブランチを正しいコミット( a13b85e)にリセットしましょう:

(master)$ git reset --hard a13b85e
HEAD is now at a13b85e

これで、バグ#21のために新しいブランチを作ることができます。

(master)$ git checkout -b 21
(21)$

それでは、ブランチ上にあるバグ#21のコミットをcherry-pickしましょう。 つまり、そのコミットだけを適用し、そのコミットをHEAD の上に置きます。

(21)$ git cherry-pick e3851e8

この時点で、競合がある可能性があります。 競合を解決する方法については、複数のコミットを一つにまとめたいの章のコンフリクトがある のセクションを見て下さい。

それでは master からバグ#14のための新しいブランチを作成しましょう。

(21)$ git checkout master
(master)$ git checkout -b 14
(14)$

そして最後に、バグ#14のコミットを cherry-pick しましょう。

(14)$ git cherry-pick 5ea5173

削除した上流 (upstream) のブランチのローカルブランチを削除したい

GitHubでプルリクエストをマージすると、マージされたブランチをフォークから削除することができます。 そのブランチで作業を続ける予定がない場合は、ブランチのローカルコピーを削除して、古くなったブランチで散らからないようにしましょう。

$ git fetch -p upstream

ここで upstream は fetch したいリモートです。

誤ってブランチを消してしまった

定期的にリモートにプッシュしているなら、ほとんどの場合なんとかなるでしょう。 しかしそれでも branch を削除してしまうこともあるでしょう。 ブランチを作成して新しいファイルを作成するとしましょう。

(master)$ git checkout -b my-branch
(my-branch)$ git branch
(my-branch)$ touch foo.txt
(my-branch)$ ls
README.md foo.txt

add してコミットします。

(my-branch)$ git add .
(my-branch)$ git commit -m 'foo.txt added'
(my-branch)$ foo.txt added
 1 files changed, 1 insertions(+)
 create mode 100644 foo.txt
(my-branch)$ git log

commit 4e3cd85a670ced7cc17a2b5d8d3d809ac88d5012
Author: siemiatj <siemiatj@example.com>
Date:   Wed Jul 30 00:34:10 2014 +0200

    foo.txt added

commit 69204cdf0acbab201619d95ad8295928e7f411d5
Author: Kate Hudson <katehudson@example.com>
Date:   Tue Jul 29 13:14:46 2014 -0400

    Fixes #6: Force pushing after amending commits

さて、master に戻って、「誤って」ブランチを削除してしまったとします。

(my-branch)$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
(master)$ git branch -D my-branch
Deleted branch my-branch (was 4e3cd85).
(master)$ echo oh noes, deleted my branch!
oh noes, deleted my branch!

改良されたロガーである 'reflog'に精通して下さい。 リポジトリ内のすべてのアクションの履歴を保存します。

(master)$ git reflog
69204cd HEAD@{0}: checkout: moving from my-branch to master
4e3cd85 HEAD@{1}: commit: foo.txt added
69204cd HEAD@{2}: checkout: moving from master to my-branch

ご覧のとおり、削除されたブランチからのコミットハッシュがあります。 削除したブランチを元に戻すことができるかどうか見てみましょう。

(master)$ git checkout -b my-branch-help
Switched to a new branch 'my-branch-help'
(my-branch-help)$ git reset --hard 4e3cd85
HEAD is now at 4e3cd85 foo.txt added
(my-branch-help)$ ls
README.md foo.txt

ほら! 削除したファイルを元に戻せました。 git reflogはリベースがひどく間違っているときにも役に立ちます。

ブランチを消したい

リモートブランチを削除するには

(master)$ git push origin --delete my-branch

別のやり方としては

(master)$ git push origin :my-branch

ローカルブランチを削除するには

(master)$ git branch -d my-branch

現在のブランチや上流 (upstream) にマージされてないローカルのブランチを削除するには

(master)$ git branch -D my-branch

複数のブランチを消したい

例えば、fix/ で始まるブランチを全て消すには

(master)$ git branch | grep 'fix/' | xargs git branch -d

ブランチ名を変えたい

現在の(ローカルの)ブランチの名前を変えるには

(master)$ git branch -m new-name

別の(ローカルの)ブランチの名前を変えるには

(master)$ git branch -m old-name new-name

他の人が作業中のリモートブランチをチェックアウトしたい

まずリモートから全てのブランチを fetch します。

(master)$ git fetch --all

例えば、リモートにあるブランチdavesをチェックアウトしたいとします。

(master)$ git checkout --track origin/daves
Branch daves set up to track remote branch daves from origin.
Switched to a new branch 'daves'

(--track is shorthand for git checkout -b [branch] [remotename]/[branch])

これでローカルにブランチdavesをコピーします。リモートにプッシュされた更新も表示されます。

現在のローカルブランチから新しいリモートブランチを作りたい

$ git push <remote> HEAD

もしリモートのブランチを現在のブランチの上流 (upstream) として設定もしたいなら、代わりに以下のコマンドを使います。

$ git push -u <remote> HEAD

push.defaultupstreamモードとsimpleモード (Git 2.0 のデフォルトの設定です)になっていると、-uで登録されていたリモートブランチに現在のブランチをプッシュします。

$ git push

git pushの他のモードの振る舞いは push.defaultのドキュメント に記載されています。

リモートブランチをローカルブランチの上流 (upstream) として設定したい

次のようにして、リモートブランチを現在のローカルブランチの上流 (upstream) として設定できます。

$ git branch --set-upstream-to [remotename]/[branch]
# 又は以下の略記も使えます
$ git branch -u [remotename]/[branch]

上流のリモートブランチを別のローカルブランチに設定するには

$ git branch -u [remotename]/[branch] [local-branch]

HEADがデフォルトのリモートブランチを追跡するようにしたい

リモートブランチをチェックすることによって、HEAD がどのリモートブランチを追跡しているかを見ることができます。 場合によっては、望ましいブランチではありません。

$ git branch -r
  origin/HEAD -> origin/gh-pages
  origin/master

origin/HEADorigin/masterを追跡するように変更するには、次のコマンドを実行します。

$ git remote set-head origin --auto
origin/HEAD set to master

誤ったブランチに変更を加えてしまった

変更をしたがまだコミットしていない段階で、間違ったブランチにいることに気づきました。 変更を stash して必要なブランチに適用します。

(wrong_branch)$ git stash
(wrong_branch)$ git checkout <correct_branch>
(correct_branch)$ git stash apply

rebase とマージ

rebase/マージを取り消す

現在のブランチに間違ったブランチをマージしたりリベースしたのに、何が起きたか理解できなかったりリベースやマージを完了することができないことがあります。 Gitは危険な操作をする前に元のHEADポインタをORIG_HEADと呼ばれる変数に保存するので、リベース/マージの前の状態にブランチを復元するのは簡単です。

(my-branch)$ git reset --hard ORIG_HEAD

rebase したが force push はしたくない

残念ながら、それらの変更をリモートブランチに反映させたい場合は、 force push する必要があります。 履歴を変えたからです。 force push しない限り、リモートブランチは変更を受付けません。 これが、多くの人がリベースワークフローではなくマージワークフローを使用する主な理由の1つです。大規模なチームが開発者の force push で問題に陥る可能性があります。 これするときは慎重になってください。 リベースを使用するより安全な方法は、リモートブランチに変更をまったく反映させず、代わりに以下をすることです:

(master)$ git checkout my-branch
(my-branch)$ git rebase -i master
(my-branch)$ git checkout master
(master)$ git merge --ff-only my-branch

この StackOverflow のスレッド を参照して下さい。

複数のコミットを一つにまとめたい

masterに対するプルリクエストとなるブランチに取り組んでいるとしましょう。 最も簡単な場合は、全てのコミットを1つにまとめてコミットタイムスタンプを気にする必要がない場合で、リセットして再度コミットできます。 master ブランチが最新のものであり、すべての変更がコミットされていることを確認してください。

(my-branch)$ git reset --soft master
(my-branch)$ git commit -am "New awesome feature"

もっと細かく制御したい、そしてタイムスタンプを保持したいのなら、インタラクティブリベースをする必要があります。

(my-branch)$ git rebase -i master

もし他のブランチに対して作業をしていないのであれば、HEADを基準にして rebase する必要があります。 たとえば、最後の2つのコミットを squash したい場合は、 HEAD~2に対してリベースする必要があります。 最後の3つをするなら、HEAD~3となります。

(master)$ git rebase -i HEAD~2

インタラクティブな rebase コマンドを実行すると、テキストエディタに次のようなものが表示されます。

pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
pick b729ad5 fixup
pick e3851e8 another fix

# Rebase 8074d12..b729ad5 onto 8074d12
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

で始まる行はすべてコメントで、リベースには影響しません。

pickコマンドを上の一覧のどれかに置き換えたり、対応する行が削除してコミットを削除することができます。

たとえば、最も古い(最初の)コミットをそのままにして、その後に続くすべてのコミットを2番目に古いコミットと結合する場合は、最初と2番目以外の各コミットの横の文字を編集して fとします。 :

pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
f b729ad5 fixup
f e3851e8 another fix

これらのコミットを組み合わせてコミットの名前を変更したい場合は、2番目のコミットの隣にさらに rを追加するか、単にfの代わりに sを使用してください。

pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
s b729ad5 fixup
s e3851e8 another fix

ポップアップされる次のテキストプロンプトでコミットの名前を変えられます。

Newer, awesomer features

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress; onto 8074d12
# You are currently editing a commit while rebasing branch 'master' on '8074d12'.
#
# Changes to be committed:
#   modified:   README.md
#

すべてが成功すれば、次のようになるはずです。

(master)$ Successfully rebased and updated refs/heads/master.

安全なマージの仕方

--no-commitはマージを実行しますが、失敗したマージのように見せかけて自動コミットしないので、コミットする前にマージ結果を調べてさらに微調整することができます。 no-ffはフィーチャーブランチがかつて存在していたという証拠を維持し、プロジェクト履歴を一貫したものにします。

(master)$ git merge --no-ff --no-commit my-branch

ブランチをマージして一つのコミットにしたい

(master)$ git merge --squash my-branch

push していない複数のコミットだけを一つのコミットにしたい

上流 (upstream) にプッシュする前に一つにしたいいくつかの進行中のコミットを持っていることがあります。 すでに上流にプッシュされているコミットを誤って一つにしたくない場合があります。他の誰かがすでにそれらを参照するコミットをした可能性があるからです。

(master)$ git rebase -i @{u}

プッシュしていないコミットだけの一覧を表示し、対話的なリベースが始まるでしょう。安全に一覧の中のコミットを並べ替え/修正/squash することができます。

マージを中断 (abort) する

マージによって特定のファイルに問題が発生することがあります。その場合はオプション abortを使って現在の競合解決プロセスを中止し、マージ前の状態を再構築します。

(my-branch)$ git merge --abort

このコマンドは バージョン 1.7.4 以上の Git で使えます。

ブランチのコミットの親を更新したい

master ブランチ、master から分岐したfeature-1ブランチ、およびfeature-1から分岐したfeature-2ブランチがあるとします。 feature-1にコミットすると、feature-2の親のコミットは正確ではなくなります(feature-2 はfeature-1 から分岐したので、feature-2 の親は feature-1の先頭になるはずです)。 これを git rebase --ontoで修正できます。

(feature-2)$ git rebase --onto feature-1 <the first commit in your feature-2 branch that you don't want to bring along> feature-2

まだマージされていない別のフィーチャーブランチからフィーチャーブランチを切り、feature-1 ブランチのバグ修正をfeature-2ブランチに反映させる必要がる厄介な場合にこの方法は役に立ちます。

ブランチの全てのコミットがマージされたか確かめる

ブランチ上のすべてのコミットが別のブランチにマージされているかどうかを確認するには、それらのブランチの先頭(または任意のコミット)の間で差分をとる必要があります。

(master)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature/120-on-scroll

一方にコミットがあり、他方にはコミットがないかかを表示し、ブランチ間で共有できていない行の一覧を表示します。 もう1つの選択肢は次のようにすることです。

(master)$ git log master ^feature/120-on-scroll --no-merges

インタラクティブな rebase で起こりうる問題

リベースの修正画面に noop と表示された

次のようなメッセージを見たとします。

noop

これは、同じコミットのブランチ、または現在のブランチよりも進んでいるブランチに対して rebase しようとしているということです。 次のことをしてみましょう。

  • master ブランチがしかるべき場所にあることを確認する
  • 代わりに HEAD~2以前に対して rebase する

コンフリクトがある

rebase を正常に完了できない場合は、競合を解決する必要があります。

最初に git statusを実行してどのファイルが競合しているのかを確認してください。

(my-branch)$ git status
On branch my-branch
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

  both modified:   README.md

この例では、 README.mdが競合しています。 そのファイルを開き、以下を探します。

   <<<<<<< HEAD
   some code
   =========
   some code
   >>>>>>> new-commit

新しいコミットで追加されたコード(この例では、中央の行から new-commitまでの行すべて)とHEADの diff を解決する必要があります。

あるブランチのバージョンのコードをチェックアウトしたい場合は、 --oursまたは--theirsを使います。

(master*)$ git checkout --ours README.md
  • マージするときは、ローカルブランチからの変更を保持するには --oursを、他のブランチからの変更を保持するには--theirsを使用します。
  • rebase するときは、ローカルブランチからの変更を保持するには --theirs、他のブランチからの変更を保持するには--oursを使用します。

なぜ逆になるかの説明は、Gitドキュメントのこのメモを参照してください。

マージがもっと複雑な場合は、視覚的な diff エディタを使用できます。

(master*)$ git mergetool -t opendiff

すべての競合を解決してコードをテストしたら、変更したファイルを git addしてからgit rebase --continueで rebase を続けます。

(my-branch)$ git add README.md
(my-branch)$ git rebase --continue

すべての衝突を解決した後でコミット前と同じツリーになった場合は、代わりに git rebase --skipを実行する必要があります。

リベースを中止してブランチの元の状態に戻りたい場合は、いつでも次のようにして中止できます。

(my-branch)$ git rebase --abort

Stash

全ての修正を stash する

作業ディレクトリのすべての編集内容を stash するには

$ git stash

追跡されていないファイルも stash したい場合は、-uオプションを使用してください。

$ git stash -u

特定のファイルを stash する

作業ディレクトリからファイルを1つだけ stash するには

$ git stash push working-directory-path/filename.ext

作業ディレクトリから複数のファイルを stash するには

$ git stash push working-directory-path/filename1.ext working-directory-path/filename2.ext

メッセージ付きで stash する

$ git stash save <message>

リスト中の stash を適用する

まずメッセージ付きの stash の一覧を確認して下さい。

$ git stash list

そして一覧から特定の stash を適用します

$ git stash apply "stash@{n}"

ここで 'n'はスタック内の stash の位置を示します。 一番上の stash は位置0になります。

検索

任意のコミットから文字列を検索する

任意のコミットで入れられた特定の文字列を検索するために、以下の仕組みを使うことができます:

$ git log -S "string to find"

共通のパラメータ:

  • --sourceは各コミットが達成されたコマンドラインで与えられた参照名を表示することを意味します。

  • --allは全てのブランチから始まることを意味します。

  • --reverseは逆の順番で表示します。つまり、変更を加えた最初のコミットを表示します。

著者/コミッタで検索する

作者/コミッタごとにすべてのコミットを見つけるには、次のコマンドを使用できます。

$ git log --author=<name or email>
$ git log --committer=<name or email>

作者とコミッタは同じではないことに留意してください。 --authorは最初にコードを書いた人です。 一方、 --committerは最初の作者の代わりにコードをコミットした人です。

特定のファイルを含むコミットの一覧を出す

特定のファイルを含むすべてのコミットを見つけるには、次のコマンドを使用できます。

$ git log -- <path to file>

通常は正確なパスを指定しますが、パスとファイル名にはワイルドカードを使用することもできます。

$ git log -- **/*.js

ワイルドカードを使用するとき、コミットされたファイルの一覧を見るために --name-statusを使うと便利です。

$ git log --name-status -- **/*.js

特定の関数のコミット履歴を見る

ある関数の変化を追跡するには、次のようにします。

$ git log -L :FunctionName:FilePath

revision rangecommit limitsのようなgit logのオプションを更に組み合わせることができます。

コミットが参照されているタグを検索する

特定のコミットを含むすべてのタグを見つけるには

$ git tag --contains <commitid>

サブモジュール

全てのサブモジュールをクローンする

$ git clone --recursive git://github.com/foo/bar.git

もし既にクローンしているなら

$ git submodule update --init --recursive

サブモジュールを削除する

サブモジュールを作成するのはとても簡単ですが、削除するのはそれほど簡単ではありません。 必要なコマンドは次のとおりです。

$ git submodule deinit submodulename
$ git rm submodulename
$ git rm --cached submodulename
$ rm -rf .git/modules/submodulename

様々なオブジェクト

削除したファイルを復活する

まずファイルが存在した最後のコミットを見つけます。

$ git rev-list -n 1 HEAD -- filename

そのファイルをチェックアウトします。

git checkout deletingcommitid^ -- filename

タグを削除する

$ git tag -d <tag_name>
$ git push <remote> :refs/tags/<tag_name>

削除したタグを復活する

すでに削除されたタグを復元したい場合は、次の手順に従えばできます。まず、到達不能なタグを見つける必要があります。

$ git fsck --unreachable | grep tag

タグのハッシュを書き留めます。 その後、git update-refを利用して、削除したタグを次のように復元します。

$ git update-ref refs/tags/<tag_name> <hash>

タグは復元されたはずです。

削除されたパッチ

誰かが GitHub でプルリクエストを送ってから、元々のフォークを削除した場合、そのリポジトリをクローンすることも、[giff am]を.diff、.patchとして使うこともできないでしょう。しかし、GitHubの特殊な参照を使ってPR自体をチェックアウトすることができます。 PR#1の内容をpr_1という新しいブランチに fetch するには、次の手順を実行します。

$ git fetch origin refs/pull/1/head:pr_1
From github.com:foo/bar
 * [new ref]         refs/pull/1/head -> pr_1

リポジトリを zip ファイルとしてエクスポートする

$ git archive --format zip --output /full/path/to/zipfile.zip master

ブランチをプッシュしてタグを同じ名前にする

ブランチと同じ名前のタグがリモートリポジトリにある場合、標準の $ git push <remote> <branch>コマンドでそのブランチを push しようとすると以下のエラーが発生します。

$ git push origin <branch>
error: dst refspec same matches more than one.
error: failed to push some refs to '<git server>'

これを修正するには、ヘッド参照を push するように指示します。

$ git push origin refs/heads/<branch-name>

ブランチと同じ名前のリモートリポジトリにタグをプッシュしたい場合は、同様のコマンドを使用できます。

$ git push origin refs/tags/<tag-name>

ファイルの追跡

ファイル内容は変えずにファイル名の大文字/小文字を変えたい

(master)$ git mv --force myfile MyFile

pull するときにローカルのファイルを上書きしたい

(master)$ git fetch --all
(master)$ git reset --hard origin/master

Git からファイルを取り除きたいが、ファイルを消去したくない

(master)$ git rm --cached log.txt

特定のバージョンにファイルを戻したい (revert)

必要なコミットのハッシュがc5f567であると仮定します。

(master)$ git checkout c5f567 -- file1/to/restore file2/to/restore

c5f567の前に1コミットだけ行った変更に戻したい場合は、コミットハッシュをc5f567~1として渡します。

(master)$ git checkout c5f567~1 -- file1/to/restore file2/to/restore

コミット間やブランチ間の変更の一覧を見たい

前回のコミットとコミットc5f567のファイルを比較したいとします。

$ git diff HEAD:path_to_file/file c5f567:path_to_file/file

ブランチに対しても同様です。

$ git diff master:path_to_file/file staging:path_to_file/file

特定のファイルへの変更を無視するようにしたい

これは、設定のテンプレートや、コミットしてはいけない秘密情報をローカルに追加する必要がある場合に役に立ちます。

$ git update-index --assume-unchanged file-to-ignore

これはソース管理からファイルを削除しないことに注意してください。ローカルでのみ無視されます。 これを元に戻して、Gitに変更に再び気付くように指示するために、ignore フラグをクリアします。

$ git update-index --no-assume-unchanged file-to-stop-ignoring

Git でデバッグしたい

git-bisectコマンドは二部探索を使用して、Git履歴のどのコミットがバグを引き起こしたかを見つけます。

masterブランチにいて、何らかの機能を壊したコミットを見つけたいとしましょう。 二等分をし始めます。

$ git bisect start

そして、どのコミットが悪いのか、どのコミットが良いと分かっているのかを指定する必要があります。 あなたの現在のバージョンが悪く、 v1.1.1が良いと仮定します。

$ git bisect bad
$ git bisect good v1.1.1

git-bisectは今指定した範囲の真ん中でコミットを選択しチェックアウトして、それが良いか悪いかを尋ねます。 次のようなものが出るはずです。

$ Bisecting: 5 revision left to test after this (roughly 5 step)
$ [c44abbbee29cb93d8499283101fe7c8d9d97f0fe] Commit message
$ (c44abbb)$

今度はこのコミットが良いのか悪いのかを確認します。 もしそれが良ければ

$ (c44abbb)$ git bisect good

そして git-bisectは範囲から別のコミットを選択します。 このプロセス( goodまたはbadを選択する)は、検査するリビジョンがなくなるまで繰り返され、最後に最初の 悪いコミットの説明が出力されます。

設定

Git コマンドに別名を付けたい

OS XとLinuxでは、git設定ファイルは ~/.gitconfigに格納されています。 私がショートカットとして[alias]セクションで使用するエイリアスの例(及び私が よく犯してしまうタイプミス)を以下に挙げます。

[alias]
    a = add
    amend = commit --amend
    c = commit
    ca = commit --amend
    ci = commit -a
    co = checkout
    d = diff
    dc = diff --changed
    ds = diff --staged
    extend = commit --amend -C HEAD
    f = fetch
    loll = log --graph --decorate --pretty=oneline --abbrev-commit
    m = merge
    one = log --pretty=oneline
    outstanding = rebase -i @{u}
    reword = commit --amend --only
    s = status
    unpushed = log @{u}
    wc = whatchanged
    wip = rebase -i @{u}
    zap = fetch -p
    day = log --reverse --no-merges --branches=* --date=local --since=midnight --author=\"$(git config --get user.name)\"
    delete-merged-branches = "!f() { git checkout --quiet master && git branch --merged | grep --invert-match '\\*' | xargs -n 1 git branch --delete; git checkout --quiet @{-1}; }; f"

リポジトリに空のディレクトリを追加したい

できません。 Gitはこれをサポートしていませんが、ハックがあります。 ディレクトリに次の内容の.gitignoreファイルを作成できます。

 # ディレクトリ下の全てのファイルを ignore する
 *
 # ただしこのファイルは除く
 !.gitignore

もう1つの一般的な慣習は、.gitkeepという名前の空のファイルをフォルダ内に作成することです。

$ mkdir mydir
$ touch mydir/.gitkeep

ファイルの名前を.keepとすることもできます。その場合、上の2行目は touch mydir/ .keepになります。

リポジトリへのユーザ名とパスワードをキャッシュしたい

認証が必要なリポジトリがあるかもしれません。 その場合、ユーザー名とパスワードをキャッシュすることができるので、プッシュとプルのたびにそれを入力する必要はありません。 認証情報ヘルパーがこれをやってくれます。

$ git config --global credential.helper cache
# git に認証情報をキャッシュさせる
$ git config --global credential.helper 'cache --timeout=3600'
# キャッシュは一時間だけ有効(秒で指定する)

認証情報ヘルパーを検索するには

$ git help -a | grep credential
# 認証ヘルパーの候補を表示する

OS 固有の認証情報のキャッシュは

$ git config --global credential.helper osxkeychain
# OS X 向け
$ git config --global credential.helper manager
# Windows 2.7.3+ 向けの Git
$ git config --global credential.helper gnome-keyring
# Ubuntu や他のGNOMEベースのディストリビューション

様々なディストリビューションやオペレーティングシステムに対して、もっと多くの認証ヘルパーが見つかるかもしれません。

パーミッションとファイルモードの変更を Git に無視させたい

$ git config core.fileMode false

これをログインユーザーのデフォルトの動作にしたい場合は、次のようにします。

$ git config --global core.fileMode false

グローバルユーザを設定したい

全てのローカルリポジトリで使用されるユーザー情報を設定し、バージョン履歴を確認するときにクレジットとして分かる名前を設定するには

$ git config --global user.name “[firstname lastname]

各履歴マーカーに紐付けられるE メールアドレスを設定するには

git config --global user.email “[valid-email]

コマンドラインに Git 用の色を付けたい

簡単にレビューできるようにGitの自動のコマンドラインカラーリングを設定するには

$ git config --global color.ui auto

何が間違えたのかさっぱり分からない

つまり、あなたは混乱してしまいました。 何かを「リセット」したか、間違ったブランチをマージしたか、あるいは force push してコミットを見つけることができません。 ある時点で、大丈夫だったことを知っています。そして以前の状態に戻したいと思っているとします。

こういう時 git reflogはうってつけです。 reflogは、たとえその先端がブランチやタグによって参照されていなくても、ブランチの先端に対するいかなる変更も追跡します。 基本的に、HEADが変わるたびに、新しいエントリがreflogに追加されます。 残念ながら、これはローカルリポジトリでのみ機能し、動きだけ追跡します(例えば、どこにも記録されていないファイルへの変更は追跡されません)。

(master)$ git reflog
0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to master
c10f740 HEAD@{2}: checkout: moving from master to 2.2

上のreflogはmasterから2.2ブランチへのチェックアウトとその逆のチェックアウトを示しています。 そこから、古いコミットへのハードリセットがあります。 最新の動きは HEAD @ {0}とラベルされた一番上に表示されます。

誤って戻ったことが判明した場合は、、誤って2つのコミットを削除する前の(0254ea7)を指すコミットマスターがreflogに含まれます。

$ git reset --hard 0254ea7

git resetを使うと、master を以前のコミットに戻すことができます。 これは、履歴が誤って変更された場合の安全策となります。

Sourceからコピーして編集しました)。

Git ショートカット

Git Bash

上記のコマンドの動作に慣れたら、Git Bashのショートカットをいくつか作成したくなるかもしれません。 これにより、複雑な作業を、本当に短いコマンドで、本当に素速く行えるようになります。

alias sq=squash

function squash() {
    git rebase -i HEAD~$1
}

これらのコマンドを .bashrc や .bash_profileにコピーして下さい。

Windows の PowerShell

WindowsでPowerShellを使用している場合は、エイリアスと関数も設定できます。 これらのコマンドをプロファイルに追加してください。そのパスは $ profile変数で定義されています。 MicrosoftのドキュメントサイトのAbout Profilesページで詳細を確認してください。

Set-Alias sq Squash-Commits

function Squash-Commits {
  git rebase -i HEAD~$1
}

他の文献

書籍

チュートリアル

スクリプトとツール

  • firstaidgit.io 最もよく寄せられるGitの質問の検索可能なセレクション
  • git-extra-commands - 便利なGitスクリプトのコレクション
  • git-extras - GITユーティリティ - リポジトリの概要、REPL、更新履歴の統計、作者のコミット率など
  • git-fire - 緊急時にすべての現在のファイルを追加し、コミットし、(マージの競合を防ぐために)新しいブランチにプッシュしてくれるGitプラグインです。
  • git-tips - ちょっとしたGitのtips
  • git-town - 一般的な、ハイレベルのGitのワークフローのサポート http://www.git-town.com

GUI クライアント

  • GitKraken - Windows、MacおよびLinux用の、実に豪華なGitクライアント
  • git-cola - WindowsとOS X用の別のGitクライアント
  • GitUp - Git の複雑さへの独特の対処策を備えた新しいGUI
  • gitx-dev - OS X の別のグラフィカルなGit クライアント
  • Sourcetree - Windows と Mac 向けの美しさを兼ね備えた無料のパワフルなGit GUI
  • Tower - OS X 向けのグラフィカルなGitクライアント (有料)
  • tig - ターミナルのテキストベースのGitインターフェース
  • Magit - Emacs パッケージとして実装されたGitのインターフェース
  • GitExtensions - シェル拡張、Visual Studio 2010-2015プラグイン、スタンドアロンのGitリポジトリツール
  • Fork - 速くてフレンドリーなMac向けのGUI クライアント (ベータ版)
  • gmaster - 3者間マージ、リファクタリングの分析、セマンティックdiff、およびマージができるWindows 向けのGitクライアント(ベータ版)
  • gitk - リポジトリの状態を簡潔に表示できるLinux向けのGitクライアント。
  • SublimeMerge - 3者間マージ、強力な検索およびシンタックスハイライトを提供する、非常に高速で拡張可能なクライアント(アクティブに開発中)。

訳者:堀田(twitter: @YoshiHotta