- Git
git rebase -iでコミットをまとめる|squash・fixup早見表と復旧
git rebase -i(インタラクティブリベース)とは、指定した範囲のコミットを一覧(todo リスト)にして、1行ずつ「残す・まとめる・直す・消す・並べ替える」を指示し、履歴を作り直すコマンドです。 プルリクエストを出す前に「wip」「typo修正」のような細かいコミットを整理するときによく使います。
この記事では、6コミットのサンプルリポジトリを作り、エディタの操作を GIT_SEQUENCE_EDITOR(sed)で自動化して、各操作の実行前後の git log --oneline をそのまま載せました。 squash と fixup の違い、fixup -C、commit --fixup と --autosquash、exec でコミットごとにテストを流す方法、コンフリクトが起きたときの続け方と reflog での復旧、push 済みのときの --force-with-lease の判断までを1本で扱います。検証は Git 2.43.0(Ubuntu の apt 版)で行い、2026年10月時点の最新版は 2.56.0 です。merge と rebase の使い分けは別記事「git mergeとrebaseの違い」で扱っています。
git rebase -iの早見表
| コマンド(略) | todo での書き方 | 動き | メッセージ |
|---|---|---|---|
| pick(p) | pick abc123 msg |
そのまま使う | そのまま |
| reword(r) | reword abc123 msg |
中身はそのまま | エディタで書き直す |
| edit(e) | edit abc123 msg |
適用後に止まる(--amend で中身を直せる) |
必要なら直す |
| squash(s) | squash abc123 msg |
直前のコミットにまとめる | 両方を並べて編集 |
| fixup(f) | fixup abc123 msg |
直前のコミットにまとめる | 直前のものだけ残す |
| fixup -C | fixup -C abc123 msg |
直前のコミットにまとめる | このコミットのメッセージを使う |
| fixup -c | fixup -c abc123 msg |
fixup -C と同じ | エディタで確認・編集 |
| drop(d) | drop abc123 msg(行の削除でも可) |
コミットを消す | ― |
| exec(x) | exec make test |
シェルコマンドを実行、失敗で止まる | ― |
| break(b) | break |
その位置で止まる | ― |
| label(l)/ reset(t)/ merge(m) | label onto など |
マージを含む履歴の再構築(--rebase-merges) |
― |
| update-ref(u) | update-ref refs/heads/x |
途中のブランチも付け替える(--update-refs) |
― |
| やりたいこと | コマンド |
|---|---|
| 直近3コミットを整理 | git rebase -i HEAD~3 |
| 最初のコミットも含めて整理 | git rebase -i --root |
| main から分岐した分だけ整理 | git rebase -i main |
| あとで直す前提のコミットを作る | git commit --fixup=<コミット> |
| メッセージも差し替える前提で作る | git commit --fixup=amend:<コミット> |
| fixup! を自動で並べる | git rebase -i --autosquash <基点> |
| 常に自動で並べる | git config rebase.autoSquash true |
| コミットごとにテスト | git rebase -i -x "npm test" <基点> |
| コンフリクトを直して続ける | git add <file> → git rebase --continue |
| このコミットを捨てて続ける | git rebase --skip |
| 始める前に戻す | git rebase --abort |
| 終わったあとに戻す | git reset --hard ORIG_HEAD または reflog で選ぶ |
| push 済みのブランチを更新 | git push --force-with-lease --force-if-includes |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「git rebase -iでコミットをまとめる|squash・fixup早見表と復旧」
サンプルリポジトリとエディタの自動化
次のスクリプトで、毎回同じ履歴のリポジトリを作れます。 日時を固定しているので、手元でも同じハッシュになります(グローバル設定の影響を避けるため GIT_CONFIG_GLOBAL=/dev/null で実行しました)。
#!/usr/bin/env bash
# setup.sh:サンプルリポジトリを作る(Git 2.43.0 で確認)
set -eu
rm -rf demo && git init -q -b main demo && cd demo
git config user.name "Dev"; git config user.email "dev@example.com"
export GIT_AUTHOR_DATE="2026-10-01T09:00:00+09:00" GIT_COMMITTER_DATE="2026-10-01T09:00:00+09:00"
c(){ printf '%s\n' "$2" >> "$1"; git add "$1"; git commit -q -m "$3"; }
c README.md "# タスク管理" "docs: READMEを追加"
c task.py "def add(tasks, t): tasks.append(t)" "feat: タスク追加を実装"
c task.py "# TODO" "wip"
c task.py "def remove(tasks, t): tasks.remove(t)" "feat: タスク削除を実装"
c README.md "使い方: python task.py" "docs: 使い方を追記"
c task.py "def count(tasks): return len(tasks)" "feat: 件数表示を実装"
git log --oneline
9b53b61 feat: 件数表示を実装
c80b1fa docs: 使い方を追記
d2581e1 feat: タスク削除を実装
c5990be wip
6efab19 feat: タスク追加を実装
7981b6c docs: READMEを追加
エディタを sed で自動化する仕組み
| 環境変数 | 何を編集するか | この記事での使い方 |
|---|---|---|
GIT_SEQUENCE_EDITOR |
todo リスト(pick ... の一覧) |
sed -i '2s/^pick/fixup/' で2行目を書き換え |
GIT_EDITOR |
コミットメッセージ | true(そのまま保存)や sed -i '1s/.*/新しい件名/' |
普段はエディタで書き換える部分を sed に任せているだけなので、手で操作する場合も結果は同じです。 todo の中身だけ見たいときは、ファイルを書き換えない sed -n を使うと表示できます。
GIT_SEQUENCE_EDITOR="sed -n '/^[a-z]/p'" git rebase -i HEAD~4
pick c5990be wip
pick d2581e1 feat: タスク削除を実装
pick c80b1fa docs: 使い方を追記
pick 9b53b61 feat: 件数表示を実装
todo は git log と逆で、上が古く下が新しい順です。 上から順に適用されます。
squashとfixupでコミットをまとめる
squash:メッセージを両方残す
「wip」を1つ前の「feat: タスク追加を実装」にまとめます。HEAD~5 の todo では、wip は2行目です。
GIT_SEQUENCE_EDITOR="sed -i '2s/^pick/squash/'" GIT_EDITOR=true git rebase -i HEAD~5
git log --oneline
git log -1 --format=%B HEAD~3
a712653 feat: 件数表示を実装
03dda92 docs: 使い方を追記
de08701 feat: タスク削除を実装
0ccb5a8 feat: タスク追加を実装
7981b6c docs: READMEを追加
feat: タスク追加を実装
wip
6コミットが5コミットになり、まとめたコミットのメッセージには両方の文が残ります。 squash ではメッセージの編集画面が開くので、通常はここで1行目だけ残すように整えます。
fixup:前のメッセージだけ残す
GIT_SEQUENCE_EDITOR="sed -i '2s/^pick/fixup/'" git rebase -i HEAD~5
02a4191 feat: 件数表示を実装
2909329 docs: 使い方を追記
eb4a341 feat: タスク削除を実装
ec135a6 feat: タスク追加を実装
7981b6c docs: READMEを追加
feat: タスク追加を実装
fixup はメッセージ編集の画面が開かず、「wip」の文は捨てられます。 「typo修正」「レビュー指摘対応」のように、メッセージを残す必要がないコミットは fixup が向いています。
fixup -C:後ろのコミットのメッセージを使う
「件数表示を実装」に、件名を直したい空のコミットを足してから、fixup -C でまとめます。
git commit -q --allow-empty -m "feat: 件数表示を実装(0件対応)"
GIT_SEQUENCE_EDITOR="sed -i '6s/^pick/fixup -C/'" git rebase -i HEAD~6
git log --oneline
ae50292 feat: 件数表示を実装(0件対応)
c80b1fa docs: 使い方を追記
d2581e1 feat: タスク削除を実装
c5990be wip
6efab19 feat: タスク追加を実装
7981b6c docs: READMEを追加
中身は「件数表示を実装」、メッセージは後ろのコミットのものになりました。 また、変更していない下の4コミットはハッシュがそのまま(c80b1fa など)です。rebase は、書き換えたコミットより後ろだけを新しいコミットとして作り直します。
squash・fixup・fixup -C の選び方
| 状況 | 選ぶもの |
|---|---|
| 両方のメッセージを見ながら新しく書きたい | squash |
| 後ろのコミットは「修正」なのでメッセージ不要 | fixup |
| 後ろのコミットのメッセージで置き換えたい | fixup -C |
| 置き換える前にエディタで確認したい | fixup -c |
reword・drop・並べ替え・edit・breakの実行例
reword:メッセージだけ直す
GIT_SEQUENCE_EDITOR="sed -i '1s/^pick/reword/'" \
GIT_EDITOR="sed -i '1s/.*/chore: 作業中のメモを追加/'" git rebase -i HEAD~4
20e25bf feat: 件数表示を実装
100e433 docs: 使い方を追記
755d030 feat: タスク削除を実装
f89a9dc chore: 作業中のメモを追加
6efab19 feat: タスク追加を実装
7981b6c docs: READMEを追加
直近のコミットのメッセージだけなら git commit --amend で足ります。 2つ以上前を直したいときに reword を使います。
drop:コミットを消す
GIT_SEQUENCE_EDITOR="sed -i '3s/^pick/drop/'" git rebase -i HEAD~4
8de194b feat: 件数表示を実装
d2581e1 feat: タスク削除を実装
c5990be wip
6efab19 feat: タスク追加を実装
7981b6c docs: READMEを追加
「docs: 使い方を追記」が消えました。todo の行を削除しても drop と同じ動きになります。
並べ替え:行の順番を入れ替える
3行目(docs: 使い方を追記)を先頭に移します。
GIT_SEQUENCE_EDITOR="sed -i -e '1{h;d}' -e '2{H;d}' -e '3G'" git rebase -i HEAD~4
60a5264 feat: 件数表示を実装
437602d feat: タスク削除を実装
c379f16 wip
92b4351 docs: 使い方を追記
6efab19 feat: タスク追加を実装
7981b6c docs: READMEを追加
並べ替えは、同じファイルの近い行を触るコミット同士だとコンフリクトしやすくなります。 この例は README.md と task.py で別ファイルなので、そのまま通りました。離れたコミットをまとめたいときは、並べ替えてから fixup します。
edit:途中で止めて中身を直す
GIT_SEQUENCE_EDITOR="sed -i '3s/^pick/edit/'" git rebase -i HEAD~4
sed -i 's/python task.py/python3 task.py/' README.md
git commit -a --amend --no-edit
git rebase --continue
Stopped at c80b1fa... docs: 使い方を追記
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
Successfully rebased and updated refs/heads/main.
57910f7 feat: 件数表示を実装
f64436f docs: 使い方を追記
d2581e1 feat: タスク削除を実装
c5990be wip
6efab19 feat: タスク追加を実装
7981b6c docs: READMEを追加
edit で止まっている間は、ファイルを直して --amend し、--continue で先へ進みます。 1つのコミットを2つに分けたい場合は、止まったところで git reset HEAD~ し、分けて git add と git commit を2回行ってから --continue します。
break:その位置で止まる
GIT_SEQUENCE_EDITOR="sed -i '2a break'" git rebase -i HEAD~4
Stopped at d2581e1 (feat: タスク削除を実装)
break は「ここまで適用した状態で一度止まって、動作を確かめたい」ときに使います。 確認が済んだら git rebase --continue で残りを適用します。
commit --fixupと--autosquashで後から差し込む
あとでまとめることが決まっている修正は、最初から fixup! 付きでコミットしておくと、todo の編集が不要になります。
# 「タスク追加」にテストファイルを入れ忘れた
printf 'from task import add\n' > test_task.py
git add test_task.py
git commit --fixup=6efab19
# 「タスク削除」のメッセージを差し替えたい(中身の変更なし)
GIT_EDITOR="sed -i '3s/.*/feat: タスク削除を実装(存在しないときは無視)/'" \
git commit --allow-empty --fixup=amend:d2581e1
git log --oneline
bf547bf amend! feat: タスク削除を実装
43a23fc fixup! feat: タスク追加を実装
9b53b61 feat: 件数表示を実装
c80b1fa docs: 使い方を追記
d2581e1 feat: タスク削除を実装
c5990be wip
6efab19 feat: タスク追加を実装
7981b6c docs: READMEを追加
--autosquash を付けると、todo が自動で並べ替えられます。
git rebase -i --autosquash 7981b6c
pick 6efab19 feat: タスク追加を実装
fixup 43a23fc fixup! feat: タスク追加を実装
pick c5990be wip
pick d2581e1 feat: タスク削除を実装
fixup -C bf547bf amend! feat: タスク削除を実装 # empty
pick c80b1fa docs: 使い方を追記
pick 9b53b61 feat: 件数表示を実装
そのまま保存した結果です。
7540ef0 feat: 件数表示を実装
c48664d docs: 使い方を追記
1b6f269 feat: タスク削除を実装(存在しないときは無視)
63f0674 wip
4fb4450 feat: タスク追加を実装
7981b6c docs: READMEを追加
git show --stat HEAD~4 では、「タスク追加」に task.py と test_task.py の両方が入っていることを確認できました。
| コミットの作り方 | 件名 | todo での扱い |
|---|---|---|
git commit --fixup=<c> |
fixup! <件名> |
fixup |
git commit --fixup=amend:<c> |
amend! <件名> |
fixup -C(メッセージを差し替え) |
git commit --fixup=reword:<c> |
amend! <件名> |
中身の変更なしでメッセージだけ差し替え |
git commit --squash=<c> |
squash! <件名> |
squash |
rebase.autoSquash の設定
git config rebase.autoSquash true
git rebase -i 7981b6c
pick 6efab19 feat: タスク追加を実装
fixup 43a23fc fixup! feat: タスク追加を実装
pick c5990be wip
...
設定すると -i だけで fixup! が並びます。 公式ドキュメントでは、この設定は対話的な rebase に効くと書かれています。また最新のドキュメントでは「--autosquash は -i を付けずにも使える」と記載されていますが、手元の Git 2.43.0 で git rebase --autosquash 7981b6c(-i なし)を実行すると、fixup! は残ったままでした。 使っている Git が古めの場合は -i を付けて実行するのが確実です。
コンフリクトしたときの続け方(--continue・--skip・--abort)
「wip」を drop すると、次の「タスク削除」がコンフリクトします。task.py の同じ位置に、wip が足した # TODO 行があったためです。
GIT_SEQUENCE_EDITOR="sed -i '1s/^pick/drop/'" git rebase -i HEAD~4
Rebasing (2/4)
Auto-merging task.py
CONFLICT (content): Merge conflict in task.py
error: could not apply d2581e1... feat: タスク削除を実装
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply d2581e1... feat: タスク削除を実装
git status で、どこまで進んで何が残っているかが確認できます。
interactive rebase in progress; onto 6efab19
Last commands done (2 commands done):
drop c5990be wip
pick d2581e1 feat: タスク削除を実装
Next commands to do (2 remaining commands):
pick c80b1fa docs: 使い方を追記
pick 9b53b61 feat: 件数表示を実装
(use "git rebase --edit-todo" to view and edit)
...
Unmerged paths:
both modified: task.py
def add(tasks, t): tasks.append(t)
<<<<<<< HEAD
=======
# TODO
def remove(tasks, t): tasks.remove(t)
>>>>>>> d2581e1 (feat: タスク削除を実装)
3つの選択肢
| コマンド | 結果 | 向いている場面 |
|---|---|---|
git add → git rebase --continue |
解消した内容で続ける | 直し方がわかっている |
git rebase --skip |
このコミットの変更を捨てて続ける | そのコミットが不要になった |
git rebase --abort |
rebase を始める前の状態に戻る | いったんやめて考え直したい |
解消して続けた例です(# TODO 行を消し、remove だけ残しました)。
printf 'def add(tasks, t): tasks.append(t)\ndef remove(tasks, t): tasks.remove(t)\n' > task.py
git add task.py
GIT_EDITOR=true git rebase --continue
[detached HEAD e1ab430] feat: タスク削除を実装
1 file changed, 1 insertion(+)
Rebasing (3/4)
Rebasing (4/4)
Successfully rebased and updated refs/heads/main.
072c31a feat: 件数表示を実装
5167309 docs: 使い方を追記
e1ab430 feat: タスク削除を実装
6efab19 feat: タスク追加を実装
7981b6c docs: READMEを追加
--skip を選んだ例では、「タスク削除」の変更ごと捨てられるため、次の「件数表示」も同じ場所でコンフリクトしました。
CONFLICT (content): Merge conflict in task.py
error: could not apply 9b53b61... feat: 件数表示を実装
--skip は「そのコミットの変更をなくす」操作なので、コンフリクトの解消方法としてではなく、不要なコミットを外す目的で使うと覚えておくと安心です。ここでは git rebase --abort で元の 9b53b61 に戻しました。
rebaseを取り消す(ORIG_HEADとreflog)
rebase が終わってから「まとめすぎた」と気づいても、元の履歴に戻せます。 wip と docs を両方 fixup して、意図せず5コミットを4つにした例です。
GIT_SEQUENCE_EDITOR="sed -i -e '2s/^pick/fixup/' -e '4s/^pick/fixup/'" git rebase -i HEAD~5
git reflog -n 8
9879be8 HEAD@{0}: rebase (finish): returning to refs/heads/main
9879be8 HEAD@{1}: rebase (pick): feat: 件数表示を実装
3e826da HEAD@{2}: rebase (fixup): feat: タスク削除を実装
eb4a341 HEAD@{3}: rebase (pick): feat: タスク削除を実装
ec135a6 HEAD@{4}: rebase (fixup): feat: タスク追加を実装
6efab19 HEAD@{5}: rebase (start): checkout HEAD~5
9b53b61 HEAD@{6}: commit: feat: 件数表示を実装
c80b1fa HEAD@{7}: commit: docs: 使い方を追記
戻したいのは rebase (start) のすぐ下、HEAD@{6}(9b53b61)です。 rebase 直後なら ORIG_HEAD も同じコミットを指しています。
git rev-parse --short ORIG_HEAD # → 9b53b61
git reset --hard ORIG_HEAD # または git reset --hard 'HEAD@{6}'
HEAD is now at 9b53b61 feat: 件数表示を実装
ORIG_HEAD は他の操作(reset や merge)でも上書きされるので、時間が経ったら reflog で選ぶのが確実です。reflog の詳しい読み方は別記事「git reset --hardを取り消す方法」で扱っています。作業前に git branch backup-before-rebase で目印を付けておくのも手軽です。
execでコミットごとにテストを流す
-x(--exec)を付けると、各コミットの後に exec 行が入り、コマンドが失敗した時点で止まります。 途中のコミットが壊れていないかを確かめられます。構文エラーを含むコミットと、その修正コミットを足して試しました。
echo 'def show(tasks) print(tasks)' >> task.py && git commit -qam "feat: 一覧表示を実装"
sed -i 's/def show(tasks) print/def show(tasks): print/' task.py && git commit -qam "fix: 一覧表示の構文を修正"
git rebase -i -x 'python3 -m py_compile task.py' HEAD~3
todo は次のようになります。
pick 9b53b61 feat: 件数表示を実装
exec python3 -m py_compile task.py
pick 5709131 feat: 一覧表示を実装
exec python3 -m py_compile task.py
pick 7a43c40 fix: 一覧表示の構文を修正
exec python3 -m py_compile task.py
Executing: python3 -m py_compile task.py
File "task.py", line 5
def show(tasks) print(tasks)
^^^^^
SyntaxError: expected ':'
warning: execution failed: python3 -m py_compile task.py
You can fix the problem, and then run
git rebase --continue
「一覧表示を実装」のコミット単体では構文エラーだとわかりました。 abort して、修正コミットを fixup でまとめ、間の exec 行を削除してやり直します。
git rebase --abort
GIT_SEQUENCE_EDITOR="sed -i -e '4d' -e '5s/^pick/fixup/'" git rebase -i -x 'python3 -m py_compile task.py' HEAD~3
Executing: python3 -m py_compile task.py
Executing: python3 -m py_compile task.py
Successfully rebased and updated refs/heads/main.
8a907b2 feat: 一覧表示を実装
9b53b61 feat: 件数表示を実装
c80b1fa docs: 使い方を追記
--autosquash と -x を一緒に使うと、fixup される途中のコミットには exec が付かないため、この手作業は不要になります。
--ontoと--rootの使い方
--onto:ブランチの付け根を付け替える
feature-a から切った feature-b を、feature-a を含めずに main の上へ移します。
git rebase --onto main feature-a feature-b
前:
* db8299d feat-b: 期限を追加
* 88432c4 feat-a: 完了フラグを追加
| * 9b53b61 feat: 件数表示を実装
| * c80b1fa docs: 使い方を追記
| * d2581e1 feat: タスク削除を実装
| * c5990be wip
|/
* 6efab19 feat: タスク追加を実装
* 7981b6c docs: READMEを追加
後:
* 88432c4 feat-a: 完了フラグを追加
| * a448857 feat-b: 期限を追加
| * 9b53b61 feat: 件数表示を実装
| * c80b1fa docs: 使い方を追記
| * d2581e1 feat: タスク削除を実装
| * c5990be wip
|/
* 6efab19 feat: タスク追加を実装
* 7981b6c docs: READMEを追加
git rebase --onto <新しい土台> <ここより後> <対象ブランチ> と読みます。 feature-b にあった feature-a のコミットは含まれず、feat-b のコミットだけが main の先に付きました。-i を足せば、移すコミットを選びながら付け替えられます。
--root:最初のコミットも対象にする
git rebase -i --root
pick 7981b6c docs: READMEを追加
pick 6efab19 feat: タスク追加を実装
...
HEAD~5 では最初のコミットは todo に出ませんが、--root なら含まれます。 最初のコミットを reword した結果です。
48cdd22 feat: タスク追加を実装
14a9d37 docs: READMEを作成
push済みのときの判断と--force-with-lease
rebase はコミットを作り直すため、すでに push したブランチを書き換えると、通常の push は拒否されます。
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to '../remote.git'
hint: Updates were rejected because the tip of your current branch is behind
flowchart TD
A[rebase -i で履歴を書き換えた] --> B{まだ push していない?}
B -- はい --> C[そのまま push]
B -- いいえ --> D{自分だけが使うブランチ?}
D -- はい --> E[git push --force-with-lease --force-if-includes]
D -- いいえ --> F[書き換えない。修正コミットを追加するか、チームで合意してから]
| 状況 | おすすめ |
|---|---|
| まだ push していない | 自由に rebase してよい |
| 自分専用の作業ブランチ(PR 前・PR 中) | --force-with-lease --force-if-includes で更新 |
| 複数人が push する共有ブランチ(main、develop など) | rebase で書き換えず、新しいコミットで直す |
| PR のレビュー中 | レビュアーと合意してから。fixup! で積んで最後にまとめる方法も便利 |
ローカルの bare リポジトリをリモートに見立てて確かめた結果です。
git push --force-with-lease origin main
To ../remote.git
+ 9b53b61...02a4191 main -> main (forced update)
他の人が先に push していた場合、--force-with-lease は拒否してくれます(stale info)。
! [rejected] main -> main (stale info)
error: failed to push some refs to '../remote.git'
ただし git fetch した後だと、--force-with-lease だけでは通ってしまいます。 fetch で origin/main が最新になり、「リモートは自分の知っている状態のまま」と判断されるためです。--force-if-includes(Git 2.30 以降)を足すと、取り込んでいない変更があることを検出して止まります。
--- fetch 後に --force-with-lease --force-if-includes
! [rejected] main -> main (remote ref updated since checkout)
hint: Updates were rejected because the tip of the remote-tracking branch has
hint: been updated since the last checkout. If you want to integrate the
hint: remote changes, use 'git pull' before pushing again.
--- fetch 後に --force-with-lease のみ
+ 38deeaf...748312e main -> main (forced update)
最後の出力は、他のメンバーの「38deeaf」を上書きしてしまった結果です。 書き換えた履歴を push するときは、--force-with-lease --force-if-includes をセットで使うと安心です。
よくあるつまずき
| 症状・エラー | 原因 | 対処 |
|---|---|---|
error: cannot 'fixup' without a previous commit |
todo の1行目を fixup / squash にした | 1つ範囲を広げる(HEAD~4 → HEAD~5) |
| 最初のコミットが todo に出ない | HEAD~n は基点を含まない |
--root を使う |
It seems that there is already a rebase-merge directory |
前の rebase が途中のまま | git rebase --continue か --abort |
| fixup! が並ばない | --autosquash が無い、または件名が一致しない |
-i --autosquash、--fixup=<c> で作る |
| 並べ替えでコンフリクトが続く | 同じ行の近くを触るコミットを入れ替えた | 並べ替えを減らし、まとめる単位を見直す |
push が non-fast-forward で拒否 |
履歴を書き換えた | 自分専用なら --force-with-lease --force-if-includes |
| 途中で何をしていたかわからない | ― | git status で done / 残りの todo を確認 |
よくある質問
Q. squash と fixup はどちらを使えばよいですか? A. メッセージを書き直したいなら squash、捨ててよいなら fixup です。「typo修正」「レビュー対応」のような修正コミットは fixup、意味のあるコミット同士をまとめるときは squash が向いています。
Q. 何コミット前まで指定すればよいかわかりません。
A. ブランチの分岐元を指定する git rebase -i main が便利です。 main から分かれた後の自分のコミットだけが todo に並びます。
Q. rebase 中にエディタを閉じてしまいました。
A. todo を保存せず閉じた場合は何も変わりません。途中で止まっているときは git status で状況を確認し、git rebase --edit-todo で残りの todo を編集できます。
Q. GitHub の「Squash and merge」との違いは何ですか? A. Squash and merge は PR 全体を1コミットにして取り込む機能です。rebase -i は手元で、残す粒度(2〜3コミットに整理するなど)を自分で決められます。
まとめ
git rebase -i <基点>は todo の各行に pick / reword / edit / squash / fixup / drop / exec / break を書いて履歴を作り直すコマンドです。todo は上が古い順です。- squash はメッセージを両方残して編集、fixup は前だけ残す、fixup -C は後ろのメッセージを使う。
git commit --fixupと--autosquash(またはrebase.autoSquash)で、todo を編集せずに差し込めます。- コンフリクトは
git add→--continue、不要なコミットなら--skip、やめるなら--abort。 - 終わった後でも
ORIG_HEADや reflog のrebase (start)の直前にreset --hardすれば戻せます。 -xでコミットごとにテスト、--ontoで付け根の付け替え、--rootで最初のコミットも対象にできます。- push 済みなら自分専用ブランチに限り
--force-with-lease --force-if-includes、共有ブランチは書き換えずに新しいコミットで直します。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「git rebase -iでコミットをまとめる|squash・fixup早見表と復旧」
株式会社RJCでは、読みやすいコミット履歴の作り方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。