Elaborate ## Rebasing and Merging

This commit is contained in:
SI 2020-02-27 21:53:30 +09:00 committed by GitHub
parent b3d43db200
commit 6fdf31d5b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 42 additions and 66 deletions

View File

@ -1299,9 +1299,8 @@ origin/HEAD set to master
<a name="undo-rebase"></a> <a name="undo-rebase"></a>
### リベースやマージを取り消したい ### リベースやマージを取り消したい
現在のブランチを間違ったブランチにリベースないしマージしてしまった、あるいはリベースないしマージが出来なさそうと気づいたとしましょう。 現在のブランチを間違ったブランチにリベースないしマージしてしまった、あるいはリベースないしマージができなさそうだと気づいたとしましょう。
Git は危険な操作の前に HEAD が指すものを変数 `ORIG_HEAD` に保存しているので、ブランチをリベースないしマージの前の状態に差し戻すのは簡単です。 Git は危険な操作の前に HEAD が指すものを変数 `ORIG_HEAD` に保存しているので、ブランチをリベースないしマージの前の状態に差し戻すのは簡単です。
<!--You may have merged or rebased your current branch with a wrong branch, or you can't figure it out or finish the rebase/merge process. Git saves the original HEAD pointer in a variable called ORIG_HEAD before doing dangerous operations, so it is simple to recover your branch at the state before the rebase/merge.-->
```sh ```sh
(my-branch)$ git reset --hard ORIG_HEAD (my-branch)$ git reset --hard ORIG_HEAD
@ -1310,14 +1309,13 @@ Git は危険な操作の前に HEAD が指すものを変数 `ORIG_HEAD` に保
<a name="force-push-rebase"></a> <a name="force-push-rebase"></a>
### リベースしたが、強制プッシュはしたくない ### リベースしたが、強制プッシュはしたくない
残念ながら、編集内容をリモートブランチに反映させるには強制プッシュをする必要があります。編集履歴を変えてしまったからです。 残念ながら、編集をリモートブランチに反映させるには強制プッシュをする必要があります。
強制プッシュしない限りリモートブランチは編集内容を受け付けません。 編集履歴を変えてしまったからです。
強制プッシュしない限り、リモートブランチは編集内容を受け付けません。
これが多くの人がリベースワークフローではなくマージワークフローを使う主な理由です。 これが多くの人がリベースワークフローではなくマージワークフローを使う主な理由です。
特に大規模な開発チームは誰かの強制プッシュでハマりやすいです。 特に大規模な開発チームは強制プッシュでハマりやすいです。
リベースの強制プッシュは注意して使いましょう。 リベースの強制プッシュは注意して使いましょう。
リベースを使う安全な方法は、リモートには編集内容を反映させず、代わりに次を実行することです: リベースの安全な使い方は、リモートには編集内容を反映させずに、代わりに次を実行することです。
<!--Unfortunately, you have to force push, if you want those changes to be reflected on the remote branch. This is because you have changed the history. The remote branch won't accept changes unless you force push. This is one of the main reasons many people use a merge workflow, instead of a rebasing workflow - large teams can get into trouble with developers force pushing. Use this with caution. A safer way to use rebase is not to reflect your changes on the remote branch at all, and instead to do the following:-->
```sh ```sh
(master)$ git checkout my-branch (master)$ git checkout my-branch
@ -1331,33 +1329,30 @@ Git は危険な操作の前に HEAD が指すものを変数 `ORIG_HEAD` に保
<a name="interactive-rebase"></a> <a name="interactive-rebase"></a>
### コミットを統合したい ### コミットを統合したい
`master` ブランチにプルリクエストを送る、またはこれから送るつもりのブランチで作業しているとしましょう。 `master` ブランチにプルリクエストを送る、あるいはこれから送るつもりのブランチで作業しているとします。
最も単純なケースとして、タイムスタンプを気にせず**全部の**コミットを一つにまとめてしまいたいとします。この場合はリセットと再コミットを行います。 最も単純なケースとして、タイムスタンプを気にせずコミット**全部**を一つにまとめたいとします。
マスターブランチが最新版で、編集内容がすべてコミットされていることを確認した上で、次を実行します: この場合はリセットと再コミットを行います。
マスターブランチが最新版で、編集内容がすべてコミットされていることを確認した上で、次を実行してください。
<!--Let's suppose you are working in a branch that is/will become a pull-request against `master`. In the simplest case when all you want to do is to combine *all* commits into a single one and you don't care about commit timestamps, you can reset and recommit. Make sure the master branch is up to date and all your changes committed, then:-->
```sh ```sh
(my-branch)$ git reset --soft master (my-branch)$ git reset --soft master
(my-branch)$ git commit -am "New awesome feature" (my-branch)$ git commit -am "New awesome feature"
``` ```
もっと細かく設定し、タイムスタンプも残したい場合は、対話的リベースと呼ばれるものを使うます: もっと細かく設定し、タイムスタンプも残したい場合は、対話的リベースを使います。
<!--If you want more control, and also to preserve timestamps, you need to do something called an interactive rebase:-->
```sh ```sh
(my-branch)$ git rebase -i master (my-branch)$ git rebase -i master
``` ```
別のブランチで作業しているわけではない場合、`HEAD` に対してリベースする必要があります。たとえば直近二件のコミットを圧縮 (squash) したい場合は `HEAD~2`、直近三件なら `HEAD~3` です。 別のブランチで作業しているわけではない場合、`HEAD` に対してリベースする必要があります。
<!--If you aren't working against another branch you'll have to rebase relative to your `HEAD`. If you want to squash the last 2 commits, for example, you'll have to rebase against `HEAD~2`. For the last 3, `HEAD~3`, etc.--> たとえば直近二件のコミットを圧縮 (squash) したい場合は `HEAD~2`、直近三件なら `HEAD~3` です。
```sh ```sh
(master)$ git rebase -i HEAD~2 (master)$ git rebase -i HEAD~2
``` ```
対話的リベースのコマンドを実行したら、テキストエディタに次のように表示されます: 対話的リベースのコマンドを実行したら、テキストエディタに次のように表示されます。
<!--After you run the interactive rebase command, you will see something like this in your text editor:-->
```vim ```vim
pick a9c8a1d Some refactoring pick a9c8a1d Some refactoring
@ -1386,11 +1381,10 @@ pick e3851e8 another fix
ここで `#` から始まる行はコメントなので、リベースに影響しません。 ここで `#` から始まる行はコメントなので、リベースに影響しません。
`pick` コマンドをリストにある好きなコマンドで置き換えればよいです。行を削除すればコミットを削除できます。 コマンド `pick` をリストの好きなコマンドで書きかえればよいです。
<!--Then you replace `pick` commands with any in the list above, and you can also remove commits by removing corresponding lines.--> 行を削除すればコミットを削除できます。
例えば、**一番古い(一番目の)コミットはそのまま残し、他のコミット全てを二番目のコミットに統合したい**場合は、最初と二番目のコミット以外のコミットの横に表示された文字を例えば `f` に修正します: 例えば、**一番古い(一番目の)コミットはそのまま残し、他のコミット全てを二番目のコミットに統合したい**場合は、最初と二番目以外のコミットの横の文字を `f` に書きかえます。
<!--For example, if you want to **leave the oldest (first) commit alone and combine all the following commits with the second oldest**, you should edit the letter next to each commit except the first and the second to say `f`:-->
```vim ```vim
pick a9c8a1d Some refactoring pick a9c8a1d Some refactoring
@ -1399,8 +1393,7 @@ f b729ad5 fixup
f e3851e8 another fix f e3851e8 another fix
``` ```
コミットを統合し、**さらに名前も変更したい**場合は、二番目のコミットの横にさらに `r` の文字を追加するか、あるいは単に `f` の代わりに `s` を使います: コミットを統合し、**さらに名前も変更したい**場合は、二番目のコミットの横にさらに `r` の文字を追加するか、あるいは単に `f` の代わりに `s` を使います。
<!--If you want to combine these commits **and rename the commit**, you should additionally add an `r` next to the second commit or simply use `s` instead of `f`:-->
```vim ```vim
pick a9c8a1d Some refactoring pick a9c8a1d Some refactoring
@ -1410,7 +1403,6 @@ s e3851e8 another fix
``` ```
するとテキストエディタが起動し、コミットの名前を変更できます。 するとテキストエディタが起動し、コミットの名前を変更できます。
<!--You can then rename the commit in the next text prompt that pops up.-->
```vim ```vim
Newer, awesomer features Newer, awesomer features
@ -1426,8 +1418,7 @@ Newer, awesomer features
``` ```
うまくいくと次のように表示されるはずです: うまくいくと次のように表示されるはずです。
<!--If everything is successful, you should see something like this:-->
```sh ```sh
(master)$ Successfully rebased and updated refs/heads/master. (master)$ Successfully rebased and updated refs/heads/master.
@ -1435,14 +1426,14 @@ Newer, awesomer features
#### 安全なマージの方法 #### 安全なマージの方法
`--no-commit` performs the merge but pretends the merge failed and does not autocommit, giving the user a chance to inspect and further tweak the merge result before committing. `no-ff` maintains evidence that a feature branch once existed, keeping project history consistent. オプション `--no-commit` を指定すると、マージを実行しつつ、あたかもマージが失敗したかのように扱って自動コミットはしません。
これにより、コミットの前にマージの結果を精査したり調整できます。
オプション `--no-ff` はフィーチャーブランチが存在したことを記録に残しておき、プロジェクト履歴の一貫性を保ちます。
```sh ```sh
(master)$ git merge --no-ff --no-commit my-branch (master)$ git merge --no-ff --no-commit my-branch
``` ```
オプション `--no-commit` を指定すると、マージを実行しつつ、あたかもマージが失敗したかのように扱って自動コミットはしません。これにより、コミットの前にマージの結果を精査したり調整できます。オプション `no-ff` はフィーチャーブランチが存在したことを記録しておき、プロジェクト履歴の一貫性を保ちます。
#### ブランチを一つのコミットにまとめたい場合 #### ブランチを一つのコミットにまとめたい場合
```sh ```sh
@ -1452,56 +1443,49 @@ Newer, awesomer features
<a name="rebase-unpushed-commits"></a> <a name="rebase-unpushed-commits"></a>
#### プッシュされていないコミットのみを統合したい場合 #### プッシュされていないコミットのみを統合したい場合
進行中の作業に関するコミットがいくつかあって、upstream にコミットする前に統合しておきたいことがあるでしょう 進行中の作業に関するコミットがいくつかあって、upstream にコミットする前に統合しておきたいとします
すでに upstream にプッシュされたコミットは、誰かがそれを参照するコミットをしている可能性があるので、それは統合しないでおきたいとします。 すでに upstream にプッシュされたコミットは、誰かがそれを参照するコミットをしている可能性があるので、それは統合しないでおきたいとします。
<!--Sometimes you have several work in progress commits that you want to combine before you push them upstream. You don't want to accidentally combine any commits that have already been pushed upstream because someone else may have already made commits that reference them.-->
```sh ```sh
(master)$ git rebase -i @{u} (master)$ git rebase -i @{u}
``` ```
上を実行すると対話的リベースが始まりますが、一覧にはまだプッシュされていないコミットだけが表示されます。これで順番を入れ替えたり、修正したり、圧縮 (squash) したりしても安全です。 上を実行すると対話的リベースが始まりますが、一覧にはまだプッシュされていないコミットだけが表示されます。
<!--This will do an interactive rebase that lists only the commits that you haven't already pushed, so it will be safe to reorder/fix/squash anything in the list.--> これで順番を入れ替えたり、修正したり、圧縮 (squash) したりしても安全です。
#### マージを中止したい #### マージを中止したい
マージがファイルに問題をきたすことがあります。 マージがファイルに問題をきたすことがあります。
こういうときはオプション `abort` を使うとコンフリクト解消の作業を中止し、マージの前の状態の復元を試みることができます。 こういうときはオプション `abort` を使うとコンフリクト解消の作業を中止し、マージの前の状態の復元を試みることができます。
<!--Sometimes the merge can produce problems in certain files, in those cases we can use the option `abort` to abort the current conflict resolution process, and try to reconstruct the pre-merge state.-->
```sh ```sh
(my-branch)$ git merge --abort (my-branch)$ git merge --abort
``` ```
ただし、このコマンドが使えるのはバージョン 1.7.4 以上の Git です。 ただし、このコマンドが使えるのはバージョン 1.7.4 以上の Git に限ります。
<!--This command is available since Git version >= 1.7.4-->
### ブランチの親コミットを更新したい ### ブランチの親コミットを更新したい
マスターブランチとそこから分岐した feature-1 ブランチがあり、feature-1 からさらに分岐した feature-2 ブランチがあるとします。 マスターブランチとそこから分岐した feature-1 ブランチがあり、feature-1 からさらに分岐した feature-2 ブランチがあるとします。
feature-1 ブランチにコミットしたとすると、feature-2 ブランチの親コミットはもはや正確ではありませんfeature-1 から分岐したので、親コミットは feature-1 ブランチの head であるべきです。) いま feature-1 ブランチにコミットしたとすると、feature-2 ブランチの親コミットはもはや正確ではありませんfeature-1 から分岐したので、親コミットは feature-1 ブランチの head であるべきです。)
こういうときは `git rebase --onto` で修正できます。 こういうときは `git rebase --onto` で修正できます。
<!--Say I have a master branch, a feature-1 branch branched from master, and a feature-2 branch branched off of feature-1. If I make a commit to feature-1, then the parent commit of feature-2 is no longer accurate (it should be the head of feature-1, since we branched off of it). We can fix this with `git rebase --onto`.-->
```sh ```sh
(feature-2)$ git rebase --onto feature-1 <the first commit in your feature-2 branch that you don't want to bring along> feature-2 (feature-2)$ git rebase --onto feature-1 <the first commit in your feature-2 branch that you don't want to bring along> feature-2
``` ```
まだマージされていないブランチからフィーチャーブランチを分岐させており、feature-1 ブランチのバグ修正を feature-2 に反映させたいときに便利です。 まだマージされていないブランチからフィーチャーブランチを分岐させており、feature-1 ブランチのバグ修正を feature-2 に反映させたいときに便利です。
<!--This helps in sticky scenarios where you might have a feature built on another feature that hasn't been merged yet, and a bugfix on the feature-1 branch needs to be reflected in your feature-2 branch.-->
### ブランチの全コミットがマージされているか確認する ### ブランチの全コミットがマージされているか確認する
ブランチの全てのコミットが別のブランチにマージされたか確認するには、それぞれのブランチの headあるいは任意のコミットの間の差分を表示します ブランチの全コミットが別のブランチにマージされているか確認するには、それぞれのブランチの headあるいは任意のコミットの間の差分を表示します。
<!--To check if all commits on a branch are merged into another branch, you should diff between the heads (or any commits) of those branches:-->
```sh ```sh
(master)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature/120-on-scroll (master)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature/120-on-scroll
``` ```
一方のブランチにしかないコミットがあるか表示され、ブランチの間で共有されていないコミットの一覧がわかります。 一方のブランチにしかないコミットがあるか表示され、ブランチ間で共有されていないコミットの一覧がわかります。
もう一つの方法は: もう一つの方法は次の通りです。
<!--This will tell you if any commits are in one but not the other, and will give you a list of any nonshared between the branches. Another option is to do this:-->
```sh ```sh
(master)$ git log master ^feature/120-on-scroll --no-merges (master)$ git log master ^feature/120-on-scroll --no-merges
@ -1512,26 +1496,24 @@ Newer, awesomer features
<a name="noop"></a> <a name="noop"></a>
#### リベース編集画面に 'noop' と表示される #### リベース編集画面に 'noop' と表示される
次のように表示されたとします: 次のように表示された場合です。
``` ```
noop noop
``` ```
これは、同じコミットのブランチ、あるいは現在のブランチよりも*先*にあるブランチに対してリベースしようとしたときに表示されるものです。こういう場合は: これは、同じコミットのブランチ、あるいは現在のブランチよりも*先*にあるブランチに対してリベースしようとしたときに表示されます。
<!--That means you are trying to rebase against a branch that is at an identical commit, or is *ahead* of your current branch. You can try:--> この場合は、
* マスターブランチが正しい場所にあることを確認してください。<!--making sure your master branch is where it should be--> * マスターブランチが正しい場所にあることを確認してください。
* `HEAD~2` あるいはより以前にリベースしてください。<!--rebase against `HEAD~2` or earlier instead--> * `HEAD~2` あるいはより以前にリベースしてください。
<a name="merge-conflict"></a> <a name="merge-conflict"></a>
#### コンフリクトがあった #### コンフリクトがあった
リベースができないときは、解消すべきコンフリクトがあるかもしれません。 リベースができないときは、解消すべきコンフリクトがあるかもしれません。
<!--If you are unable to successfully complete the rebase, you may have to resolve conflicts.-->
まず `git status` で、どのファイルがコンフリクトを起こしているか確認します: まず `git status` で、どのファイルがコンフリクトを起こしているか確認します。
<!--First run `git status` to see which files have conflicts in them:-->
```sh ```sh
(my-branch)$ git status (my-branch)$ git status
@ -1543,8 +1525,8 @@ Changes not staged for commit:
both modified: README.md both modified: README.md
``` ```
この例では `README.md` がコンフリクトをきたしています。ファイルを開き、次のようになっている部分を見てみましょう: この例では `README.md` にコンフリクトがあります。
<!--In this example, `README.md` has conflicts. Open that file and look for the following:--> ファイルを開き、次のようになっている箇所を見てみましょう。
```vim ```vim
<<<<<<< HEAD <<<<<<< HEAD
@ -1554,28 +1536,24 @@ Changes not staged for commit:
>>>>>>> new-commit >>>>>>> new-commit
``` ```
`HEAD` と新しいコミットで加えられたコードの間の差分(この例では、真ん中の行から `new-commit` までの間にあるコード)を解消する必要があります。 `HEAD` と新しいコミットで加えられたコードの間の差分(この例では、真ん中の行から `new-commit` の間にあるコード)を解消する必要があります。
<!--You will need to resolve the differences between the code that was added in your new commit (in the example, everything from the middle line to `new-commit`) and your `HEAD`.-->
一方のブランチの版のコードを残したい場合は、`--ours` あるいは `--theirs` を指定します。 一方のブランチの版のコードを残したい場合は、`--ours` あるいは `--theirs` を指定します。
<!--If you want to keep one branch's version of the code, you can use `--ours` or `--theirs`:-->
```sh ```sh
(master*)$ git checkout --ours README.md (master*)$ git checkout --ours README.md
``` ```
- *マージする*場合、ローカルブランチの編集内容を残したいとき `--ours` を指定し、他方の編集内容を残したいとき `--theirs` を指定します。 - *マージする*場合、ローカルブランチの編集内容を残したいとき `--ours` を指定し、他方の編集内容を残したいとき `--theirs` を指定します。
- *リベースする*場合、ローカルブランチの編集内容を残したいとき `--theirs` を指定し、他方の編集内容を残したいとき `--ours` を指定します。このように逆転する理由は[Git ドキュメントのこのノート](https://git-scm.com/docs/git-rebase#git-rebase---merge)を参照してください。 - *リベースする*場合、ローカルブランチの編集内容を残したいとき `--theirs` を指定し、他方の編集内容を残したいとき `--ours` を指定します。このように逆転する理由は[ Git ドキュメントのこのノート](https://git-scm.com/docs/git-rebase#git-rebase---merge)を参照してください。
マージがもっと複雑な場合はヴィジュアル差分エディタを使うことができます: マージがもっと複雑なときは、ビジュアル差分エディタを使うとよいです。
<!--If the merges are more complicated, you can use a visual diff editor:-->
```sh ```sh
(master*)$ git mergetool -t opendiff (master*)$ git mergetool -t opendiff
``` ```
コンフリクトを全て解消し、コードのテストが済んだら、`git add ` で編集内容をステージし、`git rebase --continue` でリベースを再開します。 コンフリクトを全て解消し、コードのテストが済んだら、`git add ` で編集内容をステージし、`git rebase --continue` でリベースを再開します。
<!--After you have resolved all conflicts and tested your code, `git add` the files you have changed, and then continue the rebase with `git rebase --continue`-->
```sh ```sh
(my-branch)$ git add README.md (my-branch)$ git add README.md
@ -1583,10 +1561,8 @@ Changes not staged for commit:
``` ```
コンフリクトを解消した結果、ワーキングツリーがコミット前と全く同じ状態になった場合は、代わりに `git rebase --skip` を実行します。 コンフリクトを解消した結果、ワーキングツリーがコミット前と全く同じ状態になった場合は、代わりに `git rebase --skip` を実行します。
<!--If after resolving all the conflicts you end up with an identical tree to what it was before the commit, you need to `git rebase --skip` instead.-->
リベース作業を全て中止し、ブランチを元の状態に差し戻したい場合は、次のようにします: リベース作業を全て中止し、ブランチを元の状態に差し戻したい場合は、次を実行します。
<!--If at any time you want to stop the entire rebase and go back to the original state of your branch, you can do so:-->
```sh ```sh
(my-branch)$ git rebase --abort (my-branch)$ git rebase --abort