Preserve --no-ff merge commits with git filter-branch --subdirectory-filter

I have a git repository with the following structure:

.git/ project-root/ abc/ xyz/ 

And this is what I want:

 project-root/ .git abc/ xyz/ 

I studied how to achieve this, and this is what I have found so far: http://git-scm.com/docs/git-filter-branch

Accordingly, I have to achieve what I want with the following command:

 git filter-branch --subdirectory-filter project-root -- --all 

This overwrites the commit history, but most of the commits with immediate forward forward are lost (see the figure below).

Is there a way to keep these commits?

enter image description here

+4
source share
2 answers

I tried using the -tree filter instead of the -subdirectory-filter and it seems to work for me. This is the command I used:

 git filter-branch --tree-filter 'git mv -k project-root/{,.[!.],..?}* .' -- --all 

After executing this command, the commit history will be rewritten, as if "project-root" had never been tracked with git, and the branch topology is saved as I would like.

If someone tries to use this in windows and gets the "refused permission" error, killing the explorer.exe process can help (I found this workaround here: http://academe.co.uk/2011/12/git-mv -permission-denied / ).

Edit: The above command does not rewrite tags. To rewrite tags, the right command:

git filter-branch --tree-filter 'git mv -k project-root /{,►!.†,..??** ". --tag-name-filter cat - --all

0
source

As stated in the document here :

 --subdirectory-filter <directory> Only look at the history which touches the given subdirectory. The result will contain that directory (and only that) as its project root. Implies [Remap_to_ancestor]. 

I believe that the problem is that the merge commands do not actually touch this subdirectory, which means that the filter for subdirectories does not even look at them and therefore cannot save them. Therefore, we must use a different filter that analyzes each commit. tree-filter is perfect for this, but we need to write a script to do what we want to do with each commit.

Also, my problem was actually a little wider than in the example, since there were siblings in my project-root folder that I wanted to delete. The subdirectory filter will delete them, but for this, using the tree filter, it was useful for find come to the rescue.

It was convenient to run commands every time in a separate file.

for a repo structured as follows:

 .git/ someDirectory/ someFile.txt otherDirectory/ dontDeleteThisOne/ project-root/ xyz/ abc/ 

This is what worked for me:

 git filter-branch --tree-filter /absolute/path/to/filterScript.sh --tag-name-filter cat -- --all 

where / absolute / path / to / filterScript.sh is the executable script file containing this:

 #!/bin/bash #save the folder dontDeleteThisOne so it won't get deleted git mv -fk dontDeleteThisOne project-root && \ #remove everything not under project-root find . -maxdepth 1 -mindepth 1 '(' -name project-root -o -name '.git' ')' -prune -o -exec git rm -rf --ignore-unmatch '{}' ';' && \ #move the contents of the project-root directory up to root git mv -fk project-root/{,.[!.],..?}* .; 

The resulting repo structure is as follows:

 .git/ dontDeleteThisOne/ xyz/ abc/ 

This result is equivalent to the result of git filter-branch --subdirectory-filter project-root , EXCLUSIVELY that the merge of commits is saved in the history as desired.

Of course, this takes MUCH more than using a subdirectory filter ...

0
source

Source: https://habr.com/ru/post/1500067/


All Articles