かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Git 過去の特定のコミット位置からブランチを切りたい

今のブランチの途中に戻って先に別の開発をしないといけないとか、特定のコミット位置から別のブランチを作成したい時のアレ。

例えば

$ git log --all --decorate --graph --oneline
* ab7b929 (HEAD -> develop) 図書館にレシピの本を置いたよ。たーのしー
* 7cb8ef3 図書館をつくったよ。わーい
* 9f0e540 服がぬげたよ。
* 85767d5 カレーはからい。たーのしー
* 175658e 温泉がみつかったよ。
* a9b3d53 ボスがふえたよ。たーのしー
* e0e054e じゃぱりまんを作ったよ。わーい
* 94b8397 でんち
* 6fe1920 バスてきなもの。たーのしー
* 98f5f9b ジャパリパークの敷地をつくったよ。わーい。

94b8397 でんち から別のブランチを切ってジャパリバスを作りたいような場合、
今のリポジトリをリモートにpushしているならgit reset --hard HEAD^ で必要なところまで戻してブランチを切った後、git pull origin でもとに戻しても良いのだけれどチョットめんどーです。

結論

$ git checkout -b <new_branch> <commit_hash>

で、特定のコミット位置からブランチを切ることができる。 (∩´∀`)∩わーい

起点となる位置を指定してブランチを切っている

リモートのブランチをローカルにチェックアウトして持ってくるような時、次のようなコマンドを使います。

$ git checkout -b <ローカルに作成するブランチ名> origin/<リモートのブランチ名>

これは origin/<リモートのブランチ名> の部分が「どこを基点にするか」という意味なようでコミットハッシュを指定すれば、そのコミット位置を基点にブランチを作成することができます。

先の例の 94b8397 でんち から create-bus ブランチを切る場合は下記のような感じ。

$ git checkout -b create-bus 94b8397

これで新しいcreate-busブランチが作成されそこにチェックアウトされます

$ git log --all --decorate --graph --oneline
* ab7b929 (develop) 図書館にレシピの本を置いたよ。たーのしー
* 7cb8ef3 図書館をつくったよ。わーい
* 9f0e540 服がぬげたよ。
* 85767d5 カレーはからい。たーのしー
* 175658e 温泉がみつかったよ。
* a9b3d53 ボスがふえたよ。たーのしー
* e0e054e じゃぱりまんを作ったよ。わーい
* 94b8397 でんち (HEAD -> create-bus)
* 6fe1920 バスてきなもの。たーのしー
* 98f5f9b ジャパリパークの敷地をつくったよ。わーい。

わーい

基点の指定は checkout の直後でもOK

$ git checkout <基点となる場所> -b <作成するブランチ名>

基点となる場所をcheckout-b の間に書いてもOKみたいです。
 

ちょっとした事だけれど、覚えてると必要になった時 reset の力技しなくていいから幸せになれそう。


IE11バグ display table, table-cell 内で max-width が効かない

IE11… またお前か!
max-widthのバグが多いですねw

display: table, display: table-cell の中にある img タグのmax-width: 100% が無視されて、要素からはみ出したりしてしまう問題。

<div class="table">
  <div class="table-cell">
    <img class="max-width-100per">
  </div>
</div>

table-layout: fixed を使うと max-width が効く

display: table を持っている要素に table-layout: fixed を設定すると、table-cell 中にある画像の max-width が有効になる。

‘table-cell’ 内に inner のdiv 要素などを置いても画像の max-width は無視されてしました…

サンプル

See the Pen IE11 max-width un available in display: table, table-cell by KIKIKI (@chaika-design) on CodePen.

IE 滅ぶべし!


[参考]

IEバグ inline-block 内の要素に max-width が効かないにハマる

コーディングをしていて IE で崩れていると連絡を受けてしまいました。
IE11のサポートしてたのですが、まぁ流石にもうIEでも大丈夫だろうと思ってたら、まさかIE6時代のようにバグに遭遇してしまいました。
マイクロソフトを信じたのが間違いだった…

発生していたバグ: inline-blockが親要素を突き破ってしまう例

inline-blockで横並びにした要素内の画像が親要素の幅を無視して表示されてしまっていた。
f:id:kikiki-kiki:20170707021555p:plain

HTML

div(class="inline-block width200px")
  div(class="inline-block")
    img(src="width 300px の画像")

div(class="block width200px")
  div(class="inline-block")
    img(src="width 300px の画像")

CSS

.block
  display: block
.inline-block
  display: inline-block
.width200px
  width: 200px
img
  max-width: 100% // width: 100% か max-width: 100% が無いと要素を突き破る
  height: auto

このようにwidthが200pxに指定された要素の中に、inline-block要素が入り、その中に200pxより大きな300px幅の画像が入るような構成の場合、Chrome, Safari, Firefoxではmax-widthが効き囲っている要素のwidthに画像が縮小されますがが、IE11はボックスは200pxのまま、inline-blockは画像幅の300pxになり親要素を突き破ってしまいます。

これは、画像のwidthmax-widthを指定してない時の表示と同じ感じでした。

IE 11 inline-block が中の要素の max-width: 100% が効かず親要素のwidth指定を無視してしまうっぽい

どうやらIE11ではmax-widthが指定された要素がinline-block要素中にある時、inline-block要素にwidthの指定がないと、例えinline-block要素の親要素にwidthが指定されていてもmax-widthが効かずこの幅を無視して突き破ってしまう問題があるようです。

解決方法 1. 画像の max-width:100% を辞めて、width: 100% にする

例えばレスポンシブなものとかで画像が拡大されてしまってもOKなら画像の max-width:100%width:100% に変更するのが簡単です。

div(class="inline-block width200px")
  div(class="inline-block")
    img(src="width 300px の画像") ← width: 100% にする

div(class="block width200px")
  div(class="inline-block")
    img(src="width 300px の画像") ← width: 100% にする

解決方法 2. inline-block に width の指定をする

表示幅が決まっているなら画像の親のinline-block要素にwidthの指定をするのが簡単です。

div(class="inline-block")  ← width 指定無くてもOK
  div(class="inline-block") ← この要素にwidthを指定
    img(src="width 300px の画像")

div(class="block")   ← width 指定無くてもOK
  div(class="inline-block") ← この要素にwidthを指定
    img(src="width 300px の画像")

※ widthの指定は 100% でもOKでした。

解決方法 2. 親要素を display: inline-block でないものにする

画像を囲っている要素の display: inline-blockdisplay: blockdisplay: inline にした場合も画像のmax-widthは親の親要素の指定に準拠して表示されました

div(class="inline-block width200px") 
  div(class="block") ← この要素のdisplayを block か inline に変更
    img(src="width 300px の画像")

div(class="block width200px")
  div(class="block")  ← この要素のdisplayを block か inline に変更
    img(src="width 300px の画像")
サンプル

ちょっと横幅があるので、CODEPNのサイトで見たほうが見やすいかも。
👇

See the Pen IE11 inline-block bug test by KIKIKI (@chaika-design) on CodePen.

 
最初 max-width が効かなくなってるって事に気づかなくて時間がかかってしまった。。。
未だにIE使ってる人なんているのかなぁ…? マイクロソフトのブラウザを使うのは今すぐやめるんだ!!


バグズ・ライフ [Blu-ray]

バグズ・ライフ [Blu-ray]

Git log 指定日からの作業内容をテキストファイルに出力したい

日報とか週報とかの提出を求められる時、いちいちテキストを書くのがメンドーなのでgit logをテキストファイルとして出力したいと思いました。

1. 今日とか1週間とかの git log をいい感じにして表示する

1週間分の自分のコミットを [YYYY-MM-DD] ハッシュ: コミット時のコメント で表示するコマンドは次のような感じでできました。

git log 
  --oneline --branches --reverse
  --author="$(git config user.name)"
  --date=iso --since="1week" 
  --pretty=format:"[%ad] %h: %s"

※ 長いから改行しているけど、コマンドラインなら1行で

使用しているオプションの説明

  • --branches
    全てのブランチを対象にする。
    オプションがないと今いるブランチのみが対象になる
  • --author
    author="name" で指定したAuthorのlogだけを取得
    "$(git config user.name)" とすれば .gitconfig にある user.name が対象になる
  • --date
    date="sio" - “YYYY-MM-DD HH mm ss フォーマット date="short" - YYYY-MM-DD フォーマット iso だと時刻が表示されてしまうので、作業時間がバレるのは ちょっとなー って場合は short を指定したほうが良さそう
  • --since
    いつのコミットから表示するか
    日報のように今日の分だけなら --since="today"
    --since="2017-06-25" のように日付を指定することもできる
  • --pretty=format:
    log を表示するフォーマット
    • %ad - コミットした時間 (-date= オプションに従った形式)
    • %h - コミットのハッシュ (短縮版)
    • %s - コミットコメント その他、詳しいオプションについては「git logのフォーマットを指定する - Qiita」に詳しく載っています

2. git log をファイルに出力する

いい感じの git log の表示オプションが作れたら、次はlogをファイルに出力します。
ファイルへの出力はコマンドの最後に > ファイル名 をつけるだけ。
log.txt というファイルを作って出力する場合は次のような感じ

git log 
  --oneline --branches --reverse
  --author="$(git config user.name)"
  --date=short --since="1week" 
  --pretty=format:"[%ad] %h: %s"
  > log.txt

※ 長いから改行しているけど、コマンドラインなら1行で

コマンドを実行すると、コマンドを実行した場所に log.txt が作成されます。

3. 作成したgit logのコマンドをエイリアス化して、日付を引数で渡せるようにする

毎回この長いコマンドを実行するのもメンドーなので、コマンドをエイリアス化して、開始日(since)の指定をオプションで渡せるようにしたいと思います。
git コマンドの エイリアスはglobalなら.~/.gitconfigに、現在の作業リポジトリなら .git/config ファイルに記入されるので、ここに直接コマンドを書いてエイリアスを作成します。

今回は、あるプロジェクトでだけ使いたかったので、リポジトリ内の .git/config に下記のエイリアスコマンドを追記しました。

[alias]
  logfile = "!f () {\
    git log --oneline --branches --reverse --date=short\
    --author=\"$(git config user.name)\"\
    --pretty=format:\"[%ad] %h: %s\"\
    --since=\"$1\"\
    > ../log.txt;\
  };f"

コマンドのエイリアス名は logfile としました。
で、この作業リポジトリ内で 次のようなコマンドを実行すると、、、

$ git logfile today

本日のコミットログをリポジトリのあるディレクトリの1つ上の階層に log.txt ファイルとして書き出せるようになりました!
₍₍ ᕕ(´ ω` )ᕗ⁾⁾ ヤッタゼ

global のエイリアスにする場合は ログファイルの書き出し先のパスをデスクトップにするとか適時工夫する必要があるかと思います。


[参考]

入門git

入門git

WordPress CMB2でデフォルトのページテンプレートだけにカスタムフィールドを付けたい


コードベースでWordPressのカスタムフィールドを作成できる CMB2 を愛用しています。
( 以前 Custom-Metaboxes-and-Fields-for-WordPress だったものが CMB2 という名前になり開発されています。 )

デフォルトテンプレートの固定ページにのみカスタムフィールドを作成する

テーマのテンプレートを使用していない「デフォルトテンプレート」のままの固定ページにのみ、CMB2でカスタムフィールドを作成する場合は下記のように指定をすればOK

  1. カスタムフィールドを表示する投稿タイプの設定 object_typespage にする
  2. show_on オプションで ['key' => 'page-template', 'value' => 'default'] とページテンプレートのデフォルトと指定

EX:

<?php // function.php

if( file_exists(  __PLUGINS__ . '/cmb2/init.php' ) ){
  require_once( __PLUGINS__ . '/cmb2/init.php' );
  add_filter( 'cmb2_admin_init', 'add_my_custom_metaboxes' );
}

function add_my_custom_metaboxes() {
  // Page (default template)
  $prefix = '_page_';
  $cmb_page = new_cmb2_box([
    'id'           => 'page_title',
    'title'        => 'ページタイトル',
    'object_types' => ['page'],
    'show_on'      => [
      'key'   => 'page-template',
      // 'default' を指定するのがポイント!
      'value' => 'default',
    ],
    'context'      => 'side',
    'priority'     => 'default',
    'show_names'   => false,
  ]);
  $cmb_page->add_field([
    'name'   => 'page_top_title',
    'id'     => $prefix . 'top_title',
    'type'   => 'text',
    'attributes' => [
      'placeholder' => 'ページタイトルを入力してください',
    ],
  ]);
}

👇 f:id:kikiki-kiki:20170701225158p:plain
こんな感じに、簡単に「デフォルトテンプレートの固定ページ」にのみカスタムフィールドを作成することができました!

特定のテンプレートの固定ページにカスタムフィールドを作成する場合は、show_onvalueをテンプレートファイル名にするだけ。
フォルダに入れている場合は、<フォルダ名>/<テンプレートファイル名>.php とすればOKです。

CMB2とっても簡単にカスタムフィールドが作成できるので ネ申 !!


エンジニアのためのWordPress開発入門 (Engineer's Library)

エンジニアのためのWordPress開発入門 (Engineer's Library)

WordPress 管理画面 固定ページ一覧にスラッグとテンプレート名を表示させたい

WordPressでWEBサイトを作る際に特定のページを作成するために固定ページのテンプレートを作成したりする事があります。
ただテンプレートの選択やページのURLの指定はWordPressの管理画面から行うので、テスト環境と本番環境があるような場合、本番環境でテンプレートが選択できてなかったとか、URL(スラッグ)がテスト環境と同じに設定できてなかったという事故が起こりかねません。
これらが固定ページ一覧のリスト上に表示できれば、ミスがあっても早期発見して修正できるように思います。

固定ページ一覧に表示する要素を変更する

管理画面の一覧ページに表示するカラムのカスタマイズは、manage_{$post_type}_posts_columns フィルターを使います。

(WordPress3.1以前では `manage_edit-{$post_type}_columns フィルター)

今回は固定ページなのでフィルターは manage_page_posts_columns となります。

<?php // function.php

// 固定ページ一覧に表示するカラムの変更
add_filter('manage_page_posts_columns', 'my_manage_page_columns');
function my_manage_page_columns( $columns ) {
  // 表示する順序を変えたいので一度 unset でデフォルトのカラムを外す
  unset($columns['date']);
  unset($columns['author']);
  unset($columns['comments']);
  // slug と template を表示する カラムを追加
  $columns['slug'] = __('Slug');
  $columns['template'] = __('Template');
  // unset したカラムで必要なものを戻す
  $columns['author'] = __('Author');
  $columns['date'] = __('Date');
  return $columns;
}

これで管理画面の固定ページ一覧に スラッグ と テンプレート のカラムがたされた状態になります。
見出しを変更したい場合は、__('Slug') の部分を "URL" などの表示したい文字に変えればOK。

管理画面 固定ページ 一覧の表示のカスタマイズ

カラムに表示するもののカスタマイズは manage_{$post_type}_posts_custom_column フィルターを使います。
カスタム投稿タイプなら manage_posts_custom_column とすれば投稿とカスタム投稿タイプを一括で設定できるのですが、固定ページはこのフィルターでは動作しません。
固定ページは manage_pages_custom_column というフィルターになります。

function.php に固定ページ一覧の表示をカスタマイズするコードを追加します。

<?php // function.php

// 固定ページ一覧の表示をカスタマイズ
add_action('manage_pages_custom_column', 'my_pages_custom_column', 10, 2);
function my_pages_custom_column( $column_name, $post_id ) {
  // Slug
  if( $column_name === 'slug' ) {
    $column = get_post_field('post_name', $post_id, 'display');
  }
  
  // Template Name
  if( $column_name === 'template' ) {
    $column = get_page_template_slug($post_id);
    // または
    // $column = get_post_meta( $post_id, '_wp_page_template', true );
  }

  if( isset($column) && ( $column || $column === 0 ) ) {
    echo $column;
  } else {
    echo __('None');
  }
}

👇

□タイトル スラッグ テンプレート 作成者 日時
□ HOME - フロントページ home page-home.php KiKiKi 公開済み
2017年7月1日
□ About about page-about.php KiKiKi 公開済み
2017年7月1日
□ サンプルページ sample-page なし KiKiKi 公開済み
2017年6月30日
□ Blog - 投稿ページ blog なし KiKiKi 公開済み
2017年6月30日

こんな感じに管理画面の固定ページ一覧にページのURL(Slug)と使用しているテンプレートファイル名を表示する事ができました。

固定ページのテンプレートの取得に関して

固定ページのテンプレート名は get_page_template_slug( $post_id ) で取得することができます。
ただし、特にテンプレートを設定していないデフォルトの場合 get_page_template_slug()NULLを返します。

get_post_meta( $post_id, '_wp_page_template', true ); でもテンプレート名を取得することができ、こちらの場合、テンプレートが設定されていない場合は default という文字列が返ります。

上の例では、最後に$columnが空なら__('None')を返し「なし」と表示するようにしていますが、defaultと表示させたい場合は get_post_meta( $post_id, '_wp_page_template', true ); を使うのが良いと思います。

Function Reference/get page template slug

The function get_page_template_slug() returns an empty string when the value of ‘_wp_page_template’ is either empty or 'default'.

Custom fields starting with an underscore do not display in the Edit screen’s Custom Fields module. To retrieve a Page’s custom template metadata, you can also use:

get_post_meta( $post->ID, ‘_wp_page_template’, true )
出典: Function Reference/get page template slug « WordPress Codex


[参考]

PHP json_decode がnullになるにハマる

PHPjson_decodeは値が " (ダブルコーテーションで)囲まれていないと null になるっぽい。

json_decode() でnull になるJSONのパターン

1. " で囲われていない値がある

json = {1: "星宮いちご"}
<?php
json_decode( json ); // => NULL

2. 値が ' (シングルコーテーション)で囲まれている

json = {"1": '<b class="idol">星宮いちご</b>'}
<?php
json_decode( json ); // => NULL

` ←コレもダメ

json = {"1": `星宮いちご`}
<?php
json_decode( json ); // => NULL

3. 行末に , がある

json = {"1": "星宮いちご",}
<?php
json_decode( json ); // => NULL

すべてのキーと値が " で囲まれていて、最後に不要な , もない場合ならOK

json = '{"1": "星宮いちご"}';
<?php
json_decode( json );
// => object  { ["1"]=> string(15) "星宮いちご" }
json_decode( json, true );
// => array(1) { [1]=> string(15) "星宮いちご" }

"で囲まれた値の中で'が使われているのはOK。
例えばHTMLタグを入れるような場合、値となる文字列は " で囲み、属性は'で囲めば大丈夫

json = ["<b class='idol'>星宮いちご</b>", "霧矢あおい", "紫吹蘭"];
<?php
json_decode( json );
// => array(3)  { [0]=> string(35) "星宮いちご" [1]=> string(15) "霧矢あおい" [2]=> string(9) "紫吹蘭" }
json_decode( json, true );
// => array(3)  { [0]=> string(35) "星宮いちご" [1]=> string(15) "霧矢あおい" [2]=> string(9) "紫吹蘭" }

PHP上の文字列でJSONを作る場合は全体を'で囲むか、値を囲む"エスケープすればOK (最初からObjectや配列作れば良くね?需要あるのか謎だけど…)

<?php
$json = '{"みくるの": "みらくる!"}';
json_decode( $json );
// => object  { ["みくるの"]=> string(15) "ミラクル!" }
$json = "{\"みくるの\": \"ミラクル!\"}";
json_decode( $json );
// => object  { ["みくるの"]=> string(15) "ミラクル!" }

 
javascriptだと問題ないようなjsonPHPjson_decode()だとnull になって少しハマりました。
PHPのバージョンが上がって解消されるといいな…


[参考]

Gulp gulp-uglify でmin化しようとしたらエラーになった件

Gulpでjsをminfy化しようとしたら、次のようなエラーが表示されるようになってしまいました。

[21:34:54] Starting 'uglify'...
events.js:160
      throw er; // Unhandled 'error' event
      ^
GulpUglifyError: unable to minify JavaScript
...

uglify のタスクは昔作ってたものの使い回しでこんな感じ

var uglify = require("gulp-uglify");
var rename = require('gulp-rename');

gulp.task('uglify', function(cb) {
  return gulp.src( src )
    .pipr( uglify({
      // ! から始まるコメントを残すオプションを追加
      preserveComments: 'some'
    }) )
    .pipe( rename({
      extname: '.min.js'
    }) )
    .pipe( gulp.dest( assetDir ) );
});

何がエラーなのか調べる

表示されているエラーから何が原因なのかイマイチ解らなかったので、下記のようにuglifyに.on('error')を付けてエラーを表示してみました。

gulp.task('uglify', function(cb) {
  return gulp.src( src )
    .pipr( uglify({
      // ! から始まるコメントを残すオプションを追加
      preserveComments: 'some'
    })
    .on('error', function(e){
      console.log(e);
    }) )
    .pipe( rename({
      extname: '.min.js'
    }) )
    .pipe( gulp.dest( assetDir ) );
});

↓ 表示されたエラーの中に次のように表示されていました。

[21:34:54] Starting 'uglify'...
# 中略
message: '`preserveComments` is not a supported option',

どうやら!から始まるライセンスのコメントを残すオプションのサポートが無くなっているようです。

gulp-uglify v3.0.0 から preserveComments オプションは使えない

どうやらgulp-uglify v3.0.0 からはUglifyJS2に合わせてライセンスのコメントを残す方法が変更になっており
新規プロジェクトでnpm updateした際にgulp-uglifyのバージョンが上がり無効なオプションになってしまっていたのが原因だったようです。

gulp-uglify v3.0.0 で /*!, //! で始まるコメントを残す方法 に書き換えればOKでした。

Output options

👇

gulp.task('uglify', function(cb) {
  return gulp.src( src )
    .pipr( uglify({
      output:{
        comments: /^!/
      }
    })
    .pipe( rename({
      extname: '.min.js'
    }) )
    .pipe( gulp.dest( assetDir ) );
});

その他のオプションの指定方法は UglifyJS2 のGitHubページに載っています。
 
node系はしれっとアップデートの際に後方互換無しの変更されていたりするのでパッケージをアップデートするときとかは特に注意が必要ですね。デザイナーさんやコーダーさんでエンジニアにgulpを使って言われたままに黒い画面でnpm installしたけど上手く動かない!とか結構ありそう…


[参考]

↑ gulp-uglify v2.0.0 の書き方だったので修正しました。

PHP 文字列末にある特定の文字を除去したい。

例えば WordPressを使ったサイトで home_url('/') でサイトのURLを取得してそこからURLを組み立てたい時とか。
home_url('/') は行末に / が出力されるので、/ 始まりの変数と結合するとイケてないURLに…

<?php
define('IMG_DIR', '/assets/images');
$dir = home_url('/') . IMG_DIR;
echo $dir;
// => http://example.wordpress.com//assets/images

str_replace() を使って削除できるけど正規表現が必要なので、そこまでカロリーを使いたくない…

trim系の関数で除去できた!

trim系の関数は第二引数に除去する文字列を指定することができるようです。
これを利用して、行末の/を除去したい場合は rtrim(str, '/') とすればOK。

rtrim
string rtrim ( string $str [, string $character_mask ] )
パラメータ
character_mask 削除する文字を指定することも可能です。 削除したい全ての文字をリストにしてください。.. を文字の範囲を指定する際に使用可能です。
出典: PHP: rtrim - Manual

<?php
rtrim( home_url('/'), '/' ); // => http://example.wordpress.com

echo rtrim( home_url('/'), '/' ) . IMG_DIR;
// => http://example.wordpress.com/assets/images

簡単にできた(۶•̀ᴗ•́)۶

trim() って空文字除去ってイメージしかなかったので、第二引数で除去する文字列を自由に指定できることは知りませんでした… (そういえば行末の改行削除とかでも使ってた気がしてきた…
確かにtrimって刈り込むとか切り落とすとかって意味ですから、空文字に限らなくても不思議ではないですね!


[参考]

Git 変更のあるファイルの一部だけをコミットしたい。

ファイルを色々変更しちゃったけど、コミットは分けたい時

git add -p を使う

-p オプションで変更のブロック = Hunk(ハンク)ごとにステージにaddするかどうか対話式で選択することができます。

$ git add -p <変更のあるファイル名>

コマンドを実行すると、ファイル内の変更のHunkごと、どうするかが聞きかれます。

  • ステージに追加する場合は y
  • ステージに追加しない場合は n でスキップ
  • 残りのハンクはチェックせずに終了する場合は q

基本的によく使うのはこの3つかなと思います。

変更箇所が7行以上空くと別のハンクとして扱われるそうですが、別にしたい箇所が同じハンクとして表示された場合は

  • s でハンクを分割
  • 上手くハンクを分割できない時は、e で手動でハンクを編集

とすればOK。

サンプル

次のようなステキなリストを作成しました。

// aikatsu.txt
スターライト学園
0. 神崎 美月
1. 星宮 いちご
2. 霧矢 あおい
3. 紫吹 蘭
4. 有栖川 おとめ
5. 藤堂 ユリカ
6. 北大路 さくら
7. 一ノ瀬 かえで
8. 神谷 しおん
9. 三ノ輪 ヒカリ
ドリームアカデミー
1. 音城 セイラ
2. 冴草 きい
3. 風沢 そら
4. 姫里 マリア

↓ 次のように変更してみました

@@ -5,10 +5,10 @@
 3. 紫吹 蘭
 4. 有栖川 おとめ
 5. 藤堂 ユリカ
-6. 北大路 さくら
-7. 一ノ瀬 かえで
-8. 神谷 しおん
-9. 三ノ輪 ヒカリ
+6. 一ノ瀬 かえで
+7. 神谷 しおん
+8. 三ノ輪 ヒカリ
+9. 北大路 さくら
 ドリームアカデミー
 1. 音城 セイラ
 2. 冴草 きい
 3. 風沢 そら
 4. 姫里 マリア
+その他
+夏樹 みくる

Hunk(ハンク)を分割してステージにaddする

みくるちゃん(推し)の追加と北大路劇場の並び替えは別々にコミットしたいと思います。
ターミナルに git add -p <ファイル名> を入力します。

$ git add -p aikatsu.txt
diff --git a/aikatsu.txt b/aikatsu.txt
index 3a2c5fe..d17369b 100644
--- a/aikatsu.txt
+++ b/aikatsu.txt
@@ -5,12 +5,14 @@
 3. 紫吹 蘭
 4. 有栖川 おとめ
 5. 藤堂 ユリカ
-6. 北大路 さくら
-7. 一ノ瀬 かえで
-8. 神谷 しおん
-9. 三ノ輪 ヒカリ
+6. 一ノ瀬 かえで
+7. 神谷 しおん
+8. 三ノ輪 ヒカリ
+9. 北大路 さくら
 ドリームアカデミー
 1. 音城 セイラ
 2. 冴草 きい
 3. 風沢 そら
 4. 姫里 マリア
+その他
+夏樹 みくる
Stage this hunk [y,n,q,a,d,/,s,e,?]?

Hunkが1つで表示されました。これを分割したいのでsを入力すると…

Split into 2 hunks.
@@ -5,12 +5,12 @@
 3. 紫吹 蘭
 4. 有栖川 おとめ
 5. 藤堂 ユリカ
-6. 北大路 さくら
-7. 一ノ瀬 かえで
-8. 神谷 しおん
-9. 三ノ輪 ヒカリ
+6. 一ノ瀬 かえで
+7. 神谷 しおん
+8. 三ノ輪 ヒカリ
+9. 北大路 さくら
 ドリームアカデミー
 1. 音城 セイラ
 2. 冴草 きい
 3. 風沢 そら
 4. 姫里 マリア
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?

いい感じに分かるされました。y でステージにaddします。
残りの変更は今回コミットしたくないのでnでスキップするかqで終了します。
f:id:kikiki-kiki:20170627221439p:plain
👆 SourceTreeでステージの状態
Hunkをスキップした部分がワークス―ペース内に残った状態になっています。
これで、さくらちゃんの並び順の変更とみくるちゃんの追加を別々にコミットすることができます!

さらに、美月さんが卒業して、あかりジェネレーションが入学したのでリストを編集しました。

@@ -1,5 +1,4 @@
 スターライト学園
-0. 神崎 美月
 1. 星宮 いちご
 2. 霧矢 あおい
 3. 紫吹 蘭
@@ -9,10 +8,20 @@
 7. 神谷 しおん
 8. 三ノ輪 ヒカリ
 9. 北大路 さくら
+10. 大空 あかり
+11. 氷上 スミレ
+12. 新条 ひなき
+13. 服部 ユウ
+14. 紅林 珠璃
+15. 天羽 まどか
+16. 黒沢 凛
+17. 大地のの
+18. 白樺リサ
 ドリームアカデミー
 1. 音城 セイラ
 2. 冴草 きい
 3. 風沢 そら
 4. 姫里 マリア
 その他
+神崎 美月
 夏樹 みくる

Hunkが分かれていますが、 美月さんの卒業と、あかりジェネレーションの入学のコミットがいい感じに分かれていません。
美月さんの卒業をコミットした後で、あかりジェネレーションを追加、17, 18の ののりさは編入なので、これもコミットを分けたいと思います。

ターミナルに git add -p <ファイル名> を入力します。

$ git add -p aikatsu.txt
diff --git a/aikatsu.txt b/aikatsu.txt
index d17369b..2b59a87 100644
--- a/aikatsu.txt
+++ b/aikatsu.txt
@@ -1,5 +1,4 @@
 スターライト学園
-0. 神崎 美月
 1. 星宮 いちご
 2. 霧矢 あおい
 3. 紫吹 蘭
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?

最初のHunkが表示されます。この部分はコミットしたいのでyを入力。
次のHunkに進みます。

@@ -9,10 +8,20 @@
 7. 神谷 しおん
 8. 三ノ輪 ヒカリ
 9. 北大路 さくら
+10. 大空 あかり
+11. 氷上 スミレ
+12. 新条 ひなき
+13. 服部 ユウ
+14. 紅林 珠璃
+15. 天羽 まどか
+16. 黒沢 凛
+17. 大地のの
+18. 白樺リサ
 ドリームアカデミー
 1. 音城 セイラ
 2. 冴草 きい
 3. 風沢 そら
 4. 姫里 マリア
 その他
+神崎 美月
 夏樹 みくる
Stage this hunk [y,n,q,a,d,/,k,K,g,s,e,?]?

この部分は分割したいのでsで分割します。

Split into 2 hunks.
@@ -9,9 +8,18 @@
 7. 神谷 しおん
 8. 三ノ輪 ヒカリ
 9. 北大路 さくら
+10. 大空 あかり
+11. 氷上 スミレ
+12. 新条 ひなき
+13. 服部 ユウ
+14. 紅林 珠璃
+15. 天羽 まどか
+16. 黒沢 凛
+17. 大地のの
+18. 白樺リサ
 ドリームアカデミー
 1. 音城 セイラ
 2. 冴草 きい
 3. 風沢 そら
 4. 姫里 マリア
 その他
Stage this hunk [y,n,q,a,d,/,k,K,j,J,g,e,?]?

分割され最初のHunkが表示されました。
この部分はコミットしたくないので n でスキップします。
( j で未確定にして次のHunkに進んでもOK。)

@@ -12,7 +20,8 @@
 ドリームアカデミー
 1. 音城 セイラ
 2. 冴草 きい
 3. 風沢 そら
 4. 姫里 マリア
 その他
+神崎 美月
 夏樹 みくる
Stage this hunk [y,n,q,a,d,/,K,g,e,?]?

この箇所はコミットしたい部分なので y を入力。
ステージは次のような感じになっています。
f:id:kikiki-kiki:20170627224136p:plain
神崎先輩の位置を移動させたものだけがステージに上がっているので、コミットします。

連続した変更箇所を手動で別のコミットにする

次にあかりジェネレーションのリストから、17,18 ののりさ を別にしてステージにaddしたいと思います。

$ git add -p aikatsu.txt
diff --git a/aikatsu.txt b/aikatsu.txt
index eef8b0c..2b59a87 100644
--- a/aikatsu.txt
+++ b/aikatsu.txt
@@ -8,6 +8,15 @@
 7. 神谷 しおん
 8. 三ノ輪 ヒカリ
 9. 北大路 さくら
+10. 大空 あかり
+11. 氷上 スミレ
+12. 新条 ひなき
+13. 服部 ユウ
+14. 紅林 珠璃
+15. 天羽 まどか
+16. 黒沢 凛
+17. 大地のの
+18. 白樺リサ
 ドリームアカデミー
 1. 音城 セイラ
 2. 冴草 きい
Stage this hunk [y,n,q,a,d,/,e,?]?

変更箇所が連続していて s でHunkを分割することができないので、e で手動で編集を行う必要があります。
e を入力すると下記のようなdiffを編集するエディタが立ち上がります。
そこから、差分を作成することで、Hunkを分割することができます。

Manual hunk edit mode -- see bottom for a quick guide.
@@ -8,6 +8,15 @@
 7. 神谷 しおん
 8. 三ノ輪 ヒカリ
 9. 北大路 さくら
+10. 大空 あかり
+11. 氷上 スミレ
+12. 新条 ひなき
+13. 服部 ユウ
+14. 紅林 珠璃
+15. 天羽 まどか
+16. 黒沢 凛
+17. 大地のの
+18. 白樺リサ
 ドリームアカデミー
 1. 音城 セイラ
 2. 冴草 きい
# ---
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.
#
# If the patch applies cleanly, the edited hunk will immediately be
# marked for staging.
# If it does not apply cleanly, you will be given an opportunity to
# edit again.  If all lines of the hunk are removed, then the edit is
# aborted and the hunk is left unchanged.

今回は17. 18. の追加を無しにしたいので下記のように、その行を削除してファイルを保存しました。

Manual hunk edit mode -- see bottom for a quick guide.
@@ -8,6 +8,15 @@
 7. 神谷 しおん
 8. 三ノ輪 ヒカリ
 9. 北大路 さくら
+10. 大空 あかり
+11. 氷上 スミレ
+12. 新条 ひなき
+13. 服部 ユウ
+14. 紅林 珠璃
+15. 天羽 まどか
+16. 黒沢 凛
 ドリームアカデミー
 1. 音城 セイラ
 2. 冴草 きい

ファイルの編集が完了すると、先程のdiffファイルの内容だけがそのままステージに反映されています。
f:id:kikiki-kiki:20170627225454p:plain
diffファイル内で削除した内容も元ファイルは変更箇所としてワークスペースに残っているので失われることはありませんでした!!
 

対話式コマンドのオプション
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?

? でヘルプを表示することができます。

option mean
y stage this hunk
ハンクをステージにaddする
n do not stage this hunk
ハンクをaddしないでスキップ
q quit; do not stage this hunk or any of the remaining ones
このハンクをaddせず、残りの未確定なハンクもaddしないで終了
a stage this hunk and all later hunks in the file
これ以降のハンクを全てaddする
d do not stage this hunk or any of the later hunks in the file
これ以降のハンクはaddしない
g select a hunk to go to
指定したハンクへ移動
/ search for a hunk matching the given regex
正規表現でハンクを探す
j leave this hunk undecided, see next undecided hunk
このハンクは未確定のまま、次の未確定のハンクに移動
J leave this hunk undecided, see next hunk
このハンクは未確定なまま、次のハンクに移動
k leave this hunk undecided, see previous undecided hunk
このハンクは未確定のまま、前の未確定のハンクに移動
K leave this hunk undecided, see previous hunk
このハンクは未確定なまま、前のハンクに移動
s split the current hunk into smaller hunks
ハンクを分割
e manually edit the current hunk
手動でハンクを編集
? print help
ヘルプを表示

※ 当方、英語力に全く自信がありません!

まとめ・感想

git add -p を利用すると、興が乗ってしまい色々編集しちゃったファイルも後からコミットすべき単位に比較的簡単に分ける事ができそうです。
まぁ最初から計画的にファイル編集してこまめにコミットすれば済む話ではありますが。覚えておいて損はなさそうです!特にWEBさいととかのCSSとかではやってしまいがち…

また、GUIのSourceTreeは変更箇所はHunk毎に表示されているので、ボタンからハンク毎にステージにaddすることができます。
f:id:kikiki-kiki:20170627230332p:plain
ただ、ハンクを分けたり手動での変更はGUIだけでは難しいみたいです。

 
某会社でGitを本格的に導入するということで使い方のワークショップをさせてもらいました。
僕自身、現場で使いながら覚えたゆるふわ理解だったので、ワークショップをするにあたって改めて入門gitを読んだのですが、対話形式でステージにaddしたり、変更の1部 Hunk別にaddするの方法が書かれていたり、ログを検索する git blam の便利なオプションや、運用に当たってのブランチの作り方など、改めて勉強になることがたくさんありました!

入門git

入門git


[参考]

アイカツ!1stシーズン Blu-ray BOX1

アイカツ!1stシーズン Blu-ray BOX1