何はともあれ : Git超絶まとめ
名称
-
origin
:git clone http://HOST/PROJECT
としたときのURLの別名。 -
origin/BRANCH1
: リモートのBRANCH1の先頭コミットIDを指す別名、とは言え最新化しないと古くなる。追跡ブランチ。git branch -a
で一覧したときは「remotes/origin/BRANCH1
」と表記される。 -
HEAD
: 現在チェックアウトしているブランチの最後コミットを指すコミットIDの別名。git log
の先頭。git reset
やgit reset --hard
で移動させられる。
ユーザ設定
-
git config user.name USERNAME
(普段はgit config
で設定する) git config user.email MAILADDRESS
git commit --amend --author="USERNAME <MAILADDRESS>"
設定ファイル
- リポジトリ単位 :
.git/config
(git config -e
で開く) - ユーザ単位 :
~/.gitconfig
(git config --global -e
で開く) - システム全体 :
(app-path)/etc/gitconfig
(git config --system -e
で開く)
各スペースの状態の確認
-
git status
: ワークツリー(赤色)とインデックス(緑色)が見れる。コミットされたもの(ローカルリポジトリ)は見れない。 -
git log
: コミットされているものを見る。最も打つコマンドがstatus
とlog
。
何かするたびに打ってその時その時のコミットIDをコンソールに残しておく。それらのコミットIDが様々な事故から救ってくれる。 -
git diff
: ワークツリーとインデックスとの比較。何をaddしようとしているかを知るときに便利。 -
git diff --cached
: インデックスとHEADの比較。何をコミットしようとしているか知るときに便利。 -
git diff origin/BRANCH1 HEAD
: リモートブランチをfetch
してから行うと、何をpushしようとしているかが分かる。
ログの確認
-
git log --decorate --graph --branches --tags --remotes
(おすすめ) git log --oneline --decorate --graph --branches --tags --remotes
git log --graph --date=short --decorate=short --pretty=format:'%Cgreen%h %Creset%cd %Cblue%cn %Cred%d %Creset%s'
git log -3
- 以前はエイリアス必須だったが、現在のGitBashは--decorateがなくても色が付くため、エイリアスなしでも運用できる。
リモートブランチからの取り込み方
git clone http://HOST/PROJECT -b BRANCH1
-
git pull origin BRANCH1
有名なコマンドだが使う必要がない。ブランチ名を指定せず実行するとローカルの全ブランチに対して行うため危険で簡単には元に戻せない。またdiff
せずにmerge
するため予期しない変更を取り入れることになる。pull
よりfetch
の方が安全で便利。fetch
してdiff
した上でmerge
やrebase
など次の行動を判断すれば良い。 -
git fetch origin
: ローカルの追跡ブランチであるorigin/BRANCH1, origin/BRANCH2...を全て最新化する。 (便利) -
git fetch origin BRANCH2
: origin/BRANCH2を最新化し、FETCH_HEADに入れる -
git fetch origin BRANCH3:BRANCH3
: ローカルにorigin/BRANCH3とBRANCH3の2つを作る。新しくブランチを落とすときはこれ。「git fetch origin BRANCH3 && git branch BRANCH3 origin/BRANCH3
」と同等。
最新の状態を取り込む
-
stash
してstash pop
すると競合したときstashを見失いがち。別ブランチに退避させると安心。git checkout -b BRANCH1_BK
git add .
git commit -m "MESSAGE"
git checkout BRANCH1
git fetch origin BRANCH1
git rebase origin/BRANCH1
リモートへの反映
-
git push origin BRANCH1
(ブランチ名を指定せず実行するとローカルの全ブランチがリモートに飛ぶため非常に危険) - 自分のコミットがfowardされてツリーに繋がる場合もある(
push
したローカルブランチが最新の状態ではない可能性もある)ため、push
後にfetch
しておくといい。
編集途中でのブランチの切り替え方
-
stash
してstash pop
すると競合したときstashを見失いがち。別ブランチに退避させると安心。-
git add . && git commit
で一旦コミットしてしまう。 (stash
の代わり) git checkout BRANCH2
-
git checkout BRANCH1 -- FILEPATH1 FILEPATH2
で編集していたファイル達を持ってくる。 - 「
git checkout BRANCH1 && git reset HEAD^
」で元に戻しておく。 (またはgit branch -f BRANCH1 COMMITID
)
-
コミットを別のブランチに付け替える
- マージするとツリーが繋がっちゃうけどそれは避けたいとき。
-
git reset HEAD^
で1つ前のコミットに移動し、HEAD
をコミットしようとしている状況にする。 git checkout BRANCH2
git commit -m "MESSAGE"
-
インデックスへの追加と削除
-
git add .
: カレントディレクトリ配下。普段はgitディレクトリの頂上にいるのでこれを使う。 -
git add -A
: gitディレクトリ全て。(あまり使わない) git reset FILEPATH1
git reset FILEPATH1*.txt
-
git reset /DIRNAME/
(「/DIRNAME/*」では無反応)
ローカルの変更の戻し方
-
git reset --hard HEAD
ゴミが残ったらgit clean -df
する。ただしその際にコミットできない空ディレクトリを消してしまうため、必要に応じて.gitkeep
を置く。 - HEADを色々と移動させていてリモートブランチのHEADに戻したい場合は
git reset --hard origin/BRANCH1
-
git checkout origin/BRANCH1 -- FILEPATH1 FILEPATH2 FILEPATH3
: 指定ブランチを元にファイル単位で戻す。 -
git branch -f BRANCH1 COMMITID
:BRANCH1
をCOMMITID
で上書き。今いるブランチ以外で使えて非常に強力。 - 直近の4つのコミットのコミットコメントやauthorやemailを直したい場合
-
git rebase -i HEAD~4
でvimを開いて、先頭を全てe(edit)
にして:wq
するとrebaseモードになる。 -
git commit --amend -m "MESSAGE" --author="user.name <user.email>" && git rebase --continue
を4回やるとrebaseモード終了。
-
リモートの戻し方
-
git reset COMMITID && git push -f origin BRANCH1
個人用のリモートブランチならバンバン使っていい。origin/BRANCH1をfetchしていなければ--force-with-lease
で他の作業者のコミットを消してしまう危険性を下げられるらしい。
それより怖いのは全く関係のないブランチをミスで上げてしまっても気付かないこと。そのときに備えて必ず元のリモートヘッドのコミットIDをどこかにバックアップしておく。コミットIDは、push
前にgit log
で出力してもいいし、force-push時の+ 1234abc...5678def main -> main (forced update)
というログでもいい。(左側がオリジナルのコミットIDになる)
JenkinsがGitHUB上でforce-pushをして元に戻せなくなったことがある。GitHUB上ではreflog
ができない(元のコミットIDを見失うと元に戻せなくなる)。著名なプロジェクトだったためか、最終的にはGitHUB側が対応をして難を逃れた。
参考 : 本の虫: Jenkinsの開発者、間違えて一ヶ月前のローカルレポジトリをgit push --forceしてしまう
それを受けて(?)、GitHUBではブランチごとへのforce-pushを禁止できるようになった。(masterのみ禁止、など)
マージと競合の解消
-
git merge BRANCH2 --no-ff
--no-ff
を付ける。--no-ff
を付けないとコミットログにマージの痕跡が残らない。痕跡を残したくないと思っても結局conflictが起こったものはマージ跡が残り、残ったり残らなかったりカオスな状態になる。
--no-ff
を付けるというルールを採用するプロジェクトもある。そのルールで良い。 -
git merge --abort
でマージをやめる (mergeモード中はブランチ名が(master|MERGING)
になる、それを抜ける) git checkout --ours FILEPATH1
-
git checkout --theirs FILEPATH1
一旦theirsをcheckoutして、他人の変更を基本にして、自分の修正リビジョンとdiffを取りながら修正するのが吉。diffは具体的にはgit diff HEAD
コマンドで行う。git diff
だと作ろうとしているマージコミットとのdiffになる(?)) -
git merge --continue
でマージの内容を決定する (エディタ等でconflictを解消した後にやる)
部分操作
-
git add -p FILE
+e
モード : エディタでファイルの変更の一部だけを選んでインデックスに追加する。複数の機能変更を同じファイルにしてしまった場合に利用する。意味のあるコミット単位にするための意識高い系コマンド。 -
git cherry-pick
: 他ブランチをmerge
したくはないが、そのブランチの一部コミットはmerge
したい場合に利用する。merge
の痕跡が残らず、ツリーがカオスになるためおすすめできない。必要になった時点で何かがおかしい。
タグをつける
git tag -a TAGNAME -m "MESSAGE"
git log -1 --decorate
git push origin BRANCH1 TAGNAME
タグを消す
git tag -d TAGNAME
git push origin :TAGNAME
リポジトリのプロジェクトをcloneして、それを別のリポジトリにpushする
git clone git@github.com:XXX.git - b BRANCH1
git remote remove origin
git remote add origin git@github.com:YYY.git
git push origin BRANCH1
FETCH_HEADからbranchを作成
-
git branch BRANCH1 FETCH_HEAD
。普段はgit branch BRANCH1 origin/BRANCH1
で行う。
別リポジトリからブランチを取り込む
git fetch 別リポジトリURL BRANCH1:BRANCH1
複数行のコミットコメントでコミットする
-
-m
を付けずにgit commit
。viが開き、wq
すればコミットされ、q!
すればコミットされない。git log
で他のメッセージを参照しつつコミットコメントを書けない上に、oneline指定のgit log
に出てこなかったりするため使い勝手が非常に悪い。gitはちょくちょくコミットするものなので、複数行書く必要がある時点でコミット単位が粗いとも言える。長文コメントが必要なコミットになった場合は、別ブランチを切って--no-ff
でマージしてそれを知らせたりタグを切ったりして、大きな分岐点になったことを残しておくと良いかも知れない。
rebaseの注意点
-
有名なコマンドだが、基本的には
reflog
でしか戻せずforce-pushと併用されることが多いちょっと危ないコマンド。 -
戻すとき
-
git log
などで残しておいたコミットIDでreset --hard
。一応reflog
で上から3, 4番目辺りに出てくる「reset: checkout BRANCH1」と書かれているコミットIDにreset --hard
しても戻せるが、rebase
の後に色々操作したりすると行方不明になる。
-
-
使うとき
-
ローカルで作成した自分の作業ツリーを作り直したりコミットコメントを修正したりするとき。(
rebase -i
) -
ローカルで作成した自分の作業ツリーが分岐した場合にそれを一本化してリモートにpushしたいとき。
-
リモートから
fetch
してきたものを一番下にして自分のツリーをそこから始めたいとき。 -
リモートツリーを一本化する場合。force-pushと歴史改変が行われる。以下が参考になる。
3.6 Git のブランチ機能 - リベース
マージとリベースではどちらがいいのか。
お察しのとおり、単純にどちらがよいとは言い切れません。
Git は強力なツールで、歴史に対していろんな操作をすることができます。しかし、チームやプロジェクトによって、事情はそれぞれ異なります。
あなたは既に、両者の特徴を理解しています。あなたが今いる状況ではどちらがより適切なのか、それを判断するのはあなたです。
一般論として、両者のいいとこどりをしたければ、まだプッシュしていないローカルの変更だけをリベースするようにして、 歴史をきれいに保っておきましょう。プッシュ済みの変更は決してリベースしないようにすれば、問題はおきません。
-
ローカルでプロジェクトを作ったので、それを元にGitHUBのリポジトリを作りたい
- 普通この順番。まずはGitHUBにリポジトリを切ってcloneしてから、ローカルで新規プロジェクトを作成するという順番もあるにはあるが。
- GitHUBに空のリポジトリを作成する。今はGitHUBがgitignoreを生成してくれるので便利 (以前はgitignore.ioなどを利用していた)
- ローカルに作ったプロジェクトのトップディレクトリで
git init
してgitプロジェクトにする。 - そこで
git remote add origin git@github.com:xxx/yyy.git
してGitHUBで作成したリポジトリのURLを登録する。 -
git fetch origin main:main
でGitHUBリポジトリを落として来る。 (今のGitHUBのデフォルトのブランチはmainになっている) (Git Bashもmainになっている場合は別名でアレコレすれば良し)- 「Please make sure you have the correct access rights」が出た場合 : ローカルで
ssh-keygen -t rsa
してとりあえず全部Enterを押した後、「~/.ssh/id_rsa.pub」の内容をコピーしてGitHUBのSettings -> SSH keysに登録 - 「push declined due to email privacy restrictions」が出た場合 : GitHUBのSettings->Emailからprivate設定を公開に変更 (迷惑メールがわんさか来るため危険)
- 「Please make sure you have the correct access rights」が出た場合 : ローカルで
-
git checkout main
でfetch
したブランチに移動する。 (GitHUBで作成した初期ブランチはmainブランチを想定する) - 念のため
git log
でGitHUBで行ったinitial commitが存在するか確認する。 - 念のため
git status
でローカルで作成したプロジェクトが赤くなっている(ワークツリーにいる)ことを確認する。 - コミット前に
git config user.name USERNAME
とgit config user.email MAILADDRESS
でAuthorを設定しておく。 -
git add .
してgit commit -m "MESSAGE"
してコミットログを作成する。 - 最後に
git push origin main:main
でGitHUBにpush
する。 (GitHUBで作成した初期ブランチはmainブランチを想定する) (-u
オプションがあるとoriginを付けなくても良くなるため初回のpush
で推奨されているが、個人的には必要性を感じない) - GitHUB上で無事に
push
されていることを確認して、めでたしめでたし。
実戦で使用しているGitBash用エイリアス (オリジナル)
vim ~/.bash_profile
alias a='git add'
alias b='git branch'
alias com='git commit'
alias coma='git commit --amend'
alias ch='git checkout'
alias st='git status'
alias d='git diff'
alias dc='git diff --cached'
alias l='git log --decorate --graph'
alias push='git push'
source ~/.bash_profile