How to extract a git subdirectory and make a submodule out of it?

前端 未结 5 806
别跟我提以往
别跟我提以往 2020-11-27 09:07

I started a project some months ago and stored everything within a main directory. In my main directory \"Project\" there are several subdirectories containing different thi

5条回答
  •  死守一世寂寞
    2020-11-27 09:52

    One way of doing this is the inverse - remove everything but the file you want to keep.

    Basically, make a copy of the repository, then use git filter-branch to remove everything but the file/folders you want to keep.

    For example, I have a project from which I wish to extract the file tvnamer.py to a new repository:

    git filter-branch --tree-filter 'for f in *; do if [ $f != "tvnamer.py" ]; then rm -rf $f; fi; done' HEAD
    

    That uses git filter-branch --tree-filter to go through each commit, run the command and recommit the resulting directories content. This is extremely destructive (so you should only do this on a copy of your repository!), and can take a while (about 1 minute on a repository with 300 commits and about 20 files)

    The above command just runs the following shell-script on each revision, which you'd have to modify of course (to make it exclude your sub-directory instead of tvnamer.py):

    for f in *; do
        if [ $f != "tvnamer.py" ]; then
            rm -rf $f;
        fi;
    done
    

    The biggest obvious problem is it leaves all commit messages, even if they are unrelated to the remaining file. The script git-remove-empty-commits, fixes this..

    git filter-branch --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; fi'
    

    You need to use the -f force argument run filter-branch again with anything in refs/original/ (which basically a backup)

    Of course this will never be perfect, for example if your commit messages mention other files, but it's about as close a git current allows (as far as I'm aware anyway).

    Again, only ever run this on a copy of your repository! - but in summary, to remove all files but "thisismyfilename.txt":

    git filter-branch --tree-filter 'for f in *; do if [ $f != "thisismyfilename.txt" ]; then rm -rf $f; fi; done' HEAD
    git filter-branch -f --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; fi'
    

提交回复
热议问题