Git结帐到带有符号链接

时间:2016-04-13 12:29:27

标签: git git-diff git-checkout git-archive

在这种情况下,存储库没有符号链接,但是我正在检查的工作目录。为什么?我有一个网站的现有存储库。为简化问题,我们假设其目录结构为:

bin
www

但是为了节省一些钱,我把这个网站停放在现有的ISP帐户下,这样 www 目录现在是 www 的子目录,名为停放域。所以实际的目录结构现在是:

bin
www
   parked-domain

我的解决方案我想创建一个名为 workdir 的目录,其中包含两个名为 bin www 的符号链接,它们引用了先前目录结构中的bin parked-domain 目录。然后我会使用 - work-tree 参数执行checkout命令,指定此 workdir 目录,并希望因为 bin workdir 中的www 目录已经存在,checkout命令不会重新创建它们。但是,删除了符号链接并创建了常规目录。除了复制整个目录结构之外,是否有解决此问题的方法?

1 个答案:

答案 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)