Mercurial is basically "git without an index", and as such, is proof that it can be done. Any "why" question falls into a lot of grey areas. But this is what the index provides:
Very fast "git commit": the index already contains the next commit; it merely needs to be re-formatted into tree objects and a final commit object.
A staging area, so that you can make the commit not match the working directory. (Not everyone believes this is a good thing.) In Mercurial, to commit files A and C while omitting the work version of B, you must issue the commit command with the names of all the files to include (or exclude) spelled out all at once. In git, you can set up a stage, run git diff --cached (or --staged), decide if it's right or needs tweaking, git add or git reset to adjust the stage, run another git diff --cached, and so on. (In Mercurial I've found it's by far easiest to achieve the same thing by moving all the "unwanted" changes out of the repository area, hg commit, then move those changes back.)
Ease of amending unpublished commits. Having gotten used to the staging area and amending process in git, when I did an amend in Mercurial I was surprised to find my entire current work tree became the new amended commit. (I should not have been surprised, but I was! Again this goes back to the different philosophy: in hg, you move the "not ready yet" parts out of the repo entirely, lest they sneak in.)
Tricky hacks. (Again, not everyone believes this is a good thing.) In particular you can set bits in the index like "assume unchanged" or "intent to add", which affect future commits (again because the index is, in a sense, "the next commit being built").
A way to hold on to, and hence easily access, files being merged, when there is a merge conflict.
That last one deserves some extra explanation. Suppose you are merging old-fix into feature, and in feature, a file was renamed FA. The merge realizes that it needs to get the file (under its old name) from branch old-fix, and FA from feature, and merge them. But there is a merge conflict, and your version system stops and needs your help in completing the merge.
Now suppose you want to look at the version of FA in old-fix, comparing it to FA in feature. If you literally check out branch old-fix, there is no file named FA! But git stores it in the index so that you can see it, without having to know what the old name was, since the index is constructing the next commit (which retains the new FA name).
You can also look at the feature version, although of course that's easier since you know it's named FA. But it's there in the index. In addition, the common (base) version (which is also under the same old name as in old-fix) is in the index, as noted in gitrevisions:
A colon, optionally followed by a stage number (0 to 3) and a colon, followed by a path, names a blob object in the index at the given path. A missing stage number (and the colon that follows it) names a stage 0 entry. During a merge, stage 1 is the common ancestor, stage 2 is the target branch's version (typically the current branch), and stage 3 is the version from the branch which is being merged.
That is, :1:FA is the common ancestor for file FA, :2:FA is feature's version, and :3:FA is old-fix's version.
All of this fine control results in some mistakes for beginners (or even experts, sometimes) so Mercurial's index-less version may suit your work better. However, using git commit -a, you get essentially the same behavior as with Mercurial, so you can often ignore it until you want it.
git merge some-branchalready detects merge conflicts, it could flag something internally to indicating a conflict and prevent committing until it is "unflagged internally" (e.g.,git resolve some-file).