本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。今回はGitのローカルリポジトリの内容をリモートリポジトリに送信する「git push」コマンドです。リジェクトやコンフリクトが起きたときどのように解消するのかを解説します。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。今回はGitのローカルリポジトリの内容をリモートリポジトリに送信する「git push」コマンドです。前回の基礎編に続き、今回は応用編として、リジェクトやコンフリクトが起きたときどのように解消するのかを解説します。
「git」は「Git」という分散型バージョン管理システム用のコマンドです。Gitは元々Linuxカーネルのソースコードを管理するために作られた「バージョン管理システム」で、現在は多くのソフトウェアやWebサイトのソースコード、ドキュメントの管理などに用いられています。
ソースコードを管理する際、最新版だけを保存するやり方はうまくいきません。開発中のさまざまなタイミングで状態を管理し、必要に応じて比較、参照したり、元に戻したりできるようにする仕組みが「バージョン管理システム」です。
Gitでは、テスト版など複数に枝分かれした状態も管理できます。複数のメンバーによる開発を前提としており、開発中の各時点におけるコメントや、コメントへの返信なども管理できるようになっています。
gitコマンドはほとんどの場合、「サブコマンド」と組み合わせて利用します(本連載ではgitとサブコマンドの組み合わせをコマンドとして紹介します)。
今回紹介する「git push」はローカルリポジトリの内容をリモートリポジトリに送信(アップロード)するコマンドです。
gitコマンドでは「リポジトリ(repository)」を使ってバージョンを管理します。リポジトリにはソースコードや変更履歴、コメントなどを一括して保管します。リポジトリには、自分のPC上に作る「ローカルリポジトリ」と、「GitHub」などのWebサービス上に作る「リモートリポジトリ」があり、両者を連携させることで複数の開発者による開発を1本にまとめることができます。
また、ローカルリポジトリのみで運用することも可能です。そのような運用をしているリポジトリにリモートリポジトリを追加したり、逆に、リモートリポジトリを削除したりするには、「git remote」コマンドを使用します。「git remote」コマンドには、さらに「add」や「remove」などのサブコマンドがあります。
既存のリポジトリ(リモートリポジトリ)にあるソースコードなどを入手したい場合は、まず、「git clone」(連載第381回)でリポジトリを自分の環境に複製します(※1)。リモートリポジトリの内容がバージョンアップされたら「git pull」(連載第382回)で最新版を取得します。開発に参加するのではなく、単に最新版を取得したいという場合は、「git clone」と「git pull」を利用すればよいでしょう。
※1 特定のファイルだけが欲しい場合、例えばGitHub(github.com)にあるリポジトリであれば「Raw」というボタンで表示されるURLを使い、「wget」コマンドなどを使ってダウンロードできる。この他、プロジェクト全体をダウンロードするためのリンクも用意されている([Clone or download]ボタン→「Download ZIP」)。
保管場所であるリポジトリに対し、ファイルの編集などを行う場所を「ワークツリー」「ワーキングエリア」「作業ツリー」などと呼びます。「git clone」や「git pull」で取得した最新版のファイルはワークツリーに配置されます。つまり「作業ディレクトリ」です。
ワークツリーで編集した結果をリポジトリに反映する操作を「コミット」と呼びます。「git add」(連載第384回)コマンドでコミットしたいファイルを「インデックス」あるいは「ステージングエリア」と呼ばれる領域に追加します。インデックスにはファイルの変更箇所などが記録されます。
インデックスの内容は「git commit」コマンドでローカルリポジトリにコミットされ、「git push」コマンドでローカルリポジトリの内容をリモートリポジトリに反映します。従って、「git add」や「git commit」などを行わなければ、自分の環境で編集した内容がリポジトリに影響を与えることはありません。自由に編集し、テストできます。なお、ワークツリーのファイルを過去の任意のコミット状態に戻すことも可能です。
Gitには、この他、開発中のソースコードやドキュメントを、「テスト版」「○○版」……のように枝分かれさせたり、それらを合流させたりする機能もあります。枝分かれしたそれぞれのバージョンを「ブランチ」(branch)と呼び、ブランチを合流させることを「マージ」(merge)と呼びます。
コミットには「タグ」と呼ばれる名前を付けることができます。その際には「git tag」コマンドを使います。
git [オプション] サブコマンド [サブコマンドごとのオプションや引数]
git push [オプション] リモート名
git push add [オプション] リモート名 ブランチ名
git push [オプション] リモート名 ブランチ:リモートのブランチ
git remote サブコマンド 対象
※ [ ]は省略可能な引数を示しています。
短いオプション | 長いオプション | 意味 |
---|---|---|
-C パス | カレントディレクトリではなく指定したディレクトリで実行したものとする | |
--bare | リポジトリを「bareリポジトリ」(ワーキングツリーが存在しない、管理だけを目的としたリポジトリ)として扱う | |
-c 設定=値 | 設定値を指定する(設定は「git config」で確認可能) | |
-p | --paginate | 全ての出力を「less」コマンドまたは環境変数PAGERで指定されたコマンドで表示する |
-P | --no-pager | 「less」コマンドで表示しない(「-p」の指定を打ち消す) |
--exec-path=パス | gitの実行ファイルのパスを指定する(「--exec-path」のみの場合、実行ファイルのパスを表示する) | |
--html-path | gitのHTML形式のドキュメントがインストールされたパスを表示する | |
--man-path | gitのmanファイルのパスを表示する | |
--info-path | gitのinfoファイルのパスを表示する | |
コマンド | 実行内容 |
---|---|
clone | リポジトリのクローンを作成する |
init | リポジトリを新規作成する、または既存のリポジトリを初期化する |
remote | リモートリポジトリを関連付けする |
fetch | リモートリポジトリの内容を取得する |
pull | リモートリポジトリの内容を取得し、現在のブランチに取り込む(「fetch」と「merge」を行う) |
push | ローカルリポジトリの変更内容をリモートリポジトリに送信する |
add | ファイルをインデックスに追加する(コミットの対象にする) |
rm | ファイルをインデックスから削除する |
mv | ファイルやディレクトリの名前を変更する |
reset | ファイルをインデックスから削除し、特定のコミットの状態まで戻す |
status | ワークツリーにあるファイルの状態を表示する |
show | ファイルの内容やコミットの差分などを表示する |
diff | コミット同士やコミットとワークツリーの内容を比較する |
commit | インデックスに追加した変更をリポジトリに記録する |
tag | コミットにタグを付ける、削除する、一覧表示する |
log | コミット時のログを表示する |
grep | リポジトリで管理されているファイルをパターン検索する |
branch | ブランチを作成、削除、一覧表示する |
checkout | ワークツリーを異なるブランチに切り替える |
merge | 他のブランチやコミットの内容を現在のブランチに取り込む |
rebase | コミットを再適用する(ブランチの分岐点を変更したり、コミットの順番を入れ替えたりできる) |
config | 現在の設定を取得、変更する |
短いオプション | 長いオプション | 意味 |
---|---|---|
--all | 全てのブランチを対象とする | |
--prune | ローカルで削除したブランチをリモートからも削除する | |
-u | --set-upstream | 今回対象とするブランチを上流ブランチとして設定する(一度指定すると次回以降も対象となる) |
--tags | 全てのタグを対象とする | |
--mirror | ローカルの内容をリモートにそのまま複製する | |
-d | --delete | 指定したブランチをリモートから削除する(「:」のみを指定すると全て削除する) |
--atomic | 実行途中でブランチの変更に失敗したら、実行前の状態に戻す(サーバが対応している場合のみ) | |
-f | --force | リモートブランチがローカルブランチの派生元ではない場合も、ローカルブランチの内容で強制的に上書きする |
--signed=設定 | GPG(GNU Privacy Guard)署名付きで実行するかどうかを、「true」(常に署名付き)、「false」(常に署名なし)、「if-asked」(サーバ側が署名付きに対応していた場合だけ署名付き)で指定する | |
--signed | GPG署名を付けて実行する(「--signed=true」相当) | |
--no-signed | GPG署名を付けないで実行する(「--signed=false」相当) | |
-v | --verbose | 実行時のメッセージを増やす |
-q | --quite | 実行時のメッセージを減らす |
-n | --dry-run | 実行せずに実行する内容だけを表示する |
複数人がそれぞれのローカルリポジトリでコミット後、「git push」を実行した場合、受け付けられないことがあります。
このようなことが起きる状況は大きく分けて2つあります。異なるファイルに変更を加えた場合(リジェクトされる)と同一のファイルに変更を加えた場合(リジェクトに加えてコンフリクトが生じる)です。
最初にリジェクトだけが起きた場合の対応を2種類説明します。「いったん『git pull』(git fetchとgit merge)を実行してから再度『git push』を試みる」という方法と「『git rebase』を実行してから再度『git push』を試みる」という方法です。いずれもマージを実行して問題を解決します。
チームで開発している場合、リジェクト後の対処などに一定のルールが設けられていることがあります。まずはそちらを確認するようにしてください。
リジェクトが起きた例を画面1に示しました。
最初に「git log --oneline」と「git status」で状況を確認しています。最後のコミットがまだリモートリポジトリに反映されていない様子が分かります。
ここでリモートリポジトリに反映するため「git push」を実行すると、「! [rejected]」(リジェクトされた)というメッセージが表示されて、git pushが失敗しました。
複数人がそれぞれ異なるファイルを編集していた場合など、現在のリモートリポジトリにローカルの変更をそのまま反映できる場合は、まず、「git pull」を実行するか、「git fetch」と「git merge」を順に実行します(※2)。その後、再度「git push」を試みます。
※2 「git pull」は、「git fetch」と「git merge」を実行するコマンド(連載第382回)。
画面2は、画面1の続きとして、「git fetch」と「git merge origin/master」を実行してから、「git push」を実行しています。確認のため、実行後にコミットログを一覧表示しています。
画面2の「git merge」では、途中でエディタ(デフォルトではvi)が開いています。デフォルトでマージした旨を記録する文面が入っているため、適宜修正後、メッセージを保存します(画面3)。画面3ではデフォルトのメッセージのまま保存しています。
コンフリクトを解決するもう一つの方法は、「git fetch」でリモートリポジトリのコミットを取り込み、「git rebase」を実行してから、「git push」を試みることです。
この場合、リモートリポジトリから取得した状態に対して、ローカルリポジトリでコミットを行った、という扱いになります。このような操作を「リベース」と呼びます。
画面4では、画面1の続きを示しています。「git fetch」と「git rebase origin/master」を実行してから、「git push」を実行しています。コミットログの順番が先ほどと異なりますが、最終的な結果は同じになります。
同じファイルに対して複数人が変更を加えた場合も画面1のように「! [rejected]」と表示されます。ところが画面4のように問題を解決しようとすると、「git merge」の実行後に、「CONFLICT」(コンフリクト、衝突)というメッセージが表示されます。コンフリクトを解決するにはもう一手間が必要です。
まず同じファイルに対して複数人が変更を加えた状況を画面5で再現しました。ワークツリーの「hello.txt」というファイルに変更を加えて、「git commit」と「git push」を続けて実行すると「git push」で「! [rejected]」というメッセージが表示されてエラーになりました。
画面6では先ほどと同じようにコンフリクトを解消するため、「git fetch」でリモートリポジトリの内容を取得し、「git merge origin/master」でマージを試みています。
ところが、今回の場合は「CONFLICT」というメッセージが表示されました。「hello.txt」でコンフリクトが発生していることが分かります。リモートリポジトリ側で別の個人(Penguin)が「hello.txt」にコミットを実行していたからです。
「hello.txt」の内容を確認すると、ローカルリポジトリ側の最新の状態(HEAD)では、「hello from Study」という行が追加されているのに対し、リモートリポジトリ側(origin/master)では「hello from Penguin2」という行が追加されていました。
そこで、エディタ(vi)で、「hello.txt」を編集し、今回はどちらの行も残しました。実際のソフトウェア開発ではどちらの編集を残すのか、それとも組み合わせるのか、もう少し複雑な判断が必要になります。
画面7ではあらためて「hello.txt」をコミットし、「git push」を無事に実行できました。
西村 めぐみ(にしむら めぐみ)
元々はDOSユーザー。ソフトハウスに勤務し生産管理のパッケージソフトウェアの開発およびサポート業務を担当、その後ライターになる。著書に『図解でわかるLinux』『らぶらぶLinuxシリーズ』『[新版 zsh&bash対応] macOSコマンド入門』『Accessではじめるデータベース超入門[改訂2版]』など。地方自治体の在宅就業支援事業にてMicrosoft Officeの教材作成およびeラーニング指導を担当。会社等の"PCヘルパー"やピンポイント研修なども行っている。
Copyright © ITmedia, Inc. All Rights Reserved.