-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathgit-stripe
executable file
Β·163 lines (129 loc) Β· 5.14 KB
/
git-stripe
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
#!/usr/bin/env python3
from gitz.git import GIT
from gitz.git import delete
from gitz.git import functions
from gitz.git import guess_origin
from gitz.git import root
from gitz.program import ARGS
from gitz.program import PROGRAM
SUMMARY = 'Push a sequence of commit IDs to a remote repository'
HELP = """
Starting with a given commit ID, and moving backwards from there,
push each commit ID to its own disposable branch name.
Useful to bring these commits to the attention of your continuous integration
if it has missed some of your commit IDs because you rebased or pushed a
sequences of commits too fast.
"""
PREFIX = '_gitz_stripe_'
BAD_BRANCH_CHARS = frozenset('~^: ')
def git_stripe():
root.check_git()
Stripe().stripe()
class Stripe:
def __init__(self):
self.remote_branches = functions.remote_branches()
self.remotes = list(self._remotes())
def stripe(self):
if ARGS.delete + ARGS.list + ARGS.safe > 1:
raise ValueError(_ERROR_ARGS_INCONSISTENT)
if ARGS.delete:
self._delete()
return
if ARGS.list:
self._list()
return
self.safe_offset = self._safe_offet() if ARGS.safe else 0
commits = functions.commit_ids(ARGS.commits or ['HEAD~'])
for i, commit in enumerate(commits):
for j in range(ARGS.count):
self._create_one(i, j, commit)
def _create_one(self, i, j, commit):
index = self.safe_offset + ARGS.offset + ARGS.count * i + j
branch = '%s%d' % (PREFIX, index)
refspec = '%s~%d:refs/heads/%s' % (commit, j, branch)
id = ''
for remote in self.remotes:
GIT.push('--force-with-lease', remote, refspec)
if not id:
striped = '%s/%s' % (remote, branch)
id, msg = functions.commit_message(striped)
PROGRAM.log.message('+ %s@%s: %s' % (striped, id, msg))
def _safe_offet(self):
branches = set()
for r in self.remotes:
branches.update(self.remote_branches[r])
stripes = [b for b in branches if b.startswith(PREFIX)]
stripes = [s[len(PREFIX) :] for s in stripes]
stripes = [int(s) for s in stripes if s.isnumeric()]
return max(stripes) + 1 if stripes else 0
def _remotes(self):
for remote in ARGS.remotes.split(':'):
if remote == '^':
yield guess_origin.guess_origin()
elif remote == '.' or remote in self.remote_branches:
yield remote
else:
PROGRAM.exit('Unknown remote', remote)
def _delete(self):
if ARGS.commits:
PROGRAM.exit(_ERROR_DELETE_ARGS)
if ARGS.count != 1:
PROGRAM.exit(_ERROR_DELETE_COUNT)
for remote in self.remotes:
for branch in self.remote_branches[remote]:
if branch.startswith(PREFIX):
delete.delete_remote_branch(remote, branch)
def _list(self):
for remote in self.remotes:
for branch in self.remote_branches[remote]:
if branch.startswith(PREFIX):
rbranch = '%s/%s' % (remote, branch)
id, msg = functions.commit_message(rbranch)
PROGRAM.log.message('%s@%s: %s' % (branch, id, msg))
def add_arguments(parser):
add = parser.add_argument
add('commits', nargs='*', help=_HELP_COMMITS)
add('-c', '--count', default=1, type=int, help=_HELP_COUNT)
add('-d', '--delete', action='store_true', help=_HELP_DELETE)
add('-l', '--list', action='store_true', help=_HELP_LIST)
add('-o', '--offset', default=0, type=int, help=_HELP_OFFSET)
add('-r', '--remotes', default='^', help=_HELP_REMOTE)
add('-s', '--safe', action='store_true', help=_HELP_SAFE)
_ERROR_ARGS_INCONSISTENT = """\
At most one of --delete, --list, or --safe may be set"""
_ERROR_BRANCH_NAME = 'Illegal character in branch name'
_ERROR_DELETE_ARGS = 'git stripe -d takes no arguments'
_ERROR_DELETE_COUNT = 'git stripe -d ignores --count/-c'
_HELP_COMMITS = 'Branch/commit IDs to be striped (defaults to HEAD~)'
_HELP_COUNT = 'The number of striped branches to be created'
_HELP_DELETE = 'Delete all striped branches'
_HELP_LIST = 'List all remote stripes'
_HELP_OFFSET = 'Offset to start numbering stripes'
_HELP_REMOTE = (
'One or more remote remotes to push to, separated by colon. '
' "." means the local repo, "^" means the upstream repo'
)
_HELP_SAFE = (
'Do not push over existing stripes: find an unused range of indices'
)
EXAMPLES = """
git stripe
Pushes HEAD~ into its own branch named _gitz_stripe_0
git stripe --count=3
git stripe -c3
Pushes HEAD~, HEAD~2 and HEAD~3 into their own branches named
_gitz_stripe_0, _gitz_stripe_1 and _gitz_stripe_2
git stripe --offset=5
git stripe -o5
Pushes HEAD~, HEAD~2 and HEAD~3 into their own branches named
_gitz_stripe_5, _gitz_stripe_6 and _gitz_stripe_7
git stripe 2 HEAD~3
git stripe HEAD~3 2
Pushes HEAD~3 and HEAD~4 into two branches named _gitz_stripe_0
and _gitz_stripe_1
git stripe --delete-all
git stripe -D
Delete all stripes
"""
if __name__ == '__main__':
PROGRAM.start()