git rebaseのeditで過去のリビジョンを変更する

`git rebase -i` の`squash`と`reword`はよく使っているが、`edit`もなんとなくわかったのでメモ。
具体例として「ファイルをあるリビジョンから別のRev.に移動する」、「リビジョンを分離する」の例を示す。


# git rebae の edit の動き

リビジョン（以降Rev.）が下記のようにあるとする（上ほど古い）。

```
aaa1111
bbb2222
ccc3333
ddd4444
eee5555 ← HEAD
```

この状態から Rev.bbb2222 と Rev.ddd4444 を変更したい場合、`git rebase -i`で次のように bbb2222 と ddd4444 のコマンドを e（またはedit）と変更する。

```
pick aaa1111 change a
e bbb2222 chnage b
pick ccc3333 chnage c
e ddd4444 chnage d
pick eee5555 change e
```

すると、まず e と変更したRev.のうち、一番古い Rev.bbb2222 が **コミットされた直後の状態となる**（HEADが Rev.bbb2222を指している）。

```
aaa1111
bbb2222 ← HEAD
```

コミット直後の状態なので、modifiedなファイルもstagedなファイルもない。

## リビジョンの変更の方法
ここから変更内容によって以下のように操作する。

### 新たなファイルの追加する場合
Rev.bbb2222 に含まれていないファイルを`add`して`commit --amend`する。
1. `git add ＜追加するファイル＞`
2. `git commit --amend`

### 変更対象Rev.に含まれているファイルを再編集する場合
Rev.bbb2222 に含まれているファイルを変更し、`add`して`commit --amend`する。
1. ファイルを編集する。
2. `git add ＜編集したファイル＞`
3. `git commit --amend`

### 変更対象Rev.からファイルを除外する場合
`reset @^`で1つ前のRev.に戻る。そして除外するファイル以外を`add`してコミットする。
1. `git reset @^`
2. `git add ＜Rev.に残すファイル＞`
3. `git commit`

modifiedのまま残っているファイルは、以下のように処理する。
・変更を捨てるなら`checkout`する。
・直後に別なRev.としてコミットするなら`add`して`commit`する。
・もっと先のRev.で使うなら一旦`stash`する。

## rebase --continue
Rev.bbb2222 に対する編集が終わったら `git rebase --continue` を実行する。
すると次の変更対象Rev.の Rev.ddd4444 に移動する（HEADが Rev.ddd4444 を指すようになる）。
Rev.ddd4444の変更が終わって `git rebase --continue` を実行すると、`git rebase` が完了する。


# 例

具体例として

```shell
y.into@pc01 MINGW64 /c/git_test (master)
$ git log --oneline --name-status -4
41f8473 (HEAD -> master) change d
M       d1.txt
M       d2.txt
d07aa72 change c
M       c1.txt
f721de1 change b
M       b1.txt
M       b2.txt
1109412 change a
M       a1.txt
M       a2.txt
M       c2.txt
```

という状態から次の変更を `git rebase` で行う。

- Rev.1109412 "change a" に含まている c2.txt の変更を Rev.d07aa72 "chanage c" に移す。
- Rev.f721de1 "change b" を2つのRev.に分割する。

環境は Windows10、Git for Windows、gitbash。

-----

まず、`git rebase -i @~4` を実行する。

```shell
pick 1109412 change a
pick f721de1 change b
pick d07aa72 change c
pick 41f8473 change d

# Rebase 1544237..41f8473 onto 41f8473 (4 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
＜以下略＞
```

変更対象のRev.1109412、f721de1、d07aa72 のコマンドを "e"（＝edit）と変更する。

```
e 1109412 change a
e f721de1 change b
e d07aa72 change c
pick 41f8473 change d
```

viを終了すると、最初の変更対象Rev.である Rev.1109412 をコミットした直後の状態となる（HEADが Rev.1109412 を指している）。

```shell
y.into@pc01 MINGW64 /c/git_test (master)
$ git rebase -i @~4
Stopped at 1109412...  change a
You can amend the commit now, with

  git commit --amend

Once you are satisfied with your changes, run

  git rebase --continue

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 1/4)
$ git status
interactive rebase in progress; onto 1544237
Last command done (1 command done):
   edit 1109412 change a
Next commands to do (3 remaining commands):
   edit f721de1 change b
   edit d07aa72 change c
  (use "git rebase --edit-todo" to view and edit)
You are currently editing a commit while rebasing branch 'master' on '1544237'.
  (use "git commit --amend" to amend the current commit)
  (use "git rebase --continue" once you are satisfied with your changes)

nothing to commit, working tree clean

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 1/4)
$ git log --oneline --name-status -1
1109412 (HEAD) change a
M       a1.txt
M       a2.txt
M       c2.txt
```

`reset` で1つ前のRev.に戻し、残したい変更だけ`add`して`commit`する。

```shell
y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 1/4)
$ git reset @^
Unstaged changes after reset:
M       a1.txt
M       a2.txt
M       c2.txt

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 1/4)
$ git status
interactive rebase in progress; onto 1544237
Last command done (1 command done):
   edit 1109412 change a
Next commands to do (3 remaining commands):
   edit f721de1 change b
   edit d07aa72 change c
  (use "git rebase --edit-todo" to view and edit)
You are currently splitting a commit while rebasing branch 'master' on '1544237'.
  (Once your working directory is clean, run "git rebase --continue")

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:   a1.txt
        modified:   a2.txt
        modified:   c2.txt

no changes added to commit (use "git add" and/or "git commit -a")

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 1/4)
$ git add a1.txt a2.txt

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 1/4)
$ git commit -m 'change a'
[detached HEAD dcd363b] change a
 2 files changed, 2 insertions(+)

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 1/4)
$ git log --oneline --name-status -1
dcd363b (HEAD) change a
M       a1.txt
M       a2.txt
```

残されたmodifiedな c2.txt は後のRev.に追加するので`stash`で退避しておく。

```shell
y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 1/4)
$ git status
interactive rebase in progress; onto 1544237
Last command done (1 command done):
   edit 1109412 change a
Next commands to do (3 remaining commands):
   edit f721de1 change b
   edit d07aa72 change c
  (use "git rebase --edit-todo" to view and edit)
You are currently splitting a commit while rebasing branch 'master' on '1544237'.
  (Once your working directory is clean, run "git rebase --continue")

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:   c2.txt

no changes added to commit (use "git add" and/or "git commit -a")

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 1/4)
$ git stash push
Saved working directory and index state WIP on (no branch): dcd363b change a

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 1/4)
$ git status
interactive rebase in progress; onto 1544237
＜省略＞

nothing to commit, working tree clean
```

最初の変更対象Rev.の操作は終わったので、`git rebase --continue` で次の変更対象Rev.まで進む。

```shell
y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 1/4)
$ git rebase --continue
Stopped at f721de1...  change b
You can amend the commit now, with

  git commit --amend

Once you are satisfied with your changes, run

  git rebase --continue

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 2/4)
$ git status
interactive rebase in progress; onto 1544237
Last commands done (2 commands done):
   edit 1109412 change a
   edit f721de1 change b
Next commands to do (2 remaining commands):
   edit d07aa72 change c
   pick 41f8473 change d
  (use "git rebase --edit-todo" to view and edit)
You are currently editing a commit while rebasing branch 'master' on '1544237'.
  (use "git commit --amend" to amend the current commit)
  (use "git rebase --continue" once you are satisfied with your changes)

nothing to commit, working tree clean

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 2/4)
$ git log --oneline --name-status -1
207e142 (HEAD) change b
M       b1.txt
M       b2.txt
```

第2の変更対象である Rev. は2個に分割する。
`reset`で1つ前のRev.に戻した後、2つのファイルをそれぞれ別々にコミットする。

```shell
y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 2/4)
$ git reset @^
Unstaged changes after reset:
M       b1.txt
M       b2.txt

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 2/4)
$ git status
interactive rebase in progress; onto 1544237
Last commands done (2 commands done):
   edit 1109412 change a
   edit f721de1 change b
Next commands to do (2 remaining commands):
   edit d07aa72 change c
   pick 41f8473 change d
  (use "git rebase --edit-todo" to view and edit)
You are currently splitting a commit while rebasing branch 'master' on '1544237'.
  (Once your working directory is clean, run "git rebase --continue")

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:   b1.txt
        modified:   b2.txt

no changes added to commit (use "git add" and/or "git commit -a")

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 2/4)
$ git add b1.txt

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 2/4)
$ git commit -m 'change b1'
[detached HEAD b23ff21] change b1
 1 file changed, 1 insertion(+)

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 2/4)
$ git add b2.txt

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 2/4)
$ git commit -m 'change b2'
[detached HEAD fb3874d] change b2
 1 file changed, 1 insertion(+)

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 2/4)
$ git log --oneline --name-status -2
fb3874d (HEAD) change b2
M       b2.txt
b23ff21 change b1
M       b1.txt
```

第2の変更対象Rev.の操作も終わったので、`git rebase --continue`で最後の変更対象Rev.に進む。

```shell
y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 2/4)
$ git rebase --continue
Stopped at d07aa72...  change c
You can amend the commit now, with

  git commit --amend

Once you are satisfied with your changes, run

  git rebase --continue

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 3/4)
$ git status
interactive rebase in progress; onto 1544237
Last commands done (3 commands done):
   edit f721de1 change b
   edit d07aa72 change c
  (see more in file .git/rebase-merge/done)
Next command to do (1 remaining command):
   pick 41f8473 change d
  (use "git rebase --edit-todo" to view and edit)
You are currently editing a commit while rebasing branch 'master' on '1544237'.
  (use "git commit --amend" to amend the current commit)
  (use "git rebase --continue" once you are satisfied with your changes)

nothing to commit, working tree clean

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 3/4)
$ git log --oneline --name-status -1
41567ce (HEAD) change c
M       c1.txt
```

Rev.89f04bc "change c" に Rev.9b59dca "change a" から取り除いた c2.txt を追加する。
`stash`から取り出して`add`して`commit --amend`する。

```shell
y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 3/4)
$ git stash pop
interactive rebase in progress; onto 1544237
Last commands done (3 commands done):
   edit f721de1 change b
   edit d07aa72 change c
  (see more in file .git/rebase-merge/done)
Next command to do (1 remaining command):
   pick 41f8473 change d
  (use "git rebase --edit-todo" to view and edit)
You are currently splitting a commit while rebasing branch 'master' on '1544237'.
  (Once your working directory is clean, run "git rebase --continue")

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:   c2.txt

no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (28cf2135c67665f5ca212664692fd66ad8a5d050)

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 3/4)
$ git status
interactive rebase in progress; onto 1544237
Last commands done (3 commands done):
   edit f721de1 change b
   edit d07aa72 change c
  (see more in file .git/rebase-merge/done)
Next command to do (1 remaining command):
   pick 41f8473 change d
  (use "git rebase --edit-todo" to view and edit)
You are currently splitting a commit while rebasing branch 'master' on '1544237'.
  (Once your working directory is clean, run "git rebase --continue")

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:   c2.txt

no changes added to commit (use "git add" and/or "git commit -a")

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 3/4)
$ git add c2.txt

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 3/4)
$ git commit --amend --no-edit
[detached HEAD 90c4996] change c
 Date: Sun Jul 28 20:44:25 2019 +0900
 2 files changed, 2 insertions(+)

y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 3/4)
$ git log --oneline --name-status -1
90c4996 (HEAD) change c
M       c1.txt
M       c2.txt
```

3つのRev.の変更が終わったので最後の `git rebase --continue` で作業完了。
下記の`git log`の結果にあるようにRev.の変更がなされた。

```shell
y.into@pc01 MINGW64 /c/git_test (master|REBASE-i 3/4)
$ git rebase --continue
Successfully rebased and updated refs/heads/master.

y.into@pc01 MINGW64 /c/git_test (master)
$ git log --oneline --name-status -5
5043827 (HEAD -> master) change d
M       d1.txt
M       d2.txt
90c4996 change c
M       c1.txt
M       c2.txt
fb3874d change b2
M       b2.txt
b23ff21 change b1
M       b1.txt
dcd363b change a
M       a1.txt
M       a2.txt
```