Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Corrupted images in git repository

Tags:

git

A colleague made a git commit after changing permissions on the project's files (he wasn't expecting them to be commited).

Now all our images are "corrupted" unreadable, but we don't understand how, and what to do.

Here is what git show edd703783a8802284fb975e9f354394541f1bad5 displays:

diff --git a/public/bootstrap/img/glyphicons-halflings-white.png b/public/bootstrap/img/glyphicons-halflings-white.png
index 3bf6484..c016d6b 100644
Binary files a/public/bootstrap/img/glyphicons-halflings-white.png and b/public/bootstrap/img/glyphicons-halflings-white.png differ

What does it mean?

We tried removing and re-commiting the images but the problem remains. As soon as we switch/reswitch on the branch, the images are corrupted.

I even tried to git revert the commit, but it puts all the image files in an updated state and stops with the following error:

error: 'commit' is not possible because you have unmerged files.
hint: Fix them up in the work tree,
hint: and then use 'git add/rm <file>' as
hint: appropriate to mark resolution and make a commit,
hint: or use 'git commit -a'.
fatal: Exiting because of an unresolved conflict.

Now I can't revert it, and the commit has been pushed a few days ago (and deployed to dev environment), so I can't rebase or something similar.

like image 833
Matthieu Napoli Avatar asked Jul 09 '13 09:07

Matthieu Napoli


People also ask

Does Git work with images?

Bringing files in to a repository, such as code, images, or documents, allows them to be tracked by Git, even though they may have been created elsewhere. You can add a file to a repository in your terminal, and then push to GitLab.


1 Answers

diff --git a/public/bootstrap/img/glyphicons-halflings-white.png b/public/bootstrap/img/glyphicons-halflings-white.png
index 3bf6484..c016d6b 100644
Binary files a/public/bootstrap/img/glyphicons-halflings-white.png and b/public/bootstrap/img/glyphicons-halflings-white.png differ

This simply means that glyphicons-halflings-white.png was altered in that commit, but the contents are binary and are not suitable for display in a terminal. As a result, it omits any actual diff to avoid corrupting your terminal (the terminal interprets some codes, so you don't want to stream raw data to it).

The next bit is more interesting:

error: 'commit' is not possible because you have unmerged files.
hint: Fix them up in the work tree,
hint: and then use 'git add/rm <file>' as
hint: appropriate to mark resolution and make a commit,
hint: or use 'git commit -a'.
fatal: Exiting because of an unresolved conflict.

This means you attempted a merge and the file had conflicts. This happens because the version the change was based on is not the same as the version in the tip of the branch you're merging into. Graphically, it would look like this

                       C
         .-------------*------------.
        /                            \
*------*---------------*--------------+
A      B               D              E

So, let's say you had a feature branch that updated these icons (commit C) that was based on commit B. Then someone else landed a change on master that altered the same file, say commit D. Now when you attempt to merge, commit C and D are conflicting because they touched the same binary file. Git doesn't understand binary formats and so doesn't know how to merge them together. You, as the user, need to resolve the conflict and then add and commit the result. That means you need to examine all three versions: the one based on commit B (the merge base), the one based on commit C (MERGE_HEAD/your version), and the one based on commit D (HEAD/version on master). You can see each of these versions by doing this:

git show :1:path/to/file.ext > file.merge-base.ext    # The merge base (commit B)
git show :2:path/to/file.ext > file.HEAD.ext          # The version on the branch you're merging into (commit D)
git show :3:path/to/file.ext > file.MERGE_HEAD.ext    # The version on your branch (commit C)

The commands for getting at these different version is documented in the git merge man page. For a little more information on the staging syntax, you can look at the Specifying Revisions section of the gitrevisions man page.

You can try restoring the original versions before that commit. It would look like:

git show edd703783a8802284fb975e9f354394541f1bad5~1:public/bootstrap/img/glyphicons-halflings-white.png > public/bootstrap/img/glyphicons-halflings-white.png
git commit -M "Revert the last set of changes made to icons." public/bootstrap/img/glyphicons-halflings-white.png

when run from the top of your tree. You can also choose to keep your version with:

git checkout --ours  # Keeps the version on master (commit D, the :2: version)

Or,

git checkout --theirs  # Keeps the version from your branch (commit C, the :3: version)

You then need to add the file and commit to resolve the merge.

Finally, if you don't already have a line like this in your .gitattributes file, you may want to consider it:

*.png    binary

This means to treat binary files as binary. Git won't attempt to merge the file contents. Normally git does binary detection by looking for a NUL character in the first 100 bytes (IIRC). I don't remember the PNG format, but perhaps it's possible that there isn't guaranteed to be one in the first 100 bytes, so git may attempt to merge the contents of the file and insert conflict markers. That would corrupt the image in a way that it can't be viewed by image programs or the browser, and I suspect it's what happened to you. The above line in your .gitattributes file will make sure that git doesn't insert conflict markers, and it will avoid trying to merge uncommitted changes between branches to .png files.

like image 160
John Szakmeister Avatar answered Sep 30 '22 12:09

John Szakmeister