somemo programming etc.

プログラマ、雑記、プログラミング関係はLinkから、数式はこっちでまとめていることが多い

【git】gitの練習【githug】

「githug」でgitの基本操作を算数ドリルみたいに学ぼう!で紹介されていたgitの練習ツールgithugを試してみました。

インストール環境

githugはgemでインストールするため、Rubyをインストールしている必要があります。自分の環境は以下のようになっています。

ruby -v
ruby 1.8.7 (2011-06-30 patchlevel 352) [i386-mingw32]
gem -v
1.7.2

インストール

下記コマンドを実行します。

gem install githug

以下、インストールされたものです。これらのriとRDocもインストールされました。

Successfully installed diff-lcs-1.1.3
Successfully installed grit-2.3.0
Successfully installed githug-0.2.7
3 gems installed

問題発生

githugを使うために、githugコマンドを実行したのですが、エラーが発生しました。

C:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `gem_original_require': no such file to load -- win32/open3 (LoadError)
        from C:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `require'
        from C:/ruby/lib/ruby/gems/1.8/gems/grit-2.3.0/lib/grit.rb:15
        from C:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `gem_original_require'
        from C:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `require'
        from C:/ruby/lib/ruby/gems/1.8/gems/githug-0.2.7/lib/githug.rb:1
        from C:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `gem_original_require'
        from C:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `require'
        from C:/ruby/lib/ruby/gems/1.8/gems/githug-0.2.7/lib/githug/cli.rb:2
        from C:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `gem_original_require'
        from C:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `require'
        from C:/ruby/lib/ruby/gems/1.8/gems/githug-0.2.7/bin/githug:2
        from C:/ruby/bin/githug:19:in `load'
        from C:/ruby/bin/githug:19

スタックトレースが表示され、最終的にはwin32/open3が読み込めていないとのことでした。

解決までの流れ

githugが通る理由は、ruby.exeと同じディレクトリに以下のような、githugを実行するディレクトリやファイル名を整えるためのバッチファイルが存在するためでした。このバッチから拡張子なしのgithug本体が呼ばれています。

@ECHO OFF
IF NOT "%~f0" == "~f0" GOTO :WinNT
@"ruby.exe" "C:/ruby/bin/githug" %1 %2 %3 %4 %5 %6 %7 %8 %9
GOTO :EOF
:WinNT
@"ruby.exe" "%~dpn0" %*
#!C:/ruby/bin/ruby.exe
#
# This file was generated by RubyGems.
#
# The application 'githug' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require 'rubygems'

version = ">= 0"

if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
  version = $1
  ARGV.shift
end

gem 'githug', version
load Gem.bin_path('githug', 'githug', version)

この中で、gemsディレクトリのgihugのCLIを読み込みます。

#!/usr/bin/env ruby
require 'githug/cli'

Githug::CLI.start

このようにいろいろ読み込んでいきます。siterubyに行き、その中のlibからさらに読み込んでいくと、grit.rbで以下のような処理を行っています。今回インストールしたgemです。

if defined? RUBY_ENGINE && RUBY_ENGINE == 'jruby'
  require 'open3'
elsif RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin/
  require 'win32/open3'
else
  require 'grit/open3_detach'
end

各環境によって読み込むものが異なっているようです。JRubyとWindows、その他です。原因が分かったところで、win32/open3をインストールします。

win32/open3

win32/open3については、Windows+Rubyで外部プログラムの標準エラー出力を受け取る"win32-open3" - 床のトルストイ、ゲイとするとのことが詳しいです。

Open3を用いることで、外部プログラムを実行しつつ、それに対応する標準入力、標準出力、標準エラー出力の3つ組を得ることができる。

今回は、gitを使うので必要であったのでしょう。https://rubygems.org/gems/win32-open3に従ってインストールします。

gem install win32-open3

以下、インストールされたものです。これらのriとRDocもインストールされました。

Fetching: win32-open3-0.3.2-x86-mingw32.gem (100%)
Successfully installed win32-open3-0.3.2-x86-mingw32
1 gem installed

再度実行

下記のように表示されればOKです。

githug
********************************************************************************

*                                    Githug                                    *

********************************************************************************

No githug directory found, do you wish to create one? [yn]

ここでyを入力すると、カレントディレクトリにgit_ugディレクトリが作成されます。その中には、.profile.ymlと.gitignoreの2つのファイルが存在します。.gitignoreで無視しているのは、2つのファイルです。.profile.ymlには、以下の内容が記載されています。

--- 
:completed_levels: 
- 
:level: init
:current_hint_index: 0
:current_levels: 
- 
- init
- add
- commit
- config
- clone
- clone_to_folder
- ignore
- status
- rm
- rm_cached
- rename
- log
- tag
- commit_ammend
- reset
- checkout_file
- remote
- remote_url
- pull
- remote_add
- diff
- blame
- branch
- checkout
- branch_at
- merge
- cherry-pick
- rename_commit
- squash
- merge_squash
- reorder
- bisect
- stage_lines
- find_old_branch
- revert
- restore
- contribute
:current_attempts: 0

githugの学習状態と学習するgitコマンドだと思います。

学習内容

実際にやってみました。こなしていくと、.profile.ymlの学習状態が変化したり、ファイルが作成・削除されていきます。分からなくなったときは、ヘルプやヒントを使いましょう。

  1. git init (READMEが作成される)
  2. git add .
  3. git commit -m "add README"
  4. git configで設定した名前とメールの入力(初gitでないので、設定済み)
  5. git clone 指定されたリポジトリ
  6. git clone 指定されたリポジトリ 指定された名前(知らなかった)
  7. .gitignoreを修正して指定された拡張子を無視する
  8. git statusで追跡されていないファイルの確認(TortiseGitで見れてしまった・・・)
  9. git statusとgit rmを用いてファイルシステムに存在しないが、リポジトリに存在するファイルの削除
  10. git rm --cached 指定されたファイルでリポジトリから除外しつつ、ファイルシステムに残す(resetと勘違いしてた)
  11. git mv 古いファイル名 新しいファイル名で追跡可能になるようにファイル名の変更
  12. git logで最近のコミットのハッシュ先頭7文字を入力する
  13. git tag 指定されたタグ名でタグの作成
  14. git addとgit commit --amendで、前回コミットし忘れたファイルを前回のコミットとして追加
  15. git resetとgit addで、分割してコミットするはずのファイルを、まとめてコミットしたので、いったんHEADに戻してから、1つだけ追加
  16. 編集してしまったファイルを保持せず、最後のコミットの状態にしたい。⇒ファイル削除したら次のレベルに行ってしまった。
  17. git remoteで、remoteのプロジェクトの名前を調べる(git remote xxxは知っていたけど、remote単体は知らなかった)
  18. git remote -vで、remoteにあるリポジトリのURLを調べる(git remoteのヘルプで分かった)
  19. git pull origin masterで、remoteのmasterブランチと同じ状態に更新。(知らなかった)
  20. git remote add origin 指定されたURLで、remoteに追加。
  21. git diffで、指定されたファイルの変更を確認し、変更された行を入力。
  22. git blame 指定されたファイルで、指定された変更を誰が編集したかを確認して入力。(知らなかった)
  23. git branch 指定されたブランチ名で、指定されたブランチを作成。
  24. git checkout -b 指定されたブランチ名で、指定されたブランチを作成し、ブランチを変更。
  25. git branch 指定されたブランチ名 ブランチ名~1で、ブランチを切らずにコミットしてしまったので、指定しているブランチの1つ前のコミットからブランチを切る。(知らなかった)
  26. git merge 指定されたブランチ名で、masterブランチに指定されたブランチの内容をマージする。
  27. 状況:新しい機能を追加するために、ブランチを作成したが、機能は必要なくなった。ただし、READMEの変更内容はmasterに反映したい。以下の手順で解決。   
    1. git checkout 変更内容の存在するブランチ名
    2. git logでREADMEに関するコミットのハッシュを確認
    3. git checkout master
    4. git chery-pick ハッシュで、このハッシュのときにコミットした内容をmasterに追加する。
  28. git rebase -i ハッシュまたはHEAD~nでコミットログの変更。変更時には、pickをrewordに変更後、コミットメッセージを変更する。(知らなかった。)
  29. git rebase -i HEAD~nでコミットを1つにまとめる。まとめるときには、pickをsquashに変更後、コミットメッセージを変更する。今回は、3つのコミットをまとめたので、そのままコミットするとコミットメッセージが3行以上になった。(知らなかった。fixupにすると、fixupの上のコミットのメッセージが使用される。)
  30. git merge --squash 指定されたブランチ名で、指定されたブランチの変更すべてを、現在のブランチの1つの変更にまとめる。(知らなかった。コミットはされないので、最後にコミットすること。このあと、githug実行すると、バグが発生するので、githug resetを実行すること)
  31. git rebase -i HEAD~nで、pickを入れ替えてコミット順を変更する。
  32. git bisectを使ってバグを二分探索して見つけ、そのバグを埋め込んだハッシュを入力する。やりたいことはわかるが、コマンドで特定できなかったので、ログを見て最初に埋め込んだハッシュを見つけた…。
  33. git add -pで変更箇所の編集(diff形式)を編集し、2行中最初の1行をaddした状態に変更した。-p後のオプションはたくさんあるので、参考URLを見てください。今回は、eを使いました。
  34. git reflogを使って、HEADの履歴ログを表示し、1つ前に作業していたブランチに戻る。git reflogはコミットからチェックアウトまで表示できます。また、resetで戻しすぎた場合でも確認できるようです。
  35. git revert 指定されたコミットのハッシュで、その時点のコミットに戻すコミットをします。resetと違って戻すだけではないといところがポイントです。そのため、git logでも戻したことが確認できます。resetの場合は、ログに残りません(一つ前に学んだreflogには残ります)。
  36. git reflogとgit reset --hard HEAD@{n}で、戻しすぎたresetからの復活。
  37. これが最後のレベルであり、目標はGitHub上にプル要求を行うことによってこのリポジトリに貢献することです。このレベルはあなたがプル要求を作成する能力をテストしていない、Githugに有効な貢献を追加することを奨励するように設計されていることに注意してください。受け入れられる可能性があるの貢献は、レベル、バグ修正や改良されたドキュメントです。
    gitらしい終わり方でした。Forking the repository would be a good start!とあったので、githubからforkしました。その後、githug実行したらCloning repository to /tmp/githugって言われたのでWindows詰みました。

疲れたー。

参考