2

How to remove a folder and its contents from Initial commit?

I have an initial commit having several directories. Then a lot commits based on it.

Example:

Commit A, having the following directories & files:
A/aaa.txt
B/bbb.txt
C/ccc.txt

A - B - C - D - E - F

I would like to delete directory B & C from initial commit A. Then - create B1 branch with B/bbb.txt. - create C1 branch with C/ccc.txt.

So finally the tree would look like this:

A(modified) - B1 - C1 - B - C - D - E - F

How to get this result?

Thanks!

EDIT1: I would like to remove the directory only from the Initial commit. Thus I think it is different from the other question, and not duplicate.

klor
  • 1,237
  • 4
  • 12
  • 37
  • 1
    Use `git filter-branch` as described in [this answer](https://stackoverflow.com/a/2158271/1440565). – Code-Apprentice Jan 06 '18 at 17:10
  • 1
    Possible duplicate of [How to remove/delete a large file from commit history in Git repository?](https://stackoverflow.com/questions/2100907/how-to-remove-delete-a-large-file-from-commit-history-in-git-repository) – Code-Apprentice Jan 06 '18 at 17:10
  • 1
    https://git-scm.com/book/id/v2/Git-Tools-Rewriting-History under: Removing a File from Every Commit – Stav Alfi Jan 06 '18 at 17:11

3 Answers3

3

You cannot modify any existing commit. This is a fundamental Git guarantee: any content, once saved, is saved that way forever, by the hash ID generated when saving it.

You can simply stop using any existing commit(s), and of course you can always make new commits. Hence you'll need to make a new commit that's almost the same as commit A, but not quite exactly the same:

  • extract existing commit A;
  • remove the unwanted files;
  • and last, make a new root commit A' that's a copy of A in most ways, except for the fact that those files are now gone.

You now have:

A--B--C--D--E--F   <-- old-master

A'   <-- new-master

Of course, that's not quite what you need, because you still want the other commits. So now you must copy B to B' as well. The—or at least a—difference between B and the new B' is that B''s parent is A', not A. The existing B has the existing A as its parent, and you cannot change any existing commit.

So now you extract B (and perhaps remove those same files again, if that's part of your goal), then make new commit B' whose parent is A':

A--B--C--D--E--F   <-- old-master

A'-B'   <-- new-master

Obviously, you're still not done yet. You must now copy C to C' in the same way you copied B to B', then repeat for the rest of the commits. Eventually you wind up with this:

A--B--C--D--E--F   <-- old-master

A'-B'-C'-D'-E'-F'  <-- new-master

and now you can use the new chain of commits as master and you are done (well, except for creating the new branch(es), which must be done separately—you drew them as having B attached to the last commit in the new branch, but I suspect you didn't quite mean it that way).

There is a Git command that does exactly this: git filter-branch. The git filter-branch command takes the branch(es) you name on the command line, finds every commit reachable from those names (hence F then E then D then ... then A), puts them into the right order (A first, etc), and begins copying:

  • extract original commit;
  • apply each filter, in the order listed in the documentation;
  • make a new commit whose parent(s) is/are the parent(s) made earlier during the filtering process (or for an original root, make a new root commit).

Once all the specified commits have been copied, git filter-branch adjusts the branch names to point to the final copied commits.

Typically, to do an entire repository, you use --all to copy all commits and adjust all branch names, along with --tag-name-filter cat to adjust all tag names. You can use the --tree-filter as it's the simplest: it lets you use ordinary file system operations on ordinary files, to manipulate the contents of each commit. But it's also the slowest filter, by far; so you may want to experiment until you can do the file-removal with the fastest filter, which is the --index-filter.

(You'll still need to build the new branch separately, as git filter-branch only changes any existing branch names, and builds a map of old-to-new commit hashes as it goes. It—unlike the BFG, which in some ways is better-designed—removes this map when it is done, but for your particular purposes you won't need it, you can just find the new root commit. It's wisest to experiment on a clone of the repository, probably a git clone --mirror clone.)

torek
  • 448,244
  • 59
  • 642
  • 775
1

Try rebasing from root using interactive mode:

git rebase --interactive --root 

And then use edit command on the first commit.

Gonzalo Matheu
  • 8,984
  • 5
  • 35
  • 58
  • Rebasing interactively will make possible only to edit the comment, but will not allow to delete any directory. – klor Jan 06 '18 at 17:13
  • @klor: Git doesn't save directories, it only saves files. Later, when you *extract* the commit, if Git must, it will create a directory to hold them. If you remove all the files within a directory, and make a new commit from that, you get a commit that doesn't force Git to create the directory to extract the files. – torek Jan 06 '18 at 17:14
  • 1
    You can edit the commit contents using *edit* instead of *pick* or *reword*. Something like this is explained in Splitting Commit section -> https://git-scm.com/docs/git-rebase#_splitting_commits – Gonzalo Matheu Jan 06 '18 at 17:23
-1

You can't just remove a directory from an old commit. If you are several commits after the initial I think the best thing to do is:

  • create a new branch for every directory
  • work on the directory in its branch
  • merge the updates on master branch

If you were on the commit right after the initial, you could do a git reset --soft to go to the initial commit and keep your changes.

Miso Mijatovic
  • 357
  • 3
  • 7
  • It is a big project. I have a lot directories, a lot commits, can not create new branch for each directory. I remember, there is some history rewrite feature, which might be used. Don't know, if it is relevant for this case. – klor Jan 06 '18 at 16:54
  • ok didn't know that, unfortunately you can't remove dirs from old commits, maybe someone else has an answer for you :) – Miso Mijatovic Jan 06 '18 at 17:23