何はともあれ
ブランチ戦略
gitブランチ戦略を担当するときは個人名を入れた「個人用ブランチ」をお勧めしている。
(例:feature/aaa/aaa_bob、feature/aaa/aaa_john)
気軽にcommitできるのがgitの最大の売りだが、気軽にpushすると「ビルドが通らない」といった迷惑に繋がる。
自分だけのブランチがあれば気軽にpushできるし、force pushも気軽にできる。他ブランチが事故っていてもマイペースに開発できる。
ずっと残す必要はなく、featureが完成してdevelopにマージされたタイミング辺りで消すといい。(ローカルには残しておくのがおすすめ)
コミットIDの別名
-
origin/BRANCH1: リモートのBRANCH1の先頭コミットIDを指す。リモート追跡ブランチ。最新化しないと古くなる。git branch -aで一覧したときは「remotes/origin/BRANCH1」と表記される。 -
HEAD: 現在チェックアウトしているブランチの先頭コミットを指す。git logの先頭でありgit resetで移動させられる。 -
HEAD^: HEADの1つ前のコミット。^が1つ前を意味する。 -
aaaaaaaa^:aaaaaaaaというコミットIDがあったとき、それに対しての前回コミットをaaaaaaaa^で参照できる。^はかなり便利。
ワークツリーとインデックスの呼び名 (色々あって紛らわしい)
- ワークツリー = Working Tree = 作業ディレクトリ = Unstaged Changes
- インデックス = Staging Area = ステージング領域 = Staged Changes
ユーザ設定の方法 (ユーザ名とメールアドレス)
-
git config user.name USERNAME(git config -eで設定してもいい) git config user.email MAILADDRESS- 直前のコミットの情報を直したい場合は
git commit --amend --author="USERNAME <MAILADDRESS>"(さらにさかのぼる場合はgit rebase -i)
Gitの設定ファイルが実際にどこにあるか
- リポジトリ単位 :
.git/config(git config -eで開ける) - ユーザ単位 :
~/.gitconfig(git config --global -eで開ける) - システム全体 :
(app-path)/etc/gitconfig(git config --system -eで開ける)
各スペースの状態の確認 (手癖で使うやつ)
-
git status: ワークツリー(赤色)とインデックス(緑色)が見れる。コミットされたもの(ローカルリポジトリ)は見れない。 -
git log: コミットされているものを見る。最も打つコマンドがstatusとlog。 -
git diff: ワークツリーとインデックスとの比較。何をaddしようとしているかを知るときに使う。 -
git diff --cached: インデックスとHEADの比較。何をコミットしようとしているか知るときに使う。
ログの確認
-
git log -3: 一番よく使う。直近の3つのコミットを確認。何かするたびに打って、コンソールに残したりログに残しておくと、それらのコミットIDが様々な事故から救ってくれる。 -
git log BRANCH1: 指定したブランチのログを見る。 -
git log -- FILEPATH1: ファイル別のログを見る。 -
git log --author="USERNAME": 特定のユーザ名で絞ったログを見る。 -
git log -p: ログにdiffも出す。 -
git log aaaa..bbbb --author="USERNAME" -p -- FILEPATH1:
合わせ技。あるリビジョンからあるリビジョンまでに特定ユーザが特定ファイルに対して入れた修正のdiffを全て表示する。 -
git log --graph --color=always --pretty=format:'%C(yellow)%h%Creset %C(green)%ad%Creset %C(cyan)%an%Creset %C(auto)%d%Creset%n%s' --date=format:'%Y/%m/%d %H:%M' | less -SR: オリジナルオプション。誰と誰が何日の何時にコミットしているのかが見える。GUI風。使うならエイリアス必須です。
インデックス(ステージング領域)への追加と削除
-
git add .: カレントディレクトリ配下 (普段はgitディレクトリの頂上にいるのでこれを使う) -
git add -A: gitディレクトリ全て (ほとんど使わない) -
git reset FILEPATH1: インデックスから指定のファイルだけをワークツリーに戻す -
git reset FILEPATH1*.txt: インデックスから指定のファイルだけをワークツリーに戻す (複数版) -
git reset /DIRNAME/ディレクトリごと戻す (「/DIRNAME/*」では無反応) git resetにはHEADを移動させる機能も持つため、ブランチ名を指定すると違う挙動になるので注意
HEADを移動させるときのresetの使い分け
-
git reset:git reset --miexedと同等。reset先までの変更をワークツリーに入れる。(ワークツリーに何か入っていれば混ざる)
修正し直して、addし直して、という風にやり直したいときに使う。 -
git reset --soft:reset先までの変更をインデックスに入れる。
git rebase -iで全てsquashしたいときだとこちらの方が使いやすい。
後述の「git diffとPRのdiffが違う」現象も避けられたりと、役立つ場面が多い。
リモートブランチからの取り込み方
-
git fetch origin: リモート追跡ブランチであるorigin/BRANCH1, origin/BRANCH2...を全て最新化する。(これを使うときはダウンロード容量に注意) -
git fetch origin BRANCH1: origin/BRANCH1を最新化し、FETCH_HEADに入れる。 -
git fetch origin BRANCH1:BRANCH1: origin/BRANCH1を最新化し、BRANCH1に入れる。よく使う。 -
「とにかく最新化したい!自分が修正したファイルも全部捨てていいから!」というときは
fetchしてからreset --hard FETCH_HEADとclean -dfを打つ。 -
git pull origin BRANCH1
有名なコマンドだが使わなくてもいい。pullを「最新化」だと思っている人も多いが、実際はfetchしてからmergeまたはrebaseをするコマンド。
pullの動作は普通はmergeだが、設定でpull.rebase=trueにするとrebaseになる。
pullはFETCH_HEADの状態を確認せずにいきなりmergeまたはrebaseするため、予期しない変更を取り入れたり、思わぬconflictを起こす恐れがある。
おすすめできないコマンド。そういう記事もある。Git初心者の頃わからなかった「pullするな」の意味
じゃあどうすればいいのかは下記参照
pullを使うなというなら、どうしろというのか
- 最新を見たいだけなら
fetchしてlog FETCH_HEADなど - 最新の状態を取り込みたかったら
fetchしてからmerge FETCH_HEADやrebase FETCH_HEADなどの選択をする。 -
一番多いのは、自分のブランチをローカルブランチに退避させておいて、
元のブランチをfetch origin BRANCH1:BRANCH1 -fやreset --hard FETCH_HEADなどでリモートと一致させて、そこに自分のブランチをmergeすること。 -
rebaseもいいが、conflictが多段にならないところが上記のやり方のいいところ。 -
stashは局所的には便利だが、一度別のローカルブランチを作ってそこにcommitする方が安全。(conflictした場合などに後戻りが楽) -
pullの使いどころは「触っていないブランチ」を、それこそ「最新化」するとき。(masterで作業していないmasterを落としておきたい、みたいなとき) - ブランチ名を指定せずに
pullを実行すると、ローカルの全ブランチに対して実施されるため危険。(大量にconflictを起こしたりするとぐちゃぐちゃになる)
最新にマージしたいわけではないが、自分の修正を最新の状態の上に乗せたい…と感じたとき
-
commitしたくなくてもcommitする。コミットメッセージは適当でいい。そして上記のようにする。
その後、reset origin/BRANCH1とやれば願いが叶う。適当なcommitもなくなる。めでたしめでたし。
コミットはしたくないが手元の状態を一旦残したいと感じたとき
- とりあえず
addしてcommitしてreset HEAD^して戻すといい。
そのときのコミットIDを控えておけば、その後アレコレした後でも、そのときの状態に戻せる。 - 何がなんでもコミットしたくない場合、後述する「退避シェル」を使うと安心できるかも。
リモートへの反映
-
git push origin BRANCH1(ブランチ名を指定せず実行するとローカルの全ブランチがリモートに飛ぶため非常に危険) - リモートブランチを消すときは
git push origin :BRANCH1。思っている以上にお手軽。大切なブランチは権限制御しておいたほうが良い。- 最近ではこのコマンドは玄人志向になっていて、
git push origin --delete BRANCH1が使われているらしい。
- 最近ではこのコマンドは玄人志向になっていて、
別のブランチから特定のファイルだけ持ってきたいとき (マージしたいわけではないとき)
-
git checkout BRANCH1 -- FILEPATH1 FILEPATH2 FILEPATH3
ややこしいがcheckoutはブランチ切り替え以外に、別ブランチからファイルを持ってくる機能がある。
ローカルの変更の戻し方
-
git checkout HEAD -- FILEPATH1: ファイル別に戻したいとき。指定したファイルがHEADのファイルに置き換わる。(修正前に戻る) -
git reset --hard HEAD && git clean -df: HEADまで戻して未追跡ファイルも消したいとき (つまりgit statusを空っぽにしたいとき)
これをやるより全てaddしてcommitしてそのリビジョンをメモってからreset --hard HEAD^した方が後悔が少ないと思う。
注意点としてclean -dfは空ディレクトリを消すため、空ディレクトリを残しておきたいときは.gitkeepを置く。 -
git reset --hard origin/BRANCH1: リモート追跡ブランチと同じ状態にする。注意点としてfetchしてなければ追跡ブランチは古くなる。
コミットコメントやauthorやemailを直したい場合
- コミットコメントを直す場合は
git commit --amend -m "MESSAGE" - authorやemailも直すなら
git commit --amend -m "MESSAGE" --author="user.name <user.email>" - 深掘りして直す場合 (たとえば直近の4つに対して行う場合)
-
git rebase -i HEAD~4でvimを開いて、先頭を全てe(edit)にして:wqするとrebaseモードになる。 -
git commit --amend -m "MESSAGE" --author="user.name <user.email>" && git rebase --continueを4回やれば無事完了!
-
リモートの戻し方
-
git push --force-with-lease origin BRANCH1
いわゆるforce push。個人用ブランチなら遠慮せず使っていい。
--force-with-leaseを付けて実行すれば他の作業者のコミットを消してしまうのを避けられる…と安心しがちだが、その安全装置はfetchしていないと機能しないため注意!
pushfやfpushなどのエイリアスを付けて普段からそれを使うといい。
一番怖いのが、-fなどで全く関係のないブランチで上書きしてしまうこと。(リモートのBRANCH2をBRANCH1で上書きするなど)
万が一に備えて、元のリモートヘッドのコミットIDをどこかにバックアップしておくといい。
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
ローカルブランチからローカルブランチへのどうでもいいmergeは意識しなくてもいいが、
意味のあるmergeは--no-ffを付けた方が良い。(PRによるmergeは普通は--no-ffだけど最近はsquash mergeも流行ってますね…PR番号があれば大丈夫かな)
--no-ffを付けないとコミットログにマージの痕跡が残らないし、--no-ffを付けなくても結局conflictが起こったものはマージ跡が残り、残ったり残らなかったりの状態になる。
--no-ffを付けるというルールを採用するプロジェクトもある。そのルールで大丈夫。
「ツリーを綺麗にしたら更新履歴が消える」という考えは間違っていない。(最近はPRリンクとか別の方法もあるから一概には言えなくなってきた) -
git merge --abort: マージを途中でやめる。conflict時などにmergeモードになりブランチ名が(master|MERGING)になる。それを抜ける。 -
git merge --continue: マージの内容を決定する。(commitみたいなもの) (エディタ等でconflictを解消してaddした後などに使う) -
git checkout origin/BRANCH1 -- FILEPATH1リモートのファイルを正として取り込む。これをやった後に再度自分の修正を入れ直すことも結構ある。
その前に退避用のコミットIDがあることはちゃんと確認しておく。(なかったら失われるので、merge --abortして別ブランチなどに避難させておくのが吉) -
--theirsや--oursのオプションは混乱しがちで個人的には使っていない。
部分操作
-
git add -p FILE+eモード : エディタでファイルの変更の一部だけを選んでインデックスに追加する。
複数の機能変更を同じファイルにしてしまった場合に利用する。意味のあるコミット単位にするための意識高い系コマンド。 -
git cherry-pick -x: 他ブランチをmergeしたくはないが、そのブランチの一部コミットは取り込みたいというときに利用する。-xオプションをつければcherry-pick前のリビジョンを残せる。
緊急を要したときのイレギュラーなコマンドで、ツリーを破壊しやすいため原則禁止にしていい。以下が参考になる。cherry-pick 運用の地獄から這い上がった話をしよう
この記事の筆者もプロジェクトの最初からいたわけではない上に、初期メンバーも今は一人もいないので詳細はわからないが、developブランチをfeatureブランチのように使ってしまったことが発端らしい。ChatGPTの見解はこちら。
cherry-pick は「差分のコピー」ではなく「履歴整合性の負債を前借りする操作」です。
タグの付け外し
- コミットに(HEADに)タグを付けてpush
git tag -a TAGNAME -m "MESSAGE"git log -1git push origin BRANCH1 TAGNAME
- 以前のリビジョンにタグを付けてpush
git tag -a TAGNAME xxxxxxxx -m "MESSAGE"git loggit push origin TAGNAME
- タグを消す
-
git tag -d TAGNAME(ローカルで消す) -
git push origin :TAGNAME(リモートも消す)
-
リポジトリのプロジェクトをcloneして、それを別のリポジトリにpushする
git clone git@github.com:XXX.git - b BRANCH1git remote remove origingit remote add origin git@github.com:YYY.gitgit push origin BRANCH1
FETCH_HEADからブランチを作成する
-
git branch BRANCH1 FETCH_HEADまたはgit branch BRANCH1 origin/BRANCH1。
別リポジトリからブランチを取り込む
git fetch 別リポジトリURL BRANCH1:BRANCH1
コミットコメントを複数行にする
-
-mを付けずにgit commit。viが開き、wqすればコミットされ、q!すればコミットされない。 -
git log --onelineを打ったときや、GUIで表示したときなど、1行目だけ表示されるケースが多い。1行目に重要なことを書く。 - featureブランチで複数行書きたくなったら、コミット単位が荒い可能性もある。
- PRするときや、大きな
mergeを行う場面では、しっかり複数行書いた方が良い。(そういうときはリポジトリのHPに行ってGUIでやることも多いが)
rebaseについて
-
ブランチを上手く運用するためには
rebaseが有効。
conflictした場合やsquashしたい場合はresetの方が使いやすいことも多い。
Git Bash上に残したコミットIDやreflogでしか戻せず、force-pushと併用されることが多いため、そこには注意が必要。 -
rebaseを元に戻すとき
- コンソールに残しておいたコミットIDがあればそこに
reset。残っていないとつらいので残そう。
残っていなければ、reflogで上から3, 4番目辺りに出てくる「reset: checkout BRANCH1」と書かれているコミットIDにresetしても戻せる。
rebaseの後に色々操作したりすると行方不明になりがちなので焦って色々やらない。
- コンソールに残しておいたコミットIDがあればそこに
-
使うとき
-
リモートから
fetchしてきたものを一番下にして自分のツリーをそこから始めたいとき。 -
複数のコミットを1つにまとめたいとき。(
git rebase -iでsquashする) (とにかくごそっとまとめたいときはresetの方が楽) -
コミットコメントを修正したいとき。(直前のコミットなら
commit --amendでいいが、それ以外はgit rebase -iでeditする) -
いくつか前のコミットだけを変更したいとき (直前のコミットなら
commit --amendでいいが、それ以外はgit rebase -iでeditする) -
リモートツリーの
merge跡を消して一本化されたツリーにする場合。force-pushと歴史改変が行われる。以下が参考になる。3.6 Git のブランチ機能 - リベース
マージとリベースではどちらがいいのか。
お察しのとおり、単純にどちらがよいとは言い切れません。
Git は強力なツールで、歴史に対していろんな操作をすることができます。しかし、チームやプロジェクトによって、事情はそれぞれ異なります。
あなたは既に、両者の特徴を理解しています。あなたが今いる状況ではどちらがより適切なのか、それを判断するのはあなたです。
一般論として、両者のいいとこどりをしたければ、まだプッシュしていないローカルの変更だけをリベースするようにして、 歴史をきれいに保っておきましょう。プッシュ済みの変更は決してリベースしないようにすれば、問題はおきません。
-
ワークツリーが汚いままで困っている (コミットしたいけどできないファイルがある)
-
git update-index --skip-worktree FILE_PATH
これをやるとgit statusでの表示やgit add .などの対象から外れる。
設定ファイルを誰もcommitしなくて自分で持ち続けなければならなかったり、
自分なりにカスタムした設定ファイルを持っておきたいときの必殺技。
ただしそのファイルが更新されてリモートリポジトリに入ると、
pullなどで思わぬ動作を引き起こすこともあるので「必殺技」です。 - 実行時に"unable to mark file"と出たら、そのファイルがリポジトリに上がったことが無い(ツリーに入ったことがない)と言う意味で、
ワークツリーで非表示にするためには.gitignoreで回避する必要がある(むしろその方が助かる)。
.gitignoreもcommitできない(しづらい)場合が多いので、
git config --global core.excludesfile ~/.gitignoreで自分なりの.gitignoreを作ると好きに書けるので便利。 - skipしているファイルの一覧を見るときは
git ls-files -v | grep '^S ' - skipを元に戻したいときは
git update-index --no-skip-worktree FILE_PATH
詳しい内容はこちら:既に git 管理しているファイルをあえて無視したい
こちらも非常に分かりやすい:Git における assume-unchanged と skip-worktree の技術的比較
git diffとPRのdiffが違う!
-
diff origin/develop featureはorigin/developからfeatureのdiff。普通はこれがPRに出ると思う。 - でも実際にPRがやりがちなdiffは
diff origin/develop...featureで、祖先(merge-base)を意識したdiff。
「1コミットにまとめてからPR出してね」と言われたときは、こちらのdiffで確かめてからの方が事故りづらい。 - 解消するときは、たとえばfeatureからdevelopへのPRなら、featureブランチで
reset --soft origin/developしてからcommitすると
「1コミットにまとめてからPR出してね」が叶う。もちろんfetch origin developはあらかじめしておく。
GitHubやGitLab上でsquashマージされて、自分の修正をrebaseで上乗せできない (--onto)
- feature/aaaをpushしてPR
- feature/aaaからfeature/bbbを切って作業
- feature/aaaがdevelopに、GitHubやGitLab上でsquashマージされる
- developをfetchしてくる (でももうfeature/aaaのツリーは持っていない)
- feature/bbbをdevelopでrebase ⇒ 大量のconflictが発生する
- そんなときは、
git rebase --onto develop feature/aaa feature/bbb
⇒元のfeature/aaaのことは忘れて、とにかくfeature/bbbをdevelopでrebaseしよう!という意味 - 無事解決!
マージ元とマージ先のツリーが繋がっていないがマージしたいとき (無関係なツリーに繋げたいとき)
-
git merge --allow-unrelated-histories BRANCH1
「ツリーは全く繋がってはいないが内容は一緒」というツリーに対してマージ可能。超特殊なケースで使用。
トラブルシューティング
-
git statusやgit diffで日本語が文字化けする
⇒git config --global core.quotepath falseを打つ - "Please make sure you have the correct access rights" が出た
⇒後述 - "push declined due to email privacy restrictions" が出た
⇒後述 - "Filename too long" が出た
⇒git config --global core.longpaths true - 意味不明なconflictが起こるようになって
resetも思い通りに動かない
⇒.gitのrefsが壊れた可能性がある。(ディスクI/Oが遅いとそうなる可能性がある)
GitBashはいい感じに動き続けるがGUIでは限界が来る。
⇒まずはリモート追跡ブランチのrefsを直す
git fetch origin +refs/heads/*:refs/remotes/origin/* --prune
⇒これでダメならcloneし直し (ローカルブランチが消えることを忘れないように) - TortoiseGitやElcipse(EGit)が上手く動かなくなった
⇒上記と同様の対処。
ローカルでプロジェクトを作ったので、それを元にGitHubのリポジトリを作りたい
- よくある順番。GitHubにリポジトリを切ってcloneしてから、ローカルで新規プロジェクトを作成するという順番もあるにはあるが。
- まずはGitHubに中身が空のリポジトリを作成する。(.gitignoreやLICENSEを用意するとそれが1つ目のコミットになり、用意しないと最初に自分がコミットしたものが1つ目のコミットになる)
- ローカルに作ったプロジェクトのトップディレクトリで
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でmainブランチに移動する。(GitHubで作成した初期ブランチはmainブランチを想定する) - 念のため
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されていることを確認して、めでたしめでたし。
退避シェル (編集中のファイルをコピーしてバックアップ)
色々あって、とにかく自分が「修正中のファイル」を
「単純コピーして退避」させざるを得なくなったとき用のシェルを用意しました。
これをGit Bash上で実行すると、
- ローカル上のgitリポジトリのパスを聞かれる
- 入力する
- バッチと同じディレクトリに"backup"ディレクトリを作成する
- 入力されたgitリポジトリの「修正中ファイル」を"backup"ディレクトリにコピーする
という動きをします。
コピーは上書きコピーで、ファイルは階層を維持しません。(その方が使いやすいと思う)
printf "ローカル上のgitリポジトリのパスを入力してください: "
read -r repo
mkdir -p backup
git -C "$repo" status --porcelain | cut -c4- | xargs -I{} cp -- "$repo/{}" backup
ここで言っている「修正中のファイル」とはgit statusで表示されるファイルのこと。
git status --porcelainの先頭3文字を切ると「修正中ファイル一覧」が出来上がります。
実戦で使用しているWinMerge設定
- 誰かからの贈り物だが出典が行方不明。
- gitでもWinMergeが使えるようになる設定で、
git diffでは限界があるときや、複雑なconflictを解消したいときに便利。 -
git config -e --globalでグローバル領域に書いておくといい。 -
git windiffでDIFF、git winmergeでマージ。どちらもWinMergeが立ち上がり、コンソールはそのプロセスの終了待ちになる。 - もちろん指定の場所にWinMergeがインストールされていないと使えない。
[diff]
tool = WinMerge
[difftool]
prompt = false
[difftool "WinMerge"]
path = C:/Program Files/WinMerge/WinMergeU.exe
cmd = \"C:/Program Files/WinMerge/WinMergeU.exe\" -e -r -u -x -wl -wr -dl \"a/$MERGED\" -dr \"b/$MERGED\" \"$LOCAL\" \"$REMOTE\"
trustExitCode = false
[merge]
tool = WinMerge
[mergetool]
prompt = false
keepBackup = false
[mergetool "WinMerge"]
cmd = \"C:/Program Files/WinMerge/WinMergeU.exe\" \"$MERGED\"
trustExitCode = false
[alias]
windiff = difftool -y -d -t WinMerge
winmerge = mergetool -y -t WinMerge
実戦で使用しているGitBash用エイリアス (オリジナル)
vim ~/.bash_profile
alias f='git fetch origin'
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 lo="git log --graph --color=always --pretty=format:'%C(yellow)%h%Creset %C(green)%ad%Creset %C(cyan)%an%Creset %C(auto)%d%Creset%n%s' --date=format:'%Y/%m/%d %H:%M' | less -SR"
alias push='git push'
alias pushf='git push --force-with-lease'
alias stash='git stash'
source ~/.bash_profile