Не похоже, что это выполнимо. Что нужно: (1) Возможность проверки только одного подкаталога и (2) Возможность переопределить имя каталога, которое будет использоваться в рабочем каталоге при проверке этого подкаталога. Что-то вроде следующего:
git --work-tree=$HOME/www checkout-tree master www parked-domain
Кстати, CVS, которые все любят стучать в эти дни, может сделать это.
Раствор я придумал, что выкидываю для комментариев выглядит следующим образом:
Я создал рабочий каталог, work_dir, в рамках которого я создал символические ссылки на «реальные» каталоги , Например,
ln -s ~/www/parked-domain www
у меня в удаленном хранилище (инициализируется с опцией --bare) создал после приема крюка. Мы знаем, что этот крючок не может использовать команду git checkout, потому что символические ссылки будут наложены. Вместо этого выполняется команда git diff, чтобы посмотреть, какие файлы были добавлены/изменены, какие файлы были удалены текущим push. Затем крючок выполняет команду удаления для каждого файла, который был указан как удаленный нажатием. Затем крючок выполняет команду git архива, чтобы сбрасывать каждый файл, который был добавлен или изменен нажатием, а затем командой tar, чтобы извлечь эти файлы в рабочий каталог.Для самого первого нажатия «от хеша ревизии» составляет 40 нулей, и в этом случае весь репозиторий сбрасывается и извлекается через git archive. Ниже приведена реализация в Python:
#!/usr/bin/env python
import sys, os, subprocess, re, string
SEP = os.sep
def log(s):
sys.stdout.write(s);
sys.stdout.flush();
def runCmd(cmd):
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
output = p.communicate()
return output[0]
def runPipe(cmd1, cmd2):
p1 = subprocess.Popen(cmd1, stdout=subprocess.PIPE)
p2 = subprocess.Popen(cmd2, stdin=p1.stdout, stdout=subprocess.PIPE)
output = p2.communicate()
return output[0]
def getBranch(ref, gitDir):
gitDirParam = '--git-dir=' + gitDir
return runCmd(['git', gitDirParam, 'rev-parse', '--symbolic', '--abbrev-ref', ref]).strip()
def isRenamedDirectory(fromDir, renameDict):
# simple case:
if fromDir in renameDict:
return True, renameDict[fromDir]
fDir = fromDir.split(SEP)
n = len(fDir)
if n != 1:
for i in range(n - 1, 0, -1):
fromDirNew = SEP.join(fDir[0:i])
if fromDirNew in renameDict:
newDir = renameDict[fromDirNew] + SEP + SEP.join(fDir[i:])
renameDict[fromDir] = newDir
return True, newDir
return False, fromDir
def process(fromCommit, toCommit, gitDir):
log('Processing %s - %s\n' % (fromCommit, toCommit))
gitDirParam = '--git-dir=' + gitDir
if fromCommit == 40*'0':
log('"Checking out" everything.\n')
archiveCmd = ['git', gitDirParam, 'archive', '--format=tar.gz', toCommit]
tarCmd = ['tar', '--warning=no-timestamp', '-zxf', '-']
out = runPipe(archiveCmd, tarCmd)
log(out)
return
diffs = runCmd(['git', gitDirParam, 'diff', '--name-status', '--find-renames=100', fromCommit, toCommit]).split('\n')
updateList = []
renameDict = dict()
renameSet = set()
defferedDiffs = []
for this_pass in range(1, 3):
for diff in diffs if this_pass == 1 else defferedDiffs:
m = re.match(r'^R100\t(.*)\t(.*)$', diff)
if m:
# rename:
fromPath = m.group(1)
toPath = m.group(2)
(fromDir, fromFilename) = os.path.split(fromPath)
(toDir, toFilename) = os.path.split(toPath)
isRenamed, newDir = isRenamedDirectory(fromDir, renameDict)
if isRenamed:
# it's been renamed to newDir
fromPath = newDir + SEP + fromFilename
elif fromDir != toDir and fromDir != '' and toDir != '' and not os.path.isdir(toDir):
# a directory is being renamed or moved
# has directory already been renamed or moved?
fDir = fromDir.split(SEP)
tDir = toDir.split(SEP)
l = min(len(fDir), len(tDir))
i = 0;
fDirLast = []
while i < l and fDir[-1] == tDir[-1]:
fDirLast.append(fDir.pop(-1))
tDir.pop(-1)
i += 1
subDirOld = SEP.join(fDir)
subDirNew = SEP.join(tDir)
while subDirOld not in renameSet and os.path.isdir(subDirNew):
assert len(fDirLast)
lastDir = fDirLast.pop(-1)
subDirOld += SEP + lastDir
subDirNew += SEP + lastDir
if subDirOld not in renameSet:
log("Renaming directory %s to %s\n" % (subDirOld, subDirNew))
runCmd(['mv', subDirOld, subDirNew])
renameSet.add(subDirOld)
renameDict[fromDir] = toDir
# this is the name of the file in the renamed or removed path
fromPath = toDir + SEP + fromFilename
# fall through and see if filename has also changed
if fromPath != toPath:
if os.path.isdir(toDir):
log("Renaming file %s to %s\n" % (fromPath, toPath))
runCmd(['mv', fromPath, toPath])
else:
# we have to defer this to a second pass
if this_pass == 1:
defferedDiffs.append(diff)
else:
log("Can't rename file %s to %s -- directory %s does not exist. % (fromPath, toPath, toDir)")
continue
m = re.match(r'^([ACDMTUXB])\t(.*)$', diff)
if m is None:
continue
action = m.group(1)
path = m.group(2)
if action != 'D':
updateList.append(path)
else:
(fileDir, fileName) = os.path.split(path)
if fileDir != '' and fileDir in renameDict:
path = renameDict[fileDir] + SEP + fileName
if os.path.isfile(path):
try:
os.remove(path)
except Exception as e:
log('Could not delete %s: %s\n' % (path, str(e)))
else:
log('Deleted %s\n' % path)
if len(updateList) == 0:
return
archiveCmd = ['git', gitDirParam, 'archive', '--format=tar.gz', toCommit]
archiveCmd.extend(updateList);
tarCmd = ['tar', '--warning=no-timestamp', '-zxvf', '-']
out = runPipe(archiveCmd, tarCmd)
log(out)
def process_revisions(gitDir, workDir):
log('Using Git directory %s, work directory %s\n' % (gitDir, workDir))
curDir = os.getcwd()
try:
os.chdir(workDir)
lines = sys.stdin.readlines()
for line in lines:
tokens = string.split(line.strip())
fromCommit = tokens[0]
toCommit = tokens[1]
branch = getBranch(tokens[2], gitDir)
if branch != 'master':
log('Not on master branch -- skipping.\n')
else:
process(fromCommit, toCommit, gitDir)
except Exception as e:
log(str(e))
os.chdir(curDir)
GIT_DIR = '%s%s%s' % (os.environ['HOME'], SEP, 'repository.git')
WORK_DIR = '%s%s%s' % (os.environ['HOME'], SEP, 'work_dir')
process_revisions(GIT_DIR, WORK_DIR)