git rebaseのちょっとしたまとめ
rebase
、分かったつもりになってまたわからないを繰り返しているので少しまとめておきます。
rebaseとは
- 現在いるブランチを、rebaseしてきたブランチの先頭にくっつけます。
- この時、現在いるブランチのコミットを一瞬退避(一時保存)し、rebaseしてきたブランチに現在いるブランチを
git reset --hard
して、保存していたコミットを上にのせる仕組みです。 - リベースを行うとコミットのidが変わり、別物として扱われます。これはコミットが新しく作られるためであり、リモートリポジトリに既にpushしていた場合は、ローカルと一致しなくなってしまいpushできなくなります。
- 上記のパターンでpushがrejectされた場合、
git push -f
で対応します。 - rebaseするとその際のコミットが作られません。(mergeするとマージコミットができちゃいます。)
git rebase
rebaseはこんな感じで使えます。これはgit checkout branchB
+ git rebase branchA
を組み合わせたものです。
# branchAをbranchBにリベース(Bが先行する) $ git rebase branchA branch B
git pullとpull --rebaseについて
git pull
は以下のように、fetch + mergeです。
# リモートBからリモート追跡Bにコピー $ git fetch # リモート追跡B(=リモートBのコピー)からローカルBにマージ $ git merge origin/master # 上記二つをいっぺんに $ git pull origin master
一方git pull --rebase(←オプション)
は、fetch + rebaseをします。
$ git pull --rebase origin master
git rebase -i
git rebase --interactive。過去のコミットを編集することができます。
git rebase -i <編集したいコミットID>~
と使用します。
ちなみに間違えて編集した場合は、git reset --hard <編集前のコミットID>
で修正できました。
間違えたパターンの再現
3つのcommitを用意しました。 (1・2はpush済みで3が未push状態でした。)
$ git log --oneline 058deaf (HEAD -> practise) commit3 b100354 (origin/practise) commit2 4ceebb5 commit1
push済みでもrebase -iで編集はできます。しかしこの時間違えてcommit2を、commit1にsquashしてしまいました。
pick 4ceebb5 commit1 s b100354 commit2 pick 058deaf commit3 # Rebase bf5a488..058deaf onto bf5a488 (3 commands) # # Commands: # p, pick <commit> = use commit # r, reword <commit> = use commit, but edit the commit message # e, edit <commit> = use commit, but stop for amending # s, squash <commit> = use commit, but meld into previous commit # f, fixup <commit> = like "squash", but discard this commit's log message # x, exec <command> = run command (the rest of the line) using shell # b, break = stop here (continue rebase later with 'git rebase --continue') # d, drop <commit> = remove commit # l, label <label> = label current HEAD with a name # t, reset <label> = reset HEAD to a label
その結果こうなります。
$ git log --oneline 75df8ce (HEAD -> practise) commit3 7d51144 commit1 $ git reflog 75df8ce (HEAD -> practise) HEAD@{0}: rebase (finish): returning to refs/heads/practise 75df8ce (HEAD -> practise) HEAD@{1}: rebase (pick): commit3 7d51144 HEAD@{2}: rebase (squash): commit1 4ceebb5 HEAD@{3}: rebase (start): checkout 4ceebb5~ 058deaf HEAD@{4}: commit: commit3 b100354 (origin/practise) HEAD@{5}: commit: commit2 4ceebb5 HEAD@{6}: commit: commit1
間違えた編集をした場合、git reset --hard <編集前のコミットID>
で対応できました。今回の場合は058deaf時点に戻しました。
reset --hardを使用することでコミットが抹消しないか心配だったのですが、問題なく意図通りrebase編集前状態に戻すことができました!
$ git reset --hard 058deaf HEAD is now at 058deaf commit3 $ git reflog 058deaf (HEAD -> practise) HEAD@{0}: reset: moving to 058deaf $ git log --oneline 058deaf (HEAD -> practise) commit3 b100354 (origin/practise) commit2 4ceebb5 commit1
そして本来やりたかった、commit3をcommit2にsquashしました。
※この場合であれば、$ git commit --amend
でもOKですが今回は敢えてrebase -i使っています
$ git rebase -i 4ceebb5~ pick 4ceebb5 commit1 pick b100354 commit2 s 058deaf commit3
大丈夫そうですね。
$ git log --oneline 2a18a66 (HEAD -> practise) commit2 4ceebb5 commit1 $ git reflog 2a18a66 (HEAD -> practise) HEAD@{0}: rebase (finish): returning to refs/heads/practise 2a18a66 (HEAD -> practise) HEAD@{1}: rebase (squash): commit2 b100354 (origin/practise) HEAD@{2}: rebase (start): checkout 4ceebb5~ 058deaf HEAD@{3}: reset: moving to 058deaf 75df8ce HEAD@{4}: rebase (finish): returning to refs/heads/practise 75df8ce HEAD@{5}: rebase (pick): commit3 7d51144 HEAD@{6}: rebase (squash): commit1 4ceebb5 HEAD@{7}: rebase (start): checkout 4ceebb5~
rebaseによってcommitIDが変更されてしまっているので、このままでは普通にpushできません。
なので以下のように強制pushします。--force-with-lease
を利用すれば、他の人のコミットは維持したまま上書きすることができます。
$ git push origin ブランチ --force-with-lease
Fjordのメンターodaillyさんのまとめがわかりやすかったです🙆♀️
- 参考:コミットを整理してみよう
Scrapboxに草を生やす
最近自分のメモ書き用にScrapboxを使用しており、結構使い心地が良くなってきました。ブログと違い、気軽にまとめたり小さな単位でアウトプットできることが良いなと思っています。
利用頻度がある程度増えてきたので、Scrapboxに草を生やすことにしました。
Pixela
Githubのグラフのような物を提供してくれるWebAPIです。 今回はScrapboxの投稿を可視化するのに利用しました。
手順
Pixelaにユーザー登録
$ curl -X POST https://pixe.la/v1/users -d '{"token":"XXXXX", "username":"chiroru", "agreeTermsOfService":"yes", "notMinor":"yes"}' =>{"message":"Success. Let's visit ~~~ , it is your profile page!","isSuccess":true}
今回利用するグラフの作成
$ curl -X POST https://pixe.la/v1/users/chiroru/graphs -H 'X-USER-TOKEN:XXXXX' -d '{"id":"scrapbox-graph", "name":"my-scrapbox-graph", "unit":"updates", "type":"int", "color":"shibafu", "timezone":"Asia/Tokyo"}' =>{"message":"Success.","isSuccess":true}
Webhook作成
Webhookとは、あるアプリケーションの更新情報を、他のアプリに提供する仕組みのことで、今回でいうとScrapboxの投稿情報を、Pixelaに提供してグラフの草を生やすのに利用します。
$ curl -X POST https://pixe.la/v1/users/chiroru/webhooks -H 'X-USER-TOKEN:XXXXX' -d '{"graphID":"scrapbox-graph", "type":"increment"}' =>{"message":"Success.","isSuccess":true,"webhookHash":"~~~~~"}
※ 参考:Webhookとは?
ScrapboxでNotificationsに設定する
Project settings/Notificationsに、以下を追加する。
https://pixe.la/v1/users/chiroru/webhooks/webhookHash
Scrapboxで表示する
以下のようにURLを追加します。
https://pixe.la/v1/users/chiroru/graphs/scrapbox-graph.svg
するとこんな感じで表示できます↓あっという間!
参考
2020 Rails Girlsの活動を振返る
この記事はRails Girls Japan Advent Calendar 2020の15日目の記事です。
前回はcobachieさんのRails Girls Gathering Japan を開催した理由でした。
今回は、 私のRails Girlsでの1年の活動を振返りたいと思います。
Rails Girls Nagoyaでオーガナイザーをした
1月31日・2月1日にRails Girls Nagoya 5thが開催され、オーガナイザーとして参加しました。この頃は、新型コロナウイルスが流行り始めた時期で開催が懸念されましたが、どうにかギリギリ予定通り実施できました。
私がオーガナイザーになろうと思った理由は、初めて参加したコミュニティのRails Girlsでの体験が、とても楽しくて忘れられない経験となり、次は「自分がいただいたものを、また他の誰かに届けたい」と思ったためです。
運営として私が大事にしたのは「自分の目標、また参加者の方の目標(目的)はそれぞれ異なる」ということです。 だからその上で私は、「個人の目標を達成してもらいかつ楽しんでもらう」ことを目標に、それをオープニングの言葉としました。
当日はGirls10名、運営12名での比較的小規模(?)な開催でありましたが、いちオーガナイザーとしてまたRails Girlsに携わることができ、とてもとても幸せでした🌸
Nagoya More! / Nagano More!がある
Rails Girlsのイベントが終わっても、月1ペースのMore!勉強会に参加できます💪
どちらもオンラインでの勉強会で、Nagoya More!では、『現場で使える Ruby on Rails 5速習実践ガイド』輪読会・『Railsの教科書』各自勉強・もくもく会・watchパーティー(ジョブスの映画鑑賞)などに今年は取り組みました。
またNagoyaと親和性の高いNagano More!にも参加しており、Naganoでは『webを支える技術』の輪読会と、ビブリオトークを今年やってきました。(ビブリオトークは、現在自分が読んでいる本をジャンル問わず紹介する企画です。 )
今年は本を紹介されてばかりだったので、来年はもっとたくさん本を読んで紹介する側になっていきたいなぁと思います📚
Tokyo More!にもオンライン参加
コロナの影響で、今まで参加できなかったTokyo More!(※コロナ以前は、オフライン開催)にも参加させていただきました🤲
igaigaさんが座学をしてくださる会で、参加者もたくさんいてワイワイしていました!
(その時のブログ→RailsGirls Online More!に参加しました!)
Rails Girls Gathering Japanの運営スタッフに挑戦
先日12月12日に、Rails Girls Japan初のオンラインLT会を実施しました🎉
今年の締めくくりに、私は有志で募られたスタッフとして、運営に携わらせていただきました。
およそ2ヶ月くらい前から打ち合わせが始まり、イベントの名前を決めたり、募集要項の作成や告知、また当日の運営等全部が初めてでしたが、運営チームで力を合わせ、盛況のうちに終えることができました🙏
予想以上に参加申し込みをしてくださる方がおり(イベント中にも申し込みしてくださる方がたくさんおりました!)、当日は影武者ながらとても緊張していましたが、SNSで拡散していただいたコメントを読み、勇気を出して運営やって良かったなぁと嬉しい気持ちになりました。
まとめ
Rails Girlsというコミュニティを通じて、たくさんの素敵な人々と出会うことができました。 そして (私が今通っている)FjordBootCampという素晴らしいコミュニティに出会えたのも、Rails Girlsで出会った方々のお陰です🌸
これからもこの場所を、この輪を大切にし、そして今度はまた別の誰かに運ぶことができるよう、過ごしていければなぁと思います。
Prettierの導入
【目次】
インストール
// prettierをインストール
$ npm i -D prettier
ESLintと一緒に使うために
- prettier-eslint
- prettier-eslint-cli
ESLintの記述ルール(JavaScript Standard Style)を反映するために
- eslint-config-standard
をインストールします。
$ npm i -D prettier-eslint prettier-eslint-cli eslint-config-standard
package.jsonにPretterのコマンドを追加します。
--write
オプションを指定することで整形後の内容で保存できます。コードフォーマッターを適用する対象をformat
に記述します。
// package.json { "scripts": { "format": "prettier --write '/**/*.js'" }, "devDependencies": { "prettier": "^2.1.1" } }
実行
$ npm run format
で実行できます。
ここではeslint自動修正と合わせて実行しています。
$ npx eslint test2.js /Users/name/xxx/Javascript/test2.js 1:4 error Multiple spaces found before 'hoge' no-multi-spaces 1:7 error 'hoge' is assigned a value but never used no-unused-vars 1:7 error 'hoge' is never reassigned. Use 'const' instead prefer-const 1:11 error Multiple spaces found before '=' no-multi-spaces 1:15 error 'func' is not defined no-undef 1:19 error Unexpected whitespace between function name and paren func-call-spacing 2:1 error Expected indentation of 2 spaces but found 4 indent 2:5 error 'parameter1' is not defined no-undef 2:16 error Multiple spaces found before 'parameter2' no-multi-spaces 2:19 error 'parameter2' is not defined no-undef 2:31 error 'parameter3' is not defined no-undef 2:42 error Multiple spaces found before 'parameter4' no-multi-spaces 2:46 error 'parameter4' is not defined no-undef 3:1 error Expected indentation of 2 spaces but found 3 indent 3:4 error 'parameter6' is not defined no-undef 3:15 error Multiple spaces found before 'parameter7' no-multi-spaces 3:17 error 'parameter7' is not defined no-undef 4:1 error Expected indentation of 2 spaces but found 9 indent 4:10 error 'parameter8' is not defined no-undef 4:20 error Unexpected trailing comma comma-dangle 5:1 error Expected indentation of 0 spaces but found 14 indent 5:16 error Extra semicolon semi ✖ 22 problems (22 errors, 0 warnings) 13 errors and 0 warnings potentially fixable with the `--fix` option. $ npx eslint test2.js --fix /Users/name/xxx/Javascript/test2.js 1:7 error 'hoge' is assigned a value but never used no-unused-vars 1:14 error 'func' is not defined no-undef 2:3 error 'parameter1' is not defined no-undef 2:15 error 'parameter2' is not defined no-undef 2:27 error 'parameter3' is not defined no-undef 2:39 error 'parameter4' is not defined no-undef 3:3 error 'parameter6' is not defined no-undef 3:15 error 'parameter7' is not defined no-undef 4:3 error 'parameter8' is not defined no-undef ✖ 9 problems (9 errors, 0 warnings) $ npm run format > javascript@1.0.0 format /Users/name/xxx/Javascript > prettier-eslint --write '/Users/name/xxx/Javascript/*.js' success formatting 1 file with prettier-eslint 1 file was unchanged
参考
ページごとにサイドバーのコンテンツを切替える
お手伝いしているRailsのサービスサイトで、サイドバーに全ページに共通の広告を表示しているのですが、掲載数の変更に伴いページごとに種類を変えて広告バナーを表示することにしました。
現状としてはapplication.html.erbで統一されたサイドバーを表示していますが、 これをページごとに分割するのに「名前付きyield」が使えそうです。
【目次】
名前付きyieldの指定
まずapplication.html.slim側では、= yield :name
の形式でyieldに名前をつけます。
現在広告バナーはページのcol-md-3の部分に表示しているため、col-md-9はそのまま現状のものを表示できるよう= yield
を、そしてxxxページ・yyyページでそれぞれバナーを変更したいcol-md-3の内部に名前付きyieldを記述します。
.row .col-md-9 = yield .col-md-3 .sidebar-content = yield :xxx = yield :yyy
viewのメイン部分は常に「名前なしyield」としてレンダリングされるため、コンテンツを名前付きのyieldとしてレンダリングするには、content_for
メソッドを使用します。
content_forメソッドでレイアウトに挿入
名前付きのyieldとしてレイアウトに挿入する場合、各ページ(index.html.slim)でcontent_for :name do
の形式で指定します。
xxx/index.html.slim
= h1 名前なしyieldとして挿入される - content_for :xxx do .sidebar-content h2 名前付きyieldとして挿入される .card
yyy/index.html.slim
= h1 名前なしyieldとして挿入される(yyy) - content_for :yyy do .sidebar-content h2 名前付きyieldとして挿入される(yyy) .card
これらのレンダリング結果がレイアウトに挿入されると、最終的には以下のようなHTMLとして出力されます。
xxx/index.html
<div class="row"> <div class="col-md-9"> <h1>名前なしyieldとして挿入される</h1> <div class="col-md-3"> <div class="sidebar-content"> <h2>名前付きyieldとして挿入される</h2> <div class="card">
yyy/index.html
<div class="row"> <div class="col-md-9"> <h1>名前なしyieldとして挿入される(yyy)</h1> <div class="col-md-3"> <div class="sidebar-content"> <h2>名前付きyieldとして挿入される(yyy)</h2> <div class="card">
これでxxxページ・yyyページそれぞれ異なる広告バナーを表示させることができるようになりました!
レイアウトをコントローラーごとに指定するのもいいかも
今回は各ページで広告バナーのみ切替がしたかったので使用しませんでしたが、コントローラーにレイアウトを指定することで表示を変える方法も良さそうです。
例えばxxx_controllerのshowページでは、applicationファイルのようなサイドバナー広告の表示が不要であるとします。
そんな時はviews/layouts下に、別途レイアウトファイルを作成し、コントローラーでlayoutメソッドを指定することで切り替えられます。
今回はバナーなしのno_banner.html.slimを作成したとします。
作成できたら、xxxコントローラーにて以下のように指定しましょう。
layout "no_banner", only: :show # または def show render layout: "no_banner" end
参考
Githubのデフォルトブランチがmainになった
【目次】
- mainになっていた
- そもそもgit init時にmasterブランチが作成されている
- リポジトリをcloneしてきてから作業するのも良いかも
- zshならgitの補完機能も使えるかも
- [追記] ブランチ名をあとで変更すれば良いだけかも
mainになっていた
Githubのデフォルトのブランチが「master」から「main」になったようです。
GitHub、これから作成するリポジトリのデフォルトブランチ名が「main」に。「master」から「main」へ変更
これを知らずにリポジトリを作成後、masterにプッシュしてしまいInitial commitがうまくいきませんでした。
まずこの解決方法として、以下のようにmasterブランチのコミットをmainブランチへ移しました。
$ git fetch origin * [new branch] main -> origin/main $ git checkout main Switched to a new branch 'main' $ git cherry-pick コミットID $ git push origin main
※コミットIDは、Github上でbranchのcommitsから確認できます
本来Github上でプルリクを出すことができるはずなのですが、masterブランチからmainブランチへはなぜかプルリクが出せない状態になっていた(何も表示がなかった)ので、今回はターミナル上で対応しました。
そもそもgit init時にmasterブランチが作成されている
git init
で初期化する際に、masterブランチが生成されてしまっています。
デフォルトのブランチ名がmasterからmainに変わったのはGithubのみであるので、これらを一致させると良いでしょう。
このgit init
で生成されるブランチ名をmainにするには、init.defaultBranch
で設定できるようです。
※これはgit 2.28以上のバージョンでのみになります。
Git でデフォルトのブランチ名を master 以外に変更する方法
$ git config --global init.defaultBranch main
リポジトリをcloneしてきてから作業するのも良いかも
最初にGithubでリポジトリを作成してきて、$ git clone リポジトリのURL
でクローンしてこれば、すでにあるmainブランチ上で作業を開始することもできます。
$ git clone URL
$ cd リポジトリ
$ git branch
* main
$ git add
・
・
$ git push origin main
zshならgitの補完機能も使えるかも
(私はbashなのでこちらは試せていないのですが)補完機能を利用して、持っているブランチを確認しつつ作業することもできるようです。
[追記] ブランチ名をあとで変更すれば良いだけかも
こちらを書いた時頭になかったのですが、普通にブランチ名をpush前にmainに変更するのでも良いですね。
$ git branch -m <変更するブランチ名> <変更後のブランチ名> // 現在使用しているブランチを変更する $ git branch -m <変更後のブランチ名>
Machida.rb #05に参加しました!
毎月第1金曜日に開催されているMachida.rbに、前回に引き続き2回目の参加をしました!
【目次】
今回はRubyで簡単な問題を解いてレビューし合う勉強会でした💪その中の問題を抜粋して書きます。
Rubyの問題を解いてみた
FizzBuzz
有名なFizzBuzz問題の省略版です。1~20の数字を出力するうち、3の倍数ではFizz・5の倍数ではBuzz・3と5の倍数ではFizzBuzzと出力させます。
最初のコードはこちら。
(1..20).each {|num| if num % 15 == 0 puts "fizzbuzz" elsif num % 3 == 0 puts "fizz" elsif num % 5 == 0 puts "buzz" else puts num end }
このコードで注目したのは「putsを毎回書いている」という点です。
これは以下のようにif文を()で括りそれに対してputsすることで、何度も書いていた部分が改善できるようになりました。
(1..20).each {|num| puts (if num % 15 == 0 "fizzbuzz" elsif num % 3 == 0 "fizz" elsif num % 5 == 0 "buzz" else num end) }
しかしさらに、「putsの位置」について改善できます。
上記ではループ処理の中にputsを置いているのですが、それより以下の書き方では「処理の結果(戻り値)を出力している」ことになるのでこちらの方がより良いとのことです。
puts (1..20).map {|num| if num % 15 == 0 "fizzbuzz" elsif num % 3 == 0 "fizz" elsif num % 5 == 0 "buzz" else num end }
パタトクカシーー
出題は言語処理100本ノックより「パタトクカシーー」問題です。
文字列の奇数番を取り出して「パトカー」と連結した文字列を取得します。
はじめに解いたコードはこちらなのですが、nilが出力されてしまってうまく出力ができませんでした😓
str = "パタトクカシーー" p str.chars.each_slice(2) {print _1[0]} # => パトカーnil
この原因はeach_slice(n) {|list| ... } -> nil
のように、each_sliceの戻り値がnilを返すためでした。※
文頭にp
を書いてしまっているため、この戻り値nilを拾って出力してしまっていました。
参考:リファレンスマニュアルEnumerable#each_slice
なので、こちらだったらいけますね。
putsは内部でto_sしてるいるのでnilは出力されません。
str = "パタトクカシーー" puts str.chars.each_slice(2) {print _1[0]} # => パトカー
しかしこれもまた、ブロック内部でprintをしてしまっているので、FizzBuzzの所に書いたように「戻り値を出力する」という方針でこちらの方が良いです↓
str = "パタトクカシーー" puts str.chars.each_slice(2).map { _1[0] }.join # => パトカー
※ちなみにeach_slice(n) -> Enumerator
のように、blockを持たなければeach_sliceの戻り値はEnumeratorになります。
感想
その場で解くにはちょうどいい問題の難易度で、人のコードと比較したり、すぐにレビューを受けられる&質問できるとても充実した勉強会でした! p・print・putsの挙動や適切な配置、Enumerator関連のメソッド(each_slice)がブロックを持つ時の戻り値がnilであることなど、学びもたくさんありました! また勉強会後の懇親会では、rubykaigiの直後だったこともありkaigi内の発表のことやruby3.0について技術的な話が聞けてとても楽しかったです!