Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/FolderManager/FileView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane
public const string ACTION_DELETE = "delete";
public const string ACTION_NEW_FILE = "new-file";
public const string ACTION_NEW_FOLDER = "new-folder";
public const string ACTION_CHANGE_BRANCH = "change-branch";
public const string ACTION_CHECKOUT_LOCAL_BRANCH = "checkout-local-branch";
public const string ACTION_CHECKOUT_REMOTE_BRANCH = "checkout-remote-branch";
public const string ACTION_CLOSE_FOLDER = "close-folder";
public const string ACTION_CLOSE_OTHER_FOLDERS = "close-other-folders";
public const string ACTION_SET_ACTIVE_PROJECT = "set-active-project";
Expand Down
83 changes: 64 additions & 19 deletions src/FolderManager/ProjectFolderItem.vala
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ namespace Scratch.FolderManager {

private static Icon added_icon;
private static Icon modified_icon;
private SimpleAction change_branch_action;
private SimpleAction checkout_local_branch_action;
private SimpleAction checkout_remote_branch_action;

public signal void closed ();

Expand Down Expand Up @@ -68,16 +69,21 @@ namespace Scratch.FolderManager {
);
}

change_branch_action.set_state (monitored_repo.branch_name);
checkout_local_branch_action.set_state (monitored_repo.branch_name);
}
}

construct {
monitored_repo = Scratch.Services.GitManager.get_instance ().add_project (this);
notify["name"].connect (branch_or_name_changed);
if (monitored_repo != null) {
change_branch_action = new SimpleAction.stateful (
FileView.ACTION_CHANGE_BRANCH,
checkout_local_branch_action = new SimpleAction.stateful (
FileView.ACTION_CHECKOUT_LOCAL_BRANCH,
GLib.VariantType.STRING,
""
);
checkout_remote_branch_action = new SimpleAction.stateful (
FileView.ACTION_CHECKOUT_REMOTE_BRANCH,
GLib.VariantType.STRING,
""
);
Expand All @@ -86,7 +92,8 @@ namespace Scratch.FolderManager {
monitored_repo.file_status_change.connect (() => update_item_status (null));
monitored_repo.update_status_map ();
monitored_repo.branch_changed ();
change_branch_action.activate.connect (handle_change_branch_action);
checkout_local_branch_action.activate.connect (handle_checkout_local_branch_action);
checkout_remote_branch_action.activate.connect (handle_checkout_remote_branch_action);
}
}

Expand Down Expand Up @@ -315,20 +322,44 @@ namespace Scratch.FolderManager {

protected GLib.MenuItem create_submenu_for_branch () {
// Ensures that action for relevant project is being used
view.actions.add_action (change_branch_action);

GLib.Menu branch_selection_menu = new GLib.Menu ();
foreach (unowned var branch_name in monitored_repo.get_local_branches ()) {
branch_selection_menu.append (
branch_name,
GLib.Action.print_detailed_name (
FileView.ACTION_PREFIX + FileView.ACTION_CHANGE_BRANCH,
branch_name
)
);
view.actions.add_action (checkout_local_branch_action);
view.actions.add_action (checkout_remote_branch_action);

unowned var local_branches = monitored_repo.get_local_branches ();
var local_branch_submenu = new Menu ();
var local_branch_menu = new Menu ();
if (local_branches.length () > 0) {
local_branch_submenu.append_submenu (_("Local"), local_branch_menu);
foreach (unowned var branch_name in local_branches) {
local_branch_menu.append (
branch_name,
GLib.Action.print_detailed_name (
FileView.ACTION_PREFIX + FileView.ACTION_CHECKOUT_LOCAL_BRANCH,
branch_name
)
);
}
}


unowned var remote_branches = monitored_repo.get_remote_branches ();
var remote_branch_submenu = new Menu ();
var remote_branch_menu = new Menu ();
if (remote_branches.length () > 0) {
remote_branch_submenu.append_submenu (_("Remote"), remote_branch_menu);
foreach (unowned var branch_name in remote_branches) {
remote_branch_menu.append (
branch_name,
GLib.Action.print_detailed_name (
FileView.ACTION_PREFIX + FileView.ACTION_CHECKOUT_REMOTE_BRANCH,
branch_name
)
);
}


}

var new_branch_item = new GLib.MenuItem (
_("New Branch…"),
GLib.Action.print_detailed_name (
Expand All @@ -351,22 +382,36 @@ namespace Scratch.FolderManager {
bottom_section.append_item (new_branch_item);

var menu = new GLib.Menu ();
menu.append_section (null, branch_selection_menu);
menu.append_section (null, local_branch_submenu);
menu.append_section (null, remote_branch_submenu);
menu.append_section (null, bottom_section);

var menu_item = new GLib.MenuItem.submenu (_("Branch"), menu);
return menu_item;
}

private void handle_change_branch_action (GLib.Variant? parameter) {
var branch_name = parameter.get_string ();
private void handle_checkout_local_branch_action (GLib.Variant? param) {
var branch_name = param != null ? param.get_string () : "";
try {
monitored_repo.change_local_branch (branch_name);
} catch (GLib.Error e) {
warning ("Failed to change branch to %s. %s", branch_name, e.message);
}
}

private void handle_checkout_remote_branch_action (GLib.Variant? param) {
var branch_name = param != null ? param.get_string () : "";
if (branch_name == "") {
return;
}

try {
monitored_repo.checkout_remote_branch (branch_name);
} catch (GLib.Error e) {
warning ("Failed to change branch to %s. %s", branch_name, e.message);
}
}

public void update_item_status (FolderItem? start_folder) {
if (monitored_repo == null) {
debug ("Ignore non-git folders");
Expand Down
65 changes: 64 additions & 1 deletion src/Services/MonitoredRepository.vala
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ namespace Scratch.Services {
}

public class MonitoredRepository : Object {
public const string ORIGIN_PREFIX = "origin/";
public Ggit.Repository git_repo { get; set construct; }
public string branch_name {
get {
Expand Down Expand Up @@ -67,6 +68,8 @@ namespace Scratch.Services {
// Need to use nullable status in order to pass Flatpak CI.
private Gee.HashMap<string, Ggit.StatusFlags?> file_status_map;

private List<Ggit.Ref> remote_branch_ref_list;

public Gee.Set<Gee.Map.Entry<string, Ggit.StatusFlags?>> non_current_entries {
owned get {
return file_status_map.entries;
Expand Down Expand Up @@ -97,6 +100,8 @@ namespace Scratch.Services {
Ggit.StatusShow.INDEX_AND_WORKDIR,
null
);

remote_branch_ref_list = new List<Ggit.Ref> ();
}

public MonitoredRepository (Ggit.Repository _git_repo) {
Expand Down Expand Up @@ -168,6 +173,28 @@ namespace Scratch.Services {
return branches;
}

public unowned List<string> get_remote_branches () {
unowned List<string> branch_names = null;
try {
var branch_enumerator = git_repo.enumerate_branches (Ggit.BranchType.REMOTE);

foreach (Ggit.Ref branch_ref in branch_enumerator) {
var remote_name = branch_ref.get_shorthand ();
if (!remote_name.has_suffix ("HEAD") &&
!has_local_branch_name (remote_name.substring (ORIGIN_PREFIX.length))) {

branch_names.append (branch_ref.get_shorthand ());
}

remote_branch_ref_list.append (branch_ref);
}
} catch (Error e) {
warning ("Could not enumerate local branches %s", e.message);
}

return branch_names;
}

public bool has_local_branch_name (string name) {
try {
git_repo.lookup_branch (name, Ggit.BranchType.LOCAL);
Expand All @@ -186,11 +213,47 @@ namespace Scratch.Services {
return true;
}

public void change_local_branch (string new_branch_name) throws Error {
public void change_local_branch (string new_branch_name) throws Error
requires (!new_branch_name.has_prefix (ORIGIN_PREFIX)) {

var branch = git_repo.lookup_branch (new_branch_name, Ggit.BranchType.LOCAL);
checkout_branch (branch);
}

public void checkout_remote_branch (string target_shorthand) throws Error
requires (target_shorthand.has_prefix (ORIGIN_PREFIX)) {

Ggit.Ref? branch_ref;
//Assume list is up to date as this is called from context menu
unowned var list_pointer = remote_branch_ref_list.first ();
while (list_pointer.data != null &&
list_pointer.data.get_shorthand () != target_shorthand) {

list_pointer = list_pointer.next;
}

branch_ref = list_pointer.data;
if (branch_ref == null) {
var dialog = new Granite.MessageDialog.with_image_from_icon_name (
_("Remote Branch '%s' not found").printf (target_shorthand),
_("The requested branch was not found in any remote linked to this repository"),
"dialog-warning"
) {
modal = true
};

dialog.response.connect (() => {dialog.destroy ();});
dialog.present ();
return;
}

var commit = branch_ref.lookup ();
var local_name = target_shorthand.substring (ORIGIN_PREFIX.length);
var local_branch = git_repo.create_branch (local_name, commit, NONE) as Ggit.Branch;
checkout_branch (local_branch);
local_branch.set_upstream (target_shorthand);
}

private void checkout_branch (Ggit.Branch new_head_branch, bool confirm = true) {
var new_branch_name = "";
try {
Expand Down
1 change: 1 addition & 0 deletions vapi/libgit2-glib-1.0.vapi
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ namespace Ggit {
public Ggit.Ref? get_upstream () throws GLib.Error;
public bool is_head () throws GLib.Error;
public Ggit.Branch? move (string new_branch_name, Ggit.CreateFlags flags) throws GLib.Error;
public void set_upstream (string upstream_branch_name) throws GLib.Error;
}
[CCode (cheader_filename = "libgit2-glib/ggit.h", ref_function = "ggit_branch_enumerator_ref", type_id = "ggit_branch_enumerator_get_type ()", unref_function = "ggit_branch_enumerator_unref")]
[Compact]
Expand Down