かもメモ

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

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

WordPress 投稿が0件でもpost_typeを取得したい。

WordPresspost_typeを取得する場合 通常 get_post_type() を使うのですが、投稿が無い時(0件の時) get_post_type()false を返すため今のページのpost_typeを取得できない場合があります。(カスタム投稿タイプを作って投稿が0件のアーカイブページとか)

Source File: wp-includes/post.php

<?php
function get_post_type( $post = null ) {
    if ( $post = get_post( $post ) )
        return $post->post_type;
    return false;
}

引用元: get_post_type() | Function | WordPress Developer Resources

これは投稿がないとglobal $postがNULLなためget_post( $post )が見つからないのでfalseが返る仕様になっているようです。

get_query_var() を使う

get_query_var( 'post_type' ) を使うと、投稿が0件で $post が NULL の時でも、そのページのpost_typeを取得することができます。

<?php
$post_type = get_query_var( 'post_type' );
echo  $post_type;

カスタム投稿タイプなど投稿が0件でもアーカイブページを表示したり、グローバルナビゲーションなどでpost_typeが必要になる場合はこちらを使うのが良いのではないかと思います。

投稿の一覧ページ( is_home() )の問題

WordPress管理画面の設定 表示設定 > フロントページの表示 で指定できる投稿の一覧ページでは、
投稿が1件以上ある場合でもglobal変数の$wp_queryにはpost_typeが設定されていないので get_query_var( 'post_type' )post_typeを取得することはできませんでした。
投稿がある場合は get_post_type() で取得することが出来るのですが、
投稿が0件の時はget_query_var( 'post_type' ) でも get_post_type() でも post_type を取得することができません。
投稿のpost_typeのデフォルトがpostなので、is_home() && !get_post_type() の時はデフォルト値としてpostをハードコーディングで当てるしかなさそうです。

追記
カテゴリー・タクソノミーのアーカイブでは投稿があっても、query_varにpost_typeはセットされていないようで、 get_query_var( 'post_type' ) は 常に NULL になっていました。

taxonomyオブジェクトを取得して taxonomy_obj-> object_type[0] とすれば、post_type が取得できるようです。
カテゴリーの場合は get_taxonomy('category') で、カスタムタクソノミーはget_query_var('taxonomy')でタクソノミー名を取得してget_taxonomy('タクソノミー名') とすれば taxonomyオブジェクト が取得できます。

EX: post_type を取得する関数

アーカイブページ・シングルページなどで分けてpost_typeを返す関数を作成してみました

<?php
function get_current_post_type() {
  $post_type = get_post_type();
  
 if( empty($post_type) ) {
    // 投稿が 0 件の時
    if( is_category() ) {
      $tax = get_taxonomy('category');
      $post_type = $tax->object_type[0];
    } else
    if( is_tax() ) {
      // category, taxonomy get_query_var( 'post_type' ) は常に null
      // 投稿が 1件 でもあれば get_post_type() で取得可能
      $term = get_query_var('taxonomy');
      $tax = get_taxonomy( $term );
      $post_type = $tax->object_type[0];
    } else
    if( is_archive() ) {
      // 投稿が 0 件の時 get_post_type() は false を返すので get_query_var() で取得する
      $post_type = get_query_var( 'post_type' );
    } else
    if( is_home() ) {
      // 投稿の一覧で投稿が0件の時は 'post' を設定
      $post_type = 'post';
    }
  }
  return $post_type;
}

 
はてなblogさん。。。
マークダウンで引用内にコードブロック出力するにはどうしたら良いのですか???エンジニア系のblogだと結構必要じゃない?


[参考]

PHP56 Macを再起動したらlibphp5.soが消えてapacheが起動できなくなった。

Macを再起動して普段通りapacheを起動しようとしたら次の様なエラーが表示されるようになってしまいました。先方の環境に合わせてPHP56です

$ sudo apachectl start
httpd: Syntax error on line 170 of /usr/local/etc/apache2/2.4/httpd.conf: Cannot load /usr/local/opt/php56/libexec/apache2/libphp5.so into server: dlopen(/usr/local/opt/php56/libexec/apache2/libphp5.so, 10): image not found

HomebrewでインストールしてあるPHP56のディレクトリ(/usr/local/opt/php56/)を見に行くと、libexec ディレクトリが無くlibphp5.soが存在していませんでした。※ Macシャットダウン・再起動で消えたのか定かではありません。Macの再起動前までは問題なくApacheは起動していたのですが...

PHP56を再インストールしたら解決

HomebrewでPHP56を一度アンインスールし、--without-ldap--with-httpd24 オプションを付けてPHP56を再インストールしたらlibphp5.soが作られ問題が解決しました。

$ brew uninstall php56
$ brew install php56 --without-ldap --with-httpd24

参考にしたGitHubのissueには--without-ldap だけでOKのような感じだったのですが、今回の場合、--with-httpd24オプションを指定しないとlibphp5.soが生成されることはありませんでした。
しかし、なぜ突然この問題が発生したのかが謎です...

追記
PHP56を再インストールしたらlocal環境のWordPressでデータベース接続エラーになってしまいました。どうやらlocalhostのDBに接続できなくなっていたようなのでで、php.inimysql.sock関連を指定してあげる必要があるようです。
php.iniは/usr/local/etc/php/5.6にあります。

mysql.default_socket =mysql.sock のパスが記されているので、下記の箇所もこのパスを指定します。

  • mysqli.default_socket =
  • pdo_mysql.default_socket =

参考: [php]mysqliでNo such file or directoryが出る


[参考]

↑ 今ならコレ解決できたかもしれない...