mirror of
https://github.com/Dev-Wiki/git-repo.git
synced 2025-12-13 08:04:02 +08:00
Initial Contribution
This commit is contained in:
344
git_config.py
Normal file
344
git_config.py
Normal file
@@ -0,0 +1,344 @@
|
||||
#
|
||||
# Copyright (C) 2008 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from error import GitError
|
||||
from git_command import GitCommand
|
||||
|
||||
R_HEADS = 'refs/heads/'
|
||||
R_TAGS = 'refs/tags/'
|
||||
ID_RE = re.compile('^[0-9a-f]{40}$')
|
||||
|
||||
def IsId(rev):
|
||||
return ID_RE.match(rev)
|
||||
|
||||
|
||||
class GitConfig(object):
|
||||
@classmethod
|
||||
def ForUser(cls):
|
||||
return cls(file = os.path.expanduser('~/.gitconfig'))
|
||||
|
||||
@classmethod
|
||||
def ForRepository(cls, gitdir, defaults=None):
|
||||
return cls(file = os.path.join(gitdir, 'config'),
|
||||
defaults = defaults)
|
||||
|
||||
def __init__(self, file, defaults=None):
|
||||
self.file = file
|
||||
self.defaults = defaults
|
||||
self._cache_dict = None
|
||||
self._remotes = {}
|
||||
self._branches = {}
|
||||
|
||||
def Has(self, name, include_defaults = True):
|
||||
"""Return true if this configuration file has the key.
|
||||
"""
|
||||
name = name.lower()
|
||||
if name in self._cache:
|
||||
return True
|
||||
if include_defaults and self.defaults:
|
||||
return self.defaults.Has(name, include_defaults = True)
|
||||
return False
|
||||
|
||||
def GetBoolean(self, name):
|
||||
"""Returns a boolean from the configuration file.
|
||||
None : The value was not defined, or is not a boolean.
|
||||
True : The value was set to true or yes.
|
||||
False: The value was set to false or no.
|
||||
"""
|
||||
v = self.GetString(name)
|
||||
if v is None:
|
||||
return None
|
||||
v = v.lower()
|
||||
if v in ('true', 'yes'):
|
||||
return True
|
||||
if v in ('false', 'no'):
|
||||
return False
|
||||
return None
|
||||
|
||||
def GetString(self, name, all=False):
|
||||
"""Get the first value for a key, or None if it is not defined.
|
||||
|
||||
This configuration file is used first, if the key is not
|
||||
defined or all = True then the defaults are also searched.
|
||||
"""
|
||||
name = name.lower()
|
||||
|
||||
try:
|
||||
v = self._cache[name]
|
||||
except KeyError:
|
||||
if self.defaults:
|
||||
return self.defaults.GetString(name, all = all)
|
||||
v = []
|
||||
|
||||
if not all:
|
||||
if v:
|
||||
return v[0]
|
||||
return None
|
||||
|
||||
r = []
|
||||
r.extend(v)
|
||||
if self.defaults:
|
||||
r.extend(self.defaults.GetString(name, all = True))
|
||||
return r
|
||||
|
||||
def SetString(self, name, value):
|
||||
"""Set the value(s) for a key.
|
||||
Only this configuration file is modified.
|
||||
|
||||
The supplied value should be either a string,
|
||||
or a list of strings (to store multiple values).
|
||||
"""
|
||||
name = name.lower()
|
||||
|
||||
try:
|
||||
old = self._cache[name]
|
||||
except KeyError:
|
||||
old = []
|
||||
|
||||
if value is None:
|
||||
if old:
|
||||
del self._cache[name]
|
||||
self._do('--unset-all', name)
|
||||
|
||||
elif isinstance(value, list):
|
||||
if len(value) == 0:
|
||||
self.SetString(name, None)
|
||||
|
||||
elif len(value) == 1:
|
||||
self.SetString(name, value[0])
|
||||
|
||||
elif old != value:
|
||||
self._cache[name] = list(value)
|
||||
self._do('--replace-all', name, value[0])
|
||||
for i in xrange(1, len(value)):
|
||||
self._do('--add', name, value[i])
|
||||
|
||||
elif len(old) != 1 or old[0] != value:
|
||||
self._cache[name] = [value]
|
||||
self._do('--replace-all', name, value)
|
||||
|
||||
def GetRemote(self, name):
|
||||
"""Get the remote.$name.* configuration values as an object.
|
||||
"""
|
||||
try:
|
||||
r = self._remotes[name]
|
||||
except KeyError:
|
||||
r = Remote(self, name)
|
||||
self._remotes[r.name] = r
|
||||
return r
|
||||
|
||||
def GetBranch(self, name):
|
||||
"""Get the branch.$name.* configuration values as an object.
|
||||
"""
|
||||
try:
|
||||
b = self._branches[name]
|
||||
except KeyError:
|
||||
b = Branch(self, name)
|
||||
self._branches[b.name] = b
|
||||
return b
|
||||
|
||||
@property
|
||||
def _cache(self):
|
||||
if self._cache_dict is None:
|
||||
self._cache_dict = self._Read()
|
||||
return self._cache_dict
|
||||
|
||||
def _Read(self):
|
||||
d = self._do('--null', '--list')
|
||||
c = {}
|
||||
while d:
|
||||
lf = d.index('\n')
|
||||
nul = d.index('\0', lf + 1)
|
||||
|
||||
key = d[0:lf]
|
||||
val = d[lf + 1:nul]
|
||||
|
||||
if key in c:
|
||||
c[key].append(val)
|
||||
else:
|
||||
c[key] = [val]
|
||||
|
||||
d = d[nul + 1:]
|
||||
return c
|
||||
|
||||
def _do(self, *args):
|
||||
command = ['config', '--file', self.file]
|
||||
command.extend(args)
|
||||
|
||||
p = GitCommand(None,
|
||||
command,
|
||||
capture_stdout = True,
|
||||
capture_stderr = True)
|
||||
if p.Wait() == 0:
|
||||
return p.stdout
|
||||
else:
|
||||
GitError('git config %s: %s' % (str(args), p.stderr))
|
||||
|
||||
|
||||
class RefSpec(object):
|
||||
"""A Git refspec line, split into its components:
|
||||
|
||||
forced: True if the line starts with '+'
|
||||
src: Left side of the line
|
||||
dst: Right side of the line
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def FromString(cls, rs):
|
||||
lhs, rhs = rs.split(':', 2)
|
||||
if lhs.startswith('+'):
|
||||
lhs = lhs[1:]
|
||||
forced = True
|
||||
else:
|
||||
forced = False
|
||||
return cls(forced, lhs, rhs)
|
||||
|
||||
def __init__(self, forced, lhs, rhs):
|
||||
self.forced = forced
|
||||
self.src = lhs
|
||||
self.dst = rhs
|
||||
|
||||
def SourceMatches(self, rev):
|
||||
if self.src:
|
||||
if rev == self.src:
|
||||
return True
|
||||
if self.src.endswith('/*') and rev.startswith(self.src[:-1]):
|
||||
return True
|
||||
return False
|
||||
|
||||
def DestMatches(self, ref):
|
||||
if self.dst:
|
||||
if ref == self.dst:
|
||||
return True
|
||||
if self.dst.endswith('/*') and ref.startswith(self.dst[:-1]):
|
||||
return True
|
||||
return False
|
||||
|
||||
def MapSource(self, rev):
|
||||
if self.src.endswith('/*'):
|
||||
return self.dst[:-1] + rev[len(self.src) - 1:]
|
||||
return self.dst
|
||||
|
||||
def __str__(self):
|
||||
s = ''
|
||||
if self.forced:
|
||||
s += '+'
|
||||
if self.src:
|
||||
s += self.src
|
||||
if self.dst:
|
||||
s += ':'
|
||||
s += self.dst
|
||||
return s
|
||||
|
||||
|
||||
class Remote(object):
|
||||
"""Configuration options related to a remote.
|
||||
"""
|
||||
def __init__(self, config, name):
|
||||
self._config = config
|
||||
self.name = name
|
||||
self.url = self._Get('url')
|
||||
self.review = self._Get('review')
|
||||
self.fetch = map(lambda x: RefSpec.FromString(x),
|
||||
self._Get('fetch', all=True))
|
||||
|
||||
def ToLocal(self, rev):
|
||||
"""Convert a remote revision string to something we have locally.
|
||||
"""
|
||||
if IsId(rev):
|
||||
return rev
|
||||
if rev.startswith(R_TAGS):
|
||||
return rev
|
||||
|
||||
if not rev.startswith('refs/'):
|
||||
rev = R_HEADS + rev
|
||||
|
||||
for spec in self.fetch:
|
||||
if spec.SourceMatches(rev):
|
||||
return spec.MapSource(rev)
|
||||
raise GitError('remote %s does not have %s' % (self.name, rev))
|
||||
|
||||
def WritesTo(self, ref):
|
||||
"""True if the remote stores to the tracking ref.
|
||||
"""
|
||||
for spec in self.fetch:
|
||||
if spec.DestMatches(ref):
|
||||
return True
|
||||
return False
|
||||
|
||||
def ResetFetch(self):
|
||||
"""Set the fetch refspec to its default value.
|
||||
"""
|
||||
self.fetch = [RefSpec(True,
|
||||
'refs/heads/*',
|
||||
'refs/remotes/%s/*' % self.name)]
|
||||
|
||||
def Save(self):
|
||||
"""Save this remote to the configuration.
|
||||
"""
|
||||
self._Set('url', self.url)
|
||||
self._Set('review', self.review)
|
||||
self._Set('fetch', map(lambda x: str(x), self.fetch))
|
||||
|
||||
def _Set(self, key, value):
|
||||
key = 'remote.%s.%s' % (self.name, key)
|
||||
return self._config.SetString(key, value)
|
||||
|
||||
def _Get(self, key, all=False):
|
||||
key = 'remote.%s.%s' % (self.name, key)
|
||||
return self._config.GetString(key, all = all)
|
||||
|
||||
|
||||
class Branch(object):
|
||||
"""Configuration options related to a single branch.
|
||||
"""
|
||||
def __init__(self, config, name):
|
||||
self._config = config
|
||||
self.name = name
|
||||
self.merge = self._Get('merge')
|
||||
|
||||
r = self._Get('remote')
|
||||
if r:
|
||||
self.remote = self._config.GetRemote(r)
|
||||
else:
|
||||
self.remote = None
|
||||
|
||||
@property
|
||||
def LocalMerge(self):
|
||||
"""Convert the merge spec to a local name.
|
||||
"""
|
||||
if self.remote and self.merge:
|
||||
return self.remote.ToLocal(self.merge)
|
||||
return None
|
||||
|
||||
def Save(self):
|
||||
"""Save this branch back into the configuration.
|
||||
"""
|
||||
self._Set('merge', self.merge)
|
||||
if self.remote:
|
||||
self._Set('remote', self.remote.name)
|
||||
else:
|
||||
self._Set('remote', None)
|
||||
|
||||
def _Set(self, key, value):
|
||||
key = 'branch.%s.%s' % (self.name, key)
|
||||
return self._config.SetString(key, value)
|
||||
|
||||
def _Get(self, key, all=False):
|
||||
key = 'branch.%s.%s' % (self.name, key)
|
||||
return self._config.GetString(key, all = all)
|
||||
Reference in New Issue
Block a user