0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

GitAdvent Calendar 2023

Day 4

【Git】リポジトリに接続できない状態で非リアルタイムに同期をとろうとした話(git format-patch)

Last updated at Posted at 2023-12-03

はじめに

Git Advent Calendar 2023 4日目の投稿です。よろしくおねがいします。
Gitではベアリポジトリと通信するために、いくつかのプロトコルをサポートしてます(ここ)。
が、この前、↓の状況になりました。
無題.png

他社の環境(ここでは外とも言います)にベアリポジトリがあって自社(ここでは自分とも言います)からはリポジトリを操作、確認できない状況です。
この際に、他社のベアリポジトリで行われるコミットを追従して自社のリポジトリへ非リアルタイムで同期をとろうとしてgit format-patchを使おうとしたので備忘録的にメモしておきます。

無理が分かって結局やめてます(できないことはなさそうです)。

やろうとしたこと概要

1.外のリポジトリをとりあえず再現する。

とりあえず全量リポジトリを送ってもらいます。この時点でダルい:innocent:

2.外で作業が行われる。

外でリポジトリに対して作業がしこしこ行われる。

3.外でパッチの生成をしてもらう。

git format-patchを使って指定された各コミットに対して1つずつパッチを生成する。

4.自分ちにパッチを取り込む。

git amでパッチを取り込む。

実際にやってみる

※読むのが面倒で結論を知りたい方は問題点まで飛んじゃっても読めると思います。

とりあえず外にあるベアリポジトリとして、以下のリポジトリを用意しました。
これが自分らの環境からはgit clonegit pullもできないリポジトリとします。

~/Desktop/git/remote (master)
$ git log --oneline
30c185d (HEAD -> master) third commit
46072e4 second commit
c44c928 first commit

1.に沿って、このリポジトリを物理コピーしてローカルに再現したとします。

~/Desktop/git/自分側の環境/remote (master)
$ git log --oneline
30c185d (HEAD -> master) third commit
46072e4 second commit
c44c928 first commit

ここで2.として外のリポジトリで何かしら作業が行われたとします。c8e94c6のコミットを増やしました。

~/Desktop/git/remote (master)
$ git log --oneline
c8e94c6 (HEAD -> master) fourth commit
30c185d third commit
46072e4 second commit
c44c928 first commit

3.として、外側のリポジトリでパッチを生成します。
パッチの生成はgit format-patch -nです。

~/Desktop/git/remote (master)
$ git format-patch -1
0001-fourth-commit.patch

パッチファイルがリポジトリの直下にできました。

 ~/Desktop/git/remote (master)
$ ls
0001-fourth-commit.patch  sample.txt

4.として、このパッチを自分のリポジトリにあてます。
とりあえず自分のリポジトリの直下にファイルを持ってきました。

~/Desktop/git/自分側の環境/remote (master)
$ ls
0001-fourth-commit.patch  sample.txt

パッチをあてるにはgit amです。

~/Desktop/git/自分側の環境/remote (master)
$ git am 0001-fourth-commit.patch
Applying: fourth commit

ログを見てみると自分のリポジトリが外のリポジトリと同期が取れたことが分かります。

~/Desktop/git/自分側の環境/remote (master)
$ git log --oneline
5501a99 (HEAD -> master) fourth commit
30c185d third commit
46072e4 second commit
c44c928 first commit

複数コミットがあったらどうなるか

今、外側のリポジトリで2つコミットを追加しました(bd8b31ae993c7d)。

~/Desktop/git/remote (master)
$ git log --oneline
e993c7d (HEAD -> master) 6 commit
bd8b31a 5 commit
c8e94c6 fourth commit
30c185d third commit
46072e4 second commit
c44c928 first commit

外側のリポジトリでパッチを生成します。

~/Desktop/git/remote (master)
$ git format-patch -2
0001-5-commit.patch
0002-6-commit.patch

これを自分のリポジトリに持ってきます。

~/Desktop/git/自分側の環境/remote (master)
$ ls
0001-5-commit.patch  0002-6-commit.patch  sample.txt

もってきたパッチを自分のリポジトリに取り込むためにgit amしてみます。

~/Desktop/git/自分側の環境/remote (master)
$ git am *
Applying: 5 commit
Applying: 6 commit
Patch is empty.
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

~/Desktop/git/自分側の環境/remote (master|AM 3/3)
$ git am --skip

git logして再現できたかどうか確認してみます。

~/Desktop/git/自分側の環境/remote (master)
$ git log --oneline
cf9face (HEAD -> master) 6 commit
d485670 5 commit
5501a99 fourth commit
30c185d third commit
46072e4 second commit
c44c928 first commit

コミットメッセージが同じなので無事再現できることを確認できました。
ブランチが1本の時はコミットが複数でも問題なく再現できそうです。

コミットツリーが複雑だとどうなるか

もう少しコミットツリーを複雑にしてみました。

~/Desktop/git/remote (master)
$ git log --oneline --graph --all
* 8c6827d (HEAD -> master) master3 commit
*   6a7990b Merge branch 'another'
|\
| * 7c84e0a (another) another2 commit
| * 56f65c3 another1 commit
* | 5f935ea master2 commit
* | 56dae81 master1 commit
|/
* e993c7d 6 commit
* bd8b31a 5 commit
* c8e94c6 fourth commit
* 30c185d third commit
* 46072e4 second commit
* c44c928 first commit

今、自分のリポジトリではe993c7dまで再現できている状態です。

~/Desktop/git/自分側の環境/remote (master)
$ git log --oneline --graph --all
* cf9face (HEAD -> master) 6 commit
* d485670 5 commit
* 5501a99 fourth commit
* 30c185d third commit
* 46072e4 second commit
* c44c928 first commit

とりあえずe993c7d以降のパッチを生成してみます。

~/Desktop/git/remote (master)
$ git format-patch master~4
0001-another1-commit.patch
0002-another2-commit.patch
0003-master1-commit.patch
0004-master2-commit.patch
0005-master3-commit.patch

パッチを自分のリポジトリに持ってきました。

~/Desktop/git/自分側の環境/remote (master)
$ ls
0001-another1-commit.patch  0003-master1-commit.patch  0005-master3-commit.patch
0002-another2-commit.patch  0004-master2-commit.patch  sample.txt

このパッチをよしなに適用してくれるのか、git amで自分のリポジトリに適用してみます。

~/Desktop/git/自分側の環境/remote (master)
$ git am *
error: patch failed: sample.txt:1
error: sample.txt: patch does not apply
hint: Use 'git am --show-current-patch' to see the failed patch
Applying: another1 commit
Applying: another2 commit
Applying: master1 commit
Patch failed at 0003 master1 commit
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

エラーがおこりました。
↓ログを見てみると、どうやら自動的にanotherブランチは作ってくれず、masterブランチにanotherブランチで行ったコミットを適用しているようです。これは外側のリポジトリの状況と違いますね。

~/Desktop/git/自分側の環境/remote (master|AM 3/6)
$ git log --oneline --graph --all
* 572a316 (HEAD -> master) another2 commit
* bc8c0e6 another1 commit
* cf9face 6 commit
* d485670 5 commit
* 5501a99 fourth commit
* 30c185d third commit
* 46072e4 second commit
* c44c928 first commit

もう少し調査してみます。
いまmasterブランチにanotherブランチのコミットを付け足してますね。
間違ってくっつけた最新コミットの↓572a316のファイルの状況です。「hogeanother2」と書いてあります。

~/Desktop/git/自分側の環境/remote (master|AM 3/6)
$ cat sample.txt
hogeanother2

適用しようとしているパッチファイルの中身をのぞいてみます。
適用しようとしているパッチの情報は「.git/rebase-apply/patch」ファイルにあります。

~/Desktop/git/自分側の環境/remote (master|AM 3/6)
$ cat .git/rebase-apply/patch
---
 sample.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sample.txt b/sample.txt
index 21f1c7e..862d3a0 100644
--- a/sample.txt
+++ b/sample.txt
@@ -1 +1 @@
-hogehoge6
+hogemaster1
--

パッチで適用しようとしているのは「hogehoge6」と書いてあるファイルに対して「hogemaster1」へ修正する内容なので、エラーが起こっています。
パッチの生成元のブランチの特性を知ることなしにパッチを適用することは難し(不可能?)そうです。

なんか手間がかかりそうですが、なんとか適用したいです。

そもそも今の状況だと適用が失敗しているので、とりあえずもとに戻します。

~/Desktop/git/自分側の環境/remote (master|AM 3/6)
$ git am --abort

~/Desktop/git/自分側の環境/remote (master)
$ git log --oneline --graph
* cf9face (HEAD -> master) 6 commit
* d485670 5 commit
* 5501a99 fourth commit
* 30c185d third commit
* 46072e4 second commit
* c44c928 first commit

再現したいコミットツリーはこうでした。

 ~/Desktop/git/remote (master)
$ git log --oneline --graph --all
* 8c6827d (HEAD -> master) master3 commit
*   6a7990b Merge branch 'another'
|\
| * 7c84e0a (another) another2 commit
| * 56f65c3 another1 commit
* | 5f935ea master2 commit
* | 56dae81 master1 commit
|/
* e993c7d 6 commit
...

それぞれのパッチの内容はこうです。

  • 0001-another1-commit.patch:e993c7d56f65c3の差分
  • 0002-another2-commit.patch:56f65c37c84e0aの差分
  • 0003-master1-commit.patch:e993c7d56dae81の差分
  • 0004-master2-commit.patch:56dae815f935eaの差分
  • 0005-master3-commit.patch:5f935ea8c6827dの差分
    ※パッチにはマージコミットに関する情報がないですね

まずはマージ前のmasterブランチの内容を適用していきましょう。

 ~/Desktop/git/自分側の環境/remote (master)
$ git am 0003-master1-commit.patch
Applying: master1 commit

~/Desktop/git/自分側の環境/remote (master)
$ git am 0004-master2-commit.patch
Applying: master2 commit

~/Desktop/git/自分側の環境/remote (master)
$ git log --oneline
46259c7 (HEAD -> master) master2 commit
d560904 master1 commit
cf9face 6 commit
d485670 5 commit
5501a99 fourth commit
30c185d third commit
46072e4 second commit
c44c928 first commit

git format-patchのパッチファイルではブランチを再現できないので、ブランチを作成します。
外のリポジトリではe993c7d(自分のリポジトリだとcf9face)からanotherブランチが派生してるので、対応する自分のリポジトリのコミットからブランチを作ります。

~/Desktop/git/自分側の環境/remote (master)
$ git checkout -b another cf9face
Switched to a new branch 'another'

次に、自分側で作ったanotherブランチに対して、外側のリポジトリのanotherブランチのパッチを適用していきます。

~/Desktop/git/自分側の環境/remote (another)
$ git am 0001-another1-commit.patch
Applying: another1 commit

~/Desktop/git/自分側の環境/remote (another)
$ git am 0002-another2-commit.patch
Applying: another2 commit

ここまで再現できました。

~/Desktop/git/自分側の環境/remote (master)
$ git log --oneline --graph --all
* 01c0879 (another) another2 commit
* 3a6d509 another1 commit
| * 46259c7 (HEAD -> master) master2 commit
| * d560904 master1 commit
|/
* cf9face 6 commit
* d485670 5 commit
* 5501a99 fourth commit
* 30c185d third commit
* 46072e4 second commit
* c44c928 first commit

外のリポジトリではanothermasterをマージしているので、自分の方でもマージしてあげます。
マージがどうなったのか確認するために、パッチファイルを確認します。

~/Desktop/git/自分側の環境/remote (master)
$ cat 0005-master3-commit.patch
From 8c6827dd19e21b57657676fe92cd1a64cd7d4c26 Mon Sep 17 00:00:00 2001
From: hogehoge <fugafuga>
Date: Tue, 14 Nov 2023 09:36:10 +0900
Subject: [PATCH 5/5] master3 commit

---
 sample.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sample.txt b/sample.txt
index cdfa144..352dcc4 100644
--- a/sample.txt
+++ b/sample.txt
@@ -1 +1 @@
-hogemaster2
+hogemaster3
--

マージ後は「hogemaster2」になってますね。
手動でマージして外のリポジトリと同じように修正して競合を解消してあげます。

~/Desktop/git/自分側の環境/remote (master)
$ git merge another --no-ff
Auto-merging sample.txt
CONFLICT (content): Merge conflict in sample.txt
Automatic merge failed; fix conflicts and then commit the result.

~/Desktop/git/自分側の環境/remote (master|MERGING)
$ cat sample.txt
<<<<<<< HEAD
hogemaster2
=======
hogeanother2
>>>>>>> another

~/Desktop/git/自分側の環境/remote (master|MERGING)
$ echo "hogemaster2" > sample.txt

~/Desktop/git/自分側の環境/remote (master|MERGING)
$ git add sample.txt

 ~/Desktop/git/自分側の環境/remote (master|MERGING)
$ git commit
[master 629622a] Merge branch 'another'

最後にマージコミットの後のパッチをあててあげます。

~/Desktop/git/自分側の環境/remote (master)
$ git am 0005-master3-commit.patch
Applying: master3 commit

再現できました。

~/Desktop/git/自分側の環境/remote (master)
$ git log --oneline --graph --all
*   23148e2 (HEAD -> master) master3 commit
*   629622a Merge branch 'another'
|\
| * 01c0879 (another) another2 commit
| * 3a6d509 another1 commit
* | 46259c7 master2 commit
* | d560904 master1 commit
|/
* cf9face 6 commit
* d485670 5 commit
* 5501a99 fourth commit
* 30c185d third commit
* 46072e4 second commit
* c44c928 first commit

ブランチが複雑な場合のやり方をまとめると、

  1. とりあえずブランチ全部のログと自分のリポジトリにないパッチをもらう
  2. 自分のリポジトリに足りないブランチを手動で作成する
  3. パッチファイルの中身とログからどこのコミットに何のパッチをあてるか整理する
  4. 各ブランチにパッチをあてる
  5. 外のリポジトリでマージされてるときは手動でマージして競合があれば解決してあげる

正直やってられるか、って感じです。

一応問題点を整理

  • ブランチ1本であれば問題なく再現できますが、ブランチが複数できた際にパッチではブランチを作成してくれないので自分で外側のリポジトリの状況を確認してブランチを作成したり競合解決するのがめちゃくちゃ手間です。
  • あと、実際に使用する際は、外のリモートリポジトリ保持者にgit format-patch コミット範囲でパッチを取得してもらって自分に送ってもらう必要があるのでそこもめんどくさいです。
  • もはやgit format-patchが今回の用途に合ってないことが分かった後に言うのもアレですが、1.の手順でリポジトリを再現する方法として物理コピーしてますが、プロジェクトが大きくなった後にやろうとすると、コピーもめんどくさいです。

上のような問題点があり結局git format-patchを使いませんでした。
そもそもコマンドの目的が違うんでしょうね。勉強になりましたとさ。

  • ドキュメント

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?