model management – How can I take away/delete a big file from the commit historical past within the Git repository?

model management – How can I take away/delete a big file from the commit historical past within the Git repository?


First repair your native historical past. You will have a number of choices that fluctuate in ease of use relying on how gnarly your historical past is between HEAD and the commit with the unintended rip.

  • git reset --soft
  • git rebase --interactive
  • git commit-tree
  • git filter-repo
  • git filter-branch (are likely to keep away from this one)

When you pushed the historical past with the rip, it’s possible you’ll want to repair historical past on a shared repository (deleting and re-pushing a department or git push --force), and your collaborators should realign their work with the rewritten historical past.

You may additionally discover “Eradicating delicate information from a repository” from GitHub to be a useful useful resource.

I’ll illustrate attainable fixes utilizing concrete instance historical past that simulates a easy consultant sequence of

  1. add index.html
  2. add website.css and oops.iso
  3. add website.js and delete oops.iso

To recreate the precise SHA-1 hashes from this instance in your setup, first set a few surroundings variables. When you’re utilizing bash

export GIT_AUTHOR_DATE="Mon Oct 29 10:15:31 2018 +0900"
export GIT_COMMITTER_DATE="${GIT_AUTHOR_DATE}"

When you’re operating within the Home windows command shell

set GIT_AUTHOR_DATE=Mon Oct 29 10:15:31 2018 +0900
set GIT_COMMITTER_DATE=%GIT_AUTHOR_DATE%

Then run the code beneath. To get again to the identical place to begin after experimenting, delete the repository, and rerun the code.

#! /usr/bin/env perl

use strict;
use warnings;
use Fcntl;

sub contact O_CREAT and shut FH or die "$0: contact $_: $!" for @_; 1 

my $repo = 'website-project';
mkdir $repo or die "$0: mkdir: $!";
chdir $repo or die "$0: chdir: $!";
system(q/git init --initial-branch=predominant --quiet/) == 0       or die "git init failed";
system(q/git config consumer.title 'Git Person'/) == 0              or die "consumer.title failed";
system(q/git config consumer.e-mail '[email protected]'/) == 0 or die "consumer.e-mail failed";
# for looking historical past - http://weblog.kfish.org/2010/04/git-lola.html
system "git config alias.lol  'log --graph --decorate --pretty=oneline --abbrev-commit'";
system "git config alias.lola 'log --graph --decorate --pretty=oneline --abbrev-commit --all'";

my($index,$oops,$css,$js) = qw/ index.html oops.iso website.css website.js /;
contact $index or die "contact: $!";
system("git add .")          == 0 or die "A: add failedn";
system("git commit -m A")    == 0 or die "A: commit failedn";
contact $oops, $css or die "contact: $!";
system("git add .")          == 0 or die "B: add failedn";
system("git commit -m B")    == 0 or die "B: commit failedn";
unlink $oops or die "C: unlink: $!"; contact $js or die "C: contact: $!";
system("git add .")          == 0 or die "C: add failedn";
system("git commit -a -m C") == 0 or die "C: commit failedn";

system("git lol --name-status --no-renames");

The output reveals that the repository’s construction is

* 1982cb8 (HEAD -> predominant) C
| D oops.iso
| A website.js
* 6e90708 B
| A oops.iso
| A website.css
* d29f991 A
  A index.html

Notes

  • The --no-renames choice to git lol is there to disable rename detection in order that git doesn’t see deleting one empty file and including one other as a rename. You gained’t want it more often than not.
  • Likewise, while you’re performed messing round with this instance repository, keep in mind to delete the GIT_AUTHOR_DATE and GIT_COMMITTER_DATE surroundings variables or simply exit the shell that you just have been utilizing to observe alongside.
  • Take into account stopping future unintended pickup of DVD rips by updating your .gitignore.

When you haven’t but printed your historical past, then you may repair it and be performed. A number of approaches will do what you need.
 

git reset --soft

To maintain all the things (file contents and commit messages) besides the rip, first transfer HEAD again to the commit instantly earlier than the one with the DVD rip and faux you probably did it appropriately the primary time.

git reset --soft d29f991

The precise invocation will rely in your native historical past. On this explicit case, you may comfortable reset to HEAD~2 however blindly parroting it will produce complicated outcomes when your historical past has completely different form.

After that add the recordsdata you need to preserve. The comfortable reset left the recordsdata in your working tree and index untouched, so oops.iso might be gone.

git add website.css website.js

You could possibly get away with git add ., significantly in the event you up to date your .gitignore. That’s what in all probability received you into hassle within the first place, so simply in case, run git standing first after which

git commit -q -C ORIG_HEAD

The comfortable reset retains a “bookmark” at ORIG_HEAD, so -C ORIG_HEAD makes use of its commit message.

Operating git lol --name-status --no-renames from right here provides

* a19013d (HEAD -> predominant) C
| A website.css
| A website.js
* d29f991 A
  A index.html

git rebase --interactive

To perform the identical as above however guiding git alongside, use interactive rebase.

git rebase --interactive d29f991

You’ll then see an editor with

choose 6e90708 B
choose 1982cb8 C

# Rebase d29f991..1982cb8 onto d29f991 (2 instructions)
#
# Instructions:
# p, choose  = use commit
# r, reword  = use commit, however edit the commit message
# e, edit  = use commit, however cease for amending
# s, squash  = use commit, however meld into earlier commit
# f, fixup [-C | -c]  = like "squash" however preserve solely the earlier
#                    commit's log message, except -C is used, during which case
#                    preserve solely this commit's message; -c is identical as -C however
#                    opens the editor
# x, exec  = run command (the remainder of the road) utilizing shell
# b, break = cease right here (proceed rebase later with 'git rebase --continue')
# d, drop  = take away commit
# l, label 

Change choose to squash on the C line. Bear in mind: with interactive rebase, you at all times “squash upward,” by no means downward.

Because the useful feedback beneath point out, you may change the command for the B line to reword and edit the commit message proper there if it’s easy. In any other case, save and give up the editor to get one other editor for the commit message of the results of squashing B and C.

git commit-tree

You may be tempted to do it with git rebase --onto, however this isn’t the equal of a squash. Specifically, if the commit during which you by accident added the rip additionally comprises different work that you just do need to preserve, the rebase will replay solely the commits after it, so website.css wouldn’t come alongside for the experience.

Impress your mates at events by performing a squash with git plumbing.

git reset --soft d29f991
git merge --ff-only 
  $(git commit-tree 1982cb8^{tree} -p d29f991 
      -F <(git log --format=%s -n 1 1982cb8))

Afterward, the historical past is similar to the others.

* a19013d (HEAD -> predominant) C
| A website.css
| A website.js
* d29f991 A
  A index.html

In English, the instructions above create a brand new commit whose tree is similar to what you bought after deleting the rip (1982cb8^{tree} on this case) however whose dad or mum is d29f991, after which fast-forward your present department to that new commit.

Notice that in precise utilization, you’ll probably need a fairly format of %B for the entire physique of the commit message somewhat than simply %s for its topic.

git filter-repo

The command beneath removes oop.iso wherever it reveals up in your historical past.

Create a recent clone of your repository and cd into its root. The illustration repository gained’t appear like a recent clone, so we’ve so as to add the --force choice to the command beneath.

git filter-repo --invert-paths --path oops.iso

The ensuing historical past is

* f6c1006 (HEAD -> predominant) C
| A website.js
* f2498a6 B
| A website.css
* d29f991 A
  A index.html

When you did run git push, then you are able to do one of many above, however it’s essential to rewrite historical past.

You will want to both run git push with the --force choice to overwrite the department in your distant or delete the department and push it once more. Both of those choices could require help out of your distant repository’s proprietor or administrator.

That is sadly extremely disruptive to your collaborators. See “Recovering From Upstream Rebase” within the git rebase documentation for the required steps that everybody else should do after repairing historical past.

This legacy command is saved round for historic cause, nevertheless it’s sluggish and difficult to make use of appropriately. Go this route as a final resort solely.

I had an identical drawback with cumbersome binary check information from a Subversion import and wrote about eradicating information from a git repository.

Executing the next command

git filter-branch --prune-empty -d /dev/shm/scratch 
  --index-filter "git rm --cached -f --ignore-unmatch oops.iso" 
  --tag-name-filter cat -- --all

will produce output of

WARNING: git-filter-branch has a glut of gotchas producing mangled historical past
     rewrites.  Hit Ctrl-C earlier than continuing to abort, then use an
     various filtering software reminiscent of 'git filter-repo'
     (https://github.com/newren/git-filter-repo/) as an alternative.  See the
     filter-branch handbook web page for extra particulars; to squelch this warning,
     set FILTER_BRANCH_SQUELCH_WARNING=1.
Continuing with filter-branch...

Rewrite 6e907087c76e33fdabe329da7e0faebde165f2c2 (2/3) (0 seconds handed, remaining 0 predicted)    rm 'oops.iso'
Rewrite 1982cb83f26aa3a66f8d9aa61d2ad08a61d3afd8 (3/3) (0 seconds handed, remaining 0 predicted)    
Ref 'refs/heads/predominant' was rewritten

The meanings of the varied choices are:

  • --prune-empty removes commits that develop into empty (i.e., don’t change the tree) because of the filter operation. Within the typical case, this feature produces a cleaner historical past.
  • -d names a brief listing that doesn’t but exist to make use of for constructing the filtered historical past. In case you are operating on a contemporary Linux distribution, specifying a tree in /dev/shm will lead to sooner execution.
  • --index-filter is the primary occasion and runs in opposition to the index at every step within the historical past. You need to take away oops.iso wherever it’s discovered, nevertheless it isn’t current in all commits. The command git rm --cached -f --ignore-unmatch oops.iso deletes the DVD-rip when it’s current and doesn’t fail in any other case.
  • --tag-name-filter describes methods to rewrite tag names. A filter of cat is the identification operation. Your repository, just like the pattern above, could not have any tags, however I included this feature for full generality.
  • -- specifies the top of choices to git filter-branch
  • --all following -- is shorthand for all refs. Your repository, just like the pattern above, could have just one ref (grasp), however I included this feature for full generality.

After some churning, the historical past is now:

* f6c1006 (HEAD -> predominant) C
| A website.js
* f2498a6 B
| A website.css
| * 1982cb8 (refs/unique/refs/heads/predominant) C
| | D   oops.iso
| | A   website.js
| * 6e90708 B
"https://stackoverflow.com/"   A   oops.iso
|   A   website.css
* d29f991 A
  A index.html

Discover that the brand new B commit provides solely website.css and that the brand new C commit solely provides website.js. The department labeled refs/unique/refs/heads/predominant comprises your unique commits in case you made a mistake. To take away it, observe the steps in “Guidelines for Shrinking a Repository.”

$ git update-ref -d refs/unique/refs/heads/predominant
$ git reflog expire --expire=now --all
$ git gc --prune=now

For a less complicated various, clone the repository to discard the undesirable bits.

$ cd ~/src
$ mv repo repo.previous
$ git clone file:///house/consumer/src/repo.previous repo

Utilizing a file:///... clone URL copies objects somewhat than creating hardlinks solely.

Now your historical past is:

* f6c1006 (HEAD -> predominant) C
| A website.js
* f2498a6 B
| A website.css
* d29f991 A
  A index.html

Leave a Reply

Your email address will not be published. Required fields are marked *