From 9c03ba6c317eb4adf2c35d39f074236b009c12fd Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.com>
Date: Mon, 30 Jul 2018 14:08:02 +0200
Subject: [PATCH 01/12] feat: add rake tasks to import release from github

---
 lib/tasks/repositories.rake | 57 +++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)
 create mode 100644 lib/tasks/repositories.rake

diff --git a/lib/tasks/repositories.rake b/lib/tasks/repositories.rake
new file mode 100644
index 0000000..2573490
--- /dev/null
+++ b/lib/tasks/repositories.rake
@@ -0,0 +1,57 @@
+require 'net/https'
+
+namespace :repositories do
+  def get_url_content(url, headers = {})
+    uri = URI.parse(url)
+    req = Net::HTTP::Get.new(uri)
+
+    headers.each do |header, value|
+      req[header] = value
+    end
+
+    Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
+      http.request(req)
+    end.body
+  end
+
+  desc 'Update version from github repositories'
+  task github: :environment do
+    GithubRepository.all.each do |repo|
+      begin
+        software = Software.find(repo.software_id)
+        versions = software.versions.all.map(&:number)
+        headers = {
+          'Accept' => 'application/vnd.github.v3+json',
+          'Authorization' => 'token cabbd658df98458404ab519f02bca93bb2923dfb'
+        }
+        tags =
+          JSON.parse(
+            get_url_content(
+              "https://api.github.com/repos/#{repo.name}/git/refs/tags",
+              headers
+            )
+          )
+
+        tags.each do |tag|
+          version = tag['ref'].split('/').last
+          next if versions.include?(version)
+          puts version
+
+          date =
+            JSON.parse(
+              get_url_content(tag['object']['url'], headers)
+            )['committer']['date']
+
+          Version.new(
+            software_id: software.id,
+            number: version,
+            date: date
+          ).save
+        end
+      rescue => e
+        puts e
+        next
+      end
+    end
+  end
+end

From d6d2ad0b1565d063ca9c4eb1c682f882ddeb86f9 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.com>
Date: Mon, 30 Jul 2018 14:08:02 +0200
Subject: [PATCH 02/12] feat: add rake tasks to import release from github

---
 lib/tasks/repositories.rake | 56 +++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 lib/tasks/repositories.rake

diff --git a/lib/tasks/repositories.rake b/lib/tasks/repositories.rake
new file mode 100644
index 0000000..0c132e1
--- /dev/null
+++ b/lib/tasks/repositories.rake
@@ -0,0 +1,56 @@
+require 'net/https'
+
+namespace :repositories do
+  def get_url_content(url, headers = {})
+    uri = URI.parse(url)
+    req = Net::HTTP::Get.new(uri)
+
+    headers.each do |header, value|
+      req[header] = value
+    end
+
+    Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
+      http.request(req)
+    end.body
+  end
+
+  desc 'Update version from github repositories'
+  task github: :environment do
+    GithubRepository.all.each do |repo|
+      begin
+        software = Software.find(repo.software_id)
+        versions = software.versions.all.map(&:number)
+        headers = {
+          'Accept' => 'application/vnd.github.v3+json'
+        }
+        tags =
+          JSON.parse(
+            get_url_content(
+              "https://api.github.com/repos/#{repo.name}/git/refs/tags",
+              headers
+            )
+          )
+
+        tags.each do |tag|
+          version = tag['ref'].split('/').last
+          next if versions.include?(version)
+          puts version
+
+          date =
+            JSON.parse(
+              get_url_content(tag['object']['url'], headers)
+            )['committer']['date']
+
+          Version.new(
+            software_id: software.id,
+            number: version,
+            date: date
+          ).save
+        end
+      rescue => e
+        puts e
+        next
+      end
+    end
+  end
+end

From 5d948b79bdc368ce5fed70243f8ee40f24f79656 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 30 Jul 2018 21:51:38 +0200
Subject: [PATCH 03/12] fix: after software change redirect to edit page

---
 app/controllers/softwares_controller.rb | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/app/controllers/softwares_controller.rb b/app/controllers/softwares_controller.rb
index 732eb4e..c5b812d 100644
--- a/app/controllers/softwares_controller.rb
+++ b/app/controllers/softwares_controller.rb
@@ -30,7 +30,7 @@ class SoftwaresController < ApplicationController
     @software = Software.find(params[:id])
 
     if @software.update(software_params)
-      redirect_to @software
+      redirect_to edit_software_path(@software)
     else
       render 'edit'
     end
@@ -40,7 +40,7 @@ class SoftwaresController < ApplicationController
     @software = Software.new(software_params)
 
     if @software.save
-      redirect_to @software
+      redirect_to edit_software_path(@software)
     else
       render 'new'
     end

From 4e62cb6593146e0a11957dd8b052b67fe3182ac3 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Mon, 30 Jul 2018 22:15:05 +0200
Subject: [PATCH 04/12] fix: verify software_id before save change of a gitub
 repository

---
 app/controllers/github_repository_controller.rb | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/app/controllers/github_repository_controller.rb b/app/controllers/github_repository_controller.rb
index 92f908a..92b444b 100644
--- a/app/controllers/github_repository_controller.rb
+++ b/app/controllers/github_repository_controller.rb
@@ -1,16 +1,20 @@
 class GithubRepositoryController < ApplicationController
   def create
-    @software = Software.find(params[:software_id])
-    @repository = GithubRepository.new(repository_params)
-    @repository.save
+    if params[:repository][:software_id] == params[:software_id]
+      @software = Software.find(params[:software_id])
+      @repository = GithubRepository.new(repository_params)
+      @repository.save
+    end
 
     redirect_to edit_software_path(@software)
   end
 
   def update
-    @software = Software.find(params[:software_id])
-    @repository = GithubRepository.find(params[:id])
-    @repository.update(repository_params)
+    if params[:repository][:software_id] == params[:software_id]
+      @software = Software.find(params[:software_id])
+      @repository = GithubRepository.find(params[:id])
+      @repository.update(repository_params)
+    end
 
     redirect_to edit_software_path(@software)
   end

From 4e0478429cba4cf08d615f0f8b1417942e3d561d Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 2 Aug 2018 23:13:41 +0200
Subject: [PATCH 05/12] feat: add test for github_repository update method

---
 .../github_repository_controller_test.rb      | 20 ++++++++++++++++++-
 test/fixtures/github_repositories.yml         |  4 ++++
 test/fixtures/softwares.yml                   |  3 +++
 3 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/test/controllers/github_repository_controller_test.rb b/test/controllers/github_repository_controller_test.rb
index 7384465..739d87f 100644
--- a/test/controllers/github_repository_controller_test.rb
+++ b/test/controllers/github_repository_controller_test.rb
@@ -23,7 +23,7 @@ class GithubRepositoryControllerTest < ActionDispatch::IntegrationTest
     assert_not repository.save
   end
 
-  test 'should not save repository if a repository with the same name exist' do
+  test 'should not save repository if a repository has the same name' do
     data = {
       name: 'rails/rails',
       software_id: Software.find_by_name('Kaiho').id
@@ -42,4 +42,22 @@ class GithubRepositoryControllerTest < ActionDispatch::IntegrationTest
 
     assert repository.save
   end
+
+  test 'should not update repository if a repository has the same name' do
+    data = {
+      name: 'rails/rails'
+    }
+    repository = GithubRepository.find_by_name('ruby/ruby')
+
+    assert_not repository.update(data)
+  end
+
+  test 'should update repository' do
+    data = {
+      name: 'rails/rails2'
+    }
+    repository = GithubRepository.find_by_name('rails/rails')
+
+    assert repository.update(data)
+  end
 end
diff --git a/test/fixtures/github_repositories.yml b/test/fixtures/github_repositories.yml
index f420338..5667993 100644
--- a/test/fixtures/github_repositories.yml
+++ b/test/fixtures/github_repositories.yml
@@ -3,3 +3,7 @@
 one:
   name: rails/rails
   software: two
+
+two:
+  name: ruby/ruby
+  software: three
diff --git a/test/fixtures/softwares.yml b/test/fixtures/softwares.yml
index 976f213..91ee3c9 100644
--- a/test/fixtures/softwares.yml
+++ b/test/fixtures/softwares.yml
@@ -7,3 +7,6 @@ one:
 two:
   name: Ruby on Rails
   website: https://rubyonrails.org
+
+three:
+  name: Ruby

From 50266be9092aa5fa76b8af5f430c04d51b9bd65c Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <git@yae.im>
Date: Thu, 2 Aug 2018 23:16:04 +0200
Subject: [PATCH 06/12] chore: refact github_repository controller

---
 .../github_repository_controller.rb           | 23 ++++++-------------
 1 file changed, 7 insertions(+), 16 deletions(-)

diff --git a/app/controllers/github_repository_controller.rb b/app/controllers/github_repository_controller.rb
index 92b444b..1741b4f 100644
--- a/app/controllers/github_repository_controller.rb
+++ b/app/controllers/github_repository_controller.rb
@@ -1,27 +1,18 @@
 class GithubRepositoryController < ApplicationController
   def create
-    if params[:repository][:software_id] == params[:software_id]
-      @software = Software.find(params[:software_id])
-      @repository = GithubRepository.new(repository_params)
-      @repository.save
-    end
+    @software = Software.find(params[:software_id])
+    @repository = GithubRepository.new(params.permit(:name, :software_id))
+    @repository.save
 
     redirect_to edit_software_path(@software)
   end
 
   def update
-    if params[:repository][:software_id] == params[:software_id]
-      @software = Software.find(params[:software_id])
-      @repository = GithubRepository.find(params[:id])
-      @repository.update(repository_params)
-    end
+    @software = Software.find(params[:software_id])
+    @repository = GithubRepository.find(params[:id])
+
+    @repository.update(params.require(:repository).permit(:name)) if @software.id == @repository.software_id
 
     redirect_to edit_software_path(@software)
   end
-
-  private
-
-  def repository_params
-    params.require(:repository).permit(:name, :software_id)
-  end
 end

From a36060eb843e6df117cb005bf2ddd6aee13bddde Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.com>
Date: Sat, 4 Aug 2018 07:50:21 +0200
Subject: [PATCH 07/12] chore: use before_action in softwares controller

---
 app/controllers/softwares_controller.rb | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/app/controllers/softwares_controller.rb b/app/controllers/softwares_controller.rb
index c5b812d..aa0960f 100644
--- a/app/controllers/softwares_controller.rb
+++ b/app/controllers/softwares_controller.rb
@@ -1,4 +1,6 @@
 class SoftwaresController < ApplicationController
+  before_action :find_software, only: %i[show edit update destroy]
+
   def index
     @softwares = Software.all
   end
@@ -7,13 +9,9 @@ class SoftwaresController < ApplicationController
     @software = Software.new
   end
 
-  def show
-    @software = Software.find(params[:id])
-  end
+  def show; end
 
   def edit
-    @software = Software.find(params[:id])
-
     case @software.repository_type
     when 'Github'
       if GithubRepository.exists?(software_id: @software)
@@ -27,8 +25,6 @@ class SoftwaresController < ApplicationController
   end
 
   def update
-    @software = Software.find(params[:id])
-
     if @software.update(software_params)
       redirect_to edit_software_path(@software)
     else
@@ -47,7 +43,6 @@ class SoftwaresController < ApplicationController
   end
 
   def destroy
-    @software = Software.find(params[:id])
     @software.destroy
 
     redirect_to softwares_path
@@ -58,4 +53,8 @@ class SoftwaresController < ApplicationController
   def software_params
     params.require(:software).permit(:name, :website, :repository_type)
   end
+
+  def find_software
+    @software = Software.find(params[:id])
+  end
 end

From d4e44332ea0f891035ea71e15fbb7bdd1b85a9d6 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.com>
Date: Sat, 4 Aug 2018 08:00:12 +0200
Subject: [PATCH 08/12] feat: change api tags url for github

---
 lib/tasks/repositories.rake | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/lib/tasks/repositories.rake b/lib/tasks/repositories.rake
index 0c132e1..b0a1c8c 100644
--- a/lib/tasks/repositories.rake
+++ b/lib/tasks/repositories.rake
@@ -26,24 +26,22 @@ namespace :repositories do
         tags =
           JSON.parse(
             get_url_content(
-              "https://api.github.com/repos/#{repo.name}/git/refs/tags",
+              "https://api.github.com/repos/#{repo.name}/tags",
               headers
             )
           )
 
         tags.each do |tag|
-          version = tag['ref'].split('/').last
-          next if versions.include?(version)
-          puts version
+          next if versions.include?(tag['name'])
 
           date =
             JSON.parse(
-              get_url_content(tag['object']['url'], headers)
+              get_url_content(tag['commit']['url'], headers)
             )['committer']['date']
 
           Version.new(
             software_id: software.id,
-            number: version,
+            number: tag['name'],
             date: date
           ).save
         end

From 2c28c026734686e9b2d559098b3f23bcca5014d3 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.com>
Date: Sat, 4 Aug 2018 08:19:12 +0200
Subject: [PATCH 09/12] fix: rubocop warnings

---
 .rubocop.yml                         | 5 +----
 Gemfile                              | 6 +++---
 app/controllers/home_controller.rb   | 3 +--
 test/application_system_test_case.rb | 2 +-
 4 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/.rubocop.yml b/.rubocop.yml
index d1dedf8..d4fb7d5 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -6,13 +6,10 @@ AllCops:
     - bin/*
   TargetRubyVersion: 2.4
 
-Gemspec/RequiredRubyVersion:
-  Enabled: false
-
 Naming/AccessorMethodName:
   Enabled: false
 
-Style/RescueStandardError:
+Lint/RescueWithoutErrorClass:
   Enabled: false
 
 Metrics/LineLength:
diff --git a/Gemfile b/Gemfile
index d5d4230..c025b17 100644
--- a/Gemfile
+++ b/Gemfile
@@ -38,16 +38,16 @@ gem 'bootsnap', '>= 1.1.0', require: false
 
 group :development, :test do
   # Call 'byebug' anywhere in the code to stop execution and get a debugger console
-  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
+  gem 'byebug', platforms: %i[mri mingw x64_mingw]
 end
 
 group :development do
   # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
-  gem 'web-console', '>= 3.3.0'
   gem 'listen', '>= 3.0.5', '< 3.2'
   # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
   gem 'spring'
   gem 'spring-watcher-listen', '~> 2.0.0'
+  gem 'web-console', '>= 3.3.0'
 end
 
 group :test do
@@ -59,4 +59,4 @@ group :test do
 end
 
 # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
-gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
+gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]
diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb
index 95f2992..6d3fe90 100644
--- a/app/controllers/home_controller.rb
+++ b/app/controllers/home_controller.rb
@@ -1,4 +1,3 @@
 class HomeController < ApplicationController
-  def index
-  end
+  def index; end
 end
diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb
index d19212a..23701b4 100644
--- a/test/application_system_test_case.rb
+++ b/test/application_system_test_case.rb
@@ -1,4 +1,4 @@
-require "test_helper"
+require 'test_helper'
 
 class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
   driven_by :selenium, using: :chrome, screen_size: [1400, 1400]

From 78a248286ffbe744fb3c2dcc78a409aa030e400f Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.com>
Date: Sat, 4 Aug 2018 08:19:58 +0200
Subject: [PATCH 10/12] feat: add github token in application.yml

---
 .gitignore                  | 2 ++
 Gemfile                     | 2 ++
 Gemfile.lock                | 3 +++
 lib/tasks/repositories.rake | 8 +++++---
 4 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/.gitignore b/.gitignore
index ec0b196..1b1cf0b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,5 @@
 
 # Ignore master key for decrypting credentials and more.
 /config/master.key
+
+/config/application.yml
diff --git a/Gemfile b/Gemfile
index c025b17..66cc55c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -3,6 +3,8 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
 
 ruby '2.4.1'
 
+gem 'figaro'
+
 # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
 gem 'rails', '~> 5.2.0'
 # Use sqlite3 as the database for Active Record
diff --git a/Gemfile.lock b/Gemfile.lock
index f893f8a..af23ba1 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -76,6 +76,8 @@ GEM
     erubi (1.7.1)
     execjs (2.7.0)
     ffi (1.9.25)
+    figaro (1.1.1)
+      thor (~> 0.14)
     globalid (0.4.1)
       activesupport (>= 4.2.0)
     i18n (1.0.1)
@@ -197,6 +199,7 @@ DEPENDENCIES
   capybara (>= 2.15, < 4.0)
   chromedriver-helper
   coffee-rails (~> 4.2)
+  figaro
   jbuilder (~> 2.5)
   listen (>= 3.0.5, < 3.2)
   puma (~> 3.11)
diff --git a/lib/tasks/repositories.rake b/lib/tasks/repositories.rake
index b0a1c8c..47e3c44 100644
--- a/lib/tasks/repositories.rake
+++ b/lib/tasks/repositories.rake
@@ -16,13 +16,15 @@ namespace :repositories do
 
   desc 'Update version from github repositories'
   task github: :environment do
+    headers = {
+      'Accept' => 'application/vnd.github.v3+json'
+    }
+    headers['Authorization'] = "token #{Figaro.env.github_token}" if Figaro.env.github_token
+
     GithubRepository.all.each do |repo|
       begin
         software = Software.find(repo.software_id)
         versions = software.versions.all.map(&:number)
-        headers = {
-          'Accept' => 'application/vnd.github.v3+json'
-        }
         tags =
           JSON.parse(
             get_url_content(

From b9e8dc5ef29ea4aa43c400c174d1f5101937d106 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.com>
Date: Sun, 5 Aug 2018 12:37:03 +0200
Subject: [PATCH 11/12] fix: params in new github_repository

---
 app/controllers/github_repository_controller.rb | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/app/controllers/github_repository_controller.rb b/app/controllers/github_repository_controller.rb
index 1741b4f..ef78c7b 100644
--- a/app/controllers/github_repository_controller.rb
+++ b/app/controllers/github_repository_controller.rb
@@ -1,8 +1,11 @@
 class GithubRepositoryController < ApplicationController
   def create
     @software = Software.find(params[:software_id])
-    @repository = GithubRepository.new(params.permit(:name, :software_id))
-    @repository.save
+
+    if @software.id == params[:repository][:software_id].to_i
+      @repository = GithubRepository.new(params.require(:repository).permit(:software_id, :name))
+      @repository.save
+    end
 
     redirect_to edit_software_path(@software)
   end

From 7fce3d67e8fb4932c8da9084a79718f1688421a5 Mon Sep 17 00:00:00 2001
From: Adrien Waksberg <adrien.waksberg@doctolib.com>
Date: Sun, 5 Aug 2018 12:37:35 +0200
Subject: [PATCH 12/12] feat: add branches

---
 app/models/branch.rb                          |  6 +++++
 app/models/software.rb                        |  2 +-
 app/models/version.rb                         |  4 +--
 app/views/softwares/show.html.erb             |  5 +++-
 db/migrate/20180804072602_create_branches.rb  | 10 +++++++
 ...80805081706_change_reference_to_version.rb |  8 ++++++
 db/schema.rb                                  | 15 ++++++++---
 lib/tasks/repositories.rake                   | 26 +++++++++++++++----
 test/fixtures/branches.yml                    |  9 +++++++
 test/fixtures/versions.yml                    |  4 +--
 test/models/branch_test.rb                    |  7 +++++
 11 files changed, 82 insertions(+), 14 deletions(-)
 create mode 100644 app/models/branch.rb
 create mode 100644 db/migrate/20180804072602_create_branches.rb
 create mode 100644 db/migrate/20180805081706_change_reference_to_version.rb
 create mode 100644 test/fixtures/branches.yml
 create mode 100644 test/models/branch_test.rb

diff --git a/app/models/branch.rb b/app/models/branch.rb
new file mode 100644
index 0000000..adfcb43
--- /dev/null
+++ b/app/models/branch.rb
@@ -0,0 +1,6 @@
+class Branch < ApplicationRecord
+  belongs_to :software
+  has_many :versions
+  validates :name, presence: true
+  validates :name, uniqueness: { scope: :software }
+end
diff --git a/app/models/software.rb b/app/models/software.rb
index 9b7cc26..3b75797 100644
--- a/app/models/software.rb
+++ b/app/models/software.rb
@@ -1,4 +1,4 @@
 class Software < ApplicationRecord
-  has_many :versions
+  has_many :branches
   validates :name, presence: true, uniqueness: true
 end
diff --git a/app/models/version.rb b/app/models/version.rb
index f9b86db..430f761 100644
--- a/app/models/version.rb
+++ b/app/models/version.rb
@@ -1,6 +1,6 @@
 class Version < ApplicationRecord
-  belongs_to :software
+  belongs_to :branch
   validates :number, presence: true
   validates :date, presence: true
-  validates :number, uniqueness: { scope: :software }
+  validates :number, uniqueness: { scope: :branch }
 end
diff --git a/app/views/softwares/show.html.erb b/app/views/softwares/show.html.erb
index 086626c..badad6d 100644
--- a/app/views/softwares/show.html.erb
+++ b/app/views/softwares/show.html.erb
@@ -5,6 +5,8 @@
   <%= link_to 'Delete', software_path(@software), method: :delete, class: 'uk-icon-link', data: { confirm: 'Are you sure?' } %>
 </p>
 <h3><%= @software.name %></h3>
+<% @software.branches.order(name: :desc).each do |branch| %>
+<h4><%= branch.name %></h4>
 <table class="uk-table uk-table-striped">
   <thead>
     <tr>
@@ -12,10 +14,11 @@
       <th>Date</th>
     </tr>
   </thead>
-<% @software.versions.order(date: :desc).each do |version| %>
+<% branch.versions.order(date: :desc).each do |version| %>
   <tr>
     <td><%= version.number %></td>
     <td><%= version.date.strftime('%Y-%m-%d') %></td>
   </tr>
 <% end %>
 </table>
+<% end %>
diff --git a/db/migrate/20180804072602_create_branches.rb b/db/migrate/20180804072602_create_branches.rb
new file mode 100644
index 0000000..b4b292a
--- /dev/null
+++ b/db/migrate/20180804072602_create_branches.rb
@@ -0,0 +1,10 @@
+class CreateBranches < ActiveRecord::Migration[5.2]
+  def change
+    create_table :branches do |t|
+      t.string :name, null: false
+      t.references :software, foreign_key: true
+
+      t.timestamps
+    end
+  end
+end
diff --git a/db/migrate/20180805081706_change_reference_to_version.rb b/db/migrate/20180805081706_change_reference_to_version.rb
new file mode 100644
index 0000000..1cd8a30
--- /dev/null
+++ b/db/migrate/20180805081706_change_reference_to_version.rb
@@ -0,0 +1,8 @@
+class ChangeReferenceToVersion < ActiveRecord::Migration[5.2]
+  def change
+    remove_reference :versions, :software, index: true, foreign_key: true
+    change_table :versions do |t|
+      t.references :branch, foreign_key: true
+    end
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 16c8fe4..3437d27 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,15 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 2018_07_29_191829) do
+ActiveRecord::Schema.define(version: 2018_08_05_081706) do
+
+  create_table "branches", force: :cascade do |t|
+    t.string "name", null: false
+    t.integer "software_id"
+    t.datetime "created_at", null: false
+    t.datetime "updated_at", null: false
+    t.index ["software_id"], name: "index_branches_on_software_id"
+  end
 
   create_table "github_repositories", force: :cascade do |t|
     t.string "name", null: false
@@ -26,15 +34,16 @@ ActiveRecord::Schema.define(version: 2018_07_29_191829) do
     t.datetime "created_at", null: false
     t.datetime "updated_at", null: false
     t.string "repository_type"
+    t.string "branch_pattern"
   end
 
   create_table "versions", force: :cascade do |t|
     t.string "number", null: false
     t.datetime "date", null: false
-    t.integer "software_id"
     t.datetime "created_at", null: false
     t.datetime "updated_at", null: false
-    t.index ["software_id"], name: "index_versions_on_software_id"
+    t.integer "branch_id"
+    t.index ["branch_id"], name: "index_versions_on_branch_id"
   end
 
 end
diff --git a/lib/tasks/repositories.rake b/lib/tasks/repositories.rake
index 47e3c44..c34d336 100644
--- a/lib/tasks/repositories.rake
+++ b/lib/tasks/repositories.rake
@@ -14,6 +14,20 @@ namespace :repositories do
     end.body
   end
 
+  def branch(software_id, branches, version)
+    name = version.match(/^v?(?<branch>\d+\.\d+)(\.\d+)?$/)[:branch]
+
+    return branches[name] if branches.include?(name)
+
+    branch = Branch.new(
+      name: name,
+      software_id: software_id
+    )
+    branch.save
+    branches[name] = branch.id
+    branch.id
+  end
+
   desc 'Update version from github repositories'
   task github: :environment do
     headers = {
@@ -24,7 +38,8 @@ namespace :repositories do
     GithubRepository.all.each do |repo|
       begin
         software = Software.find(repo.software_id)
-        versions = software.versions.all.map(&:number)
+        branches = software.branches.all.map { |b| [b.name, b.id] }.to_h
+        versions = software.branches.all.map { |b| b.versions.all.map(&:number) }
         tags =
           JSON.parse(
             get_url_content(
@@ -36,19 +51,20 @@ namespace :repositories do
         tags.each do |tag|
           next if versions.include?(tag['name'])
 
+          branch_id = branch(software.id, branches, tag['name'])
           date =
             JSON.parse(
               get_url_content(tag['commit']['url'], headers)
-            )['committer']['date']
+            )['commit']['committer']['date']
 
-          Version.new(
-            software_id: software.id,
+          puts Version.new(
+            branch_id: branch_id,
             number: tag['name'],
             date: date
           ).save
         end
       rescue => e
-        puts e
+        puts e.strace
         next
       end
     end
diff --git a/test/fixtures/branches.yml b/test/fixtures/branches.yml
new file mode 100644
index 0000000..2b98ff0
--- /dev/null
+++ b/test/fixtures/branches.yml
@@ -0,0 +1,9 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+  name: 1.0
+  software: two 
+
+two:
+  name: 1.1
+  software: two
diff --git a/test/fixtures/versions.yml b/test/fixtures/versions.yml
index 597d85b..b85e83d 100644
--- a/test/fixtures/versions.yml
+++ b/test/fixtures/versions.yml
@@ -3,9 +3,9 @@
 one:
   number: 1.0.0
   date: 2018-07-22 20:30:46
-  software: two
+  branch: one
 
 two:
   number: 1.1.0
   date: 2018-12-22 20:30:46
-  software: two
+  branch: two
diff --git a/test/models/branch_test.rb b/test/models/branch_test.rb
new file mode 100644
index 0000000..629eb34
--- /dev/null
+++ b/test/models/branch_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class BranchTest < ActiveSupport::TestCase
+  # test "the truth" do
+  #   assert true
+  # end
+end