Git の元に戻すオプション
Gitでは何も削除されないので、Gitで作業するときは作業を取り消すことができます。
すべてのバージョン管理システムには、作業を取り消すためのオプションがあります。しかし、Gitの非中央集権的な性質のために、これらのオプションは倍増します。あなたが取るアクションは、開発者のステージに基づいています。
GitとGitLabでの作業について、詳しくはこちらをご覧ください:
- North Western Mutualがエンタープライズ・ソースコード管理にGitLabを選んだ理由をご覧ください。
- Git を始める方法を学びましょう。
- より高度な例については、Git bookを参照してください。
変更を取り消せるとき
Git の標準的なワークフローの場合:
- ファイルを作成あるいは編集します。ファイルはステージされていない状態から始まります。新しいファイルであれば、Gitによってまだ追跡されていません。
- ローカルリポジトリ (
git add
) にファイルを追加すると、ファイルはステージされた状態になります。 - ファイルをローカルリポジトリ (
git commit
) にコミットします。 - その後、リモートリポジトリ (
git push
) にコミットすることで、他の開発者とファイルを共有することができます。
このワークフローでは、いつでも変更を取り消すことができます:
- ローカルで作業していて、まだリモートリポジトリにプッシュしていない場合。
- すでにリモートリポジトリにプッシュしていて、プッシュしたい場合:
ローカル変更の取り消し
変更をリモートリポジトリにプッシュするまでは、Git で行った変更はローカルの開発環境にしか存在しません。
ステージされていないローカルの変更を元に戻す
変更を行ったがまだステージされていない場合、作業を取り消すことができます。
-
git status
を実行して、ファイルがステージされていない(git add <file>
を使用していない)ことを確認します:$ git status On branch main Your branch is up-to-date with 'origin/main'. 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) modified: <file> no changes added to commit (use "git add" and/or "git commit -a")
-
オプションを選択して、変更を取り消します:
-
ローカルの変更を上書きします:
git checkout -- <file>
-
ローカルの変更を保存して、後で再利用できるようにします:
git stash
-
すべてのファイルのローカル変更を永久に破棄するには:
git reset --hard
-
ステージされたローカルの変更を元に戻す
ステージングにファイルを追加した場合、元に戻すことができます。
-
git status
を実行して、ファイルがステージングされている(git add <file>
を使用した)ことを確認してください:$ git status On branch main Your branch is up-to-date with 'origin/main'. Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: <file>
-
オプションを選択して、変更を取り消します:
-
ファイルのステージを解除しますが、変更は保持します:
git restore --staged <file>
-
変更を保持したまま、すべてのステージを解除するには
git reset
-
ファイルを現在のコミット(HEAD)にアンステージするには :
git reset HEAD <file>
-
ローカルでの変更をすべて破棄し、後のために保存します:
git stash
-
すべてを永久に破棄するには
git reset --hard
-
ローカルの変更を素早く保存
別のブランチに変更したい場合は、git stash
.
- 作業を保存したいブランチから、
git stash
と入力してください。 - 別のブランチ (
git checkout <branchname>
) にスワップします。 - コミット、プッシュ、テスト。
- 変更を再開したいブランチに戻ります。
-
git stash list
を使って、以前に隠したコミットをすべてリストアップします。stash@{0}: WIP on submit: 6ebd0e2... Update git-stash documentation stash@{1}: On master: 9cc0589... Add git-stash
-
git stash
を実行してください:-
git stash pop
を使って、以前に隠した変更をやり直し、隠したリストから削除してください。 -
git stash apply
を使って、以前に隠した変更をやり直しますが、隠しリストに残しておきます。
-
コミットしたローカルの変更を元に戻す
ローカルリポジトリ (git commit
) にコミットすると、Git はその変更を記録します。まだリモートリポジトリにプッシュしていないので、あなたの変更は公開されません (あるいは、他の開発者と共有されません)。この時点で、変更を取り消すことができます。
ステージしたローカルの変更を履歴を変更せずに元に戻す
コミット履歴を保持したままコミットを取り消すことができます。
この例では、A
,B
,C
,D
,E
の5つのコミットを使用しており、これらはA-B-C-D-E
の順にコミットされています。取り消したいコミットはB
です。
- 元に戻したいコミットのコミット SHA を探します。コミットのログを見るには、
git log
と入力してください。 -
オプションを選択して、変更を取り消します:
-
コミットによる追加と削除の変更を入れ替えるには
B
:git revert <commit-B-SHA>
-
B
のコミットで変更されたファイルやディレクトリを元に戻しますが、ステージ状態には保持します:git checkout <commit-B-SHA> <file>
-
コミット
B
からのファイルやディレクトリの変更を取り消します:git reset <commit-B-SHA> <file>
-
複数のコミットされた変更の取り消し
複数のコミットを取り消すことができます。たとえば、feature ブランチでA-B-C-D
をコミットした後でC
とD
が間違っていることに気づいたとしましょう。
複数の間違ったコミットからリカバリするには
-
最後に正しいコミットをチェックしましょう。この例では、
B
.git checkout <commit-B-SHA>
-
新しいブランチを作成します。
git checkout -b new-path-of-feature
-
変更を追加、プッシュ、コミットします。
コミットはA-B-C-D-E
になりました。
GitLab を使えば、そのコミットをcherry-pickして新しいマージリクエストにすることもできます。
B
にリセットしてE
をコミットすることです。しかし、この方法ではA-B-E
になってしまい、他の開発者が内部で持っているものと衝突してしまいます。ステージされたローカルの変更を履歴変更で元に戻す
以下のタスクは Git の履歴を書き換えます。
特定のコミットを削除
特定のコミットを削除することができます。たとえば、A-B-C-D
というコミットがあり、B
を削除したい場合。
-
現在のコミット
D
からB
までの範囲をリベースします:git rebase -i A
コミットの一覧がエディターに表示されます。
- コミット
B
の内部で、pick
をdrop
に置き換えてください。 - その他のコミットはデフォルトの
pick
のままにしてください。 - 保存してエディターを終了します。
特定のコミットの変更
特定のコミットを修正することができます。たとえば、A-B-C-D
というコミットがあり、B
というコミットで導入した内容を変更したい場合。
-
現在のコミット
D
からB
までの範囲をリベースします:git rebase -i A
コミットの一覧がエディターに表示されます。
- コミット
B
の内部で、pick
をedit
に置き換えてください。 - その他のコミットはデフォルトの
pick
のままにしてください。 - 保存してエディターを終了します。
-
エディタでファイルを開き、編集を行い、変更をコミットします:
git commit -a
アンドゥのやり直し
ローカルでの過去のコミットを呼び出すことができます。Git はブランチやタグで到達できないコミットを定期的に削除しているからです。
リポジトリの履歴を表示して過去のコミットを追跡するには、git reflog show
を実行します。例えば
$ git reflog show
# Example output:
b673187 HEAD@{4}: merge 6e43d5987921bde189640cc1e37661f7f75c9c0b: Merge made by the 'recursive' strategy.
eb37e74 HEAD@{5}: rebase -i (finish): returning to refs/heads/master
eb37e74 HEAD@{6}: rebase -i (pick): Commit C
97436c6 HEAD@{7}: rebase -i (start): checkout 97436c6eec6396c63856c19b6a96372705b08b1b
...
88f1867 HEAD@{12}: commit: Commit D
97436c6 HEAD@{13}: checkout: moving from 97436c6eec6396c63856c19b6a96372705b08b1b to test
97436c6 HEAD@{14}: checkout: moving from master to 97436c6
05cc326 HEAD@{15}: commit: Commit C
6e43d59 HEAD@{16}: commit: Commit B
この出力は、リポジトリの履歴を示しています:
- コミット SHA。
- コミットが行われたのは
HEAD
-changing actions のいくつ前か (HEAD@{12}
は 12HEAD
-changing actions 前)。 - コミット、リベース、マージなどのアクション。
-
HEAD
を変更したアクションの説明。
履歴を変更せずにリモートの変更を元に戻す
リモートリポジトリの変更を取り消すには、取り消したい変更内容で新しいコミットを作成します。履歴を保持し、タイムラインと開発構造を明確にするこのプロセスに従うべきです。しかし、この手順が必要なのは、自分の作業が他の開発者が作業ベースとしているブランチにマージされた場合だけです。
特定のコミットで行われた変更を差し戻すにはB
:
git revert B
履歴の変更中にリモートの変更を元に戻す
リモートの変更を取り消し、履歴を変更することができます。
履歴が更新されていても、古いコミットにはコミット SHA でアクセスできます。少なくとも、切り離されたコミットの自動クリーンアップがすべて実行されるか、手動でクリーンアップが実行されるまではそうです。クリーンアップを実行しても、古いコミットを指す参照が残っている場合は削除されないかもしれません。
履歴の変更が許容される場合
公開ブランチや他の開発者が使う可能性のあるブランチで作業しているときには、履歴を変更すべきではありません。
GitLab のような大規模なオープンソースリポジトリに貢献する場合は、コミットをひとつにまとめることができます。
機能ブランチ上のコミットをマージ先のブランチ上のひとつのコミットにまとめるには、git merge --squash
を使います。
履歴の変更方法
マージリクエストの feature ブランチは公開ブランチであり、他の開発者が使用する可能性があります。しかし、プロジェクトのルールによっては、レビューが終わった後にgit rebase
を使って対象ブランチの表示コミット数を減らす必要があるかもしれません。
git rebase -i
を使うと、履歴を修正できます。このコマンドを使うと、コミットを修正したりつぶしたり削除したりできます。
#
# 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
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Empty commits are commented out
共有ブランチやリモートブランチでは、git rebase
を慎重に使用してください。リモートリポジトリにプッシュする前に、ローカルで実験してください。
# Modify history from commit-id to HEAD (current commit)
git rebase -i commit-id
コミットから機密情報を削除
Git を使って、過去のコミットから機密情報を削除することができます。しかし、その過程で履歴が変更されてしまいます。
特定のフィルターを使って履歴を書き換えるには、git filter-branch
を実行します。
履歴からファイルを完全に削除するには、次のようにします:
git filter-branch --tree-filter 'rm filename' HEAD
git filter-branch
コマンドは、大きなリポジトリでは遅いかもしれません。Git コマンドをより高速に実行するツールがあります。これらのツールがより高速なのは、git filter-branch
のような機能セットではなく、特定のユースケースに特化しているからです。
リポジトリ履歴や GitLab ストレージからファイルをパージする方法については、リポジトリサイズの削減をご覧ください。