【git】リモート先の異なるブランチ、異なるコミット履歴を持つブランチ同士を一箇所に統合する


投稿日 2022年6月7日 >> 更新日 2022年6月8日

概要

gitプロジェクトを開始した時、GitHub上(リモート)のデフォルトブランチとは異なるブランチでプッシュ(送信)してしまいコミット履歴も異なるブランチが作成されてしまったという類の問題に対してまとめてみました。
ブランチというgit特有の機能が今一理解できていない初学者にとってもこの問題を処理していく過程で少しは理解ができるものかと思います。

実行環境

実行環境
Windows Subsystem for Linux
git version 2.17.1

リモート先に異なるブランチ、異なるコミット履歴を持つブランチを作成する

ここではあえて、リモートはリモートのデフォルトブランチでレポジトリを作成し、ローカルはローカルのデフォルトブランチでレポジトリを作成しコミット後そのままプッシュ(送信)してしまったという体(てい)で進めて行きます。

GitHubで新規にレポジトリを作成し、ファイルを追加する

「new repository」を開き、「test-merge」という名前で作成します。

「Add a README file」の項目にチェックを入れ、ファイルを追加した状態でレポジトリを作成します。リモート側でレポジトリを作成する際にファイルを追加すると、デフォルトブランチが「main」となって作成されます。

「main」というデフォルトブランチの画面に切り替わったら、もう1つ適当にファイルを追加しておきます。

ファイルを追加しコミットされたら、下図のようなレポジトリとなります。あとはもう1つ別のブランチを作成し、異なるコミット履歴を記録するだけです。

ローカルレポジトリを作成し、リモートへプッシュする

ローカル環境でgitプロジェクトを開始します。

適当な作業場となるディレクトリに移動し、「git init」でプロジェクトを開始します。

$ git init
Initialized empty Git repository in /User/home/test_merge/.git/

私は「test_merge」というディレクトリでプロジェクトを開始しました。

「ls」コマンドで確認してみると、gitの設定ファイルが格納された「.git」が配置されています。

$ ls -a
.  ..  .git

gitの設定項目は「git config --list」で取得できます。

$ git config --list
user.name=User-Name
user.email=address@gmail.com
core.repositoryformatversion=0
core.filemode=false
core.bare=false
core.logallrefupdates=true
core.ignorecase=true

では適当にファイルを追加して、コミットします。

$ touch local_repo.txt
$ git add local_repo.txt
$ git commit -m 'ローカルからファイルを追加'
[master (root-commit) f87c7c0] ローカルからファイルを追加
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 local_repo.txt

ローカルレポジトリのデフォルトブランチは「master」として作成されます。

「git branch」コマンドと打つと現在のブランチを確認できます。

$ git branch
* master

ローカルレポジトリをリモートレポジトリへ送信したいので、URLを「origin」に設定します。下図のように既に作成済みのリモートレポジトリからURLをコピーしておきます。

コピーしたURLを下記のコマンドに添えます。

$ git remote add origin https://github.com/user-name/new-merge.git

確認コマンドを打つとリモート先となるURLが追記されているのが分かります。

$ git config --list
user.name=User-Name
user.email=address@gmail.com
core.repositoryformatversion=0
core.filemode=false
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
remote.origin.url=https://github.com/user-name/new-merge.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*

送信先であるリモートレポジトリのブランチは「main」ですが、「master」としてプッシュ(送信)します。

$ git push origin master
Username for 'https://github.com': ユーザー名
Password for 'https://user-name@github.com':アクセストークン
Counting objects: 3, done.
Writing objects: 100% (3/3), 259 bytes | 51.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote:
remote: Create a pull request for 'master' on GitHub by visiting:
remote:      https://github.com/user-name/new-merge/pull/new/master
remote:
To https://github.com/user-name/new-merge.git
 * [new branch]      master -> master

無事にリモートレポジトリでは新しく「master」のブランチが作成されました。

リモートレポジトリを確認してみると、ローカルレポジトリで追加した「local_repo.txt」というファイルはありませんが、ブランチに「master」が追加されています。

ブランチを切り替えてみると、ローカルレポジトリで追加されたファイルがあることが確認できます。

リモートの異なるブランチをローカルで統合する

ローカルレポジトリが既に存在している場合

こちらの作業ではこの記事の上部でこれまで行ってきたことの続きとなります。

では、既存のファイル・ブランチ・コミット履歴を確認します。

$ ls
local_repo.txt
$ git branch
* master
$ git log
commit 57fb77f00fc258039b942e3354c4f96625c9efea (HEAD -> master, origin/master)
Author: User-Name <address@gmail.com>
Date:   Wed Jun 8 08:21:43 2022 +0900

    ローカルからファイルの追加

ローカルレポジトリではデフォルトブランチが「master」となっているのでmasterだけが作成されいます。

リモートレポジトリにある「main」と「master」ブランチのうち「main」の方に統合させ開発を進めて行きたいので、ローカルレポジトリに「main」のブランチを作成します。

$ git branch main
$ git branch
  main
* master

ブランチ「main」が作成されたので、「master」から「main」に移動します。

$ git checkout main
Switched to branch 'main'
$ git branch
* main
  master

ファイルを確認すると、ブランチ「master」で追加しコミットしたファイルがあります。この状態でリモートレポジトリのブランチ「main」とローカルレポジトリのブランチ「main」を統合させます。

$ ls
local_repo.txt
$ git pull --rebase origin main
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (6/6), done.
From https://github.com/user-name/new-merge
 * branch            main       -> FETCH_HEAD
 * [new branch]      main       -> origin/main
First, rewinding head to replay your work on top of it...
Applying: ローカルからファイルの追加
$ ls
README.md  local_repo.txt  remote_repo.txt

統合と言ってもリモートレポジトリのコミット履歴を取り込んでいる半強制的な統合なので、通常の「git pull origin main」でのマージとは違います。

コミット履歴を確認してみると、時系列順に取り込まれていることが分かります。

$ git log
commit 9bedb837be10a664ccecfb76de9c1c848dd4304d (HEAD -> main)
Author: User-Name <address@gmail.com>
Date:   Wed Jun 8 08:21:43 2022 +0900

    ローカルからファイルの追加

commit fa5e1b85bdfea5bda2dd583a6dbc7ccbcbbc1a19 (origin/main)
Author: user-name <51676019+user-name@users.noreply.github.com>
Date:   Wed Jun 8 08:20:45 2022 +0900

    リモートからファイルの追加

commit bb29e79cf851ecc8bf6a038be67c89a9f2d2d9d0
Author: user-name <51676019+user-name@users.noreply.github.com>
Date:   Wed Jun 8 08:20:21 2022 +0900

    Initial commit

問題無くブランチ「main」と「master」が統合されたので、そのままブランチ「main」としてリモートレポジトリへプッシュ(送信)します。

$ git push origin main
Username for 'https://github.com': ユーザー名
Password for 'https://user-name@github.com':アクセストークン
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 366 bytes | 18.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/user-name/new-merge.git
   fa5e1b8..9bedb83  main -> main

送信に成功したら、リモートレポジトリのブランチ「master」を削除しておきます。

$ git push origin :master
Username for 'https://github.com': ユーザー名
Password for 'https://user-name@github.com':アクセストークン
To https://github.com/user-name/new-merge.git
 - [deleted]         master

ローカルレポジトリのブランチ「master」も削除します。

$ git branch -D master
Deleted branch master (was 57fb77f).
$ git branch
* main

GitHubのレポジトリを確認してみると、ブランチ「main」に統合されブランチ「master」が削除されているのがわかります。

これでレポジトリを綺麗にまとめることができました。

clone(クローン)によるレポジトリの作成からリモートブランチを統合する

ここではリモートレポジトリのクローンによるリモートブランチの統合です。上記の方で実装したやり方とほぼ一緒で、違いは特定のブランチのレポジトリをクローンしてくる事だけです。

「git clone」コマンドを使って、ローカルで作成したブランチ「master」のレポジトリを取得します。なぜブランチ「main」ではなくブランチ「master」を取得するのかは後ほど説明します。

※特定のレポジトリを取得する場合は「-b ブランチ名」を付けます。

$ git clone -b master https://github.com/user-name/new-merge.git
Cloning into 'new-merge'...
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 9 (delta 0), reused 3 (delta 0), pack-reused 0
Unpacking objects: 100% (9/9), done.
$ ls
new-merge

リモートで作成したレポジトリ名「new-merge」ディレクトリが配置されたので、移動して中身を確認します。

$ ls
local_repo.txt

取得レポジトリはブランチ「master」なので、新しくブランチ「main」を作成してブランチ「main」に切り替えます。

$ git branch
* master
$ git branch main
$ git checkout main
Switched to branch 'main'
$ git branch
* main
  master

ブランチが「main」になったところで、リモート側のブランチ「main」を統合します。

$ git pull --rebase origin main
From https://github.com/user-name/new-merge
 * branch            main       -> FETCH_HEAD
First, rewinding head to replay your work on top of it...
Applying: ローカルからファイルの追加
$ ls
README.md  local_repo.txt  remote_repo.txt

コミット履歴を確認してみます。

$ git log
commit 33517cfd57b09a546179353bb6c218f56ad0877a (HEAD -> main)
Author: User-Name <address@gmail.com>
Date:   Wed Jun 8 12:07:00 2022 +0900

    ローカルからファイルの追加

commit ca061ed8b038c561564efc0ce6cf7482a281c451 (origin/main, origin/HEAD)
Author: user-name <51676019+user-name@users.noreply.github.com>
Date:   Wed Jun 8 12:04:35 2022 +0900

    リモートからファイルの追加

commit 2964d6f9b8b1575f4af02f0d510b84a314c02a3c
Author: user-name <51676019+user-name@users.noreply.github.com>
Date:   Wed Jun 8 12:04:04 2022 +0900

    Initial commit

コミット履歴を見ると、ベースをリモートブランチの「main」としてローカルレポジトリのブランチ「master」を取り込んでいます。このコミット履歴が逆になってしまうとリモートのブランチ「main」との整合性がなくなりプッシュ(送信)できなくなってしまうので、後ほど説明します。

問題無く進めば統合したローカルレポジトリを以下のようにプッシュ(送信)します。

$ git push origin main
Username for 'https://github.com': ユーザ名
Password for 'https://user-name@github.com':アクセストークン
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 366 bytes | 91.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/user-name/new-merge.git
   ca061ed..33517cf  main -> main

リモートのブランチ「master」とローカルレポジトリのブランチ「master」を削除して終了です。

$ git push origin :master
Username for 'https://github.com': ユーザー名
Password for 'https://user-name@github.com':アクセストークン
To https://github.com/user-name/new-merge.git
 - [deleted]         master
$ git branch -D master
Deleted branch master (was 1825ea0).
$ git branch
* main

もしもクローンするリモートレポジトリがブランチ「main」だった場合、ベースとなるのはブランチ「master」なので結果は以下のようなコミット履歴になりました。

$ git clone https://github.com/user-name/new-merge.git
Cloning into 'new-merge'...
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 9 (delta 0), reused 3 (delta 0), pack-reused 0
Unpacking objects: 100% (9/9), done.
$ ls
new-merge
$ cd new-merge
$ ls
README.md  remote_repo.txt
$ git branch
* main
$ git pull --rebase origin master
From https://github.com/user-name/new-merge
 * branch            master     -> FETCH_HEAD
First, rewinding head to replay your work on top of it...
Applying: Initial commit
Applying: リモートからファイルの追加
$ git log
commit 944f9bdee119c9967e794a05fd9ce49eb623d430 (HEAD -> main)
Author: user-name <51676019+user-name@users.noreply.github.com>
Date:   Wed Jun 8 12:04:35 2022 +0900

    リモートからファイルの追加

commit 9f31a4e7519e10b6bb5b778fbc8f045bbdde6a14
Author: user-name <51676019+user-name@users.noreply.github.com>
Date:   Wed Jun 8 12:04:04 2022 +0900

    Initial commit

commit 1825ea0f7a0340636c82b1322c2929a6dd73a406 (origin/master)
Author: User-Name <address@gmail.com>
Date:   Wed Jun 8 12:07:00 2022 +0900

    ローカルからファイルの追加

リモートブランチの「main」側ではローカルレポジトリのコミットが「過去」のものとなってしまうため、プッシュ(送信)は拒絶されてしまいます。

$ git push origin main
Username for 'https://github.com': ユーザー名
Password for 'https://user-name@github.com':アクセストークン
To https://github.com/user-name/new-merge.git
 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to 'https://github.com/user-name/new-merge.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

「git push -f」コマンドで強制的にプッシュすることはできますが、世間体やコミット履歴に準拠されている方たちからすると推奨はされていないようです。

$ git push -f origin main
Username for 'https://github.com': ユーザー名
Password for 'https://user-name@github.com':アクセストークン
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 628 bytes | 157.00 KiB/s, done.
Total 6 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), done.
To https://github.com/user-name/new-merge.git
 + 3f33f9b...c256e25 main -> main (forced update)

とはいえ、チームで強力して開発を進めていく上でも様々な方法で問題を解決する能力は必要だと思うので、失敗を重ねて技術を磨いていきましょう。

それでは以上です。

最後までご覧いただきありがとうございました。