1 """Common utility functions"""
8 from stgit
.compat
import environ_get
9 from stgit
.config
import config
10 from stgit
.exception
import StgException
11 from stgit
.out
import out
12 from stgit
.run
import Run
15 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
17 This program is free software; you can redistribute it and/or modify
18 it under the terms of the GNU General Public License version 2 as
19 published by the Free Software Foundation.
21 This program is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, see http://www.gnu.org/licenses/.
31 def strip_prefix(prefix
, string
):
32 """Return string, without the specified prefix.
34 The string must start with the prefix.
37 assert string
.startswith(prefix
)
38 return string
[len(prefix
) :]
41 class EditorException(StgException
):
47 environ_get('GIT_EDITOR'),
48 config
.get('stgit.editor'), # legacy
49 config
.get('core.editor'),
50 environ_get('VISUAL'),
51 environ_get('EDITOR'),
58 def call_editor(filename
):
59 """Run the editor on the specified filename."""
60 cmd
= '%s %s' % (get_editor(), filename
)
61 out
.start('Invoking the editor: "%s"' % cmd
)
64 raise EditorException('editor failed, exit code: %d' % err
)
68 def get_hook(repository
, hook_name
, extra_env
={}):
69 hook_path
= os
.path
.join(repository
.directory
, 'hooks', hook_name
)
70 if not (os
.path
.isfile(hook_path
) and os
.access(hook_path
, os
.X_OK
)):
73 default_iw
= repository
.default_iw
74 prefix_dir
= os
.path
.relpath(os
.getcwd(), default_iw
.cwd
)
75 if prefix_dir
== os
.curdir
:
78 prefix
= os
.path
.join(prefix_dir
, '')
79 extra_env
= add_dict(extra_env
, {'GIT_PREFIX': prefix
})
81 def hook(*parameters
):
83 argv
.extend(parameters
)
85 # On Windows, run the hook using "bash" explicitly
86 if os
.name
!= 'posix':
87 argv
.insert(0, 'bash')
89 default_iw
.run(argv
, extra_env
).run()
91 hook
.__name
__ = str(hook_name
)
95 def run_hook_on_bytes(hook
, byte_data
, *args
):
96 temp
= tempfile
.NamedTemporaryFile('wb', prefix
='stgit-hook', delete
=False)
101 with
open(temp
.name
, 'rb') as data_file
:
102 return data_file
.read()
107 def edit_string(s
, filename
, encoding
='utf-8'):
108 with
open(filename
, 'w', encoding
=encoding
) as f
:
110 call_editor(filename
)
111 with
open(filename
, encoding
=encoding
) as f
:
117 def edit_bytes(s
, filename
):
118 with
open(filename
, 'wb') as f
:
120 call_editor(filename
)
121 with
open(filename
, 'rb') as f
:
127 def find_patch_name(patchname
, unacceptable
):
128 """Find a patch name which is acceptable."""
129 if unacceptable(patchname
):
131 while unacceptable('%s-%d' % (patchname
, suffix
)):
133 patchname
= '%s-%d' % (patchname
, suffix
)
137 def patch_name_from_msg(msg
):
138 """Return a string to be used as a patch name.
140 This is generated from the first line of the specified message string.
146 name_len
= config
.getint('stgit.namelength')
150 subject_line
= msg
.split('\n', 1)[0].lstrip().lower()
151 words
= re
.sub(r
'(?u)[\W]+', ' ', subject_line
).split()
153 # use loop to avoid truncating the last name
154 name
= words
and words
[0] or 'unknown'
155 for word
in words
[1:]:
156 new
= name
+ '-' + word
157 if len(new
) > name_len
:
164 def make_patch_name(msg
, unacceptable
, default_name
='patch'):
165 """Generate a patch name from the given commit message.
167 The generated name is guaranteed to make unacceptable(name) be false. If the commit
168 message is empty, base the name on default_name instead.
171 patchname
= patch_name_from_msg(msg
)
173 patchname
= default_name
174 return find_patch_name(patchname
, unacceptable
)
177 def add_trailer(message
, trailer
, name
, email
):
178 trailer_line
= '%s: %s <%s>' % (trailer
, name
, email
)
180 Run('git', 'interpret-trailers', '--trailer', trailer_line
)
186 def parse_name_email(address
):
187 """Parse an email address string.
189 Returns a tuple consisting of the name and email parsed from a
190 standard 'name <email>' or 'email (name)' string.
193 address
= re
.sub(r
'[\\"]', r
'\\\g<0>', address
)
194 str_list
= re
.findall(r
'^(.*)\s*<(.*)>\s*$', address
)
196 str_list
= re
.findall(r
'^(.*)\s*\((.*)\)\s*$', address
)
199 return (str_list
[0][1], str_list
[0][0])
204 STGIT_SUCCESS
= 0 # everything's OK
205 STGIT_GENERAL_ERROR
= 1 # seems to be non-command-specific error
206 STGIT_COMMAND_ERROR
= 2 # seems to be a command that failed
207 STGIT_CONFLICT
= 3 # merge conflict, otherwise OK
208 STGIT_BUG_ERROR
= 4 # a bug in StGit
211 def add_dict(d1
, d2
):
212 """Return a new dict with the contents of both d1 and d2.
214 In case of conflicting mappings, d2 takes precedence.