git, github
自分で作るコードで、公開できるものはなるべくgithub経由で管理したい。そうでなくてもbitbucketとかが使える。さらにそうでなくても GitLab, 自前サーバ, …。 そうすることでどこでも開発できるしバックアップにもなる。と、言うことでその手のリポジトリ管理に使えるtipsをまとめる。
git 管理のワークフロー
すべてのコミットを一直線で管理すると、一度の機能を開発していたかわからない。ブランチを切って、そのブランチの履歴を残すことで、あとからブランチでどんな開発をしていたか把握したい。 github でもプライベートの git repository でも使える方法にしたい。
様々なワークフロー、ブランチ戦略
POSTD - GitLab flowから学ぶワークフローの実践 などを参考にすると良い。有名なのは以下3つかな:
git flow: スタンダード?思想は1つ1つ聞けば分かるし納得感はある。でも複雑だと言われていて、たしかに遵守するのは骨が折れそう。
github flow: 小規模チームやプロダクト、頻繁なリリースが求められるプロジェクトに向くと言われる。
gitlab flow: これも注目。
3つの対比をうまく残したいな。
整理しきれていない tips (ワークフローによっては当てはまらないだろう)
- master branch をいつでもデプロイ可能なブランチとする
- 特定の開発トピック用ブランチを切って開発
- そのブランチを master に
--no-ff
でマージする - マージし終えたブランチは削除
- 詳細は github-flow を参考に。
- チュートリアル: これだけは押さえておきたいGitHub Flowの基礎
- github-flow と git-flow のわかりやすい解説: 【図解】git-flow、GitHub Flowを開発現場で使い始めるためにこれだけは覚えておこう
git-flow コマンド実践
local での開発
- リポジトリの取得
- 初回:
git clone <repo> && cd <repo_dir>
- ほか:
cd <repo_dir> && git pull
- 初回:
- branch の確認
git branch
: ブランチの一覧確認、現在のブランチの確認
- branch 作成
git branch <branchname>
: でブランチ作成
- branch 切り替え
git checkout <branchname>
でコミットするブランチを切り替える (チェックアウトする)git checkout -b <branchname>
: コレで一発で作成&移動というのも場合によってアリ
- コード書く
- ステータス確認
git status
- 差分確認
git diff
- 対象をステージして、コミット
git add <files>
git commit -m "<commit_message>"
git commit --amend
: 必要ならこのコマンドでメッセージを修正可能
- リモートリポジトリにプッシュ
- そのブランチを初めてプッシュするとき:
git push --set-upstream origin <branchname>
- 2回目以降:
git push
- そのブランチを初めてプッシュするとき:
- リポジトリの取得
github, gitlab, サーバ上の bare-repository で
- no fast-forward なマージを実施
- github, gitlab等なら web UI で プルリク + マージ。
- bare-repo
cd <pare-repo-dir>
git checkout master
: merge 先 (通常は master) のブランチに移動git merge --no-ff <branchname>
: no fast-forward でマージ
- branch を削除
- no fast-forward なマージを実施
local
- マージしたブランチを削除
- master を pull
pull じゃなくて fetch & merge 使えという話
- マスターに置いていかれた (branch切って開発している間に、別のbranchがマージされてマスターが進んでしまった場合)
-
git fetch origin
- master でgit merge origin/master
リポジトリの新規作成方法
色々パターンあるよね
ローカルのプロジェクトから github にリポジトリを新規作成する
localで管理されているプロジェクトを用意する。
おもむろにgithubにsign inしてrepositoryを新規作成する
仮にユーザ名がgeorgeで myproject というプロジェクトを作ったすると、ローカルからのpushは下記のような感じになる。
$ git remote add origin git@github.com:george/myproject
- これで以下のように-u付けてpushすれば次回以降もpull,pushが楽
$ git push -u origin master
Warning: Permanently added the RSA host key for IP address '192.30.252.130' to the list of known hosts.
Counting objects: 465, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (442/442), done.
Writing objects: 100% (465/465), 84.71 KiB | 0 bytes/s, done.
Total 465 (delta 244), reused 0 (delta 0)
To git@github.com:take4mats/revreport
* [new branch] master -> master
完了!
ローカルのプロジェクトから 自分のサーバにリポジトリを新規作成する
シナリオ: よくサーバ上の本番環境でアプリを書いてしまっているが、サーバにベアリポジトリを設置→ローカルにcloneして開発→ベアリポジトリにpush→本番環境でpullして反映、としたい
(サーバ側) もともとあるgit project:
~/myproject
(サーバ側) ベアリポジトリの集積場を作成:
$ mkdir ~/repository
(サーバ側) ベアリポジトリを作成:
$ git clone --bare ~/myproject ~/repository/myproject.git
(サーバ側) 元プロジェクトにリモートリポジトリを登録(ベアリポジトリを指定):
$ git remote add origin /home/user/repository/myproject.git/
- 同じサーバ内なので/home/…となっていますが、リモートサーバを指定するときは、パスの先頭に
git://
とかssh://
って感じ
- 同じサーバ内なので/home/…となっていますが、リモートサーバを指定するときは、パスの先頭に
リモートリポジトリの登録を確認:
$ git remote -v origin /home/user/repository/myproject.git/ (fetch) origin /home/user/repository/myproject.git/ (push)
origin masterに
git push
だけで push してくれるよう、 upstream を登録する:$ git push -u origin master
- .git/configに以下が追加されているはず
[branch "master"] remote = origin merge = refs/heads/master
(クライアント側) ベアリポジトリからクローンしてプロジェクトを開始!
$ git clone ssh://user@remotehost.com/home/user/repository/myproject.git
(クライアント側)
git remote -v
してみるとちゃんとベアリポジトリがリモートリポジトリに登録されているのがわかる
git config を プロジェクトごとに 切り替える方法
個人で複数アカウント持っていたり、お仕事で使っていたりで、アカウント切り替えたいっていうこともあるよね。きっと。
グローバルに入れる設定は以下だけど、
git config --global user.name "defaultname" git config --global user.email "default@example.com"
各リポジトリ(ディレクトリ)で以下を打てばそっちが優先されるよ
git config user.name "projname" git config user.email "proj@example.com"
1つ1つのディレクトリに profile 個別指定よりも、「ある親ディレクトリ(仕事dirなど)配下の git repository は一律仕事の profile を指定したい」満たないことも出来ます。
- ~/.gitconfig で
[includeIf "gitdir:~/shachiku/"] path = ~/.gitconfig_shachiku
- そして ~/.gitconfig_shachiku を作ってこんな風にユーザプロフィールを書く、と。
[user] name = shain-a email = zangyo.daisuki@black.com
- この方法の注意点: .gitconfig で user profile が書かれているとグローバルにその設定が聞いてしまうので、ディレクトリごとにならない。触るディレクトリに対してはすべて定義が必要。オススメは、 homedir 直下でプロフィールごとに sub dir をきって、それごとに profile を定義する。これで漏れがなくせそうな気がします。 (/usr/local/hoge で作業?知らん)
- ~/.gitconfig で
hooks で git コマンド使うときは --git-dir=.git
とかしないとだめ
- 例: bare-repository に書いた post-receive で、 サーバサイドの repository で pull するちなみに無いと
#!/bin/sh cd /home/fnobi/sites/my-website git --git-dir=.git pull
fatal: Not a git repository: '.'
と怒られました
branch 操作系
新しいリモートブランチをローカルに持ってくる
- pull は現在の local branch にマージされてしまうから使ってはいけない!ので代わりにこうする:
$ git branch <newbranch> origin/<newbranch> # もしくはこうする: $ git checkout -b <newbranch> origin/<newbranch>
- pull は現在の local branch にマージされてしまうから使ってはいけない!ので代わりにこうする:
新しいローカルブランチをリモートにプッシュする
- まずローカルでブランチを切ってチェックアウト
$ git branch <newbranch> <origin/branchname> $ git checkout <newbranch> # もしくはこうする: $ git checkout -b <newbranch> <originalbranch like origin/master>
- 続いて変更を加えてコミット
git add <updated files> git commit -m '<commit message>'
- 新しいブランチを作るようにプッシュする
git push -u origin <newbranch>
- まずローカルでブランチを切ってチェックアウト
ローカルブランチをリモートのブランチ名を明示してプッシュする
- ローカル master ブランチを originにある上流ブランチに push する。
git push origin master
コロン
を使えば、ローカルブランチだけでなく、リモートブランチ名を明示できる# ローカルのブランチ hoge のコミットを、origin 上の dev ブランチに push する git push origin hoge:dev
- ローカル master ブランチを originにある上流ブランチに push する。
local/remote branch を rename する
# ローカルブランチ名の変更 $ git branch -m hoge fuga # あるいは、今いるbranchをリネームするならこれ: $ git branch -m fuga # リモートブランチを削除 $ git push origin :hoge # ブランチ名を変更したローカルbranchをプッシュしよう $ git checkout fuga $ git push origin fuga
pager を個別指定したい
自分は pager がデフォルトで less になっているんだけど、そのせいか git log -1
も git branch -a
も git config --list
もチルダで画面を埋め尽くされてしまう。
と思ったらできた。
sh git config --global pager.{branch|log|config など} cat
git log の表示形式をカスタマイズ
うまく使いこなしたいところだが、今のところはコピペで。
イケテルと思ったサンプル2つ (decorate, pretty のパターンそれぞれ)
# こっちのほうが簡潔だし使い回しやすい git log --graph --name-status --decorate --oneline # こっちは色とcommitterが見やすいけど開業が余計でうまく消せてない git log --graph --name-status --pretty=format:"%C(red)%h %C(green)%an %Creset%s %C(yellow)%d%Creset%n"
alias に登録して使おうかな
git config --global alias.log-decorate 'log --graph --name-status --decorate --oneline' git config --global alias.log-pretty 'log --graph --name-status --pretty=format:"%C(red)%h %C(green)%an %Creset%s %C(yellow)%d%Creset%n"'
今後
- commit, timestamp, committer, message \n changed files が並んでいて、グラフっぽくなっていて、色もほどほどに使われたものにしたい
参考
rebase
あるブランチが発生(枝分かれ)した、そのもと (base) となるコミットを変更すること。
例: dev ブランチが master ブランチから作成され、その後 master ブランチが更新されていたとして、その更新に dev が追いつくときは以下のような感じになる:
git checkout dev
git rebase master
もしそこで conflict が発生すれば、以下のような感じで解決する:
# conflict が発生しているファイルを手動で更新して解決
git [ add | rm ] <そのファイル>
git rebase --continue
conflict は commit 履歴の単位で繰り返しチェックされるので、何度もこれを繰り返すことがあり得る。注意して辛抱強くやる必要がある。つまり、なるべくやりたくない。
rebase -i でコミットをまとめる
サルでもわかるGit入門 - 5. rebase -i でコミットをまとめる にあるとおりだが、
# 直近3つのコミットをまとめる
git rebase -i HEAD~3
# すると vim で以下のように対象コミットが並んで出る
pick ab82f29 del c
pick 58aae43 add a
pick 3a2b1be add b
# 消したいものを pick の部分を s に置き換える。一番古い commit は残す必要がある。
e ab82f29 del c
s 58aae43 add a
s 3a2b1be add b
# これで保存・終了する
# rebase を再開
git rebase --continue
# コミットのコメントの編集画面に再度入る。ここでコミットを1つにまとめる分、対応するようにコメントを書き直すと良い
del c, add a,b
# これで完了
これと git push -f
を組み合わせれば、リモートリポジトリに push してしまっている細かい commit も綺麗にまとめることができる (ただし push -f
は慎重に。共同編集している場合は勧められない)
conflict 解消の一手順
- 前提
- master と feature で conflict が発生し、手動で編集しないと feature をマージできない
- どうも master のほうが新しいものも多い
- 対応
- 絵でいうとこんな感じ (作図途中)
- コマンド
# B の位置で、まず feature に master での更新を取り込もうとする git checkout feature git merge master # conflict を解消しようとする vim hogehoge # 一人で難しければ一度 commit してしまう git add . git commit -m 'merge to catch up' #=> C git push # その後に feature の古い commit と diff 取りがら直してもらう #=> D # その後 feature => master のマージの PR を作成 #=> E
stash で作業を一時保存する
- 動機: master で作業してしまっていることに途中で気づく、その作業をリセットしてやり直しはしたくないが、別ブランチ (feature/hoge) で commit したい。
- 解法 (commit 前)
(git reset などで変更したファイルを git add する前の状態にする) git stash save git stash list #=> stash@{0}: WIP on master: <HEADのコミット情報> git checkout feature/hoge # コミットしたい先の branch git stash pop stash@{0}
- 参考: [Git]誤ったブランチで実施した変更を正しいブランチに移動する | Developers.IO
理論
お勉強に
ブランチの種類 (local, remote, tracking)
- local branches: 自分がまさに編集する対象のブランチ
- remote branches: リポジトリ (github 等) 上にあるブランチ。
origin
とかよくついている - tracking branches: ローカルにあって (なのでローカルブランチの一種)、リモートブランチの状況を追跡するためのブランチ。
git fetch origin
とすると全てのリモートブランチの最新状況をとってくる。
git fetch
リモートリポジトリの情報を取得する。手元のブランチに反映するにはマージが必要
git show
前回のコミットの差分を表示してくれる.
Fast-Forward
merge の時に ff しないように予め設定しておく主義もあるらしいですよ。
git config --global --add merge.ff false
git config --global --add pull.ff only
git tag
- タグを打つ:
git tag <tag_name> [-m <message>] [<commit(Default latest)>]
- タグの確認:
git tag
- タグの削除:
git tag -d
- タグの共有:
git push origin <tag_name>
- tagは作成しただけではリモートには反映されないのでpushしてやる必要があるんだよ!!!