I often need to update and push some images with git but the problem is that if the changes are minimal (e.g. change of a letter from 'C' to 'K' in the image content), git doesn't detect that something changed (the size of the image stays the same but the content is different). So, I cannot add the images that changed with git add... and when I push, it say that "Everything up-to-date". Is there a way to force files not detected to be pushed?
CodePudding user response:
The git push
command does not push files. That's because Git does not store files—not at this level, anyway. Git is all about commits, and Git stores commits, not files. The commits then store files, so you might ask what's the difference ... and the difference is a lot. In fact, it explains about half the problems people have with Git.
You need to know this about commits:
Commits are numbered. Each commit gets a unique hash ID, which is a big ugly random-looking number expressed in hexadecimal. When I say unique, I mean it: the number your new commit gets, when you make a new commit, is not allowed to be used for any other commit in any Git repository ever.1
Commits are made up of two parts:
Each commit stores a full copy of every file. These full copies are compressed and de-duplicated, which keeps the repository from getting bloated and making Git unusable. Unfortunately, as Daniel Mann noted in a comment, large binaries interfere with this and the repository may become unmanageable, so try to avoid storing images in Git if possible.
Each commit stores some metadata, such as the name and email address of the person who made the commit. We won't go into detail here, but this metadata is crucial to Git's own internal operation.
Commits—and all other internal Git objects—are completely immutable. Once you make a commit, it's stuck that way forever. That hash ID means that commit. If you make a mistake in a commit, you can correct it with
git commit --amend
, but the--amend
is a lie: what you're doing is making a new replacement commit, shoving the old commit aside. Both commits remain in the repository for a while. If nothing uses the old commit (and nothing does), it eventually "falls out" of the repository. It doesn't normally get sent to any other Git either. And with the de-duplication, each commit tends to be tiny: well under a disk "block", except for any all-new files (including image binaries). So it's not really a big deal to have a few extra junk ones.
There's more to know, but the above will serve for now. Because commits are immutable snapshots of files plus some metadata, you don't actually work on a commit. Instead, you have Git extract the files from the commit, into a work area. Git calls this your working tree. Here you actually do have files—real, ordinary files, not the weird Git-ized "blob objects" that aren't really files that Git uses to store the files in commits—that you can use and work with.
When you modify or replace one of these working tree files, you use git add
to tell Git that you've done so. This does not use the size of the file as an indicator that the file is changed.2 It uses instead a whole bunch more metadata about the file as it existed at the time Git extracted it from the commit. If any of that metadata have changed, Git will see the file as changed, and will be willing to update the file in its (Git's) index.
If Git doesn't update the file in its index, you've hit one of several possible conditions:
The file isn't in the index now, and should not be in the index. Running
git check-ignore -v path/to/file.ext
will tell you why Git thinks this is the case. Runninggit ls-files --stage path/to/file.ext
will tell you about the copy of the file that is in the index; if it says nothing, the file isn't in the index.The file is in the index, but is marked with
--skip-worktree
or--assume-unchanged
. You have to do this yourself unless you're using Git's "sparse checkout" mode, so you'd probably know if you had set these. Technically, setting either flag manually is usually an abuse of the purpose of these flags, but people do abuse them for various effects, so it's worth mentioning here.Some software you're using is fiddling with the file metadata, for some reason. That's probably a bad idea, but software that does this can fool Git.
You've found a bug in Git. This is pretty unlikely, but needs to be mentioned as a possibility, especially with the Git 2.34.0 bug with
.gitignore
.