在这种情况下,存储库没有符号链接,但是我正在检查的工作目录。为什么?我有一个网站的现有存储库。为简化问题,我们假设其目录结构为:
bin www
但是为了节省一些钱,我把这个网站停放在现有的ISP帐户下,这样 www 目录现在是 www 的子目录,名为停放域。所以实际的目录结构现在是:
bin www parked-domain
我的解决方案我想创建一个名为 workdir 的目录,其中包含两个名为 bin 和 www 的符号链接,它们引用了先前目录结构中的bin 和 parked-domain 目录。然后我会使用 - work-tree 参数执行checkout命令,指定此 workdir 目录,并希望因为 bin 和 workdir 中的www 目录已经存在,checkout命令不会重新创建它们。但是,删除了符号链接并创建了常规目录。除了复制整个目录结构之外,是否有解决此问题的方法?
答案 0 :(得分:0)
这似乎并不可行。人们想要的是(1)只签出一个子目录的能力和(2)在签出这个子目录时能够覆盖在工作目录中使用的目录名。如下所示:
git --work-tree=$HOME/www checkout-tree master www parked-domain
顺便说一下,这些天大家都喜欢敲打的CVS可以做到这一点。
我提出的一个解决方案,我抛出来评论如下:
我创建了一个工作目录 work_dir ,我在其中创建了指向" real"的符号链接。目录。例如,
ln -s ~/www/parked-domain www
我在我的远程存储库(使用 - bare 选项初始化)中创建了一个 post-receive 挂钩。我们知道这个钩子不能使用 git checkout 命令,因为符号链接将被覆盖。相反,执行 git diff 命令以查看哪些文件已被添加/修改,哪些文件已被当前推送删除。然后钩子对每个被推送已被删除的文件执行删除命令。然后,钩子执行 git archive 命令,以转储由推送添加或修改的每个文件,然后使用 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)