ツナワタリマイライフ

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

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

はじめに

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

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

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

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

競争を避けること

ただ、首尾一貫していたのは、「競争を避けて、好きなことをする」ということ。結果的に、これが自分という商品の価値を高めることにつながった(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曲)

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

「コード進行を覚える方法と耳コピ&作曲のコツ」を読んだ

はじめに

音楽系の本を急に数冊買ったうちの1つ。

この本は第1章「コード進行を覚える方法」そして第2章「コード進行を耳コピするコツ」3章「コード進行作曲のコツ」の3構成。

この本が目指しているのは

生まれ持った才能がなくても、楽器を始めてまだ数年の人でも、譜面なしでコードを弾ける人になることは可能です。「あの曲」弾いてと言われて、譜面もないのにスラスラと弾き始める人ってうらやましくないですか?あれは才能を持っている人だけができることだと思っていませんか?「私には無理」と思い込んでいませんか?いいえ、そんなことはありません。本書では、「譜面なしでコードを弾ける人になる」ための方法を紹介します。(はじめに より)

とあります。

つまりこの本は、ある曲のコード進行を「永続的に」暗記するコツと、さらに耳コピで既存曲のコード進行を得る方法を紹介し、この2つによって「あぁあの曲ね」と急に弾き語りができるようになる!と言ってるわけですね。なりたい!

まぁ僕にいたっては直近のスタジオで合わせる課題曲のコードですら覚えきれないわけで、何かしら得るものはある、はず、です。

コード進行を覚えるコツ

前提知識(音程と度数とか音階とダイアトニックスケールとか)は知っていたので難なく読めました。

まぁ1ページで見られるような譜面を作ることも普段からしています。

いいなと思ったのは

  • 指板上のルートの動きを(視覚的に)覚える

ことぐらいですかね。

キーを判別して、コードを数字のコードにする。キーごとのダイアトニックコードを(覚えるのではなく)算出する。

ただ、どうやって忘れないでいられるかについては「継続して弾く」ってあって、そっかぁ。。。となりました。(笑)

コード進行を耳コピするコツ

僕もようやくコード進行を耳コピ、ならぬ、あてコピ?できるようになりました。作曲少女のおかげなんですが

作曲少女~平凡な私が14日間で曲を作れるようになった話~

作曲少女~平凡な私が14日間で曲を作れるようになった話~

今僕が確立している方法は

  • メロディを聞き取る
  • 使われている音からキーを判別する
  • キーからダイアトニックコードを判別する
  • ベースを聞き取る
  • ベースからダイアトニックコードをあてはめる
  • だいたい完成!

という方法です。まぁこれでだいたいうまくいきます。

この本ではコードを聞き取る上で、「単音を探すコツを知る」とあるんですが、僕レベルに耳が肥えてないとコードじゃジャーンでしかないので、ルートすらあやしいですね。ルートか、1番高い音かがかろうじて認識できるぐらい。耳コピのときは低音をブーストしてベース音を聞き取ってるのでギターのコードを聞き取れと言われてもさっぱりです。。。

ただ、1つだけ気になったのが「 (キーを)Cでコピーするくせをつける」ことなんですよね。

耳コピが得意な友人が昔「ドのひと」と称して、全部1度Cでとらえてから変換する、と言っていて、あぁ同じことを言っているなぁと思いました。

ただ、全部Cで聴くには、今のキーを判別した上で、原曲のキーを上げ下げさせないといけないので、現実的な手法として難しいと思いました。音源を上下させるんじゃなくて、カポを使って上下させればいいのかな。

Cでとらえて、数字コードにして、コード進行の流れを肌で感じるプロセスが大事そうなのは本書を読んでなんとなくわかりました。

ただ、ダイアトニックコードを暗記せずに自分で出す方法で、どうしても以下の意味がわかりませんでした。

  • GキーがFだけ#、FキーがBだけ♭と覚えましょう
  • 以下の図のパターンを覚えましょう

f:id:take_she12:20170801234256j:plain:w300

むむむ?動きはわかったけど、実際に何が#になっていくのかを譜面を数えて追ってみましょう。

Cキーは#なし、GキーはFだけ#、DキーはFとCだけ#、AキーはFとCとGだけ#

おぉ!この順番で増えていくってことか!なるほど理解。

あとはよくある進行パターンが紹介されていました。

おわりに

この本では「指の動きから覚えること」「ダイアトニックコードを暗記せずに出す方法」を学びました。そして、すべてのキーをいったんCで解釈して、数字コードで理解してから原曲キーに戻すほうが良いことも知りました。

理論もまったく知らず、コードブック片手に見ながらコピーしてるひとに対しては、音楽理論の解説を平易にとどめていて、自分で耳コピして自分で弾き語るための第一歩となる良い本だと思いました。

【批評】「思考力を鍛える30の習慣」

はじめに

友人の勧めで読んだ。

ポール・スローンの思考力を鍛える30の習慣

ポール・スローンの思考力を鍛える30の習慣

友人はコンサルをやっているので、僕の想像をはるかに超える量の思考を働かせないといけないのだろう。

対象読者

この本の概要を確認しておこう。

この本は「思考法の総合的なガイドブック」である。しかも、

  • 思考法の本によく見られる欠点は避けられている
  • 理論面もしっかりしている
  • 実際的なアドバイスの面も充実している
  • 思考の達人たちの成功実例がふんだんに紹介されている

とある。

そして、「世界には急いで解決しなくてはならない課題が山のように」あり、これらの課題に対する新しい解決策を打ち出すために、本書に書かれている方法が役にたつと述べている。

つまり、思考力とは、日常やビジネス上、あるいは世界平和(?)のために解決しなければならない困難な課題に対して、解決策を提示するための発想をひねり出す力のことだと言えるだろう。

私のスタンス

まず、私はこの「思考力」という言葉にそもそも懐疑的である。上記で定義した発想を提示する力が「思考力」と名付けられることにあまり腑に落ちていないのだ。

これは命名が自分にとって気にくわないというだけで大した問題ではないが、そのせいで本書もはじめから疑いの目で見てしまった。

近年の何でも〜力と名付けるムードに嫌気が刺しているのもある。はじめからこういう見方をしているので、本書に対して批判的な目線で読んでいったので、今後もその態度で説明していくであろうことを先に断っておく。

30の習慣

まずこの30の習慣をざっとながめよう。

  1. なぜ考えかたを変えるのか
  2. 反対のことを考える
  3. 思い込みと向き合う
  4. 問題を分析する
  5. 問う
  6. 組み合わせる
  7. 並行思考
  8. 想像的に考える
  9. 水平的に考える
  10. ほかの人が考えないことを考える
  11. イデアの評価をする
  12. むずかしい判断を下す
  13. 言葉で考える力を伸ばす
  14. 数学的に考える
  15. 確率を理解する
  16. 視覚的に考える
  17. 感情知能を伸ばす
  18. 会話の達人になろう
  19. 議論に勝つ
  20. じっくり考える
  21. 記憶力を最大限に高める 22.実験し、失敗し、学習する
  22. 物語の力を使う
  23. ユーモアを交える
  24. ポジティブに考える
  25. 目標を書いて実現をめざす
  26. 優先順位を決め、的を絞る
  27. 考えを行動に移す
  28. よくあるまちがいを避ける
  29. 脳を強化する

もう一度いうが、これは「思考力を鍛える30の習慣」である。

ぼくが1番嫌いというか、それこそ思考力(1つの物事、あるいは問いに対して自分で考える力)が阻害されるのは、列挙に対しての粒度がバラバラなことである。

まずこれを眺めただけでも、15は14に包含される。そして15の確率の話は29のよくある間違いを避けると同じだ。(思い込みに対して確率を検証すべきという立場)5の問うはほぼ全ての項目にあてはまるだろう

さらに考える力の習慣なのに「じっくり考える」とある。確かに考えた後一時的に別のことをすることが有益なのはすでに明らかになっているが、他は考えるための別の視点を提案したり、陥りがちな思考の癖への警告だったりするのに、これだけ考える態度になっている。

なんなら1つめは習慣なのに「なぜ考え方を変えるのか」という前提の問いになっている。習慣じゃないのか。

つまり、30項目は「習慣」で並列になっていないので、シーンごとにカテゴリわけしてほしい。

というか、無理やり30にしたんじゃないか、とすら思う。このように章立てが雑だと、ツッコミに頭がまわってしまって「結局この本は何が書いてあったのか」の把握がしづらい。

冒頭で示したように最初から批判的に読んでしまっているので最後までこの態度を覆すことができなかった。

この本が示したこと

ただケチつけるだけではダメなので、代案を自分なりに考えてみる。

この本は大きく以下のことを示している。

ひとの思考には癖があるので、それにハマらないようにすべき

  • 2 反対のことを考える - 私たちは自分たちが考えたいように考える
  • 3 思い込みと向き合う - 同上
  • 29 思い込みを避ける - バイアスとか

テクニックとしての思考法

  • 4 問題と向き合う - 「なぜなぜ法」「5W1H」「蓮の花」(調べても出ない)
  • 6 組み合わせる
  • 7 並行思考 - 立場、態度が異なるポジションを並行に交代で実施し、冷静に議論を行う
  • 9 水平思考 - 支配的な考え方をはずして考える。3に注意し、それを除いて考える方法
  • 11 アイデアの評価する
  • 12 むずかしい判断をする - 題は意味不明だが、中で扱うペア式順位法は使えるテクニックだろう。
  • 21 記憶力を鍛える - 思考法との関係が不明だが、記憶力を高めるテクニック。

基礎力を鍛える系

  • 14 数学力を鍛える - 数学の教養ぐらい身につけてねと言ってるだけ
  • 15 確率を理解する - 確率の教養ぐらい身につけてねと言ってるだけ
  • 17 感情知能を伸ばす - いきなりemotional intelligenceの話が出てくる。思考力との関係は明記されていない。

行動習慣

  • 13 言葉で考える力を増やす - 読書、知らない言葉を辞書で調べる。書く、言葉で遊ぶ、など。言語能力を鍛える、と言ってもいいかもしれない。これは習慣っぽい。
  • 16 視覚的に考える - 技術とは言えない気がする。マインドマップは手法だけど。図示する癖をつけようね、であれば習慣っぽい。
  • 18 会話の達人になろう - 意味不明。質問をする、聞く、褒めるなど。思考力との関係が不明。
  • 22 実験し、失敗し、学習する - 新しいことに取り組めと言っている。大事だと思うし、習慣っぽい。

不要なもの

  • 5 問う - 思い込みと向き合うためには問わないといけないし、なぜなぜ?も問うている。章を立てる必要がない。重複。
  • 8 創造的に考える - 意味不明。創造的に考えるためのテクニック本じゃないのか。内容も他章と重複。
  • 10 ほかの人が考えないことを考える - そのために何が必要なのかを述べてほしい。
  • 19 議論に勝つ - 習慣でもなんでもないし、議論に勝つかどうかはどうでもいいのでは。
  • 20 じっくり考える - 整理する、寝かせる、など有効なものもあるが、習慣とまでは言えない。
  • 23 物語の力を使う - 他者の説得に物語が有効という内容だが、思考力との関係が不明。
  • 24 ユーモアを交える - 何の話なんだろう
  • 25 ポジティブに考える - 自己啓発
  • 26 目標を書いて実現を目指す - あっ自己啓発
  • 27 優先順位を決め、的を絞る - 仕事術だ
  • 28 考えを行動に起こす - 自己啓発
  • 30 脳を強化する - 意味不明

後半になるに連れて雑になってしまったことは否めない。こう見てもわかる通り、無理に30にしたか、タイトルが不適切か、いろんな理由で「30の習慣」は言い過ぎであることがわかると思う。後半は思考力どこいった?ってなっています。

「陥りがちな思考の癖」を把握した上で、実用的なテクニックとそれが使えるシーンを学ぶ。さらに持っておくと良い基礎能力や、それを身につけるための習慣を紹介する。こういった程度で思考力を高めることを一切考えてこなかった人向けの入り口の本としては十分といえる。この本は範囲を広げすぎてよくわからなくなってしまってるように思える。

別のおすすめ

まず、思考法というか、発想法としては以下の本をぜひ読んでほしい。

実用的なテクニック、ツールが本書より多い42種類、かつその方法がなぜ使われてきたか、どこで使われてきたか、歴史的・哲学的ルーツまで触れている。

以下はついで。

21の記憶力については以下の本を読んだほうがいい。本書で載っている手法ももちろん載っている。

30の脳については以下。

脳が認める勉強法――「学習の科学」が明かす驚きの真実!

脳が認める勉強法――「学習の科学」が明かす驚きの真実!

さらに余談だが、そもそもこの本を読んでいた彼女は自分自身を見直して、心入れ替えてやりなおす!その5つの施作の中で、ビジネスにも通ずる思考力、のくだりでこの本をあげていた。脱線するが、

自分が幸福を目指しての心の入れ替えであれば、資本論の観点から自分の幸福を見つめ直してもいいかもしれない。

原著にあたってみた

あまりにも章のタイトルが悪いし、まとまりも悪く、この本が売れてることが疑わしかったので、英語の原著にあたってみた。ここまでくると意地である。

How to be a Brilliant Thinker

30の習慣とは言ってないしね。

しかし驚くごとに(?)内容はほぼその通りであった。そもそも章の英語名は日本語版にも書かれてある。ということは「30の習慣」というタイトルがよくなかった可能性がある。

Forewordを読んでも、日本語と内容は同じだった。

では30の習慣でないとしても、再度本書の内容を確認すると、「brilliant thinker」になるために、まずなぜ考え方を変える必要があるかの背景を説き(これは"はじめに"のようなものだろう)陥りがちな思考の罠、そして重要な思考法、そのための考え方、そしてその他周辺分野で必要なことを雑多に書いている、と言った風に見える。

原著を1から全部追いかけたわけではないが、How to be a Brilliant Thinkerだとしても、もうちょっと章構成を工夫したり、不要なものを削り厳選できたのではないかと思う。ちなみに原著のサブタイトルはExercise Your Mind and Find Creative Solutionsで、脳のエクササイズをして想像的な解決策を見つけよう!なのでやはり発想にフォーカスをあてた本で、その技術と、それらを支える周辺知識を述べた本であることはただしそうだ。

日本のAmazonのレビューは絶賛だが、アメリカのほうでも同様に絶賛だった。

www.amazon.com

top positive reviewを見てみると

An excellent book however it’s missing one extremely importand and powerful element when it comes to creative thinking; Hibernation. Often called the Archimedes Eureka! Principle! I would highly recommend little book titled ‘A Technique for Producing Ideas’ by William Bernbach. The book can be read in ten minutes and immediately employed.

素晴らしい本だけど1つ、想像的な発想をするときにとても重要な要素をが欠けている、それは冬眠。アルキメデスエウレカ!で知られている原理だよ。William Bernbeachの'A Technique for Producing Ideas'を強くおすすめするよ。この本は10分ほどで読んでしまえるからね。

一度考えた後、一度脳の外に出して、そのあとにEUREKA!がやってくるんだぜ、やってみなよ〜ってことが書いてあるけどこのひとはなんでこの本に高評価をつけたんだ。

次にtop critical review

Good But Repetitive

This book was good on telling someone that doesn’t have common sense or use their brain how to exercise their brain in order to remember and figure puzzles out. I thought it was well written, however kind of repetitive exercises, but worded differently. If your interested in psychology,and personal development then read as beginner book.

この本はパズルを解くために脳を運動する方法を知らないひとにとっては有益だろう。でも僕はここに書かれてることは同じことを違う言葉で繰り返されてるように思いました。もしあなたが興味を持てたのなら、自分を開発するための最初の本としてはおすすめです。

まったく同意ですわ。。。

おわりに

書評というのは紹介したくなるものしか書くべきではないと思っている。今回ははじめて批判的な書評をやってみた。これまで経験がなかったのでやってみたいという気持ち半分と、この本について勧めてくれた彼女と話すために自分の感想をまとめておかないといけないと思ったのが半分だ。

もっと言えば僕はきっと対象読者ではないのだと思う。ソフトウェア・エンジニアは確かにソフトウェア技術で問題を解決する職業だが、ここであげられてる「発想法」が強く必要なわけではない。(学び続ける力のほうが、よっぽど大事)そのため、僕に届かなかった上に、僕が「気に食わなかった」のでこのようなレビューになってしまった。

しかし、原典にあたる訓練もできたし、批判的思考は大切なので良い実践の場になったと思う。

繰り返し述べているが本書は大事なことが節々に書かれているが、発想法を学ぶなら別に良い本があるし、何より内容にまとまりがなく、かつ枝葉の部分しかないため、思考力を鍛えたいとはじめて思ったひとの入り口にはおすすめできるが、深く学びたい場合はこの本よりも、もっと対象を絞って深く論じた本を読んだほうがいいという見解だ。

友人から話をもらわなければ1つの本に対してここまで深く言及しなかっただろうな。感謝。