ツナワタリマイライフ

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

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つの本に対してここまで深く言及しなかっただろうな。感謝。

「作詞の勉強本」を読んで振り返る自分の作詞方法

はじめに

8月末までに2プロジェクト(バンド)に対して曲を出さないといけない!作曲はぼちぼち済んだんですが作詞に異常に難航していて、気休めにでもなればという思いで買ってみました。

作詞の勉強本

冒頭で触れられている通り、これはテクニック本ではありません。

本書では「作詞」に焦点を絞って、「作詞の勉強本」をお届けします。作詞のマニュアル本ではありません。

メタなんですね。その思想が気に入って買ってみました。

目線と発想

本書では作詞をするための基本姿勢として、目線と発想の拡張をし続けることを説いています。

発想は表現やシチュエーション、ストーリーを作り出すもので、"引き出し"と読んだりします。これが多いほうがいいというのは何となくでもわかると思います。

一方、"目線の拡張"とは、同じシーン、同じ設定でも、複数の角度から捉えられることを示しています。

この他、表現を拡張するためにどんな風な言い換えのトレーニングをすればいいか、共感を呼ぶためにどんな設定を考えればいいか、自分自身の(本書が呼ぶ)作詞脳を鍛える、トレーニング本になっています。これがテクニック本ではなく、基本理念を踏まえた上での基礎力を伸ばし続けるための本、すなわち勉強本ということになります。

この本から学んだこと

既存曲の分析

1つは単純に、既存曲の分析をしないといけないということ。著者はコラムで2000曲は分析したと書いています。また、今も昔も売れている王道曲はエッセンスがたっぷり詰まっているので聴かない手はないとも説いています。これ、至極当然のことだけど、積極的にやってないなぁと。作詞だけじゃなく、できるひとは息するように好きな曲を耳コピして楽しんで身につけてる。その才能がない自分は進んでやらないとダメに決まってる。

セクションごとの考察

作詞はEvernote上にやっていて、歌詞本体を書いた下に、背景や設定、オチ、主人公のイメージなどを書き留めています。本書ではpart5の実践連で、Aメロ、Bメロ、サビというセクションごとにそれぞれ考察をしたり、流れを決めたりしていて、この点は見習いたいと思いました。

この本から学ばなかったこと

この本はとても良書で、お勧めできる本なんだけど、メロディとの親和性については触れられていいませんでした。

メロディとの関係という意味では、確認事項の部分で、リズムを意識、区切りを意識という確認フレーズで触れられてはいます。また、確認事項のときに「声に出そう」というのは作詞の第一条件で、その点には触れられていました。

でも僕はやっぱりメロディは言葉を呼ぶと思うんですよね。その不思議な相性について知れたらなーという期待がありました。

自分の作詞方法

僕は曲先も詞先も両方やります。今は曲先が多いです。

メロディの好きな部分から好きな言葉を乗せてみて、そこからイメージを膨らませて、寝かせて、また書いてみて、よくいえばじっくり、悪くいえばダラダラと、空き時間を見つけてはevernoteに書き足していくというスタイルです。あんまりババっと30分で書き上げてしまった、みたいなのはないですね。つまり決まったやり方はない、という状況。

こんな風に作詞するんだよと他者に教えられるぐらい確立できていればいいんですが、そうはなってないみたいで、ひたすらうんうんうなって出てくるのを待つ、という状況を打破したい思いでこの本に手を出したんでした。

自分の書いた歌詞を振り返ってみる

猫の休日 - 目覚めたばかりの猫

最近は猫の休日というバンドで目覚めたばかりの猫という曲を書きました。

この曲はわりとセクションごとにシーンを明確に変えていますね。

Aメロは主人公が何か考えながら街を歩いています。 そしてBメロは1番、2番ともに主人公の奥にある「願望」 サビは呆れた顔した猫や、目覚めたばかりの猫という象徴的なフレーズが繰り返されます。 そしてブリッジ的に入っている、ギターソロ後に、自分の本当の欲求はどこにあるのかを問い、 最後のサビで、退屈を歌う猫に対して、私はまだまだ、そんな場合じゃないな、と思い明日へ向かうところで終わります。

……..と後からはいくらでも言えるんですよこんなん!!!作った時のこと覚えてないわ!!!(笑)

YAMAHA作曲コンテスト - 2人だけの夏

これはコンテスト感がすごいあるというか、綺麗(?)すぎて逆に恥ずかしくなってしまいますね。。。音的なもろもろがいろいろアレなのは目をつむってください。。。

これは仮歌のお姉さんのクオリティに助けられまくってなんとか聴ける、、、いかんいかん、作詞の話でした。

このコンテストはテーマが決められていたんですね。それは「初夏の◯◯」。で、僕は「初夏の彩り」というテーマにしました。

審査はこのテーマがどうあらわれているかが観点だったようで、そう思うと初夏感は冒頭のこのフレーズぐらいだし

春が立ち去る姿を 見送って 踊る私の髪と心 叫んでる

さらにいえば彩り要素が特に見当たらないですね(笑)

僕としては「いろんな君」が2人の夏を彩るんだよ!!!って気持ちで書いてますけど読み取れない。(笑)

一瞬で過ぎていく 君の隣を歩く時間に これからの季節のように いろんな君を見せてよ

メロディは結構いいと思うんですが、歌詞は攻めきれず変にまとまってしまって、自分としてもまだまだだなぁと思い知らされたコンテストでした。

とけてなくなる - 曖昧に揺れる15cm

この曲はきっと1番まで書き上げて、2番は似たような言い回しを使って広げたパターンだと思います。対照的に見えますしね。

この曲の良いところは、ちゃんと1番最後に「オチ」がついてるところだと思います。作詞って、どうオチをつけるか、リスナーをどう(良い意味で)裏切られるかって結構重要だと思っているので。

ただの片思いの曲なのは最初から最後までそうなんですが

あなたも同じ夢を見て 同じように苦しめば良いのにな あなたも僕のこと考えて 悩んでればいいのにな そんなわけないな

「オチ」というか「シメ」ですね。必ずしも結論を着地付ける必要はないんですが、シメがあると気持ちよく追われる気がする。

おわりに

だらだら自分の作品を味わっただけで終わってしまった。まだまだ自分の作詞法を確立できておらず、プロの作詞家には遠いですね。

とりあえず今後積極的にやっていきたいことは、既存曲の分析(きっとブログに載せます)と、自分が作詞するときにはブロックごとに明確に背景や設定のメモを書きながら、ある程度意図して作詞を行えるようになりたいです。

作曲もやるので、作詞だけやるってことはないだろうけど、そんな機会も今後持てたらなと思います。