通常、パソコンを操作する際はファイルをマウスでクリックして選択、ダブルクリックで対応するアプリケーションで開いて修正して保存、などとしていることだろう。また、スマホやタブレットは指でタッチして様々な操作を行うであろう。この際、アイコンやボタンなど、操作対象がグラフィカルに表現されたものを、マウスやタッチで操作するインタフェースを グラフィカルユーザインタフェース(Graphical User Interface, GUI) と呼ぶ。
一方、主にキーボードからコマンドを入力してコンピュータを操作する方法もある。こちらは命令(コマンド)を一行(ライン)ずつ受け付け、解釈して実行することから コマンドラインインタフェース(Command-line Interface, CLI) と呼ばれる。最初からGUIツールとして作られているWordやPowerPoint等と異なり、Gitはコマンドラインツールとして作られている。GitにはGit Guiや、SourceTreeなどのGUIツールも用意されているが、これはCLIにGUIの「皮」をかぶせたものだ。Gitを「ただ使う」だけであればGUIツールを使えばよいが、本講義の目的はGitを使うことではなく、Gitというバージョン管理ツールを理解することだ。また、GUIツールを使っていて何かトラブルが起きた場合、それがコマンドに起因するものなのか、GUIの「皮」に起因するものなのかを切り分けなければならず、そのためにはコマンドライン操作を理解していなければならない。そこで、まずはコマンドライン操作について学ぶ。
なお、コマンドライン操作において最も注目して欲しいのはエラーへの対応だ。GUIではそもそも「許されない操作」ができないように設計されていることが多いが、コマンドラインでは頻繫に「許されない操作」をしてしまい、「それはできないよ」というメッセージが表示されることだろう。これを エラーメッセージ(Error Message) と呼ぶ。エラーの多くは平易な英語で書いてあるので、ちゃんと読めば何が起きたか、そして次に何をすれば良いかがわかるはずだ。
映画などでハッカーが何やら黒い画面を見ながらキーボードをものすごい勢いで叩いているのを見たことがあるだろう。この「黒い画面」はターミナル1と呼ばれ、ユーザからの指示をコンピュータに入力するためのものだ。Gitはコマンドラインツールであるから、まずはコマンドラインの使い方に慣れなければならない。コマンドラインを入力するのはこのターミナルという黒い画面であるから、Gitを使うためにはこの「黒い画面」と友達にならなければならない。ターミナルへの命令はコマンドを通じて行われるが、このコマンドはオペレーティング・システムの種類によって異なる。GitはLinuxの開発のために作られた経緯があるため、Linux上で動作することを前提に作られた。LinuxはUnixを参考にして作られたため、Unixの直系の子孫ではないが、操作やコマンドが似ている。Unixは極めて古いOSであり、macOSもUnixの子孫である。Unixの子孫や、Unixと操作が似ているOSをまとめてUnix系システムと呼ぶ。Unix系システムでは、Unixコマンドと呼ばれるコマンド群を用いて操作する。以下では、Gitの操作に最低限必要なUnixコマンドについて説明する。ターミナルはWindowsのGit Bashを想定するが、WSL2のUbuntuやMacのターミナルでも同様である。
あまり意識していないだろうが、普段使っているパソコンやスマホ、タブレットには オペレーティングシステム (Operating System, OS) が搭載されている。Windows 10やmacOS、iOSやAndroidなどがOSだ。OSはハードウェアとソフトウェアの仲立ちをするのが役目だ。例えば、ストレージがハードディスクなのかSSDかによってその制御方法は全くことなるが、OSを介して見れば、どちらも同じファイルシステムに見える。そういう意味でOSはストレージを抽象化している。
さて、いまファイルを作りたいとしよう。OSがストレージを抽象化しているため、ユーザはOSに「ファイルを作ってください」と依頼する必要がある。この、ユーザとOSの仲立ちをするのが シェル(shell) と呼ばれるシステムだ。ユーザは、ターミナル(黒い画面)から、シェルに命令を入力する。するとシェルはそれをOSに届け、OSが実際に処理を行う、という階層構造になっている2。OSはハードウェアを抽象化し、さらにその周りを殻のように覆っていることからシェルという名前がついたようだ。
シェルには、グラフィカルなシェルと、コマンドラインシェルがある。Windowsなどではグラフィカルなシェルが用意されており、マウスでファイルの移動をすることができる。一方、ターミナル上でコマンドを入力することで命令を実行するのがコマンドラインシェルである。以下では、コマンドラインシェルのことを単に「シェル」と呼ぶことにする。
WindowsやMacでは、複数のファイルをまとめるものをフォルダと呼ぶが、Unix系システムでは ディレクトリ(directory) と呼ぶ。
この命令が実行されるディレクトリ、すなわち「いま自分がいるディレクトリ」を カレントディレクトリ(current directory)、もしくはワーキングディレクトリと呼ぶ。特に、ターミナルを開いた直後のカレントディレクトリを ホームディレクトリ(home directory) と呼ぶ。ディレクトリは一般にファイルと同様に名前がついているが、特別なディレクトリが二つある。一つは ピリオド一つ.
で表されるディレクトリで、これはカレントディレクトリを表す。もう一つはピリオド二つ ..
で表されるディレクトリで、これはカレントディレクトリの親ディレクトリを表す。これらは後述する相対パスで「カレントディレクトリの真下」以外の場所を指定するのに使う。
ディレクトリは階層構造をしているが、その一番上のディレクトリを ルートディレクトリ(root directory) と呼ぶ。これは、階層構造を木構造だと思った時に、「根(root)」にあたる部分であるからで、木の根が「上」に位置しているイメージなのに注意。
階層構造をしているディレクトリの、ファイルやディレクトリの位置を指定する文字列を パス(path) と呼ぶ。ルートディレクトリから目的のファイルまでの位置を完全に指定するパスを 絶対パス(absolute path)、カレントディレクトリからの相対的な位置を示すパスを 相対パス (relative path) と呼ぶ。
例えば上図において、カレントディレクトリがc/Data/Music
である時に、c/Data/Fig/fig1.png
をいまいる場所にコピーしたいとする。目的のファイルの絶対パスであるc/Data/Fig/fig1.png
を指定しても良いが、ディレクトリ階層が深くなると指定が面倒だ。その場合、相対パスとして../Fig/fig1.png
を指定することもできる。これは
../
:カレントディレクトリの一つ上にいってFig/
:その後Fig
というディレクトリに下ってfig1.png
:その下にあるfig1.png
を探せ
という指定の仕方となる。
これからコマンドラインでのファイル操作について説明をするが、慣れないと操作ミスによる事故が起きやすい。自信がない場合は使い慣れたグラフィカルなシェルで操作すると良いだろう。例えばWindowsのGit Bashであれば、
explorer .
を実行すると、カレントディレクトリをエクスプローラーで開くことができる(一つ空白をかえてピリオドを入力するのを忘れないこと)。
Macであれば
open .
によりカレントディレクトリをファインダーで開くことができる。あとはマウスでコピーや移動、削除などを実行すれば良い。
多くのシェルでは、ユーザからの入力を待っている時に$
が表示され、その隣でカーソルが点滅した状態となる。これをコマンドプロンプト、あるいは単にプロンプトと呼び、コマンドが入力可能であることを表している。このプロンプトにコマンドを入力し、エンターキーを押すとその命令が処理される。コマンドに何か値を渡したいことがある。例えばファイルを削除するコマンドはrm
だが、どのファイルを削除するか教えてやる必要がある。このように、コマンドに渡す値を 引数(ひきすう) と呼ぶ。一方、コマンドの動作を変えるような引数を オプション(option) と呼び、-
や--
で始まることが多い。
カレントディレクトリに存在するディレクトリやファイルを表示するコマンドがls
だ3。
$ ls
上記は、$
というコマンドプロンプトに、ls
という文字を入力し、エンターキーを押す、という意味だ。ユーザが入力するのはls
(+エンターキー)だけであり、$
は入力しないことに注意。
すると、例えば以下のような表示がされる。
$ ls
dir1/ dir2/ file1.txt file2.txt
これは、カレントディレクトリに、dir1
、dir2
というディレクトリと、file1.txt
、file2.txt
というファイルがあるよ、という意味だ。ディレクトリは名前の右側に/
がついていることが多いが、それはシェルの設定によるため、ついていない場合もある。
ls
に-l
というオプションを渡すと、結果をリスト表示する。
$ ls -l
total 0
drwxr-xr-x 1 watanabe 197121 0 8月 17 21:03 dir1/
drwxr-xr-x 1 watanabe 197121 0 8月 17 20:32 dir2/
-rw-r--r-- 1 watanabe 197121 0 8月 17 20:33 file1.txt
-rw-r--r-- 1 watanabe 197121 0 8月 17 20:33 file2.txt
リスト表示では、ファイル名の他に、そのファイルの読み書きの許可、所有者、日付などが表示される。このように、「コマンドの直接の目的語」が引数、「コマンドの振る舞いを変える」のがオプションである。
引数としてカレントディレクトリの下にあるディレクトリ(サブディレクトリと言う)を指定することで、そのディレクトリの中身を表示することもできる。
$ ls dir1
file3.txt
存在しないファイルやディレクトリを指定すると、そんなファイルは知らないよ、というエラーが出る。
$ ls non-existing-dir
ls: cannot access 'non-existing-dir': No such file or directory
頭に.
がついているファイルやディレクトリは隠しファイル、隠しディレクトリとなり、デフォルトでは表示されない。それを表示するにはls -a
オプションを使う。
$ ls -a
./ ../ dir1/ dir2/ file1.txt file2.txt
新たに表示された.
と..
は、それぞれカレントディレクトリと親ディレクトリの別名だ。どちらも良く使うので覚えておきたい。
カレントディレクトリを変更するコマンドがcd
だ4。cd
の後にディレクトリ名を指定すると、カレントディレクトリがそこに移動する。ダブルクリックでフォルダを開いた時には、そのフォルダの中身が自動的に表示された。しかし、コマンドラインインタフェースではそんな親切なことはしてくれない。カレントディレクトリをそのディレクトリに変更しておしまいである。中身を表示したければcd
した後にls
を実行しよう。
$ cd dir1
$ ls
file3.txt
存在しないディレクトリに移動しようとしたら、エラーが出る。
$ cd non-exisiting-dir
bash: cd: non-exisiting-dir: No such file or directory
これは「non-exisiting-dir
というディレクトリにcd
しようとしたけど、そんなファイルもディレクトリも無いよ」というエラーだ。ファイルに対してcd
しようとしてもエラーとなる。
$ cd file1.txt
bash: cd: file1.txt: Not a directory
これは「file1.txt
はディレクトリではないのでcd
できないよ」というエラーだ。
cd
コマンドを引数無しで実行すると、ホームディレクトリに戻る。重要なコマンドなので覚えておこう。
..
は親ディレクトリを表すため、cd ..
を実行するとカレントディレクトリが一つ上に移動する。
$ ls
dir1/ dir2/ file1.txt file2.txt
$ cd dir1 # dir1に移る
$ ls
file3.txt
$ cd .. #一つ上に戻る
$ ls
dir1/ dir2/ file1.txt file2.txt
ディレクトリを作るにはmkdir
を使う5。引数にディレクトリ名を指定すると、カレントディレクトリにその名前のディレクトリを作る。
$ ls
dir1/ dir2/ file1.txt file2.txt
$ mkdir dir3 # dir3を作成
$ ls
dir1/ dir2/ dir3/ file1.txt file2.txt # dir3/が増えた
ファイルの移動や、ファイル名の変更にはmv
を使う6。グラフィカルなシェルではファイルの移動はマウスでドラッグするだけだが、コマンドラインでは「移動させたいファイル」「移動先」の二つの情報が必要だ。mv
コマンドは、移動先がファイルかディレクトリか、移動先のファイルやディレクトリが存在するかしないかによって動作が異なるので注意したい。
まず、一番使う頻度が高いと思われるのが、移動元がファイル、移動先がディレクトリの場合だ。ファイルがそのディレクトリに移動する。カレントディレクトリを表す.
や、一つ上のディレクトリを表す..
をよく使うので覚えておきたい。
mv file1.txt dir1 # test.txtが、サブディレクトリのdir1に移動する
mv ../file1.txt . # 一つ上にディレクトリにあるtest.txtを、カレントディレクトリに移動する
ここでは全て相対パスで指定していることに注意。絶対パスでも指定できるが、少なくとも本講義では必要ないはず。
引数がどちらもファイル名、より正確には「移動先」として指定した名前のファイルやディレクトリが存在しない場合、「移動元」のファイル名をその「移動先」のファイル名にリネームする。
mv file2.txt file3.txt # カレントディレクトリにあるfile2.txtをfile3.txtに変更
移動元がディレクトリであり、移動先がディレクトリである場合、移動元のディレクトリを移動先のディレクトリの下に移動する。
mv dir1 dir2 # カレントディレクトリにあるdir1を、やはりカレントディレクトリにあるdir2に移動する
mv ../dir1 . # 上のディレクトリにあるdir1を、カレントディレクトリに移動する
mv dir1 .. # カレントディレクトリにあるdir1を、一つ上のディレクトリに移動する
移動元がディレクトリであり、移動先に存在しない名前を指定した場合、ディレクトリをその場所に移動した上で、その名前にリネームする。なお、移動元のディレクトリの下にファイルやディレクトリがあった場合は、まとめて移動する。
mv dir1 dir2 # dir2が存在しない場合、dir1をdir2にリネームする
mv dir1 dir2/dir3 # dir2は存在するがdir2/dir3は存在しない場合、dir1をdir2の下に移動した上でdir3にリネームする
以上、まとめるとmv
は移動先が存在するディレクトリであればそこに移動、移動先が存在しないファイルやディレクトリであれば、移動元のファイルやディレクトリを移動した上でリネームする。
mv
ではファイルを移動したが、元のファイルを残したまま複製したい場合はcp
を使う。移動元のファイルが消えない以外はほとんどmv
と同じだ。
cp test1.txt test2.txt # test1.txtをtest2.txtという名前で複製する
cp test1.txt dir # test1.txtをdir1/test1.txtという名前で複製する
cp test1.txt test2.txt dir # 複数のファイルを一度にdirにコピーする
しかし、ディレクトリをコピーする場合は-r
オプションが必要だ。
$ cp dir1 dir2 # コピー元がディレクトリである場合はエラーとなる
cp: -r not specified; omitting directory 'dir1'
$ cp -r dir1 dir2 # dir2が無い場合はその場所にコピー、ある場合はdir2の下にコピー
ファイルを削除するにはrm
を使う。なお、中身が空ではないいディレクトリを削除する場合はrm -r dir
と-r
オプションを付ける必要があるが、再帰的に全てのファイル、サブディレクトリが削除されてしまうので、慣れないうちは実行しない方がよい。また、空のディレクトリを削除するためにrmdir
というコマンドがあるが、普段使うことは少ないので「mkdir
の対になるコマンドがrmdir
」とだけ覚えておけばよいだろう。
ファイルの中身を表示するにはcat
を使う。これはconcatenate
(連結する)の略で、複数のファイルを指定することで結合ができるが、ここではファイル内容の表示にしか使わない。
cat filename
でfilename
で指定されたファイルの中身をターミナルに表示する。
Gitを使っていると、コミットやマージのメッセージ入力や、rebase
の処理などで、コマンドライン上でエディタを使う必要が出てくる場合がある。多くの環境でGitのデフォルトエディタはVimだ。Vimは非常に強力なエディタであり、愛好者も多い(筆者もその一人である)のだが、普段使うエディタと使い勝手が異なるので戸惑うことも多いだろう。
ここでは、Vimの最低限の使い方だけ説明をする。
まずはコマンドラインでvim
を実行すると、例えばこんな画面が出てくる。
$ vim
Vimは「普通」のエディタと異なり、「モード」がある。起動直後は ノーマルモード(Normal Mode) となっている。ノーマルモードでは、Vimの終了や、カーソルの移動、行の削除や貼り付けなどの操作ができる。
ここで、キーボードのi
を押すと、 入力モード(Insert Mode) になる。すると、画面の一番下に「-- 挿入 --」と表示される。なお、ターミナルの言語が英語だと-- INSERT --
と表示されるなど、メッセージが英語になるが、ここでは日本語を前提に説明をする。この状態でキーを入力すると、入力したキーがそのまま挿入される。カーソルキー、エンターキーによる改行、バックスペースキーやデリートキーによる削除も可能なので、この状態で文章を編集する。
ファイルを保存したり、エディタを終了するためには「入力モード」から「ノーマルモード」に戻らなくてはならない。ノーマルモードに戻るにはエスケープキーを押す。すると画面下の「-- 挿入 --」が消える。
Vimを終了するには二つの方法がある。一つは「ノーマルモード」でZZ
(シフトを押しながらZ
を二回入力)する方法だ。これは「保存して終了」という動作をする。しかし、何か編集をしており、ファイル名が未指定の場合には「E32: ファイル名がありません」と言われて終了ができない。
ファイル名を指定するには、コマンドモード (Command Mode) に入る必要がある。コマンドモードに入るためには、:
(コロン)を入力する。すると、画面下に:
が現れ、そこにカーソルが移る。
ここでw
の後に空白をあけてファイル名を指定し、エンターキーを押すと、現在編集中の内容をそのファイル名で名前をつけて保存できる。
:w test.txt
上記を実行すると、「無名」とあったところにファイル名が表示される。
この状態になったら、ZZ
の入力で終了できる。
また、コマンドモードで、q!
と入力すると、現在編集中の内容を破棄してそのまま終了できる。
慣れない人は、Vimの終了方法に戸惑いがちだ。もう一度終了方法をまとめておこう。
- もし入力モードであったら、エスケープキーを押してノーマルモードに移る。どのモードからでもエスケープキーを押せばノーマルモードになるので、現在のモードがわからなくてもとりあえずエスケープキーを押せばよい。
- 現在編集中の内容がファイル名を持っていて、編集内容を保存して終了したければそのまま
ZZ
(シフトキーを押しながらZ
を二回)を入力する。 - 現在編集中の内容を破棄して終了したければ、
:
を押してコマンドモードとなり、w!
を入力してエンターキーを押す(ノーマルモードで:q!
と順番に入力してエンターキーを押せば良い)。
GitからVimが呼び出される場合は必ず名前があるファイルを渡されるはずなので、
i
を押してインサートモードにする- 内容を編集する
- エスケープキーを押してノーマルモードに戻る
ZZ
でエディタを抜ける
という一連の操作を行えばよく、とりあえずコマンドモードを覚える必要ない。また、Vimにはもう一つ ビジュアルモード(Visual Mode) というモードがあり、こちらも使えると便利なのだが、ここでは触れない。