変更をリバートする、取り消す
Gitでの作業には、実験とイテレーションが必要です。開発中に間違いは起こるものであり、変更を元に戻す必要がある場合があります。Gitでは、Gitワークフローの任意の時点で変更を取り消す機能を使用して、コード履歴を管理できます。
誤ったコミットからの復旧、機密データの削除、正しくないマージの修正を行い、クリーンなリポジトリ履歴を維持します。他のユーザーとコラボレーションしている場合は、新しいリバートコミットで透明性を維持するか、共有する前にローカルで作業をリセットします。使用する方法は、次のどちらであるかによって異なります:
- 変更がローカルコンピューターでのみ行われている。
- 変更がGitLab.comなどのGitサーバーにリモートで保存されている。
ローカルでの変更を取り消す
変更をリモートリポジトリにプッシュするまで、Gitで行う変更はローカルの開発環境にのみ格納されます。
Gitでファイルを_ステージ_する場合、コミットの準備としてファイルへの変更を追跡するように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 reset --hard
ステージングされたローカルでの変更をリバートする
すでにステージングされたローカルでの変更は取り消すことができます。次の例では、ファイルがステージングに追加されましたが、コミットされていません:
git statusでファイルがステージングされていることを確認します: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 reset --hard
ローカルでのコミットを取り消す
git commitを使用してローカルリポジトリにコミットするとき、Gitは変更を記録します。まだリモートリポジトリにプッシュしていないため、変更は公開されておらず、他のユーザーと共有されてもいません。この時点では、変更を取り消すことができます。
履歴を変更せずにコミットを取り消す
コミット履歴を保持しながら、コミットを取り消すことができます。
この例では、5つのコミット、A、B、C、D、Eを使用します。これらは、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>
コミットを取り消して履歴を変更する
次のセクションでは、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
複数のコミットを取り消す
ブランチに複数のコミット(A-B-C-D)を作成した後、コミットCとDが間違っていることに気付いた場合は、両方の間違ったコミットを取り消します:
最後にある正しいコミットをチェックアウトします。この例では、
Bです。git checkout <commit-B-SHA>新しいブランチを作成します。
git checkout -b new-path-of-feature変更を追加、プッシュ、コミットします。
git add . git commit -m "Undo commits C and D" git push --set-upstream origin new-path-of-feature
コミットはA-B-C-D-Eになりました。
または、そのコミットを新しいマージリクエストにcherry-pickします。
別の方法は、Bにリセットし、Eをコミットすることです。ただし、この方法ではA-B-Eになり、他のユーザーのローカルにあるものと衝突します。ブランチが共有されている場合は、この方法を使用しないでください。
取り消したコミットを復元する
過去のローカルでのコミットを呼び出すことができます。ただし、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変更アクションが過去に何回行われたか(HEAD@{12}は過去に12回HEAD変更アクションがあったという意味)。 - 実行されたアクション(例: コミット、リベース、マージ)。
HEADを変更したアクションの説明。
リモートでの変更を取り消す
ブランチでリモートでの変更を取り消すことができます。ただし、自分のブランチにマージされたブランチの変更は取り消せません。その場合は、リモートブランチで変更を取り消す必要があります。
履歴を変更せずにリモートでの変更を取り消す
リモートリポジトリの変更を取り消すには、取り消す変更を含む新しいコミットを作成します。このプロセスは、履歴を保持し、明確なタイムラインと開発構造を提供します:
%%{init: { "fontFamily": "GitLab Sans" }}%%
flowchart LR
accTitle: Git revert operation workflow diagram
accDescr: Shows commits A, B, C in sequence, then commit -B that reverses B's changes, followed by D. Commit B remains in history.
REMOTE["REMOTE"] --> A(A)
A --> B(B)
B --> C(C)
C --> negB("-B")
negB --> D(D)
B:::crossed
classDef crossed stroke:#000,stroke-width:3px,color:#000,stroke-dasharray: 5 5
negB -.->|reverts| B
特定のコミットBで導入された変更を取り消すには:
git revert Bリモートでの変更を取り消して履歴を変更する
リモートでの変更を取り消して履歴を変更できます。
履歴が更新されても、少なくともデタッチされたコミットの自動クリーンアップがすべて実行されるか、手動でクリーンアップが実行されるまでは、古いコミットにコミットSHAでアクセスできます。古いコミットを参照するrefsがまだある場合、クリーンアップでも古いコミットが削除されない可能性があります。
パブリックブランチ、つまり他のユーザーが使用する可能性のあるブランチで作業している場合は、履歴を変更しないでください。
デフォルトブランチまたは共有ブランチのコミット履歴は絶対に変更しないでください。
git rebaseを使用して履歴を変更する
マージリクエストのブランチはパブリックブランチであり、他のデベロッパーが使用する可能性があります。ただし、プロジェクトルールでは、レビューが完了した後、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-idgit merge --squashを使用して履歴を変更する
大規模なオープンソースリポジトリにコントリビュートする場合は、コミットを1つのコミットにスカッシュすることを検討してください。このプラクティスで次のことができます:
- クリーンで線形のプロジェクト履歴を維持するのに役立ちます。
- すべての変更が1つのコミットに凝縮されるため、変更を取り消すプロセスが簡素化されます。
マージ時にブランチのコミットをターゲットブランチの1つのコミットにスカッシュするには、git merge --squashを使用します。例は次のとおりです:
ベースブランチをチェックアウトします。この例では、ベースブランチは
mainです:git checkout main--squashを使用してターゲットブランチをマージします:git merge --squash <target-branch>変更をコミットします:
git commit -m "Squash commit from feature-branch"
GitLab UIからコミットをスカッシュする方法については、スカッシュとマージを参照してください。
マージコミットを別の親にリバートする
マージコミットをリバートすると、マージ先のブランチが常に最初の親になります。たとえば、デフォルトブランチまたはmainです。マージコミットを別の親にリバートするには、コマンドラインからコミットをリバートする必要があります:
リバート先の親コミットのSHAを特定します。
リバート先のコミットの親番号を特定します(最初の親の場合、デフォルトは
1です)。このコマンドを実行し、
2を親番号に、7a39eb0をコミットSHAに置き換えます:git revert -m 2 7a39eb0
GitLab UIから変更を取り消す方法については、変更を取り消すを参照してください。
機密情報の処理
パスワードやAPIキーなどの機密情報が、誤ってGitリポジトリにコミットされる可能性があります。このセクションでは、この状況に対処する方法について説明します。
情報を削除する
誤ってコミットされた機密情報や社外秘の情報を完全に削除し、リポジトリの履歴からアクセスできないようにします。このプロセスでは、文字列のリストを***REMOVED***に置き換えます。
または、リポジトリから特定のファイルを完全に削除するには、blobを削除するを参照してください。
リポジトリからテキストを削除するには、リポジトリからテキストを削除するを参照してください。
コミットから情報を削除する
Gitを使用すると、過去のコミットから機密情報を削除できます。ただし、履歴はプロセスで変更されます。
特定のフィルターを使用して履歴を書き換えるには、git filter-branchを実行します。
履歴からファイルを完全に削除するには、次を使用します:
git filter-branch --tree-filter 'rm filename' HEADgit filter-branchコマンドは、大規模なリポジトリでは低速になる可能性があります。Gitコマンドをより高速で実行できるツールが用意されています。これらのツールは、git filter-branchと同じ機能セットを提供するのではなく、特定のユースケースに重点を置いているため、高速です。
リポジトリの履歴とGitLabストレージからファイルをパージする方法の詳細については、リポジトリサイズを削減するを参照してください。
コミットを取り消して削除する
最後のコミットを取り消し、すべてをステージングエリアに戻します:
git reset --soft HEAD^ファイルを追加して、コミットメッセージを変更します:
git commit --amend -m "New Message"まだプッシュしていない場合は、最後の変更を取り消して、他のすべての変更を削除します:
git reset --hard HEAD^まだプッシュしていない場合は、最後の変更を取り消して、最後の2つのコミットを削除します:
git reset --hard HEAD^^
git resetのワークフロー例
次に、一般的なGitリセットのワークフローを示します:
ファイルを編集します。
ブランチの状態を確認します:
git status誤ったコミットメッセージでブランチに変更をコミットします:
git commit -am "kjkfjkg"Gitログを確認します:
git log正しいコミットメッセージでコミットを修正します:
git commit --amend -m "New comment added"Gitログを再度確認します:
git logブランチをソフトリセットします:
git reset --soft HEAD^Gitログを再度確認します:
git logリモートからブランチの更新をプルします:
git pull origin <branch>ブランチの変更をリモートにプッシュします:
git push origin <branch>
新しいコミットでコミットを取り消す
コミットでファイルが変更されており、それを前のコミットの状態に戻しつつもコミット履歴を保持したい場合は、git revertを使用できます。このコマンドは、元のコミットで行われたすべてのアクションを元に戻す新しいコミットを作成します。
たとえば、コミットBでのファイルの変更を削除し、コミットAからコンテンツを復元するには、次を実行します:
git revert <commit-sha>リポジトリからファイルを削除する
ディスクとリポジトリからファイルを削除するには、
git rmを使用します。ディレクトリを削除するには、-rフラグを使用します:git rm '*.txt' git rm -r <dirname>ファイルをディスクに保持したまま、リポジトリから削除するには(たとえば、
.gitignoreに追加するファイルなど)、--cacheフラグを指定してrmコマンドを使用します:git rm <filename> --cache
これらのコマンドは、現在のブランチからファイルを削除しますが、リポジトリの履歴からは消滅させません。リポジトリからファイルのすべてのトレース(過去と現在)を完全に削除するには、blobを削除するを参照してください。
git revertとgit resetを比較する
git resetコマンドは、コミットを完全に削除します。git revertコマンドは、変更を削除しますが、コミットはそのまま残します。取り消しを取り消すことができるため、より安全です。
# Changed file
git commit -am "bug introduced"
git revert HEAD
# New commit created reverting changes
# Next, reapply the reverted commit
git log # take hash from the revert commit
git revert <rev commit hash>
# reverted commit is back (new commit created again)