[PR] 本記事にはプロモーションが含まれています。

【番外編】組み込みエンジニアが Git でつまずいた6つのこと

TypeScript

はじめに

このシリーズの Step 0・1 では、ツールのソースと文書を Git で管理してきました。第8回の振り返りでも触れたとおり、プログラムそのものより、Git などの道具でつまずくことが多くありました。

この番外編では、実際に出会った6つのつまずきを、そのときの表示と、確かめ方、直し方のセットでまとめます。どれも、気づけば数分で直せるものでしたが、最初に見たときは何が起きているのかわかりませんでした。同じところでつまずいた方の参考になればうれしいです。

作業は、自宅サーバー(Linux)に VS Code の Remote-SSH でつなぎ、VS Code のターミナルで行っています。リポジトリはツールごとに作り、GitHub の非公開リポジトリに送っています。

Git の基本の流れ

つまずきの話の前に、Git で変更を記録して送るまでの流れを確認しておきます。この流れのどこで何が起きたかがわかると、つまずきの原因も見えやすくなります。

エディタで保存し、git add でステージに登録し、git commit で手元のリポジトリに記録し、git push で GitHub に送る流れと、記録に git tag で目印を付けて送る流れ、それぞれの組み込みでの対応を示した図
操作何をするか組み込みでいうと
保存(Ctrl + S)エディタの内容をディスク上のファイルに書き出すエディタのバッファをディスクに書き出す
git add次の記録に入れるファイルを選ぶ(ステージに登録する)出荷候補のリストに載せる
git commit選んだ変更を、説明文を付けて手元のリポジトリに記録する手元で版を確定する
git push手元の記録を GitHub に送る構成管理サーバーに登録する
git tag特定の記録に名前の目印を付ける出荷した版にラベルを付ける

状態を確かめるコマンドも、よく使うものは3つだけです。

コマンドわかること
git status変更されたファイル、まだ記録していないファイルの一覧
git log --oneline記録の履歴を1行ずつ。GitHub に送り済みの位置には origin/main と表示される
git diff記録した状態から、どこが変わったか

以下のつまずきは、ほとんどがこの表の「保存」「git add」「git push」のどこかで起きていました。

つまずき1:初期のブランチ名が master だった

何が起きたか

新しいツールのフォルダで git init を実行したところ、長いヒントが表示されました(一部を省略しています)。

Plaintext
$ git init
hint: Using 'master' as the name for the initial branch. This default branch name
hint: will change to "main" in Git 3.0. To configure the initial branch name
hint: to use in all of your new repositories, which will suppress this warning,
hint: call:
hint:
hint:   git config --global init.defaultBranch <name>
…
Initialized empty Git repository in /home/devuser/apps/unitconv/.git/

一見エラーのようですが、最後の行の Initialized empty Git repository が成功の印で、リポジトリは正しく作られていました。

原因

Git では、作業の流れをブランチ(枝)と呼びます。最初に作られるブランチの名前が、今のバージョンでは master ですが、将来は main に変わる予定なので、「どちらにするか決めておいてください」と知らせてくれていたのです。GitHub も、新しいリポジトリでは main を標準にしています。

直し方

まだ何も記録していない状態なら、名前の変更は簡単です。今後のリポジトリの初期値も main にしておきます。

Bash
$ git config --global init.defaultBranch main   # 今後の git init は main で始まる
$ git branch -m main                            # このリポジトリのブランチ名を main に変更
$ git status

git status の1行目が On branch main になれば完了です。--global は、このユーザーのすべてのリポジトリに効く設定という意味です。組み込みでいえば、プロジェクト共通のビルド設定にあたります。

つまずき2:日本語のファイル名が数字の列になった

何が起きたか

設計書(docs/02_設計.md)を作って git status を実行すると、ファイル名が次のように表示されました。

Plaintext
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        "docs/02_\350\250\255\350\250\210.md"

原因

ファイルが壊れたわけではありません。Git は初期設定では、ASCII 以外の文字(日本語など)を、8進数のバイト値で表示します。\350\250\255 が「設」の UTF-8 の3バイト、\350\250\210 が「計」の3バイトです。組み込みでいえば、文字を表示する代わりに、メモリダンプでバイト列を見せている状態です。

直し方

次の設定で、日本語のまま表示されるようになります。

Bash
$ git config --global core.quotepath false
$ git status
Plaintext
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        docs/02_設計.md

ファイル名に日本語を使う予定があるなら、最初に設定しておくのがおすすめです。

つまずき3:空のファイルをコミットしていた

何が起きたか

設計書をコミットした後、別の作業でコミットしようとしたところ、触っていないはずの設計書が「変更あり」になっていました。そこで、その2つのコミットの間で、設計書がどう変わったかを調べました。

Bash
$ git diff 8b7c94b 2ff362c -- docs/02_設計.md

git diff <前のコミット> <後のコミット> -- <ファイル> で、2つの記録の間の差分を、ファイルを指定して表示できます。コミットの番号は、git log --oneline の左端に出ている英数字です。

Plaintext
diff --git a/docs/02_設計.md b/docs/02_設計.md
index e69de29..3a4dfd7 100644
--- a/docs/02_設計.md
+++ b/docs/02_設計.md
@@ -0,0 +1,82 @@
+# unitconv 設計書
…

原因

index e69de29..3a4dfd7 の e69de29 は、Git で「空のファイル」を表す決まった番号です。つまり、最初のコミットの時点では、設計書は空のまま記録されていたことになります。@@ -0,0 +1,82 @@ も、「0行だったものが82行になった」という意味です。

VS Code でファイルを作り、内容を貼り付けたものの、保存する前にコミットしていたのが原因でした。組み込みでいえば、エディタのバッファをディスクに書き出し忘れたまま、ビルドしてしまったような状況です。

確かめ方

同じことがほかのファイルで起きていないかは、記録されたファイルの大きさで確かめられます。

Bash
$ git ls-tree -l HEAD docs/
Plaintext
100644 blob 9f0dece038e62cea9813c95506746dbd9962f131    1963    docs/01_要件定義.md
100644 blob 3a4dfd7485df76c232242557270a38e628cd8b9a    3655    docs/02_設計.md

4列目の数字がファイルの大きさ(バイト数)です。0 のファイルがあれば、空のまま記録されています。

防ぎ方

今回は、後のコミットで中身が入ったので実害はありませんでした。それからは、コミットの前に次の2つを見る習慣にしています。

  • VS Code のタブに、未保存の印(●)が付いていないか
  • git status で、今回のコミットに入れるファイルの一覧が想定どおりか
組み込みエンジニアが Git でつまずいた6つのこと:防ぎ方

なお、git diff の結果が長いと、画面の最後に : と出て止まります。これは1画面ずつ表示する「ページ送り」の待ち状態で、スペースキーで次へ、q キーで終了できます。

つまずき4:コミットし忘れて、別の変更と混ざりかけた

何が起きたか

画面の見た目(CSS)を整えて、index.html だけを変更したつもりでコミットしようとしたところ、git status に、触っていないはずの main.ts も出ていました。

Plaintext
$ git status
Changes not staged for commit:
        modified:   index.html
        modified:   src/main.ts

原因を調べる

最後のコミットと、main.ts の変更内容を確かめました。

Bash
$ git log --oneline -3
$ git diff src/main.ts
Plaintext
b95a8ff (HEAD -> main, origin/main) 入力に応じて換算結果を表示
f03b4cf 換算テーブルと計算関数を追加
0b44c7e 画面の部品を配置

最新のコミットが「入力に応じて換算結果を表示」のままでした。その次の作業(入力チェックの追加)で、動作確認が終わった後に、コミットを忘れていたのです。動作確認の後に質問のやり取りが続き、コミットの手順が埋もれてしまったのが原因でした。git diff には、入力チェックの追加分がそのまま出ていました。

直し方:ファイルを指定して、目的ごとにコミットする

git add . でまとめて登録すると、2つの作業が1つのコミットに混ざってしまいます。そこで、ファイルを指定して、目的ごとに分けて記録しました。

Bash
$ git add src/main.ts
$ git commit -m "入力チェックを追加"
$ git add index.html
$ git commit -m "見た目を整えスマホに対応"
$ git push
Plaintext
$ git log --oneline -3
1ac5cad (HEAD -> main, origin/main) 見た目を整えスマホに対応
4a03460 入力チェックを追加
b95a8ff 入力に応じて換算結果を表示

1つのコミットに1つの目的、という形にしておくと、後から「いつ何を変えたか」を追いやすくなります。組み込みで、変更票を1件ずつ切るのと同じ考え方です。

防ぎ方

動作確認が OK になったら、次の話題に移る前にコミットする。これを習慣にしました。質問したくなったときも、先にコミットしておけば、どこまで終わったかが履歴に残ります。

逆に、先にコミットしてあったのに、もう一度 git commit を実行すると、次のように表示されます。

Plaintext
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

「記録すべき変更は何もない」という意味で、害はありません。git log --oneline -1 で、目的のコミットがすでにあるかを確かめれば十分です。

つまずき5:リポジトリ名を後から直す

何が起きたか

GitHub のリポジトリ名は、ツールが増えても見分けやすいように「apps-<ツール名>」という規則にしていました。ところが、push のときの表示を見ると、送り先が規則と違う名前になっていました。

Plaintext
To https://github.com/example-user/unitconv.git
   8b7c94b..2ff362c  main -> main

正しくは apps-unitconv です。リポジトリを作るときに、規則を確かめ忘れていました。

直し方

名前の変更は、GitHub 側と手元の両方で行います。

① GitHub でリポジトリ名を変える(ブラウザで操作)

  1. GitHub でリポジトリを開き、「Settings」タブを開く
  2. 「Repository name」を新しい名前に変える
  3. 「〜 is available.」と表示されたら、「Rename」を押す

② 手元の送り先を書き換える

Bash
$ git remote set-url origin https://github.com/example-user/apps-unitconv.git
$ git remote -v
$ git push
Plaintext
origin  https://github.com/example-user/apps-unitconv.git (fetch)
origin  https://github.com/example-user/apps-unitconv.git (push)
Everything up-to-date

git remote は、送り先(リモート)の設定を扱うコマンドです。origin は、送り先に付けた呼び名です。Everything up-to-date は「送るべき新しい記録はない」という意味で、新しい名前の送り先に正しくつながったことの確認になります。

GitHub は、名前を変えると古い名前からも自動で転送してくれます。それでも、送り先は正式な名前に直しておくほうが、後で混乱しません。

防ぎ方

リポジトリ名に限らず、名前の規則は、ものが少ないうちにそろえるのが楽です。名前の規則は「決定事項」の文書に書いてあるので、新しいツールを始めるときは、そこを確かめてからリポジトリを作るのが確実です。

つまずき6:push で認証エラーになった

何が起きたか

それまで問題なく送れていた git push が、突然次のようなエラーになりました。

Plaintext
$ git push
Missing or invalid credentials.
Error: connect ECONNREFUSED /run/user/1000/vscode-git-c10dfc6893.sock
  …
remote: Repository not found.
fatal: Authentication failed for 'https://github.com/example-user/apps-unitconv.git/'

少し前にリポジトリ名を変えていたので、それが原因のようにも見えます。

原因

このシリーズでは、サーバーに鍵を置かず、GitHub の認証を VS Code に任せています。git が push するときは、VS Code が用意した「認証の窓口」(vscode-git-….sock というソケット)に「パスワードをください」と問い合わせます。

エラーの ECONNREFUSED は、その窓口につながらなかったという意味です。VS Code が再接続や再読み込みをして窓口が作り直されたのに、開きっぱなしのターミナルが、古い窓口の住所を覚えたままになっていたのが原因でした。組み込みでいえば、デバイスをリセットした後も、古いハンドルを握ったままアクセスしようとしている状態です。

Repository not found(リポジトリが見つからない)も、名前の変更とは関係ありません。GitHub は、認証できない相手には、非公開リポジトリの存在そのものを隠すため、「見つからない」と返すのです。

直し方

まず、記録自体は手元に残っていることを確かめます。

Bash
$ git log --oneline -1
Plaintext
f03b4cf (HEAD -> main) 換算テーブルと計算関数を追加

origin/main が付いていないのは、「手元にはあるが、まだ GitHub には届いていない」という意味です。記録は消えていないので、慌てる必要はありません。

次に、VS Code のターミナルを閉じて(ターミナルパネル右上のごみ箱アイコン)、Ctrl + Shift + ` で新しいターミナルを開き、もう一度 push します。新しいターミナルは、VS Code の今の窓口の住所を受け取ります。

Bash
$ cd ~/apps/unitconv
$ git push
$ git log --oneline -1
Plaintext
To https://github.com/example-user/apps-unitconv.git
   0b44c7e..f03b4cf  main -> main
f03b4cf (HEAD -> main, origin/main) 換算テーブルと計算関数を追加

origin/main が付けば、GitHub まで届いています。それでも失敗する場合は、VS Code で F1 を押して「Developer: Reload Window」を実行してから、ターミナルを開き直すと直ることがあります。

おまけ:タグの種類をそろえる

つまずきではありませんが、Step の区切りに付けたタグ(目印)にも、知っておくとよいことがありました。

タグは2種類ある

種類中身作り方
軽量タグ記録への目印だけgit tag 名前
注釈付きタグ目印に加えて、作成者・日時・説明文を持つgit tag -a 名前 -m "説明"

Step 1 のタグを付けるとき、Step 0 で付けたタグと種類をそろえたかったので、まず Step 0 のタグを調べました。

Bash
$ git -C ~/apps/hello cat-file -t step0-done
Plaintext
commit

git -C <フォルダ> は、そのフォルダに移動せずに git を実行する指定、cat-file -t は、指定したものの種類を表示する指定です。結果が commit なら軽量タグ(記録を直接指している)、tag なら注釈付きタグです。組み込みでいえば、メモリダンプの前に、そのアドレスにあるのがコードかデータかを確かめるようなものです。

タグを付けて送る

Step 0 が軽量タグだったので、Step 1 もそろえました。タグは、通常の git push では送られないので、名前を指定して送ります。

Bash
$ git tag step1-done
$ git push origin step1-done
$ git log --oneline -1
Plaintext
31a0802 (HEAD -> main, tag: step1-done, origin/main) 振り返りを記録

tag: step1-done が付けば完了です。出荷したファームに版番号のラベルを付けておくのと同じで、いつでもその時点の状態を取り出せます。

まとめ:コミットの前のチェックリスト

6つのつまずきから、コミットと push の前に確かめることを、チェックリストにまとめました。

タイミング確かめること防げるつまずき
最初に1回init.defaultBranch main と core.quotepath false を設定した1、2
リポジトリを作るとき名前が決めた規則どおりか5
コミットの前VS Code のタブに未保存の印(●)がない3
コミットの前git status の一覧が、今回の作業のファイルだけ3、4
コミットの前違う目的の変更が混ざっていれば、ファイルを指定して分けて git add4
動作確認の後次の話題に移る前にコミットした4
push の後git log --oneline -1 に origin/main が付いた6
push が失敗したらターミナルを開き直す(記録は手元に残っている)6

振り返ると、つまずきの多くは「確かめずに次へ進んだ」ことが原因でした。組み込みで、書き込む前にベリファイし、リリースの前に差分を確認してきたのと同じで、Git でも、1つの操作ごとに git status や git log で状態を確かめる習慣が、いちばんの予防策だと感じています。

この番外編は、第8回の振り返りの補足です。シリーズは、次の Step 2(計算 API)に進みます。

この記事は、シリーズ「組み込みエンジニアの Web 開発入門」の1本です。ほかの回は、シリーズの目次からどうぞ。

ほかの開発・プログラミングの記事は、開発・プログラミングメモの目次から、テーマ別に探せます。

このブログは、にほんブログ村と人気ブログランキングに参加しています。記事が役に立ったら、下のランキングのバナーのどれかを押して応援してもらえると励みになります。

ブログランキング・にほんブログ村へにほんブログ村

サーバー構築・運用ランキング プログラムランキング

TypeScript
watasoftdevをフォローする

コメント