20210605のGitに関する記事は12件です。

Railsチュートリアル(第6版) 第12章 パスワードの再設定

第12章 第11章ではアカウント有効化の実装が完了した。 今回の章ではパスワードを忘れた時に再設定できる機能を追加する。 やることは第11章と似ている。 流れ 1.ユーザーがパスワードの再設定をリクエストすると、ユーザーが送信したメールアドレスをキーにしてデータベースからユーザーを見つける 2.該当のメールアドレスがデータベースにある場合は、再設定用トークンとそれに対応する再設定ダイジェストを生成する 3.再設定用ダイジェストはデータベースに保存しておき、再設定用トークンはメールアドレスと一緒に、ユーザーに送信する有効化用メールのリンクに仕込んでおく 4.ユーザーがメールのリンクをクリックしたら、メールアドレスをキーとしてユーザーを探し、データベース内に保存しておいた再設定用ダイジェストと比較する(トークンを認証する) 5.認証に成功したら、パスワード変更用のフォームをユーザーに表示する PasswordResetsリソース まずは、PasswordResetsリソースのモデリングから始める。 前章と同じく新たなモデルは作らず、代わりに必要なデータをUserモデルに追加する。 PasswordResetsをリソースとして扱うため、RESTfulなURLを用意する。 今回は、ビューを描画するためのnewアクションとeditアクションが必要になる。 $ git checkout -b password-reset トピックブランチを作成する。 PasswordResetsコントローラ newアクションとeditアクションを含んだコントーラーの作成 $ rails generate controller PasswordResets new edit --no-test-framework テストを生成しないオプションを指定している。 理由として、統合テストでカバーしていくから。 新しいパスワードを再設定するためのフォームとUserモデル内のパスワードを変更するためのフォームが必要。 従って、new、create、edit、updateのルーティングを用意する。 config/routes.rb Rails.application.routes.draw do resources :password_resets, only: [:new, :create, :edit, :update] end HTTPリクエスト URL Action 名前付きルート GET /password_resets/new new new_password_reset_path POST /password_resets create password_resets_path GET /password_resets/トークン/edit edit edit_password_reset_url(token) PATCH /password_resets/トークン update password_reset_url(token) RESTfulルーティング パスワード再設定画面へのリンクを追加 <%= link_to "(forgot password)", new_password_reset_path %> app/views/sessions/new.html.erb <% provide(:title, "Log in") %> <h1>Log in</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_with(url: login_path, scope: :session, local: true) do |f| %> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> <%= f.label :password %> <%= link_to "(forgot password)", new_password_reset_path %> <%= f.password_field :password, class: 'form-control' %> <%= f.label :remember_me, class: "checkbox inline" do %> <%= f.check_box :remember_me %> <span>Remember me on this computer</span> <% end %> <%= f.submit "Log in", class: "btn btn-primary" %> <% end %> <p>New user? <%= link_to "Sign up now!", signup_path %></p> </div> </div> 新しいパスワードの設定 今までのように、トークン用の仮想的な属性とそれに対応するダイジェストを用意する。 トークン関連で学んだ事だが、トークンをハッシュ化せず(平文で)データベースに保存してしまうと、攻撃者がトークンを盗み出したときセキュリティ上問題がある。なので、ダイジェストを使おう。 また、再設定用リンクは期限を設ける。そのために、送信時刻を記録する必要がある。 以上を踏まえて、reset_digest属性とreset_sent_at属性をUserモデルに追加する。 参照:railsチュートリアル 上記のUsersテーブルのするためマイグレーションを実行 $ rails generate migration add_reset_to_users reset_digest:string reset_sent_at:datetime いつものようにマイグレーション $ rails db:migrate 新しいパスワード再設定画面ビュー app/views/password_resets/new.html.erb <% provide(:title, "Forgot password") %> <h1>Forgot password</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_with(url: password_resets_path, scope: :password_reset, local: true) do |f| %> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> <%= f.submit "Submit", class: "btn btn-primary" %> <% end %> </div> </div> 演習 解答例 シンボルを使ってフォームを送信する事で、Railsが自動的に送信先に値を割り当てる。 createアクションでパスワード再設定 Forgot passwordから送信後、メールアドレスをキーとしてユーザーをデータベースから探し、パスワード再設定用トークンと送信時のタイムスタンプでデータベースの属性を更新する。 続いて、ルートURLにリダイレクトし、フラッシュメッセージを表示する。なお、送信が無効な場合は、newページを出力してfash.nowメッセージを表示する。 app/controllers/password_resets_controller.rb class PasswordResetsController < ApplicationController def new end def create @user = User.find_by(email: params[:password_reset][:email].downcase) if @user @user.create_reset_digest @user.send_password_reset_email flash[:info] = "Email sent with password reset instructions" redirect_to root_url else flash.now[:danger] = "Email address not found" render 'new' end end def edit end end Userモデルにもコードを追加していく。(create_activation_digestメソッドと似ている) app/models/user.rb class User < ApplicationRecord attr_accessor :remember_token, :activation_token, :reset_token # パスワード再設定の属性を設定する def create_reset_digest self.reset_token = User.new_token update_attribute(:reset_digest, User.digest(reset_token)) update_attribute(:reset_sent_at, Time.zone.now) end # パスワード再設定のメールを送信する def send_password_reset_email UserMailer.password_reset(self).deliver_now end end この時点では、無効なメールアドレスを入力すると正常に動作する。 そして、正しいメールアドレスを送信した場合にも正常に動作させるには、パスワード再設定のメイラーメソッドを定義する必要がある。 演習 エラーメッセージの確認 パスワード再設定のメール送信 パスワード再設定に関するメールを送信する部分を作成する。 第11章でUserメイラーを生成した時、デフォルトのpassword_restメソッドもまとめて生成されている。 パスワード再設定のメールとテンプレート UserメイラーにあるコードをUserモデルに移すリファクタリングを行う。 UserMailer.password_reset(self).deliver_now このコードの実装のために必要なpassword_resetメソッドを、Userメイラーに設定する。 app/mailers/user_mailer.rb def password_reset(user) @user = user mail to: user.email, subject: "Password reset" end パスワード再設定テンプレート(テキスト) app/views/user_mailer/password_reset.text.erb To reset your password click the link below: <%= edit_password_reset_url(@user.reset_token, email: @user.email) %> This link will expire in two hours. If you did not request your password to be reset, please ignore this email and your password will stay as it is. パスワード再設定テンプレート(HTML) app/views/user_mailer/password_reset.html.erb <h1>Password reset</h1> <p>To reset your password click the link below:</p> <%= link_to "Reset password", edit_password_reset_url(@user.reset_token, email: @user.email) %> <p>This link will expire in two hours.</p> <p> If you did not request your password to be reset, please ignore this email and your password will stay as it is. </p> Railsのメールプレビュー機能でプレビューを設定する。 test/mailers/previews/user_mailer_preview.rb class UserMailerPreview < ActionMailer::Preview def password_reset user = User.first user.reset_token = User.new_token UserMailer.password_reset(user) end end メールのプレビュー 送信メールのテスト メイラーメソッドのテストを書く。 test/mailers/user_mailer_test.rb test "password_reset" do user = users(:michael) user.reset_token = User.new_token mail = UserMailer.password_reset(user) assert_equal "Password reset", mail.subject assert_equal [user.email], mail.to assert_equal ["noreply@example.com"], mail.from assert_match user.reset_token, mail.body.encoded assert_match CGI.escape(user.email), mail.body.encoded end パスワードを再設定する PasswordResetsコントローラのeditアクションの実装を進める。 また、統合テストでも行う。 editアクションで再設定 パスワード再設定の送信メールには、以下のようなリンクがある。 https://example.com/password_resets/3BdBrXeQZSWqFIDRN8cxHA/edit?email=fu%40bar.com このリンクを機能させるには、パスワード再設定のフォームを表示するビューが必要だ。 今回ちょっと面倒なのが、メールアドレスをキーとしてユーザーを検索するのに、editアクションとupdateアクションの両方でメールアドレスが必要になる。 なので、最初はメールアドレス入りリンクのおかげでeditアクションはOK。 しかし、フォームを一度送信すると、この情報は消える。 故に、この値をどこかに保存しよう!となる。 そこで今回使うのは、隠しフィールド。これでページ内にメールアドレスを保持し、フォームから送信した時に他の情報と一緒にメールアドレスが送信されるようになる。 app/views/password_resets/edit.html.erb <% provide(:title, 'Reset password') %> <h1>Reset password</h1> <div class="row"> <div class="col-md-6 col-md-offset-3"> <%= form_with(model: @user, url: password_reset_path(params[:id]), local: true) do |f| %> <%= render 'shared/error_messages' %> <%= hidden_field_tag :email, @user.email %> <%= f.label :password %> <%= f.password_field :password, class: 'form-control' %> <%= f.label :password_confirmation, "Confirmation" %> <%= f.password_field :password_confirmation, class: 'form-control' %> <%= f.submit "Update password", class: "btn btn-primary" %> <% end %> </div> </div> 上のタグでは、フォームタグヘルパーを使っている。 hidden_field_tag :email, @user.email 今までは以下のようなコードを書いていた f.hidden_field :email, @user.email ・ポイント 前者のhidden_field_tagではメールアドレスがparams[:email]に保存される。 後者はparams[:user][:email]に保存される。 今後、上記のフォームを描画するためにPasswordResetsコントローラのeditアクション内で@userインスタンス変数を定義する。 params[:email]のメールアドレスに対応するユーザーを先ほどの変数に入れる。 そして、params[:id]の再設定用トークンとauthenticated?メソッドで、このユーザーが正当なユーザーかことを確認する。 editアクションとupdateアクションのどちらでも正当な@userが存在しなければならないため、beforeフィルタで@userの検索とバリデーションを行う。 app/controllers/password_resets_controller.rb class PasswordResetsController < ApplicationController before_action :get_user, only: [:edit, :update] before_action :valid_user, only: [:edit, :update] . . . def edit end private def get_user @user = User.find_by(email: params[:email]) end # 正しいユーザーかどうか確認する def valid_user unless (@user && @user.activated? && @user.authenticated?(:reset, params[:id])) redirect_to root_url end end end メールアドレスを入力し送信した結果にあったリンクから開くと、、、 パスワード再設定用のページが出てきた。 演習 現時点では、パスワードを更新するボタンを押しても動作しない。 パスワードを更新する フォームからの送信に対応するupdateアクションが必要だ。 updateアクションの4つのケース 1.パスワード再設定の有効期限が切れていないか 2.無効なパスワードであれば失敗させる(失敗した理由も表示する) 3.新しいパスワードが空文字列になっていないか(ユーザー情報の編集ではOKだった) 4.新しいパスワードが正しければ、更新する 1、2、4はこれまでの知識で対応可能。 1は、editとupdateアクションに以下のメソッドとbeforeフィルターを用意し対応する。 before_action :check_expiration, only: [:edit, :update] # (1)への対応案 check_expirationメソッドは、有効期限をチェックするPrivateメソッドとして定義する。 # 期限切れかどうかを確認する def check_expiration if @user.password_reset_expired? flash[:danger] = "Password reset has expired." redirect_to new_password_reset_url end end 上記のメソッドを呼び出しbeforeフィルターで保護したので、2と4のケースには対応できそう。 2は、無効なパスワードであれば失敗はOK 4は、更新が成功したらパスワードを再設定後、ログインさせればOK 問題としてパスワードが空文字だった時のケース。 以前Userモデルを作っていた時に、パスワードが空でも良いという実装をした。 従って、今回は明示的にキャッチする必要がある。 今回は、@userオブジェクトにエラーメッセージを追加するという方法を取る。 やり方は、errors.addを使ってエラーメッセージを追加する。 @user.errors.add(:password, :blank) 以上の結果から、1を除いたすべてのケースに対応したupdateアクションが完成する。 app/controllers/password_resets_controller.rb class PasswordResetsController < ApplicationController before_action :check_expiration, only: [:edit, :update] # (1)への対応 def update if params[:user][:password].empty? # (3)への対応 @user.errors.add(:password, :blank) render 'edit' elsif @user.update(user_params) # (4)への対応 log_in @user flash[:success] = "Password has been reset." redirect_to @user else render 'edit' # (2)への対応 end end private def user_params params.require(:user).permit(:password, :password_confirmation) end # トークンが期限切れかどうか確認する def check_expiration if @user.password_reset_expired? flash[:danger] = "Password reset has expired." redirect_to new_password_reset_url end end @user.password_reset_expired?を動作させるため、password_reset_expired?メソッドをUserモデルに定義する。 2時間以上パスワードが再設定されなかった場合には、期限切れとする。 reset_sent_at < 2.hours.ago 不等号の「<」は、「より少ない」と読むのではなく「早い」と考えると良い。 今回は2時間より早い=2時間以内捉えられる。 password_reset_expired?メソッドの実装。 app/models/user.rb class User < ApplicationRecord . . . # パスワード再設定の期限が切れている場合はtrueを返す def password_reset_expired? reset_sent_at < 2.hours.ago end private . . . end これで全体の実装はOK パスワードの再設定をテストする 送信に成功した場合と失敗した場合の統合テストを作成する。 パスワード再設定のテストファイルを生成する。 $ rails generate integration_test password_resets パスワード再設定手順 ・「forgot password」フォームを表示して無効なメールアドレスを送信 ・次にそのフォームで有効なメールアドレスを送信 ・後者ではパスワード再設定用のトークンが生成され、再設定用のメールが送信される。 ・続いて、メールのリンクを開き、無効な情報を送信 ・次にそのリンクから有効な情報を送信 ・それぞれが期待通りに動作しているかの確認を行う。 パスワード再設定の統合テスト test/integration/password_resets_test.rb require 'test_helper' class PasswordResetsTest < ActionDispatch::IntegrationTest def setup ActionMailer::Base.deliveries.clear @user = users(:michael) end test "password resets" do get new_password_reset_path assert_template 'password_resets/new' assert_select 'input[name=?]', 'password_reset[email]' # メールアドレスが無効 post password_resets_path, params: { password_reset: { email: "" } } assert_not flash.empty? assert_template 'password_resets/new' # メールアドレスが有効 post password_resets_path, params: { password_reset: { email: @user.email } } assert_not_equal @user.reset_digest, @user.reload.reset_digest assert_equal 1, ActionMailer::Base.deliveries.size assert_not flash.empty? assert_redirected_to root_url # パスワード再設定フォームのテスト user = assigns(:user) # メールアドレスが無効 get edit_password_reset_path(user.reset_token, email: "") assert_redirected_to root_url # 無効なユーザー user.toggle!(:activated) get edit_password_reset_path(user.reset_token, email: user.email) assert_redirected_to root_url user.toggle!(:activated) # メールアドレスが有効で、トークンが無効 get edit_password_reset_path('wrong token', email: user.email) assert_redirected_to root_url # メールアドレスもトークンも有効 get edit_password_reset_path(user.reset_token, email: user.email) assert_template 'password_resets/edit' assert_select "input[name=email][type=hidden][value=?]", user.email # 無効なパスワードとパスワード確認 patch password_reset_path(user.reset_token), params: { email: user.email, user: { password: "foobaz", password_confirmation: "barquux" } } assert_select 'div#error_explanation' # パスワードが空 patch password_reset_path(user.reset_token), params: { email: user.email, user: { password: "", password_confirmation: "" } } assert_select 'div#error_explanation' # 有効なパスワードとパスワード確認 patch password_reset_path(user.reset_token), params: { email: user.email, user: { password: "foobaz", password_confirmation: "foobaz" } } assert is_logged_in? assert_not flash.empty? assert_redirected_to user end end 新しいものとして、inputタグがある。 assert_select "input[name=email][type=hidden][value=?]", user.email これは、inputタグに正しい名前、tyepe="hidden"、メールアドレスがあるか確認している。 <input id="email" name="email" type="hidden" value="michael@example.com" /> テストはOK 本番環境でのメール送信 本番環境でのメール送信は第11章でやったので、あとはGitのトピックブランチをmasterにマージしておく。 $ rails test $ git add -A $ git commit -m "Add password reset" $ git checkout master $ git merge password-reset リモートレポジトリにプッシュし、Herokuにデプロイ $ rails test $ git push && git push heroku $ heroku run rails db:migrate 最後に 第11章と似ている個所もあったが、難しい章になったと思う。 残りの章では、マイクロポスト機能とフォローなどのステータスフィード機能を実装していく。 has_manyやhas_many:throughが出てくる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GitHub Enterprise

【GitHub Enterprise】 実務でTortoiseSVNしか使ったことがないので、GitHub Enterpriseを簡単に調べました。 GitHub Enterpriseとは。 企業の導入に適しているGit 超有名企業で数多く導入されており、信頼できる。 Gitに対応したバージョン管理システムであるGitHubと同等の機能を利用可能。 プルリクエスト、Issueなどの機能とユーザ管理やオンラインバックアップなどの運用をサポートする。 AWSやGCPでの導入も容易である。 もちろん従来のオンプレミスにも対応している。 1つのプロダクトであり、Webサービスでない。 自社サーバーにインストールすることが可能になるため、社外にソースコードを置くことなく管理できる。 オンプレミスで開発している現場でも使用できる。  → セキュリティに厳しい要件に対して、自社内で管理できる。 GitHubと同様に2段階認証や組織・チームのパーミッションの機能を備えている。  → GitHubと操作が似ているようなので、直感的に操作できそうだ。 余談 チーム開発で必須なバージョン管理システムの知識をもっと深めていく。 業務内でのTortoiseSVN、業務外でのGitHub、これらの扱いを通じて、基礎を抑える。 今後、応用が効いてくるところがあればその都度勉強していこうと思う。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GitHub プルリクエスト

【GitHub プルリクエスト】 コードレビューをする文化は根付いていますか? 自分のこれまでの実務では、コードレビューする文化はなかったです。 SIerとWeb系の違いのでしょうか。自分にはわかりません。 新人の自分が書いたコードを先輩方が確認する工数は無く、いつも納期に追われていました。。 コードレビュー無しで、push(自分はSVNを使用していたので、コミット)した結果、開発中のプロジェクトでエラーを出して、ご迷惑をお掛けしてしまうことがありました。 これを改善していくためには、プルリクエストを当たり前にした開発を行なっていく必要があるようです。 プルリクエストとは。 GitHubが最初に提供した機能であり、開発者のローカルリポジトリでの変更を他の開発者に通知するもの。 この機能のおかげで、多くの開発者がOSS開発に参加しやすくなり、結果として品質の高いコードを作ることが可能になった。 主に以下の機能がある。 ・機能追加や改修など、作業内容をレビュー・マージ担当者やその他関係者に通知 ・ソースコードの変更箇所をわかりやすく表示 ・ソースコードに関するコミュニケーションの場を提供 プルリクエストのメリット レビュー・マージ作業をタスク化して、やりとりを記録できる。  → 作成されたプルリクエストを一覧で見ることができる。  → 完了・未完了が視覚的にすぐわかるので、漏れを防ぐことができる。  → プルリクエスト上でコメントのやり取りをして、品質向上に繋げられる。 レビューを促進できる。  → ソースコードの変更部分を明確に表示することができる。  → プルリクエストの作成者が、ソースコードの意図や補足事項をコメントとして伝えることができる。  → レビュー担当者の負担を減らすことができる。 参考 サル先生のGit入門 プルリクエスト   参考にさせていただきました。 ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VSCodeのGitでpushする時の「The current branch 'master' has no upstream branch...」メッセージの解決策

はじめに プログラミング初学者の@kat_logと申します。 自分と同じ初学者の状況の方がいるか分かりませんが、 ターミナルでのgit練習後→VSCodeのgit機能も使ってみよう!と思い VSCodeの機能でmasterブランチにpushしようとした時に The current branch 'master' has no upstream branch... というメッセージが出て戸惑ったのですが、解決したため共有です。 結論 gitにて「上流ブランチ」(英語でupstream branch)の設定が必要でした。 設定 次回push時に-uオプションを付けると設定できます。 git push -u origin master これでpushと同時にpush先のmasterブランチを上流ブランチとしても設定することができるので、VSCodeからpushがうまくいくようになります。 ちなみに、 git push は、特に初めてブランチをPushするときに「-u」オプションをつけることを推奨されます。 とのことなので、 ブランチ作成後の最初のgit pushには-uをつける と覚え「ブランチ作成と上流ブランチの設定」はセットで行うようにすると良いみたいですね。 おわりに お読みいただきありがとうございました。 自分と同じく初学者の参考になれば嬉しいです。 ↓に載せております参考ページがすごく分かりやすくて勉強になりました! 一緒に頑張っていきましょう〜😄 参考 Git用語:上流ブランチとは? git push -u オプションで”上流ブランチ”を設定 [git][初心者向け]ブランチ作成直後のpushの注意事項
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gitコマンド集

共同開発で使うGitコマンド集 Gitコマンドを使用する際に、良く使うものをまとめました。 完全に自身の備忘録です。 基本 $ pwd //現在位置のパスを表示する。 $ cat ファイル名 //ファイルをターミナルに表示 $ mkdir ディレクトリ名 //ディレクトリの作成。 $ ls //ディレクトリの内容を表示する。 $ git init             //ローカル環境にリポジトリを作成。 $ git clone http://xxxxxx.xxx/xxxx //リモートリポジトリのコピーを作成する。 remote $ git remote add origin http://xxxxxx.xxx/xxxx //ローカルリポジトリをリモートリポジトリに登録する。 $ git remote rm リモートリポジトリ名 //逆にリモートリポジトリを削除する。 $ git remote seturl origin 変更先のURL //URLの変更が可能(間違ったURLを設定してしまった場合) $ git remote rename 変更前のリポジトリ名 変更後のリポジトリ名 //リポジトリ名を変更する。 ファイルの削除を記録する(removeの略) $ git rm ファイル名 //ファイルを削除する。 $ git rm -r ディレクトリ名 //ディレクトリを削除する。 $ git rm --cached ファイル名  //ファイルを残したいとき。ファイルは使うが、gitの記録(リポジトリ)には削除したい。  //ワークツリーには残っている。 削除したファイルを戻す $ git reset HEAD ファイル名 $ git checkout ファイル名 ファイルの移動を記録する(moveの略)。 ※以下のコマンドと同じ  mv 旧ファイル名 新ファイル名  git rm 旧ファイル名  git add 新ファイル名 $ git mv 旧ファイル名 新ファイル名 現在の状態を確認する。 $ git status       //gitの記録状態を確認する事ができる。 $ git log       //gitのlogを確認する事ができる。 $ git log --oneline    //gitのログを一行で表示する。 $ git log -p ファイル名  //ファイルの変更分を表示する。 $ git log -n "コミット数" //最近のコミットだけ表示する。 $ git log --since=2020-12-1 //2020年12月1日以降のログを出力する。 $ git remote     //存在しているリモートリポジトリ(アクセス先)の一覧表示ができる。 $ git remote -v //-vオプションでurlなど詳細まで表示可能。 変更差分を確認する $ git diff //git addする前の変更分 $ git diff ファイル名 $ git diff --staged  //git addした後の変更分 変更を記録する。 $ git add ファイル名 //ファイルをステージングエリアに追加する。 $ git add . //変更した内容をすべてステージングエリアに追加する。 $ git commit -m "コミットメッセージ" //リポジトリにコミットする。 $ git commit -v //-vオプションを指定することでgitエディターが開く。 $ git push origin リモートブランチ名 //リモートリポジトリにプッシュする。 checkout $ git checkout ブランチ名    //ブランチを切り替えたい時に使用するコマンド $ git checkout -b ブラント名   //「-b」オプションを指定することで、ブランチの作成とチェックアウトが同時にできる。 $ git chechout -f ブランチ名 // 変更内容を吐きして、強制的にブランチを切り替える ファイルの変更を取り消す $ git checkout -- ファイル名 //--を付けているのはブランチ名とファイル名が被った時にどちらを指しているのか、Gitが分かるようにするため。 $ git checkout -- ディレクトリ名 $ git checkout --. //全変更を取り消す ステージした変更を取り消す。 $ git reset HEAD ファイル名 //HEADは最新のコミットのこと $ git reset HEAD ディレクトリ名 $ git reser HEAD //全変更を取り消す。 直前のコミットをやり直す 注意)リモートリポジトリにPushした内容はやり直すのはNG 別の誰かがpushされた内容を自分のワークツリーに取り込んで、作業後リモートリポジトリにpushしても取り込めない為 $ git commit --amend コミット前の内容を一時的に避けておく $ git stash  //コミット前の内容を一時的に退避させておける(今の作業を中断して別ブランチに切り替えたい)。  //stachはこっそり隠すの意味。  //実行結果  Saved working directory and index state On {作業ブランチ名}: {コメント} $ git stash save "コメント" //コメントを残すことも可能。 $ git stash apply  //元の作業ブランチに戻り、退避した内容を今いるブランチに適用する。 $ git stash drop  //直近のスタッシュを削除する。 $ git stash clear //スタッシュをすべて削除する。 branch $ git branch ブランチ名 //ブランチの作成 $ git branch //作成されたブランチの確認 $ git pull origin リモートブランチ名 //リモートリポジトリの内容を取得し、現在のブランチに取り込む(「fetch」と「merge」を行う) $ git fetch  //Web上に保存されているコミットログ(更新記録)を、自分のパソコンに反映させる。  //開発している人全員の更新記録を自分のパソコンに取り込むことが出来る。 //(更新がなければ何も表示されない。)  $ git merge ブランチ名 //ローカル内のみでブランチを統合する事が出来る。     $ git rebase ブランチ名 コマンドにエイリアスをつける $ git config --global alias.エイリアス名 commit //--globalでPC全体に反映される。 管理しないファイルをGitの管理から外す方法 .gitignore //ファイルを指定する。 その他 //コミットメッセージの書き方 //1行目 変更内容の要約 //2行目 空白 //3行目 変更した理由 //originはリモートのアクセス先を示したもの。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【実務1ヶ月目で学んだ】個人開発と実務のgit開発フローの違い & テクニック

記事について 完全未経験状態から独学で勉強し、今年の4月から都内一部上場企業のwebエンジニアとして働くことになりました。独学で今までいろいろ勉強してましたが、やっぱり実務レベルだとまだまだひよっこエンジニアです。 そこで月に1~2回、学んだことをアウトプットするために記事を書いていこうと思います。 記事内容は実務使う技術だったり、技術書や動画で学んだことだったり、適当に興味あることを書いていきたいと思います。 今回の背景 実務初心者なら誰しも一度はミスするであろうgitを扱いたいと思います。 エンジニアを目指して勉強していた時期にもgitの重要性は理解していましたし、gitの基本的な使い方も理解していました。ただ実務ではミスったり、無駄に時間をかけてしまったりしてしまうことが多々起こりました。。。なので今回は反省の意味をこめてこの記事を書こうと思いました。 前提知識 gitの基本の使い方は米国AI開発者がやさしく教えるGit入門講座を通して学んでいます。そのため「コミットとは?」・「masterブランチとは?」などの基本的なこと、gitコマンドの基本的な使い方、gitとgithubを使った大まかな開発の流れについては理解している前提で記事を書いています。もしわからない方がは上記のudemy講座がおすすめです。 目次 個人レベルと実務レベルのgit開発の違い4選 複数環境が存在する場合の開発フローを学ぶ git rebaseをして最新のdevelopに追従する git revertでコミットを消す git stashを使って作業中のコードを一時対比する git cherry-pickで特定のコミットだけを適用させる --squshで複数のコミットを1つにまとめる ミスったときはこのコマンドを使うgit rebase -i & git reflog 最後に、、、 1. 個人レベルと実務レベルのgit開発の違い4選 私が感じた違いをざっと4つほど紹介します。 複数環境が存在する 個人レベルでは開発環境と本番環境の2つを使えばいいですが、実務ではテスト環境が複数あります。例えば本番環境デプロイ前に確認するstaging環境だったり、もっと気軽に試せるテスト環境だったりです。そのためgithub上のブランチもmasterだけでなく、テスト環境用のブランチも育てていく必要があります。まあ慣れれば大したことではないかもですが、個人開発しかしていないと、「git push」「git pull」などコマンドはmasterだけに行うことが多かったので初心者としては少しびっくりポイントかもです。 複数人での開発・自分の知らぬ間にブランチが更新されている これが一番驚きですね。ものによってはコンフリクトが起きまくりだったり、主要なコードが置き換わったりすると自分の作業中のコードに影響がでたり、、。適宜最新のブランチに追従していかないと、いざmergeするとき大変です。。特に大きなプロジェクトになると上がってくるPRの数も半端ないので、気をつけないといけないです。 ローカルルールが多い だろうなとは思いましたが、やはりプロジェクトによってもgitルールが全然違います。例えば私の所属しているところは比較的安定しているプロジェクトなので、丁寧なコードレビュー制度が敷かれていたり、テスト環境へ上げるためのフローがきっちり組まれています。最初のうちは誰がレビューの権限を持っていて、どのタイミングでテスト環境へデプロイがOKなのか結構迷いました。 教える側と教わる側のgitに関するgitの認識の違い 私の会社ではありがたいことに非常に丁寧に教わっているのですが、おそらく会社にいる方々はgit開発フローが日常過ぎて初心者の引っかかりポイントが見えにくいのだと思います。例えば入社当初「git rebase して最新のものに追従お願いします!」っていう指示仰いだときは、rebaseの存在をあまり知らなかったので頭?状態でした。。。(案の定ミスりましたが笑) 以上が私自身が感じたポイントでした。 入社も2ヶ月たつとgitは毎日使うので自然に身についてきます。ただそれでもたまにミスしたり、わからなくなることはあります。なので困ったことをより具体的にこれから書いていきたいと思います。 2. 複数環境が存在する場合の開発フローを学ぶ こちらに関してもおそらく会社やプロジェクトによってかなり違ってくると思います。ただ開発用・テスト用・本番用のブランチに分けて運用するという基本はどこでも使われていると思いますし、基本抑えとけば他でもある程度どこでも最初ほど苦労することはないと思います。 今回は開発用のブランチはdevelopブランチ、テスト用のブランチはstagingブランチ、本番用はmasterブランチとしてまとめます。またgithubを想定しています。 ①「git pull」 でまずは自分のローカルに開発用ブランチをセットします。このpullは定期的にしないとoriginのdevelopはどんどん進化していくので差分が大きくなってしまいます。ローカルで新しいトピックブランチを作る前に毎回pullするのが個人的には必須だと思ってます。 ②作業用のトピックブランチをつくります。今回はブランチ名をtopicにしています。機能が大きいときはここからさらに子ブランチを作って作業したりもします。 ③作業用のブランチが一息ついた、もしくは出来上がった場合は「git push」してoriginにtopicブランチを作成します。ここでPR(プルリクエスト)も作成します。 ④おそらくPRをmergeする前にレビューや確認が入ります。これがOKの場合はgithub上でPRをmergeします。 ⑤そしてここも会社によってフローが違ったり順番は前後しますが、レビューでOKもらったあとにstagingにあげます。localで「staging checkout」します。 ⑥stagingも差分がある可能性が高いので一旦pullしてから、今回の作業分の差異をとりこむためtopicをmergeします。 ⑦コンフリクトがある場合は修正し、「git push」します。 ⑧そしてテスト環境で問題なければgithub上でdevelopからmasterへmergeして完了です。 あとはこれを繰り返すだけです。あと説明すっ飛ばしましたが、基本的にpushやmergeすると同時にCI/CDが走っていることが多いと思うので、gitコマンドを打てば自動的にテストやデプロイができるはずです(これももちろん会社やプロジェクトによって違う)。やっぱり最初はブランチの量とかルールに圧倒されますが、大まかな流れさえ抑えてけばこっちのもんです。個人的にも新しいプロジェクトにもし入る場合はまずgitのこのフローを確認していきたいです。 そして次からはもっと具体的に実務ではじめてかつよく使うgitのコマンドを紹介します。 3. git rebaseをして最新のdevelopに追従する 自分以外の人がどんどん開発していくと、自分が作業しているブランチと開発ブランチの差異が発生します。pullで解決することもできますが、rebaseするとcommitもすっきりするのでrebaseすることが多いです。rebaseするとcommit履歴の歴史が変わるので一緒に作業していると不都合もあるのですが、自分の作業ブランチならほとんど問題にならない事が多いです。 rebaseについての記事は下記がとてもわかりやすかったです。 git rebase についてまとめてみた 細かい説明は上記参考にするとして、コマンドの流れだけ書きます。 ①まずは共通開発ブランチで最新版をpullします。 $ git pull origin develop ②そしたら自分が作業していたブランチに移動します $ git checkout topic ③rebaseコマンドをします $ git rebase develop 以上で完了です。このコマンドで最新developに追従した状態になります。 ただpushする際は注意が必要で、すでにPRがある場合は-fオプションをつけてpushします。 (-fは強制の意味。commitの歴史が変わってるので-fをつけないとエラー発生してpushできない) $ git push -f origin topic またrebaseしたときにconfilictが起こることも多々あります。そうした場合はまず手動でコンフリクトを修正します。そのあとに下記コマンドを入力します。 $ git add -A $ git rebase --continue またconflictが起きた場合は上記の作業を繰り返します。 4. git revertでコミットを消す 「git rebert」はその名の通りcommitを消すコマンドです。コマンドは下記です。 (commit IDをまず確認する) $ git log $ git revert [commit Id] 【gitコマンド】いまさらのrevertの記事が細かく書いてあってわかりやすいです。 あと個人的によく使うのがgithub上でrevertする方法です。どんなときに使うかというと、developブランチにマージしてそのままmasterに反映しようと思ったが、やっぱりバグが起こったのでやめたいみたいな状況で使います。ボタンポチポチするだけなので簡単です。PRをmergeしたあとにrevertボタンが現れるのでそれを押すと、そのmergeがrevertされてなかったことになります。下記画像の右下がrebvertボタンの参考です。 意外とmergeしたけどテスト環境で見たらエラーが発生したり、mergeしたけどのmasterへの反映のタイミングを少し遅らせたりみたいなことが起こります。そうしたときにこのgithub上のrevertは便利です。revertした内容をまた元に戻してmergeしたいとなったら、再revertします。つまりrevertのrevertです。 5. git stashを使って作業中のコードを一時退避する 複数のタスクを持ったり、1つの機能に関して複数のブランチで作業したりすることがあります。個人的によくあるのがコードのレビューを待っている時に違うブランチで作業していて、レビューで修正入ってブランチを切り替えて作業したいという状況です。まあ急な対応出ない限りはそんな急がなくていいこともありますが、作業中のブランチが変更中の場合だとうまく切り替えできないことが多々あります。そんなとき「git stash」コマンドを使うと変更分を一時保管してくれます。 (これで一時退避) $ git stash (退避させた変更分を元に戻す) $ git stash list stash@{0}: WIP on test: xxxx (stash listの番号を指定する) $ git stash apply stash@{0} なんだかんだよく使います。すごい余談ですけど、stashしないでブランチ切り替えたら変更した内容が切り替え先についてきて、そのまま作業してしまいごちゃごちゃになったことがあります。すごい大変でした。。。 6. git cherry-pickで特定のコミットだけを適用させる これはテスト環境で確認する時によく使います。テスト環境には様々な人の変更差分があり、コンフリクトが非常に起きやすいです。そのため確かめたい箇所をピンポイントに適用させて、確認したいということが多々あります。(ただテスト環境と本番環境の差が広がっていってしまうという問題もありますが、、、) (commit IDを確認する) $ git log $ git cherry-pick {任意のcommit ID} 上記コマンドで完了です。あとはpushすれば特定の環境に指定したコミットだけが適用されます。 7.--squshで複数のコミットを1つにまとめる 作業していくとコミットがごちゃごちゃになったり、細かく区切りすぎたりすることが多発します。重要な機能を開発しているときにコミットが煩雑すぎる場合、あとでrollbackするのが大変になります。そんな時に、「merge --sqush」をするとコミットをまとめることができます。 (現在の作業ブランチtopic1としてさらにブランチを切る) $ git checkout -b "topic2" (topic2である程度commitがたまってきたら、topic1にもどりmerge -squshでtopic2の変更を1つのコミットとしてまとめる) $ git checkout topic1 $ git merge --sqush topic2 開発規模が大きくなったり、gitの開発に慣れてきた場合はcommitの管理をしていくと、レビューワーにとってもとても親切です。(正直私はあんまり使ったことないですが、会社の人が使ってたので積極的に使っていきたいなというレベルです笑) 8. ミスったときはこのコマンドを使うgit rebase -i & git reflog rebaseしたり、コミットをまとめたりなど便利なコマンドを説明しましたが、いろんなコマンドをする機会があればあるほどミスすることが多々あります。特に私も最初のころはrebaseのミスだったり、不要なコミットを消そうとしてミスしたりで時間を食ってしまいました。そこでミスしたときに役立つコマンドを説明します。 まず1つは「git rebase -i」です。簡単にいうと任意のコミットまで戻り、その任意のコミットのあとにしたコミットの操作ができます。 (戻りたいcommit IDを確認) $ git log $ git rebase -i {戻りたいcommit ID} (そうするとテキストエディタが開かれる) pick aaaaaaa commitを追加                #文頭のpickの箇所を任意のCommandから選択して編集する。 pick bbbbbbb commit2を追加 # Rebase aaaaaa..bbbbbb onto d286baa # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. # テキストエディタ内のcommandにあるように、dropを指定すればコミットが消され、editを選択すればコミットメッセージを編集できます。とりあえずコミット関係でミスったり、なにか設定したいことがあればrebase -iを使うと便利です。個人的にはdropとpickを組み合わせてcommitを修正したりしています。 そしてもう一つがreflogです。reflogをするとすべての作業の履歴がみれます。 $ git reflog aaaaaaa HEAD@{0}: commit ・・・ bbbbbbb HEAD@{1}: merge ・・・・ ccccccc HEAD@{2}: checkout: ・・・ (戻りたい履歴のHEAD番号をresetの後に指定すると戻れる) $ git reset --hard {2} 特定の箇所に戻ってやり直したいという時に非常に便利です。よく使ってます。 9. 最後に、、、 コマンドに関しては上記で紹介したコマンド以外にもたくさんのgitコマンドがありますが、全て覚える必要なことがないとは思っています。実務でこんな操作がしたいという必要性があったときにグーグルで検索していけば良いと思うからです。とはいうもののやはり上記レベルのコマンドは覚えとくと便利ですね。 またgit開発ではどのくらいの粒度でPRをだすのか、設計に基づいてどのように作業を分割しリリースしていくかということも必要だと感じました。まだまだ入門したでもあるので、gitコマンドの基本操作も含め、設計を意識したgitでの開発の勉強をして行ければと思っています。 そして結局のところgitでの開発で意識することは後で自分が困らないようにすること、そしてレビューしてもらう先輩方にわかりやすく伝えることだと思っていますので、その思いを大事にしたいです。 来月はruby,Railsの記事を書きたい、、、、、
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

個人開発と実務のgit開発フローの違い & テクニックをまとめてみた

記事について 完全未経験状態から独学で勉強し、今年の4月から都内一部上場企業のwebエンジニアとして働くことになりました。独学で今までいろいろ勉強してましたが、やっぱり実務レベルだとまだまだひよっこエンジニアです。 そこで月に1~2回、学んだことをアウトプットするために記事を書いていこうと思います。 記事内容は実務使う技術だったり、技術書や動画で学んだことだったり、適当に興味あることを書いていきたいと思います。 今回の背景 実務初心者なら誰しも一度はミスするであろうgitを扱いたいと思います。 エンジニアを目指して勉強していた時期にもgitの重要性は理解していましたし、gitの基本的な使い方も理解していました。ただ実務ではミスったり、無駄に時間をかけてしまったりしてしまうことが多々起こりました。。。なので今回は反省の意味をこめてこの記事を書こうと思いました。 前提知識 gitの基本の使い方は米国AI開発者がやさしく教えるGit入門講座を通して学んでいます。そのため「コミットとは?」・「masterブランチとは?」などの基本的なこと、gitコマンドの基本的な使い方、gitとgithubを使った大まかな開発の流れについては理解している前提で記事を書いています。もしわからない方がは上記のudemy講座がおすすめです。 目次 個人レベルと実務レベルのgit開発の違い4選 複数環境が存在する場合の開発フローを学ぶ git rebaseをして最新のdevelopに追従する git revertでコミットを消す git stashを使って作業中のコードを一時対比する git cherry-pickで特定のコミットだけを適用させる --squshで複数のコミットを1つにまとめる ミスったときはこのコマンドを使うgit rebase -i & git reflog 最後に、、、 1. 個人レベルと実務レベルのgit開発の違い4選 私が感じた違いをざっと4つほど紹介します。 複数環境が存在する 個人レベルでは開発環境と本番環境の2つを使えばいいですが、実務ではテスト環境が複数あります。例えば本番環境デプロイ前に確認するstaging環境だったり、もっと気軽に試せるテスト環境だったりです。そのためgithub上のブランチもmasterだけでなく、テスト環境用のブランチも育てていく必要があります。まあ慣れれば大したことではないかもですが、個人開発しかしていないと、「git push」「git pull」などコマンドはmasterだけに行うことが多かったので初心者としては少しびっくりポイントかもです。 複数人での開発・自分の知らぬ間にブランチが更新されている これが一番驚きですね。ものによってはコンフリクトが起きまくりだったり、主要なコードが置き換わったりすると自分の作業中のコードに影響がでたり、、。適宜最新のブランチに追従していかないと、いざmergeするとき大変です。。特に大きなプロジェクトになると上がってくるPRの数も半端ないので、気をつけないといけないです。 ローカルルールが多い だろうなとは思いましたが、やはりプロジェクトによってもgitルールが全然違います。例えば私の所属しているところは比較的安定しているプロジェクトなので、丁寧なコードレビュー制度が敷かれていたり、テスト環境へ上げるためのフローがきっちり組まれています。最初のうちは誰がレビューの権限を持っていて、どのタイミングでテスト環境へデプロイがOKなのか結構迷いました。 教える側と教わる側のgitに関するgitの認識の違い 私の会社ではありがたいことに非常に丁寧に教わっているのですが、おそらく会社にいる方々はgit開発フローが日常過ぎて初心者の引っかかりポイントが見えにくいのだと思います。例えば入社当初「git rebase して最新のものに追従お願いします!」っていう指示仰いだときは、rebaseの存在をあまり知らなかったので頭?状態でした。。。(案の定ミスりましたが笑) 以上が私自身が感じたポイントでした。 入社も2ヶ月たつとgitは毎日使うので自然に身についてきます。ただそれでもたまにミスしたり、わからなくなることはあります。なので困ったことをより具体的にこれから書いていきたいと思います。 2. 複数環境が存在する場合の開発フローを学ぶ こちらに関してもおそらく会社やプロジェクトによってかなり違ってくると思います。ただ開発用・テスト用・本番用のブランチに分けて運用するという基本はどこでも使われていると思いますし、基本抑えとけば他でもある程度どこでも最初ほど苦労することはないと思います。 今回は開発用のブランチはdevelopブランチ、テスト用のブランチはstagingブランチ、本番用はmasterブランチとしてまとめます。またgithubを想定しています。 ①「git pull」 でまずは自分のローカルに開発用ブランチをセットします。このpullは定期的にしないとoriginのdevelopはどんどん進化していくので差分が大きくなってしまいます。ローカルで新しいトピックブランチを作る前に毎回pullするのが個人的には必須だと思ってます。 ②作業用のトピックブランチをつくります。今回はブランチ名をtopicにしています。機能が大きいときはここからさらに子ブランチを作って作業したりもします。 ③作業用のブランチが一息ついた、もしくは出来上がった場合は「git push」してoriginにtopicブランチを作成します。ここでPR(プルリクエスト)も作成します。 ④おそらくPRをmergeする前にレビューや確認が入ります。これがOKの場合はgithub上でPRをmergeします。 ⑤そしてここも会社によってフローが違ったり順番は前後しますが、レビューでOKもらったあとにstagingにあげます。localで「staging checkout」します。 ⑥stagingも差分がある可能性が高いので一旦pullしてから、今回の作業分の差異をとりこむためtopicをmergeします。 ⑦コンフリクトがある場合は修正し、「git push」します。 ⑧そしてテスト環境で問題なければgithub上でdevelopからmasterへmergeして完了です。 あとはこれを繰り返すだけです。あと説明すっ飛ばしましたが、基本的にpushやmergeすると同時にCI/CDが走っていることが多いと思うので、gitコマンドを打てば自動的にテストやデプロイができるはずです(これももちろん会社やプロジェクトによって違う)。やっぱり最初はブランチの量とかルールに圧倒されますが、大まかな流れさえ抑えてけばこっちのもんです。個人的にも新しいプロジェクトにもし入る場合はまずgitのこのフローを確認していきたいです。 そして次からはもっと具体的に実務ではじめてかつよく使うgitのコマンドを紹介します。 3. git rebaseをして最新のdevelopに追従する 自分以外の人がどんどん開発していくと、自分が作業しているブランチと開発ブランチの差異が発生します。pullで解決することもできますが、rebaseするとcommitもすっきりするのでrebaseすることが多いです。rebaseするとcommit履歴の歴史が変わるので一緒に作業していると不都合もあるのですが、自分の作業ブランチならほとんど問題にならない事が多いです。 rebaseについての記事は下記がとてもわかりやすかったです。 git rebase についてまとめてみた 細かい説明は上記参考にするとして、コマンドの流れだけ書きます。 ①まずは共通開発ブランチで最新版をpullします。 $ git pull origin develop ②そしたら自分が作業していたブランチに移動します $ git checkout topic ③rebaseコマンドをします $ git rebase develop 以上で完了です。このコマンドで最新developに追従した状態になります。 ただpushする際は注意が必要で、すでにPRがある場合は-fオプションをつけてpushします。 (-fは強制の意味。commitの歴史が変わってるので-fをつけないとエラー発生してpushできない) $ git push -f origin topic またrebaseしたときにconfilictが起こることも多々あります。そうした場合はまず手動でコンフリクトを修正します。そのあとに下記コマンドを入力します。 $ git add -A $ git rebase --continue またconflictが起きた場合は上記の作業を繰り返します。 4. git revertでコミットを消す 「git rebert」はその名の通りcommitを消すコマンドです。コマンドは下記です。 (commit IDをまず確認する) $ git log $ git revert [commit Id] 【gitコマンド】いまさらのrevertの記事が細かく書いてあってわかりやすいです。 あと個人的によく使うのがgithub上でrevertする方法です。どんなときに使うかというと、developブランチにマージしてそのままmasterに反映しようと思ったが、やっぱりバグが起こったのでやめたいみたいな状況で使います。ボタンポチポチするだけなので簡単です。PRをmergeしたあとにrevertボタンが現れるのでそれを押すと、そのmergeがrevertされてなかったことになります。下記画像の右下がrebvertボタンの参考です。 意外とmergeしたけどテスト環境で見たらエラーが発生したり、mergeしたけどのmasterへの反映のタイミングを少し遅らせたりみたいなことが起こります。そうしたときにこのgithub上のrevertは便利です。revertした内容をまた元に戻してmergeしたいとなったら、再revertします。つまりrevertのrevertです。 5. git stashを使って作業中のコードを一時退避する 複数のタスクを持ったり、1つの機能に関して複数のブランチで作業したりすることがあります。個人的によくあるのがコードのレビューを待っている時に違うブランチで作業していて、レビューで修正入ってブランチを切り替えて作業したいという状況です。まあ急な対応出ない限りはそんな急がなくていいこともありますが、作業中のブランチが変更中の場合だとうまく切り替えできないことが多々あります。そんなとき「git stash」コマンドを使うと変更分を一時保管してくれます。 (これで一時退避) $ git stash (退避させた変更分を元に戻す) $ git stash list stash@{0}: WIP on test: xxxx (stash listの番号を指定する) $ git stash apply stash@{0} なんだかんだよく使います。すごい余談ですけど、stashしないでブランチ切り替えたら変更した内容が切り替え先についてきて、そのまま作業してしまいごちゃごちゃになったことがあります。すごい大変でした。。。 6. git cherry-pickで特定のコミットだけを適用させる これはテスト環境で確認する時によく使います。テスト環境には様々な人の変更差分があり、コンフリクトが非常に起きやすいです。そのため確かめたい箇所をピンポイントに適用させて、確認したいということが多々あります。(ただテスト環境と本番環境の差が広がっていってしまうという問題もありますが、、、) (commit IDを確認する) $ git log $ git cherry-pick {任意のcommit ID} 上記コマンドで完了です。あとはpushすれば特定の環境に指定したコミットだけが適用されます。 7.--squshで複数のコミットを1つにまとめる 作業していくとコミットがごちゃごちゃになったり、細かく区切りすぎたりすることが多発します。重要な機能を開発しているときにコミットが煩雑すぎる場合、あとでrollbackするのが大変になります。そんな時に、「merge --sqush」をするとコミットをまとめることができます。 (現在の作業ブランチtopic1としてさらにブランチを切る) $ git checkout -b "topic2" (topic2である程度commitがたまってきたら、topic1にもどりmerge -squshでtopic2の変更を1つのコミットとしてまとめる) $ git checkout topic1 $ git merge --sqush topic2 開発規模が大きくなったり、gitの開発に慣れてきた場合はcommitの管理をしていくと、レビューワーにとってもとても親切です。(正直私はあんまり使ったことないですが、会社の人が使ってたので積極的に使っていきたいなというレベルです笑) 8. ミスったときはこのコマンドを使うgit rebase -i & git reflog rebaseしたり、コミットをまとめたりなど便利なコマンドを説明しましたが、いろんなコマンドをする機会があればあるほどミスすることが多々あります。特に私も最初のころはrebaseのミスだったり、不要なコミットを消そうとしてミスしたりで時間を食ってしまいました。そこでミスしたときに役立つコマンドを説明します。 まず1つは「git rebase -i」です。簡単にいうと任意のコミットまで戻り、その任意のコミットのあとにしたコミットの操作ができます。 (戻りたいcommit IDを確認) $ git log $ git rebase -i {戻りたいcommit ID} (そうするとテキストエディタが開かれる) pick aaaaaaa commitを追加                #文頭のpickの箇所を任意のCommandから選択して編集する。 pick bbbbbbb commit2を追加 # Rebase aaaaaa..bbbbbb onto d286baa # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # x, exec = run command (the rest of the line) using shell # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. # テキストエディタ内のcommandにあるように、dropを指定すればコミットが消され、editを選択すればコミットメッセージを編集できます。とりあえずコミット関係でミスったり、なにか設定したいことがあればrebase -iを使うと便利です。個人的にはdropとpickを組み合わせてcommitを修正したりしています。 そしてもう一つがreflogです。reflogをするとすべての作業の履歴がみれます。 $ git reflog aaaaaaa HEAD@{0}: commit ・・・ bbbbbbb HEAD@{1}: merge ・・・・ ccccccc HEAD@{2}: checkout: ・・・ (戻りたい履歴のHEAD番号をresetの後に指定すると戻れる) $ git reset --hard {2} 特定の箇所に戻ってやり直したいという時に非常に便利です。よく使ってます。 9. 最後に、、、 コマンドに関しては上記で紹介したコマンド以外にもたくさんのgitコマンドがありますが、全て覚える必要なことがないとは思っています。実務でこんな操作がしたいという必要性があったときにグーグルで検索していけば良いと思うからです。とはいうもののやはり上記レベルのコマンドは覚えとくと便利ですね。 またgit開発ではどのくらいの粒度でPRをだすのか、設計に基づいてどのように作業を分割しリリースしていくかということも必要だと感じました。まだまだ入門したでもあるので、gitコマンドの基本操作も含め、設計を意識したgitでの開発の勉強をして行ければと思っています。 そして結局のところgitでの開発で意識することは後で自分が困らないようにすること、そしてレビューしてもらう先輩方にわかりやすく伝えることだと思っていますので、その思いを大事にしたいです。 来月はruby,Railsの記事を書きたい、、、、、
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

git 基本<リモートでの作業用>

Gitを使用したリモート(チーム開発)を進める時に使用するコマンドなど 1.git fetch <リモート名> →リモートリポジトリからローカルリポジトリに内容を反映させる →あくまでもローカルリポジトリ内 →ローカルのワークツリーには反映させない 2.git merge →フェッチしてきたファイルなどを、ローカルのワークツリーに反映させる 3.git pull →フェッチしてから、マージまでを行うコマンド マージとブランチを使いこなそう 1.新たにブランチを作成してみる git branch <ファイル名> →新たにbranchを作成する git branch →現在あるブランチを表示する 2.作成したブランチに移動してみる git checkout <ブランチ名> →HEADを切り替える(ブランチを変更する) →ブランチを変更することで、作業を並行して行える 3.マージをしてみる git merge <ブランチ名> →ブランチないに新たに変更分を取り込んでくれる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gitの概要 <基本のコマンド>

Gitの基本的な概念 Gitの構造 ・Gitはワークツリー、ステージ、リポジトリの3つで成り立っている →ワークツリーでは、自分のローカルPC上のファイルを編集するような物 →ステージトは、ワークツリーで編集などをしたファイルをリポジトリにあげる(コミット)する準備をする場所 →リポジトリとは、ステージにおいたファイルを置く場所 →つまり、ワークツリーとステージはあくまでも、ローカルの作業(個人のファイル) →リポジトリーは、各個人のワークツリーとステージを統合するような場所 Gitのデータ構造 ・Gitは「圧縮」、「ツリー」、「コミット」、この3つのファイル →まず、ワークツリーからステージの時にあげるファイルに対して一つの圧縮ファイルが作られる →その後、コミットを行うと、ステージからリポジトリへと新たにファイルが追加される (この時のファイルは、コミットを行なった時点での圧縮ファイル情報が含まれている) Gitのコマンド 1.git init →このコマンドを使用することにより、ローカルリポジトリを作成することができる →.gitのファイルが作成させれる(.gitなので隠しファイル) 2.git clone <リポジトリ名> →リモートリポジトリに存在するファイルを、ローカルリポジトリへとコピーすることができる 3.git add <ファイル名、ディレクトリ名> →ワークツリーからステージへと変更を追加することができる 4.git commit -m "<メッセージ>", git commit -v →ステージに追加したファイルなどをリポジトリに登録する 5.git status →ワークツリーとステージ間、ステージとリポジトリ間で変更があったファイルを表示することができる 6.git diff --staged →git diff <ファイル名>はaddする前の変更差分を見ることができる (ワークツリーとステージ間の差分を確認する時に使用する) →--stageオプションを使用することでgit addした後の差分を見ることができる (ステージとリポジトリ間の差分を確認する) 7.git log →今までのコミットの履歴を確認することができる 8.git rm <ファイル名> →ファイルを削除するコマンド →ワークツリーからも自分のローカルリポジトリからも削除されてしまう →もし、ローカルから消えるのが嫌なのであれば、ファイル名の前に --cachedのオプションをつける 9,git mv<前のファイル名><新ファイル名> →ファイル名を変更する時に使用する 10,git remote add origin http::~ →originという名前で、指定したURLでリモートリポジトリ(Git Hub)にアップすることができる 11.git config (--global) alias. (a) (b) →aにはエイリアス(別名)を入力して、bには本来の名前を入力することで、今後はエイリアスを入力するとそのコマンドを使用することができる →globalオプションを使用することで、グローバルで使用することができるエイリアスにする 12.git checkout →ワークツリーないの変更を取り消すことができる (ステージと同じ状態にする) 13.git reset HEAD <ファイル名> →ステージした追加した変更を取り消す →ローカルそのものもファイルには影響を及ぼさない 14.git commit --amend →直前にコミットした内容を修正することができる 15.git remote →リモートリポジトリの名前が表示される
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Git】GitHubリポジトリの移行手順

GitHubリポジトリの移行手順について、調べた事を纏めます。 作業手順 「--mirrorオプションで実施する方法」と「1つずつリポジトリを移行後にpushする方法」 の2つに分別できるかと思います。 ※移行前と移行後のgitに関しては、本書では「移行前.git」と「移行後.git」という形で表現します。 --mirrorオプションで実施する方法 以下の--mirrorオプションを付ければ、それで完結する模様。 これがおススメ。 # git clone --mirror 移行前.git # cd 移行前ディレクトリ # git push --mirror 移行後.git 1つずつリポジトリを移行後にpushする方法 移行前リポジトリをfetch(ダウンロード)して、1つずつ移行していくといった形です。 ① 移行後のgitをcloneしておく。 # git clone 移行後.git # cd 移行後ディレクトリ ② 移行前リポジトリをremoteとして追加する。 # git remote add ikoumae_origin 移行前.git # git remote -v ikoumae_origin 移行前.git (fetch) ikoumae_origin 移行前.git (push) origin 移行後.git (fetch) origin 移行後.git (push) ③ 移行前リポジトリをfetchする。 # git fetch ikoumae_origin ④ 移行前リポジトリから対象ブランチをチェックアウトする。 (※ここでは例として、"feature/test"というブランチ名を取る。 # git checkout ikoumae_origin/feature/test ⑤ ローカルブランチ作成 # git branch feature/test ⑥ 移行後ブランチにpushする # git push origin feature/test ※※ 後はリポジトリの数分、上記の作業を繰り返して完了。 ※※ 終わり gitの移行手順なんて、ググれば沢山出てくるような情報ですが、 普段作業をしていると、単純なgitのコマンドを忘れたりする事もあるので、 それらも含めて、備忘録として活用してもらえればいいかなと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「git fetchコマンドがローカルに反映されない」という誤解

はじめに GitHub上にあるリモートリポジトリから、ブランチを取得してみたのだがGitの理解が浅かったようでgit fetchコマンドがうまく使えなかった。 GitHub上にあるブランチを取得したい 基本的な取得方法は下記のコマンドを実行するだけ。 #下記のコマンドでリモートの全ブランチをローカルに取得させる git fetch ただここで、git branchで確認してみるとmasterのみで、その他のブランチが表示されなかった。 この時点で「あれ? ブランチ取得できていない?」と思ってしまった。 調べてみると、どうやら取得するのはリモートリポジトリの情報をローカルに持ってくるだけで、実際にはワークツリーに反映されない仕様みたい。 #全てのブランチを確認 git branch -a #ブランチを指定して切り替えてみる git checkout topic_branch => * topic_branch master 切り替えることができた。 これでgit fetchコマンドが、なんとなく理解できた。 参考元 リモートブランチをローカルで取得する[git fetch][Sourcetree]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

git rebaseでconflictした際の対応をシンプルに確認

概要 下記のように、ある地点からfeatureブランチを切って開発を進めている間に、masterブランチにもcommitが行われるような状況はよくあると思います。 この状況にて、featureブランチからPRを出すときには、feature側にてmasterの先頭から変更コミットを生やすようにすることが多いと思います(fast-forward?)。 このときmasterのコミットとfeatureのコミットがコンフリクトした場合のgit rebase --continue、git rebase --skipの挙動を確認していきます。 状況 下記のようなgit logの状況から、featureブランチ側でgit rebase masterをしていきます。 target1 in feature、target2 in featureのコミットはmasterのコミットとコンフリクトしています。 target3 in featureのコミットはコンフリクトしません。 rebaseしてみる git rebase masterする featureブランチにて、git rebase masterをすると以下のようになります。 [nannany@minamiyoshihikonoMacBook-Pro practice-git (feature)]$ git rebase master First, rewinding head to replay your work on top of it... Applying: target1 in feature Using index info to reconstruct a base tree... M target1.md Falling back to patching base and 3-way merge... Auto-merging target1.md CONFLICT (content): Merge conflict in target1.md error: Failed to merge in the changes. Patch failed at 0001 target1 in feature hint: Use 'git am --show-current-patch' to see the failed patch Resolve all conflicts manually, mark them as resolved with "git add/rm <conflicted_files>", then run "git rebase --continue". You can instead skip this commit: run "git rebase --skip". To abort and get back to the state before "git rebase", run "git rebase --abort". 上記の状況はmasterブランチの先端にtarget1 in featureのコミットをつけようとしてコンフリクトしている状況です。 コンフリクトを解消したらgit add .します。 [nannany@minamiyoshihikonoMacBook-Pro practice-git (feature *+|REBASE 1/3)]$ git add . git rebase --continue 1つ目のコミットのコンフリクトを解消してgit add .したら、rebaseを続けるためgit rebase --continueします。 [nannany@minamiyoshihikonoMacBook-Pro practice-git (feature +|REBASE 1/3)]$ git rebase --continue Applying: target1 in feature Applying: target2 in feature Using index info to reconstruct a base tree... M target2.md Falling back to patching base and 3-way merge... Auto-merging target2.md CONFLICT (content): Merge conflict in target2.md error: Failed to merge in the changes. Patch failed at 0002 target2 in feature hint: Use 'git am --show-current-patch' to see the failed patch Resolve all conflicts manually, mark them as resolved with "git add/rm <conflicted_files>", then run "git rebase --continue". You can instead skip this commit: run "git rebase --skip". To abort and get back to the state before "git rebase", run "git rebase --abort". 上記は2つ目のコミットを先ほどつけたコミットのさらに先端につけようとしてコンフリクトしている状況です。 ここでもコンフリクトするので解消します。ただし、今回はmaster側の変更を取り込むこととします。コンフリクト解消したら、git add .します。 git rebase --skip 先ほどの、target2 in featureコミットは結局反映しないので、コミット履歴には残さないようにしたいです。そのような時はgit rebase --skipをします。 [nannany@minamiyoshihikonoMacBook-Pro practice-git (feature *+|REBASE 2/3)]$ git rebase --skip Applying: target3 in feature これでtarget2 in featureはコミット履歴には残らないようになります。 target3 in featureのコミットは? target3 in featureのコミットはコンフリクトしないので、何もせずともそのままコミット履歴に残ります。 最終的なコミット履歴は? コミット履歴は最終的に下記のようになります。 masterブランチの先にskipしたtarget2 in feature以外のコミットがくっついていることが分かります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む