1 """Common utility functions"""
10 from stgit
.compat
import environ_get
11 from stgit
.config
import config
12 from stgit
.exception
import StgException
13 from stgit
.out
import out
14 from stgit
.run
import Run
17 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
19 This program is free software; you can redistribute it and/or modify
20 it under the terms of the GNU General Public License version 2 as
21 published by the Free Software Foundation.
23 This program is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 GNU General Public License for more details.
28 You should have received a copy of the GNU General Public License
29 along with this program; if not, see http://www.gnu.org/licenses/.
33 def strip_prefix(prefix
, string
):
34 """Return string, without the specified prefix.
36 The string must start with the prefix.
39 assert string
.startswith(prefix
)
40 return string
[len(prefix
) :]
43 class EditorException(StgException
):
49 environ_get('GIT_EDITOR'),
50 config
.get('stgit.editor'), # legacy
51 config
.get('core.editor'),
52 environ_get('VISUAL'),
53 environ_get('EDITOR'),
60 def call_editor(filename
):
61 """Run the editor on the specified filename."""
62 cmd
= '%s %s' % (get_editor(), filename
)
63 out
.start('Invoking the editor: "%s"' % cmd
)
66 raise EditorException('editor failed, exit code: %d' % err
)
70 def get_hooks_path(repository
):
71 hooks_path
= config
.get('core.hookspath')
72 if hooks_path
is None:
73 return os
.path
.join(repository
.directory
, 'hooks')
74 elif os
.path
.isabs(hooks_path
):
77 return os
.path
.join(repository
.default_worktree
.directory
, hooks_path
)
80 def get_hook(repository
, hook_name
, extra_env
=None):
83 hook_path
= os
.path
.join(get_hooks_path(repository
), hook_name
)
84 if not (os
.path
.isfile(hook_path
) and os
.access(hook_path
, os
.X_OK
)):
87 prefix_dir
= os
.path
.relpath(os
.getcwd(), repository
.default_worktree
.directory
)
88 if prefix_dir
== os
.curdir
:
91 prefix
= os
.path
.join(prefix_dir
, '')
92 extra_env
= add_dict(extra_env
, {'GIT_PREFIX': prefix
})
94 def hook(*parameters
):
95 if sys
.platform
== 'win32':
96 # On Windows, run the hook using "bash" explicitly.
97 # Try to locate bash.exe in user's PATH, but avoid the WSL
98 # shim/bootstrapper %SYSTEMROOT%/System32/bash.exe
99 systemroot
= os
.environ
.get('SYSTEMROOT')
101 system32
= os
.path
.normcase(os
.path
.join(systemroot
, 'system32'))
102 path
= os
.pathsep
.join(
104 for p
in os
.environ
.get('PATH', '').split(os
.pathsep
)
105 if os
.path
.normcase(p
) != system32
110 # Find bash with user's path (sans System32).
111 bash_exe
= shutil
.which('bash.exe', path
=path
)
113 # Next try finding the bash.exe that came with Git for Windows.
114 git_exe
= shutil
.which('git.exe', path
=path
)
116 raise StgException('Failed to locate either bash.exe or git.exe')
117 bash_exe
= os
.path
.join(
118 os
.path
.dirname(os
.path
.dirname(git_exe
)),
123 argv
= [bash_exe
, hook_path
]
127 argv
.extend(parameters
)
129 repository
.default_iw
.run(argv
, extra_env
).run()
131 hook
.__name
__ = hook_name
135 def run_hook_on_bytes(hook
, byte_data
, *args
):
136 temp
= tempfile
.NamedTemporaryFile('wb', prefix
='stgit-hook', delete
=False)
139 temp
.write(byte_data
)
141 with
open(temp
.name
, 'rb') as data_file
:
142 return data_file
.read()
147 def edit_string(s
, filename
, encoding
='utf-8'):
148 with
open(filename
, 'w', encoding
=encoding
) as f
:
150 call_editor(filename
)
151 with
open(filename
, encoding
=encoding
) as f
:
157 def edit_bytes(s
, filename
):
158 with
open(filename
, 'wb') as f
:
160 call_editor(filename
)
161 with
open(filename
, 'rb') as f
:
167 def add_trailers(message
, trailers
, name
, email
):
169 for trailer
, user_value
in trailers
:
170 if user_value
is None:
171 trailer_args
.extend(['--trailer', '%s: %s <%s>' % (trailer
, name
, email
)])
173 trailer_args
.extend(['--trailer', '%s: %s' % (trailer
, user_value
)])
175 Run('git', 'interpret-trailers', *trailer_args
).raw_input(message
).raw_output()
179 def parse_name_email(address
):
180 """Parse an email address string.
182 Returns a tuple consisting of the name and email parsed from a
183 standard 'name <email>' or 'email (name)' string.
186 address
= re
.sub(r
'[\\"]', r
'\\\g<0>', address
)
187 str_list
= re
.findall(r
'^(.*)\s*<(.*)>\s*$', address
)
189 str_list
= re
.findall(r
'^(.*)\s*\((.*)\)\s*$', address
)
192 return (str_list
[0][1], str_list
[0][0])
197 STGIT_SUCCESS
= 0 # everything's OK
198 STGIT_GENERAL_ERROR
= 1 # seems to be non-command-specific error
199 STGIT_COMMAND_ERROR
= 2 # seems to be a command that failed
200 STGIT_CONFLICT
= 3 # merge conflict, otherwise OK
201 STGIT_BUG_ERROR
= 4 # a bug in StGit
204 def add_dict(d1
, d2
):
205 """Return a new dict with the contents of both d1 and d2.
207 In case of conflicting mappings, d2 takes precedence.