- 投稿日:2020-02-28T23:59:11+09:00
gitコマンドよく使うやつコピペ用
git checkout <ブランチ名>
ブランチ移動
git checkout -b <ブランチ名>
新規ブランチ作成
git pull
リモートにある最新のコードをローカルに反映
git add
git管理の対象に追加
git commit -m "コメント"
変更を記録する
git push
変更内容をリモートに送る
git merge <任意のブランチ>
作業ディレクトリにリモートの<任意のブランチ>をマージさせる。コンフリクトが発生する可能性もあるので随時解決
git log
commitとmergeの履歴を見れる
git reflog
HEAD やブランチ先端の動きの履歴。各個人のローカルリポジトリに存在。git reflog <ブランチ名>で任意のブランチを見れる。
git status
ワーキング・ツリーの状態を表示する(追加されたとかコミットされたとか)
git fetch
みんなの更新内容を、自分の開発環境に取り入れる機能。みんなの作ったコミットやブランチ・タグなどを、自分の環境に取り入れる。pullと違ってマージしない
git reset <--hard/--soft> HEAD
--hard:ファイルの変更、add、commitを取り消す
--soft:commitを取り消す
git reflogでHEADを確認するgit cherry-pick <コミットID>
git logでコミットIDを確認できる
任意のコミットのみを取り込む
- 投稿日:2020-02-28T19:40:47+09:00
リモート上で削除されたブランチをローカルからも削除するワンライナー
git for-each-ref --format '%(refname:short) %(upstream:track)' | awk '$2 == "[gone]" {print $1}' | xargs git branch -D
.gitconfig
にエイリアスとして登録するとなお良しです。
- 投稿日:2020-02-28T12:14:26+09:00
過去を共有するプロジェクトを monorepo に統合するまで (tomono)
tomono を使ってmonorepoに移行した話を書きます。
(※ monorepo化 = ソフトウェアプロジェクトを複数のgitリポジトリに分けずに、1つのgitリポジトリにおさめること)
monorepo に移行したかった
業務上、あるプロジェクト内でシステムコンポーネントごと(各所で動くAPIやバッチプログラム)に、gitプロジェクトを切っていたのですが、下記の理由により、だんだんmonorepoにしたくなってきました。
- そもそもメンテナが1-2人しかいないのに、リポジトリが10弱存在していて管理が煩雑。
- 依存パッケージやランタイムのバージョンアップについて、それぞれのリポジトリにプルリクだしてマージするのが超煩雑。
- 共通ライブラリもリポジトリが分かれているけれども、半分自動化されているとはいえ、共通ライブラリのアップデートのたびに下記を繰り返すのが苦痛。
- 共通ライブラリの更新とバージョン付け
- 付与したバージョンでリポジトリ(e.g. Sonatype Nexus)へのpublish
- そのバージョンを利用するように各プロジェクトの依存性ファイルを更新する
- (以後、共通ライブラリにミスを発見すると最初のステップからやり直し…)
- それぞれのプロジェクトで共通コードがある場合、共通プロジェクトにくくりだすのが超面倒 (リポジトリ作ってCI置いてpublishして参照して…)
- それぞれのリポジトリごとに開発環境のセットアップ手順があり(
docker-compose up -d
するだけですが)、無駄。- エディタ切り替える時間が無駄。
リポジトリを用途ごとに細かく分けているのはそれはそれで多人数だと回る面はあるのですが、少人数だと苦痛しか生んでいない状況はおかしいと思いました。
要するにリポジトリの凝集度がおかしかったんです。
monorepo なら俺に任せろー!(バリバリバリバリ) → 失敗
そういうことで、monorepoに下記のように移行しようと思ったのですが、
repo1 (git://example.com/git/repo1.git) repo2 (git://example.com/git/repo2.git) : repoN (git://example.com/git/repoN.git) ↓↓上記を下記に変換↓↓ monorepo/ (git://example.com/git/monorepo.git) repo1/ repo2/ : repoN/「gitのことならくわしいんだ!」と思う私は、下記のように「各プロジェクトをマージしていけばいいんじゃね?」と思い、移行を試みます。
$ git init $ git remote add repo1 git://example.com/git/repo1.git $ git fetch repo1 $ git merge --allow-unrelated-histories repo1/master $ # いそいそとrepo1の全ファイルを子ディレクトリに移動させる $ git remote add repo2 git://example.com/git/repo2.git $ git fetch repo2 $ git merge --allow-unrelated-histories repo2/master $ # いそいそとrepo2の全ファイルを子ディレクトリに移動させる
git merge
は--allow-unrelated-histories
オプションにより、全く関係のないリポジトリのコミットグラフをマージすることができます。普通、gitの履歴をたどると、かならず最初のコミットにたどり着くわけですが、これを行ったリポジトリの場合は2つ以上のコミットにたどり着くことになります。これまでプロジェクトを統合した時の経験に従って、上記のようにmonorepo統合化を始めたのですが、今回は途中でうまくいかなくなりました。
$ git remote add repo2 git://example.com/git/repo3.git $ git fetch repo3 # ここで conflict! $ git merge --allow-unrelated-histories repo3/master ? You have unmerged paths. (以下略)git 「マージしようと思ったけどな、repo2の中身の一部がコンフリクトしてんねん。あ、一部はマージできとるからな! 」 (そして一部が無駄に改変されたrepo2の内容が…!)
ここで裏設定が発覚! なんとrepo3はrepo2からはるか昔に暖簾分けされたリポジトリだったのです! …まあ、だからこそmonorepoに戻したいと思ったわけですが…
「そこは別にマージしなくていいんだよぉ!」と思ったのですが、gitさんからしたらマージせざるをえないですよね。どう見ても、マージですし。
マージさせながら、させない(謎)オプションあるかな?ないよな… うーんどうしよう… と思っていました。
tomono
そういうことで tomono の紹介です。同僚の人が見つけてくれました。
上記でやったような作業を、コンフリクトなしで全て自動的にやってくれます。
使い方は簡単で、下記のようにマージ指示のテキストを用意して、リポジトリにある tomono.sh をダウンロードして標準入力に与えてあげればいいです。
$ bash tomono.sh <<EOF git://example.com/git/repo1.git repo1 git://example.com/git/repo2.git repo2 git://example.com/git/repo3.git repo3 : git://example.com/git/repoN.git repoN EOF # 上記完了後、 core/ ディレクトリの中に全てがおさまったリポジトリができあがっている $ ls -a core .git repo1 repo2 repo3 ... repoN履歴はもちろん、手作業で諦めていた各リポジトリのタグも、すべてきちんと移行できました。特に魔法を使っているわけではなく、gitコマンドで移行してくれます。
俺たちのmonorepo化は始まったばかりだ
ここでmonorepo化が終わればいいのですが、どちらかというとリポジトリ統合の後からがmonorepo化の本番で、開発環境定義を修正したり、モジュール間の依存性を書き直したり、きちんとCI/CDの設定をしたり、READMEを修正したりなどなど、いろいろ実施する必要がありました。
特にCI/CDは、そもそも分かれていたリポジトリについて、一斉にビルド・テスト・パブリッシュをするようになるので、ビルド時間増大の懸念などあったのですが、特にそこまで問題にならずに統合することができました。(もちろんいくつかトリックは入れたのですが)
みなさんも、リポジトリ多すぎと思ったら、monorepo化してみましょう!
- 投稿日:2020-02-28T01:18:06+09:00
GitLab CIの基本的な流れ
GitLab CI
GitLabから他のツールに連携して、ビルド、テスト、デプロイなどのCI連携ができる機能です。
使ってみると意外と色んなことが出来て面白そうなので、作り方をメモしておきます。今回の構成と動作の流れ
バージョンなど
OS...Debian 9
GitLab...12.6
GitLab Runner...12.6
Ansible...2.10流れ
- Debian_1にDockerがインストールされていて、GitLabがコンテナで動作している。ここにレポジトリがあり、ソースが保存される。
- 別のDebian_2にGitLab Runner(後述)とAnsibleがインストールされている。
- Ansibleはさらに別のDebian_3を構築する。
- pushやmergeなどのタイミングでGitLab本体がGitLab runnerに通知を送る
- Runnerは実行条件に合致していたら動作を開始する
- Runnerはまずgit cloneコマンドでレポジトリの内容をローカルにコピーして、そのレポジトリに
cd
する- Runnerはそのノード上で
ansible-playbook
コマンドを実行する- Ansibleは普通に実行した時と同じようにdebian_3を構築する
- 2のところの実行条件と、4で実行するコマンドはジョブの定義として作ってあげる必要があります。
- また、5のところでAnsibleがちゃんとDebian_3を構築できるようにinventryを設定してあげる必要があります。 環境変数とかを使ってうまくやってください。
GitLab Runnerのセットアップ
上の図でDebian_2にあるGitLab Runnerをインストールします。
Runnerの種類
GitLab Runnerには大きくShared RunnerとSpecific Runnerがあります。
自分もよくわかってないので、詳しくはここを見てください。Shared Runner
複数のプロジェクトで共通して利用できるRunner。
いつどれが使われるかはRunnerとCIジョブに付けられたタグで決まります。Specific Runner
特定のプロジェクトでのみ利用されるRunner
Runnnerのインストール
GitLabの公式サイトから
.deb
のバイナリを落としてくることができます。
Debian以外にもRHEL, Windows, Mac, FreeBSDなど複数に対応します。これだけでインストールが完了します。
root@debian:~# dpkg -i gitlab-runner_amd64.deb
Runnerの初期設定
Runner側でコマンドをいくつか打つことで、GitLabにRunnerとして登録することができます。
root@debian:~# gitlab-runner register Runtime platform arch=amd64 os=linux pid=4496 revision=1b659122 version=12.8.0 Running in system-mode. Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/): http://10.91.77.8/ #GitLabサーバーのURLを入力 Please enter the gitlab-ci token for this runner: dx5bFN4PJQ9XNUj_Vxbf #GitLabのadmin画面から取得できるトークンを入力 Please enter the gitlab-ci description for this runner: [debian]: Ansible runner #このRunnerの説明を入力 Please enter the gitlab-ci tags for this runner (comma separated): ansible #コンマ区切りでタグを入力 Registering runner... succeeded runner=dx5bFN4P Please enter the executor: docker+machine, docker-ssh+machine, kubernetes, docker, docker-ssh, shell, virtualbox, custom, parallels, ssh: shell #ジョブの実行方法を入力 Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!ジョブの実行方法はローカルでシェルを実行する、SSHでリモートを操作する、Dockerコンテナを立ち上げるなど複数から選べます。
.gitlab-ci.yml
の作成環境が完成したら実際にCIを実行する定義
.gitlab-ci.yml
を作ります。
このファイルはレポジトリのルートに置いておき、他のソースと同じようにバージョン管理されます。今回はレポジトリに登録されたAnsible playbookを自動実行するように以下のようなYAMLを書きました。
詳細な文法などはGitLabの公式サイトを見てください。.gitlab-ci.yml# ステージ定義 stages: - test - deploy - result # Pushするたびに文法チェックをする sanity_check: stage: test script: - "ansible-playbook site.yml --syntax-check" only: - push tags: - ansible # MRを作成したらDRY RUNで実行し、エラーにならないかを確認 run_playbook_dry: stage: test script: - ansible-playbook -i ansible_hosts.ini site.yml --check only: - merge_requests tags: - ansible # develブランチにマージされたタイミングで自動デプロイ run_playbook: stage: deploy script: - ansible-playbook -i ansible_hosts.ini site.yml only: - devel tags: - ansible # ジョブが失敗したらMRにコメントを残す send_failure_message: stage: result script: - 'MR_IID=$(curl --request GET --header "Private-Token: $API_PRIVATE_TOKEN" "http://10.91.77.8/api/v4/projects/$CI_PROJECT_ID/repository/commits/$CI_COMMIT_SHA/merge_requests" --insecure | jq --raw-output ".[0].iid")' - 'PROJECT_URL=$(curl --request GET --header "Private-Token: $API_PRIVATE_TOKEN" "http://10.91.77.8/api/v4/projects/$CI_PROJECT_ID" --insecure | jq --raw-output ".web_url")' - 'curl --request POST --header "Private-Token: $API_PRIVATE_TOKEN" --data "body=CIジョブに失敗しました。<br><a href=$PROJECT_URL/pipelines>パイプラインの詳細</a>から内容を確認してください。" http://10.91.77.8/api/v4/projects/$CI_PROJECT_ID/merge_requests/$MR_IID/notes --insecure' when: on_failure only: - merge_requests tags: - ansibleまずGitLabから対象のサーバーにレポジトリが
git clone
されます。
前述の通りクローンした後はそのディレクトリにcd
されるので、scriptブロックに記載するコマンドはすべてレポジトリのルートから見える相対パスで書きます。ジョブ
YAMLの一番上の階層に書いてあるもの(予約語以外のもの)がジョブで、この単位でスクリプトや実行条件を指定します。
上のスクリプトでいうとsanity_test
やrun_playbook_dry
とかsend_failure_message
ですね。stages
順序立ててジョブを実行する必要がある場合、stagesを定義してジョブを所属させると順番を決めて実行できます。
同じstageで複数のジョブが定義されている場合はパラレルで実行されます。
上のでいうと、まずtest
に所属するジョブが実行され、次にdeploy
、終わったらresult
が動きます。only,except
ジョブごとに実行条件を指定できます。(exceptは除外条件)
push
はpushした時、merge_request
はMRを作成した時、ブランチ名を書くとそのブランチが更新された時です。
今回は実際にplaybookを動かすところをonly: devel
にしているので、devel
ブランチの中身が更新された時だけPlaybookが適用されます。tag
Runnerのタグを指定します。
ここに指定したRunnerでジョブが実行されます。APIを使ってコメント投稿
ジョブ
send_failure_message
ではジョブに失敗した時という条件でMR画面にコメントを書き込んでいます。
script
に書かれてる内容は普通にRunnerがインストールされているOS上でコマンドを打ってるのと同じなので、GitLab APIを使ってコメント残したり色々できます。
$API_PRIVATE_TOKEN
というのはgitlab-runnerのconfig.toml
内で定義した環境変数的なやつです。変数の中身はGitLabのWeb画面から取ってきたbot用アカウントのプライベートトークンです。実際に試す
Push
ローカルからpushすると、レポジトリの一番上に緑のチェックがつきます。
YAMLの文法に問題なかったということがわかります。MR作成
MRを作成するとAnsibleのDRY RUNが走り、不具合が起きないかをチェックできます。
成功したらさっきと同じように緑のチェックが付きます。クリックしないと具体的に何のジョブが実行されたのかわからないのは難点ですね。
マージ
マージされると実際にPlaybookが実行され、サーバーに構成が反映されます。
上の画像の下にもう一つチェックが付きます。参考:詰まったところ
よくわからんエラーで詰まったのでメモしておきます。
エラーはコピーしてなかったので後からネットで拾ってきました。HTTP 403
Running with gitlab-ci-multi-runner 9.4.2 (6d06f2ec) on host-vm (a126d8fa) Using SSH executor... Running on host-vm via host-vm... Cloning repository... Cloning into 'builds/a126d8fa/0/project/appName'... fatal: unable to access 'https://gitlab-ci-token:xxxxxxxxxxxxxxxxxxxx@gitlab.my-company.com/project/appName.git/': The requested URL returned error: 403 ERROR: Job failed: Process exited with: 1. Reason was: ()→ユーザー権限の問題でした。権限振り直したら直りました。
git fetch-packのエラー
Running with gitlab-runner 11.9.2 (fa86510e) on stage-test yMEso5rx Using SSH executor... Running on d1.XXXXXX.com via d1.XXXXXX.com... warning: templates not found builds/yMEso5rx/0/kudja/postel-deluxe.tmp/git-template Reinitialized existing Git repository in /home/qapd/builds/yMEso5rx/0/XXXX/XXXXXX/.git/ Clean repository Fetching changes with git depth set to 10... fatal: remote origin already exists. fatal: git fetch-pack: expected shallow list ERROR: Job failed: Process exited with: 1. Reason was: ()→Runnerに入ってるGitのバージョンを上げたら直りました。
- 投稿日:2020-02-28T00:40:35+09:00
git pull origin masterしたら「error: Your local changes to the following files would be overwritten by merge」となる
事象
master
ブランチを親とするfeature
ブランチをチェックアウトした状態でgit pull origin master
をしたら、「error: Your local changes to the following files would be overwritten by merge」となった。$ git branch master * feature $ git pull origin master From github.com:Example-Git/api-repo * branch master -> FETCH_HEAD Updating 5966be1..1c8aaff error: Your local changes to the following files would be overwritten by merge: Makefile Please commit your changes or stash them before you merge. Aborting原因
master
ブランチの変更により上書きされるファイルMakefile
が、ローカルのfeature
ブランチで変更されたまま未コミットであったためだった。$ git status On branch feature Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: Makefile no changes added to commit (use "git add" and/or "git commit -a")解消
Makefile
の変更はコミット不要だったので、git checkout
により変更を破棄したら正常にgit pull origin master
できた。$ git checkout Makefile $ git status On branch feature nothing to commit, working tree clean $ git pull origin master From github.com:Example-Git/api-repo * branch master -> FETCH_HEAD Updating 5966be1..1c8aaff Fast-forward Makefile | 3 +- sam.yml | 26 + 2 files changed, 28 insertions(+), 1 deletions(-) create mode 100644 src/handlers/__init__.py以上