Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

RailsConf 2015 - Breaking Down the Barrier: Dem...

RailsConf 2015 - Breaking Down the Barrier: Demystifying Contributing to Rails

http://confreaks.tv/videos/railsconf2015-breaking-down-the-barrier-demystifying-contributing-to-rails

Contributing to Rails for the first time can be terrifying. In this lab I’ll make contributing to Rails more approachable by going over the contributing guidelines and technical details you need to know. We’ll walk through traversing the source code with tools such as CTags, source_location and TracePoint. Additionally, we’ll create reproduction scripts for reporting issues and learn advanced git commands like bisect and squash. At the end of this session you’ll have the confidence to fix bugs and add features to Ruby on Rails.

Eileen M. Uchitelle

April 21, 2015
Tweet

More Decks by Eileen M. Uchitelle

Other Decks in Technology

Transcript

  1. CLONE THE REPO Grab a USB or clone the railsconf_scripts

    repo from https://github.com/eileencodes/railsconf_scripts.git $ cd railsconf_scripts # mac/linux users $ bundle install --local # windows users not using a linux VM $ git checkout windows $ bundle install --local
  2. CLONE THE REPO Grab a USB or clone the railsconf_scripts

    repo from github.com/eileencodes/railsconf_scripts $ cd railsconf_scripts/ # mac/linux users $ bundle install --local # windows users not using a linux VM $ git checkout windows $ bundle install --local
  3. % Ruby version manager (rbenv, rvm, chruby) % Ruby 2.2.2

    $ Databases for Active Record $ MySQL $ PostgreSQL $ SQLite3 ENVIRONMENT
  4. % Ruby version manager (rbenv, rvm, chruby) % Ruby 2.2.2

    % Databases for Active Record % MySQL % PostgreSQL % SQLite3 $ Git & a GitHub Account ENVIRONMENT
  5. RUNNING TESTS $ cd actionpack $ rake test Finished in

    3.237796s, 861.0796 runs/ s, 4508.3137 assertions/s.
 2788 runs, 14597 assertions, 0 failures, 0 errors, 0 skips
  6. ACTIVE RECORD $ cd activerecord $ rake test:sqlite3 $ rake

    test:mysql2 $ rake test:mysql $ rake test:postgresql
  7. $ cd activerecord/ $ rake test:sqlite3 $ ruby -Ilib:test test/cases/

    reflection_test.rb $ ARCONN=mysql2 ruby -Ilib:test test/ cases/reflection_test.rb -n test_columns RUNNING TESTS
  8. OPENING AN ISSUE % Be clear and include the Rails

    version $ Don’t open security issues on the issues tracker
  9. OPENING AN ISSUE % Be clear and include the Rails

    version % Don’t open security issues on the issues tracker $ Don’t open issues that aren’t a bug in Rails
  10. OPENING AN ISSUE % Be clear and include the Rails

    version % Don’t open security issues on the issues tracker % Don’t open issues that aren’t a bug in Rails $ Don’t open a separate issue if you have a PR
  11. OPENING AN ISSUE % Be clear and include the Rails

    version % Don’t open security issues on the issues tracker % Don’t open issues that aren’t a bug in Rails % Don’t open a separate issue if you have a PR $ Don’t open feature request tickets
  12. OPENING AN ISSUE % Be clear and include the Rails

    version % Don’t open security issues on the issues tracker % Don’t open issues that aren’t a bug in Rails % Don’t open a separate issue if you have a PR % Don’t open feature request tickets % Do include a test script or application
  13. require 'active_record' require 'minitest/autorun' require 'logger' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

    ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :projects do |t| t.string :name end end class Project < ActiveRecord::Base validates_presence_of :name end class BugTest < Minitest::Test def test_assert project = Project.create! assert_not_operator project, :valid? end end
  14. require 'active_record' require 'minitest/autorun' require 'logger' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

    ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :projects do |t| t.string :name end end class Project < ActiveRecord::Base validates_presence_of :name end class BugTest < Minitest::Test def test_assert project = Project.create! assert_not_operator project, :valid? end end
  15. require 'active_record' require 'minitest/autorun' require 'logger' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

    ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :projects do |t| t.string :name end end class Project < ActiveRecord::Base validates_presence_of :name end class BugTest < Minitest::Test def test_assert project = Project.create! assert_not_operator project, :valid? end end
  16. require 'active_record' require 'minitest/autorun' require 'logger' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

    ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :projects do |t| t.string :name end end class Project < ActiveRecord::Base validates_presence_of :name end class BugTest < Minitest::Test def test_assert project = Project.create! assert_not_operator project, :valid? end end
  17. require 'active_record' require 'minitest/autorun' require 'logger' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

    ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :projects do |t| t.string :name end end class Project < ActiveRecord::Base validates_presence_of :name end class BugTest < Minitest::Test def test_assert project = Project.create! assert_not_operator project, :valid? end end
  18. require 'active_record' require 'minitest/autorun' require 'logger' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

    ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :projects do |t| t.string :name end end class Project < ActiveRecord::Base validates_presence_of :name end class BugTest < Minitest::Test def test_assert project = Project.create! assert_not_operator project, :valid? end end
  19. require 'active_record' require 'minitest/autorun' require 'logger' ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

    ActiveRecord::Base.logger = Logger.new(STDOUT) ActiveRecord::Schema.define do create_table :projects do |t| t.string :name end end class Project < ActiveRecord::Base validates_presence_of :name end class BugTest < Minitest::Test def test_assert project = Project.create! assert_not_operator project, :valid? end end
  20. OPENING A PR % Open PR’s against master $ We

    don’t accept “cosmetic changes”
  21. OPENING A PR % Open PR’s against master % We

    don’t accept “cosmetic changes” $ Write clear and descriptive commit messages
  22. Short (50 chars or less) summary of changes More detailed

    explanatory text, if necessary. Wrap it to about 72 characters or so. In some contexts, the first line is treated as the subject of an email and the rest of the text as the body. The blank line separating the summary from the body is critical (unless you omit the body entirely); tools like rebase can get confused if you run the two together. Further paragraphs come after blank lines. - Bullet points are okay, too - Typically a hyphen or asterisk is used for the bullet, preceded by a single space, with blank lines in between, but conventions vary here
  23. OPENING A PR % Open PR’s against master % We

    don’t accept “cosmetic changes” % Write clear and descriptive commit messages $ Be prepared to squash your commits
  24. OPENING A PR % Open PR’s against master % We

    don’t accept “cosmetic changes” % Write clear and descriptive commit messages % Be prepared to squash your commits $ Don’t ping constantly or litter PR’s with +1’s
  25. OPENING A PR % Open PR’s against master % We

    don’t accept “cosmetic changes” % Write clear and descriptive commit messages % Be prepared to squash your commits % Don’t ping constantly or litter PR’s with +1’s $ We <3 documentation PR’s
  26. commit 02d3a253610eaf9c80587913b366e3fa0f56b71f Author: eileencodes <[email protected]> Date: Sat Oct 4 11:19:37

    2014 -0400 [ci skip] Clarify deletion strategies for collection proxies For detailed testing of behavior see: https://gist.github.com/eileencodes/ 5b0a2fe011dcff6203fe This shows destroy_all always destroys records and fires callbacks. It will never use nullify or delete_all delete_all's behavior varies greatly based on `hm` vs `hm:t` and deletion strategy.
  27. OPENING A PR % Open PR’s against master % We

    don’t accept “cosmetic changes” % Write clear and descriptive commit messages % Be prepared to squash your commits % Don’t ping constantly or litter PR’s with +1’s % We <3 documentation PR’s $ Keep it simple
  28. OPENING A PR % Open PR’s against master % We

    don’t accept “cosmetic changes” % Write clear and descriptive commit messages % Be prepared to squash your commits % Don’t ping constantly or litter PR’s with +1’s % We <3 documentation PR’s % Keep it simple $ Use Benchmark/ips
  29. # railsconf_scripts/benchmark_ips_example.rb require 'benchmark/ips' ARRAY = (1..100).to_a def slow ARRAY.shuffle.first

    end def fast ARRAY.sample end Benchmark.ips do |x| x.report('slow') { slow } x.report('fast') { fast } x.compare! end
  30. # railsconf_scripts/benchmark_ips_example.rb require 'benchmark/ips' ARRAY = (1..100).to_a def slow ARRAY.shuffle.first

    end def fast ARRAY.sample end Benchmark.ips do |x| x.report('slow') { slow } x.report('fast') { fast } x.compare! end
  31. # railsconf_scripts/benchmark_ips_example.rb require 'benchmark/ips' ARRAY = (1..100).to_a def slow ARRAY.shuffle.first

    end def fast ARRAY.sample end Benchmark.ips do |x| x.report('slow') { slow } x.report('fast') { fast } x.compare! end
  32. # railsconf_scripts/benchmark_ips_example.rb require 'benchmark/ips' ARRAY = (1..100).to_a def slow ARRAY.shuffle.first

    end def fast ARRAY.sample end Benchmark.ips do |x| x.report('slow') { slow } x.report('fast') { fast } x.compare! end
  33. $ bundle exec ruby benchmark_ips_example.rb Calculating ------------------------------------- slow 26.801k i/100ms

    fast 131.410k i/100ms ------------------------------------------------- slow 343.594k (± 5.4%) i/s - 1.715M fast 6.348M (±11.8%) i/s - 31.276M Comparison: fast: 6347552.5 i/s slow: 343594.2 i/s - 18.47x slower
  34. OPENING A PR % Open PR’s against master % We

    don’t accept “cosmetic changes” % Write clear and descriptive commit messages % Be prepared to squash your commits % Don’t ping constantly or litter PR’s with +1’s % We <3 documentation PR’s % Keep it simple % Use Benchmark/ips $ Write tests
  35. # nodoc example from activerecord/lib/active_record/ reflection.rb:588 class HasManyReflection < AssociationReflection

    # :nodoc: def initialize(name, scope, options, active_record) super(name, scope, options, active_record) end def macro; :has_many; end def collection?; true; end end
  36. # Deprecation from activerecord/lib/active_record/tasks/ # database_tasks.rb:207 def load_schema_for(*args) ActiveSupport::Deprecation.warn(<<-MSG.squish) This

    method was renamed to `#load_schema` and will be removed in the future. Use `#load_schema` instead. MSG load_schema(*args) end
  37. class Project < ActiveRecord::Base has_many :projects end class Comment <

    ActiveRecord::Base belongs_to :project end class BugTest < Minitest::Test def test_delete_all post = Post.create!(title: "Post title", content: "Lots of content") comment = Comment.create!(content: "I am a comment", post_id: post.id) assert 1, post.comments.count post.comments.delete_all assert 0, post.comments.count end end
  38. class Project < ActiveRecord::Base has_many :projects end class Comment <

    ActiveRecord::Base belongs_to :project end class BugTest < Minitest::Test def test_delete_all post = Post.create!(title: "Post title", content: "Lots of content") comment = Comment.create!(content: "I am a comment", post_id: post.id) assert 1, post.comments.count puts post.comments.method(:delete_all).source_location assert 0, post.comments.count end end
  39. (0.0ms) begin transaction SQL (0.1ms) INSERT INTO "comments" ("content", "post_id")

    VALUES (?, ?) [["content", "I am a comment"], ["post_id", 1]] (0.0ms) commit transaction (0.1ms) SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 1]] .../activerecord/lib/active_record/associations/ collection_proxy.rb 442 (0.0ms) SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 1]]
  40. CTags # OS X $ brew install ctags # Debian-based

    linux $ sudo apt-get install exuberant-ctags # Red Hat-based linux $ sudo yum install ctags
  41. # .vimrc map <Leader>rt :!ctags --tag-relative --extra=+f - Rf.git/tags --exclude=.git,pkg

    —languages=- javascript,sql<CR><CR> set tags+=.git/tags
  42. .../through_association.rb:48:in `construct_join_attributes' .../has_many_through_association.rb:169:in `through_records_for' .../has_many_through_association.rb:180:in `block in delete_through_records' .../has_many_through_association.rb:179:in `each'

    .../has_many_through_association.rb:179:in `delete_through_records' .../has_many_through_association.rb:154:in `delete_records' .../has_many_through_association.rb:124:in `delete_or_nullify_all_records' .../collection_association.rb:214:in `delete_all' .../collection_proxy.rb:443:in `delete_all’ ar_test_scripts/ar_rails_test.rb:85:in `<main>'
  43. def construct_join_attributes(*records) ensure_mutable if source_reflection.association_primary_key(reflection.klass) == reflection.klass.primary_key join_attributes = {

    source_reflection.name => records } else join_attributes = { source_reflection.foreign_key => records.map { |record| record.send(source_reflection.association_primary_key( reflection.klass)) } } end if options[:source_type] join_attributes[source_reflection.foreign_type] = records.map { |record| record.class.base_class.name } [...]
  44. ActiveRecord::Schema.define do create_table :projects do |t| t.string :name end end

    class Project < ActiveRecord::Base after_create :call_me def call_me puts "======== i am a callback ========" end end class BugTest < Minitest::Test def test_create project = Project.create!(name: "whatever") end end
  45. create_table :projects do |t| t.string :name end end class Project

    < ActiveRecord::Base after_create :call_me def call_me puts caller puts "======== i am a callback ========" end end class BugTest < Minitest::Test def test_create project = Project.create!(name: "whatever") end end
  46. .../activesupport/lib/active_support/callbacks.rb:428:in `block in make_lambda' .../activesupport/lib/active_support/callbacks.rb:229:in `call' .../activesupport/lib/active_support/callbacks.rb:229:in `block in halting_and_conditional'

    .../activesupport/lib/active_support/callbacks.rb:502:in `call' .../activesupport/lib/active_support/callbacks.rb:502:in `block in call' .../activesupport/lib/active_support/callbacks.rb:502:in `each' .../activesupport/lib/active_support/callbacks.rb:502:in `call' .../activesupport/lib/active_support/callbacks.rb:90:in `run_callbacks' .../activerecord/lib/active_record/callbacks.rb:305:in `_create_record' .../activerecord/lib/active_record/timestamp.rb:57:in `_create_record' .../activerecord/lib/active_record/persistence.rb:506:in `create_or_update' .../activerecord/lib/active_record/callbacks.rb:301:in `block in create_or_update' .../activesupport/lib/active_support/callbacks.rb:86:in `run_callbacks' .../activerecord/lib/active_record/callbacks.rb:301:in `create_or_update' .../activerecord/lib/active_record/persistence.rb:151:in `save!' [...]
  47. def make_lambda(filter) case filter when Symbol lambda { |target, _,

    &blk| target.send filter, &blk } when String l = eval "lambda { |value| #{filter} }" lambda { |target, value| target.instance_exec(value, &l) } when Conditionals::Value then filter when ::Proc if filter.arity > 1 return lambda { |target, _, &blk| raise ArgumentError unless block target.instance_exec(target, block, &filter) } end if filter.arity <= 0 lambda { |target, _| target.instance_exec(&filter) } else lambda { |target, _| target.instance_exec(target, &filter) } [...]
  48. def test_reject_if_method_without_arguments Pirate.accepts_nested_attributes_for :ship, reject_if: :new_record? pirate = Pirate.new(catchphrase: "Stop

    wastin' me time") pirate.ship_attributes = { name: 'Black Pearl' } assert_no_difference('Ship.count') { pirate.save! } end
  49. def test_reject_if_method_without_arguments Pirate.accepts_nested_attributes_for :ship, reject_if: :new_record? pirate = Pirate.new(catchphrase: "Stop

    wastin' me time") tp = TracePoint.new(:call) do |*args| p args end tp.enable pirate.ship_attributes = { name: 'Black Pearl' } tp.disable assert_no_difference('Ship.count') { pirate.save! } end
  50. def test_reject_if_method_without_arguments Pirate.accepts_nested_attributes_for :ship, reject_if: :new_record? pirate = Pirate.new(catchphrase: "Stop

    wastin' me time") tp = TracePoint.new(:call) do |*args| p args end tp.enable pirate.ship_attributes = { name: 'Black Pearl' } tp.disable assert_no_difference('Ship.count') { pirate.save! } end
  51. def test_reject_if_method_without_arguments Pirate.accepts_nested_attributes_for :ship, reject_if: :new_record? pirate = Pirate.new(catchphrase: "Stop

    wastin' me time") tp = TracePoint.new(:call) do |*args| p args end tp.enable pirate.ship_attributes = { name: 'Black Pearl' } tp.disable assert_no_difference('Ship.count') { pirate.save! } end
  52. def test_reject_if_method_without_arguments Pirate.accepts_nested_attributes_for :ship, reject_if: :new_record? pirate = Pirate.new(catchphrase: "Stop

    wastin' me time") tp = TracePoint.new(:call) do |*args| p args end tp.enable pirate.ship_attributes = { name: 'Black Pearl' } tp.disable assert_no_difference('Ship.count') { pirate.save! } end
  53. def test_reject_if_method_without_arguments Pirate.accepts_nested_attributes_for :ship, reject_if: :new_record? pirate = Pirate.new(catchphrase: "Stop

    wastin' me time") tp = TracePoint.new(:call) do |*args| p args end tp.enable pirate.ship_attributes = { name: 'Black Pearl' } tp.disable assert_no_difference('Ship.count') { pirate.save! } end
  54. `ship_attributes='@.../activerecord/lib/active_record/nested_attributes.rb:347 `assign_nested_attributes_for_one_to_one_association'@.../activerecord/lib/active_record/ nested_attributes.rb:382 `nested_attributes_options'@.../activesupport/lib/active_support/core_ext/class/attribute.rb:106 `nested_attributes_options'@.../activesupport/lib/active_support/core_ext/class/attribute.rb:86 `with_indifferent_access'@.../activesupport/lib/active_support/core_ext/hash/indifferent_access.rb:8 `new_from_hash_copying_default'@.../activesupport/lib/active_support/hash_with_indifferent_access.rb:75 `initialize'@.../activesupport/lib/active_support/hash_with_indifferent_access.rb:58 `update'@.../activesupport/lib/active_support/hash_with_indifferent_access.rb:127 `convert_key'@.../activesupport/lib/active_support/hash_with_indifferent_access.rb:258

    `convert_value'@.../activesupport/lib/active_support/hash_with_indifferent_access.rb:262 `ship'@.../activerecord/lib/active_record/associations/builder/association.rb:110 `association'@.../activerecord/lib/active_record/associations.rb:149 `association_instance_get’@.../activerecord/lib/active_record/associations.rb:189 `_reflect_on_association'@.../activerecord/lib/active_record/reflection.rb:109 `_reflections'@.../activesupport/lib/active_support/core_ext/class/attribute.rb:86 `association_class'@.../activerecord/lib/active_record/reflection.rb:435 `macro'@.../activerecord/lib/active_record/reflection.rb:588 `require'@.../activesupport/lib/active_support/dependencies.rb:272 `load_dependency'@.../activesupport/lib/active_support/dependencies.rb:236 `load?'@.../activesupport/lib/active_support/dependencies.rb:311 `mechanism'@.../activesupport/lib/active_support/core_ext/module/attribute_accessors.rb:60 `constant_watch_stack'@.../activesupport/lib/active_support/core_ext/module/attribute_accessors.rb:60 `watching?'@.../activesupport/lib/active_support/dependencies.rb:99 `delegate'@.../activesupport/lib/active_support/core_ext/module/delegation.rb:151 `include?'@/Users/eileen/.rbenv/versions/2.2.0/lib/ruby/2.2.0/set.rb:211 `initialize'@.../activerecord/lib/active_record/associations/association.rb:24 `check_validity!'@.../activerecord/lib/active_record/reflection.rb:331 `check_validity_of_inverse!’@.../activerecord/lib/active_record/reflection.rb:335 ...
  55. def generate_association_writer(association_name, type) generated_association_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1 if

    method_defined?(:#{association_name}_attributes=) remove_method(:#{association_name}_attributes=) end def #{association_name}_attributes=(attributes) assign_nested_attributes_for_#{type} _association(:#{association_name}, attributes) end eoruby end
  56. class User < ActiveRecord::Base has_one :avatar accepts_nested_attributes_for :avatar end class

    Avatar < ActiveRecord::Base belongs_to :user end class BugTest < Minitest::Test def test_trace_point user = User.new(name: "My Name") user.avatar_attributes = { name: "I am a file name" } user.save! end end
  57. end class Avatar < ActiveRecord::Base belongs_to :user end class BugTest

    < Minitest::Test def test_trace_point user = User.new(name: "My Name") tp = TracePoint.new(:call) do |*args| p args end user.avatar_attributes = { name: "I am a file name" } user.save! end end
  58. class Avatar < ActiveRecord::Base belongs_to :user end class BugTest <

    Minitest::Test def test_trace_point user = User.new(name: "My Name") tp = TracePoint.new(:call) do |*args| p args end tp.enable user.avatar_attributes = { name: "I am a file name" } tp.disable user.save! end end
  59. GIT $ git add upstream $ git bisect $ git

    reset --[soft|hard] $ git rebase -i $ git reflog
  60. GIT REMOTES $ cd path/to/rails/ $ git remote remove upstream

    $ git remote add upstream https://github.com/rails/rails.git
  61. GIT

  62. 1 pick e92e34f Refactor existing code 2 pick 80601e8 Oops

    add in missing code 3 pick 8d91f76 Remove extra piece I left behind 4 pick 933bd58 Fix bug reported in #9895 5 pick 99302ef Clean up changes 6 pick 5cc3c5a Fix spelling mistake in documentation 7 8 # Rebase 0131d99..5cc3c5a onto 0131d99 9 # 10 # Commands: 11 # p, pick = use commit 12 # r, reword = use commit, but edit the commit message 13 # e, edit = use commit, but stop for amending 14 # s, squash = use commit, but meld into previous commit 15 # f, fixup = like "squash", but discard this commit's log 16 # x, exec = run command (the rest of the line) using shell
  63. 1 pick e92e34f Refactor existing code 2 pick 80601e8 Oops

    add in missing code 3 pick 8d91f76 Remove extra piece I left behind 4 pick 933bd58 Fix bug reported in #9895 5 pick 99302ef Clean up changes 6 pick 5cc3c5a Fix spelling mistake in documentation 7 8 # Rebase 0131d99..5cc3c5a onto 0131d99 9 # 10 # Commands: 11 # p, pick = use commit 12 # r, reword = use commit, but edit the commit message 13 # e, edit = use commit, but stop for amending 14 # s, squash = use commit, but meld into previous commit 15 # f, fixup = like "squash", but discard this commit's log 16 # x, exec = run command (the rest of the line) using shell
  64. 1 reword e92e34f Refactor existing code 2 pick 80601e8 Oops

    add in missing code 3 pick 8d91f76 Remove extra piece I left behind 4 pick 933bd58 Fix bug reported in #9895 5 pick 99302ef Clean up changes 6 pick 5cc3c5a Fix spelling mistake in documentation 7 8 # Rebase 0131d99..5cc3c5a onto 0131d99 9 # 10 # Commands: 11 # p, pick = use commit 12 # r, reword = use commit, but edit the commit message 13 # e, edit = use commit, but stop for amending 14 # s, squash = use commit, but meld into previous commit 15 # f, fixup = like "squash", but discard this commit's log 16 # x, exec = run command (the rest of the line) using shell
  65. 1 reword e92e34f Refactor existing code 2 pick 80601e8 Oops

    add in missing code 3 edit 8d91f76 Remove extra piece I left behind 4 pick 933bd58 Fix bug reported in #9895 5 pick 99302ef Clean up changes 6 pick 5cc3c5a Fix spelling mistake in documentation 7 8 # Rebase 0131d99..5cc3c5a onto 0131d99 9 # 10 # Commands: 11 # p, pick = use commit 12 # r, reword = use commit, but edit the commit message 13 # e, edit = use commit, but stop for amending 14 # s, squash = use commit, but meld into previous commit 15 # f, fixup = like "squash", but discard this commit's log 16 # x, exec = run command (the rest of the line) using shell
  66. 1 reword e92e34f Refactor existing code 2 pick 80601e8 Oops

    add in missing code 3 edit 8d91f76 Remove extra piece I left behind 4 pick 933bd58 Fix bug reported in #9895 5 squash 99302ef Clean up changes 6 pick 5cc3c5a Fix spelling mistake in documentation 7 8 # Rebase 0131d99..5cc3c5a onto 0131d99 9 # 10 # Commands: 11 # p, pick = use commit 12 # r, reword = use commit, but edit the commit message 13 # e, edit = use commit, but stop for amending 14 # s, squash = use commit, but meld into previous commit 15 # f, fixup = like "squash", but discard this commit's log 16 # x, exec = run command (the rest of the line) using shell
  67. 1 reword e92e34f Refactor existing code 2 fixup 80601e8 Oops

    add in missing code 3 edit 8d91f76 Remove extra piece I left behind 4 pick 933bd58 Fix bug reported in #9895 5 squash 99302ef Clean up changes 6 pick 5cc3c5a Fix spelling mistake in documentation 7 8 # Rebase 0131d99..5cc3c5a onto 0131d99 9 # 10 # Commands: 11 # p, pick = use commit 12 # r, reword = use commit, but edit the commit message 13 # e, edit = use commit, but stop for amending 14 # s, squash = use commit, but meld into previous commit 15 # f, fixup = like "squash", but discard this commit's log 16 # x, exec = run command (the rest of the line) using shell
  68. 1 reword e92e34f Refactor existing code 2 fixup 80601e8 Oops

    add in missing code 3 edit 8d91f76 Remove extra piece I left behind 4 pick 933bd58 Fix bug reported in #9895 5 squash 99302ef Clean up changes 6 pick 5cc3c5a Fix spelling mistake in documentation 7 8 # Rebase 0131d99..5cc3c5a onto 0131d99 9 # 10 # Commands: 11 # p, pick = use commit 12 # r, reword = use commit, but edit the commit message 13 # e, edit = use commit, but stop for amending 14 # s, squash = use commit, but meld into previous commit 15 # f, fixup = like "squash", but discard this commit's log 16 # x, exec = run command (the rest of the line) using shell
  69. 628c168 HEAD@{0}: rebase -i (finish): returning to refs/heads/your-branch 628c168 HEAD@{1}:

    rebase -i (start): checkout master 628c168 HEAD@{2}: reset: moving to 628c168 bc503a1 HEAD@{3}: rebase -i (finish): returning to refs/heads/your-branch bc503a1 HEAD@{4}: rebase -i (pick): Add Person model and migration 1ad2ff2 HEAD@{5}: rebase -i (pick): Improve README section on git reflog 026457d HEAD@{6}: rebase -i (start): checkout master 633f620 HEAD@{7}: rebase -i (finish): returning to refs/heads/your-branch 633f620 HEAD@{8}: rebase -i (pick): Improve README section on git reflog 19f41a7 HEAD@{9}: commit (amend): Add Person model and migration 50718b1 HEAD@{10}: rebase -i (edit): Add Person model and migration 026457d HEAD@{11}: rebase -i (reword): Update README with sections on rebase and c8c6000 HEAD@{12}: rebase -i (reword): Update README with sections on rebase and 628fbd2 HEAD@{13}: rebase -i (squash): Update README description section 455dab0 HEAD@{14}: rebase -i (fixup): # This is a combination of 2 commits. 4392779 HEAD@{15}: rebase -i (start): checkout master 628c168 HEAD@{16}: rebase -i (finish): returning to refs/heads/your-branch 628c168 HEAD@{17}: rebase -i (start): checkout master 628c168 HEAD@{18}: rebase -i (finish): returning to refs/heads/your-branch 628c168 HEAD@{19}: rebase -i (start): checkout master 628c168 HEAD@{20}: rebase: aborting 1f935f7 HEAD@{21}: rebase -i (edit): Add Person model and migration
  70. WHAT TO WORK ON % Test release candidates and that

    master branch $ Fix documentation & work on WIP Guides
  71. WHAT TO WORK ON % Test release candidates and that

    master branch % Fix documentation & work on WIP Guides $ Focus on your strengths
  72. WHAT TO WORK ON % Test release candidates and that

    master branch % Fix documentation & work on WIP Guides % Focus on your strengths $ Work on ActiveJob or WebConsole
  73. WHAT TO WORK ON % Test release candidates and that

    master branch % Fix documentation & work on WIP Guides % Focus on your strengths % Work on ActiveJob or WebConsole $ Review open pull requests
  74. WHAT TO WORK ON % Test release candidates and that

    master branch % Fix documentation & work on WIP Guides % Focus on your strengths % Work on ActiveJob or WebConsole % Review open pull requests $ Refactoring methods and tests
  75. WHAT TO WORK ON % Test release candidates and that

    master branch % Fix documentation & work on WIP Guides % Focus on your strengths % Work on ActiveJob or WebConsole % Review open pull requests % Refactoring methods and tests $ Watch the issues tracker
  76. WHAT WE COVERED % Getting set up % Guidelines for

    opening issues % Writing test scripts % Guidelines for opening pull requests % Git % Bisect, Interactive Rebase, Reflog % Traversing unfamiliar code % source_location, CTags, puts caller, TracePoint % Finding issues to work on
  77. EILEEN M. UCHITELLE Programmer at Basecamp ! eileencodes.com " @eileencodes

    # @eileencodes Slides: speakerdeck.com/eileencodes