Git 2.13が公開されました (英->和訳)

More than 1 year has passed since last update.

Git 2.13.0が公開されましたね。GitHubが最近つぶやいた記事を、本当にサクッと翻訳してみたので、編集リクエストをよろしくお願いします。

「は」と「が」とか、不自然に感じてしまったら申し訳ないですが、もしGitについての新しい情報が伝わればと思います。


Git 2.13.0は公開されました

オープンソースであるGitプロジェクトはGit 2.13.0を公開し、65人以上が機能を追加したりバグを直したりしました。その機能をもっと詳しく説明する前に、セキュリティに関する短い報告があります。

自分のGitホスティング・サーバーを使用していれば、git shellプログラムでは、信用できないGitのユーザがリモートホストでシェルコマンドを実行できてしまうという脆弱性をGit 2.13が直してくれます。これはホスティング・サーバーを使用し、特にgit shellを設定した場合のみです。「よう意味が分からん」人には気にしなくていいはずです。一応、詳細はこちらです。Github.comもGitHub Enterpriseもgit shellを使用していないので、影響を受けません。

ハァ...報告が終わったということで、楽しい部分を紹介しましょう!


SHA-1衝突判定

あっ、楽しいって言ったっけ?ごめん、それはまだでして...

聞いたことがあるかもしれませんが、最近はある研究者たちがSHA-1(Gitが使っているオブジェクトを認識するためのハッシュ機能)の初めての衝突を発見しました。このテクニックを使用し、Gitのユーザに対する衝突攻撃が行われたことでしょう。運よく、その同じ研究者たちがこのテクニックを駆使して攻撃しようとしている内容を検出できる方法を提供してくれました。3月に、GitHub.comはそういう衝突攻撃を防ぐために、その方法を使いはじめました

Git 2.13にも同じような変更があり、衝突攻撃の恐れのあるオブジェクトを検出し拒否します。衝撃を判定するSHA-1はもうデフォルトとなっています。そのコードはGitに入っていますので、他のプログラムをインストールする必要はありません。この実装は他の方法と比較的に遅いですが、大体のGitの運用においては、全体的にわずかな影響しか受けません(なぜかというと、GitはSHA-1のハッシュを計算する時間はそもそも短いからです)。

他の衝突判定ニュースなんですが、Gitでより強力なハッシュアルゴリズムが使えるように、ハッシュの新しい関数を扱うコードベースの遷移の準備の計画がなお進んでいます。

[衝突判定SHA-1の遷移 1, SHA-1 遷移 2]


より便利なpathspec

Gitでこうやってパスの引数を渡したことがあるかもしれません:

$ git log foo.c

$ git grep my_pattern program.rb

でも、Gitにとってはfoo.cprogram.rbという引数の正体はpathspecs、パスのマッチを行う、特にGitのパターンであることを知っていましたか?絶対パスでも、接頭詞でもワイルドカードでもPathspecsとして使えます:

$ git log Documentation/      # Documentation/ ディレクトリの下にある全てのもの

$ git log '*.c' # ツリーの中にある全てのCのファイル

まして、強力な拡張シンタクスがあります。:(magic)で始まるpathspecsは特別のマッチ機能を有効にします。それらのリストがgit help glossaryのpathspecのところに記載してありますが、ここでその中の何個かを見ていきましょう。

例えば、grepでファイルを除外したいという時は、:(exclude)を使います:

$ git grep this.is.a src

src/foo.c:this is a C file
src/foo.rb:this is a ruby file

$ git grep this.is.a -- src ':(exclude)*.c'
src/foo.rb:this is a ruby file

この例ではいくつかの気づくべきことがあります。1つ目は、分離するために --(ダブル・ダッシュ)をpathspecの後に置く必要がありました。多くのGitのコマンドは改訂(revisions)とpathspecの組み合わせを引数として受けるからです。完全なシンタクスはこうです:[<revisions>] - [<pathspecs>]。ダブル・ダッシュが抜いていると、Gitは全ての引数が、ファイルシステムの中にある有効なオブジェクト名かファイルかをチェックします。でも、このexcludeのバターンはどちらでもないです。そのために、ダブル・ダッシュがないとGitは諦めて文句ばかりを言う(将来のGitのバージョンではこれが変わる可能性があります。*.cみたいなワイルドカードもこのような問題があったけど、そのワイルドカードをpathspecとして扱うためにルールが緩くされました)。詳細はgit help cli(リンク)で確認できます。

気づくべき2つ目のことは、:(exclude)を書くのが面倒ですし、シェルでクオートに囲む必要があります。でも、解決方法があります:pathspecの短文式魔法。excludeの短文式は!(ビックリマーク)です。.gitignoreのファイルみたいにGitの他の部分と似ているから覚えやすいです。

$ git grep this.is.a --src ':!*.c'

src/foo.rb:this is a ruby file

これはexcludeより短いですけど、多くのシェルではビックリマークが履歴拡張を発生させますから、シングルクオートを書く必要はまだ残ってます。Git 2.13では^(カレット)がビックリマークの類語になっていますので、クオートで囲まずに同じことができます:

$ git grep this.is.a --src :^*.c

src/foo.rb:this is a ruby file

ふゎ〜、すっきりしましたね。普段はワイルドカードの*.cをシェルで書く時にクオートで囲む必要がありますけど、プラクティスとしては上記の方法が使えます。:^で始まって.cで終わるファイルがない限り、シェルはワイルドカードがどんなファイルにもマッチしないことを判定し、そのままGitに渡します。

だが、それだけではない!Git 2.13はgitattributesの値によってファイルを選択できるattrトークンを追加しています。例えば、Git LFSを使っていたら、それを設定するファイルのリストを表示したい時:

$ git ls-files

.gitattributes
README
video.mp4

$ git ls-files ':(attr:filter=lfs)'
video.mp4

というのができます。

ファイルを1つのグループにまとめるためにattributeを定義することもできます。attributeを定義し、そのattributeでファイルを選択できます:

$ echo 'libfoo/ vendored' >>.gitattributes

$ echo 'imported-tool/ vendored' >>.gitattributes
$ git grep -i license -- ':(attr:vendored)'

実はattrexcludeのトークンを組み合わせられます:

$ git grep foobar -- ':(exclude,attr:vendored)'

トークンであるattrはまだ全てのコードにおいてサポートされていないことに注意してください。いくつかのコマンドは「attrが使えない」と報告しますけど、これは将来のGitのバージョンで拡張される見込みです。

[negative pathspecs, attribute pathspecs]


条件付きの設定

Gitの設定システムは優先する階層があります:システムの段階、ユーザの段階、レポジトリの段階、それとも個人的なコマンド呼び出し(git -cを使う(リンク))オプションを設定できます。一般的に、より厳密な場所で見つかったオプションは比較的に厳密でないオプションをオーバーライドします。レポジトリの.git/configuser.emailを設定したら、ユーザの段階で設定された~/.gitconfigをオーバーライドするかもしれません。

でも、レポジトリの集まりに対して1つのオプションを設定し、また別の集まりに対して別のオプションを設定する必要がある場合はないですか?例えば、普通の仕事で自分の名前とメールを使ってコミットするけど、オープンソースでは別の名前とメールを使う場合。ホームディレクトリにあるconfigではオープンソースの資格をユーザの段階ので設定してから、仕事のレポジトリでオーバーライドできます。でもそれを一々アップデートするのは面倒だし、もし新しいレポジトリの設定をするのを忘れた場合、違う資格でコミットしてしまうこのになります!

Git 2.13は条件付き設定インクルードを提供しています。今はレポジトリのファイルシステムパスの認識しかサポートされていないけど、それで充分です。ホームディレクトリの~/.gitconfigで2つの条件付きインクルードを設定できます。

[includeIf "gitdir:~/work/"]

path = .gitconfig-work
[includeIf "gitdir:~/play/"]
path = .gitconfig-play

これで、そのファイルに適切なオプションを設定できます:

$ cat ~/.gitconfig-work

[user]
name = Serious Q. Programmer
email = serious.programmer@business.example.com

$ cat ~/.gitconfig-play
[user]
name = Random J. Hacker
email = rmsfan1979@example.com

workplayのディレクトリの中にあるレポジトリでは、適切な設定のオプションが自動的に適用されます。

[条件付きインクルード]


その他

--decorate=autogit logのデフォルトとなりました。ユーザのターミナルに出力が送られてくる時、ブランチかタグに指定されたコミットはそのブランチの名前で修飾されます。(ソース

git branchの出力ルーチンはgit for-each-refgit tagが共有しているref-filterに移りました。ということは、カスタムな出力を実現するためにgit branch --format=を使うことができます。代入のリストを閲覧するにはgit help for-each-ref(リンク)を参照してください。ところで、このパッチを作ったのは、2015年にGitのGoogle Summer of Codeの学生だったKarthik Nayakさんです。2年前に、GSoCでのプロジェクトとして紹介したref-filterはほぼ完成していた状態だったにもかかわらず、彼はまだ貢献しつづけています。Great work!(ソース)

git branch, git tag, そしてgit for-each-ref全てがもう既にあった--containsオプションと更に、--no-containsオプションが使えるようになりました。バグ(それともbugfix)がないタグかブランチを調べられる機能です。

git stashはpathspecsを受け取れるようになりました。作業のツリーの一部としてこのstashを作成できます。コミットをきれいにするために変更をいじる時には便利です。(ソース

@{upstream}@{u}、と@{push}という特別のブランチ名は小文字と大文字を区別しないようになりました。これの便利な点といえば、多くのキーボードでは@{を入力するためにshiftを押す必要があるから、大文字のUを入力してしまいます。このアップデートではshiftキーを押し続けまくってもいいぞソース

・過去のGitのバージョンでは、checkoutgrep、とls-filesみたいなコマンドがサブモジュールにおいて再起を使うことができるようになっています。git status --shortもサブモジュールについて情報を報告できるようになりました。[ソースソースソースソース]

・最近のGitのバージョンはレポジトリの発見と初期化においてのコーナーケースを直しています。その作業の最後のステップとしては、Git 2.13は見逃されたケースをキャッチするアサーションを取り入れています。数ヶ月の間に渡って開発版でこれをテストしてきたので、出てくるはずはないです。でも、BUG: setup_git_env called without repositoryは出てくるかもしれません。もし出たら、バグレポートを送るように、ご協力をよろしくお願いします。(ソース


諸事万端

700コミット以上からなるGit 2.13で追加・変更した項目は、上記にいくつかしか書いてないです。リスト全体を閲覧するには、完全リリースノートを参照してください。