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
- add
index.html
- add
website.css
andoops.iso
- add
website.js
and deleteoops.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 togit 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
andGIT_COMMITTER_DATE
surroundings variables or simplyexit
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 awayoops.iso
wherever it’s discovered, nevertheless it isn’t current in all commits. The commandgit 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 ofcat
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 togit 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