ツナワタリマイライフ

日常ネタから技術ネタ、音楽ネタまで何でも書きます。

GCPのコンピューティングサービスの料金をざっくり確認する

はじめに

カレー好きなエンジニア、たけしです。ついついGCPでがっつりカレーパーティに申し込んでしまったのですがGCPを使ったことがありません!

topgate.connpass.com

やばくない?何だこのイベント!これを機に俺もGCP好きのカレーエンジニアになるぞオォォォとなりました。

真面目な理由としては、今後仕事でクラウドを"使う側"に立つ可能性があり、AWSだけではなく、GCPやAzureで何ができるか、何が違うのかは把握する必要があると思っていること。コンテナオーケストレーションのKubernetesを使ってみたいことがあります。

で、まずは料金からです。

料金体系

公式の日本語サイトが充実しています。

cloud.google.com

料金計算ツールもあるので合わせて見ていきましょう。

cloud.google.com

App Engine(GAE)

アプリケーションを載せることができる、PaaSです。

cloud.google.com

言語はNode.js、JavaRubyC#、Go、PythonPHPに対応。トラフィック分割やアプリケーションのバージョニングなど、リリースに関する仕組みは気になりますね。

Herokuに似たようなサービスです。

気になるお値段は?

  • インスタンスのクラスによって、0.05〜0.30 USD / hour のようです。
  • 起動時には15分間の起動時間が加算されます。つまり最低課金料金が15分間ということですね。
  • Google Cloud Databaseは無料枠あり。1日1GBのデータ保存まで。

料金計算ツールを使ってみました。

Outgoing Network Traffic: 1 GB
Cloud Storage: 10 GB
$0.13

1ヶ月です。適当にネットワークとcloud storageを入れてみましたが、app-engineのインスタンスには課金は発生しないようですね。search apiなど他のサービスを呼んだり、トラヒックを使うと課金されていくようです。

Compute Engine(GCE)

cloud.google.com

EC2と同等のものですね。

なんとf1-microだと料金はかからない!あとは基本的にはフレーバーのレベルによって変わるみたいです。

f1-microで1ヶ月使った場合

730 total hours per month
VM class: regular
Instance type: f1-micro
Region: Iowa
Sustained Use Discount: 30%  
?
Effective Hourly Rate: $0.0053
Estimated Component Cost: $3.88 per 1 month

わずか3.88ドル。永続ストレージ30GBをつけても

Storage: 30 GB
$1.20

合計5ドルです。めちゃくちゃ安いですね。

Container Engine(GKE)

Google Container Engine ドキュメント  |  Container Engine  |  Google Cloud Platform

ECSと同等のサービス。

クラスタ内のノード数が5までなら無料なんですね!クラスタのノードにはGCEを利用するようで、GCEの料金がそのまま加算されます。

6ノード以上だと、アイオワでは1ヶ月

6 ノード以上    標準クラスタ  $109.50

結構かかりますね。

Container Registry

cloud.google.com

DocukerHubのような、Container Registryサービスは、StorageとNetworkの料金のみしかかからないようです。

Google Container Registry では Docker イメージが使用した Google Cloud Storage とネットワーク出力に対してのみ料金が発生します。詳しくは、料金ガイドをご覧ください。

Cloud Functions

cloud.google.com

lambdaに代表される、Function as a Serviceですね。

こちらも無料枠が用意されてます。試すだけならできそうですね!

  • 呼び出し
無料試用枠 無料枠を超えた料金(単価) 課金単位
200 万回 $0.40 呼び出し 100 万回あたり
  • コンピューティング時間
無料試用枠 無料枠を超えた料金(単価) 課金単位
400,000 GB 秒 $0.0000025 GB 秒単位
200,000 GHz 秒 $0.0000100 GHz 秒単位
  • 送信データ(下り)
無料試用枠 無料枠を超えた料金(単価) 課金単位
5GB $0.12 GB 単位
  • 受信データ(上り)
無料試用枠 無料枠を超えた料金(単価) 課金単位
無制限 無料 GB 単位
  • 同じリージョン内の Google API への送信データ
無料試用枠 無料枠を超えた料金(単価) 課金単位
無制限 無料 GB 単位

結構安い気がする。何個ノード?を立てるかは関係ないのか。

おわりに

ざっくりComputeサービスの料金を見てみました。StorageやNetworkの料金が関係してくるので一概には言えませんが、個人が簡単に試してすぐ消す分にはCloud Funtion、f1-microでのGCE/CKEはそんなに大金かけずに使えそうです。

次回はTerraformを使って実際にインスタンスを作ってみたいと思います。

「友人たちのためのブラウザ」Vivaldiのショートカット調べてみる

はじめに

vivaldi使ってますか?ぼくは毎日楽しいvivaldiライフを送っています!Tシャツ買っちゃいました!

steers.jp

以前記事も書きました。

take-she12.hatenablog.com

長く使っているのにもかかわらず、ショートカット全然使いこなしていません。今から覚えよう!のモチベーションで記事を書き始めています。一緒に触っていきましょう。

チートシートを見る

まずcommand + F1でチートシートを眺めていきます。

ウインドウ

  • Windowを閉じる、Command + Space + W は押しづらいですね。使いそうだけど。
  • Command + E、もしくはF2のクイックコマンドはまず覚えたい
  • Command + Shift + Bでブックマークが出たり消えたりする。知らなかった。

表示

  • Command + / でアドレスバー。検索が早くなりそう。

  • Command + F11、UI切り替え。タブやアドレスバーなどがなくなってWeb画面だけになります。

  • 拡大/縮小 拡大がCommand + ^、縮小がCommand + -でした。

  • F4でパネル

  • F7でパネルにフォーカスを移す

  • Command + Control + 何かで履歴やブックマークをだせる

タブ

  • Command + Tで新しいタブ
  • Command + Wでタブを閉じる。なんでWなんだろう。
  • タイルにしたり縦にするCommand + F6-F9は解除しか効かなかった

表示

  • Command + D ブックマークの作成​

Macのショートカットキー記号

そもそものことを言いますとMacのあのマークが覚えづらくて全然ショートカットキー覚えられないんですよ。Commandのアレはキーボードに書いてますけど、家マーク、ハット(^)、右下がりのエスカレーター、、、これのいい覚え方はないんですかね。

support.apple.com

もう2年Mac使ってるやつのセリフじゃないな。。。

その他カスタマイズ

せっかくなのでこれを機会に設定見直しました。 * アドレスバーに検索欄を表示、をオフ(アドレスバーで検索できるので不要) * マウスジェスチャー ALTと一緒にジェスチャー有効

を変更しました。

おわりに

大好きなvivaldi、たまにタイルにするぐらいしか特有の機能使っていないですが、ショートカットキーこまめに確認して少しずつ覚えようと思います。

「あなた」という商品を高く売る方法 キャリア戦略をマーケティングから考える を読んだ

はじめに

同期氏のすすめで読みました。

キャリアをどう進めるかについては「キャリアショック」がとても良かったですね。具体的に社内で、どうやって自分の都合の良いようにキャリアを進めていくかが書かれてあります。

そして今回読んだこの本は、商品を売る「マーケティング」のアイデアを元に、キャリア戦略を自分という商品をどう売るかになぞって解説した本です。

以下、本書で紹介されている内容を少しずつピックアップして紹介してみます。

競争を避けること

ただ、首尾一貫していたのは、「競争を避けて、好きなことをする」ということ。結果的に、これが自分という商品の価値を高めることにつながった(kindle 位置196)

いかに戦わなくて済む領域を探せるか。レッドオーシャンではないブルーオーシャンを探せというわけである。

会社組織にいても、"出世競争"という言葉があるように、戦って上にいくという意識を持つひともいるだろう。自分もそこまでの熱はないが、"勉強し続けないと死ぬ"という気持ちはもっていて、それは周囲のひとに負けない、ついていく、という気持ちがあったのだと思う。

消費者が「より自分らしく、よりワガママに」なったからこそ、あなたを必要とする人は必ずどこかにいる。現代では、細分化されたニーズに特化すれば、競争は避けられるのだ(kindle 位置221)

ニーズが多様化、細分化したことによって、どこにでも当てはまる太いスキルというものはもはや存在しなくなった。どこかにその細かい1部分にマッチする仕事があり、それが自分が得意とする可能性は高い。

バリュープロポジション

① 自分の強み、② ターゲット、 ③ ニーズ、 ④ 自分の仕事、の四つ (Kindle の位置No.515-516)

​このように「 相手が求めていて、 自分しか提供できない価値」 のことを、「 バリュープロポジション」 という。(Kindle の位置No.495-496).

自分の中のバリュープロポジションを育てることにより、戦わずして勝つことが可能になるという。実際に考えてみよう。

とはいえ、自分の強みを自分で見つけるのはなかなか難しい。

あなたという商品の価値を高める出発点は、自分の強みを見極めて育てることだ。 しかし自分の強みは、自分だけでは意外と気がつか ないものである。(Kindle の位置No.579-581)

強みは、先天的な才能と、後天的な技術と知識の掛け合わせでできるという。ここでいう先天的な才能とはよくいう突出的な能力をいう意味ではなく、個性の1つであり、優劣はないという。

「才能」 とは、 個人が必ず持っている資質や性格のことだ。どの資質がいいとか悪いとかいうことではない。 自分が持っている資質 を、 仕事で活かすことができるかどうかが 重要なのだ。

Amazon CAPTCHA

これを参考に自分の強みを探し、育てていくことが重要である。苦手なことを克服するよりは、得意なことを伸ばしたほうがいい。

したいことと好きなこと

最初から自分のしたい仕事はできないし、選べないひとも多いだろう。それでもまず、与えられた今の仕事を好きになることが第一だという。

まずはいまの仕事を 好きになることが、 あなたの強みをつくり、 あなたという商品の価値を高める近道 だ。いまの仕事でできること をいろいろ試してみて、 好きになる部分を見つける。「 これは自分がやりたい仕事ではない」 といった先入観にとらわれず、 その仕事 で試行錯誤してみて、 自分の才能を活かす方法を見つけていくのだ。 そこを起点に自分の強みをつくっていけばいい(Kindle の位置No.681-685)

なおキャリアショックでは、今与えられてる仕事はちゃんとこなした上で、別の自分のしたいことについても勝手にやって声をあげることをお勧めしていた。そのうちそちらに需要がでてきて、本来やるべきであった仕事は他のひとにまわされることになる。

リアルオプション理論

そうはいっても、どうしても好きになれない仕事もある。つまり、新しいことに挑戦する回数が多ければ多いほど自分の好きな仕事に出会える確率は当然あがる。しかし、そう簡単になんども挑戦できるものだろうか。

そのためには、挑戦する回数を増やすこと。そしてリスクをただ回避するのではなく、リスクを上手に管理しながら、リスクを下げる ことだ。リスクを下げれば、 挑戦回数を増やせるし、 あなたの強みを創り出す機会も増える。(Kindle の位置No.847-849).

つまり、許容できるリスクを設定し、その期間内で思いっきり挑戦すること繰り返す。これにより挑戦回数を増やすことと、リスクを最低にすることができる。損切りみたいなもんですね。

仮説思考

たくさん挑戦をして、たくさん学ぶためには失敗し、その失敗から学ぶサイクルが重要。よく言われるPDCAは円環ではなく、らせん状にあがっていくものだと解説されている。

正しい仮説検証は「 円」でなく「らせん」である。まず大まかな仮説を立てて(プラン)、すぐに実行する(ドゥ)。結果を検証し( チェック)、学びをもとに対応策を考え( アクション)、新しい仮説を立てる(新しいプラン)。こうしてらせんを1段上がる。そして 新しい仮説を実行・検証 し、新たな仮説を立てると、らせんを2段上がる。 (Kindle の位置No.1226-1229).

僕は「なんでもやってみる」精神ではありますが、仮説を立ててやっているかというと少しかけている気がします。(もっとピンポイントに、障害調査等は仮説を立てて原因を詰めていきますが)もう少し広い範囲で、何をはじめるときにも仮説/実験を繰り返していきたい。

コンフォートゾーンからの脱出

セブンイレブンAmazonの例をあげて、常に安泰の状態(コンフォートゾーン)にいてはならない、自ら挑戦をしてリスクのある分野に展開していかなければならないと述べている。これはまったくその通りだと思う。(セブンイレブンの紹介で、それをいえばAmazonでは、と思ったら案の定でてきた。)

セブンのトップは「競う相手は他社ではない。お客さまニーズの変化だ」と言っ ている。実際、セブンは他社コンビニと戦っているようには見えない。たとえば他社に先駆けて新サービスを始めるケースが多い。セブンはライバルではなく過去の自分と戦い続けているの だ。その結果、追いかけてくるライバルを尻目に、「 戦わずして勝つ」状態で独走を続けている。(Kindle の位置No.1304-1308).

大きな会社にいると、変わることを恐れるひとは多い。それに、何といってもソフトウェア開発では、ソースコードの変更を恐れ、「動いているうちは触るな」という伝説のような話もある。(実際にある)

ソフトウェア文化に重ねても、容易に変更可能なようにリファクタリングやテストコードを書くことであったり、インフラに関してもInfrastructure as Codeの考え方が当たり前になってきて、何度でも壊して作り直すことが当たり前になってきている。

Infrastructure as Code ―クラウドにおけるサーバ管理の原則とプラクティス

Infrastructure as Code ―クラウドにおけるサーバ管理の原則とプラクティス

僕はInfrastructure as Codeの「アンチフラジャイル」という考え方を知って、これはキャリアも同じだなと思った。つまり、変更に強くなる。変更しても、損失しない。変更するたびに、(フィードバックを得ることによって)強くなる。自分自身の仕事内容も、どんどん変化させていかないといけないし、そのたびに抽象的な考え方を学ぶことで、他分野に活かすことができる。これが1つを極めたら2つめ、3つめを極めるのはより短時間で済む、ことにもつながっているのではないだろうか。

個人が専門分野をつくるときも同じだ。初めて地下水脈を掘り当てるまでは、 勝手がわからないため試行錯誤の回数も多く、1万時間 の集中が必要だ。しかし新しい専門 分野をもう1本掘ろうとすると、経験的に地下水脈がどのあたりにあるか見当がつくので、より短い 時間で地下水脈を掘り当てることができる。3本目は寄り道せずに、さらに短い時間で掘り当てることができる(Kindle の位置No.1427-1431).

そして話はコンフォートゾーンに戻るが、僕が現在猛烈に危機を感じているのは、(ビジネス的に、ではなく)私個人が非常にコンフォートゾーンにいると感じているからだ。会社が潰れる可能性はあるし、会社じゃなくとも自分が所属する部署、本部が経営危機になって異動や、期待しない仕事をさせられる可能性もある。しかし、それでもしばらく数年は今の給与のままで、特にストレスも失敗もすることなくやっていける確信がある。それではいけないと思っている。リスクをとって、自分自身の成長と、周囲やビジネスを動かす経験をしたい。

関連するが、チーム開発における"心理的安全"とは少し違う。私が所属する部署の上司はとても良い上司ばかりだ。否定したり、怒鳴ったり、原因究明のときに心理的に追い込むような聞き方はしないし、マイクロマネジメントもしない。必要な相談は乗ってくれるし、雑談もするし、ビジネスに必要な指示、アドバイスはしてくれる。これにより私自身は心理的安全な状態で仕事ができている。その状態で行う仕事内容が、少し物足りなく、このままでは成長スピードが遅くなってしまう、という危機感を持っている。

mirai.doda.jp

本書ででてくる"強みの掛け算"は堀江さんが言うところの"多動力"だろう。1つのプロフェッショナルならライバルが多いが、それらを掛け合わせるとライバルが減っていき、本書でいう「戦わずして勝つ」ことをやがて実現できるというわけだ。

多動力 (NewsPicks Book)

多動力 (NewsPicks Book)

弱いつながりと強いつながり

自分の商品価値を自分のためにあげていくと、やがてそれは社会貢献につながる。その中で、昔は1企業内や家族でガッチリと絆で結ばれた強い絆がメインな社会的つながりだったが、現代は世の中の多様なニーズに対応するために絶えず新しい発想をしていかなければならず、そのために多種多様な人材を交流する弱いつながりが多くなってきたという。

滅多に会わないSNS上でつながった知り合いから、自分の周囲では誰も話題にしていないような道の情報を得たことがある人も多いはずだ。彼らは自分の周囲とは全く異なる集団に属しているから、知らないことが大きな話題になっていることも多い。つまり弱いつながりは新しいアイデアを得るのに向いているのだ。

本書では自組織では得られない発想を得るために弱いつながりが有効であると述べている。そして弱いつながりから得られる資本、ソーシャルキャピタルを元に社会貢献につなげていく。

弱いつながりといえば、東浩紀の以下の本が記憶に新しい。記事も書いた。

弱いつながり 検索ワードを探す旅

弱いつながり 検索ワードを探す旅

take-she12.hatenablog.com

この本は観光として出会う"弱いつながり"こそがよりよく生きるために寄与するよ、という哲学的内容になっていますが、この偶然の出会いが面白い発想なり、未知の経験を生むと解釈すれば、それはキャリアでも同じことが言えるでしょう。

自分のバリュープロポジションを考えてみる

さて、せっかくなのでここで自分のバリュープロポジションを考えてみましょう。バリュープロポジションとは

  • どんな強みを活かして(自分の強み)
  • 誰の(ターゲット)
  • どんな悩みに(ニーズ)
  • いかに応えるか(自分の仕事)

を埋めることでした。

まずは現職を考えると、最近ではCI/CDの取り組みをしたので

  • ソフトウェア開発におけるCI/CD導入・実践経験の強みを活かして
  • CI/CDサイクルを導入していないチームの
  • CI/CDサイクルを導入したいけど何をすればいいかわからないという悩みに
  • 現状を一緒に考え、必要な施策を立て、支援しながら導入していく

になるでしょうか。まぁ、まだまだ「CI/CDの導入・実践経験」で君はなにができるの?何がすごいの?っていうところを詰めないといけないですし、今時そんなチームいるのかって気がしますね。しかも自分しかできないことでは到底ないですね。

次に自分が今考えているサービスに当てはめてみましょう。

  • スパイスカレーを自ら作ることができることと、ソフトウェア開発の文化をよく知っている強みを活かして
  • スパイスカレーを作りたい人の
  • 自分にあったレシピを探したいという悩みに
  • スパイスを含んだカレー料理のレシピを共同作成できるサービスを提供する

クックパッドでいいじゃん、となってしまうので、悩みをもう少し深堀したほうがよさそうです。ただしカレー×IT両方に通じてるひとはあまりいない気がします。

趣味の音楽についてはどうでしょう。

  • 作詞・作曲・ギター・ベース・ドラム・CD制作・MV制作の経験している強みを活かして
  • 自分で曲が作りたい人の
  • 曲の作り方がわからないという悩みに
  • 曲の作り方のフレームワークを提供する

すでに多くの人がやっとるわいって感じですね。半端にいろいろ手を出してるから初学者への導入支援っていうのは自分の得意としていることなのかもしれない。確かに関心はある。ただこれも自分だけにできることではないですね。

おわりに

マーケティングの考えをベースに自分のキャリアを進める方法、付随する大切な考え方が多くのっている。非常にわかりやすい良書でした。まだ自分自身のキャリアを考えたことがない人は1度読んでみると良いと思います。

僕もそろそろキャリア次に進めます。

SoftwareDesign 7月号復習

はじめに

読んだので気になった部分をメモ。

特集:理論&応用でシェル力の幅を広げる

第1章 シェル初心者から中級者への次に一歩

パイプやリダイレクトの話が中心でした。が、論理演算の&&と||はあらためてなるほどと思いました。

よく見りゃ当たり前なんですが、&&は両方真でないとtrueにならない、だから前コマンドが失敗したときは次のコマンドは実行されない。前コマンドが成功したとき"だけ"後コマンドが実行されるわけですね。

そして||は前コマンドが成功した場合は全体として真になるので、後コマンドは実行されない。つまり前コマンドが失敗したときのみ後コマンドが実行されます。

よく考えればそうなんだけど、なるほどなぁと感心しました。

ちなみに僕は普段fishを使ってるんですが&&使えません。

第2章 シェルスクリプト初心者から中級者への次の一歩

タイトル同じやん。

シェル哲学についてですね。

  1. 成否は終了ステータスで返す
  2. 成功したときは何も表示しない
  3. 失敗したときは静かなエラー
  4. エラーは標準エラー出力に出力する
  5. フィルタとパイプを意識する
  6. bashに依存しているのに#!/bin/shと書かない

特に6は僕も最近引っかかったので明示的に#!/bin/bashと書くようにしています。

ただ、失敗したときは静かなエラー、例としてcpで存在しないファイルをしていしたときの「No such file or directory」をあげていますが、例えばgitなんかはコマンド間違いのときていねいに「もしかしてこれじゃない?」と教えてくれたり「たぶんpullしないといけないよ」って言ってくれますよね。コマンドを組み合わせて使うことを考えるとエラーは静かな方がいいかもしれませんが、親切なのも助かりますね。

第3章 しくみを知れば、bashは怖くない

bashはシェルの一種。そしてシェルはOSを操作するためのベースのインターフェイスだということを体感するために、phpインタラクティブモードをデフォルトシェルにしてみるお話。

ただのプログラミング言語の一種なんだよーということをわかってもらう記事ですね。

ビルトインコマンドと外部コマンドの違いを確認しておきます。

whichやpwdは外部コマンドで、cdはビルトインコマンドのようです。

vagrant@docker:~$ which cd
vagrant@docker:~$ which which
/usr/bin/which
vagrant@docker:~$ which pwd
/bin/pwd

typeで確認できます。

vagrant@docker:~$ type cd
cd is a shell builtin
vagrant@docker:~$ type pwd
pwd is a shell builtin
vagrant@docker:~$ type which
which is hashed (/usr/bin/which)

pwdはどっちやねん。

vagrant@docker:~$ help
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
These shell commands are defined internally.  Type `help' to see this list.
Type `help name' to find out more about the function `name'.
Use `info bash' to find out more about the shell in general.
Use `man -k' or `info' to find out more about commands not in this list.

A star (*) next to a name means that the command is disabled.

 job_spec [&]                                                   history [-c] [-d offset] [n] or history -anrw [filename] or>
 (( expression ))                                               if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS;>
 . filename [arguments]                                         jobs [-lnprs] [jobspec ...] or jobs -x command [args]
 :                                                              kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... >
 [ arg... ]                                                     let arg [arg ...]
 [[ expression ]]                                               local [option] name[=value] ...
 alias [-p] [name[=value] ... ]                                 logout [n]
 bg [job_spec ...]                                              mapfile [-n count] [-O origin] [-s count] [-t] [-u fd] [-C >
 bind [-lpsvPSVX] [-m keymap] [-f filename] [-q name] [-u nam>  popd [-n] [+N | -N]
 break [n]                                                      printf [-v var] format [arguments]
 builtin [shell-builtin [arg ...]]                              pushd [-n] [+N | -N | dir]
 caller [expr]                                                  pwd [-LP]
 case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac     read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N>
 cd [-L|[-P [-e]] [-@]] [dir]                                   readarray [-n count] [-O origin] [-s count] [-t] [-u fd] [->
 command [-pVv] command [arg ...]                               readonly [-aAf] [name[=value] ...] or readonly -p
 compgen [-abcdefgjksuv] [-o option]  [-A action] [-G globpat>  return [n]
 complete [-abcdefgjksuv] [-pr] [-DE] [-o option] [-A action]>  select NAME [in WORDS ... ;] do COMMANDS; done
 compopt [-o|+o option] [-DE] [name ...]                        set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...]
 continue [n]                                                   shift [n]
 coproc [NAME] command [redirections]                           shopt [-pqsu] [-o] [optname ...]
 declare [-aAfFgilnrtux] [-p] [name[=value] ...]                source filename [arguments]
 dirs [-clpv] [+N] [-N]                                         suspend [-f]
 disown [-h] [-ar] [jobspec ...]                                test [expr]
 echo [-neE] [arg ...]                                          time [-p] pipeline
 enable [-a] [-dnps] [-f filename] [name ...]                   times
 eval [arg ...]                                                 trap [-lp] [[arg] signal_spec ...]
 exec [-cl] [-a name] [command [arguments ...]] [redirection >  true
 exit [n]                                                       type [-afptP] name [name ...]
 export [-fn] [name[=value] ...] or export -p                   typeset [-aAfFgilrtux] [-p] name[=value] ...
 false                                                          ulimit [-SHabcdefilmnpqrstuvxT] [limit]
 fc [-e ename] [-lnr] [first] [last] or fc -s [pat=rep] [comm>  umask [-p] [-S] [mode]
 fg [job_spec]                                                  unalias [-a] name [name ...]
 for NAME [in WORDS ... ] ; do COMMANDS; done                   unset [-f] [-v] [-n] [name ...]
 for (( exp1; exp2; exp3 )); do COMMANDS; done                  until COMMANDS; do COMMANDS; done
 function name { COMMANDS ; } or name () { COMMANDS ; }         variables - Names and meanings of some shell variables
 getopts optstring name [arg]                                   wait [-n] [id ...]
 hash [-lr] [-p pathname] [-dt] [name ...]                      while COMMANDS; do COMMANDS; done
 help [-dms] [pattern ...]                                      { COMMANDS ; }

helpで表示されるものがビルトインコマンドなんですね。

ビルトインコマンドはbashから直接実行されていることをpsで確かめています。

第4章 じつはこんな機能があった!bashの新機能、便利機能

bashのバージョンとか全然意識してないですね。vagrantで作ったubuntuで確認してみます。

vagrant@docker:~$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
vagrant@docker:~$ bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

4.3.48です。2016年9月に4.4がリリースされたと書かれていますね。

|&で標準出力と標準エラー出力をまとめてパイプに出せるのは使いそうです。shoptで有効にできるglobster、failglob、autocdは何でしょうか。

globsterは*以下をすべて再帰マッチする。autocdはディレクトリ名を入力したときcdの引数とみなす、faillobはパターンマッチしなかったときに失敗とみなす。

他にもいろいろあるみたいで、別の機会に見ておきたい。

コプロセス

絶対子プロセスだと思った。co-process。ざっくりいうとコマンドを立ち上がりっぱなしにする、サーバ化するもののようです。coprocコマンドで検索したほうがでますね。ループでなんども同じコマンドを実行するときに検討できる機能とのこと。使わないだろうな。。。

参考

第5章 意外と使える!? Bash on Ubuntu on Windows

Windows10の機能であるbash on windowsについて。仕事ではWindows10なんですがこの機能使えないんですよね。プライベートだとmacなので、もちろん使わない。記事によるとだいたいのことが問題なくできる、とのことです。

ハッシュ関数を使いこなしていますか?後編

ハッシュアルゴリズムの安全性指標を復習しておきましょう。

衝突困難性

同じハッシュ値を持つ2つのデータを見つけることが難しいという性質

原像計算困難性

ハッシュ値から元のデータを算出することが難しいという性質

第2原像計算困難性

あるデータがあったときに、そのデータのハッシュ値と同じハッシュ値を持つほかのデータを見つけることが難しいという性質

また、データベースのキーからハッシュでurlを生成するときの注意はなるほどと思いました。キーをハッシュ化しているということは、連番になっているので、1つわかれば全てのキーが推測可能になります。対策として、ソルトを足してやることが大切です。

また、パスワードに関しては、ソルトを使ったとして「よく使われるパスワード」であれば同じハッシュ値が生成されてしまいます。このときはユーザ名など、キー固有の値をソルトに使うことで推測を困難にさせることができます。

レッドハット系ソフトウェア最新解説 コンテナを使ってみよう

Red Hat Summit2017が7月にあり、OpenShiftが注目を浴びたとのこと。OpenShiftとは?

Docker、kubernetesの上にのるコンテナプラットフォームのようです。

http://jp-redhat.com/openeye_online/column/omizo/4093/

おわりに

雑誌ははやく読んで、インプットの入り口にしないといけない。買うはいいけどためがちなのではやめの消化を今後も心がけたい。

「エンジニアのためのgit教科書」からgitの内部構造を学ぶ 実践編その2

はじめに

前回に引き続き、エンジニアのgit教科書から中級編の内容をなぞっていきます。

git init —bare

正直説明見てもベアリポジトリの意味がよくわかりませんでした。ワーキングディレクトリを持たない、つまりファイル編集ができないリポジトリということですね。普段あまり意識しませんが、中央リポジトリ(gitlabやgithub)はbareリポジトリが望ましそうです。クライアント側でbareリポジトリを持つメリットは思いつきませんでした。

diffをとってみましょう。

> ~/g/git_study diff -r git_init git_init_bare/                            12:08:42
diff -r git_init/.git/config git_init_bare/.git/config
4,5c4
<  bare = false
<  logallrefupdates = true
---
>  bare = true

gitconfigの、bareかどうかの値と、logallrefupdatesが増えていますね。

manを見てみます。

       core.logAllRefUpdates
           Enable the reflog. Updates to a ref <ref> is logged to the file
           "$GIT_DIR/logs/<ref>", by appending the new and old SHA-1, the
           date/time and the reason of the update, but only when the file
           exists. If this configuration variable is set to true, missing
           "$GIT_DIR/logs/<ref>" file is automatically created for branch
           heads (i.e. under refs/heads/), remote refs (i.e. under
           refs/remotes/), note refs (i.e. under refs/notes/), and the
           symbolic ref HEAD.

           This information can be used to determine what commit was the
           tip of a branch "2 days ago".

           This value is true by default in a repository that has a working
           directory associated with it, and false by default in a bare
           repository.

ベアリポジトリではfalse、そうでない場合はtrueがデフォルトのようだ。というか、指定しない場合のデフォルトはfalseで、通常trueが入ってると思えばいいのかな。

reflogとはHEADの動きに関する履歴で、それを有効するオプションのようだ。bareリポジトリでは不要と判断されているんですね。

参考

git remote

正直常にcloneからはじまるのでremoteコマンドめったに使わないんですよね。remoteに追加してdiffをみてみましょう。

> ~/g/g/git_init on master ⨯ 
git remote add origin https://github.com/takeshe12/git_study.git
⋊> ~/g/g/git_init on master ⨯ diff -r .git_bak/ .git                        12:21:59
diff -r .git_bak/config .git/config
7a8,10
> [remote "origin"]
>  url = https://github.com/takeshe12/git_study.git
>  fetch = +refs/heads/*:refs/remotes/origin/*

はい、configにremoteが追加されましたね。

git push

それでは適当に変更を加えて、remoteにpushしてみましょう。push前とpush後でdiffを見てみます。

> ~/g/g/git_init on master ⨯ touch test                                    12:23:44> ~/g/g/git_init on master ⨯ git add test                                  12:23:46> ~/g/g/git_init on master ⨯ git commit -m "test commit"                   12:23:53
[master (root-commit) 8ac0e4f] test commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 test

コミットしたあと、pushします。

> ~/g/g/git_init on master  git push                                       12:24:07
fatal: The current branch master has no upstream branch.
To push the current branch and set the remote as upstream, use

    git push --set-upstream origin master

しかし、単にpushしようとすると怒られます。ローカルのmasterに対応するupstreamを指定しなさいよということです。

> ~/g/g/git_init on master  git push --set-upstream origin master          12:24:40
Counting objects: 3, done.
Writing objects: 100% (3/3), 206 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/takeshe12/git_study.git
 * [new branch]      master -> master
Branch master set up to track remote branch master from origin.

追跡ブランチが設定されました。

> ~/g/g/git_init on master ⨯ diff -r .git_bak/ .git/                       12:28:06
diff -r .git_bak/config .git/config
10a11,13
> [branch "master"]
>  remote = origin
>  merge = refs/heads/master
Only in .git/logs/refs: remotes
Only in .git/refs: remotes

configにbranch masterという項目が追加されました。refsにremotesが増えました。中を見てみましょう。

> ~/g/g/git_init on master ⨯ cat .git/refs/remotes/origin/master           12:30:24
9ac0f722fe53561aaf99a2858ab0c39d46e93477

remoteのrefが差しているのはcommitオブジェクトですね。

> ~/g/g/git_init on master ⨯ git cat-file -t 9ac0f7                        12:30:34
commit
⋊> ~/g/g/git_init on master ⨯ git cat-file -p 9ac0f7                        12:31:02
tree f05af273ba36fe5176e5eaab349661a56b3d27a0
author kondo takeshi <take.she12@gmail.com> 1502422008 +0900
committer kondo takeshi <take.she12@gmail.com> 1502422008 +0900

test commit

git clone

ではこのリモートのリポジトリをcloneしてみましょう。

> ~/g/git_study diff -r git_init/ git_study/                               12:33:27
Only in git_init/.git: COMMIT_EDITMSG
Binary files git_init/.git/index and git_study/.git/index differ
diff -r git_init/.git/logs/HEAD git_study/.git/logs/HEAD
1c1
< 0000000000000000000000000000000000000000 9ac0f722fe53561aaf99a2858ab0c39d46e93477 kondo takeshi <take.she12@gmail.com> 1502422008 +0900  commit (initial): test commit
---
> 0000000000000000000000000000000000000000 9ac0f722fe53561aaf99a2858ab0c39d46e93477 kondo takeshi <take.she12@gmail.com> 1502422351 +0900  clone: from https://github.com/takeshe12/git_study.git
diff -r git_init/.git/logs/refs/heads/master git_study/.git/logs/refs/heads/master
1c1
< 0000000000000000000000000000000000000000 9ac0f722fe53561aaf99a2858ab0c39d46e93477 kondo takeshi <take.she12@gmail.com> 1502422008 +0900  commit (initial): test commit
---
> 0000000000000000000000000000000000000000 9ac0f722fe53561aaf99a2858ab0c39d46e93477 kondo takeshi <take.she12@gmail.com> 1502422351 +0900  clone: from https://github.com/takeshe12/git_study.git
Only in git_study/.git/logs/refs/remotes/origin: HEAD
Only in git_init/.git/logs/refs/remotes/origin: master
Only in git_study/.git: packed-refs
Only in git_study/.git/refs/remotes/origin: HEAD
Only in git_init/.git/refs/remotes/origin: master

まったく同じかと思ったんですが結構違いますね。直前のCOMMITMESSAGEがない。HEADのreflogがcloneかそうでないかの違いがあります。

git tag

次はタグをつけてみます。

> ~/g/g/git_init on master ⨯ git tag -a "v1.0" -m "version 1.0"            12:36:54> ~/g/g/git_init on master ⨯ diff -r .git_bak/ .git/                       12:37:05
Only in .git/objects: b3
Only in .git/refs/tags: v1.0

タグオブジェクトが作られていますね。内容を確認しましょう。

> ~/g/g/git_init on master ⨯ 
git cat-file -t b3587da3d2f4854f53580867b013189b793323db 
tag
⋊> ~/g/g/git_init on master ⨯ 
git cat-file -p b3587da3d2f4854f53580867b013189b793323db 
object 
type commit
tag v1.0
tagger kondo takeshi <take.she12@gmail.com> 1502422625 +0900

version 1.0

tagオブジェクトはcommitオブジェクトを参照しています。最新コミットを参照していますね。

> ~/g/g/git_init on master ⨯ git cat-file -t 9ac0f722fe53561aaf99a2858ab0c39d46e93477                                14:58:15
commit
⋊> ~/g/g/git_init on master ⨯ git cat-file -p 9ac0f722fe53561aaf99a2858ab0c39d46e93477                                14:58:52
tree f05af273ba36fe5176e5eaab349661a56b3d27a0
author kondo takeshi <take.she12@gmail.com> 1502422008 +0900
committer kondo takeshi <take.she12@gmail.com> 1502422008 +0900

test commit

git branch

次はbranchの作成です。

> ~/g/g/git_init on master ⨯ git branch                                                                              15:14:17
* master
⋊> ~/g/g/git_init on master ⨯ git branch develop                                                                      15:14:19> ~/g/g/git_init on master ⨯ git branch                                                                              15:14:25
  develop
* master
⋊> ~/g/g/git_init on master ⨯ diff -r .git_bak/ .git/                                                                 15:14:28
Only in .git/logs/refs/heads: develop
Only in .git/refs/heads: develop

refsとlogsにdevelopが作成されています。

> ~/g/g/git_init on master ⨯ cat .git/logs/refs/heads/develop                                                        15:14:33
0000000000000000000000000000000000000000 9ac0f722fe53561aaf99a2858ab0c39d46e93477 kondo takeshi <take.she12@gmail.com> 1502432065 +0900 branch: Created from master

logsにはmasterからbranchされたことが記載されています。

> ~/g/g/git_init on master ⨯ cat .git/refs/heads/develop                                                             15:15:45
9ac0f722fe53561aaf99a2858ab0c39d46e93477

refsにはコミットオブジェクトが参照されていますね。

つまりブランチの実態はコミットへの参照ということがわかりました。

git checkout

developブランチにcheckoutしてみましょう。

> ~/g/g/git_init on master ⨯ git checkout develop                                                                    15:18:19
Switched to branch 'develop'> ~/g/g/git_init on develop ⨯ diff -r .git_bak/ .git/                                                                15:18:24
diff -r .git_bak/HEAD .git/HEAD
1c1
< ref: refs/heads/master
---
> ref: refs/heads/develop
diff -r .git_bak/logs/HEAD .git/logs/HEAD
1a2
> 9ac0f722fe53561aaf99a2858ab0c39d46e93477 9ac0f722fe53561aaf99a2858ab0c39d46e93477 kondo takeshi <take.she12@gmail.com> 1502432304 +0900   checkout: moving from master to develop

HEADの参照がdevelopに変わりました。logにcheckoutしたlogが増えていますね。

reflogで確認できます。

> ~/g/g/git_init on develop ⨯ git reflog                                                                             15:18:30
9ac0f72 HEAD@{0}: checkout: moving from master to develop
9ac0f72 HEAD@{1}: commit (initial): test commit

git mv

ファイル名を変更します。

> ~/g/g/git_init on develop ⨯ git mv test mv_test                                                                    15:24:01> ~/g/g/git_init on develop ⨯ diff -r .git_bak/ .git/                                                                15:24:08
Binary files .git_bak/index and .git/index differ

indexが変わっただけでした。

これをコミットしてみましょう。

> ~/g/g/git_init on develop ⨯ git commit -m "mv test"                                                                15:25:39
[develop 0e6acf4] mv test
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename test => mv_test (100%)> ~/g/g/git_init on develop ⨯ diff -r .git_bak/ .git/                                                                15:25:50
diff -r .git_bak/COMMIT_EDITMSG .git/COMMIT_EDITMSG
1c1
< test commit
---
> mv test
Binary files .git_bak/index and .git/index differ
diff -r .git_bak/logs/HEAD .git/logs/HEAD
2a3
> 9ac0f722fe53561aaf99a2858ab0c39d46e93477 0e6acf4abacc6914cb47e1b787a47cd0a7f76e73 kondo takeshi <take.she12@gmail.com> 1502432750 +0900   commit: mv test
diff -r .git_bak/logs/refs/heads/develop .git/logs/refs/heads/develop
1a2
> 9ac0f722fe53561aaf99a2858ab0c39d46e93477 0e6acf4abacc6914cb47e1b787a47cd0a7f76e73 kondo takeshi <take.she12@gmail.com> 1502432750 +0900   commit: mv test
Only in .git/objects: 0e
Only in .git/objects: 2b
diff -r .git_bak/refs/heads/develop .git/refs/heads/develop
1c1
< 9ac0f722fe53561aaf99a2858ab0c39d46e93477
---
> 0e6acf4abacc6914cb47e1b787a47cd0a7f76e73

直近のコミットメッセージであるCOMMITMASSAGEは当然変わりますね。また、logも追加されました。新しく2つオブジェクトが追加されています。

1つはコミットオブジェクトです。

> ~/g/g/git_init on develop ⨯ git cat-file -t 0e6acf                                                                 15:27:30
commit
⋊> ~/g/g/git_init on develop ⨯ git cat-file -p 0e6acf                                                                 15:27:46
tree 2b78e1ae5a68db3ca8755f185b87ac5cf8b47a85
parent 9ac0f722fe53561aaf99a2858ab0c39d46e93477
author kondo takeshi <take.she12@gmail.com> 1502432750 +0900
committer kondo takeshi <take.she12@gmail.com> 1502432750 +0900

mv test

もう1つはtreeオブジェクトです。

> ~/g/g/git_init on develop ⨯ git cat-file -t 2b78e1                                                                 15:27:48
tree
⋊> ~/g/g/git_init on develop ⨯ git cat-file -p 2b78e1                                                                 15:28:27
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391   mv_test

今回はファイルないように変更はないのでblobオブジェクトは変化がありません。treeオブジェクトとcommitオブジェクトでmvを表現していますね。

ちなみにコミットオブジェクトがもつparentは何を差しているのでしょうか。

> ~/g/g/git_init on develop ⨯ git cat-file -t 9ac0f7                                                                 15:30:28
commit
⋊> ~/g/g/git_init on develop ⨯ git cat-file -p 9ac0f7                                                                 15:30:32
tree f05af273ba36fe5176e5eaab349661a56b3d27a0
author kondo takeshi <take.she12@gmail.com> 1502422008 +0900
committer kondo takeshi <take.she12@gmail.com> 1502422008 +0900

test commit

コミットオブジェクトです。1つ前のコミットをparentとして差しているんですね。

> ~/g/g/git_init on develop ⨯ git log                                                                                15:30:35
commit 0e6acf4abacc6914cb47e1b787a47cd0a7f76e73
Author: kondo takeshi <take.she12@gmail.com>
Date:   Fri Aug 11 15:25:50 2017 +0900

    mv test

commit 9ac0f722fe53561aaf99a2858ab0c39d46e93477
Author: kondo takeshi <take.she12@gmail.com>
Date:   Fri Aug 11 12:26:48 2017 +0900

    test commit

ちなみにファイルに変更があった場合は新しいblobオブジェクトが作成され、参照されることになります。

git merge

developブランチでは最初のコミットのあと、mvと、内容を修正したコミットがあります。

> ~/g/g/git_init on develop ⨯ git log --oneline                                                                      15:35:10
8008476 change test file
0e6acf4 mv test
9ac0f72 test commit

masterにcheckoutしてみましょう。

> ~/g/g/git_init on develop ⨯ git checkout master                                                                    15:35:15
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
⋊> ~/g/g/git_init on master ⨯ git log --oneline                                                                       15:36:14
9ac0f72 test commit

masterは最初のコミットのみです。この状態でmergeして、差分を見てみましょう。

> ~/g/g/git_init on master ⨯ git merge develop                                                                       15:36:48
Updating 9ac0f72..8008476
Fast-forward
 mv_test | 1 +
 test    | 0
 2 files changed, 1 insertion(+)
 create mode 100644 mv_test
 delete mode 100644 test> ~/g/g/git_init on master ⨯ diff -r .git_bak/ .git/                                                                 15:37:21
Only in .git/: ORIG_HEAD
Binary files .git_bak/index and .git/index differ
diff -r .git_bak/logs/HEAD .git/logs/HEAD
5a6
> 9ac0f722fe53561aaf99a2858ab0c39d46e93477 80084768599bd0852fa0bb1ff18e3ccd138c2721 kondo takeshi <take.she12@gmail.com> 1502433441 +0900   merge develop: Fast-forward
diff -r .git_bak/logs/refs/heads/master .git/logs/refs/heads/master
1a2
> 9ac0f722fe53561aaf99a2858ab0c39d46e93477 80084768599bd0852fa0bb1ff18e3ccd138c2721 kondo takeshi <take.she12@gmail.com> 1502433441 +0900   merge develop: Fast-forward
diff -r .git_bak/refs/heads/master .git/refs/heads/master
1c1
< 9ac0f722fe53561aaf99a2858ab0c39d46e93477
---
> 80084768599bd0852fa0bb1ff18e3ccd138c2721

⋊> ~/g/g/git_init on master ⨯ git log --oneline                                                                       15:37:30
8008476 change test file
0e6acf4 mv test
9ac0f72 test commit

ORIG_HEADというファイルが作成されました。その名の通り、1つ前にHEADが差していたコミットを差しています。

> ~/g/g/git_init on master ⨯ cat .git/ORIG_HEAD                                                                      15:38:10
9ac0f722fe53561aaf99a2858ab0c39d46e93477
⋊> ~/g/g/git_init on master ⨯ git log --oneline                                                                       15:41:02
8008476 change test file
0e6acf4 mv test
9ac0f72 test commit

ところで今回はmergeコミットが作成されていません。fast-forwardとかいてあるとおり、元のmasterとdevelopで差分がないので、単純に先送りする形でmergeが行われたようです。

参考:ブランチの統合【ブランチ】 | サルでもわかるGit入門 〜バージョン管理を使いこなそう〜 | どこでもプロジェクト管理バックログ

では分岐するパターンを見てみましょう。master、developそれぞれに別のコミットを行いました。

> ~/g/g/git_init on master ⨯ git log --oneline                                                                       15:43:11
e7095b5 add test2 to master
8008476 change test file
0e6acf4 mv test
9ac0f72 test commit

⋊> ~/g/g/git_init on develop ⨯ git log --oneline                                                                      15:43:55
78b981a update mv_test to develop
8008476 change test file
0e6acf4 mv test
9ac0f72 test commit

mergeしてみましょう。

> ~/g/g/git_init on master ⨯ git merge develop                                                                       15:48:20
Merge made by the 'recursive' strategy.
 mv_test | 1 +
 1 file changed, 1 insertion(+)

mergeコミットが作成されたので、メッセージ入力を促されます。

  1 Merge branch 'develop'
  2 
  3 # Please enter a commit message to explain why this merge is necessary,
  4 # especially if it merges an updated upstream into a topic branch.
  5 #
  6 # Lines starting with '#' will be ignored, and an empty message aborts
  7 # the commit.
~                   

今回はrecursive戦略が取られたようです。gitのマージ戦略はあとで取り上げるとして、差分を見ていきましょう。

> ~/g/g/git_init on master ⨯ diff -r .git_bak/ .git/                                                                 16:09:51
diff -r .git_bak/ORIG_HEAD .git/ORIG_HEAD
1c1
< 9ac0f722fe53561aaf99a2858ab0c39d46e93477
---
> e7095b5a3636bb36f8bd60680832fe35213dfb19
Binary files .git_bak/index and .git/index differ
diff -r .git_bak/logs/HEAD .git/logs/HEAD
10a11
> e7095b5a3636bb36f8bd60680832fe35213dfb19 ec6a338ce5cb17538e274e1087a2aec6d4c34019 kondo takeshi <take.she12@gmail.com> 1502434106 +0900   merge develop: Merge made by the 'recursive' strategy.
diff -r .git_bak/logs/refs/heads/master .git/logs/refs/heads/master
3a4
> e7095b5a3636bb36f8bd60680832fe35213dfb19 ec6a338ce5cb17538e274e1087a2aec6d4c34019 kondo takeshi <take.she12@gmail.com> 1502434106 +0900   merge develop: Merge made by the 'recursive' strategy.
Only in .git/objects/e2: a1fac55638da3adc5c1a1ef0401300415ced02
Only in .git/objects: ec
diff -r .git_bak/refs/heads/master .git/refs/heads/master
1c1
< e7095b5a3636bb36f8bd60680832fe35213dfb19
---
> ec6a338ce5cb17538e274e1087a2aec6d4c34019

refsはlogの変更はいいとして、2つのオブジェクトが作られています。

> ~/g/g/git_init on master ⨯ git cat-file -t e2a1fa                                                                  16:10:56
tree
⋊> ~/g/g/git_init on master ⨯ git cat-file -p e2a1fa                                                                  16:11:15
100644 blob 884e2cc3e9f6fb0d9df2a316041914095f7012e9   mv_test
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391   test2
⋊> ~/g/g/git_init on master ⨯ git cat-file -t ec6a33                                                                  16:11:17
commit
⋊> ~/g/g/git_init on master ⨯ git cat-file -p ec6a33                                                                  16:11:55
tree e2a1fac55638da3adc5c1a1ef0401300415ced02
parent e7095b5a3636bb36f8bd60680832fe35213dfb19
parent 78b981af7038583efb9b3e19016af18eff702a27
author kondo takeshi <take.she12@gmail.com> 1502434106 +0900
committer kondo takeshi <take.she12@gmail.com> 1502434106 +0900

Merge branch 'develop'

treeオブジェクトとcommitオブジェクトです。マージコミットはparentが2つありますね。2つの親から合流していることを示すのがマージコミットということがわかりました。

merge戦略

recursibleとfast-forwardの戦略がありましたが、他には何があるのでしょうか。

https://git-scm.com/docs/merge-strategies

octopus、ours、subtreeがあり、recursiveがdefaultとありますね。

octopus

This resolves cases with more than two heads, but refuses to do a complex merge that needs manual resolution. It is primarily meant to be used for bundling topic branch heads together. This is the default merge strategy when pulling or merging more than one branch.

2つ以上のHEADをマージする戦略。ただし、手作業でのコンフリクト解決はできない。1つ以上のブランチをpullまたはmergeするときのデフォルト戦略である。

ours

This resolves any number of heads, but the resulting tree of the merge is always that of the current branch head, effectively ignoring all changes from all other branches. It is meant to be used to supersede old development history of side branches. Note that this is different from the -Xours option to the ‘recursive’ merge strategy.

ほかのブランチの変更を効果的に無視し、現在のブランチの変更だけを採用する戦略。コンフリクトしたときに片方だけを採用したい場合に使いそうですね。

subtree

This is a modified recursive strategy. When merging trees A and B, if B corresponds to a subtree of A, B is first adjusted to match the tree structure of A, instead of reading the trees at the same level. This adjustment is also done to the common ancestor tree.

ブランチAに対してBが単純なサブツリー、つまりマージ元に新規コミットがない場合は単に遡って同じコミットに合わせる戦略。これは前述のfast-forwardのことのようですね。

git rebase

さぁ、最後rebaseです。初心者には一番気味悪がられるコマンドですが、結構便利なので一度覚えるとかなり使うようになりますね。主にコミットを1つにまとめるときに使っています。

ここではmergeの手段としてのrebaseを見ていきましょう。

現在のグラフは以下のようになっています。

* master
⋊> ~/g/g/git_init on master ⨯ git log --graph --oneline                                                               16:26:38
*   ec6a338 Merge branch 'develop'
|\  
| * 78b981a update mv_test to develop
* | e7095b5 add test2 to master
|/  
* 8008476 change test file
* 0e6acf4 mv test
* 9ac0f72 test commit

この状態で、再度枝分かれを作成しましょう。まずmasterに1コミット。

> ~/g/g/git_init on master ⨯ git commit -m "update to master"                                                        16:27:28
[master 288cf43] update to master
 1 file changed, 1 insertion(+)

次にdevelopへ1コミット

> ~/g/g/git_init on master ⨯ git checkout develop                                                                    16:27:32
Switched to branch 'develop'> ~/g/g/git_init on develop ⨯ git commit -m "update to develop"                                                      16:27:48
[develop a859860] update to develop
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 rebase_test.txt

developブランチで、masterに向けてrebaseを実行します。

> ~/g/g/git_init on develop ⨯ git rebase master                                                                      16:30:07
First, rewinding head to replay your work on top of it...
Applying: update to develop

この状態で差分を見てみましょう。

> ~/g/g/git_init on develop ⨯ git log --graph --oneline                                                              16:30:56
* 89748b7 update to develop
* 288cf43 update to master
*   ec6a338 Merge branch 'develop'
|\  
| * 78b981a update mv_test to develop
* | e7095b5 add test2 to master
|/  
* 8008476 change test file
* 0e6acf4 mv test
* 9ac0f72 test commit

コミットがまっすぐ並びました。

diffをとると、新たに2つのオブジェクトが作成されています。

Only in .git/objects: 89
Only in .git/objects: dc

コミットオブジェクトとツリーオブジェクトです。

> ~/g/g/git_init on develop ⨯ git cat-file -t 89748b                                                                 16:34:29
commit
⋊> ~/g/g/git_init on develop ⨯ git cat-file -p 89748b                                                                 16:34:33
tree dc568b43edd0bb751256a65e3bcfa7dbd03b97ba
parent 288cf4374fad6791ae744c5ac4ef57eec803ff7c
author kondo takeshi <take.she12@gmail.com> 1502436476 +0900
committer kondo takeshi <take.she12@gmail.com> 1502436614 +0900

update to develop

おっと、このコミットは以前はこちらのコミットでした。

> ~/g/g/git_init on develop ⨯ git cat-file -p a859860                                                                16:35:10
tree 7d8604b49710a922ff3e58adb0c6c5f185ccc013
parent 78b981af7038583efb9b3e19016af18eff702a27
author kondo takeshi <take.she12@gmail.com> 1502436476 +0900
committer kondo takeshi <take.she12@gmail.com> 1502436476 +0900

update to develop

rebaseすると新しくコミットが生成されるんですね。parentとtreeが更新されています。

ここまで書いて気づきましたけど、master上でdevelopをrebaseすべきでしたね。

おわりに

branch作成からrebaseまで、一通りのgitコマンドの動きを追いかけることができました。かなりボリューミー。感想としては「だいたい参照のリレー」だなーってところです。うまくできてるなと思いました。

「エンジニアのためのgit教科書」からgitの内部構造を学ぶ 実践編その1

はじめに

読みました。

もうgitがないと仕事にならない生活をしています。仕事のすべてはgitに預けた、ここにないものは何もない、というスタイルで仕事しているのでチームメンバーに対しての情報開示が非常に楽ですし、差分管理、つまりdiffが容易に確認できて、save / resetが簡単にできるが気持ちよすぎて、何でもgit以下においちゃいますね。でもバイナリはダメですよ。

で、今でこそ一通りのコマンドは使いこなせるようになりましたが、まだまだ周囲にはgitの使い方がわかってないひとも多く、チーム内ではわりとgitがわかるほうになってきました。

せっかくなのでもう一歩深く学んでgitマスターになって、勉強会開こう!ということで、何事も知るには内部構造。.git以下の動きを解説してくれるこの本を読みました。

この本、かなり良いです。ていねいに、各コマンドで何が生成され、中身がどうなっているのかを、実際に動かしながら示してくれます。基本オブジェクトの解説から入り、各コマンドの内部の動き、最後には低レベルのgitコマンドを使ってのgit commit / add のshell実装までします。本書の内容を実際に動かし、内容を理解できたら1段階レベルがあがるでしょう。

とはいえ、動かしながらだと内容も大きくなるので2回に分けて実践していきます。

git init

git initしたら何が起きるの?

適当なディレクトリを作って試してみましょう。

take@MacBook-Air ~/g/git_study> mkdir git_init_test
take@MacBook-Air ~/g/git_study> cd git_init_test/
take@MacBook-Air ~/g/g/git_init_test> git init
Initialized empty Git repository in /Users/take/github/git_study/git_init_test/.git/
take@MacBook-Air ~/g/g/git_init_test> tree .git/
.git/
├── HEAD
├── config
├── description
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   ├── prepare-commit-msg.sample
│   └── update.sample
├── info
│   └── exclude
├── objects
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags

8 directories, 14 files

HEAD,config,descriptionの3ファイルに、hooks,info,objects,refsのディレクトリが作成されます。

HEAD

headファイルは現在のブランチを示すシンボリック参照情報を保存しています。

take@MacBook-Air ~/g/g/git_init_test> cat .git/HEAD 
ref: refs/heads/master

config

git configで設定できる、gitのconfigファイルです。これはリポジトリ固有設定になります。システム固有設定(–system)は/etc/gitconfig、ユーザ固有設定(–global)は~/.gitconfig になります。

take@MacBook-Air ~/g/g/git_init_test> cat .git/config 
[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
    ignorecase = true
    precomposeunicode = true

参考:Git - Git の設定

詳細はman git-configで見るとして、ここではconfig内容の解説は行いません。

description

take@MacBook-Air ~/g/g/git_init_test> cat .git/description 
Unnamed repository; edit this file 'description' to name the repository.

リポジトリのdescriptionです。githubではここの値は読まないようですが、gitlabはどうなんだろうか。会社行ったら確認します。

hooks

ここ以下はgitが用意しているhookのsampleが置かれています。これらの拡張子を外して編集すれば動かすことができます。デフォルトのコミットメッセージを用意したり、コミット規約を守らせるためにshellでスクリプトが書けます。

take@MacBook-Air ~/g/g/git_init_test> ls .git/hooks/
applypatch-msg.sample     post-update.sample        pre-commit.sample         pre-rebase.sample         prepare-commit-msg.sample
commit-msg.sample         pre-applypatch.sample     pre-push.sample           pre-receive.sample        update.sample

info/exclude

take@MacBook-Air ~/g/g/git_init_test> cat .git/info/exclude 
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~

除外ファイルパターンを記載することができます。なお.gitignoreは除外ディレクトリパターンと記載していますが。。。.gitignoreでも除外ファイル記載可能なので、大きな違いは除外パターンを共有したいかどうか、ではないでしょうか。自分だけ除外したい場合はここのinfo/excludeに記載するといいでしょう。(.gitはgitの管理対象ではないでしょうから)

参考:Git | excludeファイルにローカル環境だけ無視したいファイルを登録 - Tbpgr Blog

object

gitはオブジェクトという単位で情報を持ちます。objectには

  • blob … ファイルのデータを保持
  • tree … blobへの参照と、1階層分のディレクトリ情報
  • commit … 変更時のメタデータ。treeへの参照。

の3種類があります。

refs

ブランチの参照とタグオブジェクトを格納する場所です。

git addしてみる

では、git addしたときどのように変化するか見てみましょう。

take@MacBook-Air ~/g/g/git_init_test> echo "git test" > test.txt
take@MacBook-Air ~/g/g/git_init_test> git add test.txt 

実行前の.gitディレクトリをコピーしておいたので、diffをとってみます。

take@MacBook-Air ~/g/g/git_init_test> diff -r .git_before/ .git
Only in .git: index
Only in .git/objects: f6

indexファイルと、objectが増えたようですね。

index

indexファイルはindex情報、どのファイルが追跡状態にあるかを保存してます。しかし、バイナリファイルなので中身を読むことはできません。

stageしているファイルは以下で確認できます。git statusでも確認できますね。

take@MacBook-Air ~/g/g/git_init_test> git ls-files --stage
100644 f6edd6e7a290f009aa685d3acd3153b495a69ea8 0 test.txt

brob object

brobオブジェクトが作成されました。git cat-fileで見てみましょう。

take@MacBook-Air ~/g/g/git_init_test> git cat-file -t f6edd6e7a290f009aa685d3acd3153b495a69ea8 
blob
take@MacBook-Air ~/g/g/git_init_test> git cat-file -p f6edd6e7a290f009aa685d3acd3153b495a69ea8 
git test

この16進数の数字はSHA1のhashです。

take@MacBook-Air ~/g/g/git_init_test> openssl sha1 test.txt
SHA1(test.txt)= 06aaaf302f1370f12298c6cbf7e436c8a6bdef05

確認できましたね。brobの内容のハッシュがobject名になっています。かつ、先頭2文字でディレクトリを分けていますね。名前空間をわけて処理をいい感じにしてるんでしょうね。

take@MacBook-Air ~/g/g/git_init_test> tree .git/objects/
.git/objects/
├── f6
│   └── edd6e7a290f009aa685d3acd3153b495a69ea8
├── info
└── pack

3 directories, 1 file

git commitしてみる

さて、次はcommitをしてみましょう。同じように現在の.gitをコピーしておいて、diffをとってみようと思います。

take@MacBook-Air ~/g/g/git_init_test> git commit -m "test commit"
[master (root-commit) 379e87a] test commit
 1 file changed, 1 insertion(+)
 create mode 100644 test.txt
take@MacBook-Air ~/g/g/git_init_test> diff -r .git_before/ .git
Only in .git: COMMIT_EDITMSG
Binary files .git_before/index and .git/index differ
Only in .git: logs
Only in .git/objects: 37
Only in .git/objects: 5e
Only in .git/refs/heads: master

indexファイルが変わり、COMMIT_EDITMSG、logs、そしてobjectが2つ増えました。refs以下にheadsが増えました。

COMMIT_EDITMSG

直前のcommitメッセージが保存されています。–amendで直前のみ変更されるのはこのためなんでしょうね。

take@MacBook-Air ~/g/g/git_init_test> cat .git/COMMIT_EDITMSG 
test commit

logs

HEADの参照情報です。直前のコミットがないので0000… になっていますね。

take@MacBook-Air ~/g/g/git_init_test> cat .git/logs/HEAD 
0000000000000000000000000000000000000000 379e87a44b39250d5717de286499cd1c52f9d623 kondo takeshi <take.she12@gmail.com> 1502256942 +0900 commit (initial): test commit

reflogで同等の情報を得られます。

take@MacBook-Air ~/g/g/git_init_test> git reflog
379e87a HEAD@{0}: commit (initial): test commit

commit object

ではHEADが参照している379e87aのオブジェクトを見てみましょう。

take@MacBook-Air ~/g/g/git_init_test> git cat-file -t 379e87
commit
take@MacBook-Air ~/g/g/git_init_test> git cat-file -p 379e87
tree 5ed0c0c091b633152a962cfd92f538204ae2847c
author kondo takeshi <take.she12@gmail.com> 1502256942 +0900
committer kondo takeshi <take.she12@gmail.com> 1502256942 +0900

test commit

これがメタデータを保存しているcommitオブジェクトですね。そしてcommitオブジェクトはtreeオブジェクトを参照しています。

なお、これもコミット内容のsha1ハッシュなはずだが、単純に内容をhashかけただけではだめだった。

これはcommitという文字と、commitオブジェクトの中身のバイト数、そしてコミットオブジェクトの中身を合わせているらしいが、なぜか再現できず。。。

参考:Gitのコミットハッシュ値は何を元にどうやって生成されているのか - Mercari Engineering Blog

tree object

take@MacBook-Air ~/g/g/git_init_test> git cat-file -t 5ed0c0
tree
take@MacBook-Air ~/g/g/git_init_test> git cat-file -p 5ed0c0
100644 blob f6edd6e7a290f009aa685d3acd3153b495a69ea8   test.txt

treeオブジェクトはファイルモードと、blobオブジェクトの参照を持っています。

おわりに

エンジニアのためのgit教科書の初級編相当をなぞってみました。.git以下にあるファイルの種類、3つのオブジェクトの内容と関係、git add/commit時の動きを学びました。

次回はより多くのコマンドについて挙動を追いかけてみます。

8月の目標

はじめに

年始に毎月の目標を立てることを年の目標にしましたが見事にダメでした。定期的に自分の向いている方向と、今いる場所を確認することは大切かつ困難だということがわかりますね。

最後に書いたのは5月の目標。関心領域はさほど変わっていませんね。

take-she12.hatenablog.com

8月の目標

もうシンプルに行こう。

  • 猫の休日ミュージックビデオ公開
  • 猫の休日オリジナル2曲提出
  • とけてなくなる次回作の作詞・作曲(計4 or 5曲)

以上!!!本読むとか走るとか泳ぐとか筋トレするとかは日常に組み込んでやっていけばいい。数字と結果が出るものだけを目標にしよう。