20200123のGitに関する記事は4件です。

Git公開用に第2のブランチ&リポジトリを作りつつ履歴は極力残すドン!

やりたいこと:おいらもGithubに進出だドン!

いざGithubへ、そんなとき、次のようなこと、悩みませんか

  • 個人or社内開発のリポジトリはOnedriveや社内クラウドなどのクローズド環境に置いている(クローズドリポ)。
  • 公開用ソースは、それとは別のリポジトリ(Github等)にしたい(パブリックリポ)。
  • パブリックリポでは、公開するファイルを削りたい。
  • 見せても良い開発履歴は、クローズドリポからパブリックリポへ極力残したい。

ここで、今回の方法では、クローズドリポに紐づいているクローズドブランチと、パブリックリポに紐づいているパブリックブランチがそれぞれ別物としてあって、クローズドブランチで日々の開発を勧めつつ、パブリックブランチに定期的にマージすることにします。

もうだめカッ・・・

さて、上記の目的のために検索すると、.gitignoreを書き換えて、.gitattributesmerge=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 mergegit 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"が呼ばれるには条件があって、それはマージ元とマージ先の両方のブランチで編集されている必要があると。

  1. git filter-branchで過去の全てのcommitから画像ファイルの追加/変更をなかったことにしてリポジトリを軽量化する
  2. Git ファイルの履歴を完全に削除する
  3. ブランチごとに特定ファイルの内容を変える
  4. Gitでブランチをリモートに送る時の注意点
  5. gitで空ブランチを作る
  6. git merge 時に内容が異なるファイルをマージ対象から除外する
  7. git の merge, rebase, revert で衝突した際の ours, theirs はどんな状態になるか
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 commit

2. 変更したいリポジトリのブランチから作成したダミーリポジトリの新ブランチに取り込む

変更したいリポジトリの「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    -> dummy

3. 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 rewritten

4. 「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-develop

4. 変更したいリポジトリに移動し、ブランチを「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ブランチを削除等のゴミ掃除をして作業完了です。

最後に、履歴を書き換えることになるので、場合によってはリポジトリが壊れる可能性があります。
バックアップは確実に取得したうえで参考にされてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

gitコマンド 覚書

この記事の概要

  • gitコマンドでパッと打てなかったものの覚書
  • 随時更新

Commit

空コミット

git commit -m "empty" --allow-empty

Tag

リモートTag削除

一件削除

git tag -d <tag_name>
git push origin :<tag_name>

一括削除

-- リモート
git tag | xargs -I{} git push origin :{}

-- ローカル
git tag | xargs -I{} git tag -d {}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[卒論・修論を守れ!]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で管理していきます。

  1. Cloud LaTeXマイページ右上のユーザ名をクリック、Dropboxとの連携設定で、PCに設定したアカウントと同じものを連携
  2. Cloud LaTeX上でプロジェクトを作成
  3. 右上のプロジェクト設定(三のようなマーク)からDropbox同期をonにする
  4. Dropboxのフォルダに(Dropbox)/アプリ/Cloud LaTeX/(プロジェクト名)というプロジェクトフォルダができていることを確認
  5. (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で同じ環境で論文書けるのは楽)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む