git-process 0.9.5 → 0.9.6
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/README.md +52 -35
- data/bin/git-sync +5 -0
- data/lib/git-process/changed_file_helper.rb +106 -0
- data/lib/git-process/git_branch.rb +1 -1
- data/lib/git-process/git_branches.rb +1 -1
- data/lib/git-process/git_lib.rb +36 -6
- data/lib/git-process/git_process.rb +76 -11
- data/lib/git-process/git_status.rb +32 -12
- data/lib/git-process/new_fb.rb +1 -1
- data/lib/git-process/rebase_to_master.rb +12 -5
- data/lib/git-process/sync.rb +19 -4
- data/lib/git-process/version.rb +1 -1
- data/spec/changed_file_helper_spec.rb +143 -0
- data/spec/git_process_spec.rb +171 -0
- data/spec/git_status_spec.rb +23 -2
- data/spec/new_fb_spec.rb +4 -4
- data/spec/rebase_to_master_spec.rb +58 -72
- data/spec/sync_spec.rb +41 -42
- metadata +6 -3
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,29 +1,25 @@
|
|
1
1
|
[![Build Status](https://secure.travis-ci.org/jdigger/git-process.png)](http://travis-ci.org/jdigger/git-process)
|
2
2
|
|
3
|
-
# Purpose
|
3
|
+
# Purpose #
|
4
|
+
|
5
|
+
This provides an easy way to work with a sane git workflow process that encourages using highly-focussed branches to encourage collaboration, enable fearless changes, and improve team communication.
|
6
|
+
|
7
|
+
See the F.A.Q. for a much more complete explanation for the thoughts and assumptions that motivates this project.
|
4
8
|
|
5
|
-
This provides an easy way to work with a sane git workflow process.
|
6
|
-
Short-lived feature branches can work quite well, but the formal git-flow process can be rather heavy in cost.
|
7
9
|
|
8
10
|
# Installation #
|
9
11
|
|
10
12
|
$ sudo gem install git-process
|
11
13
|
|
12
|
-
## Configurables ##
|
13
|
-
See notes for more details
|
14
|
-
|
15
|
-
* OAuth2 Token
|
16
|
-
* The name of the integration branch (defaults to `origin/master`, but can be set to `develop` or other)
|
17
14
|
|
18
|
-
---
|
19
15
|
# Overview #
|
20
16
|
|
21
17
|
## Anticipated Use Cases ##
|
22
18
|
|
23
|
-
1. User
|
24
|
-
1. User pushes local branch to remote (as feature branch) by rebasing integration branch, then pushing branch to remote
|
25
|
-
1. User closes local branch by rebasing integration branch first, then pushing local to integration
|
26
|
-
1. User initiates GitHub "pull request"
|
19
|
+
1. User creates new local branch for focussed work.
|
20
|
+
1. User pushes local branch to remote (as feature branch) by merging/rebasing with the integration branch, then pushing to the branch to remote.
|
21
|
+
1. User closes local branch by rebasing integration branch first, then pushing local to integration.
|
22
|
+
1. User initiates GitHub "pull request" to ease collaboration.
|
27
23
|
|
28
24
|
## Command List ##
|
29
25
|
|
@@ -34,6 +30,7 @@ See notes for more details
|
|
34
30
|
|
35
31
|
**All commands are well documented within themselves: Use the "-h" switch to see the full documentation.** (e.g., "`git sync -h`")
|
36
32
|
|
33
|
+
|
37
34
|
# Workflow #
|
38
35
|
|
39
36
|
_The following assumes that the integration branch is "origin/master"._
|
@@ -53,6 +50,17 @@ _The following assumes that the integration branch is "origin/master"._
|
|
53
50
|
5. If you still need to make changes, do so and use "`git sync`" to keep your branch on the
|
54
51
|
server for that feature updated with your work until all issues have been resolved.
|
55
52
|
|
53
|
+
```
|
54
|
+
$ git new-fb my-feature # 1
|
55
|
+
# do work # 2
|
56
|
+
$ git commit # 3
|
57
|
+
$ git sync # 4
|
58
|
+
# repeat #2-#4 as necessary # 5
|
59
|
+
$ git pull-request # 6
|
60
|
+
# repeat #2-#4 as necessary # 7
|
61
|
+
$ git to-master # 8
|
62
|
+
```
|
63
|
+
|
56
64
|
## Working Alone or When Pairing ##
|
57
65
|
|
58
66
|
1. When starting work on a new feature, use "`git new-fb feature-name`".
|
@@ -64,31 +72,34 @@ _The following assumes that the integration branch is "origin/master"._
|
|
64
72
|
3. When you are ready to merge your work into the mainline, "`git to-master`".
|
65
73
|
* This will merge and push your changes into "`origin/master`"
|
66
74
|
|
75
|
+
```
|
76
|
+
$ git new-fb my-feature # 1
|
77
|
+
# do work # 2
|
78
|
+
$ git commit # 3
|
79
|
+
$ git sync # 4
|
80
|
+
# repeat #2-#4 as necessary # 5
|
81
|
+
$ git to-master # 6
|
82
|
+
```
|
83
|
+
|
84
|
+
|
85
|
+
# Assumptions #
|
86
|
+
|
87
|
+
* You should **_never_** do any work directly on "`master`" (or whatever you define the mainline branch as): everything is done on a feature branch. This is a much safer and more flexible practice than doing everything on the same branch, but may seem odd to people used to old VCSs. In addition to being a much better way of working in general (see the F.A.Q. for more information), it is also a requirement to take advantage of Pull Request functionality.
|
88
|
+
* When working on a branch, you should be integrating with "`master`" as often as possible.
|
89
|
+
* "`git sync`" makes it extremely easy for you to get any changes that are made in "`master`" into your branch so you can react to it immediately. "`git to-master`" then makes it easy to cleanly integrate the changes you have made, along with encouraging code-review/sign-off, and doing various house-keeping duties.
|
90
|
+
* The process that you use should be essentially the same, regardless of whether you are working alone, or on a large distributed team.
|
91
|
+
* The exception here is "`git pull-request`" since you do not use pull requests when working solo or when pair-programming.
|
67
92
|
|
68
93
|
# Notes #
|
69
94
|
|
70
|
-
*
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
* After publishing changes to the main integration branch (i.e., "`git to-master`") the
|
75
|
-
old feature branch is removed as part of cleanup. Git is then "parked" on a "`_parking_`"
|
76
|
-
branch until a new feature branch is created. Work is not expected to be done on this
|
77
|
-
branch, but any that is done is brought over to a newly created feature branch (i.e.,
|
78
|
-
"`git new-fb`").
|
79
|
-
* If there is a problem (such as a merge conflict), this will try to resolve such errors
|
80
|
-
for you as much as it can do safely. When it can't do so in an automated way, it will try
|
81
|
-
to tell you the process for doing so manually.
|
82
|
-
* The first time you use a GitHub feature (e.g., "`git pull-request`"), this will ask for your
|
83
|
-
username and password. It does not store them, but instead uses them to get an OAuth2 token,
|
84
|
-
which is stored in "`git config gitProcess.github.authToken`".
|
85
|
-
* If you want to use a different integration branch other than "`master`", set the
|
86
|
-
"`gitProcess.integrationBranch`" configuration value. (e.g.,
|
87
|
-
"`git config gitProcess.integrationBranch my-integ-branch`")
|
95
|
+
* After publishing changes to the main integration branch (i.e., "`git to-master`") the old feature branch is removed as part of cleanup. Git is then "parked" on a "`_parking_`" branch until a new feature branch is created. Work is not expected to be done on this branch, but any that is done is brought over to a newly created feature branch (i.e., "`git new-fb`").
|
96
|
+
* If there is a problem (such as a merge conflict), this will try to resolve such errors for you as much as it can do safely. When it can't do so in an automated way, it will try to tell you the process for doing so manually.
|
97
|
+
* The first time you use a GitHub feature (e.g., "`git pull-request`"), this will ask for your username and password. It does not store them, but instead uses them to get an OAuth2 token, which is stored in "`git config gitProcess.github.authToken`".
|
98
|
+
* If you want to use a different integration branch other than "`master`", set the "`gitProcess.integrationBranch`" configuration value. (e.g., "`git config gitProcess.integrationBranch my-integ-branch`")
|
88
99
|
* This tries to respond "intelligently" to the use of 'rerere'.
|
89
100
|
|
90
101
|
|
91
|
-
#
|
102
|
+
# F.A.Q. #
|
92
103
|
|
93
104
|
## Q: How is this different from git-flow or GitHub flow? ##
|
94
105
|
|
@@ -101,7 +112,7 @@ _The following assumes that the integration branch is "origin/master"._
|
|
101
112
|
|
102
113
|
Branches are extremely powerful tools that allow for clean organization/modularization of development.
|
103
114
|
|
104
|
-
* Branches make it easy to sandbox changes while they are in a state of flux, while at the same time be very fearless about making potentially breaking changes.
|
115
|
+
* Branches make it easy to sandbox changes while they are in a state of flux, while at the same time allowing you to be very fearless about making potentially breaking changes.
|
105
116
|
* For example, I commit "green to green": Doing [TDD](http://en.wikipedia.org/wiki/Test-driven_development), I commit every time I have a newly passing test case. So, assuming I'm in a regular development flow, I'm committing my changes every five minutes. Tiny commits, but lots of them. What that means is that if I make a "less than wise choice" at some point, it's trivial to rewind to before I'd made the mistake, potentially keep the throw-away code in another branch while I do my cleanup, and generally use the full power of a revision control system to make my life safer and easier. The branch(es) are pretty chaotic, but that's not a problem because before integrating with the mainline, I take a moment to cleanup: Squash related commits together, write clearer commit messages (since now I know what "the answer" is), and generally move from my drafts to a more finished result. (See below on objections related to "lying with rebase.") That may just be me, though, because I'm very paranoid when it comes to computers. I tend to automatically hit Cmd/Ctl-S every time I type a period when I'm writing, or when I close a block when I'm programming. I have a minimum of three copies/backups around the world of all my important documents. And I "`git sync`" frequently to make sure my machine isn't the only place where all my hard work is being stored. Have I mentioned I don't trust computers?
|
106
117
|
|
107
118
|
* Branches allow for focused collaboration. Because a branch is about exactly one thing, it means that a team can collaborate around a feature/bug (especially when used in conjunction with a "pull request"), and keep such changes sandboxed until such time that they are ready to bring a larger audience into the mix.
|
@@ -114,7 +125,7 @@ Making it "easier to do things right than wrong" (i.e., using branches and keepi
|
|
114
125
|
|
115
126
|
## Q: Why so much emphasis on rebasing? Isn't rebasing a dangerous lie? ##
|
116
127
|
|
117
|
-
Like any powerful tool, "`git rebase`" is "dangerous" if used incorrectly, just like "`rm
|
128
|
+
Like any powerful tool, "`git rebase`" is "dangerous" if used incorrectly, just like "`rm`"/"`del`". You simply need to know when and how to use it safely. And in the world of version control systems, "rebasing" is easily one of the most _**useful**_ tools to come around since the "`commit`" command.
|
118
129
|
|
119
130
|
[A famous article](http://paul.stadig.name/2010/12/thou-shalt-not-lie-git-rebase-ammend.html) that people have been parroting in various forms for a while makes the case that rebasing (and its various forms, such as squashing, amending commits, etc.) is a "lie." As with so many things, context is everything.
|
120
131
|
|
@@ -124,8 +135,14 @@ Rebasing "your" code is an extremely useful way of communicating clearly. In the
|
|
124
135
|
|
125
136
|
If you have ever seen an "active" project that uses a process like "git-flow" that encourages a lot of branching and merging, you've seen how hard it can be to follow a particular line of development. Branch lines are flying around everywhere, and half the commits are pretty much pure noise. (e.g., "Merge branch 'develop' of ... into develop".) It's also hard to follow the order in which commits actually impacted the mainline. In many ways, in practice merges turn into "a truth effectively being a lie (because it's buried in the noise)" versus rebases that are "a lie (changed from it's 'original' form) to tell an effective truth (clean and very clear about its impact)."
|
126
137
|
|
127
|
-
|
138
|
+
This project is trying to promote clear communication about reality as it applies to the code, over micro-management over no-longer-relevant history. Thus rational for the judicious use of rebase.
|
139
|
+
|
128
140
|
|
141
|
+
## Configurables ##
|
142
|
+
See notes for more details
|
143
|
+
|
144
|
+
* GitHub authentication token
|
145
|
+
* The name of the integration branch (defaults to `origin/master`, but can be set to `develop` or other)
|
129
146
|
|
130
147
|
# Contributing #
|
131
148
|
|
data/bin/git-sync
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
# require "rubygems"
|
4
|
+
# require "bundler/setup"
|
5
|
+
# $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '../lib')
|
6
|
+
|
3
7
|
require 'git-process/git_process_options'
|
4
8
|
require 'git-process/sync'
|
5
9
|
|
10
|
+
|
6
11
|
class SyncOptions
|
7
12
|
include GitProc::GitProcessOptions
|
8
13
|
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
2
|
+
# you may not use this file except in compliance with the License.
|
3
|
+
# You may obtain a copy of the License at
|
4
|
+
#
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
9
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
# See the License for the specific language governing permissions and
|
11
|
+
# limitations under the License.require 'shellwords'
|
12
|
+
|
13
|
+
require 'git-process/git_lib'
|
14
|
+
require 'highline/import'
|
15
|
+
|
16
|
+
|
17
|
+
module GitProc
|
18
|
+
|
19
|
+
#
|
20
|
+
# Provides support for prompting the user when the dir/index is dirty.
|
21
|
+
#
|
22
|
+
# = Assumes =
|
23
|
+
# log_level
|
24
|
+
# workdir
|
25
|
+
#
|
26
|
+
module ChangeFileHelper
|
27
|
+
include GitLib
|
28
|
+
|
29
|
+
|
30
|
+
def offer_to_help_uncommitted_changes
|
31
|
+
stat = status
|
32
|
+
|
33
|
+
if stat.unmerged.empty?
|
34
|
+
handle_unknown_files(stat)
|
35
|
+
handle_changed_files(status) # refresh status in case it changed earlier
|
36
|
+
else
|
37
|
+
logger.info { "Can not offer to auto-add unmerged files: #{stat.unmerged.inspect}" }
|
38
|
+
raise UncommittedChangesError.new
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def handle_unknown_files(stat)
|
44
|
+
if not stat.unknown.empty?
|
45
|
+
resp = ask_how_to_handle_unknown_files(stat)
|
46
|
+
if resp == :add
|
47
|
+
add(stat.unknown)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def handle_changed_files(stat)
|
54
|
+
if not stat.modified.empty? or not stat.added.empty? or not stat.deleted.empty?
|
55
|
+
resp = ask_how_to_handle_changed_files(stat)
|
56
|
+
if resp == :commit
|
57
|
+
add((stat.added + stat.modified - stat.deleted).sort.uniq)
|
58
|
+
remove(stat.deleted)
|
59
|
+
commit(nil)
|
60
|
+
else
|
61
|
+
stash_save
|
62
|
+
@stash_pushed = true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def ask_how_to_handle_unknown_files(stat)
|
69
|
+
show_changes(:unknown, stat)
|
70
|
+
resp = ask("Would you like to (a)dd them or (i)gnore them? ") do |q|
|
71
|
+
q.responses[:not_valid] = "Please respond with either (a)dd or (i)gnore. (Ctl-C to abort.) "
|
72
|
+
q.case = :down
|
73
|
+
q.validate = /a|i/i
|
74
|
+
end
|
75
|
+
|
76
|
+
resp == 'a' ? :add : :ignore
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def show_changes(type, stat)
|
81
|
+
files = stat.send(type)
|
82
|
+
|
83
|
+
if type != :deleted
|
84
|
+
files -= stat.deleted
|
85
|
+
end
|
86
|
+
|
87
|
+
if not files.empty?
|
88
|
+
say("You have <%= color('#{type}', [:underline]) %> files:\n <%= color('#{files.join("\n ")}', [:bold]) %>")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
def ask_how_to_handle_changed_files(stat)
|
94
|
+
[:added, :modified, :deleted].each { |t| show_changes(t, stat) }
|
95
|
+
resp = ask("Would you like to (c)ommit them or (s)tash them? ") do |q|
|
96
|
+
q.responses[:not_valid] = "Please respond with either (c)ommit or (s)tash. (Ctl-C to abort.) "
|
97
|
+
q.case = :down
|
98
|
+
q.validate = /c|s/i
|
99
|
+
end
|
100
|
+
|
101
|
+
resp == 'c' ? :commit : :stash
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
@@ -61,7 +61,7 @@ module GitProc
|
|
61
61
|
def [](branch_name)
|
62
62
|
branch_name = current.name if branch_name == 'HEAD'
|
63
63
|
br = @items.find {|b| b.name == branch_name}
|
64
|
-
if br.nil? and branch_name !~ /origin\//
|
64
|
+
if br.nil? and branch_name !~ /origin\// and branch_name != '_parking_'
|
65
65
|
@lib.logger.warn {"Could not find '#{branch_name}' in #{@items.map{|i|i.name}.join(',')}"}
|
66
66
|
end
|
67
67
|
br
|
data/lib/git-process/git_lib.rb
CHANGED
@@ -28,9 +28,16 @@ class String
|
|
28
28
|
end
|
29
29
|
|
30
30
|
|
31
|
+
class NilClass
|
32
|
+
def to_boolean
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
31
38
|
module GitProc
|
32
39
|
|
33
|
-
class GitExecuteError <
|
40
|
+
class GitExecuteError < GitProcessError
|
34
41
|
end
|
35
42
|
|
36
43
|
|
@@ -44,7 +51,7 @@ module GitProc
|
|
44
51
|
module GitLib
|
45
52
|
|
46
53
|
def logger
|
47
|
-
|
54
|
+
if @logger.nil?
|
48
55
|
@logger = Logger.new(STDOUT)
|
49
56
|
@logger.level = log_level || Logger::WARN
|
50
57
|
@logger.datetime_format = "%Y-%m-%d %H:%M:%S"
|
@@ -77,26 +84,31 @@ module GitProc
|
|
77
84
|
|
78
85
|
|
79
86
|
def add(file)
|
87
|
+
logger.info { "Adding #{[*file].join(', ')}" }
|
80
88
|
command(:add, ['--', file])
|
81
89
|
end
|
82
90
|
|
83
91
|
|
84
|
-
def commit(msg)
|
85
|
-
|
92
|
+
def commit(msg = nil)
|
93
|
+
logger.info "Committing changes"
|
94
|
+
command(:commit, msg.nil? ? nil : ['-m', msg])
|
86
95
|
end
|
87
96
|
|
88
97
|
|
89
98
|
def rebase(base)
|
99
|
+
logger.info { "Rebasing #{branches.current.name} against #{base}" }
|
90
100
|
command('rebase', base)
|
91
101
|
end
|
92
102
|
|
93
103
|
|
94
104
|
def merge(base)
|
105
|
+
logger.info { "Merging #{branches.current.name} with #{base}" }
|
95
106
|
command(:merge, [base])
|
96
107
|
end
|
97
108
|
|
98
109
|
|
99
110
|
def fetch(name = remote_name)
|
111
|
+
logger.info "Fetching the latest changes from the server"
|
100
112
|
command(:fetch, ['-p', name])
|
101
113
|
end
|
102
114
|
|
@@ -211,6 +223,21 @@ module GitProc
|
|
211
223
|
end
|
212
224
|
|
213
225
|
|
226
|
+
def stash_save
|
227
|
+
command(:stash, ['save'])
|
228
|
+
end
|
229
|
+
|
230
|
+
|
231
|
+
def stash_pop
|
232
|
+
command(:stash, ['pop'])
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
def show(refspec)
|
237
|
+
command(:show, refspec)
|
238
|
+
end
|
239
|
+
|
240
|
+
|
214
241
|
def checkout(branch_name, opts = {}, &block)
|
215
242
|
args = []
|
216
243
|
args << '--no-track' if opts[:no_track]
|
@@ -237,10 +264,10 @@ module GitProc
|
|
237
264
|
end
|
238
265
|
|
239
266
|
|
240
|
-
def remove(
|
267
|
+
def remove(files, opts = {})
|
241
268
|
args = []
|
242
269
|
args << '-f' if opts[:force]
|
243
|
-
args <<
|
270
|
+
args << [*files]
|
244
271
|
command(:rm, args)
|
245
272
|
end
|
246
273
|
|
@@ -250,6 +277,9 @@ module GitProc
|
|
250
277
|
end
|
251
278
|
|
252
279
|
|
280
|
+
private :config_hash
|
281
|
+
|
282
|
+
|
253
283
|
def config(key = nil, value = nil, global = false)
|
254
284
|
if key and value
|
255
285
|
args = global ? ['--global'] : []
|
@@ -13,6 +13,7 @@
|
|
13
13
|
require 'git-process/git_lib'
|
14
14
|
require 'git-process/git_rebase_error'
|
15
15
|
require 'git-process/git_merge_error'
|
16
|
+
require 'highline/import'
|
16
17
|
|
17
18
|
|
18
19
|
module GitProc
|
@@ -23,25 +24,20 @@ module GitProc
|
|
23
24
|
def initialize(dir, opts = {})
|
24
25
|
@log_level = Process.log_level(opts)
|
25
26
|
|
26
|
-
|
27
|
-
@workdir = find_workdir(dir)
|
28
|
-
if @workdir.nil?
|
29
|
-
@workdir = dir
|
30
|
-
logger.info { "Initializing new repository at #{workdir}" }
|
31
|
-
command(:init)
|
32
|
-
else
|
33
|
-
logger.debug { "Opening existing repository at #{workdir}" }
|
34
|
-
end
|
35
|
-
end
|
27
|
+
set_workdir(dir)
|
36
28
|
end
|
37
29
|
|
38
30
|
|
39
31
|
def run
|
40
32
|
begin
|
33
|
+
verify_preconditions
|
34
|
+
|
41
35
|
runner
|
42
36
|
rescue GitProc::GitProcessError => exp
|
43
37
|
puts exp.message
|
44
38
|
exit(-1)
|
39
|
+
ensure
|
40
|
+
cleanup
|
45
41
|
end
|
46
42
|
end
|
47
43
|
|
@@ -51,6 +47,22 @@ module GitProc
|
|
51
47
|
end
|
52
48
|
|
53
49
|
|
50
|
+
def set_workdir(dir)
|
51
|
+
if !dir.nil?
|
52
|
+
@workdir = find_workdir(dir)
|
53
|
+
if @workdir.nil?
|
54
|
+
@workdir = dir
|
55
|
+
logger.info { "Initializing new repository at #{workdir}" }
|
56
|
+
command(:init)
|
57
|
+
else
|
58
|
+
logger.debug { "Opening existing repository at #{workdir}" }
|
59
|
+
end
|
60
|
+
else
|
61
|
+
logger.debug "Process dir is nil"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
54
66
|
def workdir
|
55
67
|
@workdir
|
56
68
|
end
|
@@ -76,8 +88,39 @@ module GitProc
|
|
76
88
|
end
|
77
89
|
|
78
90
|
|
91
|
+
def verify_preconditions
|
92
|
+
if should_remove_master
|
93
|
+
if ask_about_removing_master
|
94
|
+
branches[master_branch].delete!
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def cleanup
|
101
|
+
# extension point
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
def should_remove_master
|
106
|
+
my_branches = branches()
|
107
|
+
has_a_remote? and
|
108
|
+
my_branches.include?(master_branch) and
|
109
|
+
my_branches.current.name != master_branch and
|
110
|
+
!keep_local_integration_branch? and
|
111
|
+
my_branches[integration_branch].contains_all_of(master_branch)
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
def keep_local_integration_branch?
|
116
|
+
keep_local_integration_branch_config_value.to_boolean
|
117
|
+
end
|
118
|
+
|
119
|
+
|
79
120
|
def Process.log_level(opts)
|
80
|
-
if opts[:
|
121
|
+
if opts[:log_level]
|
122
|
+
opts[:log_level]
|
123
|
+
elsif opts[:quiet]
|
81
124
|
Logger::ERROR
|
82
125
|
elsif opts[:verbose]
|
83
126
|
Logger::DEBUG
|
@@ -96,6 +139,28 @@ module GitProc
|
|
96
139
|
private
|
97
140
|
|
98
141
|
|
142
|
+
def keep_local_integration_branch_config_value
|
143
|
+
config('gitProcess.keepLocalIntegrationBranch')
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
def ask_about_removing_master
|
148
|
+
resp = ask("You should remove your obsolete <%= color('local', [:bold]) %> branch, '#{master_branch}'. Should I remove it for you? (Yn) ") do |q|
|
149
|
+
q.responses[:not_valid] = 'Please respond with either (y)es or (n)o. Defaults to (y)es.'
|
150
|
+
q.case = :down
|
151
|
+
q.default = 'Y'
|
152
|
+
q.validate = /y|n/i
|
153
|
+
end
|
154
|
+
|
155
|
+
if resp == 'n'
|
156
|
+
say("(You can turn off this message using \"git config gitProcess.keepLocalIntegrationBranch true\").")
|
157
|
+
false
|
158
|
+
else
|
159
|
+
true
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
|
99
164
|
def find_workdir(dir)
|
100
165
|
if dir == File::SEPARATOR
|
101
166
|
nil
|
@@ -41,26 +41,41 @@ module GitProc
|
|
41
41
|
stat = s[0..1]
|
42
42
|
file = s[3..-1]
|
43
43
|
#puts "stat #{stat} - #{file}"
|
44
|
+
f = unquote(file)
|
44
45
|
case stat
|
45
46
|
when 'U ', ' U'
|
46
|
-
unmerged <<
|
47
|
+
unmerged << f
|
47
48
|
when 'UU'
|
48
|
-
unmerged <<
|
49
|
-
modified <<
|
50
|
-
when 'M ', ' M'
|
51
|
-
modified <<
|
49
|
+
unmerged << f
|
50
|
+
modified << f
|
51
|
+
when 'M ', ' M', 'MM'
|
52
|
+
modified << f
|
53
|
+
when 'MD'
|
54
|
+
modified << f
|
55
|
+
deleted << f
|
52
56
|
when 'D ', ' D'
|
53
|
-
deleted <<
|
57
|
+
deleted << f
|
54
58
|
when 'DU', 'UD'
|
55
|
-
deleted <<
|
56
|
-
unmerged <<
|
59
|
+
deleted << f
|
60
|
+
unmerged << f
|
57
61
|
when 'A ', ' A'
|
58
|
-
added <<
|
62
|
+
added << f
|
63
|
+
when 'AD'
|
64
|
+
added << f
|
65
|
+
deleted << f
|
59
66
|
when 'AA'
|
60
|
-
added <<
|
61
|
-
unmerged <<
|
67
|
+
added << f
|
68
|
+
unmerged << f
|
62
69
|
when '??'
|
63
|
-
unknown <<
|
70
|
+
unknown << f
|
71
|
+
when 'R '
|
72
|
+
old_file, new_file = file.split(' -> ')
|
73
|
+
deleted << unquote(old_file)
|
74
|
+
added << unquote(new_file)
|
75
|
+
when 'C '
|
76
|
+
old_file, new_file = file.split(' -> ')
|
77
|
+
added << unquote(old_file)
|
78
|
+
added << unquote(new_file)
|
64
79
|
else
|
65
80
|
raise "Do not know what to do with status #{stat} - #{file}"
|
66
81
|
end
|
@@ -74,6 +89,11 @@ module GitProc
|
|
74
89
|
end
|
75
90
|
|
76
91
|
|
92
|
+
def unquote(file)
|
93
|
+
file.match(/^"?(.*?)"?$/)[1]
|
94
|
+
end
|
95
|
+
|
96
|
+
|
77
97
|
# @return [Boolean] are there any changes in the index or working directory?
|
78
98
|
def clean?
|
79
99
|
@unmerged.empty? and @modified.empty? and @deleted.empty? and @added.empty? and @unknown.empty?
|
data/lib/git-process/new_fb.rb
CHANGED
@@ -14,6 +14,7 @@ require 'git-process/git_process'
|
|
14
14
|
require 'git-process/git_rebase_error'
|
15
15
|
require 'git-process/git_process_error'
|
16
16
|
require 'git-process/parked_changes_error'
|
17
|
+
require 'git-process/uncommitted_changes_error'
|
17
18
|
require 'git-process/github_pull_request'
|
18
19
|
|
19
20
|
|
@@ -21,18 +22,23 @@ module GitProc
|
|
21
22
|
|
22
23
|
class RebaseToMaster < Process
|
23
24
|
|
24
|
-
def
|
25
|
+
def verify_preconditions
|
26
|
+
super
|
27
|
+
|
25
28
|
raise UncommittedChangesError.new unless status.clean?
|
26
29
|
raise ParkedChangesError.new(self) if is_parked?
|
30
|
+
end
|
27
31
|
|
32
|
+
|
33
|
+
def runner
|
28
34
|
if has_a_remote?
|
29
35
|
fetch(server_name)
|
30
|
-
proc_rebase(
|
36
|
+
proc_rebase(integration_branch)
|
31
37
|
push(server_name, branches.current, master_branch)
|
32
38
|
close_pull_request
|
33
39
|
remove_feature_branch
|
34
40
|
else
|
35
|
-
proc_rebase(
|
41
|
+
proc_rebase(integration_branch)
|
36
42
|
end
|
37
43
|
end
|
38
44
|
|
@@ -42,6 +48,7 @@ module GitProc
|
|
42
48
|
|
43
49
|
remote_master = mybranches[remote_master_branch]
|
44
50
|
current_branch = mybranches.current
|
51
|
+
logger.debug { "Removing feature branch (#{current_branch})" }
|
45
52
|
|
46
53
|
unless remote_master.contains_all_of(current_branch.name)
|
47
54
|
raise GitProcessError.new("Branch '#{current_branch.name}' has not been merged into '#{remote_master_branch}'")
|
@@ -56,12 +63,12 @@ module GitProc
|
|
56
63
|
|
57
64
|
logger.warn {bad_parking_branch_msg}
|
58
65
|
else
|
59
|
-
parking_branch.delete
|
66
|
+
parking_branch.delete!
|
60
67
|
end
|
61
68
|
end
|
62
69
|
remote_master.checkout_to_new('_parking_', :no_track => true)
|
63
70
|
|
64
|
-
current_branch.delete(true)
|
71
|
+
current_branch.delete!(true)
|
65
72
|
if mybranches["#{server_name}/#{current_branch.name}"]
|
66
73
|
push(server_name, nil, nil, :delete => current_branch.name)
|
67
74
|
end
|