GITを使うフォークの管理

Date: 2020/04/02 (initial publish), 2022/02/24 (last update)

Source: jp/note-00021.md

Previous Post Top Next Post

TOC

Gitを使う際に、アップストリームのレポの更新に合わせて、 自分がフォークしたレポを如何にスムーズに更新していくかは、 フォークしたレポの開始方法やブランチ名の設定や実行するコマンド 手順に複数のアプローチや複数の対応方法があるだけに、いつも 混乱して悩まされます。

備忘録を兼ねて、アップストリームがGITHUBを使っている 場合を軸にメモを作成します。

世の流れで、デフォルトブランチ名に「master」ではなく「main」等を使うことが最近増えています。その場合は適宜以下も読み替えてください。

GITを使うフォークの管理手順(基本)

簡単のために以下の前提を置きます。(追跡=tracking)

この際の、GITを使うフォークの管理手順は以下です。

  1. アップストリームのレポのGITHUBウエッブページで「Fork」をクリックして、 自分のアカウントにフォークされたリモートのレポを作成

  2. フォークされたレポをローカルに「git clone」して、ローカルのレポを作成

    • $ git clone git@github.com:my_name/project_name.git ; cd project_name
  3. 自分が書き換える開発ブランチ(ここでは「devel」)を作成

    • $ git checkout -b devel
  4. ファイルを編集し、開発ブランチにコミット

  5. アップストリームのリモートのレポをローカルにリモート「upstream」として追加。

    • $ git remote add upstream https://github.com/upstream_name/project_name.git
  6. フォークされたレポの「master」ブランチをアップストリームのレポの「master」ブランチに同期

    • $ git checkout master ; git fetch -p upstream ; git pull upstream master ; git push origin master

    -p--pruneのショートハンドで、不要なリモート追跡ブランチを削除してくれます。

  7. 開発ブランチに戻り、開発再開

    • $ git checkout devel
  8. 状況に合わせて、merge/rebase等をする。(rebaseしたブランチのpushは要注意)

    • $ git merge master
    • $ git rebase -i master

これは QMKプロジェクト 等で推奨されるフォーク手順です。

GITブランチ操作のヒント集

GITのブランチの作成・ブランチ間の移動・ブランチのチェックアウト等の 多くの操作は、gitk --allをした後、右マウスクリックで表示される GUIメニューから出来ます。

ただリモートレポURLの登録・変更や、リモートのレポ上のブランチの作成・設定変更 等にはコマンドライン入力操作が必要です。

-u--set-upstreamのショートハンド

(-m--moveのショートハンド)

GITブランチ関連操作の応用例

作業中のブランチ履歴の非公開保存

作業中履歴をそのままリモートの公開共有レポに残すと、履歴が錯綜し 他の人に迷惑で、初歩的ミスが晒され格好も悪いし、コード変更の レビューも困難にします。

作業中履歴はローカルへのコミットだけにして、git rebase操作で 綺麗に整理されたあとの履歴をリモートの公開共有レポにアップする のが普通かと理解しています。ただこれだと、何らかの事故で作業途中の ローカルデーターを失うと、全ての直近の作業結果が無くなってしま います。もちろんローカルシステムのバックアップが良いタイミングで リモートにされていれば良いのですが、それを保証するのは難しい面が あります。またHDDやSSDというものはいつ死んでも不思議はない ので、同一デバイス上のスナップショット記録等は当てにすべきでは ありません。

こんな場合には、共有のmasterにコミットする代わりに作業用のブランチ として例えばwipを作成し作業中履歴をそこにコミットしgit push する回避策等が考えられます。ただそれでも他人に見られるので格好が 良くないことは否めません。

またDebianのパッケージング操作で、gbp ...等を使用する場合ツール インフラ上ブランチ名を作業用のブランチ名に変えるのを避けたい事も あります。

ブランチ名を変えたくないが作業中履歴を簡単に保存したい時には、 リモートを変更する策があります。例えば、USBメモリー等に作った 非公開レポをリモートに設定します。

$ cd /path/to
$ git clone https://some_site/repo_name.git
$ cd repo_name
$ git remote set-url --push origin DISABLED_FOR_PUSH
$ mkdir -p /media/usbmemory/repo_name
$ cd /media/usbmemory/repo_name
$ git init --bare
$ cd /path/to/repo_name
$ git remote add usbmemory /media/usbmemory/repo_name
$ git push --all -u usbmemory

ここまですれば、作業中履歴がそのまま非公開レポにも記録され るようになっています。ここでは、うっかり公開レポにgit pushしな いように、git remote set-url ...操作で安全錠を掛けています。

... hack source
$ git push --all
$ git fetch -p origin
$ git rebase -i origin/master
... gack more
$ git push --all

上記のように作業を進め、要点毎に非公開レポにもgit push記録します。 さらに念のため公開レポのmasterの最新内容にgit rebaseして同期して いきます。作業結果が満足となった時点で、安全錠を外して公開レポに綺麗な 形でgit pushします。

$ git remote set-url --delete --push origin ""
$ git push -u origin master
...

ここで、合計2回行ったgit remote set-url ...操作は間違ってリモートの 公開共有レポにアップするのを防ぐオプショナルな操作で必須ではありませ ん。ただ今回使ったgit version 2.26.2では--delete操作が少々 トリッキーでした。ここは、単純に.git/configファイルのpushurl設定行 をエディターで直接消す方が確実な気がしました。

非公開レポはUSBメモリーではなく、sshgitが使えるサーバーに作って も良いでしょう。

Debianのパッケージング操作の場合

アップストリームのブランチ名とローカルのブランチ名を変えるのは、 技術的にはできますが、我々の正気を保つため基本的に避けたい状況です。

Debianのパッケージング操作等では、パッケージング操作用レポ上の「upstream」 と「master」というブランチ名を普通デフォルトでパッケージング特定用途に 用いています。実際、アップストリームのGITのmasterブランチの リリースをベースによく自動生成されたファイルを追加して作成されたTARBALL をベースにパッケージング操作用レポ上の「upstream」中にコミット作成、 パッケージングデーターを追加してパッケージング操作用レポ上の「master」 中にコミット作成しするのがデフォルトです。両方のレポをトラッキングしよう とするとアップストリームとパッケージングのブランチ名「master」がかち合う のでこれを避ける工夫が必要です。git-buildpackageを使う場合には、 debian/gbp.confを用いて、パッケージング操作用レポ上にある パッケージングデーターを追加して結果がコミットされるブランチ名を「debian」 等という別のブランチ名を用いるようにするのが良いようです。dgitを使う場合 には、マニュアルででもdebian/gbp.confを用いるとともに、git-configを 使い調整することに触れられています。

上記の正攻法が、何らかの理由でうまくいかない際には、避けたいといったアプローチ ですが、アップストリームの「master」ブランチを追跡するローカルブランチの名前を 「master」以外の別の名前から選ぶことで逃げる手があります。

例えばフォークされたレポでは「upstream-master」を追跡に用いるブランチの名前 とします。「upstream-master」ブランチのコンテントをアップストリームの 「master」ブランチのコンテントに同期させる操作は次のようになります。

$ git clone https://upstream_site/repo_name.git
$ cd repo_name
$ git branch upstream-master
$ git remote rename origin upstream
$ git remote set-url --push upstream DISABLED_FOR_PUSH
$ git remote add origin git@salsa.debian.org:myname/repo_name.git
$ git push -u origin
 ...
$ git checkout upstream-master
$ git fetch -p upstream
$ git pull upstream master
$ git push origin upstream-master
$ git checkout master
...

上記でローカルの「upstream-master」ブランチが、アップストリームの リモートの「master」ブランチ(gitkが「remotes/upstream/master」 と表示)を追跡するようになります。

また、ローカルの「upstream-master」ブランチは、自分のリモートの 「upstream-master」ブランチ(gitkが「remotes/origin/upstream-master」 と表示)にpushされ保存されます。

push対象のローカルの「upstream-master」ブランチが、現在Checkoutしている デフォルトブランチで、push先のリモートでもブランチ名が同じ 「upstream-master」だから、pushの際のコマンドラインへの入力文字列 をかなり省略できるので短くて済んでます。

上記の後半の操作を繰り返すことで、ローカルの「upstream-master」が、 常にアップストリームが更新するアップストリームのリモートの「master」 ブランチ(gitkが「remotes/upstream/master」と表示)を追跡していけます。

上記のようにDebianのパッケージングの際に用いるGitレポではupstream ブランチはアップストリームがリリースしたtarballを展開した内容を コミットして作ることが原則です。一方リリースされるtarballは単純に アップストリームのGitレポそのままでないことがよくあります。こんな 場合はアップストリームのGitレポのヒストリーとうまく繋がりません。 そんな場合には、「upstream-master」をパッケージング用レポの 「master」上に作ることはできません。

こんな場合でもアップストリームとパッケージングのレポからの チェックアウトを共用ローカルディレクトリーにすると、gitkupstream/masterにある最新のアップストリーム変更からパッチを cherry-pickして使えるので便利です。そのためだけなら、ローカルの upstream-masterブランチの作成や更新にあたる作業をスキップする のも手です。例えば、過去のパッケージ履歴を含むパッケージ用のGit レポを、gbp import-descs ...等で作成し、更に git remote add upstream upstream_url(このupstreamはremotes名) してリモート名を登録、ここで単純にgit fetch -p upstreamすること までにし、アップストリームのレポ中のmasterブランチのみをfetch すれば、ローカルのブランチ名との競合を気にせずgitk --allで アクセスできるアップストリームのレポ履歴がローカルに落とせます。 履歴は繋がってませんがcherry-pickするだけならこれで充分です。

こうしてできた履歴に、gitk --allでアクセスしてローカルブランチ名を 後からにupstream-master等とGUIで設定すれば、ローカルチェックアウトも できます。まあ、ローカルとリモートでブランチ名が変わるのが嫌ですが。

どこにも繋がらない履歴のブランチ作成

gbpのような、どこにも繋がらない履歴のブランチ作成はちょっとトリッキーなので、ここに2ケースの例をメモしておきます。

新規のREADME.mdがコミットされたどこにも繋がらない履歴を、ブランチ名orphan_branch_nameで新規作成するのは以下です。

$ git checkout --orphan orphan_branch_name
$ git rm -rf .
$ editor README.md
 ....
$ git add README.md
$ git commit -m 'Initial commit'

別のリモートのREPO(another)にあるブランチ(例えばmaster)の内容のどこにも繋がらない履歴を、既存のローカルREPO内にブランチ名another-masterで作成するのは以下です。

$ git remote add another http://another.example.org/another
$ git fetch another master:another-master

トラッキング

ブランチがトラッキングするリモートレポの設定は、上記のように最初に プッシュする際にgit push -u ...とするのも手ですが、直接変更するのは git branch -u ...です。

git remoteの際にLONGオプションを用いると話がややこしくなるので要注意です。

完全なリセット(gitattributes設定とautocrlf関連)

当方では改行関係に変な事が起こらないようにしています。

 $ git config --global core.autocrlf false

他人のレポで、.gitattributes設定でeol=lfされていて、折角の上記設定が 効かないときに、どいうわけがCR入りでファイルがコミットをされていて、その コミットにgitkでブランチをリセットしようとしたら勝手にCRを外した ローカルファイルに変更され困りました。

以下で、問題解決しました。

 $ git rm --cached -r .
 $ git reset --hard
 $ git clean -d -f -x

GITHUB/GITLAB

GITHUBとGITLABは、ともに類似の無償のGITレポのホスティングを提供している 非常にポピュラーなWEBサービスです。ここではGITHUBとGITLABの サービスの細かな違いワークフロー流儀の細かな違い には深入りしません。

簡単に特徴をまとめると、GITHUBは先発のクローズドソフトプラットフォーム でユーザー数が多いのが特徴で、GITLABは後発ですが基本のソフトプラット フォームがオープンなFLOSS(Free and open-source software)として開放され ていて、Debian等のFLOSSプロジェクトが自らのソースコードWEBサービスの ホスティングに採用してているのが特徴です。

「Debianのパッケージング操作」で行ったGITHUBやGITLABのウエッブページ を使わずにフォークしたレポを、git clone ...経由でDebianのサーバー上 に作成した操作手法は、コマンドライン環境だけでアップストリームと交流 することが前提です。

GITHUBやGITLAB上のウエッブサービスを利用する場合には状況によっては 別の配慮が必要です。例えばGITHUBやGITLAB内のレポ間フォークの場合、 GITHUBやGITLAB上でウエッブページを使いフォークしないとPULL request等 のウエッブ連携操作に支障がでる恐れがあります。

デフォールトのブランチ名

ブランチ名を変えずにフォークしたレポの「master」は、アップストリーム の「master」を同じ内容なので、オープニングのページで表示される README.MDがアップストリームを同じ内容になります。これでは折角のフォークが 隠れてしまいます。これは、ログインしてGITHUBやGITLABの設定に入り、 Default branch(デフォルトのブランチ)をもとの「master」ではない、 フォークした内容がコミットされたブランチに設定することで解決します。

自由なブランチ名の設定

特段の理由なくローカルやリモートでブランチ名が変わる複雑な設定をする のは無用な混乱を起こすのでできるだけ避けるべきです。以下はあくまで 参考情報です。

この様なことをすると、push を便利なcurrentに設定できなくなり、 同名のブランチにpushするにも必ずgit pushコマンドラインの最後引数 を明示する必要が出ます。

さて本題です。

例えばgitkで見つけた既存のリモートブランチ(例えばgitkが 「remotes/upstream/foo」と表示するブランチ)のある最終コミットに GUIでローカルのブランチ(例えば「bar」)を新規追加した場合を考え ましょう。

この「bar」ブランチを、自分が公開しているoriginという リモートレポに、他人からは「baz」ブランチと呼ばれることと なるブランチとして、(別の見方では自分のgitkで「remotes/origin/baz」 と表示されるブランチとして)、最初pushするには以下の操作をします。

$ git checkout bar
$ git push -u origin bar:baz

一度この操作をすると、.git/comfigファイルにこの設定が記録され ます。この設定はgit comfigコマンドで調整できますし、また エディターで.git/comfigファイルを直接編集しても調整できます。

さらにこの状況は以下のようにして確認できます。

$ git config --list
...
branch.bar.remote=origin
branch.bar.merge=refs/heads/baz

こう設定した後、「bar」ブランチをチェックアウトして、ファイル を変更しローカルにコミット後、アップストリームのGITレポからの変更 を取り込むには以下とします。(設定していれば--rebaseは不要。)

$ git pull --rebase origin

ローカルのコミットは最新アップストリームにrebaseされます。

2回目以降のpushでは、必ずブランチ名の明示指定が必要です。(一方、今回は-u不要) (push.defaultupstreamに設定していれば別ですが、、、)

$ git checkout bar
... hack
$ git push origin bar:baz

ややこしいですが、「bar」ブランチの最新コミットがgitkで 「remotes/origin/baz」と表示されるoriginというリモートレポ上 のリモートブランチ「baz」にpushされます。

参考Cookbookサイト

Previous Post Top Next Post