Skip to content

Commit b8b94d6

Browse files
committed
Add support for setting default branch on repos
This commit adds support for setting the default branch on a repository. * Add :default_branch to GitGud.Repo schema. * Add <form> field to templates/repo/{edit.html.eex, new.html.eex}. * Refactor GitRekt.RepoStorage.init/2 to set initial_head accordingly. * Ensure that HEAD is updated when :default_branch field changes. * Add workaround for views relying on @Head (which can be nil). See #87 for more details.
1 parent 5a5fe51 commit b8b94d6

File tree

15 files changed

+201
-90
lines changed

15 files changed

+201
-90
lines changed

apps/gitgud/lib/gitgud/repo_storage.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ defmodule GitGud.RepoStorage do
2323
"""
2424
@spec init(Repo.t, boolean) :: {:ok, Git.repo} | {:error, term}
2525
def init(%Repo{} = repo, bare?) do
26-
GenServer.call(RepoSupervisor.volume_name(__MODULE__, repo.volume), {:init, workdir(repo), bare?})
26+
GenServer.call(RepoSupervisor.volume_name(__MODULE__, repo.volume), {:init, workdir(repo), bare?, repo.default_branch})
2727
end
2828

2929
@doc """
@@ -93,8 +93,8 @@ defmodule GitGud.RepoStorage do
9393
end
9494

9595
@impl true
96-
def handle_call({:init, workdir, bare?}, _from, state) do
97-
{:reply, Git.repository_init(workdir, bare?), state}
96+
def handle_call({:init, workdir, bare?, initial_head}, _from, state) do
97+
{:reply, Git.repository_init(workdir, bare?, initial_head), state}
9898
end
9999

100100
def handle_call({:rename, old_workdir, new_workdir}, _from, state) do

apps/gitgud/lib/gitgud/schemas/repo.ex

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ defmodule GitGud.Repo do
99

1010
alias Ecto.Multi
1111

12+
alias GitRekt.GitAgent
13+
1214
alias GitGud.DB
1315
alias GitGud.Issue
1416
alias GitGud.IssueLabel
@@ -25,6 +27,7 @@ defmodule GitGud.Repo do
2527
field :owner_login, :string
2628
field :name, :string
2729
field :public, :boolean, default: true
30+
field :default_branch, :string, default: "main"
2831
field :volume, :string, autogenerate: {RepoStorage, :volume, []}
2932
field :description, :string
3033
has_many :issue_labels, IssueLabel, on_replace: :delete
@@ -41,6 +44,7 @@ defmodule GitGud.Repo do
4144
owner: User.t,
4245
name: binary,
4346
public: boolean,
47+
default_branch: binary,
4448
volume: binary,
4549
description: binary,
4650
maintainers: [User.t],
@@ -217,8 +221,8 @@ defmodule GitGud.Repo do
217221
@spec changeset(t, map) :: Ecto.Changeset.t
218222
def changeset(%__MODULE__{} = repo, params \\ %{}) do
219223
repo
220-
|> cast(params, [:name, :public, :description, :pushed_at])
221-
|> validate_required([:name])
224+
|> cast(params, [:name, :public, :description, :default_branch, :pushed_at])
225+
|> validate_required([:name, :default_branch])
222226
|> validate_format(:name, ~r/^[a-zA-Z0-9_-]+$/)
223227
|> validate_length(:name, min: 3, max: 80)
224228
|> validate_exclusion(:name, ["repositories", "settings"])
@@ -285,6 +289,7 @@ defmodule GitGud.Repo do
285289
defp update_and_rename(changeset) do
286290
Multi.new()
287291
|> Multi.update(:repo, changeset)
292+
|> Multi.run(:update_head, &update_head(&1, &2, changeset))
288293
|> Multi.run(:rename, &rename(&1, &2, changeset))
289294
|> DB.transaction()
290295
end
@@ -306,5 +311,19 @@ defmodule GitGud.Repo do
306311
else: RepoStorage.rename(changeset.data, repo)
307312
end
308313

314+
defp update_head(_db, %{repo: repo}, changeset) do
315+
if branch = get_change(changeset, :default_branch) do
316+
target = "refs/heads/" <> branch
317+
case GitAgent.reference_create(repo, "HEAD", :symbolic, target, force: true) do
318+
:ok ->
319+
{:ok, target}
320+
{:error, reason} ->
321+
{:error, reason}
322+
end
323+
else
324+
{:ok, false}
325+
end
326+
end
327+
309328
defp cleanup(_db, %{repo: repo}), do: RepoStorage.cleanup(repo)
310329
end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
defmodule GitGud.DB.Migrations.AddRepositoryDefaultBranchColumn do
2+
use Ecto.Migration
3+
4+
def change do
5+
alter table("repositories") do
6+
add :default_branch, :string, null: false, default: "main"
7+
end
8+
end
9+
end

apps/gitgud_web/lib/gitgud_web/controllers/codebase_controller.ex

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,17 @@ defmodule GitGud.Web.CodebaseController do
290290
{:error, reason} ->
291291
{:error, reason}
292292
end
293+
else
294+
branches = Enum.sort_by(branches, &elem(&1, 2), {:desc, Date})
295+
page = paginate(conn, branches)
296+
slice = resolve_revisions_db(page.slice)
297+
slice = Enum.map(slice, fn {ref, author, timestamp} -> {ref, author, timestamp, {0, 0}} end)
298+
render(conn, "branch_list.html",
299+
repo: repo,
300+
repo_open_issue_count: IssueQuery.count_repo_issues(repo, status: :open),
301+
head: head,
302+
page: Map.put(page, :slice, slice)
303+
)
293304
end
294305
end
295306
end || {:error, :not_found}
@@ -432,10 +443,14 @@ defmodule GitGud.Web.CodebaseController do
432443
end
433444

434445
defp resolve_branches(agent) do
435-
with {:ok, head} <- GitAgent.head(agent),
436-
{:ok, branches} <- GitAgent.branches(agent),
446+
with {:ok, branches} <- GitAgent.branches(agent),
437447
{:ok, branches} <- resolve_revisions(agent, branches) do
438-
{:ok, {head, branches}}
448+
case GitAgent.head(agent) do
449+
{:ok, head} ->
450+
{:ok, {head, branches}}
451+
{:error, _reason} ->
452+
{:ok, {nil, branches}}
453+
end
439454
end
440455
end
441456

apps/gitgud_web/lib/gitgud_web/live/branch_select_live.ex

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -109,21 +109,37 @@ defmodule GitGud.Web.BranchSelectLive do
109109
end
110110

111111
defp resolve_references(agent) do
112-
with {:ok, head} <- GitAgent.head(agent),
113-
{:ok, refs} <- GitAgent.references(agent, with: :commit, target: :commit) do
114-
refs = Enum.to_list(refs)
115-
head_index = Enum.find_index(refs, &elem(&1, 0) == head)
116-
{
117-
:ok,
118-
{
119-
head,
120-
refs
121-
|> List.delete_at(head_index)
122-
|> Enum.map(&map_reference_timestamp!(agent, &1))
123-
|> Enum.sort_by(&elem(&1, 1), {:desc, NaiveDateTime})
124-
|> Enum.map(&elem(&1, 0))
125-
}
126-
}
112+
case GitAgent.references(agent, with: :commit, target: :commit) do
113+
{:ok, refs} ->
114+
case GitAgent.head(agent) do
115+
{:ok, head} ->
116+
refs = Enum.to_list(refs)
117+
head_index = Enum.find_index(refs, &elem(&1, 0) == head)
118+
{
119+
:ok,
120+
{
121+
head,
122+
refs
123+
|> List.delete_at(head_index)
124+
|> Enum.map(&map_reference_timestamp!(agent, &1))
125+
|> Enum.sort_by(&elem(&1, 1), {:desc, NaiveDateTime})
126+
|> Enum.map(&elem(&1, 0))
127+
}
128+
}
129+
{:error, _reason} ->
130+
{
131+
:ok,
132+
{
133+
nil,
134+
refs
135+
|> Enum.map(&map_reference_timestamp!(agent, &1))
136+
|> Enum.sort_by(&elem(&1, 1), {:desc, NaiveDateTime})
137+
|> Enum.map(&elem(&1, 0))
138+
}
139+
}
140+
end
141+
{:error, reason} ->
142+
{:error, reason}
127143
end
128144
end
129145

apps/gitgud_web/lib/gitgud_web/live/branch_select_live.html.heex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@
4040
<a class={if @tab == :branch, do: "is-active"} phx-click="switch_tab" phx-value-tab="branch" phx-target={@myself}>Branches</a>
4141
<a class={if @tab == :tag, do: "is-active"} phx-click="switch_tab" phx-value-tab="tag" phx-target={@myself}>Tags</a>
4242
</p>
43-
<%= if @tab == :branch do %>
44-
<%= live_redirect to: Routes.codebase_path(@socket, @action, @repo.owner_login, @repo, @head, @tree_path), class: ["panel-block", @head.oid == @commit.oid && "is-active"] do %>
43+
<%= if @tab == :branch && @head do %>
44+
<%= live_redirect to: Routes.codebase_path(@socket, @action, @repo.owner_login, @repo, @repo.default_branch, @tree_path), class: ["panel-block", @head.oid == @commit.oid && "is-active"] do %>
4545
<div class="control">
46-
<span class="head"><%= @head.name %></span>
46+
<span class="head"><%= @repo.default_branch %></span>
4747
<span class="tag is-pulled-right">default</span>
4848
</div>
4949
<% end %>

apps/gitgud_web/lib/gitgud_web/live/commit_history_live.ex

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ defmodule GitGud.Web.CommitHistoryLive do
5858
{:ok, head} ->
5959
{:noreply, push_patch(socket, to: Routes.codebase_path(socket, :history, socket.assigns.repo.owner_login, socket.assigns.repo.name, head, Map.get(params, "path", [])))}
6060
{:error, error} ->
61-
raise error
61+
{:noreply, put_flash(socket, :error, error)}
6262
end
6363
end
6464

@@ -114,11 +114,15 @@ defmodule GitGud.Web.CommitHistoryLive do
114114

115115
defp resolve_revision_history(agent, commit, tree_path, cursor, limit) when is_struct(commit), do: resolve_history(agent, commit, tree_path, cursor, limit)
116116
defp resolve_revision_history(agent, rev_spec, tree_path, cursor, limit) do
117-
with {:ok, head} <- GitAgent.head(agent),
118-
{:ok, {obj, ref}} <- GitAgent.revision(agent, rev_spec),
117+
with {:ok, {obj, ref}} <- GitAgent.revision(agent, rev_spec),
119118
{:ok, commit} <- GitAgent.peel(agent, obj, target: :commit),
120119
{:ok, {tree_entry_type, page}} <- resolve_history(agent, commit, tree_path, cursor, limit) do
121-
{:ok, {head, ref, commit, tree_entry_type, page}}
120+
case GitAgent.head(agent) do
121+
{:ok, head} ->
122+
{:ok, {head, ref, commit, tree_entry_type, page}}
123+
{:error, _reason} ->
124+
{:ok,{nil, ref, commit, tree_entry_type, page}}
125+
end
122126
else
123127
{:error, reason} ->
124128
{:error, reason}

apps/gitgud_web/lib/gitgud_web/live/tree_browser_live.ex

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,22 @@ defmodule GitGud.Web.TreeBrowserLive do
8787
if resolve_stats?,
8888
do: resolve_revision_tree_with_stats!(socket.assigns.agent, resolve_revision? && rev_spec || socket.assigns.commit, tree_path),
8989
else: resolve_revision_tree!(socket.assigns.agent, resolve_revision? && rev_spec || socket.assigns.commit, tree_path)
90-
assigns = Map.update!(assigns, :tree_commit_info, &resolve_commit_info_db/1)
91-
assigns = Map.update!(assigns, :tree_entries, &Enum.sort_by(&1, fn tree_entry -> tree_entry.name end))
92-
assigns = Map.update!(assigns, :tree_entries, &Enum.map(&1, fn tree_entry -> {tree_entry, nil} end))
93-
assigns =
94-
if resolve_stats?,
95-
do: Map.update(assigns, :stats, %{}, &Map.put(&1, :contributors, RepoQuery.count_contributors(socket.assigns.repo))),
96-
else: assigns
9790

98-
socket
99-
|> assign(rev_spec: rev_spec, tree_path: tree_path)
100-
|> assign(assigns)
91+
if assigns != %{} do
92+
assigns = Map.update!(assigns, :tree_commit_info, &resolve_commit_info_db/1)
93+
assigns = Map.update!(assigns, :tree_entries, &Enum.sort_by(&1, fn tree_entry -> tree_entry.name end))
94+
assigns = Map.update!(assigns, :tree_entries, &Enum.map(&1, fn tree_entry -> {tree_entry, nil} end))
95+
assigns =
96+
if resolve_stats?,
97+
do: Map.update(assigns, :stats, %{}, &Map.put(&1, :contributors, RepoQuery.count_contributors(socket.assigns.repo))),
98+
else: assigns
99+
100+
socket
101+
|> assign(rev_spec: rev_spec, tree_path: tree_path)
102+
|> assign(assigns)
103+
else
104+
socket
105+
end
101106
end
102107

103108
defp assign_tree_commits_async(socket) do
@@ -118,23 +123,28 @@ defmodule GitGud.Web.TreeBrowserLive do
118123
end
119124

120125
defp resolve_revision(agent, nil) do
121-
with {:ok, false} <- GitAgent.empty?(agent),
122-
{:ok, head} <- GitAgent.head(agent),
123-
{:ok, commit} <- GitAgent.peel(agent, head) do
124-
{:ok, {head, head, commit}}
125-
else
126-
{:ok, true} ->
127-
{:error, "repository is empty"}
128-
{:error, reason} ->
129-
{:error, reason}
126+
case GitAgent.head(agent) do
127+
{:ok, head} ->
128+
case GitAgent.peel(agent, head) do
129+
{:ok, commit} ->
130+
{:ok, {head, head, commit}}
131+
{:error, reason} ->
132+
{:error, reason}
133+
end
134+
{:error, _reason} ->
135+
{:error, :invalid_head}
130136
end
131137
end
132138

133139
defp resolve_revision(agent, rev_spec) do
134-
with {:ok, head} <- GitAgent.head(agent),
135-
{:ok, {obj, ref}} <- GitAgent.revision(agent, rev_spec),
140+
with {:ok, {obj, ref}} <- GitAgent.revision(agent, rev_spec),
136141
{:ok, commit} <- GitAgent.peel(agent, obj, target: :commit) do
137-
{:ok, {head, ref, commit}}
142+
case GitAgent.head(agent) do
143+
{:ok, head} ->
144+
{:ok, {head, ref, commit}}
145+
{:error, _reason} ->
146+
{:ok, {nil, ref, commit}}
147+
end
138148
end
139149
end
140150

@@ -144,6 +154,8 @@ defmodule GitGud.Web.TreeBrowserLive do
144154
%{head: head, revision: ref || commit, commit: commit, tree_commit_info: commit_info, tree_entries: tree_entries, tree_readme: readme}
145155
{:ok, {commit_info, tree_entries, readme}} ->
146156
%{tree_commit_info: commit_info, tree_entries: tree_entries, tree_readme: readme}
157+
{:error, :invalid_head} ->
158+
%{}
147159
{:error, error} ->
148160
raise error
149161
end
@@ -166,6 +178,8 @@ defmodule GitGud.Web.TreeBrowserLive do
166178
%{head: head, revision: ref || commit, commit: commit, tree_commit_info: commit_info, tree_entries: tree_entries, tree_readme: readme, stats: stats}
167179
{:ok, {commit_info, tree_entries, readme, stats}} ->
168180
%{tree_commit_info: commit_info, tree_entries: tree_entries, tree_readme: readme, stats: stats}
181+
{:error, :invalid_head} ->
182+
%{}
169183
{:error, error} ->
170184
raise error
171185
end

apps/gitgud_web/lib/gitgud_web/live/tree_browser_live.html.heex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,13 +200,13 @@
200200
git add README.md
201201
git commit -m "first commit"
202202
git remote add origin <%= Routes.codebase_url(@socket, :show, @repo.owner_login, @repo) %>.git
203-
git push -u origin master
203+
git push -u origin <%= @repo.default_branch %>
204204
</pre>
205205

206206
<p>or push an existing repository from the command line</p>
207207
<pre>
208208
git remote add origin <%= Routes.codebase_url(@socket, :show, @repo.owner_login, @repo) %>.git
209-
git push -u origin master
209+
git push -u origin <%= @repo.default_branch %>
210210
</pre>
211211
</div>
212212
<% end %>

0 commit comments

Comments
 (0)