- 投稿日:2020-01-23T20:24:52+09:00
Git公開用に第2のブランチ&リポジトリを作りつつ履歴は極力残すドン!
やりたいこと:おいらもGithubに進出だドン!
いざGithubへ、そんなとき、次のようなこと、悩みませんか
- 個人or社内開発のリポジトリはOnedriveや社内クラウドなどのクローズド環境に置いている(クローズドリポ)。
- 公開用ソースは、それとは別のリポジトリ(Github等)にしたい(パブリックリポ)。
- パブリックリポでは、公開するファイルを削りたい。
- 見せても良い開発履歴は、クローズドリポからパブリックリポへ極力残したい。
ここで、今回の方法では、クローズドリポに紐づいているクローズドブランチと、パブリックリポに紐づいているパブリックブランチがそれぞれ別物としてあって、クローズドブランチで日々の開発を勧めつつ、パブリックブランチに定期的にマージすることにします。
もうだめカッ・・・
さて、上記の目的のために検索すると、
.gitignore
を書き換えて、.gitattributes
にmerge=ours
追加して、という情報が出てきます。が、私の環境ではうまくいかなかった。具体的には、パブリックリポを作った後、クローズドリポで開発を進めて、再度パブリックリポへマージしようとすると、コンフリクトの嵐。原因は、パブリックリポでは一部のファイルもなくなっているし、場合によって履歴のコミットIDが変わっているため。また、
merge=ours
が動作するにはある条件が必要なようで[^1]、動作しないことが結構多い。そうするとパブリックリポ向けの.gitignoreに書いてあろうがmergeによってファイルが取り込まれてしまうことがありました。なんとか目的を達成だドン!
先に例題のコマンド載せます。私の冗長な文章よむよりもコマンド見た方が早いって人も多いと思うんで。クローズドブランチを
master
、パブリックブランチをpub
とします。close.txt
が公開したくないファイルで、pub
ブランチからはそれを除きます。mkdir gittest cd gittest #(1)prepare closed branch "master", and do commit 3 times. git init echo "bigdata" > .gitignore git add .gitignore git commit -a -m "master1" echo "master close2" > close.txt git add close.txt git commit -a -m "master2" echo "master open3" > open.txt echo "master close3" > close.txt git add open.txt git commit -a -m "master3" git log --oneline -n 5 open.txt git log --oneline -n 5 close.txt cat *.txt #(2)make public branch "pub" git branch pub git checkout pub git log --oneline -n 5 open.txt git log --oneline -n 5 close.txt cat *.txt #(3)remove "close.txt", which is unpublic file, on public "pub" branch git filter-branch -f --index-filter "git rm -f --cached --ignore-unmatch close.txt" --prune-empty HEAD echo "close.txt" >> .gitignore git reflog expire --expire=now HEAD git gc --aggressive --prune=now git log --oneline -n 5 open.txt git log --oneline -n 5 close.txt cat *.txt #(4)advance to develop on closed "master" branch git checkout master echo "master open4" > open.txt echo "master close4" > close.txt git commit -a -m "master4" echo "master open5" > open.txt echo "master close5" > close.txt git commit -a -m "master5" git log --oneline -n 5 open.txt git log --oneline -n 5 close.txt cat *.txt #(5)make temporary branch "tmp" git branch tmp git checkout tmp cat *.txt #(6)remove unpublic files on "tmp" branch git filter-branch -f --index-filter "git rm -f --cached --ignore-unmatch close.txt" --prune-empty HEAD git reflog expire --expire=now HEAD git gc --aggressive --prune=now git log --oneline -n 5 open.txt git log --oneline -n 5 close.txt cat *.txt #(7)merge the developed files into public "pub" branch git checkout pub git merge -Xtheirs tmp -m "pub5" git log --oneline -n 5 open.txt git log --oneline -n 5 close.txt cat *.txt #(8)remove temporary branch git branch -d tmpここで、開発が進むたびに(4)から(8)が繰り返されます。
つまるところ、次のことをやっています。
- 公開したくないファイルは
git filter-branch
で履歴からも削除- 二度目以降のマージではコンフリクトを防ぐために、temporaryなbranchで
git filter-branch
してからパブリックブランチにマージするここで、経験則でしかないのですが、同じ履歴を持つブランチに同じ引数でgit filter-branchした場合には、書き換わったコミットIDに再現性がある(同じになる)という性質を使っています。ここで、ある時点まで同じ履歴をもっていれば、片方のブランチがそれ以降の履歴を持っていても、大丈夫です。この経験則を利用して、マージ時に
tmp
ブランチでのgit filter-branch
にて書き換わったコミットIDが、実はpub
ブランチで最初にgit filter-branch
して書き換えたコミットIDと同一になり、マージ可能になります。もしコミットIDが違っちゃうと、同じ履歴とみなされず、
fatal: refusing to merge unrelated histories
エラーが出て、optionで--allow-unrelated-histories
をつけるという話になってきますが、今回はそれも必要ありません。注意だドン!
Windows環境では、PowerShellなら問題ないですが、WSL(Windows10 1903)上で動かすと、ファイル操作が遅いためか時々おかしな動作になります。ファイルが消えて居なかったり。消したはずのファイルが残っていたり。よって、上記コマンドの例題のコマンドも途中で失敗することがあります。特に
git merge
やgit filter-branch
で消したはずのファイルが消えてなかったりとかでErrorになります。ぶっちゃけこれで相当ハマりました。WSL2でこの辺り改善されるといいですね。
おわりに
git mergeマジ卍むずかしい(言ってみたかっただけ)。
殆ど経験則だけで書いているので、ご指摘大歓迎です。
Reference
[^1]StackOverFlowの回答にある"The merge driver is only called in non-trivial cases, i.e. if both master and test have touched setup"や、StackOverFlow-2の回答にある"Git only invokes a three-way merge driver if there are *two diffs to combine."あたりが、どうやら的を射てるのかな。要は、マージドライバーの"ours"が呼ばれるには条件があって、それはマージ元とマージ先の両方のブランチで編集されている必要があると。
- 投稿日:2020-01-23T18:25:32+09:00
Gitでファイルの変更履歴を残したままリポジトリ階層を変更する
はじめに
Gitはパスが変わってしまうと別のファイルとして認識されます。
プロジェクト途中でリポジトリの階層を変更したいが、ファイルの変更記録を追えるようにしたいというような要望があり、
対応した結果を備忘として残します。具体的には、↓のようになっているものを
project-root |-.git |-app1 |-app2↓のようなディレクトリ構成に修正したい。(Rootディレクトリ階層を一つ上げたい。)
project-root |-.git |-src | |-app1 | |-app2手順
0. 現状の確認
$ pwd /path/to/work $ ls -1p project-root/ $ ls -1pa project-root/ ./ ../ app1/ app2/ .git/1. 現在利用しているローカルリポジトリとは別のリポジトリを作成
※仮にここで作成したリポジトリをダミーリポジトリと呼ぶことにします。
$ pwd /path/to/work $ mkdir project-dummy $ cd project-dummy $ git init Initialized empty Git repository in /path/to/work/project-dummy/.git/ $ git commit --allow-empty -m "First commit" [master (root-commit) 6dcc8bb] First commit2. 変更したいリポジトリのブランチから作成したダミーリポジトリの新ブランチに取り込む
変更したいリポジトリの「develop」ブランチの内容をダミーリポジトリの「dummy」ブランチに取り込みます。
$ git fetch ../project-root/.git refs/heads/develop:refs/heads/dummy remote: Counting objects: 1736, done. remote: Compressing objects: 100% (528/528), done. remote: Total 1736 (delta 1142), reused 1736 (delta 1142) Receiving objects: 100% (1736/1736), 240.36 KiB | 0 bytes/s, done. Resolving deltas: 100% (1142/1142), done. From ../project-root/ * [new branch] develop -> dummy3. filter-branch で dummyブランチの中身をごっそり src に移動する
$ git filter-branch -f --tree-filter ' > [ -d src ] || mkdir src; > find . -mindepth 1 -maxdepth 1 ! -path ./src | xargs -i{} mv {} src > ' dummy Rewrite 42d984ecce571e5980dde69cf36fd6c9219cad1e (190/190) Ref 'refs/heads/dummy' was rewritten4. 「dummy」ブランチをダミーリポジトリの「master」ブランチに取り込む
$ git merge --no-ff dummy Merge made by the 'recursive' strategy. src/.gitignore | 122 +++++++++++++++ ~略~ # 念のため、src配下にあるファイルの変更ログを参照できるか確認する。 $ git log src/.gitignore commit 59f33d2b17a6e686b423a0b39e8665f203199ed2 Author: HOGEHOGE Date: Wed Jan 15 10:43:50 2020 +0900 [update] delete .idea ~略~3. ダミーリポジトリの「master」ブランチを変更したいリポジトリの新規ブランチとしてpushする。
$ git push ../project-root/.git master:new-develop Counting objects: 1924, done. Compressing objects: 100% (538/538), done. Writing objects: 100% (1924/1924), 247.80 KiB | 0 bytes/s, done. Total 1924 (delta 1140), reused 1546 (delta 1134) To ../project-root/.git * [new branch] master -> new-develop4. 変更したいリポジトリに移動し、ブランチを「new-develop」が存在し、意図した構成となっていることを確認する
注意 : 変更履歴は最初からproject-root/src配下で作成されていたように修正されている為、これまで使っていたブランチに統合することはできません。masterブランチにcommit/pushされている場合は、一度masterブランチを削除/作成し、new-developをmergeすることになるはずです。
$ cd ../project-root/ $ pwd /path/to/work/project-root $ git branch * develop new-develop # new-developブランチが作成されている $ git checkout new-develop # ブランチを切り替え Switched to branch 'new-develop' $ ls -1ap # project-root配下にsrcと.gitディレクトリが存在している ./ ../ .git/ src/ $ ls -1 src # project-root/src配下にapp1,app2が移動している app1 app2まとめ
無事↓のようなディレクトリ構成に修正できました。
project-root |-.git |-src | |-app1 | |-app2確認後、project-dummyディレクトリや、developブランチを削除等のゴミ掃除をして作業完了です。
最後に、履歴を書き換えることになるので、場合によってはリポジトリが壊れる可能性があります。
バックアップは確実に取得したうえで参考にされてください。
- 投稿日:2020-01-23T15:06:50+09:00
gitコマンド 覚書
- 投稿日:2020-01-23T09:26:13+09:00
[卒論・修論を守れ!]Cloud LaTeXとGitの連携[まだ間に合う!]
2020年1月23日改稿
Cloud LaTeX 便利ですよね。テキストエディタにこだわりがなく、論文を書くPCは気分で決めたい僕はとても重宝しています。決してtexの環境を整えるのが面倒くさいというわけではないです。
しかし、Cloud LaTeXの恐ろしいところは、一度消してしまったファイルはすぐには復元できないことです。
俺みたいな卒論のtexファイル消して泣いたアホ野郎、他に、いますかっていねーか、はは(運営の方に連絡すれば復旧してもらえるらしいですが)
また、ファイルごとの更新履歴は残りますが、差分が見れないのは厳しいところです。そこで、Cloud LaTeX標準のDropbox連携機能を応用し、Gitを導入することでバージョン管理ができるようにします。
大事なこと
データはクラウドに保存されるため、PCが水没した!データ復旧できない!というような問題からは解放されます。
しかし、texのコンパイルをクラウドサービスのみに頼るのは危険です。本稿の内容を行うことで、ドロップボックスを介してローカルにもファイルは保存されるようになりますが、卒論・修論提出寸前にCloud LaTeXが落ちてtexファイルがコンパイルできなくなる、ということがあるかもしれません。
そのため、必ずローカルでtexをコンパイルできる環境、または24時間いつでもtexをコンパイルしてくれる友達(通称tex友)を用意しておきましょう。前提
PCにDropboxのアプリをインストールし、Dropboxフォルダとしてアクセスできるようにしてください。
(※Dropbox ダウンロードなどで検索してください。Windows Mac Linuxのいずれでもできます。)また、gitのインストールも済んでいるものとします。
git連携のための手順
Cloud LaTeXにはDropboxからインポートという機能があります。また、Dropboxには自動でローカルのファイルを更新してくれます。これらをうまくつなぎ合わせてgitで管理していきます。
- Cloud LaTeXマイページ右上のユーザ名をクリック、Dropboxとの連携設定で、PCに設定したアカウントと同じものを連携
- Cloud LaTeX上でプロジェクトを作成
- 右上のプロジェクト設定(三のようなマーク)からDropbox同期をonにする
- Dropboxのフォルダに(Dropbox)/アプリ/Cloud LaTeX/(プロジェクト名)というプロジェクトフォルダができていることを確認
- (Dropbox)/アプリ/Cloud LaTeX/ でgit initし、(プロジェクト名)ファイルをgitに追加
(※注 (Dropbox)/アプリ/Cloud LaTeX/(プロジェクト名)フォルダでgitを管理したいところなのですが、この中に.gitファイルが存在するとcloud latex側でおかしくなるため、(Dropbox)/アプリ/Cloud LaTeX/に.gitを置いて管理するようにします。
)IDE派の方もいらっしゃるかもしれませんが、一応gitコマンドを置いておきます。()内は各自のフォルダ名で読み替えてください。
cd (Dropbox)/アプリ/Cloud LaTeX/ git init git add (プロジェクト名) git commit -m "first init"以上でGitとの連携が可能になります。
以後は、こまめにgit add & git commitを繰り返しましょう。さらにGithubとの連携も行ってリモート管理も行いたい場合はこちらが参考になると思います。
参考:https://qiita.com/sodaihirai/items/caf8d39d314fa53db4db問題点
プロジェクトファイルの一つ上の階層でgit管理しているため、複数プロジェクトを別々のgitで管理できません(ダメでは)
ま、まあ卒論とか修論とか大事なやつをちゃんと守ろうという話ですね...
ちなみにちょっと前までのCloud latexだったらプロジェクトフォルダの中に.gitを入れることができたので、プロジェクトごとのgit管理が可能でした。なんでできなくなったんだろう...さいごに
texの環境を整えるのが面倒だから使っていたのに余計な面倒を抱え込んでしまいました。(でもやっぱりいろんなPCで同じ環境で論文書けるのは楽)