Linux/Unix環境でのGit入門

更新履歴_

  • 2020年5月4日: Ubuntu 18.04上での作業用に更新
  • 2020年5月8日: MacOS上の作業について追加

はじめに_

このページにはUbuntu 18.04を前提として構築されている部分があります。適宜、自分の環境に読み替えてください。

Mac OSの方はこちらのページを参考にしてください。

このページの画像は以下のスライドと同じです。

バージョン管理システム_

バージョン管理システム(Version Control System, VCS)とは、コンピュータ上で作成、編集されるファイルの変更履歴を管理するためのソフトウェアのこと。

VCSは以下のような状況を解決するために使用される。

  • 間違ってファイルを消してしまった
  • 変更を保存した後に、変更前の方が良いことに気付いた
  • 複数人でファイルを編集しているとき、どの変更を誰が行ったのかわからない

代表的なVCSには以下のようなものがある。

集中と分散_

VCSでは、変更履歴(リビジョン、Revision。ファイルの編集前と編集後の差分、編集日時、編集者、および、編集に対する編集者のコメント)をリポジトリと呼ばれるデータベースに格納して保管している。

現在のVCSはリポジトリの管理方法から集中型と分散型の2つの種類に分類できる。

集中型VCSでは、1つのリポジトリを1箇所で管理する。作業ディレクトリのファイル/ディレクトリの変更履歴をリポジトリに保存するのが「コミット (commit)」と呼ばれる操作である。リポジトリから(変更履歴を反映させた)ファイルやディレクトリを取り出すのが「チェックアウト (checkout)」と呼ばれる操作である。

一方、分散型VCSは、複数のリポジトリを複数の箇所で分散的に管理する。分散VCSではリポジトリ間で格納している変更履歴の同期をとることができる。ローカルリポジトリの変更履歴をリモートリポジトリ(別の場所にあるリポジトリ)へ送るのが「プッシュ (push)」という操作であり、その逆が「プル (pull)」という操作である。リモートリポジトリをローカルにコピーするのが「クローン (clone)」という操作である。

分散型バージョン管理ソフトウェアGit_

Gitは分散VCSの一種である。Gitは、Linux kernelのソースコード管理のために提案・開発されているバージョン管理システムであり、最近、もっともよく利用されている分散VCSである。

リポジトリ間の通信方式として以下のものを用意している。

  • ファイルシステム(同一計算機上の場合)
  • SSH (Secure Shell)
  • Git専用プロトコル(認証機能なし)
  • HTTP/HTTPS(WebDAV)(pushするためにはWebDAVが必要)

ネットワークに接続できないときでも、リポジトリに変更履歴を保存できるという利点の他に、集中VCSでは大変な作業であったブランチ管理を簡単に行えるという利点がある。

ブランチ管理_

変更履歴の分岐のことを「ブランチ (branch)」という。Gitにおいてはブランチはブランチを管理する操作名でもある。

ブランチを1つにまとめる操作を「マージ(merge)」という。ブランチ間で変更履歴に矛盾がない場合は、マージは問題なく成功するが、矛盾がある場合(同じ部分に異なる変更がほどこされている場合)は、「衝突 (conflict)」が発生したとみなされ、編集者による衝突の解決が必要となる。

Gitの良く使われるコマンドとリポジトリ、インデックス、作業ツリーの関係_

Gitコマンドの練習_

初期設定(エディタの設定)_

git内で用いるエディタの設定を行う.Ubuntu上のお好きなエディタを用いること。

以下ではエディタとしてemacsを使うこととする。Ubuntu 18.04 LTSは標準ではシェルにbashを用いている。そこで、~/.bash_profileを編集する。

% echo "export EDITOR='emacs -nw'" >> ~/.bash_profile
% source ~/.bash_profile
% env | grep EDITOR
EDITOR=emacs -nw

Gitがインストール済みかどうかの確認_

% which git
/usr/bin/git (gitが存在するディレクトリが表示されたならばOK)

バージョンも確認してみる。Ubuntu 18.04 LTSのGitのバージョンは結構古い(2020年5月4日現在の最新バージョン 2.26.2 ⇒ Git

% git --version
git version 2.17.1

Gitのインストール(自分で準備したLinuxの場合)_

  • RedHat、CentOS系の場合
    % sudo yum install git
    
  • Debian, Ubuntu系の場合
    % sudo apt install git
    

インストールに成功したかどうかをwhichコマンドで確かめる。

% which git

作業ディレクトリの作成_

以下の作業はGitがインストール済み(サーチパスも通してある)のLinuxでの作業と仮定する。

まず、作業ディレクトリを作ります。

% cd
% mkdir -p GitSandbox
% ls
% cd GitSandbox

リポジトリの作成 (init, config)_

gitには「作業ツリー+リポジトリ」と「リポジトリのみ」の作成方法がある。まず、頻繁に利用する「作業ツリー+リポジトリ」を作成する。

% mkdir firstrepo
% ls
% cd firstrepo
% git init

リポジトリは.gitというディレクトリ内に生成される。削除しないように注意する。

% ls -a
% ls -a .git

つづいて、このリポジトリにおけるユーザ名とメールアドレスを設定する。Gitでは編集履歴に「誰が」編集したのかを記録するため、ユーザ名とメールアドレスの設定が不可欠である。自分の名前と自分のメールアドレス(今回は大学発行のメールアドレスにしておく)に置き換えて入力すること。

% git config --global user.name "Yuichi Goto"
% git config --global user.email gotoh@aise.ics.saitama-u.ac.jp
% git config -l

なお、このリポジトリだけで設定したい場合は global オプションを外すとよい。

% git config user.name "Yuichi Goto"
% git config user.email gotoh@aise.ics.saitama-u.ac.jp

ファイルの追加 (add)_

変更履歴をリポジトリに保管するために、Indexに保管対象のファイルを明示的に指定する必要がある。これは add コマンドを使う。以下の例では、新たに作成した Readme.md をGitでの管理対象にし、かつ、Indexへ登録している。

% touch Readme.md

% git status   (現在の変更対象、Indexへの追加状況を表示するコマンド)
ブランチ master

No commits yet

追跡されていないファイル:
  (use "git add <file>..." to include in what will be committed)

	Readme.md

nothing added to commit but untracked files present (use "git add" to track)

% git add Readme.md
% git status
ブランチ master

No commits yet

コミット予定の変更点:
  (use "git rm --cached <file>..." to unstage)

	new file:   Readme.md

既に管理対象になっているファイルであっても、変更をほどこしたならば add コマンドで Indexに追加する必要がある(後述)

変更履歴のリポジトリへの保存(commit)_

Index に列挙されているファイルの変更履歴をリポジトリへ保存する。この操作を commit という。Gitでは commit を行う場合に必ずコメントを登録しなければならない。今回はコメントとして「First commit.」と入力する。

% git commit (コメント追加のためのエディタが起動する。emacsの場合、保存はC-x C-s)
[master (root-commit) febc49e] First Commit.
 0 files changed
 create mode 100644 Readme.md

参考:commitにオプションをつけることで、addコマンドを省いたり、コメントをエディタを開かずにつけることができる。

% git commit (コメント追加のためのエディタが起動する)
% git commit -m "コメント" (一言コメントであれば-mオプションをつけると便利)
% git commit -a (既に管理対象になっており、かつ、編集済のファイルをコミットする)
% git commit -a -m "コメント" (-m と -a は同時に使うこともできる)

たとえば、Readme.mdを編集してみる。まず、emacsを起動する。

% emacs &

Readme.mdを以下のように編集する(emacs上で C-x C-f を押し、Readme.mdを開く)

## Introduction

Hello, World!

## Conclusion

Bye, bye!

すると、ファイルに編集済みであるが、Indexに追加されていない状態になっているのがわかる。

% git status
ブランチ master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   Readme.md

no changes added to commit (use "git add" and/or "git commit -a")

先ほどの例に従うと以下のようにコマンドを実行し、変更履歴をリポジトリに格納すべきである(今回はこちらのコマンドは使わない)。

% git add Readme.md
% git commit

上をまとめて実行すると以下のようになる。実行してみること。

% git commit -a -m "Second commit."
[master d8dd1fc] Second commit.
 1 file changed, 7 insertions(+)

変更履歴の閲覧(log)_

変更履歴を閲覧する場合は log コマンドを使う。「commit」の横に表示されているのがハッシュキー。リビジョンを区別するためのIDとして使われている。「Author」が編集者、「Date」が編集日時、そして、その下には commit 実行時のコメントが表示される。

% git log
commit d8dd1fc15a5594bbeee12ef8a076fa0a7c8b9802
Author: Yuichi Goto <gotoh@aise.ics.saitama-u.ac.jp>
Date:   Tue Jun 2 13:17:41 2015 +0900

    Second commit.

commit febc49eac79a7c7525122e8166241060a89aa01d
Author: Yuichi Goto <gotoh@aise.ics.saitama-u.ac.jp>
Date:   Tue Jun 2 13:12:31 2015 +0900

    First Commit.

ログが多い場合は more コマンドと同様にページごとに表示される。

  • ページ送り(1画面送り):スペースキー
  • 1行送り:Enterキー
  • 終了:qキー

任意の時点との差分の表示(diff)_

Readme.md を以下のように変更する。

## Introduction

Hello, World!

## Section 1

An apple is a fruit.

## Conclusion

Bye, bye!

そして、Indexに登録する。

% git add Readme.md

さらにReadme.md を以下のように変更する。

## Introduction

Hello, World!

## Section 1

An apple is a fruit.

## Section 2

A dog is not a fruit.

## Conclustion

Bye, bye!

これで、Readme.md について、3つの異なる内容が以下のように存在している。

  • 現在のReadme.mdファイル(Section 1と2がある)
  • Indexに登録されている変更(Section 1がある)
  • リポジトリに保存されている最新のコミット(HEADという)(IntroductionとConclusionだけ)

現在の当該ファイルと過去の状態の差分を調べるためには diff コマンドを利用する。オプションなしの場合は、Indexに列挙されている状態と現在の状態の差分を表示する。

% git diff Readme.md   
diff --git a/Readme.md b/Readme.md
index fe3df5c..ab37b41 100644
--- a/Readme.md
+++ b/Readme.md
@@ -6,6 +6,10 @@ Hello, World!
 
 An apple is a fruit.
 
+## Section 2
+
+A dog is not a fruit.
+
 ## Conclusion
 
 Bye, bye!

cachedオプションをつけるとIndexとHEAD(リポジトリに登録された最新の変更履歴)との差分を表示する。

% git diff --cached Readme.md
diff --git a/Readme.md b/Readme.md
index 4c4b7d0..fe3df5c 100644
--- a/Readme.md
+++ b/Readme.md
@@ -2,6 +2,10 @@
 
 Hello, World!
 
+## Section 1
+
+An apple is a fruit.
+
 ## Conclusion
 
 Bye, bye!

オプションなしで、HEADを指定すると現在のファイルとHEADの差分を表示する。

% git diff HEAD Readme.md    
diff --git a/Readme.md b/Readme.md
index 4c4b7d0..ab37b41 100644
--- a/Readme.md
+++ b/Readme.md
@@ -2,6 +2,14 @@
 
 Hello, World!
 
+## Section 1
+
+An apple is a fruit.
+
+## Section 2
+
+A dog is not a fruit.
+
 ## Conclusion
 
 Bye, bye!

任意のリビジョンと比較する場合はコミットIDを指定する。最初のコミット「febc49eac79a7c7525122e8166241060a89aa01d」時点でのReadme.txtと現在のファイルの差分を表示する。

% git log
commit d8dd1fc15a5594bbeee12ef8a076fa0a7c8b9802
Author: Yuichi Goto <gotoh@aise.ics.saitama-u.ac.jp>
Date:   Tue Jun 2 13:17:41 2015 +0900

    Second commit.

commit febc49eac79a7c7525122e8166241060a89aa01d
Author: Yuichi Goto <gotoh@aise.ics.saitama-u.ac.jp>
Date:   Tue Jun 2 13:12:31 2015 +0900

    First Commit.

% git diff febc49eac79a7c7525122e8166241060a89aa01d Readme.md 
diff --git a/Readme.md b/Readme.md
index e69de29..ab37b41 100644
--- a/Readme.md
+++ b/Readme.md
@@ -0,0 +1,15 @@
+## Introduction
+
+Hello, World!
+
+## Section 1
+
+An apple is a fruit.
+
+## Section 2
+
+A dog is not a fruit.
+
+## Conclusion
+
+Bye, bye!

一旦、commit する。

% git commit -a -m "Third commit"

ファイル名の変更、移動および削除(mv, rm)_

たとえば、新たに tmp_dir/newfile を追加するとする。

% mkdir tmp_dir
% touch tmp_dir/newfile
% git add tmp_dir/newfile
% git commit -m "Add new file."
% git log 
(終了はqキーを押す)

一度、リポジトリに登録したあとにこのディレクトリ名やファイル名を変更したい、また、ファイルの場所を移動させたいときには mv コマンドを使う。

% ls
% git mv tmp_dir renamed_dir
% ls
% git status
ブランチ master
コミット予定の変更点:
  (use "git reset HEAD <file>..." to unstage)

	renamed:    tmp_dir/newfile -> rename_dir/newfile

% git mv renamed_dir/newfile ./newfile
% ls
% git status
ブランチ master
コミット予定の変更点:
  (use "git reset HEAD <file>..." to unstage)

	renamed:    tmp_dir/newfile -> newfile

% git commit -a -m "Renamed and moved a directory and a file."

なお、Gitでは空のディレクトリは管理対象から外される。

ファイルやディレクトリを削除するときには rm コマンドを使う。

% git rm newfile
% ls
% git status
ブランチ master
コミット予定の変更点:
  (use "git reset HEAD <file>..." to unstage)

	deleted:    newfile

% git commit -a -m "Removed a file."

オンラインマニュアル(help)_

Linuxコマンドのmanコマンドと同じように、gitの各命令に関するオンラインマニュアルを表示するコマンドhelpがある。

% git help

基本的にgitの命令は「git hogehoge」という形式になっている。このhogehogeについてオンラインマニュアルを表示したい場合には「git help hogehoge」と入力する。オンラインマニュアルの操作はmoreコマンドと同じ

  • ページ送り(1画面送り):スペースキー
  • 1行送り:Enterキー
  • 終了:qキー

1リポジトリ上の基本操作まとめ_

ここまでの復習も兼ねて、オンラインマニュアルを眺めてみる。

まず、Gitで管理するディレクトリの作成コマンド

% git help init

各種設定コマンド

% git help config

管理対象のファイル追加

% git help add

変更履歴の保存

% git help commit

変更履歴のログ

% git help log

変更履歴間の差分表示

% git help diff

管理対象のファイルの名前変更、移動

% git help mv

管理対象のファイルの削除

% git help rm

1リポジトリ上の基本的な操作は以上となる。

ブランチの確認、作成、名前変更、削除 (branch)_

1人利用でのバックアップ方法としてGitを用いる場合にはブランチを気にする必要がないが、公開するソフトウェア開発や複数人での開発の際にはブランチを用いた方が便利である。

そこでブランチを作成する。status コマンドのコメントで「ブランチ master (On branch master)」という表示があったが、デフォルトのブランチは「master」ブランチである。個人でソフトウェアを開発し、かつ、Gitを使って管理する場合には GitHub flowが便利だと思う。

GitHub flowは master(稼働し、公開できるブランチ), dev(開発用ブランチ), その他トピックブランチ(機能追加やバグ修正ごとに作成するブランチ)の3種類のブランチを運用するブランチ管理モデルである。

ブランチの一覧を表示する場合は branch コマンドを使う。アスタリスクがついているブランチが現在のブランチである。

% git branch 
* master

新たに dev ブランチを作成する。

% git branch dev
% git branch
  dev
* master

トピックブランチ「tmp_b」を作成する。

% git branch tmp_b
% git branch
  dev
* master
  tmp_b

ブランチ名を変えたい場合はmオプションを使う。

% git branch -m tmp_b tmp_c
% git branch
  dev
* master
  tmp_c

ブランチを削除する場合はdオプションを使う

% git branch -d tmp_c
Deleted branch tmp_c (was 7bb4c22).

% git branch
  dev
* master

ブランチ間の移動 (checkout)_

まず、カレントディレクトリが~/GitSandbox/firstrepoであることを確認する(ユーザ名によって絶対パスは異なるので注意。)

% pwd
/home/gotoh/GitSandbox/firstrepo

% ls -1
Readme.md
rename_dir

まず、masterブランチで新たにファイルを付け加える。マージ(merge)の綴りが間違っているが、これは後で修正するので今はこのままとする。

% touch unmarged_file
% git add unmarged_file
% git commit -m "Add a file."
% ls 
% git log

dev ブランチに移動する。移動は checkout コマンドを使う。

% git checkout dev
Switched to branch 'dev'

% git branch
% ls

% git log   # unmarged_fileを追加したコミットがない。

この dev ブランチは unmarged_file を追加する前のリビジョンから分岐しているため、unmarged_file はこのブランチには含まれない。

このようにGit はブランチごとに作業ツリーをリポジトリから構築している。これにより、あるブランチでの変更を他のブランチにまったく影響させずに作業することができる。

他ブランチの変更履歴の現ブランチへの反映 (merge)_

他ブランチの変更履歴を現ブランチへ反映させる際には merge コマンドを使う。

% git branch
% git merge master
Updating 7bb4c22..caa8706
Fast-forward
 0 files changed
 create mode 100644 unmarged_file

unmarged_fileが存在すること、また、同ファイルを追加したコミットがログに含まれていることを確認する。

% ls
% git log

unmarged_file の merge の綴りがまちがっているので修正してコミットする。

% git mv unmarged_file unmerged_file
% git commit -m "Modified spell miss."
% ls

master ブランチへ戻る。当然、unmarged_fileはunmerged_fileに変更されていない(devブランチの変更は他のブランチに影響を与えない)。

% git checkout master
% git branch
% ls

dev ブランチの変更履歴をmaster ブランチへ反映させる。

% git merge dev
% ls
% git log

ここまでをまとめると以下のとおり。 あるブランチAの変更結果は他のブランチBに影響を与えない。Aの変更結果をBに反映させたい場合はマージを行う必要がある。手順は以下のとおり。

  • まず、ブランチBに移動する。
    % git checkout B
    
  • 次にAをBにマージする。
    % git merge A
    

競合・衝突(conflict)とその解消_

ブランチ間でgitが解決できない変更の競合・衝突が起きたときにどうなるかを確認してみる。

unmerged_file の中身を以下のように変更する。

## May be conflict.
Hello.

コミットする。

% git commit -a -m "Edit unmerged_file on master."

dev ブランチへ移動する。masterブランチの変更結果はdevブランチに影響を与えないので、unmerged_file の中身は空のままである。

% git checkout dev
% git branch
% ls -l unmerged_file
% more unmerged_file

unmerged_file の中身を以下のように変更する。なお、この際にはエディタでunmerged_fileを開きなおした上で作業すること。

## May be conflict.
Good night.

コミットする。

% git commit -a -m "Edit unmerged_file on dev."

master ブランチの変更履歴を devブランチに反映させる。すると衝突が起こっていることが報告される。

% git merge master
Auto-merging unmerged_file
CONFLICT (content): Merge conflict in unmerged_file
Automatic merge failed; fix conflicts and then commit the result.

unmerged_file は以下のようになっている。これはファイルの2行目が現在のブランチ(dev)の最新のコミット(HEAD)とmasterブランチのHEADとの間で食い違っていることを表している。

% more unmerged_file 
## May be conflict
<<<<<<< HEAD
Good night.
=======
Hello.
>>>>>>> master

このように変更間に矛盾が発生し、Gitでは機械的に解決できないとき衝突が報告される。この場合は、人間が衝突を解消してあげないといけない。

衝突解決用ツールがインストールされている場合は mergetool コマンドで解決用ツールが立ち上がるがインストールされていない場合は以下のようなメッセージが表示される。

% git mergetool
This message is displayed because 'merge.tool' is not configured.
See 'git mergetool --tool-help' or 'git help config' for more details.
'git mergetool' will now attempt to use one of the following tools:
opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc codecompare emerge vimdiff
Merging:
unmerged_file

Normal merge conflict for 'unmerged_file':
  {local}: modified file
  {remote}: modified file
Hit return to start merge resolution tool (bc):  (Enterキーを押す)
The merge tool bc is not available as 'bcompare'

今回はエディタで衝突を解消する。エディタでunmerged_file を開きなおし、以下のように修正する。

## Solved the conflict.
Good night.
Hello.

コミットする。Gitが勝手にコメントをつけてくれる。

% git commit -a
% git log

以上が衝突の発生、および、衝突の解消方法である。

次の作業のため master ブランチに戻り、mergeする。

% git checkout master
% more unmerged_file

% git merge dev
% more unmerged_file

ブランチの基本操作まとめ_

ここまでの復習も兼ねて、オンラインマニュアルを眺めてみる。

ブランチ作成、名前変更、削除など

% git help branch

ブランチ間の移動、あるいは、作業ツリーの展開

% git help checkout

他のブランチの変更履歴の自ブランチへの反映

% git help merge

以上がブランチに関する基本操作である。

作業ツリーなしリポジトリの作成(init --bare)_

リポジトリ間の操作を練習するために二つ目のリポジトリを作業ツリーなしで作成する。

% cd ~/GitSandbox
% mkdir secondrepo
% cd secondrepo
% git init --bare
% ls -a  # 作業ツリー+リポジトリの場合の.gitの中身が直接生成される

以後、この secondrepo をリモートリポジトリ(作業ツリーのあるリポジトリとデータのやりとりをする別の場所にあるリポジトリのこと)としてリポジトリ間の同期の練習を行なう。

リモートリポジトリの設定(remote)_

ここまで、 firstrepo ではリモートリポジトリを設定していなかった。リモートリポジトリの設定は remote コマンドを用いて行なう。まず、現在の設定を確認する。今のところリモートリポジトリは設定されていないので git remote -v コマンド何も表示されない。

% cd ../firstrepo
% git remote -v

先ほど作成した secondrepo をリモートリポジトリとして設定する。リモートリポジトリは複数個設定することができる。標準で使うリポジトリは origin という名前をつけて管理することになっている。secondrepo は同一コンピュータ上に存在するのでファイルアクセスでリポジトリにアクセスする。

% pwd
/home/gotoh/GitSandbox/firstrepo

## 「file://」にsecondrepoの絶対パス(pwdの出力結果で分かる)を入力すること
% git remote add origin file:///home/gotoh/GitSandbox/secondrepo

# git remote addの書式
% git remote add リポジトリ名 リモートリポジトリのURL(file://, ssh://, git://, https://, http://)

正しくリモートリポジトリが設定できているかを確認する。

% git remote -v
origin	file:///home/gotoh/GitSandbox/secondrepo (fetch)
origin	file:///home/gotoh/GitSandbox/secondrepo (push)

もし、リモートリポジトリのURL(今回の場合はリポジトリのある場所の絶対パス表記)が間違っているならば、以下のコマンドで修正する。

% git remote set-url リポジトリ名 リモートリポジトリのURL

リモートリポジトリのリポジトリ名を間違えた場合は、以下のコマンドでリポジトリ名を修正する。

% git remote rename 現在のリモートリポジトリ名 新しいリポートリポジトリ名

リモートリポジトリにローカルリポジトリの内容を反映させる(push)_

リモートリポジトリとローカルリポジトリ(現在作業しているリポジトリ)の間で変更履歴の同期をとることができる。ローカルリポジトリの変更履歴をリモートリポジトリに反映させるときはpushコマンドを用いる。

以下のように実行する。

% git push origin master
% git push origin dev

## pushの書式
% git push リモートリポジトリ名 ブランチ名

これで、firstrepoのmasterブランチとdevブランチがsecondrepo に反映された。

リモートリポジトリのコピー(clone)_

すでに存在するリモートリポジトリの内容をコピーするときに clone コマンドを使う。cloneコマンドで3つめのリポジトリを作成する。cloneコマンドは標準では、コピー元のディレクトリ名と同じ名前のディレクトリ名でコピーしたリポジトリを作成してしまう(つまり、この例題だと GitSandbox 以下でcloneコマンドを実行すると失敗してしまう)。この例題ではthirdrepoというディレクトリ名でリポジトリのコピーをしている。

% cd ..
% pwd
/home/gotoh/GitSandbox

% git clone file:///home/gotoh/GitSandbox/secondrepo
fatal: destination path 'secondrepo' already exists and is not an empty directory.
(標準ではリモートディレクトリの名前のディレクトリが生成される。
 今回はsecondrepoがすでに存在するため上記のエラーがでている)

% git clone file:///home/gotoh/GitSandbox/secondrepo thirdrepo
% ls
% cd thirdrepo

clone コマンドでコピーしてきたリポジトリでは、(基本的には)リモートリポジトリはコピー元のリポジトリになっている。

% git remote -v
origin	file:///home/gotoh/GitSandbox/secondrepo (fetch)
origin	file:///home/gotoh/GitSandbox/secondrepo (push)

cloneコマンドでリポジトリをコピーしてきた場合、ブランチは標準でmaster だけコピーされる。

% git branch
* master

リモートリポジトリに存在するブランチも見たい場合は、branch コマンドに -a オプションをつける。

% git branch -a  
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/dev
  remotes/origin/master

リモートリポジトリに存在するブランチに対して checkout するとローカルにリモートのブランチがコピーされる。

% git checkout dev
Branch dev set up to track remote branch dev from origin.
Switched to a new branch 'dev'

% git branch

Readme.mdにSection 3を付け加える。

## Introduction

Hello, world!

## Section 1

An apple is a fruit.

## Section 2

A dog is not a fruit.

## Section 3

Everything is a fruit.

## Conclusion

Bye, bye!

commitして、リモートリポジトリへpushする。

% git commit -a -m "Edit a file in third repository."

% git push    
Counting objects: 3, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 360 bytes | 360.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To file:///home/gotoh/GitSandbox/secondrepo
   e116920..51e5312  dev -> dev

リモートリポジトリの内容をローカルリポジトリへ反映させる(pull)_

ここまでで、secondrepoの内容は firstrepo よりもリビジョンが進んでいる。そこで、secondrepo の内容を firstrepo へ反映させる。リモートリポジトリの内容の反映は pull コマンドを用いる。

% cd ~/GitSandbox/firstrepo
% git branch
  dev
* master

% more Readme.md (Section 3がない)

% git pull origin master
From file:///home/gotoh/GitSandbox/secondrepo
 * branch            master     -> FETCH_HEAD
Already up-to-date.

# pull コマンドの書式
% git pull リモートリポジトリ名 リモートリポジトリのブランチ名

現在、secondrepoとfirstrepoの内容は devブランチにおいてのみ違うため、masterブランチをpullしても何も変更はおこらない。

devブランチの変更をローカルリポジトリへ反映させる。

% git checkout dev
% git pull origin dev
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From file:///home/gotoh/GitSandbox/secondrepo
 * branch            dev        -> FETCH_HEAD
   e116920..51e5312  dev        -> origin/dev
Updating e116920..51e5312
Fast-forward
 Readme.md | 4 ++++
 1 file changed, 4 insertions(+)

% more Readme.md 
% git log

これで、2つ目のリポジトリを経由して、1つ目と3つ目のリポジトリの同期を行なうことができた。

たとえば、ノートパソコンと自宅のデスクトップパソコンの間で同期をとりたい場合は以下のようにリポジトリを作って運用する。

  • ノートパソコン上に作業ディレクトリ+リポジトリ
  • ネットワーク上にリモートリポジトリ(多くの場合はGitHubGitLab, backlogなどのGitフロントエンドを用いる)
  • デスクトップパソコン上に作業ディレクトリ+リポジトリ

リポジトリ間の基本操作まとめ_

ここまでの復習も兼ねて、オンラインマニュアルを眺めてみる。

ローカルリポジトリと連携するリモートリポジトリの設定。

% git help remote

ローカルリポジトリの変更履歴をリモートリポジトリに反映させる。

% git help push

リモートリポジトリをローカルリポジトリとして保存する。

% git help clone

リモートリポジトリの変更履歴をローカルリポジトリに反映させる。

% git help pull

ファイルの各行がどのコミットで変更されたのかを調べる(blame)_

どの行がどのコミット(リビジョン)で変更されたのかを調べる場合は blame コマンドを使う。

% git blame Readme.md             
a0236847 (Yuichi Goto 2020-05-04 02:09:27 +0900  1) ## Introduction
a0236847 (Yuichi Goto 2020-05-04 02:09:27 +0900  2) 
a0236847 (Yuichi Goto 2020-05-04 02:09:27 +0900  3) Hello, World!
a0236847 (Yuichi Goto 2020-05-04 02:09:27 +0900  4) 
61f60057 (Yuichi Goto 2020-05-04 02:17:44 +0900  5) ## Section 1
61f60057 (Yuichi Goto 2020-05-04 02:17:44 +0900  6) 
61f60057 (Yuichi Goto 2020-05-04 02:17:44 +0900  7) An apple is a fruit.
61f60057 (Yuichi Goto 2020-05-04 02:17:44 +0900  8) 
61f60057 (Yuichi Goto 2020-05-04 02:17:44 +0900  9) ## Section 2
61f60057 (Yuichi Goto 2020-05-04 02:17:44 +0900 10) 
61f60057 (Yuichi Goto 2020-05-04 02:17:44 +0900 11) A dog is not a fruit.
61f60057 (Yuichi Goto 2020-05-04 02:17:44 +0900 12) 
51e5312a (Yuichi Goto 2020-05-04 15:30:40 +0900 13) ## Section 3
51e5312a (Yuichi Goto 2020-05-04 15:30:40 +0900 14) 
51e5312a (Yuichi Goto 2020-05-04 15:30:40 +0900 15) Everything is a fruit.
51e5312a (Yuichi Goto 2020-05-04 15:30:40 +0900 16) 
a0236847 (Yuichi Goto 2020-05-04 02:09:27 +0900 17) ## Conclusion
a0236847 (Yuichi Goto 2020-05-04 02:09:27 +0900 18) 
a0236847 (Yuichi Goto 2020-05-04 02:09:27 +0900 19) Bye, bye!

ブランチをグラフ形式で見る(log --graph)_

どこで分岐してどこで合流したのかなど見ることができる。

% git log --graph --pretty=oneline
* 51e5312a4997a960cc822be52a90170422691577 (HEAD -> dev, origin/dev) Edit a file in third repository.
*   e116920a894ff26b4ea1328e56c16073acdd711b (origin/master, master) Merge branch 'master' into dev
|\  
| * a6a16a86a997d69941f00ed1c28203b600d6e941 Edit unmerged_file on master.
* | c8a40a2d35d5de19c5706264d12de40bd7583e39 Edit unmerged_file on dev.
|/  
* 9909c8d8ac094dca986ee8885a9d3473e1cae768 Modified spell miss.
* aebc2ca892759391069089a67592b170a4b62235 Add a file.
* 95fc2f9eccdadbbb3f91296cf3a181408acc4162 Removed a file.
* 71606375bdf3311583444f1adb72e54d52aaadfe Renamed and moved a directory and a file.
* ecab7a4204e3982963fb98fe978bb6c0514a4e19 Add new file.
* 61f6005706995d83937bf306b91a2842a93252ff Third commit
* a023684783cf46aeb35e896c58d5c3354505a5a3 Second commit.
* 1b1121e369013a0ae7efdd24e7bc851677489da7 First commit.

過去のコミットからブランチを作成する/ファイルを作成する(checkoutの発展的使い方)_

作業を進めていく中で、一旦、ある時点まで戻って作業したくなることは良くある(たとえば、不要だと思って削除した部分が、やはり必要だったなど)。そのようなときには、過去のコミットからブランチを作成することで対応できる。

たとえば、途中で削除したnewfileを復活させる。まず、「git log」コマンドを使って、newfileを削除する直前のコミットを探す。

% git log --pretty=oneline
~中略~
95fc2f9eccdadbbb3f91296cf3a181408acc4162 Removed a file.
71606375bdf3311583444f1adb72e54d52aaadfe Renamed and moved a directory and a file.
ecab7a4204e3982963fb98fe978bb6c0514a4e19 Add new file.

~後略~

コミットのハッシュ値が「71606375bdf3311583444f1adb72e54d52aaadfe」の次のコミットでnewfileを削除していたのが分かる。このハッシュ値を使えば特定のファイルだけ過去のコミット時に戻すことや、過去のコミットを別ブランチとして呼び出すことが可能である。

  • 「git checkout ハッシュ値 ファイル名」:特定のファイルだけを復帰させる
  • 「git checkout ハッシュ値 -b 新ブランチ名」:過去のコミットの状態を別ブランチとして呼び出す

newfileを削除前のコミットから復帰させる。

% git checkout 71606375bdf3311583444f1adb72e54d52aaadfe newfile
% ls (newfileが復帰していることを確認する)

% git status
ブランチ dev
コミット予定の変更点:
  (use "git reset HEAD <file>..." to unstage)

	new file:   newfile

追跡されていないファイル: (注:以下のファイルは衝突時のbcコマンドで生成された)
  (use "git add <file>..." to include in what will be committed)

	unmerged_file_BACKUP_13441
	unmerged_file_BASE_13441
	unmerged_file_LOCAL_13441
	unmerged_file_REMOTE_13441

newfileが必要な場合はこのままコミットする。今回はコミットしてみる。

% git commit -a -m "Return newfile from previous commit."
% git log --graph --pretty=oneline

別ブランチとして呼び出してみる。

% git log --pretty=oneline
2cbb4420f448e3e5d34baf10d74c6845fe196025 (HEAD -> dev) Return newfile from previous commit.
51e5312a4997a960cc822be52a90170422691577 (origin/dev) Edit a file in third repository.
e116920a894ff26b4ea1328e56c16073acdd711b (origin/master, master) Merge branch 'master' into dev
c8a40a2d35d5de19c5706264d12de40bd7583e39 Edit unmerged_file on dev.
a6a16a86a997d69941f00ed1c28203b600d6e941 Edit unmerged_file on master.
9909c8d8ac094dca986ee8885a9d3473e1cae768 Modified spell miss.
aebc2ca892759391069089a67592b170a4b62235 Add a file.
95fc2f9eccdadbbb3f91296cf3a181408acc4162 Removed a file.
71606375bdf3311583444f1adb72e54d52aaadfe Renamed and moved a directory and a file.
ecab7a4204e3982963fb98fe978bb6c0514a4e19 Add new file.
61f6005706995d83937bf306b91a2842a93252ff Third commit
a023684783cf46aeb35e896c58d5c3354505a5a3 Second commit.
1b1121e369013a0ae7efdd24e7bc851677489da7 First commit.

% git checkout 71606375bdf3311583444f1adb72e54d52aaadfe -b before_removed
% git branch
* before_removed
  dev
  master

% git log --pretty=oneline
71606375bdf3311583444f1adb72e54d52aaadfe (HEAD -> before_removed) Renamed and moved a directory and a file.
ecab7a4204e3982963fb98fe978bb6c0514a4e19 Add new file.
61f6005706995d83937bf306b91a2842a93252ff Third commit
a023684783cf46aeb35e896c58d5c3354505a5a3 Second commit.
1b1121e369013a0ae7efdd24e7bc851677489da7 First commit.

Gitコマンドまとめ_

参考リンク_

Gitフロントエンドの使い方_

余裕がある場合はぜひGitHubの使い方も体験してみてください。埼玉大学工学部情報工学科3年次科目「実践的システム開発演習」ではGitHubを用いてチーム間での開発を行ってもらっています。

戻る_