Git

はじめての

絵があって簡単で面白い

↓↓↓↓↓↓↓↓↓↓↓↓

http://kinokoru.jp/archives/1017

アニメーションで面白い

↓↓↓↓↓↓↓↓↓↓↓↓

http://k.swd.cc/learnGitBranching-ja/

コミット取り消し

http://freak-da.hatenablog.com/entry/20111105/p1

コマンド

  • commitする バージョンを1つ進める

  • stageする 特定の変更内容をindexに登録する(次回commit分に含める)

  • index 次回commit分のファイル一覧

|.コマンド|.意味|

|git init|カレントディレクトリ以下をgit管理下に置く|

|git add |をstageする。に.(ピリオド)を指定すると変更分全て|

|git commit -m "" | 1行メッセージ付きでcommitする|

|git status | stage(to be commited)と変更分(not updated)の状態を確認する|

|git reset []| (git logで表示されるハッシュ文字列部分)まで戻る。指定が無ければindexを全てunstageする|

|git log [--format=oneline]|commitの履歴を見る。オプションでformatが幾つか指定出来る|

|git rm|ファイルを削除してgitに削除したことを伝える|

|git clean -fdxn ←確認 危険!必ず確認すること 実行→ git clean -fdx |管理対象外のファイルを削除する|

初期設定

git config --global user.name "your name"
git config --global user.email [email protected]
git config --global color.ui auto

ヘッドの種類

  • HEAD 現在作業しているワーキングツリーのコミットを指します。コミットが重なる度に先頭へ移動していきます。また git reset や git merge git rebase などのコマンドでも移動します。

  • ORIG_HEAD git reset や git merge など、通常のコミットとは違う極端な HEAD の移動が発生した時に、移動前の HEAD のコミットを指します。

  • MERGE_HEAD git merge を実行して、コンフリクトなどが発生してマージをしている最中にマージ元のブランチの先頭のコミットを指します。

  • CHERRY_PIKC_HEAD MERGE_HEAD とほぼ同じですが git cherry-pick を実行した場合のものです。

  • FETCH_HEAD git fetch した時、リモートから取得したコミットの先頭のコミットを指します。

http://tkengo.github.io/blog/2014/02/10/how-to-track-git-history/

違い

resetオプションの違い

コミットメッセージだけ修正してコミットし直したい:git reset --soft

変更内容を追加してコミットし直したい:git reset --mixed

コミット自体なかったことにしたい:git reset --hard

世代のたどり方

  • ^ について ^ を指定することで、親のコミットを取得することができます。よく聞く話かとは思いますが git のコミットには必ず 1 つは親が存在します(一番最初のコミットを除いて)。親というのは要するに直前のコミットのこと。そしてマージが発生すると、そのマージコミットについては 2 つの親を持つことになります

  • ~ について 親のコミットを取得できるのは ^ と同じなのですが ~ の方はマージコミットがあっても本流の方だけをたどっていきます。

Gitの中身

中の挙動が詳しく書いてある

http://koseki.hatenablog.com/entry/2014/04/22/inside-git-1

コミットメッセージ規約

  1. WhyやHowを書くこと。 WhereやWhatはいらん、diffを見ればわかる

  2. 言語は英語にする(最初の1文は必ず英語。そのあと補足で日本語は可)

  3. 1文の場合にはピリオドを付けない

  4. 主語は省き時制は現在の文章形式にする

  5. 文頭の英単語を大文字にする

  6. ファーストコミットは「Initial commit」 とする

  7. issueから発生したコミットは先頭に「[refs #"issue番号"]」を記述する。クローズの場合は[close #"issue番号"]

Gitリポジトリブラウザ比較

  • Gitlab

  • Gitosis

  • Gitolite

  • Gitorious インストールが難しいらしい

  • Gitblit インストール簡単だが、機能がまだそろっていないらしい

Gitリモートリポジトリでgit init --bare --sharedを付け忘れた場合

git config core.sharedRepository group
chmod -R g+ws hooks
chmod -R g+ws info
chmod -R g+ws objects
chmod -R g+ws refs

で--shareと同じ効果

ブランチの定義

issue-XXX/(大分類名)/ブランチ名で命名

issue-123/chat_timeline/extract-url_linkみたいな感じ

  1. developブランチ 開発を行うためのブランチ。開発者は、主にこのブランチ上で作業を行う。次に紹介するfeatureブランチなど、他のブランチで行った作業は、ここにマージされる

  2. featureブランチ 主要な機能を実装するためのブランチ。機能の実装やバグフィックスなど、タスクごとにfeatureブランチを作成し、作業を行う

  3. releaseブランチ リリースの準備を行うためのブランチ。プロダクトをリリースする前に、このブランチを作成し、微調整を行う。releaseブランチを作成することで、リリース準備と次のバージョンに向けた開発のコードを分けることができる

  4. masterブランチ リリースしたソースコードを管理するためのブランチ。リリース作業を行うと、releaseブランチはmasterブランチへマージされて、リリースタグが打たれる。開発者は、このブランチへのコミットは行わない

  5. hotfixブランチ リリースされたソフトウェアに緊急の修正を行うためのブランチ。このブランチでの修正内容は、すぐにリリースされるので、hotfixブランチはリリースを管理するmasterブランチへマージされる

ルール

原則

  • masterで作業しない。ブランチを作って作業すること

  • ブランチでは1機能もしくは1バグのみ作業すること

  • メジャーバージョンは常に動くようにしておくこと

  • チケットとリンクをとれるようにすること

リポジトリ名規約

  • 基本すべて小文字

  • "_"でなく"-"の方がよい(使っている人が多いかつかっこいい)

便利なコマンドとオプション

リポジトリ名取得

basename $(git remote show origin -n | grep "Fetch URL:" | sed 's/.*://;s/.git$//'

https://stackoverflow.com/questions/15715825/how-do-you-get-git-repos-name-in-some-git-repository

ブランチ表示ログ

git log --graph --branches --pretty=format:"%d [%h] "%s""

統計情報

変更行数

git diff <branch name> --shortstat *.cc
git log --author="Taro Tanaka" --oneline --shortstat
git log --numstat --pretty="%H" | awk 'NF==3 {plus+=$1; minus+=$2} END {printf("+%d, -%dn", plus, minus)}'

総追加行数

git log --since=2013-01-01 --until=2013-06-30 --oneline --numstat --no-merges --pretty=format:"" | cut -f1 | awk 'BEGIN {sum=0} {sum+=$1} END {print sum}'
git log -- <branch name> --oneline --numstat --no-merges --pretty = format: "" | cut -f1 | awk 'BEGIN {sum=0} {sum+=$1} END {print sum}'
git log --since=2015-09-01 --oneline --no-merges --numstat --pretty="" *.cc *.h | cut -f1 | awk 'BEGIN {sum=0} {sum+=$1} END {print sum}'

総削除行数

git log --since = 2013-01-01 --until = 2013-06-30 --oneline --numstat --no-merges --pretty = format: "" | cut -f2 | awk 'BEGIN {sum=0} {sum+=$1} END {print sum}'

コントリビューター一覧

git shortlog -se | awk -F't' '{print $2,$3}'

総コミット回数

git log --since = 2013-01-01 --until = 2013-06-30 --oneline --no-merges | wc -l

よくマージしている人

git log --merges --format="%cn" | sort | uniq -c | sort -r | head

よく編集しているファイル

git log --name-only --pretty="format:" | grep -ve '^$' | sort | uniq -c | sort -r | head

よくfu*kって書く人

git log --pretty="format:%cn:%s" | grep fu.k | cut -d":" -f1 | sort | uniq -c | sort -r

submodule

一括更新

git submodule foreach --recursive 'git checkout master; git pull'

ルートリポジトリからsubmoduleを更新してpushしたい

git submodule foreach --recursive git add -A .

git submodule foreach --recursive git commit -m 'submodule commit message'

git push --recurse-submodules=on-demand

孫もいるなら

git submodule foreach --recursive git push origin master

じゃないとだめっぽい

https://ja.stackoverflow.com/questions/17501/git-submodule%E3%82%92%E8%A6%AA%E3%81%AE%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%81%A8%E5%90%8C%E6%99%82%E3%81%AB%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88-%E3%83%97%E3%83%83%E3%82%B7%E3%83%A5%E3%81%97%E3%81%9F%E3%81%84

2017 Q3にリリースされる予定のgitで孫がいてもpushできるようになる見込み

https://stackoverflow.com/questions/29564257/git-push-recurse-submodules-on-demand-is-not-truly-recursive

submoduleの削除

git submodule deinit vendor/pelican-sober
git rm vendor/pelican-sober
rm -rf .git/modules/path/to/submodule
git commit -a

履歴を保持したままリポジトリを統合する

# Fetch the submodule commits into the main repository
git remote add submodule_origin git://url/to/submodule/origin
git fetch submodule_origin
# Start a fake merge (won't change any files, won't commit anything)
git merge --allow-unrelated-histories -s ours --no-commit submodule_origin/master
# ここでgit log しても表示されない
# Do the same as in the first solution
git rm --cached submodule_path # delete reference to submodule HEAD
git rm .gitmodules # if you have more than one submodules,
# 他のsubmoduleがある場合はviで対象部分のみ削除
# you need to edit this file instead of deleting!
rm -rf submodule_path/.git # make sure you have backup!!
git add submodule_path # will add files instead of commit reference
# Commit and cleanup
git commit -m "removed submodule"
# ここでgit log したら表示される
git remote rm submodule_origin

https://stackoverflow.com/questions/1759587/un-submodule-a-git-submodule

Tips

No newline at end of fileを一括で修正する

for i in $(git ls-files | xargs -i file {} | grep --line-buffered "ASCII\|UTF-8" | cut -d ":" -f 1); do echo $i; \
if diff /dev/null "$i" | tail -1 | \
grep '^\\ No newline' > /dev/null; then echo >> "$i"; \
fi; done

一括で改行コードを修正する

git ls-files | xargs -i file {} | grep --line-buffered "ASCII\|UTF-8" | cut -d ":" -f 1 | xargs -i nkf -Lu --overwrite {}

gitconfigにユーザー情報を入れたくない

GIT_AUTHOR_NAME="" GIT_AUTHOR_EMAIL=""

を定義しておけばよい

一部のファイルのみをstashする

stashしないファイルをgit addする git stash -k git reset # ステージからもとに戻す

ノンパスワード設定

ssh-keygen -t rsa -C "[email protected]" -b 4096

cat ~/.ssh/id_rsa.pub

確認

ssh -T [email protected]

あとは、

https:// を [email protected]:に切り替える

これを使えばhttpsをsshに置き換えてくれる

git config --global "[email protected]:.pushinsteadof" "https://github.com/"

push先とfetch先を変える

git remote set-url --push origin [email protected]:User/forked.git

pushurl = http://192.168.1.20/Test/hubot-rocketchat.git

http://sleepycoders.blogspot.jp/2012/05/different-git-push-pullfetch-urls.html

http://stackoverflow.com/questions/948354/default-behavior-of-git-push-without-a-branch-specified

複数のコミットをまとめる

git rebase -i xxx

pick->s

http://iwb.jp/git-commit-rebase-squash/

Troubleshooting

パスワードを入力するときに ( gnome-ssh-askpass:11826 ) : Gtk-WARNING **: cannot open display: とか言われる

unset SSH_ASKPASS

間違ってgit resetしてしまった

  • コミットしているなら

    git reflog

git rest --hard xxx

  • ステージングしている

    git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)")

git show xxx

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") xxx

http://stackoverflow.com/questions/7374069/undo-git-reset-hard-with-uncommitted-files-in-the-staging-area

  • ステージングしていない

    諦める

EUCでコミットメッセージを書いてしまった

git filter-branch ~~f --msg-filter 'nkf -w' -~~ --all

http://hiroom2.jimdo.com/2015/07/09/git-filter-branch%E3%81%A7%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%82%92%E4%B8%80%E6%8B%AC%E5%A4%89%E6%8F%9B%E3%81%99%E3%82%8B/

gitignoreでホワイトリスト化できない

階層構造になっていると工夫しないと認識されない

/*
/.*
/.*/*
/.*/*/*

という感じに階層分だけ最初に除外する。

ディレクトリがある場合は

/*

!/src/

/src/*

!/src/hello.c

というように上のディレクトリを一回対象外にして入れ直す

http://seesaawiki.jp/aki/d/.gitignore %A5%D5%A5%A1%A5%A4%A5%EB%A4%CE%BD%F1%A4%AD%CA%FD

.gitignoreが反映されない

git rm -r --cached .

git add .

git commit -m "Update .gitignore"

でかいファイルをコミットしてしまった場合

重いファイルをコミットした場合、ツリーから消さない限りリポジトリが重くなってしまう。

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch でかいファイル.tar.gz' --prune-empty --tag-name-filter cat -- --all

これでツリー上から完全削除される

http://www.walbrix.com/jp/blog/2013-10-github-large-files.html

"軽量化の仕方":http://techracho.bpsinc.jp/baba/2012_05_22/5594

でかくなったリポジトリの原因を特定する

これが一番良さそう

#my problem was that I had there a refs/remotes/origin/master line for a remote repository, delete it, otherwise git won't remove those files
check .git/packed-refs
#(optional)to check for the largest files
git verify-pack -v .git/objects/pack/#{pack-name}.idx | sort -k 3 -n | tail -5
#(optional)to check what are those files
git rev-list --objects --all | grep a0d770a97ff0fac0be1d777b32cc67fe69eb9a98
#to remove a file from all revisions
git filter-branch --index-filter 'git rm --cached --ignore-unmatch file_names' -- --all
#to remove git's backup
rm -rf .git/refs/original/
rm -rf .git/logs/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
# old
#to expire all the loose objects
#git reflog expire --all --expire='0 days'
#to check if there are any loose objects
#git fsck --full --unreachable
#repacking
#git repack -A -d
#to finally remove those objects
#git prune

リモートリポジトリにあげたら容量がもとに戻ってしまうので、リモートリポジトリを一旦削除して再度pushする必要がある。

http://stackoverflow.com/questions/2164581/remove-file-from-git-repository-history

gitのリポジトリがでかくなったときの削減の昔のやり方

一部のファイルだけ違うブランチのものを使う

git checkout ブランチ名 ファイル名

コミットするユーザやメールアドレスを間違えた場合

コミット時のユーザやメールアドレスを変更するとき

git commit --amend --author="sea_mountain [email protected]"

結構前のコミットするユーザやメールアドレスを間違えた場合

$ git rebase -i <コミット>

  1. エディタが開くので以下のように変更して保存

  2. (変更後)対象のコミットをeditに変更

    $ git commit --amend -m "コミットメッセージ" --author="user.name "

$ git log

$ git rebase --continue

管理下のファイルの行末のスペース(trailing whitespace)を削除する

git ls-files | sed -i 's/[ t]*$//'

refspecやremoteやrefsやらがわからなくなったとき

以下のサイトにわかりやすく解説されている。

http://d.hatena.ne.jp/hokaccha/20120404/1333507076

公式文書は難解なので。。。

リモートリポジトリのコミットログを修正する

  1. git checkout -b ローカルのブランチ名 origin/リモートのブランチ名 でブランチを切ります。

  2. git rebase -iします。

  3. git push origin +ローカルのブランチ名:リモートのブランチ名 して強制的にコミットツリーを変更します。

    他のリポジトリから変更を取得する際は、git fetchしてgit rebase origin/リモートのブランチ名 としましょう。

コミット日付をタイムスタンプに復元

for FILE in `git ls-files`; do
TIME=`git log --pretty=format:%ci -n1 $FILE`
echo $TIME't'$FILE
STAMP=`date -d "$TIME" +"%y%m%d%H%M.%S"`
touch -t $STAMP $FILE
done

Gitのルートディレクトリに移動する

cd "$(git rev-parse --show-toplevel)"

commitの際にオーナー情報を入れていなかった

git config --global user.name "your name"
git config --global user.email [email protected]
git commit --amend --reset-author

OR

.gitconfig

[user]
name = name

Gitでプロキシを通すURLと通さないURLを使い分ける

どうやらversion:1.8ではダメのよう

~/.gitconfig

[http "http://192.168.1.30"]
postBuffer = 524288000
proxy =
[http "http://192.168.1.40"]
postBuffer = 524288000
proxy =
[http "http://192.168.1.50"]
postBuffer = 524288000
proxy =
#[http]
# postBuffer = 524288000
# proxy = http://192.168.1.30:10080
#[https]
# postBuffer = 524288000
# proxy = http://192.168.1.30:10080
[url "https://"]
insteadOf = git://

Trivia

objectsディレクトリの中

idxファイル:インデックスを保持

pack ファイル:データ実体

おもしろいinitial commit

This is where it all begins...

Commit committed

Version control is awful

COMMIT ALL THE FILES!

The same thing we do every night, Pinky - try to take over the world!

Lock S-foils in attack position

This commit is a lie

I'll explain when you're older!

Here be Dragons

Reinventing the wheel. Again.

This is not the commit message you are looking for

Batman! (this commit has no parents)

FAQ

[email protected]形式でダウンロードできない場合

~/.gitconfig

[url "http://github.com/"]
insteadOf = [email protected]:

現在のディレクトリでgit clone

git clone http://xxx .

※古いバージョンだとできない?v1.8系は無理だった。

.gitignoreに設定しているファイルがリモートに存在するエラーの解決法

エラー文:

error:The following untracked workding tree files would be overwritten by merge:

解決法:

ようわからんけど、gitでリモートのブランチにローカルを強制一致させたい時

git fetch

git reset --hard FETCH_HEAD

Contents
はじめての
コマンド
初期設定
ヘッドの種類
違い
resetオプションの違い
世代のたどり方
Gitの中身
コミットメッセージ規約
コミットメッセージでよく使う英語
Gitリポジトリブラウザ比較
Gitリモートリポジトリでgit init --bare --sharedを付け忘れた場合
ブランチの定義
ルール
原則
Gitワークフロー
リポジトリ名規約
便利なコマンドとオプション
リポジトリ名取得
ブランチ表示ログ
統計情報
変更行数
総追加行数
総削除行数
コントリビューター一覧
総コミット回数
よくマージしている人
よく編集しているファイル
よくfu*kって書く人
submodule
一括更新
ルートリポジトリからsubmoduleを更新してpushしたい
submoduleの削除
履歴を保持したままリポジトリを統合する
Tips
No newline at end of fileを一括で修正する
一括で改行コードを修正する
gitconfigにユーザー情報を入れたくない
一部のファイルのみをstashする
ノンパスワード設定
push先とfetch先を変える
複数のコミットをまとめる
Troubleshooting
パスワードを入力するときに ( gnome-ssh-askpass:11826 ) : Gtk-WARNING **: cannot open display: とか言われる
間違ってgit resetしてしまった
EUCでコミットメッセージを書いてしまった
gitignoreでホワイトリスト化できない
.gitignoreが反映されない
でかいファイルをコミットしてしまった場合
でかくなったリポジトリの原因を特定する
一部のファイルだけ違うブランチのものを使う
コミットするユーザやメールアドレスを間違えた場合
結構前のコミットするユーザやメールアドレスを間違えた場合
管理下のファイルの行末のスペース(trailing whitespace)を削除する
refspecやremoteやrefsやらがわからなくなったとき
リモートリポジトリのコミットログを修正する
コミット日付をタイムスタンプに復元
Gitのルートディレクトリに移動する
commitの際にオーナー情報を入れていなかった
Gitでプロキシを通すURLと通さないURLを使い分ける
Trivia
objectsディレクトリの中
おもしろいinitial commit
FAQ
[email protected]形式でダウンロードできない場合
Gitで大量のファイルの中から必要ファイルのみをaddする方法
CentOS6.x系でhttp認証に失敗する
GitのGUI比較
現在のディレクトリでgit clone
.gitignoreに設定しているファイルがリモートに存在するエラーの解決法