カテゴリー別アーカイブ: Git

git | マージのタイミングで毎回gcが発生する際の対処例

gitリポジトリを使った開発中に、何故かマージ動作が起きるタイミングで毎回git gcが実行されてしまう状態になりました。環境は次の通り。


$ git --version
git version 2.6.2.windows.1

マージが発生するコマンド全部(merge、rebase、その他)なので非常にウザいです。で調べてみてもよくわかりません。

そこで試したのが以下の手順

  1. git gc –aggressive
    ガッツリGCやってゴミを減らす
  2. git prune
    自動gcの後に「warning: There are too many unreachable loose objects; run ‘git prune’ to remove them.」と表示されたので実行

これで毎回gcが発生しなくなりました!やったね!

同じ状況になった人は試してみてはいかがでしょうか?

master以外でgit svn dcommitできなくする

!!! CAUTION !!!

このエントリはGit Advent Calendar 2014 – Qiitaではありません。

 

背景

訳あってセントラルリポジトリがSubversionだった場合、”当然”git-svnを使って開発を進めていきます。

そんなとき、たまに間違ってフィーチャーブランチ上でgit svn dcommitしてしまうことがあります。これをやってしまうとmasterをSVNと同期させるのが少し手間だったりします。

そこで、master以外ではgit svn dcommitできなくしてしまおうというのが、このエントリの趣旨です。

 

前準備

gitには当然コマンドをフックする仕組みがあります。しかし、標準のフック機構はgit-svnでは使えません。

そこで、まずは以下の「git-svn-hooks」を導入します。

rkitover/git-svn-hooks

導入手順がREADME.mdに書いてありますので、参考にして導入します。

なお、私の場合、次のような手順で入れました。

  1. ダウンロードしたgit-svn.shを~/binにコピー
  2. ~/.bashrcに次の記述を追加
    source ~/bin/git-svn.sh

 

フックスクリプトの作成

あとはgit svn dcommitをフックして、master以外のブランチでは警告メッセージを出すようにします。例えば、こんな感じの.git/hooks/pre-svn-dcommitファイルを作成します。


#!/bin/sh
WORKING_BRANCH=`git branch -l | grep "*" | cut -d " " -f 2`
if [ $WORKING_BRANCH != "master" ]
then
echo "WARNING: curret branch is NOT \"master\""
exit 1
fi
exit 0

view raw

pre-svn-dcommit

hosted with ❤ by GitHub

 

動作確認

任意のgit svn cloneしたリポジトリで、フィーチャーブランチを作ってgit svn dcommitすると、次のようにエラーになります。

$ git checkout -b some-feature
$ git svn dcommit
WARNING: curret branch is NOT "master"

これで安心してgit svn dcommitできますね!

git-nowにてカレントブランチのnowコミットだけfixupするようにした


#!/bin/sh
PREFIX="from now"
MESSAGE="[${PREFIX}] `date +\"%Y/%m/%d %T\"`"
MAIN_BRANCH=${1:-master} # デフォルトは 'master'、第1引数で上書き可能
shift # 引数リストからメインブランチ名を除外
get_amend() {
if [ -z `git log –pretty=oneline –no-color -1 | cut -d " " -f 2- | grep "^\[${PREFIX}]"` ]
then
return 1
fi
local added_files=`git diff –name-only –cached`
for f1 in `git diff –name-only HEAD^ HEAD`
do
for f2 in $added_files
do
if [ $f1 == $f2 ]
then
return 1
fi
done
done
echo "–amend"
return 1
}
if [ $# -eq 0 ]
then
git add -u
git commit -m "${MESSAGE}"
elif [ $1 == "–compact" ]
then
git add -A
AMEND=`get_amend`
git commit $AMEND -m "${MESSAGE}"
elif [ $1 == "–recent" ]
then
git add -A
if [ -z "`git log –since=1.minute`" ]
then
git commit -m "${MESSAGE}"
else
git commit –amend -m "${MESSAGE}"
fi
elif [ $1 == "–fixup" ]
then
WORKING_BRANCH=`git branch -l | grep "*" | cut -d " " -f 2`
if [ $WORKING_BRANCH == $MAIN_BRANCH ]
then
FIRST_NOW_COMMIT=`git log –pretty=oneline –no-color –grep="${PREFIX}" | tail -n 1 | cut -d " " -f 1`
else
FIRST_NOW_COMMIT=`git log $MAIN_BRANCH.. –pretty=oneline –no-color –grep="${PREFIX}" | tail -n 1 | cut -d " " -f 1`
fi
git reset ${FIRST_NOW_COMMIT}
git add -A
if [ $# -eq 2 ]
then
git commit –amend -m "$2"
else
git commit –amend
fi
elif [ $1 != "–rebase" ]
then
if [ $1 != "–all" ]
then
git add $@
else
git add -u
git add .
fi
printf "${MESSAGE}\n\n%s" "`git diff –cached`" | git commit -F –
else
FIRST_NOW_COMMIT=`git log $MAIN_BRANCH.. –pretty=oneline –no-color –grep="${PREFIX}" | tail -n 1 | cut -d " " -f 1`
INITIAL_COMMIT=`git log $MAIN_BRANCH.. –pretty=oneline –no-color | tail -n 1 | cut -d " " -f 1`
if [ ${FIRST_NOW_COMMIT} != ${INITIAL_COMMIT} ]
then
git rebase -i ${FIRST_NOW_COMMIT}^
else
WORKING_BRANCH=`git branch -l | grep "*" | cut -d " " -f 2`
git checkout ${FIRST_NOW_COMMIT}
git commit –amend
git rebase –onto HEAD ${FIRST_NOW_COMMIT} ${WORKING_BRANCH}
fi
fi

view raw

git-now

hosted with ❤ by GitHub

 

問題

git-now --fixup

は内部で最初のgit-nowで作成したコミットを探す際、簡単にいえばログから最初のnowコミット(メッセージに”from now”が含まれるコミット)を抽出しています。しかし、これだとうっかりpushしてしまったコミットも含んで探してしまい、fixupしたくないところまでfixupしてしまいます。

 

対応

そこで、この問題の軽減として、フィーチャーブランチ上で作業しているときは、フィーチャーブランチに含まれるnowコミットだけをfixupしようと考えたわけです。

で、どうするか?

答えは「masterから分岐したコミットだけを対象としたログから最初のnowコミットを探す」です。そのために、git logを実行する際に

git log master..

のように「ログ取得対象のコミットを明示的に指定」してやります。上記の指定は、「masterからHEADまで」のログのうち、master部分のログを取り除いたものを取得できます。(参考:transitive.info – git log 使い方

 

こんなコミットグラフだっとします。

 

a-b <master>
   \
     c-d <*temp>

※<branchname>:ブランチ名(*はカレントブランチ)

コミットログを見るとこんな感じです。

$ git log --pretty=format:"%h %Cgreen%d%Creset %s"
cfeb93c (HEAD, temp) [from now] 2014/10/23 13:02:36
1f48b0d [from now] 2014/10/23 13:00:20
0a64b5f (master) [from now] 2014/10/23 12:46:36
b9656d0 [from now] 2014/10/23 12:46:25

ここで、master..を指定するとどうなるか?

$ git log --pretty=format:"%h %Cgreen%d%Creset %s" master..
cfeb93c (HEAD, temp) [from now] 2014/10/23 13:02:36
1f48b0d [from now] 2014/10/23 13:00:20

ご覧のようにmasterから分岐後の2つのコミットだけが取得できます。

この動作を利用したのが、今回の修正です。

 

なお

今回の記事を作成するにあたりこんな変更も加えました。

GistSharpExtension:VS14に対応しました

GistSharpExtension

VS 14 CTPも4まで来たので、正式リリースより二足ほど先に対応しました。

 

残念ながらVS 14の拡張機能マネージャーに出す方法がわからなかったのですが、ダウンロードしたVSIXを開けば、次のように問題なくインストールできます。

 

image

 

VS 14でも快適なGistライフを!

ASP.NET vNextのソースコードがGitHubで公開されました

Microsoftがやってくれました!!!

 

aspnet/Home https://github.com/aspnet/Home

ASP.NET vNext Home

In the next version of ASP.NET we are working with multiple teams around Microsoft to create a lean, composable .NET stack that provides a familiar and modern framework for web and cloud scenarios.

 

ソース公開を心待ちにしていた、「ASP.NET Identity」もその中には含まれていたので、時間をとって中身を見てみようと思います。

aspnet/Identity https://github.com/aspnet/Identity

.gitignoreはBOM付UTF-8で書いてはいけない

小ネタ。

編集

今日なんとなしに.gitignoreをVSで編集してBOM付UTF-8で保存したんですよ。

 


bin/
obj/
*.suo
*.user
TestResults/

view raw

.gitignore

hosted with ❤ by GitHub

 

で、この状態でgit add . した結果がこちら。

 

image

 

除外したはずのbinフォルダーが含まれてしまっています。この結果から、BOM付UTF-8で保存した.gitignoreは、先頭行のファイル名にBOM部分も付けたものがファイル名と解釈されているようです。

 

一度リセット後、.gitignoreのエンコーディングをBOMなしUTF-8に直すと、git add .した結果は次の通り。

 

image

 

無事binフォルダーが除外されました。

 

というわけで、.gitignoreはBOM付UTF-8で保存してはいけない、という話でした。

git commit –allow-emptyなコミットは、git rebase -iの初期状態で無視される

小ネタ。

こんなリポジトリがあったとして。

image

“feature1”というメッセージの付いたコミットは、git commit –allow-emptyによるものです。

で、このときに、git rebase –i masterとすると……

image

空コミット行が、初期状態でコメントアウトされています。

なんでこんなことをしようと思ったかというと、「git commit –allow-empty を使った WIP PR ワークフロー – Qiita」を読んで、からコミットできるなら、フィーチャーブランチの最初にコメント書いちゃえばいいんじゃね?と思ったからでした。

でも、rebase -iで先頭行のコメント解除してから他のコミットをsquashとか、地味にめんどいので、お蔵入りかなぁ……

Gistにディレクトリーを含むリポジトリーをpushできなくなった?

以前はできてた気がするのですが、今さっきディレクトリーを含むリポジトリーをgistにpushしようとしたら、こんなのが出てしまいました。

 

$ git push
Counting objects: 16, done.

Delta compression using up to 4 threads.

Compressing objects: 100% (13/13), done.

Writing objects: 100% (14/14), 5.67 KiB, done.

Total 14 (delta 1), reused 0 (delta 0)

remote: Gist does not support directories.

remote: These are the directories that are causing problems

remote: WebApplication1/

To https://gist.github.com/4722888.git

 ! [remote rejected] master -> master (pre-receive hook declined)

error: failed to push some refs to 'https://gist.github.com/4722888.git

 

原因は書いてある通り「Gist does not support directories.」だとは思うんですが、なんか変わったんですかねぇ・・・

 

[追記]

Gistサイトのほうで”directoryname/filename.txt”な感じでファイル追加しようとしても、やっぱりできなくなってた・・・

image

Git:HTTPS接続の認証情報入力を初回のみにするgit-credential-winstoreをクリーンインストール

git-credential-winstore | OPC Diary – No Code, No Life.

で紹介されていますが、普通に実行すると%AppData%(%UserProfile%\AppData\Roaming)フォルダーの下にGitCredStoreというフォルダを作ってそこに入れてしまうので、何となくいやです。可能なら~/binフォルダーに入れてポータブルにしたいですよね、という時の方法。

 

インストール

git-credential-winstore – Homeからgit-credential-winstore.exeをダウンロードして、~/bin(%UserProfile%\bin)フォルダーに配置します。

 

設定

~/.gitconfigに以下の一文を加えます。

 


[credential]
helper = !'git-credential-winstore.exe'

view raw

.gitconfig

hosted with ❤ by GitHub

 

もしくは、以下のコマンドを実行します。

 


git config –global credential.helper \!git-credential-winstore.exe

 

動作原理

Git Bashは実行時、自動的に~/binにパスが通るので、ただそこにgit-credential-winstore.exeを置いてやればよい、という理屈です。

msysGitへのクリーンなgit-flowインストール手順 « be freeでgit-flowをインストールしたやり方と同じですね。

 

これで、新しいPCにGit環境を作るときは~/binフォルダーコピーで行けるようになりますね!