git:如何从项目中分离库? filter-branch,subtree?

时间:2011-06-19 16:45:21

标签: git git-submodules git-filter-branch git-subtree

所以,我有一个更大的(闭源)项目,并且在这个项目的上下文中创建了一个在其他地方也有用的库,我认为。

我现在想要在自己的项目中拆分库,它可以作为github或类似的开源。当然,图书馆(及其历史)不应包含我们项目的痕迹。

git-subtree在这里似乎是一个解决方案,但它并不完全适合。

我的目录布局是这样的(因为它是一个Java项目):

  • 击剑游戏(git workdir)
    • SRC
        • fencing_game
          • 传输 (我的图书馆)
            • 协议 (图书馆的一部分)
            • fencing (与库连接的主项目的一部分)
            • client (与库连接的主项目的一部分)
            • server (与库连接的主项目的一部分)
          • client (主要项目的一部分)
          • server (主项目的一部分)
          • ... (主要项目的一部分)
    • 其他文件和目录(构建系统,网站等 - 主项目的一部分)

拆分后,我希望库的目录布局如下所示(包括直接在粗体目录中的任何文件):

  • my-library (名称待定)
    • SRC
        • fencing_game
          • 传输 (我的图书馆)
            • 协议 (图书馆的一部分)

历史记录还应该只包含主要项目历史记录的一部分,该部分涉及存储库的这一部分。

第一眼看到我git-subtree split --prefix=src/de/fencing_ame/transport,但这将

  1. 给我一棵植根于transport(不会编译)和
  2. 的树
  3. 包含transport/clienttransport/servertransport/fencing目录。
  4. 第一点可以通过在接收方使用git subtree add --prefix=src/de/fencing_ame/transport <commit>来缓解,但我不认为git-subtree可以对导出这些子目录做很多事情。 (这个想法真的是只能在这里分享完整的树。)

    我必须在这里使用git filter-branch吗?

    在拆分之后,我希望能够导入我的主项目中的库,使用git-subtree或git-submodule,在一个单独的子目录中而不是现在的位置。我想象这样的布局

    • 击剑游戏(git workdir)
      • SRC
          • fencing_game
            • transport (空)
              • fencing (与库连接的主项目的一部分)
              • client (与库连接的主项目的一部分)
              • server (与库连接的主项目的一部分)
            • client (主要项目的一部分)
            • server (主项目的一部分)
            • ... (主要项目的一部分)
      • 我的库
        • SRC
            • fencing_game
              • 传输 (我的图书馆)
                • 协议 (图书馆的一部分)
      • 其他文件和目录(构建系统,网站等 - 主项目的一部分)
    这样做最无痛苦的方法是什么?除了git-subtree和git-filter-branch之外还有其他工具可用于此目标吗?

5 个答案:

答案 0 :(得分:3)

我认为你有一些真正的洞察力要做。如果您只想拆分“协议”,可以使用“git subtree split ...”或“git filter-branch ...”

git filter-branch --subdirectory-filter fencing-game/src/de/fencing_game/transport/protocol -- --all

但如果您在传输以及传输/协议中有文件,它就会变得毛茸茸。

我为我正在进行的项目编写了一些自定义工具。它们不会在任何地方发布,但您可以使用reposurgeon执行类似的操作。

答案 1 :(得分:3)

拆分与父项目

中的文件混合的子树

这似乎是一个常见的请求,但是当文件夹混合在一起时,我认为没有 简单的 答案。

我建议拆分与其他文件夹混合的库的一般方法是:

  1. 使用库的新根创建分支:

    git subtree split -P src/de/fencing_game -b temp-br
    git checkout temp-br
    
    # -or-, if you really want to keep the full path:
    
    git checkout -b temp-br
    cd src/de/fencing_game
    
  2. 然后使用某些东西重写历史记录以删除不属于库的部分。我不是这方面的专家,但我能够尝试并找到这样的工作:

    git filter-branch --tag-name-filter cat --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch client server otherstuff' HEAD
    
    # also clear out stuff from the sub dir
    cd transport 
    git filter-branch --tag-name-filter cat --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch fencing client server' HEAD
    

    注意:您可能需要删除连续命令之间filter-branch所做的备份。

    git update-ref -d refs/original/refs/heads/temp-br
    
  3. 最后,只需为库创建一个新的仓库并拉入剩下的所有内容:

    cd <new-lib-repo>
    git init
    git pull <original-repo> temp-br
    
  4. 我建议您的最终库路径更像/transport/protocol而不是完整的父项目路径,因为这似乎与项目有关。

答案 2 :(得分:2)

这里的问题是,图书馆的内容与图书馆的内容并没有很好的分离。我强烈建议重构该解决方案,然后您可以将该库作为子模块包含在内。

如果此库的重用仅由其他开发人员在同一个repo中,则只需在单独的分支上跟踪这些更改,而不必担心额外的repos。

答案 3 :(得分:1)

项目的历史是仅为了您的利益,还是为了github上的人的利益?

如果历史记录仅供您使用,那么使用移植物的方法很简单。基本上,只需为github创建一个全新的存储库,删除所有专有代码。现在你有一个只有公共代码的开源仓库,你可以推送到github。在您的本地开源仓库副本中,您可以将专有仓库的历史记录移植到开源仓库中。

这样做意味着您(或任何有权访问专有仓库的人)都可以看到完整的历史记录,但普通公众只能从您开源的那一点看到代码。

What are .git/info/grafts for?

答案 4 :(得分:1)

我做了类似的事情,但是在加密分区(/ secure / tmp / newrepo)上将几个东西拆分成一个完全独立的仓库,因此笔记本电脑小偷无法使用它们。 我写了shell脚本然后做了: git filter-branch --tree-filter'~ / bin / tryit / secure / tmp / newrepo personal private' - 95768021ff00216855868d12556137115b2789610..HEAD (SHA避免在任一目录存在之前提交)


#!/bin/sh
# to be used with  e.g:
# git filter-branch --tree-filter '~/bin/tryit /secure/tmp/newrepo personal private' 
# Don't do it on any repository you can't repeatedly do: 
#   rm -rf foo ; git clone /wherever/is/foo 
# when it breaks
SRC=`pwd`
DEST=$1
shift
MSG=/dev/shm/msg.txt
TAR=/dev/shm/tmp.tar
LIST=/dev/shm/list.txt
LOG=/dev/shm/log
DONE=''

echo $GIT_AUTHOR_DATE >> $LOG
git show --raw $GIT_COMMIT > $MSG 

for A in $* 
do 

if [ -d $A ] 
then 
DONE=${DONE}x
tar -cf $TAR $A 
tar -tf $TAR > ${LIST}
cat ${LIST} >> ${LOG}
rm -rf ${A}
cd ${DEST}
tar -xf $TAR
else
echo $A non-existant >> ${LOG}
fi
cd $SRC
done

if [ -z "${DONE}" ]
then
echo Empty >>$LOG
else
cd ${DEST}
unset GIT_INDEX_FILE
unset GIT_DIR
unset GIT_COMMIT
unset GIT_WORK_TREE
touch foo
git add .
git commit -a -F ${MSG}  >> ${LOG}
fi
exit 0

出于您的目的,您可能希望为tar设置不同的规范(例如--exclude =),然后使用cat $ {LIST} | xargs rm只能删除tar中的内容,但是我希望这样做并不是太棘手。

未设置的东西和退出0很重要,因为filter-branch将它们设置为你的源代码(不是你想要的!),如果sh传递了脚本中最后一个命令的非零退出代码,它将会死掉。