OmniAICreator/Qiita-1.07M huggingface dataset:
pythonbrewでPythonインストールしてFlaskをWSGIサーバで動かすまで


##はじめにおことわりの追記
いきなりだけど僕はpythonbrewもう使っていません。
理由はいくるかあるんだけど

#### pythonbrewがもうメンテされてなかったです
今だとpythonzでしょうか。複数バージョンをインストールしたい場合はpythonz使うといいと思います。
でも僕は今pythonzも使ってないです。

#### そもそも複数バージョン必要無かった
たまにライブラリの都合で2系と3系を切り替えるくらいで、それなら別に外部ツール使わなくてもできる。
Macには2系は入っていて、あとはbrewとかで3系入れれば事足りる。

あと複数バージョンが必要なときって、なんだろ、ぼくの場合は何か公開するためにテストするときくらいなんだけど、それもTravis CIとか使えば出来るしなあ、とか。

#### バージョンを切り替えたいんじゃなくて、環境を汚したくないだけだった
思えばpythonbrewでよく使ってたのは仮想環境作るのだけだった。それvenvじゃん。
ということで、自分のマシンには、シンプルにvenvで環境を作ることで落ち着きました。普通！ 今のところ困ること一つも無いです。みんなが良いっていう方法は良いの法則。

http://methane.hatenablog.jp/entry/2013/11/05/Mac_%E3%81%A7_Python_%E3%81%AE%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83%E6%A7%8B%E7%AF%89(2013_11)

追記おしまい。




##インストール

###pythonbrew入れる
```
$ git clone https://github.com/utahta/pythonbrew
$ ./pythonbrew-install
```

### パス通す
```
$ vi ~/.bashrc
source $HOME/.pythonbrew/etc/bashrc
# rootで入れた場合はこっちかもよ〜↓↓↓
source /usr/local/pythonbrew/etc/bashrc
```
.bashrc読み直すと、pythonbrew か pybrew コマンドが使える

###Python入れる
```
# 思い切ってもうv3で！
$ pybrew install 3.3.0
$ pybrew venv create testenv -p 3.3.0 #testenvは任意
```
※追記だけども、macでやるなら frameworkオプションつけたほうがいいかも。大抵問題ないけど、matplotlib とか使うときにおこられてあとで再インストールした。

```
$ pybrew install --framework 3.3.0  # macのばあい
```


###使う
```
$ pybrew venv use testenv  
$ python --version
Python 3.3.0 # わーい
```

###いろいろ入れる
```
#pybrewでなんかuseしてる状態で
$ pip install Flask
$ pip install WTForms
$ pip install sqlalchemy
$ pip install psycopg2 #PostgreSQLの場合
$ pip install flask-mail
$ pip install Flask-Script
$ pip install Beaker #セッション管理
$ pip install uwsgi
```


### 簡単なアプリ
``` hello.py
# coding=utf-8
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'こんにちは'

@app.route('/indo')
def hello_world_indo():
    return 'नमस्ते'

if __name__ == '__main__':
    app.run(host='0.0.0.0',debug=True)
```

### 起動
```
$ python hello.py
```
http://[サーバのIP]:5000
で見える。やったー。

### uWSGIから起動
開発中は、さっきみたいにuWSGI使わずに普通に起動して開発して、本番環境では、nginx+uWSGI+Flask みたいな感じ。
今回はuWSGI使ってほんとに起動できんのかよ、みたいなのを試すだけ。

```とりあえず版
$ uwsgi --http :9090  --python-path /var/app/testpy -w hello:app 
```
http://[サーバのIP]:9090
で見える。

起動スクリプトとかにするにはこれだとだめかも。
いつでも動く版はこっち。

```いつでも動く版
$ /usr/local/pythonbrew/venvs/Python-3.3.0/testenv/bin/uwsgi --http :9090 -H /usr/local/pythonbrew/venvs/Python-3.3.0/testenv --python-path /var/app/testpy -w hello:app 
```


http://[サーバのIP]:9090
で見える。


解散！
Apache httpd IPv4のみで通信


netbios(samba)やbonjour(avahi)等で名前解決すると、IPv6で接続されることがある。
IPv6アドレスがマッチしないため、allowディレクティブ等が期待した動作をしなくなってしまう。

IPv6通信を遮断するには、Listenディレクティブを修正する。

```apache:httpd.conf
#Listen 80
#Listen *:80
Listen 0.0.0.0:80
```
PostgreSQL 外部キー一覧の取得


information_schema.table_constraints を参照する.

```sql:
SELECT table_name, constraint_name
FROM information_schema.table_constraints
WHERE table_schema = 'public'
	AND constraint_type = 'FOREIGN KEY';
```

外部キー制約を削除するには…

```sql:
ALTER TABLE <<table_name>>
DROP CONSTRAINT IF EXISTS <<constraint_name>>;
```
go get コマンドで x509: failed... と怒られた場合の対処方法 (homebrew)

# go getしたらなんかエラーが...

仕事で利用するサーバー監視系のIRC botをGoで書こうと思って、
見つけたIRCライブラリ、 [goirc](https://github.com/fluffle/goirc/) をgithubから落としてインストールすっかと
思って```go get``` したらエラーメッセージが出た...

```
> go get github.com/fluffle/goirc/client                [~/Kinmoza]
package code.google.com/p/gomock/gomock: Get https://code.google.com/p/gomock/source/checkout?repo=: x509: failed to load system roots and no roots provided
```

ファッ？！なんだこれ、って思って
ぐぐってみたらgolang-nutsより対処方法が書いてあった。

[対処方法が書かれたフォーラム](https://groups.google.com/forum/#!topic/golang-nuts/gyL_jeOyxQA)

1. homebrewでgoをインストールする場合、```--with-cgo``` オプションを付けた上でインストールしなければならない

   homebrew の [go formulaを見てみたらオプションとして書かれてた](https://github.com/mxcl/homebrew/blob/master/Library/Formula/go.rb#L26)...

2. ```go env``` にて ```CGO_ENABLED``` の環境変数を確認し、 ```1``` で設定されていること。

ということで、goをhomebrewにてインストールし直した。

```
> brew uninstall go
> brew install --with-cgo go
> go env
> go env CGO_ENABLED                                    [~/Kinmoza]
1
```

goをインストールし直した後、改めてgoircを```go get```

```
> go get github.com/fluffle/goirc/client
```

エラーメッセージは出なくなった。解決。
プロパティやメソッドを非推奨にしてビルド時に警告メッセージを出す

```objc:LegacyClass.h
@interface LegacyClass : NSObject
@property(nonatomic, readonly) NSArray* bugs __deprecated_msg("いつまで経っても変更作業が終わりません");
- (void)attendDeathMarch __deprecated_msg("もうウンザリです。何も改善できません");
@end
```

<a href="http://gyazo.com/6f584b8c92c64d48f7fed23e82a275d6"><img src="http://gyazo.com/6f584b8c92c64d48f7fed23e82a275d6.png"></a>
<a href="http://gyazo.com/694b3f4ddfe6ceafbf3b7304134f35a9"><img src="http://gyazo.com/694b3f4ddfe6ceafbf3b7304134f35a9.png"></a>

呼び出してる箇所にこんな感じにXcodeへ表示される
OS XでターミナルからカレントディレクトリをFinderで開く方法

Terminalでのコマンドラインの作業も慣れてくると楽しいのですが、やっぱりFinderで「GUIだ！ マウスを使ってグリグリ動かすんだ！」という気分の時もあります。

そういう時にDockからFinderを開いて目的の場所に移動してもいいんですが、コマンドラインのカレントディレクトリが目的の場所だったらいちいち開き直すのは二度手間なのでめんどくさい。

## そういう時の対策がこれ！ ♫テレホンショッピングの音楽♫

```
$ open .
```

これだけでOK、ちゃんと別プロセスで立ち上げてくれるのでTerminalの作業も継続できるのがまたとてもよい感じ。


## 参考
http://osxdaily.com/2009/12/15/open-finder-from-the-terminal/
JRubyでRuby on Rails4を動かす

## はじめに

JRuby使って、Rails4を動かした時にちょっと戸惑ったので、メモしておく。

## 構築方法

[Rails開発環境の構築（rbenvでRuby導入からBundler、Rails導入まで）] (http://qiita.com/emadurandal/items/a60886152a4c99ce1017)
このqiita記事が、良いです。JRubyでもほぼ同じ方法で綺麗に構築できます。
実行時の接頭辞に`jruby -S`を忘れないでね。

## 一旦確認

```shell-session
$ jruby -S bundle exec rails server
```
![Ruby on Rails  Welcome aboard.png](https://qiita-image-store.s3.amazonaws.com/0/1361/5c0787a8-a24a-b3dd-c0fd-a1c7edfa34df.png)

サクっと表示されるはず。

## Controllerを作ってみる

```shell-session
$ jruby -S bundle exec rails generate controller Hello index
```

![9fb5e366-731d-66b5-f462-bd97d576f3a2.png](https://qiita-image-store.s3.amazonaws.com/0/1361/b173940b-2234-504d-39ab-94edf152967a.png)

残念賞

## JCEを持ってきましょう

[Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 7 Download](http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html)
上記のURLからDLしてください。
解凍後、`local_policy.jar`, `US_export_policy.jar`を取り出して、`jre\lib\security\`に入れましょう。
(当然ですが、上書き前のファイルは退避させておくと良いと思います。)

## 再度、確認

```shell-session
$ jruby -S bundle exec rails server
```

![TestRails4.png](https://qiita-image-store.s3.amazonaws.com/0/1361/882397f7-a41e-0478-04ed-c651c4e216ec.png)

おつかれさまでした。
MAMP環境でbake時にMySQLに接続できなかった時の対処法


bake時に使われるPHPはOSX標準付属のPHPが使われる。

このPHPのphp.iniは/etc/php.iniですが存在しないので、デフォルトをコピーするところからスタート

```
sudo cp /etc/php.ini.default /etc/php.ini
```

MAMP用にするには pdo_mysql.default_socketを次のように書き換えればOK

```
pdo_mysql.default_socket=/Applications/MAMP/tmp/mysql/mysql.sock
```
octopressで画像まとめブログの作成


みなさんお手持ちの秘蔵画像を溜め込んでいたり画像収集スクリプトなんかをぶいぶい言わしちゃっていると思いますが、その画像群を使ってお手軽に画像まとめブログでも作っちゃおうというやつです。

使うのはoctopressが動かせる環境と公開する場所はs3なんかがいいですかね。要領無制限だし。
デプロイ先はおのおのなんかいい感じに頑張ってください。

# タスクの追加
octopressのRakefileに下記を追加

```ruby:Rakefile
images_dir      = "images"

desc "Begin a new image in #{source_dir}/#{images_dir}"
task :new_image, :title do |t, args|
  if args.title
    title = args.title
  else
    title = get_stdin("Enter a title for your post: ")
  end
  raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." unless File.directory?(source_dir)
  mkdir_p "#{source_dir}/#{images_dir}"
  dirname = "#{source_dir}/#{images_dir}/#{Time.now.strftime('%Y')}/#{Time.now.strftime('%m')}/#{Time.now.strftime('%d')}/#{title}"
  mkdir_p "#{dirname}"
end

desc "Begin a new image2post in #{source_dir}/#{posts_dir}"
task :image2post, :title do |t, args|
  raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." unless File.directory?(source_dir)
  mkdir_p "#{source_dir}/#{posts_dir}"
  raise "### You haven't set anything up yet. First run `rake new_image` to set up an Octopress theme." unless File.directory?("#{source_dir}/#{images_dir}")
  Dir.glob("#{source_dir}/#{images_dir}/*/*/*/*").each do |dir|
    dir_paths = dir.scan(/.+\/.+\/(\d+)\/(\d+)\/(\d+)\/(.+)/)
    year  = dir_paths[0][0]
    month = dir_paths[0][1]
    day   = dir_paths[0][2]
    title = dir_paths[0][3]
    filename = "#{source_dir}/#{posts_dir}/#{year}-#{month}-#{day}-#{title}.#{new_post_ext}"
    next if File.exist?(filename)
    puts "Creating new post: #{filename}"
    open(filename, 'w') do |post|
      post.puts "---"
      post.puts "layout: post"
      post.puts "title: \"#{title.gsub(/&/,'&amp;')}\""
      post.puts "date: #{Time.now.strftime('%Y-%m-%d %H:%M')}"
      post.puts "comments: true"
      post.puts "categories: "
      post.puts "---"
      Dir.glob("#{dir}/*").each do |image|
        post.puts ""
        image_name = image.gsub(/#{dir}\//, '')
        image_path = dir.gsub(/#{source_dir}/, '') + '/' + image_name
        post.puts "![#{image_name}](#{image_path} '#{image_name}')"
      end
    end
  end
end
```

<https://gist.github.com/soramugi/7007229>

# 使い方
octopressの初期設定は済んでいるものとします。
使い方は至って簡単。

    $ bundle exec rake new_image['かしこいかわいいエリーチカ']
    mkdir -p source/images
    mkdir -p source/images/2013/10/17/かしこいかわいいエリーチカ
    $ mv ~/Downloads/img/* source/images/2013/10/17/かしこいかわいいエリーチカ/
    $ bundle exec rake image2post
    mkdir -p source/_posts
    Creating new post: source/_posts/2013-10-17-かしこいかわいいエリーチカ.markdown
    $ bundle exec rake gen_deploy

と4つのステップで簡単にブログの完成です。

[かしこいかわいいエリーチカ - pic.soramugi.net](http://pic.soramugi.net/blog/2013/10/17/かしこいかわいいエリーチカ/)

octopressのテーマも[色々](https://github.com/imathis/octopress/wiki/3rd-Party-Octopress-Themes)あるので自分好みのもあるかと思います。

一つ欠点があって、octopressはsourceディレクトリにあるものをpublicディレクトリに書き出してからデプロイなりなんなりをするので、要領が2倍になります。あまりローカル環境で要領が無い場合にはリモート環境に置くとか考えないといけないかもです。

いじょ
DDNSっぽいのをGoで(バリュードメイン用)

バリュードメインのDDNSっぽいのをGoで書いてみました。

- `pit`を利用してドメインとパスワードを設定する
- `subDomains`で設定したいレコードを指定する

で取得できるのでお試し下さい。

```go
package main

import (
	"fmt"
	"github.com/typester/go-pit"
	"io/ioutil"
	"log"
	"net"
	"net/http"
)

type PitSetting struct {
	Domain   string
	Password string
}

type Record struct {
	Ip     string
	Domain string
}

func GetPitData(data string) (*PitSetting, error) {
	vd, err := pit.Get(data, pit.Requires{})
	if err != nil {
		return nil, err
	}
	p := new(PitSetting)

	p.Domain = (*vd)["domain"]
	if p.Domain == "" {
		log.Fatalln("no such domain")
	}
	p.Password = (*vd)["password"]
	if p.Password == "" {
		log.Fatalln("no such password")
	}
	return p, nil
}

func GetCurrentIP() (string, error) {
	response, err := http.Get("http://temple-kun.appspot.com/")
	if err != nil {
		return "", err
	}
	defer response.Body.Close()

	contents, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return "", err
	}
	return string(contents), nil
}

func GetDomainIP(domain string, subDomains []string) ([]Record, error) {
	records := make([]Record, 0, len(subDomains))

	for _, subDomain := range subDomains {
		s := subDomain + "." + domain
		v, err := net.LookupHost(s)
		if err != nil {
			return nil, err
		}
		records = append(records, Record{s, v[0]})
	}
	return records, nil
}

func main() {
	pitDomain := "value-domain"
	subDomains := []string{"www", "www1", "www2"}

	pit, err := GetPitData(pitDomain)
	if err != nil {
		log.Fatal(err)
	}

	currentIpAddr, err := GetCurrentIP()
	if err != nil {
		log.Fatal(err)
	}

	records, err := GetDomainIP(pit.Domain, subDomains)
	if err != nil {
		log.Fatal(err)
	}

	for i := range subDomains {
		fmt.Printf("domain : %s\ndomain  IP : %s\ncurrent IP : %s\n",
			records[i].Domain,
			records[i].Ip,
			currentIpAddr,
		)

		if records[i].Domain == currentIpAddr {
			fmt.Println("STATUS : same\n")
			continue
		}

		fmt.Println("STATUS : not equal")

		url := fmt.Sprintf("http://dyn.value-domain.com/cgi-bin/dyn.fcg?d=%s&p=%s&h=%s&i=%s",
			pit.Domain,
			pit.Password,
			subDomains[i],
			currentIpAddr,
		)
		fmt.Println(url)

		response, err := http.Get(url)
		if err != nil {
			log.Fatal(err)
		}
		defer response.Body.Close()

		contents, err := ioutil.ReadAll(response.Body)
		if err != nil {
			log.Fatal(err)
		}
		log.Println(string(contents))
	}
}
```
【rails】【デバッグ】railsコンソールでモジュール内のクラスメソッドの呼び出し方法

### moduleのソース
```irb
module G08M11

     class Test1Class

          def test1method
               return 222
          end
     end
end

```


###railsコンソールの呼び出し

```irb
rails c

a = G08M11::Test1Class.new
a.test1method

```


### 実行結果：
```irb
=> 222 
```
<br>
**ポイント**
- インスタンス化
- railsコンソールで呼び出す際はモジュールの後は「::」が付く
<br>
**追記**
結合テスト時に機能によっては「self.」があることで外部から呼び出せないなどで
途中で落ちる場合があります。
そのため、単体で確認後は結合でも確認することをおすすめします。
Closure libraryを使ったJavaScriptの最近流行りのより効果的なプロパティの配置

# 結論

古い書き方

```js
/**
 * @param {number} x
 * @constructor
 */
foo.bar.Hoge = function(x) {
  /** @private */
  this.x_ = x;
}

/**
 * @private {number}
 */
foo.bar.Hoge.prototype.y_;
```

新しい書き方

```js
/**
 * @param {number} x
 * @constructor
 * @struct
 */
foo.bar.Hoge = function(x) {
  /** @private */
  this.x_ = x;

  /** @private {?number} */
  this.y_ = null;
}
```

気をつけるべき点は2点．

- 全てのプロパティをコンストラクタにまとめる．
- @structをコンストラクタに可能ならばつける．

# 背景

以前までは，動的に生成されうるプロパティについては，メモリ使用量を減らすため，コンストラクタにはおかれませんでした．Closure libraryにおいては依然型チェックのため，コンストラクタの外に書いていました．これは，型チェックにのみ用いられ，Closure compile後のコードには残りませんでした．

一方で，V8など昨今のJavaScript engineにおいては，より高速なプロパティアクセスのために，lookup tableではなく，[hidden classを用いた方法を採用している](https://github.com/v8/v8/wiki/Design%20Elements#fast-property-access)ため，動的なプロパティの追加はオブジェクトのhidden classのcache missをうみだし，パフォーマンスの低下につながります．つまり， __オブジェクトの全てのプロパティをコンストラクタで定義しその後追加を行わない__ ことが，パフォーマンスの面では効果的と言えます．
これはClosure libraryでは`@struct` tagにて保証できます．
KIF2.0 の導入手順を翻訳しました

KIF2.0 は 自動テスト用のライブラリです. 詳しくはこちらをご覧ください. https://github.com/kif-framework/KIF また @_ishkawa さんのこちらの記事をご覧いただくと動作イメージが掴めると思います. http://blog.ishkawa.org/blog/2013/08/31/kif-next/

###1. Podfileに以下を記入 

```
pod 'AFNetworking'
target 'Acceptance Tests', :exclusive => true do
  pod 'KIF', '~> 2.0'
end

```

exclusive => true にすると Acceptance Tests ターゲットには AFNetworkingが含まれなくなります.

###2. Xcodeで新しくターゲットを追加.

ターゲット名は Acceptance Tests にする

###3. pod install する

###4.1. プロジェクトの設定

TARGETS > Acceptance Tests を選択 > Build Settings > Linking の Bundle Loader の項目に以下を設定 (My Appはテスト対象のアプリ名)

	$(BUILT_PRODUCTS_DIR)/My App.app/My App

同じく Build Settings > Unit Testing >  Test Host に以下を設定

	$(BUNDLE_LOADER)


###4.2. テスト実行の設定

画面左上の アプリ名の Edit Scheme... > Test を選択 し 下の + を押して Acceptance Tests を追加

これで, Build ターゲットをアプリにした状態で cmd + u でテストを実行できます.


###5. テストコードを追加

このファイルを Acceptance Tests というグループができているのでその中に入れて cmd + u します.
左上のナビバーをタップして 編集画面に移行することを確認するテストです. 

```objc

#import <KIF.h>
#import "KIFTestCase.h"
#import "KIFUITestActor+KNEAdditions.h"

@interface KNESignInTests : KIFTestCase
@end

@implementation KNESignInTests

- (void)beforeEach
{
}

- (void)testSuccessfulSignIn
{ 
    [tester tapScreenAtPoint:CGPointMake(40, 40)];    
    [tester waitForTappableViewWithAccessibilityLabel:@"編集"];   
}

@end

```

### その他.
* KIFは 非公式のAPIを使っているので プロダクションのコードにテストコードを誤って含まないように注意して下さい.

### 参考.
* 公式: https://github.com/kif-framework/KIF
* http://blog.ishkawa.org/blog/2013/08/31/kif-next/
Heroku上でRedmineとSendGridを連携させる

* Heroku に Redmine を立てた
* Heroku からはメールが配信できないので，外部SMTPサーバー等を利用する必要がある
* 今回はHerokuのアドオンとして提供されている SendGrid [1] を利用した
* Redmine の構築は基本的には [2] を参考に行った（ので，割愛）

# Redmine のメール配信に SendGrid を利用する

* アドオンの画面からSendGridを選ぶ
 * https://addons.heroku.com/sendgrid
* プランと利用するAppの名前を選択して，Add ボタンを押す
 * もしくは，手元から `heroku addons:add sendgrid:plan` でも追加できる
 * Starter（Free）プランだと200通/日メールを送ることができる

# Redmine のから SendGrid を利用するように設定する

* SendGrid をアドオンとして追加すると，アプリケーションの設定としてユーザ名とパスワードがHeroku上で設定された状態になる
 * `$ heroku config` で一覧することができる

```
$ heroku login
$ heroku config
DATABASE_URL: postgres://~~~
...
SENDGRID_PASSWORD: ****
SENDGRID_USERNAME: ****@heroku.com
```

* この値は環境変数として設定されているので，Rails（Ruby）側から簡単に取得することができる
* config/configuration.yml を編集（config/configuration.yml.example からコピー）
 * 例として記述されている（コメントアウトされている）行以外は削除するのがおすすめ（コメントアウトされていない行を見落とし，デフォルト値として読み込まれてハマった）

```config/configuration.yml
default:
  email_delivery:
    delivery_method: :smtp
```

* config/environment.rb にメールサーバーの情報をそのまま書くこともできそうだが，どう頑張ってもエラーになってしまって利用できなかったため，実際の設定は config/environments/production.rb と分割して書いた（参考 [3]）
* config/environments/production.rb を編集
 * 最下部（`RedmineApp::Application.configure do ~ end` の外側）に下記を追記

```confit/environments/production.rb
ActionMailer::Base.smtp_settings = {
  :address => 'smtp.sendgrid.net',
  :port    => '587',
  :authentication => :plain,
  :user_name => ENV['SENDGRID_USERNAME'],
  :password => ENV['SENDGRID_PASSWORD'],
  :domain => 'heroku.com'
}
ActionMailer::Base.delivery_method = :smtp
```

* これを Heroku へ push することで，SendGrid をメール配信に利用することができる
 * .gitignore ファイルに config/configuration.yml がリストされている場合は削除する必要がある

# SendGrid のダッシュボード

* Heroku ダッシュボードからSnedGridのダッシュボードを見に行くことができる
 * 何通送った，何通届いた，オープンされた（トラッキングするかの設定が必要）などが見られる

![sendgrid_dashboard.png](https://qiita-image-store.s3.amazonaws.com/0/1896/8c3d22b4-25ed-ff77-b3fa-f00c36061889.png)

# 参考

* [1] [Email Delivery & Transactional Email Servers | SendGrid](http://sendgrid.com/)
* [2] [HerokuにRedmine 2.3.1を展開する - momoto.github.io](http://momoto.github.io/blog/2013/05/31/deploy-redmine-2-dot-3-1-on-heroku/)
* [3] [Redmine email configuration with environment variables - stackoverflow.com](http://stackoverflow.com/questions/15045369/)
Cannot init the Global objectまたはConfiguration errorでonStartできないとき

GlobalSettingsでアホみたいなミスをしてハマってしまっていた・・・

GlobalSettingsのoverrideでonStartでこけて進めない。

> Oops, cannot start the server.
> Configuration error: Configuration error[Cannot initialize the custom Global object (%s) (perhaps it's a wrong reference?)]
> 	at play.api.Configuration\$.play\$api\$Configuration\$\$configError(Configuration.scala:92)
> 	at play.api.Configuration.reportError(Configuration.scala:570)
> 	at play.api.WithDefaultGlobal\$class.play\$api\$WithDefaultGlobal\$\$scalaGlobal(Application.scala:39)
> 	at play.api.DefaultApplication.play\$api\$WithDefaultGlobal\$\$scalaGlobal\$lzycompute(Application.scala:399)
> 	at play.api.DefaultApplication.play\$api\$WithDefaultGlobal\$\$scalaGlobal(Application.scala:399)
> …

もしくは
> Oops, cannot start the server.
> @6g1d77o6c: Cannot init the Global object
> 	at play.api.WithDefaultGlobal\$\$anonfun\$play\$api\$WithDefaultGlobal\$\$globalInstance\$1.apply(Application.scala:55)
> 	at play.api.WithDefaultGlobal\$\$anonfun\$play\$api\$WithDefaultGlobal\$\$globalInstance\$1.apply(Application.scala:49)
> 	at play.utils.Threads\$.withContextClassLoader(Threads.scala:18)
> 	at play.api.WithDefaultGlobal\$class.play\$api\$WithDefaultGlobal\$\$globalInstance(Application.scala:48)
> 	at play.api.DefaultApplication.play\$api\$WithDefaultGlobal\$\$globalInstance\$lzycompute(Application.scala:399)
> 	at play.api.DefaultApplication.play\$api\$WithDefaultGlobal\$\$globalInstance(Application.scala:399)
> 	at play.api.WithDefaultGlobal\$class.global(Application.scala:64)
> 	at play.api.DefaultApplication.global(Application.scala:399)
> …

というようなエラーが出てplay start, play runできないといった内容。
原因はimport文（笑）

```scala
import play.GlobalSettings
import play.Application
```

play.*にもGlobalSettingsというクラスがあるが、objectを使う際はこっち。

```scala
import play.api.GlobalSettings
import play.api.Application
```

あーばかみたいｗ
複数サーバ間でPHPなどの設定を比較,確認したい時のTips

## その① 異なるサーバの設定ファイルをdiffりたい

必要な知識は2つ。

1.

```
diff <(echo hoge) <(echo huga)
```

のように書けば、( )の中で実行したコマンド出力同士を比較できます。

2.

```
ssh remote cat hoge.txt
```
sshコマンドの後にコマンドを書くと、
remote先のサーバでコマンド実行した結果を出力できます。
上記例だと、remoteサーバにあるhoge.txtの中身を出力できます。

1と2を組み合わせて、

```
diff <(ssh remote1 cat /etc/php.ini) <(ssh remote2 cat /etc/php.ini)
```

と書けば、remote1サーバとremote2サーバのphp.iniファイルを比較できます。

言わずもがなですが、
localとremoteを比較したいのならば、

```
diff <(cat /etc/php.ini) <(ssh remote cat /etc/php.ini)
```

でOK。

うまいことエスケープすれば、
例えば```php -r "phpinfo();"```なんてのもdiffれます。

```
diff <(ssh remote1 "php -r \"phpinfo();\"") <(ssh remote2 "php -r \"phpinfo();\"")
```

## その② 設定ファイルを見やすくする

さて、サーバ間diffで設定に差があることが判明したとして、
php.iniや、他にもhttpd.confなど、各種設定ファイルは大抵1000行近くあるため、
初心者にとってはとっつきづらく、恐る恐る読み下すうちに嫌になってしまったりします。

そんなときは思い切ってコメントアウト行、空行を省いてしまいましょう。
例えばphp.iniなら";"で始まる行はコメントアウト行なので、

```
grep -v "^$\|^;" /etc/php.ini
```

とすれば有効な行だけ抽出できます。

試しに、どのくらい省けたのかカウントしてみます。

```
% cat /etc/php.ini | wc -l
1801
% grep -v "^$\|^;" /etc/php.ini | wc -l
179
```

当方の環境で試したら実に約1/10になりました。
実際目を通す部分は1/10で良い、とわかるだけで心理的抵抗感も幾分減るはずです。
複数の電話番号に電話をかける。

Twilio一斉コール

```php:rest_outgoing_call.php
<?php
  require("Services/Twilio.php");
	
	$ACCOUNT_SID = "your twilio account sid";
	$AUTH_TOKEN = "your twilio auth token";
 
	$tel_nos = array('+8180xxxxyyyy','+8180yyyyxxxx');
 
	$client = new Services_Twilio($ACCOUNT_SID, $AUTH_TOKEN);
		foreach($tel_nos as $tel_no):
			$call = $client->account->calls->create(
				'+8150xxxxyyyy', // From a Twilio number in your account
				 $in_tel_to, // Call any number
				'http://your server url'
			);
		endforeach;
?>
配列valuesと、valuesと同じ要素数の配列keysを結合して、keyをキーとする値valueのHashを作る方法


```
keys=[:orange, :apple, :banaba, :strawberry, :blueberry]
values=[3,2,2,50,100]

hash_fruits=Hash[*[keys,values].transpose.flatten]
```
p hash_fruits

実行結果

{:orange=>3, :apple=>2, :banaba=>2, :strawberry=>50, :blueberry=>100}
PHPUnit実行中に例外発生「Execution aborted after 1 second」


PHPUnitに次の例外が発生したときの対応。

```
Execution aborted after 1 second
```

これはstrictをtrueにしている場合に発生する例外。
テストの処理時間が1秒超えてテスト失敗と判断されている。

```phpunit.xml
<phpunit strict="true">
(省略)
```
 
テストに1秒以上かかるなら、テストメソッドに`@medium`を付けると失敗判定を10秒にできる。
`@large`を付けると60秒。

``` sample.php
/**
* @medium
*/
public function test_sample() {
```

詳細は[PHPUnitアノテーション](http://phpunit.de/manual/3.7/ja/appendixes.annotations.html)に記載されています。
Vimで、カーソル位置を行頭トグルする(改)

# 問題

`^`(空白文字をのぞいた行頭に移動)は、プログラミングをしてると頻繁に使うのに、`^`なんて押せない。[カーソル位置を行頭トグルする（Vim） - Qiita [キータ]](http://qiita.com/hatchinee/items/c8510babfc5ed85810ef)という素晴らしいTipsが投稿されたが、順番的に、`^`してから`0`したい。

# 解決

以下のような設定をほどこす。

```vim
nnoremap <expr> 0
\  match(strpart(getline('.'), 0, col('.') - 1), '^\s\+$') >= 0 ? '0' : '^'
```

こんな感じになる。

![](http://gifzo.net/dybc89LErS.gif)

# 議論

`^` → `0` → `$`で循環できるともっといいのかも？
for文で二重ループとついでにprint文の挙動


ちらちら調べたりググったりしてると世にはCかC++のばかり出てきてアレなのでCで書かれたソースや解説も理解しなければという。

Cでの二重forループの例文の参考先さま
[http://tokyo-ct.net/usr/kosaka/for_students/CIntro/loop/loop.html](http://tokyo-ct.net/usr/kosaka/for_students/CIntro/loop/loop.html)

```C:for文.c
#include <stdio.h>

int main()
{
    int i,j;
    for (i=0;i<5;i++) {
        for (j=0;j<10;j++) {
            printf("*");
        }
        printf("\n");
    }
    return 0;
}
```

```(python):for文.py
#!/usr/bin/env python
import sys

 for x in range(5):
     for y in range(10):
         sys.stdout.write('*')
     sys.stdout.write('\n')
```
はい、これでCで書かれたお手本のあんなこんなのも脳内で置き換えて読み放題に。
まぁ二重ループ理解するよりpythonでprint出力は自動で改行が付くのに苦労しました。
','で改行させないようにすると半角スペースでスカスカに。。。
改行させないようにすると sys.stdout.write('*') 以外の方法はちょっと見当たらなかったっぽい。
現時点では挙動の違いの把握と使い分けが出来ていればいいかな？と。。

ああ、、九九の表を練習してみたら表示で見える挙動は、"sys.stdout.write('\n')" と "print" のみの行は同等の挙動っぽい。

```py:九九の表.py
#!/usr/bin/env python
import sys

for x in range(1,10):
  for y in range(1,10):
    print "{0:02d}".format(x*y),
# print
  sys.stdout.write('\n')
```

AOJで何度もprintのみで使う場面があったのはこういう挙動が必要な時だったのね。

Cの二重ループが脳内で置き換えの目的とは別に今回の意外な収穫はprintの挙動の違いが少し見えたこと。
もっと多重ループの場合やら
空の配列への書き込みベンチマーク

任意の配列のデータを空の配列に移し替える幾つかのベンチマーク取ってみた

この例だと、配列Wのデータを配列WWに１つずつ逆順に移し替えるもの。
現実的な場面としてはランダムな順番に入れ返るケースを想定してのベンチマーク

case1: 配列Wのデータを末尾から抜いて、空の配列WWの頭からプッシュしていく
case2: 配列WWの長さを配列Wの長さに指定しておいて、配列Wのデータを末尾から抜いて、配列WWの適当な位置に置く
case3: 配列Wのデータを頭から抜いて、空の配列WWの頭に差し込む

結果としては、case2が一番早くて、若干case1が遅くなる。case3はかなり遅い


```js:array_benchmark.js
var Benchmark = require('benchmark')
var words = 'a b c d e f g h i j k l m n o p q r s t u v w x y z'

function not_set_length () {
    var w = words.split(' ')
    var ww = []
    while (w.length) {
        ww.push(w.pop())
    }
    return true
}
function set_length () {
    var w = words.split(' ')
    var ww = []; ww.length = w.length
    var i = 0
    while (w.length) {
        ww[i++] = w.pop()
    }
    return true
}
function maybe_most_slowly () {
    var w = words.split(' ')
    var ww = []
    while (w.length) {
        ww.unshift(w.shift())
    }
    return true
}


(new Benchmark.Suite)
.add('配列の長さを事前に指定する',function () {
    set_length()
})
.add('配列の長さを指定しない', function () {
    not_set_length()
})
.add('配列の長さを指定しない かつ unshift() で足す', function () {
    maybe_most_slowly()
})

.on('cycle', function (ev) {
    console.log(String(ev.target))
})
.on('complete', function () {
    console.log('Fastest is ' + this.filter('fastest').pluck('name'))
})

.run({async: true})
```

```benchmark.txt
配列の長さを事前に指定する x 1,227,097 ops/sec ±1.26% (90 runs sampled)
配列の長さを指定しない x 1,141,372 ops/sec ±0.42% (96 runs sampled)
配列の長さを指定しない かつ unshift() で足す x 227,978 ops/sec ±0.47% (91 runs sampled)
Fastest is 配列の長さを事前に指定する
```
Androidの開発で役に立つサイト

+ [ANDROID VIES](http://www.androidviews.net/)
+ [Android App Development with Clojure: An Interactive Tutorial](https://github.com/krisc/events/wiki/Android-App-Development-with-Clojure:-An-Interactive-Tutorial)
+ [Clojure Development on Android](http://clojure-android.blogspot.jp/)
+ [Modifying the Android logcat stream for full-color debugging](http://jsharkey.org/blog/2009/04/22/modifying-the-android-logcat-stream-for-full-color-debugging/)
+ [Androidのテキストサイズはdimens.xmlに書いておく](http://htomiyama.blogspot.jp/2013/02/androiddimensxml.html)
+ [Setting Action Bar title and subtitle](http://stackoverflow.com/questions/14297178/setting-action-bar-title-and-subtitle)
+ [ActionBarにMenuを表示する](http://techbooster.jpn.org/andriod/ui/3383/)
+ [onCreateOptionsMenuが呼び出されない](http://androyer.blogspot.jp/2013/01/oncreateoptionsmenu.html)
+ [オプションメニュー(OptionsMenu)を追加する](http://techbooster.org/android/application/246/)
+ [いつか使いたい!!! Android 用ライブラリ 24 個](http://slumbers99.blogspot.jp/2013/04/android-24.html)

# Tips
- [Android　カスタムタイトルバーを使う](http://y-anz-m.blogspot.jp/2010/11/android.html)
- [Android Custom ListView with Image and Text](http://www.androidhive.info/2012/02/android-custom-listview-with-image-and-text/)
- [ListViewを使いこなそうね](http://androidadvent.blog.shinobi.jp/2011/android-listview)

# Library
+ support-package
+ apache-commons
+ android-pulltorefresh
+ Actionbar-sherlock
コアファイルを移動した後に忘れずにやること

wordpressのコアファイルをドメイン直下のフォルダに置くのではなくて、別のディレクトリに置く。ここまではwordpressの公式見ればやりかたはわかる。

ただ、この方法では、wordpressによって生成される画像パスにコアファイルのディレクトリ名が入っているので、名前を変えることでログイン画面をわかりにくくしたい、という要望を満たすという観点では残念な感じである。

そこで、`wp_get_attachemnt_thumb_url`と`wp_get_attachemnt_url`のフィルターで生成される画像パスからディレクトリ名を除去し、nginxやapacheなりのrewriteでwp-content/uploadsに来たアクセスをコアファイルディレクトリ/wp-content/uploads/...に変えてあげると良い。

pluginで使っているcssやjsがある場合は、それもフィルターでやればよいでしょう。
WebViewネイティブアプリのアクセストラッキング


##方法
webかネイティブかUAで判別し、該当のプロパティに対してsendする。



##複数プロパティでの管理

ga('create', ..)の第三引数にnameを持たせることで、複数アカウントを管理できる（UniversalAnalyticsのみ）


```
var strUA = "";
strUA = navigator.userAgent.toLowerCase();

if(strUA.indexOf("native-ios") != -1){

  #ネイティブiOSの場合
  ga('create', 'UA-XXXX-Y', {'name':'ios'} );
  ga('ios.send', 'pageview');

} else if(strUA.indexOf("native-android") != -1) {

  #ネイティブAndroidの場合
  ga('create', 'UA-XXXX-Y', {'name':'android'} );
  ga('android.send', 'pageview');

} else{
  
  #webの場合
  ga('create', 'UA-XXXX-X', {'name':'web'} );
  ga('web.send', 'pageview');

}
```



###参考
【最新版】Google Analyticsチートシート完全マニュアル（日本語解説付）
http://www.find-job.net/startup/analytics_cheatsheet

##注意事項
 ※当然、正しくUA判定出来るように、nativeのios/androidを判別出来る必要がある。
webviewアプリなら、大抵はクライアント側でその準備をしてあるはず・・という想定。

UIWebViewを用いる際にUserAgentを独自に設定する方法
http://www.yoheim.net/blog.php?q=20121001


##他の手段
Google Analytics SDKを使う。
（というか、ネイティブの場合の正攻法はこちら）

Google Analytics SDK を使ったスマホアプリのトラッキング
http://www.ayudante.jp/column/2012-08-24/13-10/

ネイティブのイベントも取得できる。

が、アップデートが必要なので、今回は本題のサーバ側のみの方法で手軽に対応。
secure erase する秘伝のタレ

secure erase する秘伝のタレ

    hdparm --user-master u --security-set-pass password /dev/sdX
    time hdparm --user-master u --security-erase password /dev/sdX

`hdparm -I /dev/sda` などして __not frozen__ であることを確認する

もしなってなかったらプラグを抜き差しする（遠隔からは BIOS から無効にしてまた有効にする rebootではなく、一度完全に電源を切ること）
ansibleでMySQLをインストールするPlaybookメモ


*ansible初心者なので、改善点とかありましたら教えてください*

予めインストールするMySQLのバージョンのrpmを用意してください。

```XML:install-mysql-playbook.yml
- hosts: [db-server]
  vars:
    local_mysql_rpm_dir: '/root/ansible/mysql-rpm'

    src_dir: '/usr/local/src/mysql'
    version: 5.6.14-1

    mysql_shared_compat: "MySQL-shared-compat-$version.linux_glibc2.5.x86_64.rpm"
    mysql_devel: "MySQL-devel-$version.linux_glibc2.5.x86_64.rpm"
    mysql_server: "MySQL-server-$version.linux_glibc2.5.x86_64.rpm"
    mysql_client: "MySQL-client-$version.linux_glibc2.5.x86_64.rpm"

  tasks:
    - name: "remove pre installed mysql-libs.x86_64"
      yum: name="mysql-libs.x86_64" state=absent

    - name: "remove pre installed mysql"
      yum: name="mysql" state=absent

    - name: "remove pre installed mysql-server"
      yum: name="mysql-server" state=absent

    - name: "mkdir dest src dir"
      file: dest=$src_dir state=directory

    - name: "copy local mysql rpms to target"
    - copy: src=$local_mysql_rpm_dir/$mysql_shared_compat dest=$src_dir
    - copy: src=$local_mysql_rpm_dir/$mysql_server dest=$src_dir
    - copy: src=$local_mysql_rpm_dir/$mysql_client dest=$src_dir
    - copy: src=$local_mysql_rpm_dir/$mysql_devel dest=$src_dir

    - name: "install MySQL-shared-compat"
      yum: name=$src_dir/$mysql_shared_compat state=present

    - name: "install MySQL-devel"
      yum: name=$src_dir/$mysql_devel state=present

    - name: "install MySQL-server"
      yum: name=$src_dir/$mysql_server state=present

    - name: "install MySQL-client"
      yum: name=$src_dir/$mysql_client state=present
```

起動確認

```Bash
/etc/init.d/mysql start
```

「Starting MySQL. SUCCESS! 」と表示されてば成功です。
Vimで、デフォルト定義されているEXコマンドを独自のものでおきかえる

# 問題

たとえば、`:bd`でwindowまで消えちゃうのがうざい。[bufkill.vim](https://github.com/vim-scripts/bufkill.vim)を使うと、`:BD`でバッファをkillしつつ、windowはそのまま残すということができる。しかし、そもそも`:bd`がそのような挙動であってほしい。

# 解決

[vim-altercmd](https://github.com/kana/vim-altercmd/)を使う。

  * オリジナル: https://github.com/kana/vim-altercmd/
  * フォーク版: https://github.com/tyru/vim-altercmd/
    * [オリジナルのkana版とtyru版の違い · tyru/vim-altercmd Wiki](https://github.com/tyru/vim-altercmd/wiki/%E3%82%AA%E3%83%AA%E3%82%B8%E3%83%8A%E3%83%AB%E3%81%AEkana%E7%89%88%E3%81%A8tyru%E7%89%88%E3%81%AE%E9%81%95%E3%81%84)

これを用いて、以下のように設定する。

```vim
AlterCommand bd BD
AlterCommand BD bd
```

これで、`:bd`と`:BD`を交換することができた。

# 議論

特になし。

この件、以下でご教示いただいた。
http://lingr.com/room/vim/archives/2013/10/16#message-17075846
ansibleでApache httpdをソースからコンパイルしてインストールするPlaybookメモ


*ansible初心者なので、改善点とかありましたら教えてください*

```Bash:intall-httpd-playbook.yml
- hosts: [web-server]
  vars:
    src_dir: '/usr/local/src'

    pcre_version: "8.33"
    pcre_name: "pcre-$pcre_version"
    pcre_dl_url: "ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/$pcre_name.tar.gz"

    httpd_version: "2.4.6"
    httpd_name: "httpd-$httpd_version"
    httpd_dl_url: "http://ftp.jaist.ac.jp/pub/apache/httpd/httpd-$httpd_version.tar.gz"
    apache_name: "apache-$httpd_version"

    apr_version: "1.4.8"
    apr_name: "apr-$apr_version"
    apr_dl_url: "http://ftp.yz.yamagata-u.ac.jp/pub/network/apache/apr/apr-$apr_version.tar.gz"

    apr_util_version: "1.5.2"
    apr_util_name: "apr-util-$apr_util_version"
    apr_util_dl_url: http://ftp.kddilabs.jp/infosystems/apache//apr/$apr_util_name.tar.gz

  tasks:
    - name: "make build dir"
      file: dest=$src_dir state=directory

    - name: "wget the pcre"
      get_url: url=$pcre_dl_url dest=$src_dir

    - name: "expand the pcre src"
      command: tar zxvf $src_dir/$pcre_name.tar.gz chdir=$src_dir

    - name: "pcre configure"
      command: ./configure chdir=$src_dir/$pcre_name

    - name: "pcre make"
      command: make chdir=$src_dir/$pcre_name

    - name: "pcre install"
      command: make install chdir=$src_dir/$pcre_name

    - name: "wget the httpd src"
      get_url: url=$httpd_dl_url dest=$src_dir

    - name: "expand httpd src"
      command: tar zxvf $httpd_name.tar.gz chdir=$src_dir

    - name: "wget the apr"
      get_url: url=$apr_dl_url dest=$src_dir

    - name: "expand apr src"
      command: tar zxvf $apr_name.tar.gz chdir=$src_dir

    - name: "copy apr to httpd_dir"
      command: cp -rp $src_dir/$apr_name $src_dir/$httpd_name/srclib/apr

    - name: "wget ther apr-util"
      get_url: url=$apr_util_dl_url dest=$src_dir

    - name: "expand ther src"
      command: tar zxvf $apr_util_name.tar.gz chdir=$src_dir

    - name: "copy apr-util to httpd_dir"
      command: cp -rp $src_dir/$apr_util_name $src_dir/$httpd_name/srclib/apr-util

    - name: "mkdir httpd"
      file: dest=/usr/local/$apache_name state=directory

    - name: "make link"
      file: src=/usr/local/$apache_name dest=/usr/local/apache2 state=link

    - name: "mkdir log"
      file: dest=/var/log/httpd state=directory

    - name: "configure httpd"
      command: chdir=$src_dir/$httpd_name env OPTIM="-02" ./configure --prefix=/usr/local/apache2 --enable-module=so --enable-module=proxy --enable-module=rewrite --enable-module=expires

    - name: "httpd make"
      command: make chdir=$src_dir/$httpd_name

    - name: "httpd install"
      command: make install chdir=$src_dir/$httpd_name

    - name: "copy apachectl"
      command: cp -i /usr/local/apache2/bin/apachectl /etc/init.d/httpd

    - name: "chkconfig"
      action: service name=httpd state=started enabled=yes
```

```Bash:実行方法
ansible-playbook install-httpd-playbook.yml -i hosts -k
```

*configureの箇所で改行したかったんですけど、分からず1行にしてしましました。どこかで見たんだけどな・・・*
質問: 綺麗なコミットログの作り方

`git branch`:

* master
* myWorks

```bash
# masterをremoteに合わせて前進
git checkout master
git pull --rebase origin master

# 作業ブランチをmasterから分岐し直す
git checkout myWorks
git rebase master

# 作業ブランチをmasterに混ぜ込む
git checkout master
git merge --no-ff myWorks
```

コミットログを綺麗にしようと思ったらこんな感じ？

もっと簡単なやり方ってないですか？
addTo* が mock されなくて鬱になったとき

例えばこんなユーティリティ・メソッドを定義して

```groovy
    static addAddTos(Class clazz) {
        def hasMany = clazz.metaClass.properties.find { it.name == "hasMany" }
        hasMany.getProperty(clazz).each { k, v ->
            clazz.metaClass."addTo${k.capitalize()}" = { delegate."${k}"?delegate."${k}".add(it):(delegate."${k}"=[it]); delegate }
        }
    }
```

こんな風に使ってやれば

```groovy
    def setup() {
        ......
        ...addAddTos(Foo)
        ......
    }
```

ちょっとは気が晴れるかも
VCS入門：付録A 参考文献

[↑目次](331715ae02cc54c5deb1 "目次") | [← 7章 SCMの次のステップ](71bdd9f9ed75c985616d "SCMの次のステップ")

# 付録A 参考文献

- [バージョン管理システム - Wikipedia](http://ja.wikipedia.org/wiki/バージョン管理システム)
- [ソフトウェア構成管理 - Wikipedia](http://ja.wikipedia.org/wiki/ソフトウェア構成管理)
- [構成管理 - Wikipedia](http://ja.wikipedia.org/wiki/構成管理)
- [ソフトウェア構成管理(SCM)の入門資料 | 長沢智治のブログ | SoftwareEngineeringPlatform.com](http://softwareengineeringplatform.com/articles/scm-fundamental-material/)
- [チケット駆動開発 - Wikipedia](http://ja.wikipedia.org/wiki/チケット駆動開発)
VCS入門：6章 SCMを意識したVCS利用

[↑目次](331715ae02cc54c5deb1 "目次") | [← 5章 SCMという智慧](78255dae9f7320b2dd17 "5章 SCMという智慧")

# 6章 SCMを意識したVCS利用

SCMの目的が分かったところで、その考えをどのようにVCS利用方法に取り入れていくか、考えていきましょう。

## VCSの活用法

SCMでやりたいことを今一度まとめると、仕様変更、バグなどをトリガーとした「変更依頼」で発生した、ソフトウェア構成の変更履歴について、

- 識別
- 参照
- 再現

が出来なくてはなりません。

このことから、VCSを使う際には次のことを意識する必要があります。

1. ソースコード以外も管理する
1. 履歴を識別するメタデータを付加する
1. 「タグ」を活用する

### 1. ソースコード以外も管理する

VCSに対するよくある誤解の一つが「ソースコードのバージョン管理を行うツール」というものです。ですが、前の章で述べたように「ソースコード」はソフトウェアを構成する要素のほんの一部でしかありません。ソースコードだけ履歴を残しても、ソフトウェアを再現することはできないのです。

したがって、原則としてソースコードの他に設定ファイルや設計書などのドキュメント類も、VCSで管理する必要があります。

![VCS管理対象](//qiita-image-store.s3.amazonaws.com/0/12039/e2a58b4a-9169-8ec3-c750-1f1f9047001d.jpeg)

図6-1 VCS管理対象

### 2. 履歴を識別するメタデータを付加する

ソースコード以外の履歴を管理するようにしたとして、今度はその履歴をどうやって「識別」するのかを考えなければいけません。4章で紹介した

- 「どのように」ではなく、その作業を「何故」「何を」「何のために」行うかをメッセージに残す

という使い方を心がけても、どんな変更が行われたかはわかりますが、それがどういった「変更依頼」を元に行われたかはわかりません。

この問題を解決するには、まずVCSとは別のところで「変更依頼」が管理されていることが前提として必要になります。それは例えば、Excelなどで作られた「バグ票」、「QA票」、「仕様変更依頼書」などのドキュメント類があるでしょう。

こういったドキュメントには、その名前とともに識別するための「番号」などが、大体付いています。この情報を利用しない手はありません。

つまりどうするかというと、履歴のメッセージに「文書名-文書番号」といった「メタデータ」を埋め込んでしまうのです。このルールがメンバーで共有できてさえいれば、この「メタデータ」で変更履歴を識別することができるようになります。

| 日時 |コメント | 作業者 |
|:--:|:--|:--:|
| 4/14 10:35 |バグ管理票-XX001 : ○○が××になっていたため、△△に修正する。 | 高橋太郎 |
|4/13 17:45 |QA管理表-1 : ○○については××とするよう変更する。 | 田中一郎 |
| ... |... | ... |

図6-2 メタデータを付加した履歴

### 3. 「タグ」を活用する

VCSには「タグ」という、特定のバージョンを識別するためのマークを付ける機能があります。

この機能を使って、製品のリリースなどの重要なタイミングに「タグ」を付けることで、検索するまでもなくバージョンを識別することができるようになります。

![リリースバージョンへのタグ付け](//qiita-image-store.s3.amazonaws.com/0/12039/b06eb935-4b18-1d5f-3e48-2e324cff2bc7.jpeg)

図6-3 リリースバージョンへのタグ付け

## 必要なのはルール、プロセス

上記のVCS利用方法は、事前に「どこまでをVCSで管理する」、「変更依頼は必ず文書に残す」といったような、ルール、プロセスを定め、運用されているのが前提です。これらができていなければ、そもそも管理などできないからです。

もし、あなたがいる組織でこのようなルール、プロセスが無いようなら、積極的にそのルール、プロセスを定めるように働きかけてみてください。

## VCSだけではできないこともある

ここまで、SCMを意識したVCSの使い方を説明してきましたが、メッセージに文書番号を入れるなど、まだ運用に頼っている部分も多いです。

また、ソフトウェアを構成する要素の内、ソースコードから成果物を作る部分や、その成果物のテスト、関係者への通知など、VCSだけではどうにもならない部分もあります。

こういったものにどう対処すれば良いかというと、「VCS以外のツールに頼る」が正解です。

所詮VCSは「バージョンを管理するだけ」の機能しか持ち合わせていません。苦手な部分は他のツールに任せてしまいましょう。

次の章で、そういったツールのごく一部と活用イメージを紹介します。

[→ 7章 SCMの次のステップ](71bdd9f9ed75c985616d "7章 SCMの次のステップ")
VCS入門：5章 SCMという智慧

[↑目次](331715ae02cc54c5deb1 "目次") | [← 4章 VCSのみでの限界](7148358f80e1309aeb36 "4章 VCSのみでの限界")

# 5章 SCMという智慧

VCSという「ツール」を導入しても、そこに「ルール」が加わらなければ意味がないと言いました。ここでの「ルール」とは何なのか、もう少し詳しく考えてみましょう。

## 何がしたいのか？

ここで最初に立ち戻り、VCSを導入することで何がしたかったのか、考えてみることにしましょう。

VCSがない状態では、次の問題がありました。

- 作業履歴が残らない
- 他の人と作業がぶつかる

また、VCSを導入した後、ルールが無ければこんな問題がありました。

- 履歴から目的の変更を探せない
- 履歴の一つが、どういったきっかけで変更されたかわからない

以上の問題から、VCSを使って行いたかった事とは、

- 各バージョンを識別したい
- 特定のバージョンを再現したい
- 何故変更が行われたのか知りたい
- 他の人と連携して作業を行いたい

ともいえると思います。

これらを実現するための考え方が「SCM（Software Configuration Management、ソフトウェア構成管理）」です。

## 「構成」とは

いきなりSCMと言われても何のことかわからないと思います。そんな時は、問題を小さくしていきましょう。

- Software : ソフトウェア
- Configuration : 構成
- Management : 管理

ですので、そのまま読めば「ソフトウェアの構成を管理する」となります。「ソフトウェア」と「管理」は良いとしても、「構成」とは何のことでしょうか？

「構成」を辞書で引くと、次のように説明されています。

> こうせい 0 【構成】
>
> （名）スル
>
> 1. いくつかの要素を組み立てて一つのまとまりあるものにすること。また、その組み立て。
> 「社会を―する一員」
>
> 1. 〔哲〕〔construction〕経験によらずに概念・形式・イメージなどを操作することで対象を組み立てること。
>
> \- [構成とは - 三省堂 大辞林 Weblio辞書](http://www.weblio.jp/content/構成) より -

つまり、「ソフトウェア構成」とは「ソフトウェアというまとまりがどのような要素の組み合わせでできているか」となります。

![ソフトウェア構成](//qiita-image-store.s3.amazonaws.com/0/12039/33b133eb-b9e0-27f4-cd44-bf97454fa9ca.jpeg)

図5-1 ソフトウェア構成

## ソフトウェアを構成する「要素」

では、ソフトウェアがどのような「要素」から成り立っているのか考えてみると、

- ソースコード
- コンパイラー
- ライブラリ
- ランタイム

といったものが出てきます。もう一歩踏み込んで、ソフトウェアの振る舞いがどこから出てきたかを考えると、

- 設計書
- バグ管理表
- QA票

といったドキュメント類まで範囲に入ります。さらに踏み込むと、その「要素」の大本は、「こうしたい」、「こうであってほしい」という

- 依頼

もまた、ソフトウェアを構成する要素と考えることができます。

![ソフトウェアを構成する要素](//qiita-image-store.s3.amazonaws.com/0/12039/e054a1c7-1c4e-abdd-df04-35e7ff8d4ee3.jpeg)

図5-2 ソフトウェアを構成する要素

## どう管理するのか

では、構成を管理するとはどういうことかを、そもそもの目的から導くと、次のようになります。

**ある時点でのソフトウェアを構成する要素すべてを、識別、再現、追跡出来るようメンバー間で成果物を共有し、連携を促すための仕組み、ルール、プロセスを構築し、運用する**

このためには、最低限その変更がどのような「依頼」によるものなのかを、それぞれの要素に紐付け、その組み合わせの履歴を残していかなければなりません。

そのために、VCSをどのように活用すればよいのか次の章で説明していきましょう。

[→ 6章 SCMを意識したVCS利用](200a6c8b6d145aa5d621 "6章 SCMを意識したVCS利用")
VCS入門：4章 VCSのみでの限界

[↑目次](331715ae02cc54c5deb1 "目次") | [← 3章 VCSのある世界](7709aec58972d679ea57 "3章 VCSのある世界")

# 4章 VCSのみでの限界

さて、VCSを使うと、それだけでハッピーになれるかというと、世の中そんなに甘くありません。例えツールを使っていても、協調作業（コラボレーション）を行うには、各人が好き勝手していては上手くいくはずもありません。

そんな世界をまた少し覗いてみましょう。

## 数週間後

VCSを導入したあなたのチームは、しばらく何の問題もなく開発を続けていました。

そんなある日、何日か前に行った仕様変更で、影響があった成果物を調査してほしいという依頼を受けました。何でも、どうやらその仕様変更で、他の機能の動作が少しおかしくなっているらしいのです。

あなたは早速VCSの機能を使って、リポジトリの変更履歴を見てみました。

| 日時 |コメント | 作業者 |
|:--:|:--|:--:|
| 4/14 13:31 |○○機能に△△処理と××処理を追加した。ついでに□□のバグを修正 | 斎藤京子 |
| 4/14 11:55 |昼休み前のコミット | 田中一郎 |
| 4/14 10:35 |コンパイル失敗するけど、原因が分からないので一応コミット | 高橋太郎 |
| 4/14 10:14 |hoge.csファイルにdoFugaメソッドを追加 | 田中一郎| 
| 4/13 18:01 |4/13日分コミット | 鈴木花子 |
| ... |... | ... |

履歴を見ると、メンバーは好き勝手にリポジトリに反映しているようです。そのため、目的の仕様変更に関連する記述がなかなか見つかりません。

30分ほどして、ようやく目的の履歴を見つけました。が、今度は一度に色んな変更、追加、削除を、いっぺんに行っていることが分かりました。

仕方ないので、その履歴をPC内の作業フォルダに取り込んで再現した後、変更箇所を一つ一つ確認していかなければなりませんでした。

## VCSでもアンハッピー

VCSを使えばハッピー・・・なはずでしたが、どうやらVCSを使った「だけ」では、そうはならないようです。

問題を整理してみると、リポジトリへの反映時に次のようなことが起きているようです。

1. 「とりあえず」反映している
1. 「どうやったのか」をメッセージに書いている
1. 動かないものも反映している
1. 複数の変更を盛り込んでいる

### 1. 「とりあえず」反映している

| 日時 |コメント | 作業者 |
|:--:|:--|:--:|
| 4/14 11:55 |昼休み前のコミット | 田中一郎 |
| 4/13 18:01 |4/13日分コミット | 鈴木花子 |

**作業の区切り** ではなく、「帰る前」、「休憩前」といった **時間の区切り** で、リポジトリに反映しているケースです。

このケースでは、本来関係ないもの、中途半端なものが、その履歴に含まれてしまう懸念があります。そのため、後で履歴を参照する際にノイズにしかなりませんし、メッセージを見ても何もわかりません。

### 2. 「どうやったのか」をメッセージに書いている

| 日時 |コメント | 作業者 |
|:--:|:--|:--:|
| 4/14 10:14 |hoge.csファイルにdoFugaメソッドを追加 | 田中一郎| 

こちらは「とりあえず」よりは、一見幾分マシのように思えます。

しかし、実際のVCSを履歴を見ると「どこをどう直したか」は簡単に分かります（例では省いています）。

反面、「何故」変更したか、「何のために」変更したかについては、履歴からではわかりません。したがって、履歴を調べる為の情報としては、あと一歩足りません。

### 3. 動かないものも反映している

| 日時 |コメント | 作業者 |
|:--:|:--|:--:|
| 4/14 10:35 |コンパイル失敗するけど、原因が分からないので一応コミット | 高橋太郎 |

このケースは一番避けなければならないやり方です。

なぜなら、「動かない」ものが成果物として管理されてしまうため、例えば他の人がテストをしようとしてもできない、といったチーム全体の問題に発展してしまう可能性があるためです。

### 4. 複数の変更を盛り込んでいる

| 日時 |コメント | 作業者 |
|:--:|:--|:--:|
| 4/14 13:31 |○○機能に△△処理と××処理を追加した。ついでに□□のバグを修正 | 斎藤京子 |

このケースはこれまでで一番マシではあります。何を行ったかも明確です。

しかし、「複数の変更」を一度に反映していると、その履歴で行われたファイルの変更が、「どの」変更によるものなのかが分からなくなってしまいます。そのため、上のシミュレーションで行ったように、手作業でファイル変更を切り分ける必要があります。

## ではどうするか？

VCSは便利ですが、あくまでツールです。ツールは人の作業を補助はしてくれますが、それだけではうまくいきません。ましてやシステム開発はチームで行うものです。2人以上がコミュニケーションを取るには、最低限のルールが必要です。

このことを言い換えると、VCSはルールと対になって初めてその真価を発揮する、ということです。

そこで、まずは最低限作業フォルダーの内容をリポジトリに反映する際のルールを決めましょう。先ほどの良くない例を見れば、守るべきルールが見えてきます。それが次のルールです。

- 「時間」ではなく「作業」の単位で行う
- 「どのように」ではなく、その作業を「何故」「何を」「何のために」行うかをメッセージに残す
- 動かない成果物はリポジトリに反映しない
- 一度に一つの変更だけ反映する

これらのことから、VCSのVersion（バージョン、版）とは「ファイル単位の改訂内容」を表すのではないことがわかると思います。Versionとは「作業単位の変更内容」を表すのです。このことを忘れないようにしてください。

そして、上記ルールに従って考えると、「一言で何をやるのか表せる単位で作業を区切って、リポジトリに反映する」ことが重要です。一言で表せないのならば、それは行おうとしている作業が大きすぎるのです。こんなときは、より小さな単位に分けて作業を行えないか考えてみましょう。

なお、この考えはやむなく共有フォルダーで管理しなければならないようなときでも大事な考えです。作業単位で考えることで、バックアップを取るタイミング≒作業の開始前後と考えることができ、多少は管理がしやすくなります。

さて、ここではリポジトリへの反映時のルールだけを定めました。しかし、開発には様々な要因が絡むものです。次の章ではそういった要因を管理するための考え方について説明していきましょう。

[→ 5章 SCMという智慧](78255dae9f7320b2dd17 "5章 SCMという智慧")
VCS入門：3章 VCSのある世界

[↑目次](331715ae02cc54c5deb1 "目次") | [← 2章 VCSでできること](f00bf3d14b9cd63b31d4 "2章 VCSでできること")

# 3章 VCSのある世界

さて、VCSが無いことで痛い目を見た開発プロジェクトが、その後VCSを導入した世界をまた覗いてみましょう。

## 後日
あなたの開発チームは先日の反省を踏まえ、VCSを導入することになり、サーバーにリポジトリが用意されました。

![リポジトリ](//qiita-image-store.s3.amazonaws.com/0/12039/7986b147-2b0d-8215-6310-5399a120ff1f.jpeg)

図3-1 リポジトリ

あなたはまず、作業を行う準備として、PC内に作業フォルダーを作成しました。

![作業フォルダー作成](//qiita-image-store.s3.amazonaws.com/0/12039/e120d6a8-1200-dc22-33b5-4f7e64a0a873.jpeg)

図3-2 作業フォルダー作成

次に作業対象のファイルの作業コピーを、リポジトリから取得しました。

![作業コピー取得](//qiita-image-store.s3.amazonaws.com/0/12039/24981d08-5641-1e37-c111-8227313db38f.jpeg)

図3-3 作業コピー取得

あなたは、幾つかの作業コピーのドキュメントやソースを変更し、動作を確認しました。

![作業コピー編集](//qiita-image-store.s3.amazonaws.com/0/12039/415129db-0b99-5301-4b67-c4249fc4685b.jpeg)

図3-4 作業コピー編集

作業を終えたので、作業コピーの変更をリポジトリに反映しようとしました。すると、競合が発生したため、反映処理に失敗しました。

![競合発生](//qiita-image-store.s3.amazonaws.com/0/12039/ff0c1ee8-f087-01f8-3b36-47bb5ef3752b.jpeg)

図3-5 競合発生

発生した競合についてVCSを使って調べてみました。すると、田中一郎さんが先にリポジトリに反映した変更と競合していることが分かりました。

あなたは早速田中一郎さんと相談し、田中一郎さんの編集した部分を残しつつ、自分の変更を競合したファイルに反映しました。

![競合ファイルの編集](//qiita-image-store.s3.amazonaws.com/0/12039/de06903a-e793-d3f9-06d0-df523f0afaf1.jpeg)

図3-6 競合ファイルの編集

競合が無くなったところで、改めてリポジトリに変更を反映して無事作業を終えました。

![リポジトリに反映](//qiita-image-store.s3.amazonaws.com/0/12039/6786601f-9000-afed-7194-c365191ca13f.jpeg)

図3-7 リポジトリに反映

## VCSでハッピーに

これまでシミュレーションしてきた話は、いささか恣意的な例であることは否定しません。

しかし、実際の開発現場ではもっと構造が複雑で、対象となるファイルも多く、メンバーも多数であることがほとんどです。こういった環境では、共有フォルダーでの管理は、事実上不可能であるといっても過言ではありません。

そういった複雑な環境でもVCSというツールを使うことで、自分も他のメンバーも、安心して作業を進めることができます。こういった連携を行うためにも、VCSは不可欠なものなのです。

[→ 4章 VCSのみでの限界](7148358f80e1309aeb36 "4章 VCSのみでの限界")
VCS入門：2章 VCSでできること

[↑目次](331715ae02cc54c5deb1 "目次") | [← 1章 VCSのない世界](17e50dfe39df5f47b77e "1章 VCSのない世界")

# 2章 VCSでできること

VCSのない世界で何が起きるかイメージ出来たところで、VCSで何ができるのかを見ていきましょう。

## VCSの力

VCSの能力は、主に次の2つです。

1. 履歴を残す
1. 作業の競合を防ぐ

### 1. 履歴を残す

VCSでできることの一番大きなものは「作業履歴を残すこと」です。これは、言い換えると「誰が」「いつ」「何を」したかが残るということになります。

| 日時 |コメント | 作業者 |
|:----------:|:-------------------|:------:|
| 4/14 10:14 |○○機能を追加      |田中一郎|
| 4/14 09:34 |△△機能のバグを修正|鈴木花子|
|    ...     |...                 |...     |

表2-1 履歴のイメージ

なお、この「履歴」を管理する場所を「リポジトリ」と呼びます。また、履歴にはその履歴の内容を表すメッセージを付けることができます。

履歴が残ると何ができるのか。大まかには次の3つができるようになります。

- 参照
- 再現
- 分岐

一つずつ説明していきましょう。

#### (1) 参照

履歴をたどることで、誰がいつ何をやったかを確認することができます。一般的なVCSでは履歴の検索機能もありますので、簡単に対象となる履歴を参照することができます。

VCSのない世界では何が原因でテスト環境が壊れたのか、メンバーの記憶などを頼りにやらざるを得なかった作業が、VCSを使うことで「記憶」ではなく「記録」を元に行うことができるようになります。

また、変更されたファイルを指定して履歴を調べることもできる為、作業効率が大幅にアップします。

#### (2) 再現

VCSを使うと、履歴を参照するだけでなく、その時点の状態を再現することも可能です。つまり、動かなくなったとしても正常に動作していた状態に巻き戻せる、ということです。

この特徴を利用して、動作しなくなる一つ手前に戻り作業をやり直すといったことや、バグが発生する時点の成果物を、他のメンバーに確認してもらう、といったことが可能になります。

#### (3) 分岐

過去の状態を再現した上で、そこから別の変更を加えることで、履歴を分岐することもできます。つまり、複数のアプローチにより、作業を行うことができるということです。

このことから、動作しなくなる手前の状態を元に別の変更を加えていき、それぞれのパターンをメンバーに評価してもらう、といったこともできるようになります。

### 2. 作業の競合を防ぐ

VCSのもう一つの大きな機能が、「作業の競合を防ぐ」です。VCSを使うことで、誰かが先に変更していればそのことを検知し、VCSが競合箇所を知らせてくれます。

![競合検知イメージ](//qiita-image-store.s3.amazonaws.com/0/12039/4dfcfcd0-e602-95d0-b516-41e5b7b6187e.jpeg)

図2-1 競合検知イメージ

VCSのない世界では、同じファイルを複数人で同時に変更すると、後勝ちになってしまいます。すると、前に変更した人の変更内容が失われてしまい、時として動作不良を引き起こします。

しかし、VCSではこういったことは起こらず、誤って他のメンバーの変更内容を消してしまうことがありません。

また、使うVCSによっては、ファイル内の変更箇所を解析し、自動的に自分が行った変更内容と統合（マージ）する機能を持っています。この機能を活用することで、他のメンバーと効率よく並行して作業を進めることができるようになります。

VCSの特徴をつかんだところで、実際にVCSがある世界では、どのように開発が進むのか、次の章でシミュレーションしてみましょう。

[→ 3章 VCSのある世界](7709aec58972d679ea57 "3章 VCSのある世界")
VCS入門：1章 VCSのない世界

[↑目次](331715ae02cc54c5deb1 "目次")
 
# 1章 VCSのない世界

VCSについて学ぶ前に、VCSのない世界ではどんなことが起こるのか、ちょっと覗いてみましょう。

## ある日・・・

あなたはとあるシステム会社で開発を行うプログラマーです。今関わっている案件は小さなプロジェクトということもあり、少人数で開発を行っているため、ファイルサーバーの共有フォルダーで成果物を管理していました。

![共有フォルダー](//qiita-image-store.s3.amazonaws.com/0/12039/f1d853ff-83bd-c961-baec-7cef6523c303.jpeg)

図1-1 共有フォルダー

あなたは、今日もいつものように開発を行おうと作業を始めました。

### 1. 作業対象ファイルの取得

まずは作業対象となるドキュメント、ソースコードを共有フォルダーからコピーしなければなりません。あなたが昨日帰った後、他の誰かが作業しているかもしれないからです。

あなたはいつものように、PCの作業フォルダーに、共有フォルダーの内容一式を上書きコピーしました。

![作業対象ファイルの取得](//qiita-image-store.s3.amazonaws.com/0/12039/ec87b430-cc02-39ee-0581-7363df3f661e.jpeg)

図1-2 作業対象ファイルの取得

### 2. ファイルの編集

作業対象ファイルをコピーしたので、あなたは作業に入ります。ドキュメントを編集し、ソースコードを合わせて編集し、ビルドして結果を確認する、こういった作業を繰り返します。

![ファイルの編集](//qiita-image-store.s3.amazonaws.com/0/12039/43451fcc-9f00-6d29-0080-9a677c9bfdc0.jpeg)

図1-3 ファイルの編集

### 3. 作業結果の反映

気が付くとそろそろ業務時間が終わります。あなたはとりあえずここまでの作業結果をメンバーと共有するために、作業フォルダーを丸ごとファイルサーバーに上書きコピーしました。

![作業結果の反映](//qiita-image-store.s3.amazonaws.com/0/12039/9e51710b-9a3b-eb0e-8a07-bfa99a36e8df.jpeg)

図1-4 作業結果の反映

あなたは今日の仕事の充実感に浸りながら、帰路につきました。

## 明くる朝

次の日あなたが出社すると、何やらプロジェクトメンバーが揉めています。話を聞くと、システムをテストしようと思ったら、上手く動かなくなっていたと言うことです。

![テスト環境破壊](//qiita-image-store.s3.amazonaws.com/0/12039/3b4137fb-4440-1539-e15c-75f47c8e4447.jpeg)

図1-5 テスト環境破壊

あなたは早速メンバーと協力して、障害の原因を調べ始めました。しかし、なかなか原因がつかめません。

そして調査すること数時間、ようやく原因を見つけました。それはなんと、あなたが昨日編集したファイルが原因でした。同じファイルを他のメンバーも編集していたことに気付かずに、そのまま上書きコピーしてしまっていたことが原因でした。

あなたとメンバーは、各自のPCに残ったファイルを元に、修復と変更箇所の統合を行いました。しかし対象となるファイルの数が多く、その作業は深夜まで及び、ようやくシステムがテストできるようになりました。

## 何が問題だったのか？

以上のような出来事は、共有フォルダーで成果物を管理する限り、大なり小なり必ず起こります。その原因を詳しく見ていきましょう。

### 1. 作業対象ファイルの取得

まず、共有フォルダーから作業対象ファイルを取得する際の問題を見ていきましょう。

#### (1) 本当に「最新」なのか？

共有フォルダーであるということは、他の開発メンバーも常に変更が可能です。つまり、「最新」と思っているものが実はコピー途中であるといった可能性が否定できない
のです。

#### (2) 特定の「バージョン」を取得できない

共有フォルダーは常にもっとも後に変更されたものしか管理できません。したがって、「一昨日変更したファイルを元に変更したい」といったケースに対応できません。

### 2. ファイルの編集

ファイルを編集する際の問題点は次の通りです。

#### (1) どのファイルを変更したのかわかりにくい

変更したファイルを簡単に特定する方法が無いため、変更後の共有フォルダーへの反映時に、漏れる可能性があります。

#### (2) 元に戻せない

何らかの失敗や、変更のやり直し、試行錯誤段階など、変更を無かったことにしたいケースが開発の中ではよくあります。しかし、共有フォルダーからコピーするやり方では、再度コピーするしか方法がありません。

#### (3) 作業対象の「バージョン」が一定しない

上記の元に戻す際、共有フォルダーから再度コピーすると、最初にコピーしたときと作業対象のファイルの「バージョン」が変わってしまいます（この作業対象のファイルの「バージョン」を、作業ベースと呼びます）。

### 3. 作業結果の反映

作業結果を共有フォルダーに反映するときも、落とし穴があります。

#### (1) 他の人が同時に変更している可能性

自分が編集したファイルを他の人が同時に変更している可能性が常にあります。この時は「後勝ち」となるため、先に共有フォルダーにアップした人の変更分が、失われてしまいます。

#### (2) 誤操作で削除する可能性

共有フォルダーは、誤操作で簡単にファイルを消すことができます。

#### (3) 環境を壊す可能性

(1)、(2)にも関連しますが、開発中の成果物が不正な状態になることで、簡単に環境が壊れてしまい、テストが不可能になってしまいます。

## では、どうするのか？

人間は間違う生き物です。これらの問題は「気を付ける」レベルで対処できるものではありません。

では、どうするのか？

そこで登場するのがVCSです。人間が間違いやすいところを、ツールでサポートすることにより、システム開発を円滑に進めることができます。

では、次の章でVCSの特徴を学んでいきましょう。

[→ 2章 VCSでできること](f00bf3d14b9cd63b31d4 "2章 VCSでできること")