tekkub-fugit 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :minor: 0
3
- :patch: 4
3
+ :patch: 5
4
4
  :major: 0
data/fugit.gemspec CHANGED
@@ -1,15 +1,15 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = %q{fugit}
3
- s.version = "0.0.4"
3
+ s.version = "0.0.5"
4
4
 
5
5
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
6
6
  s.authors = ["Tekkub"]
7
- s.date = %q{2009-02-18}
7
+ s.date = %q{2009-02-27}
8
8
  s.default_executable = %q{fugit}
9
9
  s.description = %q{A cross-platform replacement for git-gui based on wxruby}
10
10
  s.email = %q{tekkub@gmail.com}
11
11
  s.executables = ["fugit"]
12
- s.files = ["fugit.gemspec", "SciTE.properties", "VERSION.yml", "bin/fugit", "lib/fugit", "lib/fugit/commit.rb", "lib/fugit/commit_tab.rb", "lib/fugit/console.rb", "lib/fugit/diff.rb", "lib/fugit/graph_renderer.rb", "lib/fugit/history_list.rb", "lib/fugit/history_tab.rb", "lib/fugit/icon_loader.rb", "lib/fugit/index_list.rb", "lib/fugit/io_get_line.rb", "lib/fugit/main_frame.rb", "lib/fugit/messages.rb", "lib/fugit/push_dialog.rb", "lib/fugit/SciTE.properties", "lib/fugit.rb", "lib/icons", "lib/icons/asterisk_yellow.png", "lib/icons/disk.png", "lib/icons/folder_add.png", "lib/icons/folder_delete.png", "lib/icons/page_add.png", "lib/icons/page_delete.png", "lib/icons/page_down.gif", "lib/icons/page_up.gif", "lib/icons/plus_minus.gif", "lib/icons/script.png", "lib/icons/script_add.png", "lib/icons/script_delete.png", "lib/icons/script_edit.png", "lib/icons/text_signature.png", "lib/icons/tick.png"]
12
+ s.files = ["fugit.gemspec", "SciTE.properties", "VERSION.yml", "bin/fugit", "lib/fugit", "lib/fugit/commit_dialog.rb", "lib/fugit/commit_tab.rb", "lib/fugit/commit_tab_toolbar.rb", "lib/fugit/console.rb", "lib/fugit/delete_branch_dialog.rb", "lib/fugit/diff.rb", "lib/fugit/graph_renderer.rb", "lib/fugit/history_list.rb", "lib/fugit/history_tab.rb", "lib/fugit/icon_loader.rb", "lib/fugit/index_list.rb", "lib/fugit/io_get_line.rb", "lib/fugit/main_frame.rb", "lib/fugit/messages.rb", "lib/fugit/push_dialog.rb", "lib/fugit/SciTE.properties", "lib/fugit.rb", "lib/icons", "lib/icons/arrow_divide.png", "lib/icons/arrow_divide_delete.png", "lib/icons/arrow_join.png", "lib/icons/arrow_undo.png", "lib/icons/asterisk_yellow.png", "lib/icons/cherry.png", "lib/icons/disk.png", "lib/icons/folder_add.png", "lib/icons/folder_delete.png", "lib/icons/page_add.png", "lib/icons/page_delete.png", "lib/icons/page_down.gif", "lib/icons/page_up.gif", "lib/icons/plus_minus.gif", "lib/icons/script.png", "lib/icons/script_add.png", "lib/icons/script_delete.png", "lib/icons/script_edit.png", "lib/icons/text_signature.png", "lib/icons/tick.png"]
13
13
  s.has_rdoc = true
14
14
  s.homepage = %q{http://github.com/tekkub/fugit}
15
15
  s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
data/lib/fugit.rb CHANGED
@@ -3,18 +3,21 @@ $:.unshift File.dirname(__FILE__)
3
3
 
4
4
  require 'wx'
5
5
 
6
- require "fugit/io_get_line"
7
- require "fugit/messages"
8
6
  require "fugit/icon_loader"
9
- require "fugit/main_frame"
7
+
8
+ require "fugit/commit_dialog"
10
9
  require "fugit/commit_tab"
11
- require "fugit/commit"
10
+ require "fugit/commit_tab_toolbar"
12
11
  require "fugit/console"
12
+ require "fugit/delete_branch_dialog"
13
13
  require "fugit/diff"
14
- require "fugit/index_list"
15
- require "fugit/history_tab"
16
14
  require "fugit/graph_renderer"
17
15
  require "fugit/history_list"
16
+ require "fugit/history_tab"
17
+ require "fugit/index_list"
18
+ require "fugit/io_get_line"
19
+ require "fugit/main_frame"
20
+ require "fugit/messages"
18
21
  require "fugit/push_dialog"
19
22
 
20
23
 
@@ -2,58 +2,66 @@ include Wx
2
2
  include IconLoader
3
3
 
4
4
  module Fugit
5
- class Commit < Panel
5
+ class CommitDialog < Dialog
6
6
  def initialize(parent)
7
- super(parent, ID_ANY)
7
+ super(parent, ID_ANY, "Commit changes", :size => Size.new(500, 250))
8
8
 
9
9
  @input = TextCtrl.new(self, ID_ANY, nil, nil, nil, TE_MULTILINE|TE_DONTWRAP)
10
+ @amend_check = CheckBox.new(self, ID_ANY)
11
+ @amend_check.set_label("&Amend previous commit")
10
12
  @author = TextCtrl.new(self, ID_ANY)
11
13
  @committer = TextCtrl.new(self, ID_ANY)
12
14
  @committer.disable
13
15
 
14
- @toolbar = ToolBar.new(self, ID_ANY)
15
- @toolbar.set_tool_bitmap_size(Size.new(16,16))
16
- @toolbar.add_tool(101, "Commit", get_icon("disk.png"), "Commit")
17
- @toolbar.add_tool(102, "Sign off", get_icon("text_signature.png"), "Sign off")
18
- @toolbar.add_separator
19
- @toolbar.add_tool(103, "Push", get_icon("page_up.gif"), "Push")
20
- #~ @toolbar.add_tool(104, "Pull", get_icon("page_down.gif"), "Pull")
21
- @toolbar.realize
22
-
23
- box = BoxSizer.new(HORIZONTAL)
24
- box.add(@committer, 1, EXPAND)
25
- box.add(@author, 1, EXPAND)
26
-
27
- flex = FlexGridSizer.new(2,2,0,0)
28
- flex.add(StaticText.new(self, ID_ANY, "Committer/Author:"), 0, EXPAND)
29
- flex.add(box, 0, EXPAND)
30
- flex.add(StaticText.new(self, ID_ANY, "Commit message:"), 0, EXPAND)
16
+ evt_checkbox(@amend_check, :on_amend_checked)
17
+
18
+ flex = FlexGridSizer.new(4,2,4,4)
19
+ flex.add(StaticText.new(self, ID_ANY, "Committer:"), 0, ALIGN_RIGHT)
20
+ flex.add(@committer, 0, EXPAND)
21
+ flex.add(StaticText.new(self, ID_ANY, "Author:"), 0, ALIGN_RIGHT)
22
+ flex.add(@author, 0, EXPAND)
23
+ flex.add(StaticText.new(self, ID_ANY, "Commit message:"), 0, ALIGN_RIGHT)
31
24
  flex.add(@input, 0, EXPAND)
32
- flex.add_growable_row(1)
25
+ flex.add(1,1) # Filler
26
+ flex.add(@amend_check, 0)
27
+ flex.add_growable_row(2)
33
28
  flex.add_growable_col(1)
34
29
 
30
+ butt_sizer = create_button_sizer(OK|CANCEL)
31
+ butt_sizer.get_children.map {|s| s.get_window}.compact.each {|b| b.set_label("Commit") if b.get_label == "OK"}
32
+ evt_button(get_affirmative_id, :on_ok)
33
+
35
34
  box = BoxSizer.new(VERTICAL)
36
- box.add(@toolbar, 0, EXPAND)
37
- box.add_spacer(4)
38
- box.add(flex, 1, EXPAND)
35
+ box.add(flex, 1, EXPAND|ALL, 4)
36
+ box.add(butt_sizer, 0, EXPAND|BOTTOM, 4)
39
37
  self.set_sizer(box)
40
38
 
41
- evt_tool(101, :on_commit_clicked)
42
- evt_tool(103, :on_push_clicked)
43
-
44
- register_for_message(:save_clicked, :on_commit_clicked)
45
- register_for_message(:commit_saved, :on_commit_saved)
46
- register_for_message(:refresh, :update)
39
+ self.accelerator_table = AcceleratorTable.new(AcceleratorEntry.new(MOD_CMD, ?s, ID_OK))
40
+ end
47
41
 
42
+ def show_modal
48
43
  name = `git config user.name`
49
44
  email = `git config user.email`
50
45
  @committer.set_value("#{name.chomp} <#{email.chomp}>")
51
46
  @author.set_value("#{name.chomp} <#{email.chomp}>")
47
+ @input.set_value("")
48
+ @amend_check.set_value(false)
49
+ @input.set_focus
50
+
51
+ super
52
+ end
53
+
54
+ def on_amend_checked(event)
55
+ return unless event.is_checked && @input.get_value.empty?
56
+
57
+ raw_log = `git log -1 --pretty=raw`
58
+ @author.set_value($1) if raw_log =~ /author (.+>)/
59
+ @input.set_value($1.split("\n").map {|l| l.strip}.join("\n")) if raw_log =~ /\n\n (.+)\n\Z/m
52
60
  end
53
61
 
54
- def on_commit_clicked
62
+ def on_ok
55
63
  msg = @input.get_value
56
- if !has_staged_changes?
64
+ if !has_staged_changes? && !@amend_check.is_checked
57
65
  @nothing_to_commit_error ||= MessageDialog.new(self, "No changes are staged to commit.", "Commit error", OK|ICON_ERROR)
58
66
  @nothing_to_commit_error.show_modal
59
67
  elsif msg.empty?
@@ -62,19 +70,13 @@ module Fugit
62
70
  else
63
71
  commit_file = File.join(Dir.pwd, ".git", "fugit_commit.txt")
64
72
  File.open(commit_file, "w") {|f| f << msg}
65
- `git commit --file=.git/fugit_commit.txt --author="#{@author.get_value}"`
73
+ amend = @amend_check.is_checked ? "--amend " : ""
74
+ `git commit #{amend}--file=.git/fugit_commit.txt --author="#{@author.get_value}"`
66
75
  File.delete(commit_file)
67
- send_message(:commit_saved)
76
+ end_modal ID_OK
68
77
  end
69
78
  end
70
79
 
71
- def on_commit_saved
72
- name = `git config user.name`
73
- email = `git config user.email`
74
- @author.set_value("#{name.chomp} <#{email.chomp}>")
75
- @input.set_value("")
76
- end
77
-
78
80
  def has_staged_changes?
79
81
  staged = `git ls-files --stage`
80
82
  last_commit = `git ls-tree -r HEAD`
@@ -95,10 +97,5 @@ module Fugit
95
97
  !staged.empty?
96
98
  end
97
99
 
98
- def on_push_clicked
99
- @push_dialog ||= PushDialog .new(self)
100
- @push_dialog.show
101
- end
102
-
103
100
  end
104
101
  end
@@ -9,18 +9,16 @@ module Fugit
9
9
  @vsplitter = SplitterWindow.new(self, ID_ANY)
10
10
  @vsplitter.set_minimum_pane_size(150)
11
11
 
12
- @hsplitter = SplitterWindow.new(@vsplitter, ID_ANY)
13
- @hsplitter.set_minimum_pane_size(150)
14
- @hsplitter.set_sash_gravity(1.0)
15
12
 
16
- @diff = Diff.new(@hsplitter)
17
- @commit = Commit.new(@hsplitter)
18
- @hsplitter.split_horizontally(@diff, @commit)
13
+ @toolbar = CommitTabToolbar.new(self)
14
+ @diff = Diff.new(@vsplitter)
19
15
 
20
16
  @index = IndexList.new(@vsplitter)
21
- @vsplitter.split_vertically(@index, @hsplitter, 200)
17
+ @vsplitter.split_vertically(@index, @diff, 200)
22
18
 
23
19
  box = BoxSizer.new(VERTICAL)
20
+ box.add(@toolbar, 0, EXPAND)
21
+ box.add_spacer(3)
24
22
  box.add(@vsplitter, 1, EXPAND)
25
23
  self.set_sizer(box)
26
24
 
@@ -0,0 +1,111 @@
1
+ include Wx
2
+ include IconLoader
3
+
4
+ module Fugit
5
+ class CommitTabToolbar < ToolBar
6
+ def initialize(parent)
7
+ super(parent, ID_ANY, nil, nil, TB_HORIZONTAL|NO_BORDER|TB_NODIVIDER)
8
+
9
+ self.set_tool_bitmap_size(Size.new(16,16))
10
+
11
+ stage_all_button = self.add_tool(ID_ANY, "Stage all", get_icon("folder_add.png"), "Stage all")
12
+ evt_tool(stage_all_button, :on_stage_all_clicked)
13
+
14
+ stage_button = self.add_tool(ID_ANY, "Stage", get_icon("page_add.png"), "Stage changed files")
15
+ evt_tool(stage_button, :on_stage_changed_clicked)
16
+
17
+ unstage_all_button = self.add_tool(ID_ANY, "Unstage all", get_icon("folder_delete.png"), "Unstage all")
18
+ evt_tool(unstage_all_button, :on_unstage_all_clicked)
19
+
20
+ self.add_separator
21
+
22
+ commit = self.add_tool(ID_ANY, "Commit", get_icon("disk.png"), "Commit")
23
+ evt_tool(commit, :on_commit_clicked)
24
+
25
+ self.add_separator
26
+
27
+ push = self.add_tool(ID_ANY, "Push", get_icon("page_up.gif"), "Push")
28
+ evt_tool(push, :on_push_clicked)
29
+
30
+ pull = self.add_tool(ID_ANY, "Pull", get_icon("page_down.gif"), "Pull")
31
+ self.enable_tool(pull.get_id, false)
32
+
33
+ self.add_separator
34
+
35
+ self.add_control(@branch = Choice.new(self, ID_ANY))
36
+ set_branches
37
+ evt_choice(@branch, :on_branch_choice)
38
+
39
+ merge_branch_button = self.add_tool(ID_ANY, "Merge branch", get_icon("arrow_join.png"), "Merge branch")
40
+ self.enable_tool(merge_branch_button.get_id, false)
41
+
42
+ delete_branch_button = self.add_tool(ID_ANY, "Delete branch", get_icon("arrow_divide_delete.png"), "Delete branch")
43
+ evt_tool(delete_branch_button, :on_delete_branch_clicked)
44
+
45
+ self.realize
46
+
47
+ register_for_message(:tab_switch, :update_tools)
48
+ register_for_message(:branch_deleted, :update_tools)
49
+ register_for_message(:refresh, :update_tools)
50
+ register_for_message(:save_clicked, :on_commit_clicked)
51
+ register_for_message(:push_clicked, :on_push_clicked)
52
+ end
53
+
54
+ def update_tools
55
+ return unless is_shown_on_screen
56
+ set_branches
57
+ end
58
+
59
+ def set_branches
60
+ branches = `git branch`
61
+ current = branches.match(/\* (.+)/).to_a.last
62
+ @branch.clear
63
+ branches.split("\n").each {|b| @branch.append(b.split(" ").last)}
64
+ @branch.set_string_selection(current) if current
65
+ end
66
+
67
+ def on_stage_all_clicked(event)
68
+ `git add --all 2>&1`
69
+ send_message(:index_changed)
70
+ end
71
+
72
+ def on_stage_changed_clicked(event)
73
+ `git add --update 2>&1`
74
+ send_message(:index_changed)
75
+ end
76
+
77
+ def on_unstage_all_clicked(event)
78
+ `git reset 2>&1`
79
+ send_message(:index_changed)
80
+ end
81
+
82
+ def on_commit_clicked
83
+ @commit_dialog ||= CommitDialog.new(self)
84
+ send_message(:commit_saved) if @commit_dialog.show_modal == ID_OK
85
+ end
86
+
87
+ def on_push_clicked
88
+ @push_dialog ||= PushDialog.new(self)
89
+ @push_dialog.show
90
+ end
91
+
92
+ def on_branch_choice(event)
93
+ branch = @branch.get_string(event.get_selection)
94
+ err = `git checkout #{branch} 2>&1`
95
+ if err =~ /Switched to branch "#{branch}"/
96
+ send_message(:branch_checkout)
97
+ else
98
+ MessageDialog.new(self, err, "Branch checkout error", OK|ICON_ERROR).show_modal
99
+ branches = `git branch`
100
+ current = branches.match(/\* (.+)/).to_a.last
101
+ @branch.set_string_selection(current) if current
102
+ end
103
+ end
104
+
105
+ def on_delete_branch_clicked
106
+ @delete_branch_dialog ||= DeleteBranchDialog.new(self)
107
+ @delete_branch_dialog.show
108
+ end
109
+
110
+ end
111
+ end
@@ -0,0 +1,48 @@
1
+ include Wx
2
+
3
+ module Fugit
4
+ class DeleteBranchDialog < Dialog
5
+ def initialize(parent)
6
+ super(parent, ID_ANY, "Delete branches", :size => Size.new(250, 300))
7
+
8
+ @branch_list = CheckListBox.new(self, ID_ANY)
9
+
10
+ butt_sizer = create_button_sizer(OK|CANCEL)
11
+ butt_sizer.get_children.map {|s| s.get_window}.compact.each {|b| b.set_label("Delete") if b.get_label == "OK"}
12
+ evt_button(get_affirmative_id, :on_ok)
13
+
14
+ box = BoxSizer.new(VERTICAL)
15
+ box.add(StaticText.new(self, ID_ANY, "Select branches:"), 0, EXPAND|ALL, 4)
16
+ box.add(@branch_list, 1, EXPAND|LEFT|RIGHT|BOTTOM, 4)
17
+ box.add(butt_sizer, 0, EXPAND|BOTTOM, 4)
18
+
19
+ self.set_sizer(box)
20
+ end
21
+
22
+ def show
23
+ branches = `git branch`
24
+ branches = branches.split("\n").reject {|b| b[0..0] == "*"}.map {|b| b.strip}
25
+ @branch_list.set(branches)
26
+
27
+ super
28
+ end
29
+
30
+ def on_ok
31
+ unmerged = `git branch --no-merged`
32
+ unmerged.split("\n").map {|b| b.strip}
33
+
34
+ branches = @branch_list.get_checked_items.map {|i| @branch_list.get_string(i)}
35
+ unless (unmerged_to_delete = branches.reject {|b| !unmerged.include?(b)}).empty?
36
+ dialog = MessageDialog.new(self, "These branches are not merged into the current HEAD:\n #{unmerged_to_delete.join("\n ")}\n\nDeleting them may cause data loss, continue?",
37
+ "Unmerged branches", YES_NO|NO_DEFAULT|ICON_EXCLAMATION)
38
+ return if dialog.show_modal != ID_YES
39
+ end
40
+ `git branch -D #{branches.join(" ")} 2>&1`
41
+
42
+ send_message(:branch_deleted)
43
+
44
+ end_modal(ID_OK)
45
+ end
46
+
47
+ end
48
+ end
data/lib/fugit/diff.rb CHANGED
@@ -3,11 +3,10 @@ include Wx
3
3
  module Fugit
4
4
  class Diff < Panel
5
5
  def initialize(parent)
6
- super(parent, ID_ANY)
6
+ super(parent, ID_ANY, :style => SIMPLE_BORDER)
7
7
  self.set_font(Font.new(8, FONTFAMILY_TELETYPE, FONTSTYLE_NORMAL, FONTWEIGHT_NORMAL))
8
8
 
9
- @list = TreeCtrl.new(self, ID_ANY, nil, nil, NO_BORDER|TR_MULTIPLE|TR_HIDE_ROOT|TR_FULL_ROW_HIGHLIGHT|TR_NO_LINES)
10
- @root = @list.add_root("root")
9
+ @list = ListCtrl.new(self, ID_ANY, :style => LC_REPORT|LC_VRULES|NO_BORDER|LC_NO_HEADER)
11
10
  @list.hide
12
11
 
13
12
  @list_menu = Menu.new
@@ -25,8 +24,8 @@ module Fugit
25
24
  self.set_sizer(@box)
26
25
 
27
26
  #~ evt_tree_sel_changed(@list.get_id, :on_click)
28
- evt_tree_item_menu(@list.get_id, :on_item_menu_request)
29
- evt_tree_item_activated(@list.get_id, :on_double_click)
27
+ evt_list_item_right_click(@list.get_id, :on_item_menu_request)
28
+ evt_list_item_activated(@list.get_id, :on_double_click)
30
29
 
31
30
  register_for_message(:commit_saved, :clear)
32
31
  register_for_message(:diff_clear, :clear)
@@ -43,8 +42,11 @@ module Fugit
43
42
  @text.hide
44
43
  @list.hide
45
44
 
46
- @list.delete_children(@root)
45
+ @list.clear_all
46
+ @list.insert_column(0, "Graph")
47
47
 
48
+ reverse_diff = type == :staged ? ["-", "\\+"] : ["+", "-"]
49
+ last_id = -1
48
50
  chunks.each do |chunk|
49
51
  chunk_diff = header + "\n" + chunk
50
52
  chunk_diff += "\n" if chunk_diff[-1..-1] != "\n" # git bitches if we don't have a proper newline at the end of the diff
@@ -57,16 +59,14 @@ module Fugit
57
59
  diff_val = chunk_lines.first.match(/\A@@ -\d+,(\d+)/)[1].to_i + (line[0..0] == "+" ? 1 : -1)
58
60
  chunk_lines[0] = chunk_lines.first.gsub(/\+(\d+),\d+/, '+\1,' + diff_val.to_s)
59
61
  chunk_lines.delete_at(i)
60
- chunk_lines.map! {|l| l.gsub(/\A-/, " ")}
62
+ chunk_lines.map! {|l| l[0..0] == reverse_diff[0] ? "#{line}~~~DELETE~~~" : l.gsub(/\A#{reverse_diff[1]}/, " ")}
61
63
  chunk_lines.insert(i, line)
62
- chunk_lines.reject! {|l| l[0..0] == "+" && l != line}
64
+ chunk_lines.reject! {|l| l == "#{line}~~~DELETE~~~"}
63
65
  header + "\n" + chunk_lines.join("\n") + "\n"
64
66
  else
65
67
  ""
66
68
  end
67
69
 
68
- id = @list.append_item(@root, line.gsub("\t", " "), -1, -1, [chunk_diff, line_diff, type])
69
-
70
70
  color = case line[0..0]
71
71
  when "+"
72
72
  Colour.new(0, 96, 0)
@@ -83,11 +83,22 @@ module Fugit
83
83
  when "@"
84
84
  Colour.new(220, 220, 225)
85
85
  end
86
- @list.set_item_text_colour(id, color) if color
87
- @list.set_item_background_colour(id, bgcolor) if bgcolor
86
+
87
+ item = ListItem.new
88
+ item.set_id(last_id += 1)
89
+ item.set_column(0)
90
+ item.set_data([chunk_diff, line_diff, type])
91
+ item.set_text(line.gsub("\t", " "))
92
+ item.set_text_colour(color) if color
93
+ item.set_background_colour(bgcolor) if bgcolor
94
+
95
+ @list.insert_item(item)
88
96
  end
89
97
  end
90
98
 
99
+ @list.set_column_width(0, -1)
100
+ @list.set_column_width(0, [@list.get_column_width(0), self.size.width].max)
101
+
91
102
  @list.show
92
103
  @list.set_focus
93
104
  @box.layout
@@ -107,15 +118,11 @@ module Fugit
107
118
  end
108
119
 
109
120
  def on_item_menu_request(event)
110
- i = event.get_item
111
- @menu_data = nil
112
- unless @root == i
113
- @menu_data = @list.get_item_data(i)
114
- @list_menu.set_label(@menu_stage_chunk.get_id, (@menu_data[2] == :staged ? "Unstage chunk" : "Stage chunk"))
115
- @list_menu.set_label(@menu_stage_line.get_id, (@menu_data[2] == :staged ? "Unstage line" : "Stage line"))
116
- @menu_stage_line.enable(!@menu_data[1].empty?)
117
- @list.popup_menu(@list_menu)
118
- end
121
+ @menu_data = event.get_item.get_data
122
+ @list_menu.set_label(@menu_stage_chunk.get_id, (@menu_data[2] == :staged ? "Unstage chunk" : "Stage chunk"))
123
+ @list_menu.set_label(@menu_stage_line.get_id, (@menu_data[2] == :staged ? "Unstage line" : "Stage line"))
124
+ @menu_stage_line.enable(!@menu_data[1].empty?)
125
+ @list.popup_menu(@list_menu)
119
126
  end
120
127
 
121
128
  def on_menu_stage_chunk(event)
@@ -127,11 +134,8 @@ module Fugit
127
134
  end
128
135
 
129
136
  def on_double_click(event)
130
- i = event.get_item
131
- unless @root == i
132
- menu_data = @list.get_item_data(i)
133
- apply_diff(menu_data[1], menu_data[2]) if menu_data
134
- end
137
+ menu_data = event.get_item.get_data
138
+ apply_diff(menu_data[1], menu_data[2]) if menu_data
135
139
  end
136
140
 
137
141
  def apply_diff(diff, type)
@@ -139,8 +143,12 @@ module Fugit
139
143
  reverse = (type == :staged ? "--reverse" : "")
140
144
  diff_file = File.join(Dir.pwd, ".git", "fugit_partial.diff")
141
145
  File.open(diff_file, "wb") {|f| f << diff} # Write out in binary mode to preserve newlines, otherwise git freaks out
142
- `git apply --cached #{reverse} .git/fugit_partial.diff`
143
- send_message(:index_changed)
146
+ err = `git apply --cached #{reverse} .git/fugit_partial.diff 2>&1`
147
+ if err.empty?
148
+ send_message(:index_changed)
149
+ else
150
+ MessageDialog.new(self, err, "Error applying diff", OK|ICON_ERROR).show_modal
151
+ end
144
152
  end
145
153
  end
146
154
  end
@@ -2,7 +2,9 @@
2
2
 
3
3
  module Fugit
4
4
  module GraphRenderer
5
- def graphify(commits)
5
+ def graphify(commits, branch_refs)
6
+ branch_refs = branch_refs.map {|b| b[1]}.uniq
7
+
6
8
  graph = []
7
9
  branch_parents = [commits.first[0]]
8
10
  commits.each do |sha, parents, comment|
@@ -14,7 +16,8 @@ module Fugit
14
16
  indicator = "│"
15
17
  indicator = " " if parent.empty?
16
18
  indicator = "┼" if parent_found && children.size > 1 && !parent.empty?
17
- indicator = "" if sha == parent && !parent_found
19
+ indicator = "" if sha == parent && !parent_found
20
+ indicator = "□" if sha == "uncomitted"
18
21
  indicator = "┘" if sha == parent && parent_found
19
22
  children.shift if sha == parent && parent_found
20
23
  parent_found = true if sha == parent
@@ -35,7 +38,7 @@ module Fugit
35
38
  branch_parents << p
36
39
  branches << "┐" if sha_index
37
40
  end
38
- branches << "" unless sha_index
41
+ branches << "" unless sha_index
39
42
  if parents.empty?
40
43
  found = false
41
44
  branchoffs = branches.select{|b| b == "┘"}
@@ -49,7 +52,7 @@ module Fugit
49
52
  else
50
53
  b
51
54
  end
52
- found ||= b == ""
55
+ found ||= b == ""
53
56
  val
54
57
  end
55
58
  branches.reverse!
@@ -73,7 +76,7 @@ module Fugit
73
76
  else
74
77
  b
75
78
  end
76
- found = b == "" if !found
79
+ found = b == "" if !found
77
80
  val
78
81
  end
79
82
  end
@@ -86,6 +89,7 @@ module Fugit
86
89
  match
87
90
  end
88
91
  branch_parents.reverse!
92
+ branches.map! {|b| b == "○" ? "●" : b} if branch_refs.include?(sha)
89
93
  graph << [branches.join, comment, sha]
90
94
  end
91
95
  graph
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
  include Wx
3
+ include IconLoader
3
4
  include Fugit::GraphRenderer
4
5
 
5
6
  module Fugit
@@ -10,45 +11,172 @@ module Fugit
10
11
 
11
12
  @list = ListCtrl.new(self, ID_ANY, :style => LC_REPORT|LC_VRULES|NO_BORDER)
12
13
 
14
+ @list_menu = Menu.new
15
+ @menu_create_branch = MenuItem.new(@list_menu, ID_ANY, 'Create new branch here')
16
+ @menu_create_branch.set_bitmap(get_icon("arrow_divide.png"))
17
+ @list_menu.append_item(@menu_create_branch)
18
+ evt_menu(@menu_create_branch, :on_menu_create_branch)
19
+
20
+ @menu_cherry_pick = MenuItem.new(@list_menu, ID_ANY, 'Cherry-pick this commit')
21
+ @menu_cherry_pick.set_bitmap(get_icon("cherry.png"))
22
+ @list_menu.append_item(@menu_cherry_pick)
23
+ evt_menu(@menu_cherry_pick, :on_menu_cherry_pick)
24
+
25
+ @list_menu.append_separator
26
+
27
+ @menu_soft_reset = @list_menu.append('Soft-reset branch to here')
28
+ evt_menu(@menu_soft_reset, :on_menu_soft_reset)
29
+
30
+ @menu_mixed_reset = @list_menu.append('Mixed-reset branch to here')
31
+ evt_menu(@menu_mixed_reset, :on_menu_mixed_reset)
32
+
33
+ @menu_hard_reset = MenuItem.new(@list_menu, ID_ANY, 'Hard-reset branch to here')
34
+ @menu_hard_reset.set_bitmap(get_icon("arrow_undo.png"))
35
+ @list_menu.append_item(@menu_hard_reset)
36
+ evt_menu(@menu_hard_reset, :on_menu_hard_reset)
37
+
13
38
  @box = BoxSizer.new(VERTICAL)
14
39
  @box.add(@list, 1, EXPAND)
15
40
  self.set_sizer(@box)
16
41
 
17
- register_for_message(:history_tab_shown) do
18
- update_list unless @has_initialized
19
- @list.set_focus
20
- end
42
+ evt_list_item_right_click(@list.get_id, :on_list_menu_request)
43
+
44
+ register_for_message(:history_tab_shown, :update_list)
45
+ register_for_message(:tab_switch, :update_list)
46
+ register_for_message(:refresh, :update_list)
21
47
  register_for_message(:exiting) {self.hide} # Things seem to run smoother if we hide before destruction
22
48
  end
23
49
 
24
50
  def update_list
51
+ return unless is_shown_on_screen
25
52
  @list.hide
26
53
  @list.clear_all
27
54
 
28
- @list.insert_column(0, "Graph")
29
- @list.insert_column(1, "SHA1")
30
- @list.insert_column(2, "Commit note")
55
+ @list.insert_column(0, "")
56
+ @list.insert_column(1, "Branches")
57
+ @list.insert_column(2, "SHA1")
58
+ @list.insert_column(3, "Commit note")
31
59
 
32
60
  mono_font = Font.new(8, FONTFAMILY_TELETYPE, FONTSTYLE_NORMAL, FONTWEIGHT_NORMAL)
33
61
 
62
+ branches = `git branch -v -a --no-abbrev`
63
+ branches = branches.split("\n").map {|b| [b[2..-1].split(" ")[0..1], b[0..0] == "*"].flatten}
64
+
34
65
  output = `git log --pretty=format:"%H\t%P\t%s" --date-order --all`
35
66
  lines = output.split("\n").map! {|line| line.split("\t")}
36
- log = graphify(lines)
67
+ current_sha = branches.reject {|b| !b.last}.first[1]
68
+ lines.insert(0, ["uncomitted", current_sha, "<Uncomitted changes>"]) if has_uncomitted_changes?
69
+ log = graphify(lines, branches)
37
70
 
38
71
  log.each_index do |i|
39
72
  (graph, comment, sha) = log[i]
73
+ comment_branches = branches.reject {|b| b[1] != sha}.map {|b| (b.last ? "*" : "") + b.first}
40
74
  @list.insert_item(i, sha)
41
75
  @list.set_item(i, 0, graph)
42
- @list.set_item(i, 1, sha[0..7])
43
- @list.set_item(i, 2, (comment.nil? || comment.empty?) ? "<No comment>" : comment)
76
+ @list.set_item(i, 1, comment_branches.join(" "))
77
+ @list.set_item(i, 2, sha == "uncomitted" ? "" : sha[0..7])
78
+ @list.set_item(i, 3, (comment.nil? || comment.empty?) ? "<No comment>" : comment)
79
+ @list.set_item_data(i, sha)
80
+ @list.set_item_background_colour(i, Colour.new(255, 220, 220)) if sha == "uncomitted"
81
+ @list.set_item_background_colour(i, Colour.new(220, 220, 225)) if comment_branches.join =~ /\*/
44
82
  end
45
83
 
46
84
  @list.set_column_width(0, -1)
85
+ @list.set_column_width(0, [150, @list.get_column_width(0)].min)
47
86
  @list.set_column_width(1, -1)
48
- @list.set_column_width(2, -2)
87
+ @list.set_column_width(1, [150, @list.get_column_width(1)].min)
88
+ @list.set_column_width(2, -1)
89
+ @list.set_column_width(3, -1)
49
90
 
50
91
  @list.show
51
- @has_initialized = true
92
+ @list.set_focus
93
+ end
94
+
95
+ def on_list_menu_request(event)
96
+ @menu_data = event.get_item.get_data
97
+ @list.popup_menu(@list_menu)
98
+ end
99
+
100
+ def on_menu_create_branch(event)
101
+ @new_branch_dialog ||= TextEntryDialog.new(self, "New branch name:", "Create branch")
102
+ @new_branch_dialog.set_value("")
103
+ if @new_branch_dialog.show_modal == ID_OK
104
+ err = `git branch #{@new_branch_dialog.get_value} #{@menu_data} 2>&1`
105
+ if err.empty?
106
+ send_message(:refresh)
107
+ else
108
+ MessageDialog.new(self, err, "Error creating branch", OK|ICON_ERROR).show_modal
109
+ end
110
+ end
111
+ end
112
+
113
+ def on_menu_cherry_pick(event)
114
+ err = `git cherry-pick #{@menu_data} 2>&1`
115
+ if err =~ /Automatic cherry-pick failed/
116
+ MessageDialog.new(self, err, "Error cherry-picking", OK|ICON_ERROR).show_modal
117
+ else
118
+ send_message(:refresh)
119
+ end
120
+ end
121
+
122
+ def on_menu_soft_reset(event)
123
+ err = `git reset --soft #{@menu_data} 2>&1`
124
+ if !err.empty?
125
+ MessageDialog.new(self, err, "Error resetting", OK|ICON_ERROR).show_modal
126
+ else
127
+ send_message(:refresh)
128
+ end
129
+ end
130
+
131
+ def on_menu_mixed_reset(event)
132
+ err = `git reset --mixed #{@menu_data} 2>&1`
133
+ #~ if !(err =~ /HEAD is now at/)
134
+ #~ MessageDialog.new(self, err, "Error resetting", OK|ICON_ERROR).show_modal
135
+ #~ else
136
+ send_message(:refresh)
137
+ #~ end
138
+ end
139
+
140
+ def on_menu_hard_reset(event)
141
+ if has_uncomitted_changes?
142
+ @uncomitted_hard_dialog ||= MessageDialog.new(self, "Uncommitted changes will be lost, continue?", "Uncomitted changes", YES_NO|NO_DEFAULT|ICON_EXCLAMATION)
143
+ return if @uncomitted_hard_dialog.show_modal != ID_YES
144
+ end
145
+
146
+ err = `git reset --hard #{@menu_data} 2>&1`
147
+ if !(err =~ /HEAD is now at/)
148
+ MessageDialog.new(self, err, "Error resetting", OK|ICON_ERROR).show_modal
149
+ else
150
+ send_message(:refresh)
151
+ end
152
+ end
153
+
154
+ def has_uncomitted_changes?
155
+ deleted = `git ls-files --deleted`
156
+ modified = `git ls-files --modified`
157
+ staged = `git ls-files --stage`
158
+ last_commit = `git ls-tree -r HEAD`
159
+
160
+ committed = {}
161
+ last_commit.split("\n").map do |line|
162
+ (info, file) = line.split("\t")
163
+ sha = info.match(/[a-f0-9]{40}/)[0]
164
+ committed[file] = sha
165
+ end
166
+
167
+ deleted = deleted.split("\n")
168
+ modified = modified.split("\n")
169
+ staged = staged.split("\n").map do |line|
170
+ (info, file) = line.split("\t")
171
+ sha = info.match(/[a-f0-9]{40}/)[0]
172
+ [file, sha]
173
+ end
174
+ committed.each_pair do |file, sha|
175
+ staged << [file, ""] unless staged.assoc(file)
176
+ end
177
+ staged.reject! {|file, sha| committed[file] == sha}
178
+
179
+ return !(deleted + modified + staged).empty?
52
180
  end
53
181
 
54
182
  end
@@ -2,7 +2,7 @@
2
2
  module IconLoader
3
3
  IconBasePath = File.expand_path(File.join(File.dirname(__FILE__), "..", "icons"))
4
4
 
5
- def get_icon(name, type = "png")
5
+ def get_icon(name)
6
6
  icon = File.join(IconBasePath, name)
7
7
  case name[-3..-1].downcase
8
8
  when "png"
@@ -6,7 +6,7 @@ module Fugit
6
6
  def initialize(parent)
7
7
  super(parent, ID_ANY)
8
8
 
9
- @index = TreeCtrl.new(self, ID_ANY, nil, nil, NO_BORDER|TR_MULTIPLE|TR_HIDE_ROOT|TR_FULL_ROW_HIGHLIGHT|TR_NO_LINES)
9
+ @index = TreeCtrl.new(self, ID_ANY, nil, nil, TR_MULTIPLE|TR_HIDE_ROOT|TR_FULL_ROW_HIGHLIGHT|TR_NO_LINES)
10
10
 
11
11
  imagelist = ImageList.new(16, 16)
12
12
  imagelist << get_icon("asterisk_yellow.png")
@@ -23,15 +23,6 @@ module Fugit
23
23
  @index.set_item_bold(@unstaged)
24
24
  @index.set_item_bold(@staged)
25
25
 
26
- @toolbar = ToolBar.new(self, ID_ANY, nil, nil, TB_HORIZONTAL|NO_BORDER|TB_NODIVIDER)
27
- @toolbar.set_tool_bitmap_size(Size.new(16,16))
28
- stage_all_button = @toolbar.add_tool(ID_ANY, "Stage all", get_icon("folder_add.png"), "Stage all")
29
- stage_button = @toolbar.add_tool(ID_ANY, "Stage", get_icon("page_add.png"), "Stage file")
30
- @toolbar.add_separator
31
- unstage_button = @toolbar.add_tool(ID_ANY, "Unstage", get_icon("page_delete.png"), "Unstage file")
32
- unstage_all_button = @toolbar.add_tool(ID_ANY, "Unstage all", get_icon("folder_delete.png"), "Unstage all")
33
- @toolbar.realize
34
-
35
26
  @unstaged_menu = Menu.new
36
27
  @menu_stage_file = @unstaged_menu.append('Stage file')
37
28
  @menu_revert_changes = @unstaged_menu.append('Revert changes')
@@ -43,19 +34,16 @@ module Fugit
43
34
  evt_tree_item_menu(@index.get_id, :on_menu_request)
44
35
 
45
36
  box = BoxSizer.new(VERTICAL)
46
- box.add(@toolbar, 0, EXPAND)
47
37
  box.add(@index, 1, EXPAND)
48
38
  self.set_sizer(box)
49
39
 
50
40
  evt_tree_sel_changed(@index.get_id, :on_click)
51
41
  evt_tree_item_activated(@index.get_id, :on_double_click)
52
42
 
53
- evt_tool(stage_all_button, :on_stage_all_clicked)
54
- evt_tool(unstage_all_button, :on_unstage_all_clicked)
55
-
56
43
  evt_tree_item_collapsing(@index.get_id) {|event| event.veto}
57
44
 
58
- register_for_message(:refresh, :update_tree)
45
+ register_for_message(:refresh) {update_tree if is_shown_on_screen}
46
+ register_for_message(:branch_checkout) {update_tree if is_shown_on_screen}
59
47
  register_for_message(:commit_saved, :update_tree)
60
48
  register_for_message(:index_changed, :update_tree)
61
49
  register_for_message(:exiting) {self.hide} # Things seem to run smoother if we hide before destruction
@@ -145,21 +133,6 @@ module Fugit
145
133
  end
146
134
  end
147
135
 
148
- def on_stage_all_clicked(event)
149
- children = @index.get_children(@unstaged).map {|child| @index.get_item_data(child)}
150
- to_delete = children.reject {|file, change, status| change != :deleted}.map {|f,c,s| f}
151
- to_add = children.map {|f,c,s| f} - to_delete
152
- `git rm --cached "#{to_delete.join('" "')}"` unless to_delete.empty?
153
- `git add "#{to_add.join('" "')}"` unless to_add.empty?
154
- send_message(:index_changed)
155
- end
156
-
157
- def on_unstage_all_clicked(event)
158
- children = @index.get_children(@staged).map {|child| @index.get_item_data(child)[0]}
159
- `git reset "#{children.join('" "')}"` unless children.empty?
160
- send_message(:index_changed)
161
- end
162
-
163
136
  def set_diff(file, change, status)
164
137
  case status
165
138
  when :unstaged
@@ -212,12 +185,12 @@ module Fugit
212
185
  when :unstaged
213
186
  case change
214
187
  when :deleted
215
- `git rm --cached "#{file}"`
188
+ `git rm --cached "#{file}" 2>&1`
216
189
  else
217
- `git add "#{file}"`
190
+ `git add "#{file}" 2>&1`
218
191
  end
219
192
  when :staged
220
- `git reset "#{file}"`
193
+ `git reset "#{file}" 2>&1`
221
194
  end
222
195
 
223
196
  send_message(:index_changed)
@@ -24,7 +24,7 @@ module Fugit
24
24
  icon_file = File.expand_path(File.join(IconBasePath, "plus_minus.gif"))
25
25
  self.icon = Icon.new(icon_file, BITMAP_TYPE_GIF)
26
26
 
27
- evt_notebook_page_changed(@notebook) {|event| send_message(:history_tab_shown) if event.get_selection == 1}
27
+ evt_notebook_page_changed(@notebook) {|event| send_message(:tab_switch)}
28
28
 
29
29
  menu_bar = MenuBar.new
30
30
 
@@ -32,6 +32,7 @@ module Fugit
32
32
  menu_file = Menu.new
33
33
  # Using ID_EXIT standard id means the menu item will be given the right label for the platform and language, and placed in the correct platform-specific menu - eg on OS X, in the Application's menu
34
34
  menu_file.append(ID_SAVE, "&Save commit\tCtrl-S", "Save commit")
35
+ push = menu_file.append(ID_ANY, "&Push\tCtrl-P", "Push commits to a remote repo")
35
36
  refresh = menu_file.append(ID_ANY, "&Refresh\tF5", "Refresh the index list")
36
37
  menu_file.append(ID_EXIT, "E&xit", "Quit this program")
37
38
  menu_bar.append(menu_file, "&File")
@@ -45,6 +46,7 @@ module Fugit
45
46
  self.menu_bar = menu_bar
46
47
 
47
48
  evt_menu(ID_SAVE) {|event| send_message(:save_clicked)}
49
+ evt_menu(push) {|event| send_message(:push_clicked)}
48
50
  evt_menu(refresh) {|event| send_message(:refresh)}
49
51
  evt_menu(ID_EXIT) {|event| close} # End the application; it should finish automatically when the last window is closed.
50
52
  evt_menu(ID_ABOUT) do |event|
@@ -3,35 +3,30 @@ include Wx
3
3
  module Fugit
4
4
  class PushDialog < Dialog
5
5
  def initialize(parent)
6
- super(parent, ID_ANY, "Push branches", :size => Size.new(350, 500))
6
+ super(parent, ID_ANY, "Push branches", :size => Size.new(400, 500))
7
7
 
8
8
  @branch_list = CheckListBox.new(self, ID_ANY)
9
+ @tag_check = CheckBox.new(self, ID_ANY)
10
+ @tag_check.set_label("Include &tags")
11
+ @force_check = CheckBox.new(self, ID_ANY)
12
+ @force_check.set_label("&Force update")
13
+ @remote = ComboBox.new(self, ID_ANY)
9
14
  @log = TextCtrl.new(self, ID_ANY, :size => Size.new(20, 150), :style => TE_MULTILINE|TE_DONTWRAP|TE_READONLY)
10
15
  @progress = Gauge.new(self, ID_ANY, 100, :size => Size.new(20, 20))
11
16
 
12
- @remote = ComboBox.new(self, ID_ANY)
13
-
14
- check_panel = Panel.new(self, ID_ANY)
15
- @tag_check = CheckBox.new(check_panel, ID_ANY)
16
- @force_check = CheckBox.new(check_panel, ID_ANY)
17
-
18
- flex = FlexGridSizer.new(1,2,4,4)
19
- flex.add(@tag_check, 0, EXPAND)
20
- flex.add(StaticText.new(check_panel, ID_ANY, "Include tags"), 0, EXPAND)
21
- flex.add(@force_check, 0, EXPAND)
22
- flex.add(StaticText.new(check_panel, ID_ANY, "Force update"), 0, EXPAND)
23
-
24
- check_panel.set_sizer(flex)
25
-
26
17
  butt_sizer = create_button_sizer(OK|CANCEL)
27
18
  butt_sizer.get_children.map {|s| s.get_window}.compact.each {|b| b.set_label(b.get_label == "OK" ? "Push" : "Close")}
28
19
  evt_button(get_affirmative_id, :on_ok)
29
20
 
30
21
  box = BoxSizer.new(VERTICAL)
31
22
  box2 = BoxSizer.new(HORIZONTAL)
23
+ box3 = BoxSizer.new(VERTICAL)
24
+
25
+ box3.add(@tag_check, 1)
26
+ box3.add(@force_check, 1, TOP, 4)
32
27
 
33
28
  box2.add(@branch_list, 1, EXPAND|LEFT|RIGHT|BOTTOM, 4)
34
- box2.add(check_panel, 1)
29
+ box2.add(box3, 1, ALL, 4)
35
30
 
36
31
  box.add(StaticText.new(self, ID_ANY, "Select branches:"), 0, EXPAND|ALL, 4)
37
32
  box.add(box2, 1, EXPAND)
@@ -49,8 +44,9 @@ module Fugit
49
44
  branches = `git branch`
50
45
  remotes = `git remote`
51
46
  @remote.clear
52
- remotes.split("\n").each {|r| @remote.append(r)}
53
- @remote.set_value(@remote.get_string(0))
47
+ remotes = remotes.split("\n")
48
+ remotes.each {|r| @remote.append(r)}
49
+ @remote.set_value(remotes.include?("origin") ? "origin" : remotes[0])
54
50
  current = branches.match(/\* (.+)/).to_a.last
55
51
  branches = branches.split("\n").map {|b| b.split(" ").last}
56
52
  @branch_list.set(branches)
Binary file
Binary file
Binary file
Binary file
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tekkub-fugit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tekkub
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-18 00:00:00 -08:00
12
+ date: 2009-02-27 00:00:00 -08:00
13
13
  default_executable: fugit
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -36,9 +36,11 @@ files:
36
36
  - VERSION.yml
37
37
  - bin/fugit
38
38
  - lib/fugit
39
- - lib/fugit/commit.rb
39
+ - lib/fugit/commit_dialog.rb
40
40
  - lib/fugit/commit_tab.rb
41
+ - lib/fugit/commit_tab_toolbar.rb
41
42
  - lib/fugit/console.rb
43
+ - lib/fugit/delete_branch_dialog.rb
42
44
  - lib/fugit/diff.rb
43
45
  - lib/fugit/graph_renderer.rb
44
46
  - lib/fugit/history_list.rb
@@ -52,7 +54,12 @@ files:
52
54
  - lib/fugit/SciTE.properties
53
55
  - lib/fugit.rb
54
56
  - lib/icons
57
+ - lib/icons/arrow_divide.png
58
+ - lib/icons/arrow_divide_delete.png
59
+ - lib/icons/arrow_join.png
60
+ - lib/icons/arrow_undo.png
55
61
  - lib/icons/asterisk_yellow.png
62
+ - lib/icons/cherry.png
56
63
  - lib/icons/disk.png
57
64
  - lib/icons/folder_add.png
58
65
  - lib/icons/folder_delete.png