public
Description: The Git TextMate Bundle
Homepage: http://tim.theenchanter.com/
Clone URL: git://github.com/timcharper/git-tmbundle.git
git-tmbundle / Support / spec / controllers / branch_controller_spec.rb
100644 248 lines (207 sloc) 11.44 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
require File.dirname(__FILE__) + '/../spec_helper'
 
describe "deleting branches locally warns and allows you to cancel", :shared => true do
  it "Ask you if you'd like to delete the branch, and then delete it with -D if yes" do
    @set_branch_to_choose.call("task")
    TextMate::UI.should_receive(:alert).with(:informational, "Deleted branch", "Deleted branch task.", "OK")
    TextMate::UI.should_receive(:alert).with(*@really_delete_params).and_return("Yes")
    dispatch(:controller => "branch", :action => "delete")
  end
 
  it "allow you to cancel" do
    @set_branch_to_choose.call("task")
    TextMate::UI.should_receive(:alert).with(*@really_delete_params).and_return("No")
    dispatch(:controller => "branch", :action => "delete")
  end
end
 
 
describe "deleting branches remotely recognizes success and failure responses", :shared => true do
  it "should delete a remote branch via a push" do
    @set_branch_to_choose.call("origin/task")
    Git.command_response["push", "origin", ":task"] = @success_delete_response
 
    TextMate::UI.should_receive(:alert).with(:informational, "Success", "Deleted remote branch origin/task.", "OK").and_return("No")
    dispatch(:controller => "branch", :action => "delete")
 
    Git.commands_ran.should include(["push", "origin", ":task"])
  end
  
  it "should show a message when server reports branch doesn't exist" do
    @set_branch_to_choose.call("origin/task")
    Git.command_response["push", "origin", ":task"] = @failure_delete_response
    TextMate::UI.should_receive(:alert).with(:warning, "Delete branch failed!", "The source 'origin' reported that the branch 'task' does not exist.\nTry running the prune remote stale branches command?", "OK")
    dispatch(:controller => "branch", :action => "delete")
  end
end
 
describe BranchController do
  before(:each) do
    Git.reset_mock!
    Git.command_response["branch", "-r"] = " origin/master\n origin/release\n origin/task"
    Git.command_response["branch"] = "* master\n task"
  end
  
  include SpecHelpers
  
  describe "when switching branches" do
    before(:each) do
      @set_branch_to_choose = lambda { |response|
        TextMate::UI.should_receive(:request_item).with({:prompt=>"Current branch is 'master'.\nSelect a new branch to switch to:", :items=>["master", "task", "origin/master", "origin/release", "origin/task"], :title=>"Switch to Branch", :force_pick => true}).and_return(response)
      }
    end
    
    describe "when switching to a local branch" do
      it "should switch to a local branch" do
        @set_branch_to_choose.call("task")
        Git.command_response["checkout", "task"] = %{Switched to branch "task"\n}
        output = capture_output do
          dispatch(:controller => "branch", :action => "switch")
        end
        
        output.should include(%{Switched to branch "task"})
      end
      
      it "should alert you if the switch isn't possible because you're in the middle of a merge" do
        @set_branch_to_choose.call("task")
        Git.command_response["checkout", "task"] = %{fatal: you need to resolve your current index first\n}
        TextMate::UI.should_receive(:alert).with(:warning, "Error - couldn't switch", "Git said:\nfatal: you need to resolve your current index first\n\nYou're probably in the middle of a conflicted merge, and need to commit", "OK").and_return("Yes")
        dispatch(:controller => "branch", :action => "switch")
      end
      
      it "should ask you if you'd like to force when uncommitted files exist" do
        @set_branch_to_choose.call("task")
        Git.command_response["checkout", "task"] = %{error: Entry 'branch_spec.rb' not uptodate. Cannot merge.\n}
        TextMate::UI.should_receive(:alert).with(:informational, "Conflicts may happen if you switch", "There are uncommitted changes that might cause conflicts by this switch (branch_spec.rb).\nSwitch anyways?", "No", "Yes").and_return("Yes")
        
        Git.command_response["checkout", "-m", "task"] = <<-EOF
Auto-merged Support/spec/lib/commands/branch_spec.rb
CONFLICT (content): Merge conflict in Support/spec/lib/commands/branch_spec.rb
M  Support/spec/lib/commands/branch_spec.rb
EOF
        output = capture_output do
          dispatch(:controller => "branch", :action => "switch")
        end
        
        output.should include("CONFLICT (content): Merge conflict in Support/spec/lib/commands/branch_spec.rb")
      end
      
      describe "when you have submodules" do
        it "should call submodules.init_and_update" do
          @set_branch_to_choose.call("task")
          
          git = Git.singleton_new
          git.submodule.should_receive(:list).and_return([{:name => "mod"}])
          git.submodule.should_receive(:init_and_update)
          output = capture_output do
            dispatch(:controller => "branch", :action => "switch")
          end
        end
      end
    end
    
    describe "when switching to a remote branch" do
      before(:each) do
        @get_branch_name_params = {:title=>"Switch to remote branch", :prompt=>"You must set up a local tracking branch to work on 'origin/release'.\nWhat would you like to name the local tracking branch?", :default=>"release"}
      end
      
      it "should switch to a remote branch" do
        @set_branch_to_choose.call("origin/release")
        TextMate::UI.should_receive(:request_string).with(@get_branch_name_params).and_return("release")
        Git.command_response["branch", "--track", "release", "origin/release"] = %{Branch release set up to track remote branch refs/remotes/origin/release.\n}
        Git.command_response["checkout", "release"] = %{Switched to branch "release"\n}
        output = capture_output do
          dispatch(:controller => "branch", :action => "switch")
        end
        
        output.should include(%{Branch release set up to track remote branch refs/remotes/origin/release.})
        output.should include(%{Switched to branch "release"})
      end
      
      it "should not allow you to create a branch with an existing local name" do
        @set_branch_to_choose.call("origin/release")
        TextMate::UI.should_receive(:request_string).once.with(@get_branch_name_params).and_return("task")
        TextMate::UI.should_receive(:alert).with(:warning, "Branch name already taken!", "The branch name 'task' is already in use.\nVery likely this is the branch you want to work on.\nIf not, pick another name.", "Pick another name", "Switch to it", "Cancel").and_return("Cancel")
        dispatch(:controller => "branch", :action => "switch")
      end
    end
    
    describe "when merging" do
      before(:each) do
        @git = Git.singleton_new
        @controller = BranchController.singleton_new
        @git.branch.stub!(:current_name).and_return("master")
        @git.branch.stub!(:list_names).and_return(["master", "release", "old_skool"])
        
        TextMate::UI.should_receive(:request_item).with(:title => "Merge", :prompt => "Merge which branch into 'master':", :items => ["release", "old_skool"], :force_pick => true).and_return("release")
        @git.should_receive(:merge).with("release").and_return({:text => "Success!", :conflicts => [] })
      end
      
      it "should merge a branch" do
        output = capture_output do
          dispatch(:controller => "branch", :action => "merge")
        end
        
        output.should include("Success!")
      end
      
      it "should update_submodules_si_hay" do
        @controller.should_receive(:update_submodules_si_hay)
        capture_output { dispatch(:controller => "branch", :action => "merge") }
      end
    end
  end
  
  describe "when deleting branches" do
    before(:each) do
      @set_branch_to_choose = lambda { |response|
        TextMate::UI.should_receive(:request_item).with(:title => "Delete Branch", :prompt => "Select the branch to delete:", :items => ["master", "task", "origin/master", "origin/release", "origin/task"]).and_return(response)
      }
    end
    describe "locally" do
      describe "when branch is not fully merged" do
        before(:each) do
          Git.command_response["branch", "-d", "task"] = "error: branch 'task' is not a strict subset of HEAD\nnot going to allow you to delete it!"
          Git.command_response["branch", "-D", "task"] = "Deleted branch task."
          @really_delete_params = [:warning, "Warning", "Branch 'task' is not an ancestor of your current HEAD (it has unmerged changes)\nReally delete it?", 'Yes', 'No']
        end
        
        describe "Git 1.5.3.4" do
          before(:each) do
            Git.command_response["branch", "-d", "task"] = "error: branch 'task' is not a strict subset of HEAD\nnot going to allow you to delete it!"
          end
          
          it_should_behave_like "deleting branches locally warns and allows you to cancel"
        end
        
        describe "Git 1.5.3.4" do
          before(:each) do
            Git.command_response["branch", "-d", "task"] = "error: The branch 'fixtures' is not an ancestor of your current HEAD.\nIf you are sure you want to delete it, run 'git branch -D fixtures'."
          end
          
          it_should_behave_like "deleting branches locally warns and allows you to cancel"
        end
      end
    
      describe "when branch is fully merged" do
        before(:each) do
          Git.command_response["branch", "-d", "task"] = "Deleted branch task."
        end
      
        it "should delete" do
          @set_branch_to_choose.call("task")
          TextMate::UI.should_receive(:alert).with(:informational, "Success", "Deleted branch task.", "OK").and_return("No")
          dispatch(:controller => "branch", :action => "delete")
        end
      end
    end
    
    describe "creating a branch" do
      it "should run" do
        TextMate::UI.should_receive(:request_string).with(:title => "Create Branch", :prompt => "Enter the name of the new branch:").and_return("task")
        Git.command_response["checkout", "-b", "task"] = %{Switched to a new branch "tt"\n}
        output = capture_output do
          dispatch(:controller => "branch", :action => "create")
        end
        output.should == %{Switched to a new branch "tt"\n}
      end
    end
    
    describe "remotely" do
      describe "git 1.5.3" do
        before(:each) do
          @success_delete_response = <<EOF
deleting 'refs/heads/task'
Also local refs/remotes/origin/task
refs/heads/task: d8b368361ebdf2c51b78f7cfdae5c3044b23d189 -> deleted
Everything up-to-date
EOF
          @failure_delete_response = <<EOF
error: dst refspec origin does not match any existing ref on the remote and does not start with refs/.
fatal: The remote end hung up unexpectedly
error: failed to push to '../origin/'
EOF
        end
        
        it_should_behave_like "deleting branches remotely recognizes success and failure responses"
      end
      
      describe "git 1.5.4.3" do
        before(:each) do
          @success_delete_response = <<EOF
To ../origin/
- [deleted] boogy
EOF
          @failure_delete_response = <<EOF
error: dst refspec task does not match any existing ref on the remote and does not start with refs/.
fatal: The remote end hung up unexpectedly
error: failed to push some refs to '../origin/'
EOF
        end
        
        it_should_behave_like "deleting branches remotely recognizes success and failure responses"
      end
    end
  end
end