堀田(@YoshiHotta)です。この記事はGithubで3万スター⭐以上を集めた人気リポジトリ git-flight-rules の翻訳です。
Git はエンジニアが毎日何十回も使うコマンドであるにも関わらず、難しいツールです。 コマンドを間違えて、元に戻そうとあれこれ試しているうちにもっと悲惨な状況になり、復旧できなくなった経験が誰でも一度はあるのではないでしょうか。
Git でトラブルに見舞われてもこの Git フライトルールがあれば安心です! このガイドで対処策が必ず見つかります。
落ち着いてガイドの手順に従えばトラブルから脱出できます。
毎日Git を使う仕事を何年もしていますが、今でも困ることがよくあります。そういう時は git flight rules をすぐに参照していました。本家に日本語版がなかったので、仕方がなくずっと母語ではない英語のバージョンを読んでいました。塵も積もれば山となるで結構な時間をロスしていたと思います。読者の皆さんには Git で困って欲しくない、母語でこの素晴らしい Git フライトルールを読んで、git の操作ミスで苦労して書いたコードを吹き飛ばして欲しくないという思いから、この長い長いガイドを翻訳しました。
Git 初心者からベテランまでぜひ手元に置いておいて下さい。
※ この記事は Attribution-ShareAlike 4.0 International ライセンス で配布します。
目次
- 「フライトルール」とは何ですか?
- 文書規約
- リポジトリ
- コミットの編集
- ステージング
- ステージングしていない修正
- ブランチ
- 全てのブランチをリストする
- コミットからブランチを作る
- 誤ったブランチから pull してしまった
- ローカルのコミットを捨てて、ローカルのブランチがリモートと同じになるようにしたい
- 新しいブランチではなく master にコミットしてしまった
- 別のブランチからファイルを取ってきたい
- 別々のブランチするべき複数のコミットを一つのブランチにしてしまった
- 削除した上流 (upstream) のブランチのローカルブランチを削除したい
- 誤ってブランチを消してしまった
- ブランチを消したい
- 複数のブランチを消したい
- ブランチ名を変えたい
- 他の人が作業中のリモートブランチをチェックアウトしたい
- 現在のローカルブランチから新しいリモートブランチを作りたい
- リモートブランチをローカルブランチの上流 (upstream) として設定したい
- HEADがデフォルトのリモートブランチを追跡するようにしたい
- 誤ったブランチに変更を加えてしまった
- rebase とマージ
- Stash
- 検索
- サブモジュール
- 様々なオブジェクト
- ファイルの追跡
- Git でデバッグしたい
- 設定
- 何が間違えたのかさっぱり分からない
- Git ショートカット
- 文献
「フライトルール」とは何ですか?
問題が発生した場合の対処方法についての、宇宙飛行士のためのガイドです。今の場合、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^2
はHEAD~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
)に入れたいときは、ファイル全体を保存しておきたいはずです。 大きなコミットを小さなコミットに分割したいのです。
たとえば、
- スパイクの解決策を含むブランチ
solution
。solution
はdevelop
よりも一つだけ進んでいます。 - 変更を加えたいブランチ
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.default
がupstream
モードと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/HEAD
がorigin/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 rangeやcommit 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 }
他の文献
書籍
- Learn Enough Git to Be Dangerous - Michael Heart の Git の基礎をカバーしている本
- Pro Git - Scott Chacon と Ben Straub の Git についての優れた本
- Git Internals - Scott Chacon のGit についての別の優れた本
チュートリアル
- 19 Git Tips For Everyday Use - 便利なGit のワンライナー集
- Atlassian's Git tutorial 初心者から上級者までチュートリアルを読んでGitを正しく使おう
- Learn Git branching インタラクティブなWebベースのブランチ/マージ/リベースのチュートリアル
- Getting solid at Git rebase vs. merge
- Git Commands and Best Practices Cheat Sheet - Gitについて沢山説明しているブログ
- Git from the inside out - Gitの内部に迫るチュートリアル
- git-workflow - Aaron Meurer のGitを使ってどうやってオープンソースリポジトリに貢献するかの解説
- GitHub as a workflow - GitHubをワークフローとして使用することに関する興味深い取り組み (特に空のPRで)
- Githug - より一般的なGitワークフローを学ぶためのゲーム
- learnGitBranching - インタラクティブなGitの可視化にチャレンジして学ぼう!
スクリプトとツール
- 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)