2766

私は通常、レビューのためにコミットのリストを提出します。次のコミットがある場合:

  1. HEAD
  2. Commit3
  3. Commit2
  4. Commit1

... head commit を で変更できることは知っていgit commit --amendます。しかし、それがコミットCommit1ではない場合、どうすれば変更できますか?HEAD

4

17 に答える 17

3722

git rebaseを使用できます。たとえば、 commit を変更する場合は、次bbc643cdを実行します。

$ git rebase --interactive 'bbc643cd^'

コマンドの最後のキャレットに注意してください。実際には、変更したいコミットののコミット^にリベースする必要があるためです。

デフォルトのエディターで、「bbc643cd」に言及している行を変更pickします。edit

ファイルを保存して終了します。git はファイル内のコマンドを解釈して自動的に実行します。commit を作成したばかりの前の状況にいることに気付くでしょうbbc643cd

この時点で、bbc643cdが最後のコミットであり、簡単に修正できます。変更を加えてから、次のコマンドでコミットします。

$ git commit --all --amend --no-edit

その後、次のように入力します。

$ git rebase --continue

前の HEAD コミットに戻ります。

警告: これにより、そのコミットとすべての子の SHA-1 が変更されることに注意してください。つまり、これにより、その時点からの履歴が書き換えられます。コマンドを使用してプッシュすると、これを行うリポジトリを壊すことができますgit push --force

于 2009-07-27T05:28:02.240 に答える
609

すばらしいインタラクティブなリベースを使用します。

git rebase -i @~9   # Show the last 9 commits in a text editor

目的のコミットを見つけて( ) に変更pickし、ファイルを保存して閉じます。Git はそのコミットまで巻き戻して、次のいずれかを実行できるようにします。eedit

  • を使用git commit --amendして変更を行う、または
  • git reset @~ファイルへの変更ではなく、最後のコミットを破棄するために使用します (つまり、ファイルを編集したが、まだコミットしていない時点に移動します)。

後者は、複数のコミットに分割するなど、より複雑なことを行うのに役立ちます。

次に、 を実行するgit rebase --continueと、Git は変更されたコミットに基づいて後続の変更を再生します。マージの競合を修正するよう求められる場合があります。

注:@は の省略形でHEADあり~、指定されたコミットの前のコミットです。

履歴の書き換えについて詳しくは、Git のドキュメントをご覧ください。


リベースを恐れないでください

ProTip™: 履歴を書き換える「危険な」コマンドを試すことを恐れないでください* — Git はデフォルトで 90 日間コミットを削除しません。それらはreflogで見つけることができます:

$ git reset @~3   # go back 3 commits
$ git reflog
c4f708b HEAD@{0}: reset: moving to @~3
2c52489 HEAD@{1}: commit: more changes
4a5246d HEAD@{2}: commit: make important changes
e8571e4 HEAD@{3}: commit: make some changes
... earlier commits ...
$ git reset 2c52489
... and you're back where you started

*や などのオプションに注意してください — これらはデータを破棄する可能性があります。--hard--force
* また、共同作業しているブランチの履歴を書き換えないでください。



多くのシステムでgit rebase -iは、デフォルトで Vim が開きます。Vim は最新のテキスト エディターのようには機能しないため、Vim を使用してリベースする方法をご覧ください。別のエディターを使用したい場合は、 で変更してgit config --global core.editor your-favorite-text-editorください。

于 2015-04-29T17:50:53.873 に答える
111

Interactive rebase with--autosquashは、以前のコミットの履歴をより深く修正する必要があるときによく使用するものです。これは基本的に、ZelluX の回答が示すプロセスを高速化し、編集する必要があるコミットが複数ある場合に特に便利です。

ドキュメントから:

--autosquash

コミット ログ メッセージが「squash! … 」(または「fixup! … 」)で始まり、タイトルが同じ… で始まるコミットがある場合、rebase -i の todo リストを自動的に変更して、コミットが修正するコミットの直後にスカッシュのマークが付けられます

次のような履歴があるとします。

$ git log --graph --oneline
* b42d293 Commit3
* e8adec4 Commit2
* faaf19f Commit1

Commit2に修正したい変更があり、次を使用して変更をコミットします

$ git commit -m "fixup! Commit2"

または、コミット メッセージの代わりに commit-sha を使用すること"fixup! e8adec4も、コミット メッセージのプレフィックスだけを使用することもできます。

次に、コミットのインタラクティブなリベースを開始してから

$ git rebase e8adec4^ -i --autosquash

エディターは、コミットが既に正しく順序付けられた状態で開きます

pick e8adec4 Commit2
fixup 54e1a99 fixup! Commit2
pick b42d293 Commit3

保存して終了するだけです

于 2015-09-29T17:59:45.897 に答える
51

走る:

$ git rebase --interactive commit_hash^

それぞれ^は、編集するコミットの数を示します。それが 1 つのみ (指定したコミット ハッシュ) の場合は、1 つ追加するだけ^です。

Vim を使用して、変更したいコミットの単語pickを変更し、保存して終了します( )。次に、git は、コミット メッセージを変更できるように、reword としてマークしたコミットごとにプロンプ​​トを表示します。reword:wq

:wq次のコミットメッセージに移動するには、保存して終了する必要がある各コミットメッセージ

変更を適用せずに終了する場合は、 を押します。:q!

EDIT : ナビゲートするにはvimjkに移動し、下に移動し、h左に移動し、l右に移動します (これらはすべてNORMALモードで、 を押しESCNORMALモードに移動します)。テキストを編集するには、 を押して、テキストを挿入するモードにi入ります。INSERTを押しESCてモードに戻りNORMALます:)

更新: github リストからのすばらしいリンクです。git で(ほぼ) 何かを元に戻す方法

于 2015-07-02T19:11:31.507 に答える
26

完全非対話型コマンド(1)

これに使用しているエイリアスを共有したいと思いました。非対話型の対話型リベースに基づいています。これを git に追加するには、次のコマンドを実行します (以下に説明があります)。

git config --global alias.amend-to '!f() { SHA=`git rev-parse "$1"`; git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"; }; f'

または、ステージングされていないファイルも処理できるバージョン (stash してから un-stash することにより):

git config --global alias.amend-to '!f() { SHA=`git rev-parse "$1"`; git stash -k && git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^" && git stash pop; }; f'

このコマンドの最大の利点は、no-vimであることです。


(1)もちろん、リベース中に競合がない場合

使用法

git amend-to <REV> # e.g.
git amend-to HEAD~1
git amend-to aaaa1111

名前amend-toは適切なようです。フローを次のものと比較し--amendます。

git add . && git commit --amend --no-edit
# vs
git add . && git amend-to <REV>

説明

  • git config --global alias.<NAME> '!<COMMAND>'<NAME>- git 以外のコマンドを実行するという名前のグローバル git エイリアスを作成します<COMMAND>
  • f() { <BODY> }; f- 「匿名」bash 関数。
  • SHA=`git rev-parse "$1"`;- 引数を git リビジョンに変換し、結果を変数に割り当てますSHA
  • git commit --fixup "$SHA"- の修正コミットSHAドキュメントを見るgit-commit
  • GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"
    • git rebase --interactive "$SHA^"一部は他の回答でカバーされています。
    • --autosquashと組み合わせて使用​​されるものです。詳細についてはドキュメントgit commit --fixupを参照してくださいgit-rebase
    • GIT_SEQUENCE_EDITOR=true全体を非インタラクティブにするものです。このハックは、このブログ投稿から学びました。
于 2018-02-27T01:47:51.570 に答える
21

なんらかの理由でインタラクティブ エディターが気に入らない場合は、 を使用できますgit rebase --onto

を変更したいとしますCommit1。まず、before Commit1から分岐します:

git checkout -b amending [commit before Commit1]

次に、次のようにグラブCommit1cherry-pickます。

git cherry-pick Commit1

次に、変更を修正して、次を作成しますCommit1'

git add ...
git commit --amend -m "new message for Commit1"

そして最後に、他の変更を隠した後、残りのコミットをmaster新しいコミットの上に移植します。

git rebase --onto amending Commit1 master

読んでください:「リベース、ブランチに、 (非包括的)と(包括的)のamending間のすべてのコミット」。つまり、Commit2 と Commit3 で、古い Commit1 を完全に切り捨てます。それらをチェリーピックすることもできますが、この方法の方が簡単です.Commit1master

枝をきれいにすることを忘れないでください!

git branch -d amending
于 2016-10-22T12:19:44.887 に答える
9

自動化されたインタラクティブなリベース編集と、やり直しの準備が整ったコミットの復帰

過去のコミットを頻繁に修正していることに気づき、そのためのスクリプトを書きました。

ワークフローは次のとおりです。

  1. git commit-edit <commit-hash>
    

    これにより、編集したいコミットにドロップされます。

  2. そもそもコミットを修正してステージングします。

    (git stash saveコミットしていないファイルを保持するために使用することができます)

  3. でコミットをやり直します--amend。例:

    git commit --amend
    
  4. リベースを完了します。

    git rebase --continue
    

上記を機能させるには、次のスクリプトを実行可能ファイルに入れgit-commit-editます$PATH

#!/bin/bash

set -euo pipefail

script_name=${0##*/}

warn () { printf '%s: %s\n' "$script_name" "$*" >&2; }
die () { warn "$@"; exit 1; }

[[ $# -ge 2 ]] && die "Expected single commit to edit. Defaults to HEAD~"

# Default to editing the parent of the most recent commit
# The most recent commit can be edited with `git commit --amend`
commit=$(git rev-parse --short "${1:-HEAD~}")
message=$(git log -1 --format='%h %s' "$commit")

if [[ $OSTYPE =~ ^darwin ]]; then
  sed_inplace=(sed -Ei "")
else
  sed_inplace=(sed -Ei)
fi

export GIT_SEQUENCE_EDITOR="${sed_inplace[*]} "' "s/^pick ('"$commit"' .*)/edit \\1/"'
git rebase --quiet --interactive --autostash --autosquash "$commit"~
git reset --quiet @~ "$(git rev-parse --show-toplevel)"  # Reset the cache of the toplevel directory to the previous commit
git commit --quiet --amend --no-edit --allow-empty  #  Commit an empty commit so that that cache diffs are un-reversed

echo
echo "Editing commit: $message" >&2
echo
于 2018-09-14T03:32:42.740 に答える