20200907のGitに関する記事は15件です。

.gitignoreに指定した対象はコミットできないのか?

git add . で追加しても追加されない。
git add した場合には追加できなとメッセージが表示される。

% git add sample.html
The following paths are ignored by one of your .gitignore files:
sample.html
Use -f if you really want to add them.

ただし -f オプションを使うと追加できるようだ。
.gitignoreに指定したファイルを-f(--force)で追加するのは、追加してはいけないと設定しているのに、でも強制的に追加します!みたいな感じなので、かなりの割合でやらないほうが良いです。

git rm は可能で、特に警告も出ません。
追加してはいけない設定にしたファイルを消すのは特に不自然ではなからでしょうね。

% git rm sample2.html
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

バージョン管理のすゝめ

0.1. はじめに ver.0.1

 元々は学内団体向けの記事として作成しましたが、入門者向けに幅広く使えそうなので公開します。
 ゲーム制作でバックアップ撮らずに作業してたら、突然HDD死んでプロジェクトデータ消えた…みたいなことがありませんでしたか?
 (僕は昔USBでやらかしました… 死にたくなりました)
 このような悲劇的な事故を起こさないためにも日頃からバックアップをとる習慣をつけましょう。

1. はじめに ver.1.0

 じゃあバックアップはどうやって取ろうか?
 一定時間ごとにプロジェクトフォルダ圧縮して、ファイル名変えて…

 こんなことやってるともれなくファイル名が悲惨なことになるのでやめましょう。

  • Backup
    • HogeProject20200904_01
    • HogeProject20200904_02
    • HogeProject20200906_Final
    • HogeProject20200906_Final_02
    • HogeProject20200906_Final_Final
    • HogeProject20200906_Final_Final_Fin
    • HogeProject20200906_Final_Final_Fin_02

 キリがなくなるのでやめます…

 今までこういう命名して苦労してきたって人も少なくないと思います。
 このような無駄な労力を避けるためにも、次に説明するバージョン管理ツールを採用するようにしましょう。

2. バージョン管理ツールとは

 バージョン管理ツール(version control system, 以下vcs)とはファイル群の変更履歴を管理するためのツールです。
 当たり前のように聞こえるかもしれませんが、大事なのは変更履歴を差分を取って管理するということです。
 算数あるいは数学で学んだ樹形図のように変更履歴を記録してくれます。
 ある段階まで遡ったり、パラレルワールドのように用途に合わせて分岐させることも可能です(詳細は後程)。
 
 またはじめにで書いたダメな例のようにバックアップを取るたびにすべてコピーしていては、容量が大変なことになりますよね?
 vcsを導入することでバックアップ時の容量削減にも繋がります。

 ここまでvcsのメリットについて紹介してきました。次は具体的なツールの説明をしていきます。

3. Git

 おそらく今世の中で最も主流のvcsです。次に説明するSubversionなどと比較して分散型と位置付けられています(最初は覚えなくていいです)。

3.1. 下準備

 使用にはPCにGitを導入する必要があります。
 Windowsの人はGit for Windowsのリンクに飛んでダウンロードしましょう。
 Macの人は元々入っているので何もしなくて大丈夫です。

 以下Windowsを基準に説明していきます。Macの場合の相違点は適宜説明していきます。

 Gitはコマンドを実行することで操作していきます。
 最初はよくわからない文字列ばかりで難しく思うかもしれませんが、実際覚えることは少ないので安心してくださいね。
 なお今回はUnityを想定して説明していきます。

 ここからの作業はWindowsならGit CMD、Macならターミナルを開いて作業してください(以下ターミナルと呼びます)。
 
 まずターミナルでAssetsフォルダがある階層を開きます。
 GitSample 2020_09_08 23_48_57.png

 例えば写真のプロジェクトはG:/Develop/Unity/GitSampleにあるので、次のコマンドで移動できます。

cd/d G:/Develop/Unity/GitSample

3.2. バージョン管理しないリスト (.gitignore)

 Unityプロジェクトを構成するファイル一覧が掲載されています。
 このうち全てをバージョン管理する必要はないので、バージョン管理しないファイルリストを作成します。
 .gitignoreというファイルを作成し、その中に記述します。

 ここでは一般的なUnityプロジェクトで使用されるものを掲載しておきます。
 導入する外部ツールなどに応じて適宜変更してください。


.gitignore (例)
/[Ll]ibrary/
/[Tt]emp/
/[Oo]bj/
/[Bb]uild/
/[Bb]uilds/
/Assets/AssetStoreTools*

# Visual Studio cache directory
.vs/

# vscode
.vscode/

# Autogenerated VS/MD solution and project files
ExportedObj/
*.csproj
*.unityproj
*.sln
*.suo
*.tmp
*.user
*.userprefs
*.pidb
*.booproj
*.svd
*.pdb
*.opendb

# Unity3D generated meta files
*.pidb.meta
*.pdb.meta

# Unity3D Generated File On Crash Reports
sysinfo.txt

# Builds
*.apk
*.unitypackage

# OS generated
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
Icon?
ehthumbs.db
Thumbs.db

#log files, for some plugins
*.log

#python bytecode cache, for some plugins.
*.pyc


3.3. リポジトリ初期化 (git init)

 次にこの階層(以下ディレクトリと呼びます)をGitで管理するために初期化します。

 git init

 Assetsフォルダがあるディレクトリまで移動し、上のコマンドを実行しましょう。
 このコマンドによりディレクトリがGitのために初期化されます。
 Gitが使える状態のディレクトリをGitリポジトリと呼びます。

3.4. Git管理下に追加 (git add)

 これでGitを使う準備ができました。次に管理するファイルを登録します。 

git add .

 このコマンドを実行することで、.gitignoreに記載されていないすべてのファイルがGitの管理下に追加されます。

3.5. コミット (git commit)

 次はいよいよ変更履歴を記録していきます。

git commit -m "<commit message>"

 <commit message>にはコミットの理由やファイルの状態などを書き残しておきます (<>は不要です)。
 後々見返したときにわかりやすくするためです。
 このコマンドを実行すると現段階での変更情報が保存されます。

3.6. リモートリポジトリの追加 (git remote add)

 ここまでを実行することで、自分のPC上には変更履歴が記録されるようになっています。
 Gitでは外部サーバー上にリモートリポジトリを追加し、変更履歴を共有することができます。
 外部サービスにはいろいろな種類がありますが、今回はGitHubを利用します。

 Create a New Repository - Google Chrome 2020_09_09 11_09_22.png

 Newを押して上の写真のように設定します。
 Create repositoryを押し、リポジトリを作成します。

 続いてターミナルでの作業に戻ります。
 リモートリポジトリをローカル上のリポジトリで追加するには以下のコマンドを実行します。

git remote add origin <URL>

Create a New Repository - Google Chrome 2020_09_09 11_25_15.png

 URLには上の写真に記載されているURLを使用します (<>は不要です)。

3.7. リモートに反映 (git push)

 続いて今追加したリモートリポジトリにローカルリポジトリの変更を共有します。

git push origin master

 このコマンドを実行することでリモートリポジトリにコミットが反映されます。

3.8. 複数人での共有

 GitHubにはCollaboratorという機能があり、これを利用することで複数人で一つのリポジトリにpushすることができます。
 リポジトリのSettings -> Manage Accessからユーザーを追加できます。

 詳細は実演で説明します。
 リモートリポジトリを自分のPCに複製するにはクローンします。

git clone <URL>

URLには上のリモートリポジトリの追加で使用したものと同じものです。

3.9. 他人の変更をローカルに反映 (git pull)

 他人がpushしたリポジトリへの変更を取り入れるには以下のコマンドを使います。

git pull origin  master

3.10. クライアントソフトの利用

 ここまでコマンドによる操作でGitを使用してきました。
 コマンド操作と同様の行程をGUIで可能にしたソフトがクライアントソフトです。
 
 以下いくらかツールを紹介します。

  • GitHub Desktop
    • GitHubの公式クライアントソフトです。
  • Source Tree
    • GitHubだけでなくBitBucketやGitLabなどにも対応しています。

3.11. [発展] ブランチ (branch)

 ブランチとは木の枝という意味ですが、Gitではコミットしたポイントから複数のラインで並行して編集ラインを立てることができます。
 またそれらを任意のタイミングで統合(マージ)することも可能です。
 正しく使うと機能ごとやリリースごとにブランチを切ることで強力な効果を発揮しますが、適当にやっても無駄に面倒になるだけなので使う前には勉強しておくことをお勧めします。
 興味がある人はGit-flowなどで検索してみてもいいかもしれません。

3.12. [発展] バイナリファイルに注意

 最初に説明した通り、差分を取って管理することで時間や容量を抑えています。
 ここで注意が必要で、テキストファイルは差分が取れますがバイナリファイルは差分が取れません。
 よってバイナリファイルは変更が入るたびに丸ごと管理するようになっています。
 このためバイナリファイルの数や容量によってはプッシュやプルに時間がかかることも出てきます。
 あまりに時間がかかるようであれば、バイナリファイルはGit以外で管理するのがいいかもしれません。

4. Subversion

 SubversionもGitと同様、バージョン管理ツールの一つです。
 Gitと比較して集中型と位置付けられています。
 Subversionもコマンドで扱うことができますが、こちらはクライアントソフトを利用していきたいと思います。
 
 Windowsの人はTortoise SVN、Macの人はSnail SVNがおすすめです。
 
 Gitと比較した際に特徴的な相違点はリポジトリがリモート一つのみであることです。
 GitもSubversionもローカルにコピーして作業した後リモートに反映するという流れは同じです。
 ただしGitはローカルにコミットした後リモートにプッシュするのに対し、Subversionにはローカルリポジトリはただのコピーであり、直接リモートリポジトリにコミットするようになっています。

 基本的な使い方はGitと変わらないので、ここでは相違点を中心に説明します。

4.1. コミット&リモートへ反映 (commit)

 前述のとおりSubversionではリモートリポジトリに直接コミットするようになっています。
 ですのでGitのようにコミットしてからプッシュするのではなく、コミットするだけでリモートに反映することができます。

4.2. リポジトリをローカルにコピー (checkout)

 Gitではリモートリポジトリからローカルリポジトリを作成する際cloneしていました。
 Subversionではcheckoutとなっています。

 尤もgit cloneはローカルにもオリジナルのリポジトリを作成しているのに対し、Subversionではあくまでコピーなので正確には全く同じではないです。

4.3. リモートの変更を反映 (update)

 Gitでpullでしたが、Subversionではupdateになっています。


力尽きたのでこの辺にしておきます。
Subversion編、要望あれば追記するかもです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クジラとネコの親子プログラミング - Docker for Windowsを使ってScratch3.0のオリジナル拡張機能を試してみよう。

親子プログラミングはGitとNode.jsが使えることが前提になっている?

小学校でのプログラミング教育の必須化に伴って、子供たちがScratch 3.0に触れる機会も増えるかと思います。そんな中、ブロックをくっつけるだけのお遊びツールだとあなどっていると、次のような連載が始まりました。

「親子でできる!Scratch と AWS を使った "ものづくり" 体験 - 1. 準備 〜 疎通確認編」
2020-09-01
How to be a Developer
金澤 圭

この連載の「2. 作業の前提について」で、衝撃的な次の一文があります。

PC に Git と Node.js がインストールされている必要があります。

保護者の方も、GitやNode.jsを当たり前のようにインストールしなければいけない時代になりましたね。でも、安心してください。Dockerという便利なツールがありますので、簡単に開発環境を整えることができます。

本記事では、Webベースの統合開発環境であるcode-serverとScratch 3.0 拡張機能の開発と実行に必要なソースコード(scratch-vm/scratch-gui)を詰め合わせたDockerコンテナ(jprad/s3coder)を使って、Windowsパソコン上で、Scratch 3.0 GUIサーバーを起動できるようにします。
後半では、「Scratch 3.0の拡張機能を作ってみよう」を参考にして、拡張機能を追加します。

拡張機能を追加して、Scratch 3.0 GUIでコーディング
image.png

必要なもの

Windows 10 パソコンにDockerをインストールしてください。Dockerのインストール方法は、リンク先や各種記事を参照してください。

セットアップと起動

Dockerイメージ(jprad/s3coder)を元にDockerコンテナを起動し、Scratch 3.0 GUIサーバーをWindowパソコンで実行できるようにします。

1. 共有フォルダの作成とDockerコンテナの初回起動

Windows 10 で、コマンドプロンプトを起動し、次の一連のコマンドを入力します。

コマンドプロンプト.cmd
cd c:\
mkdir scratch-ws
docker run --name s3coder -d -p 8080:8080 -p 8601:8601 ^
-v C:/scratch-ws:/home/coder/s3/scratch-ws -e PASSWORD=password ^
jprad/s3coder /home/coder/s3

REM docker run ... のコマンドが長いので「^(キャレット)」で改行しています。

C\scratch-wsフォルダは、Dockerコンテナにマウントされる共有フォルダです(/home/coder/s3/scratch-ws)。
docker runコマンドを開始すると、必要なDockerイメージ(合計:844.17 MB)のダウンロードが始まり、Dockerコンテナが起動されます(コンテナ名:s3coder)。

ダウンロード中のコマンドプロンプト
image.png

ダウンロード完了後、コンテナ起動済みのコマンドプロンプト
image.png

Dockerコンテナの初回起動時に、共有フォルダへのアクセスを許可するかどうかの確認通知が表示されますので、[Share it]で許可します。

共有を許可(Share it
image.png

2.code-severへのログインとScratch 3.0 GUIの起動

Dockerコンテナの起動完了後、code-server ( http://localhost:8080 ) へアクセスし、ログインします(パスワードは、passwordです)。統合開発環境が開きますので、Dockerイメージに同梱されている scratch-guiを起動します。起動方法が構成ファイル(./s3/.vscode/launch.json)に設定されていますので、クリック操作のみで起動できます。scratch-guiの起動完了後、Scratch 3.0 GUI ( http://localhost:8601 ) へアクセスすると、公式ページとは少しばかり異なっていますが、使い慣れたScratch 3.0でのコーディングが行えます。

手順
1. code-server ( http://localhost:8080 ) へアクセスする。
2. パスワードにpasswordと入力し、[SUBMIT]ボタンをクリックし、ログインする。
3. [Run (Ctrl + Shift + D)]アイコンをクリックする。
4. ドロップダウンリストで'Run Scratch 3.0 GUI'(既定)を選択する。
5. [Start Debuging]アイコンをクリックする。
6. [TERMINAL]タブをクリックし、ビルド完了後の'Compiled successfully' が表示されるのを待つ。
7. Scratch 3.0 GUI ( http://localhost:8601 ) へアクセスする。

scratch-guiの起動
image.png

Scratch 3.0 GUI
image.png

3.停止・再開

scratch-guiの再開・停止
scratch-guiを停止せずに再開するには、coder-serverの[Restart (Ctrl+Shift+F5)]アイコンをクリックします。
scratch-guiを停止するには、code-serverの[Stop (Shift+F5)]アイコンをクリックします。

scratch-guiの開始

  1. [Run (Ctrl + Shift + D)]アイコンをクリックする。
  2. ドロップダウンリストで'Run Scratch 3.0 GUI'(既定)を選択する。
  3. [Start Debuging]アイコンをクリックする。
  4. [TERMINAL]タブをクリックし、ビルド完了後の'Compiled successfully' が表示されるのを待つ。

Dockerコンテナの停止
コマンドプロンプトで次のコマンドを実行するとDockerコンテナ(コンテナ名:s3coder)が停止します。

コマンドプロンプト.cmd
docker stop s3coder

Dockerコンテナの再開
コマンドプロンプトで次のコマンドを実行するとDockerコンテナ(コンテナ名:s3coder)が再開します。

コマンドプロンプト.cmd
docker start s3coder

拡張機能の開発

Scratch 3.0 Extensions の開発に関する情報には、次のようなものがあります。

ここでは、「Scratch 3.0の拡張機能を作ってみよう」を参考にログを出力する拡張機能(ブロック)を実装(コピペ)してみます。

拡張機能に必要なファイル

拡張機能(s3coder_newblocks)を実装するには、次のファイルを追加・編集します。

scratch-vm

# フォルダ ファイル 追加 編集 説明
1 scratch-vm/src/extensions/s3coder_newblocks index.js 拡張機能(newblocks)の本体です。
2 scratch-vm/src/extension-support extension-manager.js 追加した拡張機能(s3coder_newblocks)をbuiltinExtensionsに追加登録します。

scratch-gui

# フォルダ ファイル 追加 編集 説明
1 scratch-gui/src/lib/libraries/extensions/s3coder_newblocks newblocks.png 600x372ピクセルのPNG形式ファイルです。拡張機能の選択時にバナーとして表示されます。
2 scratch-gui/src/lib/libraries/extensions/s3coder_newblocks newblocks-small.png 80x80ピクセルのPNG形式ファイルです。ブロックのアイコンとして表示されます。
3 scratch-gui/src/lib/libraries/extensions index.js PNG形式ファイルの読込定義と拡張ブロックの定義を設定します。

scratch-vm での実装

scratch-vmで、拡張機能そのものを実装します。

フォルダ作成

拡張機能用のフォルダを次の手順で作成します。

  1. code-serverのEXPLORERで、s3/scratch-vm/src/extensionsフォルダを選択します。
  2. 右クリックで、コンテキストメニューからNew Folderを選択し、's3coder_newblocks'フォルダを作成します。

index.jsファイルの実装

次の手順で、index.jsファイルを実装します。

  1. 作成した's3coder_newblocks'フォルダで右クリックし、New Fileを選択し、ファイル名をindex.jsとします。
  2. 「Scratch 3.0の拡張機能を作ってみよう/基本の書式 - 拡張機能の追加」からindex.jsのソースコードをコピー&ペーストします。

index.js
index.js
const ArgumentType = require('../../extension-support/argument-type');
const BlockType = require('../../extension-support/block-type');
const Cast = require('../../util/cast');
const log = require('../../util/log');

/**
 * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len
const blockIconURI = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48ZyBpZD0iSUQwLjA4NjgyNDQzOTAwMDMzODMyIiB0cmFuc2Zvcm09Im1hdHJpeCgwLjQ5MTU0NjY2MDY2MTY5NzQsIDAsIDAsIDAuNDkxNTQ2NjYwNjYxNjk3NCwgLTY0LjUsIC03Ny4yNSkiPjxwYXRoIGlkPSJJRDAuNTcyMTQ2MjMwMzc3MjU2OSIgZmlsbD0iI0ZGOTQwMCIgc3Ryb2tlPSJub25lIiBkPSJNIDE4OCAxNDEgTCAyNTAgMTQxIEwgMjUwIDIwMyBMIDE4OCAyMDMgTCAxODggMTQxIFogIiB0cmFuc2Zvcm09Im1hdHJpeCgxLjI4NzkwMzMwODg2ODQwODIsIDAsIDAsIDEuMjg3OTAzMzA4ODY4NDA4MiwgLTExMC45LCAtMjQuNCkiLz48cGF0aCBpZD0iSUQwLjYzODMzNjEzNTA3NDQ5NjMiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Ik0gMTk2IDIwNCBDIDE5NiAyMDQgMTkyLjcwNiAxOTAuMDU4IDE5MyAxODMgQyAxOTMuMDc0IDE4MS4yMzYgMTk1Ljg4NiAxNzguNDU4IDE5NyAxODAgQyAyMDEuNDU1IDE4Ni4xNjggMjAzLjQ0MyAyMDMuNzU0IDIwNiAyMDEgQyAyMDkuMjExIDE5Ny41NDIgMjEwIDE2NiAyMTAgMTY2ICIgdHJhbnNmb3JtPSJtYXRyaXgoMSwgMCwgMCwgMSwgLTU3LCAxNS44KSIvPjxwYXRoIGlkPSJJRDAuNzU4NzMwMzU2NTgxNTA5MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgZD0iTSAyMTUgMTY5IEMgMjE1IDE2OSAyMTguMzY3IDE2OS41MzQgMjIwIDE3MCBDIDIyMC43MTYgMTcwLjIwNSAyMjEuMjc4IDE3MC44MTkgMjIyIDE3MSBDIDIyMi42NDYgMTcxLjE2MiAyMjMuMzY4IDE3MC43ODkgMjI0IDE3MSBDIDIyNC40NDcgMTcxLjE0OSAyMjUgMTcyIDIyNSAxNzIgIiB0cmFuc2Zvcm09Im1hdHJpeCgxLCAwLCAwLCAxLCAtNTcsIDE1LjgpIi8+PHBhdGggaWQ9IklEMC4yNDM2NzMwNzMxMjc4NjU4IiBmaWxsPSJub25lIiBzdHJva2U9IiNGRkZGRkYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBkPSJNIDIyNyAxNTQgQyAyMjcgMTU0IDIxOC41NTUgMTQ3Ljg5MCAyMTcgMTUxIEMgMjEyLjM0NSAxNjAuMzEwIDIxMS4yODkgMTcxLjczMyAyMTMgMTgyIEMgMjEzLjYxMiAxODUuNjcyIDIyMyAxODcgMjIzIDE4NyAiIHRyYW5zZm9ybT0ibWF0cml4KDEsIDAsIDAsIDEsIC01NywgMTUuOCkiLz48cGF0aCBpZD0iSUQwLjc5MzkzOTQ4MTk1NTAyMTYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Ik0gMTc1IDIwMC41MDAgQyAxNzUgMjAwLjUwMCAxNjkuODA1IDIyMS45MTMgMTcxIDIyMi43NTAgQyAxNzIuMTk1IDIyMy41ODcgMTc4Ljc5NSAyMDUuMjk1IDE4Mi41MDAgMjA1Ljc1MCBDIDE4NS45MjAgMjA2LjE3MCAxODEuODU5IDIyNC41MDAgMTg1LjI1MCAyMjQuNTAwIEMgMTg5LjIxMyAyMjQuNTAwIDE5Ny4yNTAgMjA1Ljc1MCAxOTcuMjUwIDIwNS43NTAgIi8+PC9nPjwvc3ZnPg==';

/**
 * Icon svg to be displayed in the category menu, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len
const menuIconURI = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48ZyBpZD0iSUQwLjA4NjgyNDQzOTAwMDMzODMyIiB0cmFuc2Zvcm09Im1hdHJpeCgwLjQ5MTU0NjY2MDY2MTY5NzQsIDAsIDAsIDAuNDkxNTQ2NjYwNjYxNjk3NCwgLTY0LjUsIC03Ny4yNSkiPjxwYXRoIGlkPSJJRDAuNTcyMTQ2MjMwMzc3MjU2OSIgZmlsbD0iI0ZGOTQwMCIgc3Ryb2tlPSJub25lIiBkPSJNIDE4OCAxNDEgTCAyNTAgMTQxIEwgMjUwIDIwMyBMIDE4OCAyMDMgTCAxODggMTQxIFogIiB0cmFuc2Zvcm09Im1hdHJpeCgxLjI4NzkwMzMwODg2ODQwODIsIDAsIDAsIDEuMjg3OTAzMzA4ODY4NDA4MiwgLTExMC45LCAtMjQuNCkiLz48cGF0aCBpZD0iSUQwLjYzODMzNjEzNTA3NDQ5NjMiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Ik0gMTk2IDIwNCBDIDE5NiAyMDQgMTkyLjcwNiAxOTAuMDU4IDE5MyAxODMgQyAxOTMuMDc0IDE4MS4yMzYgMTk1Ljg4NiAxNzguNDU4IDE5NyAxODAgQyAyMDEuNDU1IDE4Ni4xNjggMjAzLjQ0MyAyMDMuNzU0IDIwNiAyMDEgQyAyMDkuMjExIDE5Ny41NDIgMjEwIDE2NiAyMTAgMTY2ICIgdHJhbnNmb3JtPSJtYXRyaXgoMSwgMCwgMCwgMSwgLTU3LCAxNS44KSIvPjxwYXRoIGlkPSJJRDAuNzU4NzMwMzU2NTgxNTA5MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgZD0iTSAyMTUgMTY5IEMgMjE1IDE2OSAyMTguMzY3IDE2OS41MzQgMjIwIDE3MCBDIDIyMC43MTYgMTcwLjIwNSAyMjEuMjc4IDE3MC44MTkgMjIyIDE3MSBDIDIyMi42NDYgMTcxLjE2MiAyMjMuMzY4IDE3MC43ODkgMjI0IDE3MSBDIDIyNC40NDcgMTcxLjE0OSAyMjUgMTcyIDIyNSAxNzIgIiB0cmFuc2Zvcm09Im1hdHJpeCgxLCAwLCAwLCAxLCAtNTcsIDE1LjgpIi8+PHBhdGggaWQ9IklEMC4yNDM2NzMwNzMxMjc4NjU4IiBmaWxsPSJub25lIiBzdHJva2U9IiNGRkZGRkYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBkPSJNIDIyNyAxNTQgQyAyMjcgMTU0IDIxOC41NTUgMTQ3Ljg5MCAyMTcgMTUxIEMgMjEyLjM0NSAxNjAuMzEwIDIxMS4yODkgMTcxLjczMyAyMTMgMTgyIEMgMjEzLjYxMiAxODUuNjcyIDIyMyAxODcgMjIzIDE4NyAiIHRyYW5zZm9ybT0ibWF0cml4KDEsIDAsIDAsIDEsIC01NywgMTUuOCkiLz48cGF0aCBpZD0iSUQwLjc5MzkzOTQ4MTk1NTAyMTYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI0ZGRkZGRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGQ9Ik0gMTc1IDIwMC41MDAgQyAxNzUgMjAwLjUwMCAxNjkuODA1IDIyMS45MTMgMTcxIDIyMi43NTAgQyAxNzIuMTk1IDIyMy41ODcgMTc4Ljc5NSAyMDUuMjk1IDE4Mi41MDAgMjA1Ljc1MCBDIDE4NS45MjAgMjA2LjE3MCAxODEuODU5IDIyNC41MDAgMTg1LjI1MCAyMjQuNTAwIEMgMTg5LjIxMyAyMjQuNTAwIDE5Ny4yNTAgMjA1Ljc1MCAxOTcuMjUwIDIwNS43NTAgIi8+PC9nPjwvc3ZnPg==';


/**
 * Class for the new blocks in Scratch 3.0
 * @param {Runtime} runtime - the runtime instantiating this block package.
 * @constructor
 */
class Scratch3NewBlocks {
    constructor (runtime) {
        /**
         * The runtime instantiating this block package.
         * @type {Runtime}
         */
        this.runtime = runtime;

        //this._onTargetCreated = this._onTargetCreated.bind(this);
        //this.runtime.on('targetWasCreated', this._onTargetCreated);
    }


    /**
     * @returns {object} metadata for this extension and its blocks.
     */
    getInfo () {
        return {
            id: 'newblocks',
            name: 'New Blocks',
            menuIconURI: menuIconURI,
            blockIconURI: blockIconURI,
            blocks: [
                {
                    opcode: 'writeLog',
                    blockType: BlockType.COMMAND,
                    text: 'log [TEXT]',
                    arguments: {
                        TEXT: {
                            type: ArgumentType.STRING,
                            defaultValue: "hello"
                        }
                    }
                },
                {
                    opcode: 'getBrowser',
                    text: 'browser',
                    blockType: BlockType.REPORTER
                }
            ],
            menus: {
            }
        };
    }

    /**
     * Write log.
     * @param {object} args - the block arguments.
     * @property {number} TEXT - the text.
     */
    writeLog (args) {
        const text = Cast.toString(args.TEXT);
        log.log(text);
    }

    /**
     * Get the browser.
     * @return {number} - the user agent.
     */
    getBrowser () {
        return navigator.userAgent;
    }
}

module.exports = Scratch3NewBlocks;

image.png

extension-manager.jsファイルの変更

scratch-vm/src/extension-supportフォルダのextension-manager.jsファイルを開き、builtinExtensionsに、次のように拡張機能(s3coder_newblocks)を追加登録します。
※カンマで区切ることを忘れないでください。

extension-manager.js
const builtinExtensions = {
    ...
    gdxfor: () => require('../extensions/scratch3_gdx_for'),
    newblocks: () => require('../extensions/s3coder_newblocks'),
};

image.png

scratch-gui での設定

scratch-guiで、拡張機能を選択できるように設定します。

フォルダ作成

画像保存用のフォルダを次の手順で作成します。

  1. code-serverのEXPLORERで、s3/scratch-gui/src/lib/libraries/extensionsフォルダを選択します。
  2. 右クリックで、コンテキストメニューからNew Folderを選択し、's3coder_newblocks'フォルダを作成します。

画像ファイル

画像ファイルをダウンロードし、作成したフォルダへ保存します。

2つの画像ファイル(クリックして展開)

newblocks.png
image.png

newblocks-small.png
image.png

  1. 2つの画像ファイルをダウンロードし、C:\scratch-wsフォルダへ保存します。
  2. code-serverのEXPLORERで、s3/scratch-wsフォルダを開き、2つの画像ファイルを選択し、右クリックでコンテキストメニューからCopyします。
  3. s3/scratch-gui/src/lib/libraries/extensions/s3coder_newblocksフォルダを選択し、右クリックでコンテキストメニューからPasteします。

image.png

index.jsファイルの設定

s3/scratch-gui/src/lib/libraries/extensionsフォルダのindex.jsファイルを開き、次のように拡張機能の設定を行います。これにより、Scratch 3.0 GUIの拡張機能の追加で、拡張機能を選択できるようになります。

index.js
...
import gdxforConnectionSmallIconURL from './gdxfor/gdxfor-small.svg';

import newblocksImage from './s3coder_newblocks/newblocks.png';
import newblocksInsetImage from './s3coder_newblocks/newblocks-small.png';

export default [
...
        ),
        helpLink: 'https://scratch.mit.edu/vernier'
    },
    {
        name: (
            <FormattedMessage
                defaultMessage="New Blocks"
                description="Name for the 'New Blocks' extension"
                id="gui.extension.newblocks.name"
            />
        ),
        extensionId: 'newblocks',
        iconURL: newblocksImage,
        insetIconURL: newblocksInsetImage,
        description: (
            <FormattedMessage
                defaultMessage="New extension"
                description="Description for the 'New Blocks' extension"
                id="gui.extension.newblocks.description"
            />
        ),
        featured: true
    },
];

image.png

動作確認

scratch-guiを開始し、Scratch 3.0 GUIを開きなおすと、「拡張機能を追加」で、新しい拡張機能を選択できます。
image.png

logブロックを実行すると、ブラウザのConsoleにhelloと出力されます。

image.png

おわりに

【メモ】Dockerイメージの作成
dos.cmd
cd c:\
mkdir scratch-ws
docker run -it -p 8080:8080 -p 8601:8601 -v C:/scratch-ws:/home/coder/s3/scratch-ws -e PASSWORD=password codercom/code-server /home/coder/s3

http://localhost:8080/ へアクセス

sudo apt-get update -y
sudo apt-get install -y nodejs npm
sudo npm install -g npm
sudo chown -R coder:coder /home/coder/s3
git clone --depth 1 https://github.com/llk/scratch-vm.git
git clone --depth 1 https://github.com/llk/scratch-gui.git

cd scratch-vm
npm ci
sudo npm link

cd ../scratch-gui
npm ci
sudo npm link scratch-vm

npm start
--->> [Ctrl]+[C]で中断

http://localhost:8601/ へアクセス

  • scratch-guiのポート
    ENV環境変数で設定可能

Dockerfile の記述

docker build -t test-image .

docker run --name test-container -it -p 8080:8080 -p 8601:8601 -v C:/scratch-ws:/home/coder/s3/scratch-ws -e PASSWORD=password test-image /home/coder/s3

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

stdout is not a ttyに叱られる

完全に自分用の健忘録です

Git Bash(ver2.28)で実行しようとした際に、

$ node hogehuga.js 
stdout is not a tty

と叱られてしまった。悲しい。
原因はMinTTYのアレです。大体winptyを先頭につければ解決すると思ってたけど、今回は解決しなかった。
Gitを再インストールしてもダメ。

5分くらい悩んで、拡張子をつけたら動いた。

$ node.exe hogehuga.js

以下参考
https://github.com/mintty/mintty/wiki/Tips#inputoutput-interaction-with-alien-programs

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Git】別のブランチにpushしたいとき

【Git】別のブランチにpushしたいとき

  • gitで、ローカルのブランチから別名のリモートブランチにPUSHしたいときのやり方

方法

git push origin <PUSHしたいローカルブランチ名>:<PUSH先リモートブランチ名>

解説

  • もともとGitのpushの際の git push origin <ブランチ名>は、git push origin <ブランチ名>:<ブランチ名>の省略形であり、<src>:<dst>の関係性になっている
  • 上記のようになっているので、srcの位置に送りたいブランチ、dstに送り先ブランチにしてあげると、別名のブランチにPUSHできる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【備忘録】はじめてのGit環境構築 #2

前置き

前回の記事では,Gitの環境構築を行った。
今回はGitとGitHubの連携方法について,簡単にまとめていこうと思う。

環境

OS:Windows10
Git:2.28.0

GitHubアカウントの作成

GitHubを利用するには,まずはアカウントを作成する必要がある。
まずは,GitHubにアクセス。

image.png
[Username],[Email],[Password]をそれぞれ入力し,「Sign up for GitHub」をクリック。
その後,表示される画面に従って登録を進める。
(※特に複雑なことを聞かれたりはしないので省略。)

リポジトリの作成

登録が完了し,ログインしたら画面の右上付近にある「+」ボタンをクリック。
更にその中にある,「New repository」をクリック。
image.png

リポジトリの作成画面に遷移出来たら,「Repository name」に好きなリポジトリ名を入力する。
公開設定について選べるので今回は「Public(公開)」を選択。
「Add a README file」にチェックを入れるとリポジトリ内にREADMEファイルが作成される。
「Create repository」をクリック。
image.png

リポジトリが無事作成されると,以下のような画面が表示される。
「Code」をクリックすると,作成したリモートリポジトリのリンクが表示されるのでメモしておく。
※「https:://github.com/ユーザ名/リポジトリ名.git」みたいなリンク
image.png

Gitとの連携

Gitで管理するディレクトリを作成していく。
まず,GitBushを起動。
image.png

以下コマンドを実行する。

$ mkdir Qiita_test
$ cd Qiita_test

image.png
そうするとディレクトリが作成される。
作成したディレクトリをGitで管理出来るよう,「init」コマンドを実行する。

$ git init

image.png

「init」出来たら,先程GitHubで作成した,リモートリポジトリのURLをコピーし,以下コマンドを実行する。

$ git remote add origin "https:://github.com/ユーザ名/リポジトリ名.git"

プッシュ先の指定が出来たので,ファイルを作成し,コミットする。

$ touch test.py
$ git add test.py
$ git commit -m "Create test.py"
$ git push origin master

image.png
すると,上記のように,エラーが発生する場合がある。
どうやら,リモートリポジトリとローカルリポジトリの最新情報が異なっていることが原因らしい。
その場合は,「-f」をプッシュコマンドに付与することで,強制的にプッシュ出来る。
(※:今回はテスト環境なので強引にやってしまったが,実環境では推奨しない...)

$ git push -f origin master

プッシュ後,再度リモートリポジトリを見てみると・・・
image.png

作成した「test.py」がリモートリポジトリに登録されている!

ちなみに,リモートリポジトリの更新内容をローカルに反映したい時は,
以下コマンドを実行する。

$ git pull origin master

参考にした記事

・git pushがreject(拒否)されたときの対処法 @Takao_ さん
https://qiita.com/Takao_/items/5e563d5ea61d2829e497

今回のまとめ

これにてGit及びGitHubの環境が出来た。
おそらくもっと色々な設定やコマンドがあると思うのでその辺はまた別途記事に起こしていこうかな。
今後は自分で書いたソースコードとかもガンガン上げてアウトプットしていきたいと思う。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

よく使うGitコマンド集

はじめに

エンジニア未経験の方が、現場に入ってチーム開発を行うようになるまでに身に付けておいた方が良いと思うGitコマンドを、最低限抽出してまとめてみました。

また今回紹介するGitコマンドは、実際にチーム開発で使用する時の順序で紹介していきます。

よく使うGitコマンド集

git clone

まずはプロジェクトに参画したら、
ソースコードをGitHubなどのリモートリポジトリからローカルに持ってこないといけないですよね。

その際に使うのは、cloneです。
このコマンドでリモートリポジトリのソースコードをローカルに引っ張ってこれます。

git clone リモートリポジトリURL

git pull

ただこれを行うのは最初の一回だけで、
その後リモートリポジトリのコードが更新されるたびに変更内容を引っ張ってくるにはpullを使います。

git pull origin ブランチ名

git checkout -b

ローカルに持ってこれたら、
今度は作業ブランチをcheckout -bで作成します。
またこのコマンドはブランチを作成したらそのまま作ったブランチへ移動します。

git checkout -b 新しいブランチ名

git checkout

すでに存在するブランチに移動するだけならcheckoutを使います。

git checkout ブランチ名

git merge

今度はpull又はcloneで引っ張ってきたコードを作業ブランチへ反映させるためにmergeを使います。

git merge pull又はcloneで引っ張ってきたブランチ名

git branch

git branchを使うと、
ローカルにあるブランチ一覧と、現在どこのブランチにいるのかが確認できます。

git branch

git add

mergeしてきたあとは実際に担当箇所の開発を行っていきます。
機能の開発が終わったら、新しく書いたコードを保存します。
そのためにまずaddを使って変更内容をインデックスに追加します。
addの後ろの「 . 」は、変更した部分を対象にするという意味です。

git add .

git commit

addしたらcommitで変更内容を保存します。

git commit -m "コミットメッセージ"

git push

そして最後に、開発した内容をpushを使ってGitHubなどのリモートリポジトリへ送ります。
「head」は現在いるブランチを表しています。

git push origin head

あとはGitHubなどでpull requestを行う形になります。

最後に

以上が、チーム開発を行う上でのGitの大まかな使い方になります。
実際に使いそうな順番で解説していきましたので、参考になれば幸いです。

最後までご覧くださりありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

未経験が現場に入るまでに覚えておくと良いGitコマンド集

はじめに

エンジニア未経験の方が、現場に入ってチーム開発を行うようになるまでに身に付けておいた方が良いと思うGitコマンドを、最低限抽出してまとめてみました。

また今回紹介するGitコマンドは、実際にチーム開発で使用する時の順序で紹介していきます。

よく使うGitコマンド集

git clone

まずはプロジェクトに参画したら、
ソースコードをGitHubなどのリモートリポジトリからローカルに持ってこないといけないですよね。

その際に使うのは、cloneです。
このコマンドでリモートリポジトリのソースコードをローカルに引っ張ってこれます。

git clone リモートリポジトリURL

git pull

ただこれを行うのは最初の一回だけで、
その後リモートリポジトリのコードが更新されるたびに変更内容を引っ張ってくるにはpullを使います。

git pull origin ブランチ名

git checkout -b

ローカルに持ってこれたら、
今度は作業ブランチをcheckout -bで作成します。
またこのコマンドはブランチを作成したらそのまま作ったブランチへ移動します。

git checkout -b 新しいブランチ名

git checkout

すでに存在するブランチに移動するだけならcheckoutを使います。

git checkout ブランチ名

git merge

今度はpull又はcloneで引っ張ってきたコードを作業ブランチへ反映させるためにmergeを使います。

git merge pull又はcloneで引っ張ってきたブランチ名

git branch

git branchを使うと、
ローカルにあるブランチ一覧と、現在どこのブランチにいるのかが確認できます。

git branch

git add

mergeしてきたあとは実際に担当箇所の開発を行っていきます。
機能の開発が終わったら、新しく書いたコードを保存します。
そのためにまずaddを使って変更内容をインデックスに追加します。
addの後ろの「 . 」は、変更した部分を対象にするという意味です。

git add .

git commit

addしたらcommitで変更内容を保存します。

git commit -m "コミットメッセージ"

git push

そして最後に、開発した内容をpushを使ってGitHubなどのリモートリポジトリへ送ります。
「head」は現在いるブランチを表しています。

git push origin head

あとはGitHubなどでpull requestを行う形になります。

最後に

以上が、チーム開発を行う上でのGitの大まかな使い方になります。
実際に使いそうな順番で解説していきましたので、参考になれば幸いです。

最後までご覧くださりありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

チーム開発でよく使うGitコマンド集

はじめに

エンジニア未経験の方が、現場に入ってチーム開発を行うようになるまでに身に付けておいた方が良いと思うGitコマンドを、最低限抽出してまとめてみました。

また今回紹介するGitコマンドは、実際にチーム開発で使用する時の順序で紹介していきます。

よく使うGitコマンド集

git clone

まずはプロジェクトに参画したら、
ソースコードをGitHubなどのリモートリポジトリからローカルに持ってこないといけないですよね。

その際に使うのは、cloneです。
このコマンドでリモートリポジトリのソースコードをローカルに引っ張ってこれます。

git clone リモートリポジトリURL

git pull

ただこれを行うのは最初の一回だけで、
その後リモートリポジトリのコードが更新されるたびに変更内容を引っ張ってくるにはpullを使います。

git pull origin ブランチ名

git checkout -b

ローカルに持ってこれたら、
今度は作業ブランチをcheckout -bで作成します。
またこのコマンドはブランチを作成したらそのまま作ったブランチへ移動します。

git checkout -b 新しいブランチ名

git checkout

すでに存在するブランチに移動するだけならcheckoutを使います。

git checkout ブランチ名

git merge

今度はpull又はcloneで引っ張ってきたコードを作業ブランチへ反映させるためにmergeを使います。

git merge pull又はcloneで引っ張ってきたブランチ名

git branch

git branchを使うと、
ローカルにあるブランチ一覧と、現在どこのブランチにいるのかが確認できます。

git branch

git add

mergeしてきたあとは実際に担当箇所の開発を行っていきます。
機能の開発が終わったら、新しく書いたコードを保存します。
そのためにまずaddを使って変更内容をインデックスに追加します。
addの後ろの「 . 」は、変更した部分を対象にするという意味です。

git add .

git commit

addしたらcommitで変更内容を保存します。

git commit -m "コミットメッセージ"

git push

そして最後に、開発した内容をpushを使ってGitHubなどのリモートリポジトリへ送ります。
「head」は現在いるブランチを表しています。

git push origin head

あとはGitHubなどでpull requestを行う形になります。

最後に

以上が、チーム開発を行う上でのGitの大まかな使い方になります。
実際に使いそうな順番で解説していきましたので、参考になれば幸いです。

最後までご覧くださりありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pull Reaquest template 作成手順

 PRのテンプレート作成手順

チーム開発をしていて、必ず必要だと思ったので記事にしました。
今までなかった現場もあったので、あるととても良いです。
PRの内容がすっきりして、レビュー時にコミュニケーションコストが省けるし、何をやったのかcloseしてもわるのであるととても便利です。
無いチームは書くことをお勧めします。

作り方

$ cd 作りたいディレクトリに移動
$ mkdir .github
$ cd .github
$ touch pull_request_template.md

テンプレ中身(Markdown)

サンプル

## PRの概要

<!-- このPRの概要を記入してください -->

## レビューして欲しい箇所

<!-- PRで特にレビューして欲しい箇所を記載してください -->

## Draftにした理由(WIPでも良いと思う)

<!-- Draftにした理由を記載してください -->

## スクリーンショット

<!-- 作業した箇所のスクリーンショットなどあれば貼ってください -->
Before       | After
------------- | -------------
<image src="" width="320">|<image src=""  width="320">


## その他

 <!-- レビュワーへの参考情報(実装上の懸念点や注意点などあれば記載) -->

上記の内容をマージすればマージ以降PRの入力欄にこの内容が反映されます。

まとめ

上記意外にもチーム内でいろいろ決めて追加していくと良さそう。これはあくまでも参考程度の内容です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Git】エラー、コンフリクトのお悩み解決 - Gitの気持ちになって理解する checkout/merge/pull

よく見るエラーメッセージ

Gitを使っていると頻繁に目にするエラーメッセージ……
例えば次のメッセージが表示されたとき、あなたはどのように対応するでしょうか?

error: Your local changes to the following files would be overwritten by checkout:
    test.txt
Please commit your changes or stash them before you switch branches.
Aborting

メッセージの意味、その対処法や背景までわかる人は、おそらく先を読む必要はないでしょう。
そうでない人はこの記事を読むことで、いままでより自信をもってgitを使うことができるようになるかもしれません。

この記事で説明すること

  • チェックアウトするときに、なぜエラーが発生するのか
  • マージやリベースするときに、なぜコンフリクトが発生するのか
  • チェックアウト時のエラーやコンフリクトを理解するためのgitのキホン
  • チェックアウトやマージ、プルが具体的になにをやっているのか

この記事が対象とする人

  • 冒頭のエラーメッセージの意味がわからない人
  • 冒頭のエラーメッセージへの対処法はわかるけど、その意味がいまいちわかっていない人
  • 検索で出たページのコマンドをコピペして、痛い目にあったことのある人

この記事が対象としない人

  • 冒頭のエラーメッセージの意味や対処法が完璧にわかる人
  • Gitで困ったことのない人
  • Gitに触ったことのない人、これから勉強する人

Gitのキホン

Gitの気持ちを理解する上で大切なことは以下の2点です。

  1. コミットはその時点におけるすべてのファイルのスナップショットである1
  2. ブランチはコミットを指し示しているだけ

これは本当に大事なので、いまここで目を閉じて復唱してください。

コミットはスナップショット

各コミットは、コミット作成時点でのgitの管理下にあるファイルすべてのスナップショット(その時点でのファイルの状態)を保存しています。

gitの管理下にあるというのは、コミットを作成したときにgit addコマンドやGUIツールでインデックスに登録されていたファイルのことです。過去(=ツリーの上流)のコミット作成時に登録されていたファイルも含みます。

「コミットする」という言葉の意味

注意
この節は飛ばしても構いません。以降を読み進めるにあたって、用語に引っ掛かりを感じたら戻って来てみてください。

「ファイルをコミットする」という言葉がよく使われますが、これには二つの動作が含まれています。

  1. ファイルAをインデックスに登録する
  2. ファイルAがインデックスに登録された状態で、コミットを作成する

また「コミットされていないファイル」という言葉もよく使われますが、これは以下のような意味です。

  • すでにgitの管理下にあるファイルAに(いまのコミットから)なんらかの変更が加えられている
  • ファイルAがインデックスに登録されていない、または、インデックスに登録されているが新たなコミットがまだ作成されていない

ブランチはコミットに貼り付けた付箋

masterやorigin/master, developなど色々なブランチを使用したことがあると思います。

ブランチは変更履歴のツリー(コミットの連なり)ではありません。ただ単にひとつのコミットを指し示しているだけの存在です。たとえるなら、区別しやすいようにコミットに付箋を貼り付けているようなものです。

Gitはコミットを作るときに、自動的にいまのブランチ(=付箋)を新しく作ったコミットに移動してくれているのです。

なぜチェックアウト時にエラーが発生するのか?

ここまでのキホン知識があると、チェックアウト時にエラーが発生する理由がわかります。

そもそもチェックアウトとは?

いまいる場所(HEAD)を指定したコミットに移動するコマンドです。
指定先としてブランチ(=付箋)を指定することもできて、その場合はブランチを切り替えるとともに、ブランチが指し示しているコミットに移動します。

ターミナルであれば以下のようなコマンドで実行できます。

git checkout 1a104f1 # コミットハッシュを指定して移動
git checkout develop # developブランチに移動

コミットを移動するとなにが起こるのか?

思い出してください。コミットはファイルのスナップショットを保存しているのでした。

コミットを移動すると、gitの管理下にあるファイルが、移動先のコミット作成時のスナップショットと同じ状態になります。
言い換えると、ファイルの内容が移動前の状態から移動先のコミット作成時の状態に変更されます

ここにチェックアウト時にエラーが発生する理由があります。

チェックアウト時に発生したエラーをよく読んでみる

冒頭のエラーをもう一度読んでみましょう。

error: Your local changes to the following files would be overwritten by checkout:
    test.txt
Please commit your changes or stash them before you switch branches.
Aborting

エラー:以下のファイルのローカルの変更がチェックアウトによって上書きされます。
test.txt
ブランチを切り替える前に、変更をコミットするかスタッシュしてください。
中断されました。

メッセージの通り、このエラーはファイルにコミットされていない変更がある場合に発生します。

Gitの気持ちになって考える

ここでgitの気持ちになってみましょう。

Git「お、masterブランチにチェックアウトしろっていう命令がきたよ」
Git「じゃあ、masterブランチが指し示しているこのコミットAに移動しなきゃ!」
Git「管理してるファイルたちをコミットAの状態に変更しないとね。」
Git「……あれ?このtext.txtってファイル、いまのコミット(の状態)から変更されてるよ。」
Git「このtext.txtってファイルはまだコミットされてないんだね。」
Git「でも、コミットAに移動しちゃったら、text.txtはコミットAのときの状態に戻さなきゃ。」
Git「(勝手に戻しちゃったら怒られそう…………どうしたらいいの……?)」
Git「」
Git「」
Git「」
Git「error: Your local changes to the following files would be overwritten by checkout……Aborting」

あたなの気持ちにもなってみましょう。

別のブランチをちょっと確認しようと、うっかりチェックアウトしただけで、(コミットするのを忘れていた)1時間かけて編集したファイルの変更がなかったことになったら嫌ですよね?

そんな悲劇を避けるために、gitは丁寧にエラーを返してくれているのです。

チェックアウトができる条件

チェックアウトができる条件は

  • Gitの管理下にあるファイルがすべてコミット済みであること2

です。これが満たされていない場合、gitはファイルを移動先のコミットの状態に変更することができない(変更していいかどうかわからない)のでエラーを返します。

Gitの管理下にないファイルが変更されたり新規に作成されている場合は、gitの管理下にあるファイルの状態とは関係ないため、チェックアウト可能です。

チェックアウト時にエラーがでたときの対処法

ここまでの説明でおわかりかと思いますが、エラーが発生した場合は、メッセージに従って、

  • コミットされていないファイルをコミットするかスタッシュする
  • または、コミットされていないファイルの変更を元に戻す

ことで、チェックアウトができる条件が満たされます。

例外的な状況で発生するエラー - スタッシュが効かない!

Gitの管理下にないファイルが新規作成・変更されていても、チェックアウト可能と書きました。しかし、この状況でも例外的にエラーが発生する場合があります。それは、

  • 移動先のコミットで同名のファイルがgitの管理下にある

場合です。このときも、gitは「いまのコミット下では管理下にないファイルA」を、移動先のコミットでの状態に変更していいかどうかわからないため、エラーが発生します。

Gitの管理下にないファイルはスタッシュの対象になりません。そのため、この状況ではスタッシュではチェックアウトできない状況が解消できないので、注意してください。

なぜマージするとコンフリクトが発生するのか?

次はマージすると発生するこのメッセージについて考えていきます。

CONFLICT (content): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.

そもそもマージとは?

二つの変更履歴を合流させるコマンドです。

典型的には「masterブランチにdevelopブランチをマージする」などの使い方ができます。

注:ブランチは変更履歴そのものではなく、コミットを指し示す付箋です。そのため、この言葉遣いは正確ではありません3。しかし、便宜上このような表現のほうが表面的にはわかりやすく、また通じやすいでしょう。

マージは具体的になにをやっているのか?

この節はGitのmergeコマンドのドキュメントからの引用です。

以下のような変更履歴をもつ二つのブランチ(masterとtopic)があるとします。

      A---B---C topic # topicブランチはコミットCを指し示している
     /
D---E---F---G master # masterブランチはコミットGを指し示している

このときに、masterブランチ側からtopicブランチをマージします。ターミナルからなら、次のコマンドです。

git checkout master
git merge topic

マージコマンドを受け取ると、gitはそれぞれのブランチが指し示すコミット(CとG)の共通の祖先であるコミットEまで遡ります。
そして、masterが指し示すコミットGに対して、コミットEからtopicブランチの指し示すコミットCまでの変更を、以下のように順番に再現していきます。

コミットGに対して・・・

  • <E → A>の変更を追加する
  • <A → B>の変更を追加する
  • <B → C>の変更を追加する

これらの変更を追加し終わったあと、コミットHを新しく作成して、マージ完了です(下図)。

      A---B---C topic
     /         \
D---E---F---G---H master

マージでコンフリクトが発生する理由

これもgitの気持ちになって考えてみれば、理由がおわかりいただけると思います。

masterブランチにtopicブランチをマージするとき、コミットEからコミットCへと向かう変更を、コミットGに対して順番に適用していきます。
しかし、コミットGそれ自体もコミットEからいくつかの変更を経て出来上がっているわけです。
たとえば、

  1. <E → A>への変更
  2. <F → G>への変更

これら2つの変更の両方で、同じ test.txt というファイルが編集されていたらどうでしょう?
マージコマンドが実行されて、コミットGに<E → A>の変更が適用されるとき、1の変更を使うのか2の変更を使うのか、gitには自分で判断することはできません。

このためコンフリクトを発生させて、どちらの変更を使うかの選択はユーザーに委ねるのです。

<<<<<<< HEAD
<F → G>への変更
=======
<E → A>への変更
>>>>>>> topic

このようなコンフリクトが発生した場合は、いずれか一方の変更を残したり、二つの変更のいいとこ取りをするなりしてファイルを保存します。そして、改めてインデックスに登録したあと、新しいコミットを作成します。

 # <E → A>への変更だけを残して、他の部分は削除した
<E → A>への変更

プルとは

プルとは主に、リモートリポジトリのブランチをローカル(手元)のリポジトリのブランチにマージするときに使うコマンドです。

このコマンドは、リモートリポジトリのブランチをローカルに取得してくるフェッチ(fetch)コマンドを使ったあとに、対象のブランチをマージする動作を組み合わせたものに過ぎません。

# リモートリポジトリのorigin/masterブランチを手元のmasterにマージするコマンド
git pull origin master 

# フェッチしたあとマージしても同様のことが実現できる
git fetch # ローカルのorigin/masterがリモートのorigin/masterと同じ位置に同期される
git merge origin/master # origin/masterをmasterにマージする

そのため、プルで発生するコンフリクトはマージと同じように扱うことができます。

おすすめのgitの解説サイト

ぼくがgitを使い始めたときは、次のサイトで勉強しました。
それ以降は、少し高度なことをやりたくなったときに、都度検索して調べる方式でやっていますが、業務上gitで困ることはほとんどありません。

このサイトで解説されていなくて、いざというときに知っておいた方がいいコマンドとして、git reflogがあります。このコマンドは、直前のgitでの操作(失敗)をほとんどの場合、取り消すことができます。
ただし、gitの基礎がわかっていないと難しいコマンドなので、Gitをはじめからていねいになどで、基礎的な知識をしっかり固めたあとで挑戦するほうがよいです。次のページは比較的丁寧に解説してくれています。

謝辞

この記事の内容や言い回しの多くは、@akio0911さんがアプリ道場サロン(iOSアプリ開発をテーマとしたサロン)内で行った「git講座」を参考に書かせていただきました。
また、サロン内でのgitに関するやりとりを参考に書かせていただいた節もあります。

akioさんはもちろんのこと、サロンの皆様にはいつもモチベーションをいただいています。ありがとうございます。
iOSアプリを勉強中で仲間が欲しい人は是非覗いてみてください!

この記事がgitで困っている人の助けになれば嬉しいです。

参考


  1. 内部的にはデータ容量を減らすためにgitが保存方法を工夫していますが、gitの気持ちを理解する上ではこう理解するのがよいです。 

  2. チェックアウトには「コミットを移動せずブランチだけを切り替える」場合もあります。この場合は、この条件が満たされている必要はありません。 

  3. 正確に書くならば、「masterブランチが指し示すコミットαとdevelopブランチが指し示すコミットβの共通の祖先であるコミットγからの、それぞれの変更履歴をマージする」という感じになるでしょうか。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gitのお悩み解決 - Gitの気持ちになって考える checkout/merge/pull

よく見るエラーメッセージ

Gitを使っていると頻繁に目にするエラーメッセージ……
例えば次のメッセージが表示されたとき、あなたはどのように対応するでしょうか?

error: Your local changes to the following files would be overwritten by checkout:
    test.txt
Please commit your changes or stash them before you switch branches.
Aborting

メッセージの意味、その対処法や背景までわかる人は、おそらく先を読む必要はないでしょう。
そうでない人はこの記事を読むことで、いままでより自信をもってgitを使うことができるようになるかもしれません。

この記事で説明すること

  • チェックアウトするときに、なぜエラーが発生するのか
  • マージやリベースするときに、なぜコンフリクトが発生するのか
  • チェックアウト時のエラーやコンフリクトを理解するためのgitのキホン
  • チェックアウトやマージ、プルが具体的になにをやっているのか

この記事が対象とする人

  • 冒頭のエラーメッセージの意味がわからない人
  • 冒頭のエラーメッセージへの対処法はわかるけど、その意味がいまいちわかっていない人
  • 検索で出たページのコマンドをコピペして、痛い目にあったことのある人

この記事が対象としない人

  • 冒頭のエラーメッセージの意味や対処法が完璧にわかる人
  • Gitで困ったことのない人
  • Gitに触ったことのない人、これから勉強する人

Gitのキホン

Gitの気持ちを理解する上で大切なことは以下の2点です。

  1. コミットはその時点におけるすべてのファイルのスナップショットである1
  2. ブランチはコミットを指し示しているだけ

これは本当に大事なので、いまここで目を閉じて復唱してください。

コミットはスナップショット

各コミットは、コミット作成時点でのgitの管理下にあるファイルすべてのスナップショット(その時点でのファイルの状態)を保存しています。

gitの管理下にあるというのは、コミットを作成したときにgit addコマンドやGUIツールでインデックスに登録されていたファイルのことです。過去(=ツリーの上流)のコミット作成時に登録されていたファイルも含みます。

「コミットする」という言葉の意味

注意
この節は飛ばしても構いません。以降を読み進めるにあたって、用語に引っ掛かりを感じたら戻って来てみてください。

「ファイルをコミットする」という言葉がよく使われますが、これには二つの動作が含まれています。

  1. ファイルAをインデックスに登録する
  2. ファイルAがインデックスに登録された状態で、コミットを作成する

また「コミットされていないファイル」という言葉もよく使われますが、これは以下のような意味です。

  • すでにgitの管理下にあるファイルAに(いまのコミットから)なんらかの変更が加えられている
  • ファイルAがインデックスに登録されていない、または、インデックスに登録されているが新たなコミットがまだ作成されていない

ブランチはコミットに貼り付けた付箋

masterやorigin/master, developなど色々なブランチを使用したことがあると思います。

ブランチは変更履歴のツリー(コミットの連なり)ではありません。ただ単にひとつのコミットを指し示しているだけの存在です。たとえるなら、区別しやすいようにコミットに付箋を貼り付けているようなものです。

Gitはコミットを作るときに、自動的にいまのブランチ(=付箋)を新しく作ったコミットに移動してくれているのです。

なぜチェックアウト時にエラーが発生するのか?

ここまでのキホン知識があると、チェックアウト時にエラーが発生する理由がわかります。

そもそもチェックアウトとは?

いまいる場所(HEAD)を指定したコミットに移動するコマンドです。
指定先としてブランチ(=付箋)を指定することもできて、その場合はブランチを切り替えるとともに、ブランチが指し示しているコミットに移動します。

ターミナルであれば以下のようなコマンドで実行できます。

git checkout 1a104f1 # コミットハッシュを指定して移動
git checkout develop # developブランチに移動

コミットを移動するとなにが起こるのか?

思い出してください。コミットはファイルのスナップショットを保存しているのでした。

コミットを移動すると、gitの管理下にあるファイルが、移動先のコミット作成時のスナップショットと同じ状態になります。
言い換えると、ファイルの内容が移動前の状態から移動先のコミット作成時の状態に変更されます

ここにチェックアウト時にエラーが発生する理由があります。

チェックアウト時に発生したエラーをよく読んでみる

冒頭のエラーをもう一度読んでみましょう。

error: Your local changes to the following files would be overwritten by checkout:
    test.txt
Please commit your changes or stash them before you switch branches.
Aborting

エラー:以下のファイルのローカルの変更がチェックアウトによって上書きされます。
test.txt
ブランチを切り替える前に、変更をコミットするかスタッシュしてください。
中断されました。

メッセージの通り、このエラーはファイルにコミットされていない変更がある場合に発生します。

Gitの気持ちになって考える

ここでgitの気持ちになってみましょう。

Git「お、masterブランチにチェックアウトしろっていう命令がきたよ」
Git「じゃあ、masterブランチが指し示しているこのコミットAに移動しなきゃ!」
Git「管理してるファイルたちをコミットAの状態に変更しないとね。」
Git「……あれ?このtext.txtってファイル、いまのコミット(の状態)から変更されてるよ。」
Git「このtext.txtってファイルはまだコミットされてないんだね。」
Git「でも、コミットAに移動しちゃったら、text.txtはコミットAのときの状態に戻さなきゃ。」
Git「(勝手に戻しちゃったら怒られそう…………どうしたらいいの……?)」
Git「」
Git「」
Git「」
Git「error: Your local changes to the following files would be overwritten by checkout……Aborting」

あたなの気持ちにもなってみましょう。

別のブランチをちょっと確認しようと、うっかりチェックアウトしただけで、(コミットするのを忘れていた)1時間かけて編集したファイルの変更がなかったことになったら嫌ですよね?

そんな悲劇を避けるために、gitは丁寧にエラーを返してくれているのです。

チェックアウトができる条件

チェックアウトができる条件は

  • Gitの管理下にあるファイルがすべてコミット済みであること2

です。これが満たされていない場合、gitはファイルを移動先のコミットの状態に変更することができない(変更していいかどうかわからない)のでエラーを返します。

Gitの管理下にないファイルが変更されたり新規に作成されている場合は、gitの管理下にあるファイルの状態とは関係ないため、チェックアウト可能です。

チェックアウト時にエラーがでたときの対処法

ここまでの説明でおわかりかと思いますが、エラーが発生した場合は、メッセージに従って、

  • コミットされていないファイルをコミットするかスタッシュする
  • または、コミットされていないファイルの変更を元に戻す

ことで、チェックアウトができる条件が満たされます。

例外的な状況で発生するエラー - スタッシュが効かない!

Gitの管理下にないファイルが新規作成・変更されていても、チェックアウト可能と書きました。しかし、この状況でも例外的にエラーが発生する場合があります。それは、

  • 移動先のコミットで同名のファイルがgitの管理下にある

場合です。このときも、gitは「いまのコミット下では管理下にないファイルA」を、移動先のコミットでの状態に変更していいかどうかわからないため、エラーが発生します。

Gitの管理下にないファイルはスタッシュの対象になりません。そのため、この状況ではスタッシュではチェックアウトできない状況が解消できないので、注意してください。

なぜマージするとコンフリクトが発生するのか?

次はマージすると発生するこのメッセージについて考えていきます。

CONFLICT (content): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.

そもそもマージとは?

二つの変更履歴を合流させるコマンドです。

典型的には「ブランチAに別のブランチBをマージする」などの使い方ができます。

注:ブランチは変更履歴そのものではなく、コミットを指し示す付箋です。そのため、この言葉遣いは正確ではありません3。しかし、便宜上このような表現のほうが表面的にはわかりやすく、また通じやすいでしょう。

マージは具体的になにをやっているのか?

この節はGitのmergeコマンドのドキュメントからの引用です。

以下のような変更履歴をもつ二つのブランチ(masterとtopic)があるとします。

      A---B---C topic # topicブランチはコミットCを指し示している
     /
D---E---F---G master # masterブランチはコミットGを指し示している

このときに、masterブランチ側からtopicブランチをマージします。ターミナルからなら、次のコマンドです。

git checkout master
git merge topic

マージコマンドを受け取ると、gitはそれぞれのブランチが指し示すコミット(CとG)の共通の祖先であるコミットEまで遡ります。
そして、masterが指し示すコミットGに対して、コミットEからtopicブランチの指し示すコミットCまでの変更を、以下のように順番に再現していきます。

コミットGに対して・・・

  • <E → A>の変更を追加する
  • <A → B>の変更を追加する
  • <B → C>の変更を追加する

これらの変更を追加し終わったあと、コミットHを新しく作成して、マージ完了です(下図)。

      A---B---C topic
     /         \
D---E---F---G---H master

マージでコンフリクトが発生する理由

これもgitの気持ちになって考えてみれば、理由がおわかりいただけると思います。

masterブランチにtopicブランチをマージするとき、コミットEからコミットCへと向かう変更を、コミットGに対して順番に適用していきます。
しかし、コミットGそれ自体もコミットEからいくつかの変更を経て出来上がっているわけです。
たとえば、

  1. <E → A>への変更
  2. <F → G>への変更

これら2つの変更の両方で、同じ test.txt というファイルが編集されていたらどうでしょう?
マージコマンドが実行されて、コミットGに<E → A>の変更が適用されるとき、1の変更を使うのか2の変更を使うのか、gitには自分で判断することはできません。

このためコンフリクトを発生させて、どちらの変更を使うかの選択はユーザーに委ねるのです。

<<<<<<< HEAD
<F → G>への変更
=======
<E → A>への変更
>>>>>>> topic

このようなコンフリクトが発生した場合は、いずれか一方の変更を残したり、二つの変更のいいとこ取りをしてファイルを保存します。そして、改めてインデックスに登録したあと、新しいコミットを作成します。

 # <E → A>への変更だけを残して、他の部分は削除した
<E → A>への変更

プルとは

プルとは主に、リモートリポジトリのブランチをローカル(手元)のリポジトリのブランチにマージするときに使うコマンドです。

このコマンドは、リモートリポジトリのブランチをローカルに取得してくるフェッチ(fetch)コマンドを使ったあとに、対象のブランチをマージする動作を組み合わせたものに過ぎません。

# リモートリポジトリのorigin/masterブランチを手元のmasterにマージするコマンド
git pull origin master 

# フェッチしたあとマージしても同様のことが実現できる
git fetch # ローカルのorigin/masterがリモートのorigin/masterと同じ位置に同期される
git merge origin/master # origin/masterをmasterにマージする

そのため、プルで発生するコンフリクトはマージと同じように扱うことができます。

おすすめのgitの解説サイト

ぼくがgitを使い始めたときは、次のサイトで勉強しました。
それ以降は、少し高度なことをやりたくなったときに、都度検索して調べる方式でやっていますが、業務上gitで困ることはほとんどありません。

このサイトで解説されていなくて、いざというときに知っておいた方がいいコマンドとして、git reflogがあります。このコマンドは、直前のgitでの操作(失敗)をほとんどの場合、取り消すことができます。
ただし、gitの基礎がわかっていないとけっこう難しいコマンドなので、Gitをはじめからていねいになどで、基礎的な知識をしっかり固めたあとで挑戦するほうがよいです。次のページは比較的丁寧に解説してくれています。

謝辞

この記事の内容や言い回しの多くは、@akio0911さんがアプリ道場サロン(iOSアプリ開発をテーマとしたサロン)内で行った「git講座」を参考に書かせていただきました。
また、サロン内でのgitに関するやりとりを参考に書かせていただいた節もあります。

akioさんはもちろんのこと、サロンの皆様にはいつもモチベーションをいただいています。
iOSアプリを勉強中で仲間が欲しい人は是非覗いてみてください!

この記事がgitで困っている人の助けになれば嬉しいです。

参考


  1. 内部的にはデータ容量を減らすためにgitが保存方法を工夫していますが、gitの気持ちを理解する上ではこう理解するのがよいです。 

  2. チェックアウトには「コミットを移動せずブランチだけを切り替える」場合もあります。この場合は、この条件が満たされている必要はありません。 

  3. 正確に書くならば、「ブランチAが指し示すコミットαとブランチBが指し示すコミットβの共通の祖先であるコミットγからの、それぞれの変更履歴をマージする」という感じになるでしょうか。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gitのお悩み解決 - Gitの気持ちになって理解する checkout/merge/pull

よく見るエラーメッセージ

Gitを使っていると頻繁に目にするエラーメッセージ……
例えば次のメッセージが表示されたとき、あなたはどのように対応するでしょうか?

error: Your local changes to the following files would be overwritten by checkout:
    test.txt
Please commit your changes or stash them before you switch branches.
Aborting

メッセージの意味、その対処法や背景までわかる人は、おそらく先を読む必要はないでしょう。
そうでない人はこの記事を読むことで、いままでより自信をもってgitを使うことができるようになるかもしれません。

この記事で説明すること

  • チェックアウトするときに、なぜエラーが発生するのか
  • マージやリベースするときに、なぜコンフリクトが発生するのか
  • チェックアウト時のエラーやコンフリクトを理解するためのgitのキホン
  • チェックアウトやマージ、プルが具体的になにをやっているのか

この記事が対象とする人

  • 冒頭のエラーメッセージの意味がわからない人
  • 冒頭のエラーメッセージへの対処法はわかるけど、その意味がいまいちわかっていない人
  • 検索で出たページのコマンドをコピペして、痛い目にあったことのある人

この記事が対象としない人

  • 冒頭のエラーメッセージの意味や対処法が完璧にわかる人
  • Gitで困ったことのない人
  • Gitに触ったことのない人、これから勉強する人

Gitのキホン

Gitの気持ちを理解する上で大切なことは以下の2点です。

  1. コミットはその時点におけるすべてのファイルのスナップショットである1
  2. ブランチはコミットを指し示しているだけ

これは本当に大事なので、いまここで目を閉じて復唱してください。

コミットはスナップショット

各コミットは、コミット作成時点でのgitの管理下にあるファイルすべてのスナップショット(その時点でのファイルの状態)を保存しています。

gitの管理下にあるというのは、コミットを作成したときにgit addコマンドやGUIツールでインデックスに登録されていたファイルのことです。過去(=ツリーの上流)のコミット作成時に登録されていたファイルも含みます。

「コミットする」という言葉の意味

注意
この節は飛ばしても構いません。以降を読み進めるにあたって、用語に引っ掛かりを感じたら戻って来てみてください。

「ファイルをコミットする」という言葉がよく使われますが、これには二つの動作が含まれています。

  1. ファイルAをインデックスに登録する
  2. ファイルAがインデックスに登録された状態で、コミットを作成する

また「コミットされていないファイル」という言葉もよく使われますが、これは以下のような意味です。

  • すでにgitの管理下にあるファイルAに(いまのコミットから)なんらかの変更が加えられている
  • ファイルAがインデックスに登録されていない、または、インデックスに登録されているが新たなコミットがまだ作成されていない

ブランチはコミットに貼り付けた付箋

masterやorigin/master, developなど色々なブランチを使用したことがあると思います。

ブランチは変更履歴のツリー(コミットの連なり)ではありません。ただ単にひとつのコミットを指し示しているだけの存在です。たとえるなら、区別しやすいようにコミットに付箋を貼り付けているようなものです。

Gitはコミットを作るときに、自動的にいまのブランチ(=付箋)を新しく作ったコミットに移動してくれているのです。

なぜチェックアウト時にエラーが発生するのか?

ここまでのキホン知識があると、チェックアウト時にエラーが発生する理由がわかります。

そもそもチェックアウトとは?

いまいる場所(HEAD)を指定したコミットに移動するコマンドです。
指定先としてブランチ(=付箋)を指定することもできて、その場合はブランチを切り替えるとともに、ブランチが指し示しているコミットに移動します。

ターミナルであれば以下のようなコマンドで実行できます。

git checkout 1a104f1 # コミットハッシュを指定して移動
git checkout develop # developブランチに移動

コミットを移動するとなにが起こるのか?

思い出してください。コミットはファイルのスナップショットを保存しているのでした。

コミットを移動すると、gitの管理下にあるファイルが、移動先のコミット作成時のスナップショットと同じ状態になります。
言い換えると、ファイルの内容が移動前の状態から移動先のコミット作成時の状態に変更されます

ここにチェックアウト時にエラーが発生する理由があります。

チェックアウト時に発生したエラーをよく読んでみる

冒頭のエラーをもう一度読んでみましょう。

error: Your local changes to the following files would be overwritten by checkout:
    test.txt
Please commit your changes or stash them before you switch branches.
Aborting

エラー:以下のファイルのローカルの変更がチェックアウトによって上書きされます。
test.txt
ブランチを切り替える前に、変更をコミットするかスタッシュしてください。
中断されました。

メッセージの通り、このエラーはファイルにコミットされていない変更がある場合に発生します。

Gitの気持ちになって考える

ここでgitの気持ちになってみましょう。

Git「お、masterブランチにチェックアウトしろっていう命令がきたよ」
Git「じゃあ、masterブランチが指し示しているこのコミットAに移動しなきゃ!」
Git「管理してるファイルたちをコミットAの状態に変更しないとね。」
Git「……あれ?このtext.txtってファイル、いまのコミット(の状態)から変更されてるよ。」
Git「このtext.txtってファイルはまだコミットされてないんだね。」
Git「でも、コミットAに移動しちゃったら、text.txtはコミットAのときの状態に戻さなきゃ。」
Git「(勝手に戻しちゃったら怒られそう…………どうしたらいいの……?)」
Git「」
Git「」
Git「」
Git「error: Your local changes to the following files would be overwritten by checkout……Aborting」

あたなの気持ちにもなってみましょう。

別のブランチをちょっと確認しようと、うっかりチェックアウトしただけで、(コミットするのを忘れていた)1時間かけて編集したファイルの変更がなかったことになったら嫌ですよね?

そんな悲劇を避けるために、gitは丁寧にエラーを返してくれているのです。

チェックアウトができる条件

チェックアウトができる条件は

  • Gitの管理下にあるファイルがすべてコミット済みであること2

です。これが満たされていない場合、gitはファイルを移動先のコミットの状態に変更することができない(変更していいかどうかわからない)のでエラーを返します。

Gitの管理下にないファイルが変更されたり新規に作成されている場合は、gitの管理下にあるファイルの状態とは関係ないため、チェックアウト可能です。

チェックアウト時にエラーがでたときの対処法

ここまでの説明でおわかりかと思いますが、エラーが発生した場合は、メッセージに従って、

  • コミットされていないファイルをコミットするかスタッシュする
  • または、コミットされていないファイルの変更を元に戻す

ことで、チェックアウトができる条件が満たされます。

例外的な状況で発生するエラー - スタッシュが効かない!

Gitの管理下にないファイルが新規作成・変更されていても、チェックアウト可能と書きました。しかし、この状況でも例外的にエラーが発生する場合があります。それは、

  • 移動先のコミットで同名のファイルがgitの管理下にある

場合です。このときも、gitは「いまのコミット下では管理下にないファイルA」を、移動先のコミットでの状態に変更していいかどうかわからないため、エラーが発生します。

Gitの管理下にないファイルはスタッシュの対象になりません。そのため、この状況ではスタッシュではチェックアウトできない状況が解消できないので、注意してください。

なぜマージするとコンフリクトが発生するのか?

次はマージすると発生するこのメッセージについて考えていきます。

CONFLICT (content): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.

そもそもマージとは?

二つの変更履歴を合流させるコマンドです。

典型的には「masterブランチにdevelopブランチをマージする」などの使い方ができます。

注:ブランチは変更履歴そのものではなく、コミットを指し示す付箋です。そのため、この言葉遣いは正確ではありません3。しかし、便宜上このような表現のほうが表面的にはわかりやすく、また通じやすいでしょう。

マージは具体的になにをやっているのか?

この節はGitのmergeコマンドのドキュメントからの引用です。

以下のような変更履歴をもつ二つのブランチ(masterとtopic)があるとします。

      A---B---C topic # topicブランチはコミットCを指し示している
     /
D---E---F---G master # masterブランチはコミットGを指し示している

このときに、masterブランチ側からtopicブランチをマージします。ターミナルからなら、次のコマンドです。

git checkout master
git merge topic

マージコマンドを受け取ると、gitはそれぞれのブランチが指し示すコミット(CとG)の共通の祖先であるコミットEまで遡ります。
そして、masterが指し示すコミットGに対して、コミットEからtopicブランチの指し示すコミットCまでの変更を、以下のように順番に再現していきます。

コミットGに対して・・・

  • <E → A>の変更を追加する
  • <A → B>の変更を追加する
  • <B → C>の変更を追加する

これらの変更を追加し終わったあと、コミットHを新しく作成して、マージ完了です(下図)。

      A---B---C topic
     /         \
D---E---F---G---H master

マージでコンフリクトが発生する理由

これもgitの気持ちになって考えてみれば、理由がおわかりいただけると思います。

masterブランチにtopicブランチをマージするとき、コミットEからコミットCへと向かう変更を、コミットGに対して順番に適用していきます。
しかし、コミットGそれ自体もコミットEからいくつかの変更を経て出来上がっているわけです。
たとえば、

  1. <E → A>への変更
  2. <F → G>への変更

これら2つの変更の両方で、同じ test.txt というファイルが編集されていたらどうでしょう?
マージコマンドが実行されて、コミットGに<E → A>の変更が適用されるとき、1の変更を使うのか2の変更を使うのか、gitには自分で判断することはできません。

このためコンフリクトを発生させて、どちらの変更を使うかの選択はユーザーに委ねるのです。

<<<<<<< HEAD
<F → G>への変更
=======
<E → A>への変更
>>>>>>> topic

このようなコンフリクトが発生した場合は、いずれか一方の変更を残したり、二つの変更のいいとこ取りをしてファイルを保存します。そして、改めてインデックスに登録したあと、新しいコミットを作成します。

 # <E → A>への変更だけを残して、他の部分は削除した
<E → A>への変更

プルとは

プルとは主に、リモートリポジトリのブランチをローカル(手元)のリポジトリのブランチにマージするときに使うコマンドです。

このコマンドは、リモートリポジトリのブランチをローカルに取得してくるフェッチ(fetch)コマンドを使ったあとに、対象のブランチをマージする動作を組み合わせたものに過ぎません。

# リモートリポジトリのorigin/masterブランチを手元のmasterにマージするコマンド
git pull origin master 

# フェッチしたあとマージしても同様のことが実現できる
git fetch # ローカルのorigin/masterがリモートのorigin/masterと同じ位置に同期される
git merge origin/master # origin/masterをmasterにマージする

そのため、プルで発生するコンフリクトはマージと同じように扱うことができます。

おすすめのgitの解説サイト

ぼくがgitを使い始めたときは、次のサイトで勉強しました。
それ以降は、少し高度なことをやりたくなったときに、都度検索して調べる方式でやっていますが、業務上gitで困ることはほとんどありません。

このサイトで解説されていなくて、いざというときに知っておいた方がいいコマンドとして、git reflogがあります。このコマンドは、直前のgitでの操作(失敗)をほとんどの場合、取り消すことができます。
ただし、gitの基礎がわかっていないと難しいコマンドなので、Gitをはじめからていねいになどで、基礎的な知識をしっかり固めたあとで挑戦するほうがよいです。次のページは比較的丁寧に解説してくれています。

謝辞

この記事の内容や言い回しの多くは、@akio0911さんがアプリ道場サロン(iOSアプリ開発をテーマとしたサロン)内で行った「git講座」を参考に書かせていただきました。
また、サロン内でのgitに関するやりとりを参考に書かせていただいた節もあります。

akioさんはもちろんのこと、サロンの皆様にはいつもモチベーションをいただいています。ありがとうございます。
iOSアプリを勉強中で仲間が欲しい人は是非覗いてみてください!

この記事がgitで困っている人の助けになれば嬉しいです。

参考


  1. 内部的にはデータ容量を減らすためにgitが保存方法を工夫していますが、gitの気持ちを理解する上ではこう理解するのがよいです。 

  2. チェックアウトには「コミットを移動せずブランチだけを切り替える」場合もあります。この場合は、この条件が満たされている必要はありません。 

  3. 正確に書くならば、「masterブランチが指し示すコミットαとdevelopブランチが指し示すコミットβの共通の祖先であるコミットγからの、それぞれの変更履歴をマージする」という感じになるでしょうか。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

git で 正規表現を使った操作したい

概要

  • ブランチに対して,gitコマンドを一斉操作したい場合
  • git branchコマンドに対して grepでブランチ絞り込み
  • | パイプでつなげて,任意のgitコマンドを渡す

例.リモートブランチで一致したブランチ名だけfetchしたい

git branch -r | grep "origin/develop*" | git fetch
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

bashプロンプトの表示をNerdfontsで格好良くする

無限人がやってるPowerLineのあのパンくずリスト型の表示が格好いいアレです。

最近は色々なshがあるしプラグイン的な形でサクッと導入できる中、わざわざbashでひいこらやる必要も無いと思うけど、他のshを導入するのは面倒くさいし、それはともかく自分用にカスタマイズしたいなと思ったので頑張りました。

作ってる途中で色々知見を得たので、備忘録的な。

格好いい画像

2020-09-07.png
2020-09-07 (1).png

筆者環境

WindowsTerminal (fontには@tawara_氏のHackGenNerd35 Consoleを利用)
WSL2 (Ubuntu 18.04.5)
bash 4.4.20
これと完全一致じゃなくても別にどうにかなるとは思います

とにかくソースコード

nerdps1.sh
#!/bin/bash
set -euC        # bash option

# 色分け用のエスケープ文字
ESC=$(printf '\033')
declare -l DSPCOLOR="none"

# 次の項目を作る関数
C() {
  if [ "$DSPCOLOR" = "none" ]; then
    echo -n ""
  else
    echo -n " "
  fi
  # > background color
  case "$1" in
    "red" )     echo -e -n "${ESC}[41m";;
    "green" )   echo -e -n "${ESC}[42m";;
    "yellow" )  echo -e -n "${ESC}[43m";;
    "blue" )    echo -e -n "${ESC}[44m";;
    "purple" )  echo -e -n "${ESC}[45m";;
    "cyan" )    echo -e -n "${ESC}[46m";;
    "gray" )    echo -e -n "${ESC}[47m";;
    * )         echo -e -n "${ESC}[49m";;
  esac
  if [ "$DSPCOLOR" = "none" ]; then
    echo -n " "
  fi
  # > color
  local isUsed=true
  case "$DSPCOLOR" in
    "red" )     echo -e -n "${ESC}[31m";;
    "green" )   echo -e -n "${ESC}[32m";;
    "yellow" )  echo -e -n "${ESC}[33m";;
    "blue" )    echo -e -n "${ESC}[34m";;
    "purple" )  echo -e -n "${ESC}[35m";;
    "cyan" )    echo -e -n "${ESC}[36m";;
    "gray" )    echo -e -n "${ESC}[37m";;
    * )         isUsed=false;;
  esac
  # >
  if "${isUsed}"; then
    echo -n " " # \ue0b0 nf-pl-left_hard_divider パンくずの肝
    echo -e -n "${ESC}[30m"
  fi
  # 文字色
  case "$1" in
    "red" )     echo -e -n "${ESC}[37m";;
    "green" )   echo -e -n "${ESC}[37m";;
    "yellow" )  echo -e -n "${ESC}[30m";;
    "blue" )    echo -e -n "${ESC}[37m";;
    "purple" )  echo -e -n "${ESC}[37m";;
    "cyan" )    echo -e -n "${ESC}[30m";;
    "gray" )    echo -e -n "${ESC}[30m";;
    * )         echo -e -n "${ESC}[0m";;
  esac
  DSPCOLOR="$1"
}

export VIRTUAL_ENV_DISABLE_PROMPT=1 # pythonのvenv仮想環境でPS1を書き換えさせない
nerdPS1() {
  local userName="$1"
  # if userName yourname, use short name
  # [TODO] Change YOUR-USER-NAME
  if [[ $userName == "YOUR-USER-NAME" ]]; then
    userName="?" # terminalによってはカラーフォント絵文字も使える。自分っぽいものに置き換えよう
  fi
  local hostName="$2"
  # if hostName ..
  # [TODO] Change YOUR-HOST-NAME
  if [[ $hostName == "YOUR-HOST-NAME" ]]; then
    hostName="" # \uf878 nf-mdi-monitor 一番ホストっぽかった
  fi
  local pwdInfo="$3"

  # chroot
  # とりあえず書き足しておいたけどvenvと関係なかった。ここのは動作検証してないので……
  if [[ -v debian_chroot ]]; then
    C "purple"
    echo -e -n "\uf306 $debian_chroot" #  nf-linux-debian 
  fi
  # (optional) python venv
  if [[ -v VIRTUAL_ENV ]]; then
    local PYTHON_VER="$(python -V)"
    local PYTHON_ENVNAME="$(basename $VIRTUAL_ENV)"
    C "cyan"
    # for remove uniquename (pipenv hoge-{uniquename})
    echo -e -n "\ue235 ${PYTHON_VER#Python } ${PYTHON_ENVNAME%-*}" #  nf-fae-python 一番見やすいPythonロゴ
  fi

   # host
  C "blue"
  echo -n "$userName@$hostName"
  # pwd
  C "gray"
  echo -e -n "\ue5ff $pwdInfo" #  nf-custom-folder フォルダアイコン

 # (optional) git
  # [TODO] `source git-prompt.sh` (you have to download or find)
  if git status --ignore-submodules &>/dev/null; then
    # You Use Git
    local gitps1="$(__git_ps1)"
    if [[ $gitps1 =~ [*+?%] ]]; then
      C "yellow"
    else
      C "green"
    fi
    echo -e -n "\ue725 $gitps1" #  nf-dev-git_branch 一番見やすかったGitぽいアイコン
  fi
  C "reset" # 忘れずに
}

# 右端に時刻を表示
# コピペ https://orebibou.com/ja/home/201810/20181002_001/
__command_rprompt() {
  local rprompt=$(date "+%Y/%m/%d %H:%M:%S")

  local num=$(($COLUMNS - ${#rprompt} - 2))
  printf "%${num}s$rprompt\\r" ''
}
set +e # これが無いと、プロンプトで実行したコマンドにエラーが有った時に動かなくなる

PS1='$(nerdPS1 \u \h \w)\n\$ '
PROMPT_COMMAND=__command_rprompt
.bashrc
# ...
if [ -e ~/nerdps1.sh ]; then
  source ~/nerdps1.sh
fi
source ~/.git-prompt.sh
GIT_PS1_SHOWDIRTYSTATE=true
GIT_PS1_SHOWUPSTREAM=true
GIT_PS1_SHOWUNTRACKEDFILES=true
GIT_PS1_SHOWSTASHSTATE=true
GIT_PS1_COMPRESSSPARSESTATE=true # 増えた
GIT_PS1_STATESEPARATOR=' '

# ...

説明

プロンプトの表示の変更

一般に見えているホスト名とか書いてある部分は環境変数PS1で指定されています。この内容は1行実行ごとに解釈して都度都度実行されます。
後述のC関数を1つのパンくずごとに実行させたかったのですが、これをそのままCに埋め込むと思うように動かない(同時に処理される?)ので、今回その内容について全て関数に投げています。

Nerdfonts

powerline用のフォントを始めとする様々なアイコンフォントが含まれたフォントです。外字領域を利用しているいて、Nerdfontsと他の一般的なfontを合成することもできるため、様々な派生があります。

パンくずの仕組み

そもそもあのパンくずは、Nerdfontsに含まれるnf-pl-left_hard_dividerという文字を、文字色をそれ以前の背景色にすることで表現しています。
今回DSPCOLORという変数で以前の色を管理しCという変数で、背景色の変更とnf-pl-left_hard_dividerの表示を行っています。
一元管理しているので、好きなようにパンくずを生やしたり消したりできます。
変数名の汚染は少し心配

色を変える仕組み

\e[{hoge}mの形で入力する(制御文字を使うためechoには-eオプションが必要)することで、{hoge}に応じた色変更が行えます。具体的な記入例は詳しい説明がいくらでも他の人の記事にあるので任せます。
\e\033は同じ意味ですが、後者のほうが互換性が良いみたいです

ユーザ名とホスト名のアイコン化

長ったるいのは嫌だけど非表示にするのも何かあった時アレだな、ということで、普段のものに一致していたら1字に書き換えるようにしておきます。

python venv実行中の表示

pythonにはバージョン管理を司るvenvという機能がありますが、デフォルトだと環境名をPS1に埋め込みやがります。埋め込むかどうかは環境変数VIRTUAL_ENV_DISABLE_PROMPTで指定できるし、仮想環境名はbasename $VIRTUALENVで取得できます。
pipenvを使う時、環境作成時のユニークな値がこれの後ろに付いてきてしまうので、ハイフン以後を削除する設定しています。

git の表示

色々細々した項目について場合分けするのが大変そうですが、公式のgit-prompt.shというのがいい感じに処理してくれるので、これを見つけてきて読み込みましょう。ブランチ名と状況を出力してくれる関数__git_ps1を含んでいます。
これの出力で変更の有る無しを判断しています。
他の記事ではあまり書いてませんでしたが、最近ではGIT_PS1_COMPRESSSPARSESTATEについても設定する必要があるようです。

ただ、WSLにおいて、通常のWindowsのファイルにこれを実行しようとすると明らかに時間がかかるようになってしまいます。__git_ps1の仕業です。いつか改善されることを期待して。

右端に時刻を表示

bashでPROMPT_COMMANDを利用して右プロンプトを出力させるという記事のものが今回文句なく動いてくれたのでこれをそのまま使用しました。
もうちょっと飾っても良いかもしれない。

やらかしたとき

これで遊んでると.bashrcにバグを埋め込んで起動できなくなることが有るので、気をつけましょう。
WSLの場合は、cmdなりPowerShellなりからWSL内部のbashをrcを読みこまずに実行することができます。
bash
wsl.exe -e bash --norc

.bashrcでsourceを埋め込む前にとりあえずテストして確認しましょう。(先達の知恵)

最後に

パンくず(そういう言い方で良いのか?)再現の記事は多いけど、このスクリプトでは割と楽に好きな色のパンくずを追加できるので、色々各々の状況に応じた形にカスタムしやすいと思います。作ってる内に互換性への意識がおざなりになっちゃったけど……

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む