2017年07月30日

git チェックアウトをもっと便利に使う

ブランチを切り替えるときに使う git checkout コマンド。
ブランチのチェックアウト操作を行うことで、作業ツリーの状態をそのブランチの最新状態に切り替えることができる。

が、チェックアウトできるのは、ブランチだけではない。
他の使い方もあるので、知っていればより便利に git checkout が使える。


まず "HEAD" の状態を確認しておく。".git/HEAD" というファイルが実体。
git_checkout01.png
$ cat .git/HEAD
ref: refs/heads/master

"refs/heads/master" を参照していることを示している。
では、"refs/heads/" にどのようなファイルがあるのか確認してみる。
└ refs
├ heads
├ master
└ new_branch
"master" ブランチと "new_branch" ブランチのファイルがある。では、肝心の "refs/heads/master" には何が書いてあるのか。
$ cat .git/refs/heads/master
b94d4058ea3ef1537213e35212debb2214507a05
"master" ブランチの最新のコミットIDが書かれている。
この実体は ".git/objects/" にあり、コミットIDをもとに探すことができる。
今、参照するコミットIDが "b94d405" なので ".git/objects/b9/4d4058ea3ef1537213e35212debb2214507a05" が "HEAD" が参照するものとなる。
"HEAD"

.git/HEAD

.git/refs/heads/master

.git/objects/b9/4d4058ea3ef1537213e35212debb2214507a05






ファイルの過去のリビジョンをチェックアウトする


「全体としては今の雰囲気が良いいけど、この部分については前の内容のほうがバランスが取れているよね」的なことを言われたり、「落ち着いて考えたらやっぱりこの内容は無し」と思うこともある。
そういう場合は、git reset で作業ツリーを元に戻すことができる。
しかし git reset は作業ツリー上のすべてのファイルを指定状態に戻す。もし、特定のファイルだけ巻き戻したいという場合には、git checkout が使える。

"HEAD" 状態に戻したい場合は以下のようにする。

git checkout HEAD <対象ファイル>

"HEAD" が参照するコミットIDから対象ファイルをロードしてきて、作業ツリー上のファイルを上書きする。
git_checkout02.png

ファイルを戻すのは、"HEAD" だけではなく、コミットIDを指定すれば過去のコミットから特定ファイルをロードすることができる。

git checkout <コミットID> <対象ファイル>

このときの "HEAD" の状態も確認してみる。
$ git checkout b60971b a.txt
$ cat .git/HEAD
ref: refs/heads/master
"HEAD" の状態に変化はなく、"master" ブランチを参照している。

ステータスを見てみる。
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD ..." to unstage)

modified: a.txt
チェックアウトしたファイルは、ロードするだけではなく、ステージされている。このような動きになることを知っておくとよい。

また、コミットIDだけではなく、タグを指定することもできる。

$ git checkout <タグ> <対象ファイル>



ファイルの変更を元に戻すためだけではなく、過去の内容をちょっと確認したい場合にも使える。コミットIDだけではなく、タグを指定することもできるので、過去のリリースバージョン当時のファイルを確認したいときになどに、その当時のファイルをチェックアウトし、確認が終われば、"HEAD"を指定して戻せば良い。




過去のリビジョンを一時的に取り出す


特定のファイルではなく、特定のコミットを指定すれば、作業ツリーがすべてその当時の状態になるのでは?
と思って、特定のコミットだけ指定してチェックアウトしてみる。

$ git checkout b60971b

Note: checking out 'b60971b'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

git checkout -b

HEAD is now at b60971b... Add new file : b.txt
何かメッセージが出て警告を受けた気がする。

メッセージ内容はこんな感じ。
"detached HEAD" 状態です。この時点のファイルを見て回ったり、実験的な変更を加えてコミットすることができる。別のブランチをチェックアウトすれば、その他のブランチに影響を与えることなく、ここでの変更を破棄することができる。
もし作成したコミットを残しておくために新しいブランチを作りたいなら、-b を使って再度チェックアウトをすればいい。
例:git checkout -b <new-branch-name>
"HEAD" は、今、 b60971b... Add new file : b.txt

git_checkout03.png

このときの "HEAD" の状態を確認してみる。
$ cat .git/HEAD
b60971b05d47cb17ce3f4ad01be89e3c1d9c8e29
今までと違い、コミットの実体を直接指定している。
HEAD

.git/objects/b6/0971b05d47cb17ce3f4ad01be89e3c1d9c8e29


間違って "detached HEAD" したのであれば、再度ブランチをチェックアウトして、きちんと "detached" では無い状態にする。


意図的に "detached HEAD" にしたのであれば問題無いが、もしgitがせっかく教えてくれたメッセージを無視してブランチをチェックアウトしたものだと勘違いしたまま作業してコミットまでしてしまったとする。
git_checkout04.png
この状態でブランチを切り替えてみる。
$ git checkout new_branch
Warning: you are leaving 1 commit behind, not connected to
any of your branches:

2c8dbd4 detached HEAD

If you want to keep it by creating a new branch, this may be a good time
to do so with:

git branch 2c8dbd4

Switched to branch 'new_branch'

またメッセージが出た。この内容はこんな感じ。
どのブランチにも繋がっていないコミットが1個残っている。

2c8dbd4 detached HEAD

これを新しいブランチを作ることで残したいのであれば、今がいいタイミングです。
以下のようにすればいい。

git branch <new-branch-name> 2c8dbd4

git_checkout05.png
"detached HEAD" になっていることに気づかずにコミットした状態で、他のブランチにチェックアウトすると、"detached HEAD" 状態でコミットした内容は見えなくなってしまうが、無くなったわけではない。どこからも参照されないだけで記録は残っている。

これを復活させるためには、gitが教えてくれた通りに実行すればいい。
一時的なブランチを作ればいい。

$ git checkout tmp


これで参照できるようにつながったので、後はこのブランチをマージするなりして対応すればいい。作業した内容を無事に復旧できれば、一時的なブランチなので削除すれば終了。






まとめ


git checkout でファイルの変更を無かったことにしたり、巻き戻したりできるので知っておくと、いざというときにさっと作業できるので便利。ただし、コミットIDでチェックアウトし、"detached HEAD" にしてしまうと、この状態を知っていると知らないでは、このあとの作業の内容が違ってくる。間違ったまま作業していくと、コミット内容が見える範囲から消えてしまうことになりかねない。
gitがきちんとメッセージで教えてくれているので、慣れた作業であってもメッセージを読み飛ばさないように注意したほうがいい。





gitをインストール
gitサーバーのセットアップ
git ブランチについて
git 変更を一時的に退避 stash
git ブランチを合流するマージ
git ブランチを付け替える
git コミット履歴を変更する
git 変更をリセットする
git リモートでの操作
git ファイルの追跡
git リリース準備
git リモートブランチを追加
git プロジェクトの構成
git 変更をpatchファイルにする
git コンフリクトに対処する
git 失敗したときの復元
posted by Zorinos at 20:00| Comment(0) | Linux | 更新情報をチェックする

2017年07月25日

git リモートブランチを追加

複数メンバーで作業するプロジェクトを新規で作成する場合、リモートリポジトリを準備してから作業する。これは git サーバーのセットアップ時に触れた。

この場合の手順をざっくりと言えば、こんな感じ。
  1. 空のリモートリポジトリを準備
  2. リーダーのローカルマシンへクローン
  3. ローカルマシンで初期セットアップ
  4. リモートへプッシュ
  5. メンバーが各自のマシンへクローン
  6. 各自で作業開始


でもサーバー側の準備が遅かったら、リモートリポジトリができるまで待たないといけないので、先にローカルで準備をしたほうが時間の無駄がない。
また、単独で作業をしていたプロジェクトを複数メンバーで作業することになり、公開する必要ができた場合は、後からリモートリポジトリを設定できなければいけない。



後からリモートリポジトリを設定する


  1. ローカルリポジトリで作業が進行
  2. サーバー側で準備ができ、空のリモートリポジトリを作成
  3. ローカルリポジトリにリモートリポジトリを設定
  4. リモートへプッシュ
  5. メンバーが各自のマシンへクローン
  6. 各自で作業開始



現在のブランチの状態を確認してみる。
$ git branch -a
* develop
master

まだ、リモート追跡ブランチは存在していない。

ローカルリポジトリに、新たなリモートリポジトリを設定するためには git remote を使う。

$ git remote add origin gitserver:~/git/project1.git


リポジトリを作成したら、git push する。
--all オプションを使えば、すべてのブランチを git push できる。

$ git push --all




現在のブランチの状態を確認してみる。
$ git branch -a
* develop
master
remotes/origin/develop
remotes/origin/master

リモートリポジトリが設定され、リモート追跡ブランチが設定されたことがわかる。






リモートリポジトリの設定を削除する


設定を削除するのも git remote を使う。

$ git remote rm origin



現在のブランチの状態を確認してみる。
$ git branch -a
* develop
master






リモートリポジトリの変更


gitサーバーが変更になり、サーバーアドレスが変更になった場合は、リモートリポジトリも変更しなければならない。リモートリポジトリを変更するには、一旦、削除してから再設定しても良いが、1つのコマンドで対応できる。

$ git remote set-url origin <新しいリポジトリのアドレス>


変更されたことを確認するには git remote -v を使えばよい。
$ git remote -v
origin 新しいリポジトリのアドレス/new-repo.git/ (fetch)
origin 新しいリポジトリのアドレス/new-repo.git/ (push)





まとめ


そんなに頻繁にリモートリポジトリを変更する場面は無さそうな気がするが、例えばペアを組んで作業するメンバー同士のマシンを相互にリモートリポジトリに設定すれば、作業途中の状態を他のメンバーに公開する必要がない。作業に必要な範囲だけで共有し、完成してから全メンバーへ公開すればよい。このようなやり方をするのであれば、リモートリポジトリの設定・削除は頻繁に発生する。
なんでもメインとなるリモートリポジトリを使って作業していると、メンバー数が増えれば増えるほど、サーバーの負荷があがり、逆に作業の効率が悪くなることが予想できる。gitの機能を理解して、効率よく作業を進めれる方法を身に付けたい。





gitをインストール
gitサーバーのセットアップ
git ブランチについて
git 変更を一時的に退避 stash
git ブランチを合流するマージ
git ブランチを付け替える
git コミット履歴を変更する
git 変更をリセットする
git リモートでの操作
git ファイルの追跡
git リリース準備
git チェックアウトをもっと便利に使う
git プロジェクトの構成
git 変更をpatchファイルにする
git コンフリクトに対処する
git 失敗したときの復元
posted by Zorinos at 20:00| Comment(0) | Linux | 更新情報をチェックする

2017年07月23日

git リリース準備

リリースのタイミング限定での操作ではないが、リリース時によくやることについて整理する。

コミットに印を付ける


タグの作成


リリースや、目標とするマイルストーンなどの到達点で印を付ける。
リポジトリの中でのブックマークみたいな役割として、タグを打つ。

チェックアウトしているブランチの最新のコミットに対して付ける。

$ git tag "version_1.0"




コミットID を指定して過去のコミットに対して付ける。

$ git tag "release_test" 1c0a080



  • 既に存在する同名のタグはつけれない
    $ git tag "v1.0" 9fe9714
    fatal: tag 'v1.0' already exists
  • 同一コミットに複数のタグを付けることはできる
  • 有効でない文字(space、:(コロン)、~(チルダ)、?(クエスチョンマーク)、*(アスタリスク)など)は使えない
    $ git tag "version 1.0"
    fatal: 'version 1.0' is not a valid tag name.



タグだけでは、どのようなマイルストーンなのか判りにくいが、タグに注釈コメントを付けることで詳細な情報を残すことができる。
-a オプションを使う。

$ git tag -a "version_1.0" -m "Release: deployment"


-m オプションを省略して実行するとエディタが立ち上がるので、そこで編集することも可能。

注釈コメント付きにすると、タグをつけた人の情報・日時も格納されるので、タグを付けるときは、この -a オプションを使うことをススメる。



タグの確認


リポジトリ内のタグの確認。
現在チェックアウトしているブランチ以外のタグも見える。

$ git tag

release_test
v0.1
version_1.0



特定のタグの内容を確認するときには、git show を使い、タグを指定する。

$ git show version_1.0

tag version_1.0
Tagger: hoge hogehoge <hogehoge@email.com>
Date: Sat Jul 8 21:00:00 2017 +0900

Release: deployment

commit 762bccf8cb34d4b900ddb2832dc1869c7391d218
Author: hoge hogehoge <hogehoge@email.com>
Date: Sat Jul 8 20:00:00 2017 +0900

master 追加 3

diff --git a/index.html b/index.html
index a4bf772..70450f3 100755
--- a/index.html
+++ b/index.html
@@ -8,5 +8,6 @@
<body>
<h2>master</h2>
<p>master 追加</p>
+ <p>master 追加2</p>
</body>
</html>

注釈コメントをつけたタグであれば、タグをつけた人・日時・注釈コメントも表示される。



タグの削除


タグを違う場所につけた場合に削除できる。
-d オプションを使う。

$ git tag -d release_test

Deleted tag 'release_test' (was 1c0a080)




タグのリネーム


タグをつけたが、後から名称を変更する必要が出た場合、
タグを直接的に編集してリネームすることはできない。
この場合、元のタグを使って、新しいタグを同じ位置に付けてから、元のタグを削除することで対応する。

$ git tag new_tag old_tag
$ git tag -d old_tag






タグの共有


タグを付けただけでは、ローカルリポジトリにしか反映されないので、リモートリポジトリに反映するため、git push する。

$ git push origin v1.0



個別に反映するのではなく、すべてのタグを反映するときは、--tags オプションを使う。

$ git push origin --tags




リモートリポジトリのタグを削除するには、リモートのブランチを削除する操作と同じようにし、タグ名を指定すればよい。

$ git push origin :v1.0








リリース用ファイルのアーカイブ


作業の成果物として作成コードを納品するような場合、最新の安定版のファイルだけでよく、リリースのたびに履歴は含める必要がないという場合、git archive でアーカイブが使える。

$ git archive --format=zip --prefix=mysite-1.0/ HEAD > mysite-1.0.zip


--prefix= を使うことで、作成したアーカイブファイルを展開したときにディレクトリ "mysite-1.0/" を作成し、その中に展開する。このオプションを指定しないと、その場所でフラットに展開される。

また、指定ディレクトリ "mysite-1.0/" は、"/"(スラッシュ)を付けているが、これは必須。これを忘れて "mysite-1.0" と指定すると、展開時にディレクトリ内に展開されるのではなく、各ファイルの接頭語 "mysite-1.0" が付いたファイル群ができあがってしまう。

HEAD ではなく、タグを指定しても良い。
タグ "v1.0" を使って、次は "tar.gz" で作成してみる。

$ git archive --format=tar --prefix=mysite-1.0/ v1.0 | gzip > mysite-1.0.tar.gz





もしgit管理下に ".gitignore" があると、これもアーカイブ対象となってしまう。

リリースに含める必要はないので、これを除外したい場合は、".gitattributes" というファイルを作成し、これを指定することで除外可能。
以下のような構成とする。
.
├ .gitattributes
├ .gitignore
└ index.html

".gitattributes" の中身は以下とする。

.gitignore export-ignore
.gitattributes export-ignore


".gitattributes" も含めることがポイント。


これを git archive で実行するときに、--worktree-attributes オプションを指定することで除外フィルタが有効となる。

$ git archive --format=tar --worktree-attributes --prefix=mysite-1.0/ v1.0 | gzip > mysite-1.0.tar.gz




これを展開すると、
$ tar -zxvf mysite-1.0.tar.gz
mysite-1.0/
mysite-1.0/index.html

不要なファイルが除外されていることがわかる。






まとめ


タグを付けることは、作業のマイルストーンとして重要。いついつのバージョンでのリリース状態が見たいと思ったときに、すぐに引っ張ってこれる。リリース時だけではなく、作業の目標到達点があればタグを付けるようにしておくと、履歴を遡る場合に楽になる。
アーカイブは成果物として納品するような場合でないとなかなか使わないが、知っておくと役立つ。




gitをインストール
gitサーバーのセットアップ
git ブランチについて
git 変更を一時的に退避 stash
git ブランチを合流するマージ
git ブランチを付け替える
git コミット履歴を変更する
git 変更をリセットする
git リモートでの操作
git ファイルの追跡
git リモートブランチを追加
git チェックアウトをもっと便利に使う
git プロジェクトの構成
git 変更をpatchファイルにする
git コンフリクトに対処する
git 失敗したときの復元
posted by Zorinos at 20:00| Comment(0) | Linux | 更新情報をチェックする

2017年07月22日

git ファイルの追跡

ファイルの追加


新たなファイルを追加する場合は、
作業ツリー上に対象ファイルを追加したら、ステージし、コミットすればよい。
$ echo "aaa" > a.txt
$ git add a.txt
$ git commit -m "Add new file : a.txt"
[master (root-commit) ebd2002] Add new file : a.txt
1 file changed, 1 insertion(+)
create mode 100644 a.txt


作業ツリーは、ディレクトリを追加し階層構造にすることも可能。
$ mkdir nextdir
$ cd nextdir/
$ echo "bbb" > b.txt
$ git add b.txt
$ git commit -m "Add new file : b.txt"
[master b60971b] Add new file : b.txt
1 file changed, 1 insertion(+)
create mode 100644 nextdir/b.txt


追跡対象ファイルを一覧を確認してみると、

$ git ls-files

a.txt
nextdir/b.txt

階層構造でファイルが追跡できていることがわかる。


ただし、空のディレクトリだけではgit管理下に入らない。
何らかのファイルを置く必要がある。慣習的に ".gitkeep" という空ファイルを置くことが多い。



いつ追跡が開始されるのか


git add により、一度ステージされるとファイルが追跡対象となる。
$ echo "ccc" > c.txt
$ git add c.txt
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD ..." to unstage)

new file: c.txt

"c.txt" を作成し、ステージした。

ここで、ファイルを確認してみる。
$ git ls-files
a.txt
c.txt
nextdir/b.txt

追跡対象のファイルに、"c.txt" が含まれている。

もう1つファイルを作成した後で、追跡対象ファイルを確認してみる。
$ echo "ddd" > d.txt
$ git ls-files
a.txt
c.txt
nextdir/b.txt

"d.txt" はまだ対象になっていない。

追跡されていないファイルの一覧を表示して確認してみる。
-o オプションを使う。

$ git ls-files -o

d.txt

追跡対象外のファイルとして表示された。git add する前はまだ追跡対象になっていないことがわかる。





ファイルの削除


不要となったファイルを追跡から外す。
git rm した後に、コミットする。

$ git rm c.txt
$ git commit -m "Delete file : c.txt"

[master 87816aa] Delete file : c.txt
1 file changed, 1 deletion(-)
delete mode 100644 c.txt


ディレクトリごとすべて削除する場合は、ディレクトリを指定することと、-r オプションを指定する。

$ git rm -r nextdir/
$ git commit -m "Delete files"

[master 0ba11c3] Delete files
3 files changed, 3 deletions(-)
delete mode 100644 nextdir/b.txt
delete mode 100644 nextdir/e.txt
delete mode 100644 nextdir/f.txt



作業ツリー上にファイルを残しつつ、追跡対象から外す場合は、--cached オプションを使う。

$ git rm --cached d.txt
$ git commit -m "Untracked files : d.txt"

[master bdf7fa7] Untracked files : d.txt
1 file changed, 1 deletion(-)
delete mode 100644 d.txt







ファイルの移動


作業するファイル数が増加してくると、ファイルをグループ化して管理するほうがわかりやすい。また間違ったディレクトリに作成してしまうこともある。
このような場合にファイルの配置を移動させる。
git mv した後に、コミットする。
$ git ls-files
a.txt
c.txt
d.txt
nextdir/b.txt

$ git mv d.txt ./nextdir/
$ git commit -m "move file d.txt -> next/d.txt"

[master 099158f] move file d.txt -> next/d.txt
1 file changed, 0 insertions(+), 0 deletions(-)
rename d.txt => nextdir/d.txt (100%)

$ git ls-files
a.txt
c.txt
nextdir/b.txt
nextdir/d.txt




git mv は、ファイル名の変更もできる。

$ git mv c.txt c_new.txt
$ git commit -m "rename c.txt -> c_new.txt"

[master 0aa0120] rename c.txt -> c_new.txt
1 file changed, 0 insertions(+), 0 deletions(-)
rename c.txt => c_new.txt (100%)

$ git ls-files
a.txt
c_new.txt
nextdir/b.txt
nextdir/d.txt




ここで、リネームしたファイルのログを確認してみる。
$ git log --oneline c_new.txt
0aa0120 rename c.txt -> c_new.txt

通常のログ確認では、リネームした以降のコミット履歴しか見れなくなっていた。



もし変更履歴が積み重なっているファイルの場合は、履歴が追えなくて困る。
リネーム前の履歴も見たければ--follow オプションを使う。

$ git log --follow --oneline c_new.txt

0aa0120 rename c.txt -> c_new.txt
947cfa3 Add new file : c.txt






git管理させないファイルを無視させる


プロジェクト単位で無視する


".gitignore" ファイルを作成し作業ツリーに配置する。
$ vi .gitignore


中身はこのような感じ。
# テキストファイルを無視する
*.txt

#tempディレクトリ以下は全て無視
temp/

#readme.txt は git管理下
!readme.txt

  • ".gitignore" は複数のディレクトリに配置できる
  • ".gitignore" が配置されたディレクトリのサブディレクトも適用される。
  • 深い階層に置かれた ".gitignore" が後から解釈されるため、マッチングの優先度が高い
  • ファイルに記述されたパターンの上の行から順に解釈されるので、複数マッチする場合は、最後にマッチするパターンが適用される
  • #(ハッシュ記号)で始まる行はコメントとして扱う
  • 空行は無視
  • /(スラッシュ)で終わる場合はディレクトリを示し、対象ディレクトリに含まれるファイルすべてが無視される
  • ! マークで始まる行は以降のパターンで示される対象を無視しない


以下のようなファイル構成の場合に、
├ .gitignore
├ a.txt
├ index.html
├ log
│ └ .gitkeep
├ nextdir
│ ├ b.txt
│ └ readme.txt
└ temp
└ temp.html

git add .追跡ファイルを登録してみると、
$ git ls-files
.gitignore
index.html
log/.gitkeep
nextdir/readme.txt

"temp" ディレクトリは無視されている。テキストファイルは無視されているが "readme.txt" だけは追加されている。".gitignore" ファイルも追加されている。

これをコミットし、リモートリポジトリへ push すれば、リポジトリで共有するメンバーすべてで無視することができる。



以下のような構成があり、"log/" 以下はすべて無視したいが、".gitkeep" だけは例外として許可したい場合にどう書くか?
├ log
├ .gitkeep
├ a.log
└ b.log

以下は、間違い。
log/
!log/.gitkeep

ディレクトリ全体を無視した場合、そのディレクトリ内の一部のファイルを無視しないようにすることはできない。
※Git 2.7.0 で機能修正されたが、うまく動かないため、Git 2.7.1で revert されている。

正しくは以下のように設定する。
log/*
!log/.gitkeep





自分の環境だけで無視する


プロジェクトで共通の無視ファイルを準備するには、".gitignore" を使えばよいが、
例えば「自分の環境では、エディタの設定でバックアップファイルの自動作成を有効としているので、自動的にファイルが作られる。このバックアップファイルを無視するようにしたい」と言った場合は、
".git/info/exclude" に記述すればよい。
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~

#エディタのバックアップファイルを無視
*.bak

こうしておけば、リポジトリで共有することはないので、メンバーごとの環境に合わせて無視ファイルを設定できる。





リポジトリに依存せず無視する


上記2つのやり方は、どちらもリポジトリに依存していた。
自分のマシン(PC)では、複数のプロジェクト(リポジトリ)を扱っていて、共通で無視ファイルを設定したい場合、global な gitignore を設定する。
gitignore ファイルを用意し、git config で準備したファイルのパスを設定する。

$ git config --global core.excludesfile $HOME/.gitignore_global


自分の環境設定に依存した無視ファイルを設定できる。".git/info/exclude" だとすべてのリポジトリで設定する必要があるので、自分の環境依存であれば、こちらに設定するほうが良い。




無視ファイルの注意点


無視ファイルで設定する前に、git add して、すでにgit管理下に入ったファイルは、".gitignore" で設定しても追跡対象からは外れない。

そのような場合は git rm で削除してから、追加する。
ファイルを残して、git管理から削除するには、--cached オプションを使う

$ git rm --cached d.txt







まとめ


作業していると後から関連ファイルをまとめておこうと思ったり、ファイル名を統一しようと思ったりすることもある。そのような場合に、gitで管理しているものをどうしたらいいのだろうかと疑問に思うことがあったので整理して再確認した。
ファイルの変更はいつも発生するわけではないが、"gitignore" の設定は必要。こちらのほうが必要な知識だと思う。



gitをインストール
gitサーバーのセットアップ
git ブランチについて
git 変更を一時的に退避 stash
git ブランチを合流するマージ
git ブランチを付け替える
git コミット履歴を変更する
git 変更をリセットする
git リモートでの操作
git リリース準備
git リモートブランチを追加
git チェックアウトをもっと便利に使う
git プロジェクトの構成
git 変更をpatchファイルにする
git コンフリクトに対処する
git 失敗したときの復元
posted by Zorinos at 20:00| Comment(0) | Linux | 更新情報をチェックする

2017年07月21日

git リモートでの操作

リモートリポジトリの操作のために、まずはローカルリポジトリとの関係を整理する。

git_remote01.png
リポジトリファイルやディレクトリの履歴を管理する場所。
ローカルリポジトリ各ユーザごとに、自分のマシン上に配置するリポジトリ。
リモートリポジトリローカルリポジトリとは別の領域に配置されるリポジトリ。複数人で作業する場合、共有マシンやインターネット上に配置されることが多い。ローカルリポジトリに対して異なるマシンである必要はなく、同一マシン上にあってもよい。デフォルトで "origin" と名称がつけられる。
作業ツリーgit管理下に置かれた、実際に作業を行うディレクトリ。
ローカルブランチ作業ツリーと結びついているブランチ。checkout を行うことで作業するブランチを切り替え、作業ツリーの内容を切り替える。
masterブランチ最初にコミットすると作成されるブランチ。デフォルトで "master" と名称がつけられる。
リモートブランチリモートリポジトリ上のブランチを参照するブランチ。『リモート追跡ブランチ』とも言う。
リモート追跡ブランチローカルリポジトリ内にあり、リモートブランチの状態を保持するポインタ。"origin/master" というのは、"origin" というリモートリポジトリにある "master" ブランチを追跡していることを示す。
追跡ブランチリモート追跡ブランチからローカルブランチに checkout すると自動的に作成されるブランチ。リモートリポジトリのブランチと直接のつながりを持つローカルブランチ。
上流ブランチupstream branch。『追跡ブランチ』が追跡するブランチのこと。

参考:

『リモートブランチ』について
「Pro Git Book(日本語訳)」は『リモートブランチ』と『リモートリポジトリにあるブランチ』を両方、『リモートブランチ』として混在して記述しているのではないかと思う。非常に混乱したが「Pro Git Book」の原文や図などから
『リモートブランチ』=『リモート追跡ブランチ』で解釈した。






リモートリポジトリの複製


他メンバーと作業を共有するためにはリモートリポジトリが必要。リモートリポジトリを使うためにはローカルリポジトリに clone する。

$ git clone <リモートリポジトリの場所>


リモートリポジトリの場所は、http,httpsや、SSHプロトコル、同一マシンであればディレクトリパスを指定する。

リモートリポジトリを clone したら、自動的に "master" ブランチを作成し、"origin/master" を追跡するようになる。



ローカルとリモート(リモート追跡ブランチ)両方のブランチ一覧を確認する。
-a オプションを使う。

$ git branch -a

* master
remotes/origin/HEAD -> origin/master
remotes/origin/master

リモートリポジトリを区別するために、"remotes/" が頭に付く。
"remotes/origin/HEAD" は、リモートリポジトリの現在の "HEAD" が存在するブランチを指す。



orign/master ブランチ以外のブランチを複製


git clone では "master" ブランチしかローカルに複製されていない。
他のブランチを確認するには、-r オプションを使う。

$ git branch -r

  origin/HEAD -> origin/master
origin/master
origin/new_branch

確認できたら、追跡ブランチを作れば良い。-b オプションを使う。

$ git checkout -b new_branch origin/new_branch



ローカルリポジトリのブランチを確認してみる。
 git branch -a
master
* new_branch
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/origin/new_branch







ローカルリポジトリの変更を反映


ローカルリポジトリにコミットされた内容を他メンバーへ公開するために、ローカルリポジトリの内容をリモートリポジトリへ反映する。

$ git push <リモートリポジトリ> <ローカルのブランチ名>



試しに git push を引数無しで実行してみる。

$ git push


リモートリポジトリへ反映はできたようだが、警告が出た。
warning: push.default is unset; its implicit value has changed in
Git 2.0 from 'matching' to 'simple'. To squelch this message
and maintain the traditional behavior, use:

git config --global push.default matching

To squelch this message and adopt the new behavior now, use:

git config --global push.default simple

When push.default is set to 'matching', git will push local branches
to the remote branches that already exist with the same name.

Since Git 2.0, Git defaults to the more conservative 'simple'
behavior, which only pushes the current branch to the corresponding
remote branch that 'git pull' uses to update the current branch.

See 'git help config' and search for 'push.default' for further information.
(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode
'current' instead of 'simple' if you sometimes use older versions of Git)

この意味は、git push を引数無しで実行したことによる警告。

git push を引数無しで実行したときの動作が push.default が指定されていないため警告がでている。また、Git 2.0 以降では matching から simple にデフォルトが変わったと教えてくれている。

この警告を表示させないようにするためには、push.default の設定を追加すればよい。
Defines the action git push should take if no refspec is explicitly given. Different values are well-suited for specific workflows; for instance, in a purely central workflow (i.e. the fetch source is equal to the push destination), upstream is probably what you want. Possible values are:

  • nothing - do not push anything (error out) unless a refspec is explicitly given. This is primarily meant for people who want to avoid mistakes by always being explicit.


  • current - push the current branch to update a branch with the same name on the receiving end. Works in both central and non-central workflows.


  • upstream - push the current branch back to the branch whose changes are usually integrated into the current branch (which is called @{upstream}). This mode only makes sense if you are pushing to the same repository you would normally pull from (i.e. central workflow).


  • tracking - This is a deprecated synonym for upstream.


  • simple - in centralized workflow, work like upstream with an added safety to refuse to push if the upstream branch’s name is different from the local one.

    When pushing to a remote that is different from the remote you normally pull from, work as current. This is the safest option and is suited for beginners.

    This mode has become the default in Git 2.0.


  • matching - push all branches having the same name on both ends. This makes the repository you are pushing to remember the set of branches that will be pushed out (e.g. if you always push maint and master there and no other branches, the repository you push to will have these two branches, and your local maint and master will be pushed there).


To use this mode effectively, you have to make sure all the branches you would push out are ready to be pushed out before running git push, as the whole point of this mode is to allow you to push all of the branches in one go. If you usually finish work on only one branch and push out the result, while other branches are unfinished, this mode is not for you. Also this mode is not suitable for pushing into a shared central repository, as other people may add new branches there, or update the tip of existing branches outside your control.

This used to be the default, but not since Git 2.0 (simple is the new default).
引用:git help config

simple現在のブランチを "upstream branch" に push する。ただし "upstream branch" 名が現在のブランチと異なる場合は push しない。
Git 2.0 でデフォルトとなった。
matchingローカルとリモートで同一名称のブランチに全て push する。いままでのデフォルト。
nothing引数が省略された場合は push しない。
upstream現在のブランチに "upstream branch" が設定されている場合に push する。ブランチ名が異なっていても push する。
current現在のブランチをリモートの同一名称のブランチに push する。"upstream branch" が設定されていなくても push される。


git push 操作には、安全性と効率を考えて simple を設定しておく。

$ git config --global push.default simple






ここで、新しいブランチ "new_branch" を作り、リモートリポジトリへ反映してみる。
$ git push origin new_branch

$ git branch -a
master
* new_branch
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/origin/new_branch

新たにリモート追跡ブランチ "remotes/origin/new_branch" が現れた。



さらに、上流ブランチと追跡ブランチの関係を見てみる。
-vv オプションを使う。

$ git branch -vv

  master     18ccb33 [origin/master] master 追加 3
* new_branch 6fd4bd7 new_branch 追加 1

"master" ブランチに対しては、上流ブランチ(upstream branch) "origin/master" が設定されているが、先ほど作った "new_branch" ブランチには、上流ブランチが設定されていない。



この状態で、"new_branch" ブランチを更新し、再度 push する。
ただし、次は引数無しで push してみる。
$ git push
fatal: The current branch new_branch has no upstream branch.
To push the current branch and set the remote as upstream, use

git push --set-upstream origin new_branch

push.defaultsimple を設定したので、上流ブランチが設定されていない "new_branch" ブランチでは、引数無しの push 操作ができないため警告がでている。


"new_branch" ブランチは、リモート追跡ブランチから checkout したものではなく、ローカルで準備したブランチのため、追跡ブランチにはなっていない。追跡ブランチとして使うためには、上流ブランチを設定すれば良い。

$ git branch --set-upstream-to=origin/new_branch new_branch

$ git branch -vv
master 18ccb33 [origin/master] master 追加 3
* new_branch 6fd4bd7 [origin/new_branch] new_branch 追加 1

上流ブランチが設定できたことがわかる。
これで push を引数無しでも実行できるようになった。


上流ブランチを設定する別のやり方もある。
push するときに合わせて指定すれば良い。
-u オプションを使う。

$ git push -u origin new_branch








リモートリポジトリの履歴取得


複数人で作業していると、リモートリポジトリは他メンバーによって、どんどん更新されていく。ローカルリポジトリも最新履歴を取り込む必要がある。
リモートリポジトリの最新履歴をローカルリポジトリに持ってくる操作が fetch

$ git fetch origin


fetch で更新されるのは、リモート追跡ブランチまで。
ローカルブランチはまだ更新されない。

git_remote02.png

リモートリポジトリの履歴をローカルブランチへ反映するためには、マージすればよい。
"master" ブランチへマージするために、"checkout" してから実行する。

$ git merge origin/master








リモートリポジトリの履歴反映


git fetch + git merge することで、リモートリポジトリから履歴を取得し、ローカルブランチへ反映した。
これをまとめて実行するのが、git pull 操作。

$ git pull origin master


ローカルブランチでの作業の区切りが悪い状態で実行してしまうと面倒なことになる。
なので、git pull は使わなくてもいい。git fetch + git merge で事が足りる。

操作が増えるほうが面倒だと思うかもしれないが、良くわからず git pull を実行したことでローカルブランチが大変なことになるぐらいなら、確実に操作できるほうが良い。

リモートリポジトリ、ローカルブランチ、リモート追跡ブランチなどの関係性が整理できていれば、「このタイミングであれば、まとめて実行したほうが楽。だから git pull を使っても大丈夫」だとわかる。それでなければ git pull を使わないほうが安全。





リモートリポジトリからのブランチ削除


役目を終えたローカルブランチを削除するには、-d オプションを使って、

$ git branch -d new_branch



リモートリポジトリに push されているのであれば、リモートリポジトのブランチも削除する。

$ git push origin :new_branch


この操作の意味を解説すると、
push 操作は、次のように、
$ git push origin new_branch

と、<リモートリポジトリ> <ローカルブランチ名> を指定するが、これは push する先のリモートリポジトリ側のブランチ名が省略されている。ローカル、リモートともに "new_branch" ブランチを指定したということで省略されている。

git push は、本来、次のコマンドのように :(コロン)を使って、リモートのブランチを指定する。

$ git push <リモートリポジトリ> <ローカルブランチ名>:<リモートのブランチ名>




話を戻して、リモートブランチの削除する操作は、git push origin :new_branch

これは push するローカルのブランチが指定されていない →「何もないブランチ」を「リモートのブランチ」へ push する → 「存在しない状態に更新」ということになり、ブランチが削除できる。


次のようにして削除することもできる。
こちらのほうが明示的に削除を指定するのでわかり易い。

$ git push --delete origin new_branch








まとめ


複数人で作業するときはリモートリポジトリが必要となる。リモートリポジトリとローカルリポジトリの関係性が理解できていれば、どのようにデータが更新されるのか理解できるので、コンフリクトしても焦らず対応できる。
git push の逆にあたる操作は、git pull ではなく、git fetch 。"push" の反対なので "pull" だと思ってしまうが、"pull" はローカルブランチに影響を与えてしてしまう操作だが、"push"・"fetch" ともローカルブランチに影響を与えない操作。
今回、リモートとローカルリポジトリの関係性を理解できたことで、今後のリモート操作が安心してできる。



gitをインストール
gitサーバーのセットアップ
git ブランチについて
git 変更を一時的に退避 stash
git ブランチを合流するマージ
git ブランチを付け替える
git コミット履歴を変更する
git 変更をリセットする
git ファイルの追跡
git リリース準備
git リモートブランチを追加
git チェックアウトをもっと便利に使う
git プロジェクトの構成
git 変更をpatchファイルにする
git コンフリクトに対処する
git 失敗したときの復元
posted by Zorinos at 20:00| Comment(0) | Linux | 更新情報をチェックする