I use git diff mainline
This form of git diff compares the given commit—the hash ID will be the result of running:
git rev-parse mainline
which may well be quite different from that of:
git rev-parse origin/mainline
—to the current work-tree. More precisely, it compares the saved snapshot in the given commit, to the current work-tree (the files you can see).
I use git diff COMMIT_HASH_1 COMMIT_HASH_2
This form of git diff compares the saved snapshot in the first commit to that in the second.
Given these fragments of output from git log:
commit COMMIT_HASH_1 (HEAD -> local_tracking_branch)
commit COMMIT_HASH_2 (origin/mainline, origin/HEAD)
we can predict that mainline resolves to a third commit hash. We cannot say for sure whether your current work-tree matches the saved snapshot in COMMIT_HASH_1, but if git status says that everything is "clean" and there is nothing to commit, it probably does match that.
So the most likely source of all the oddness is that mainline refers to some third commit whose snapshot is not much like the snapshot in the commit to which the name origin/mainline resolves. (You already have that hash ID, but to see it again, by itself, use the earlier-mentioned git rev-parse origin/mainline.) But another possibility is that the current work-tree is different from any and/or all of these other commits. Since the work-tree consists of ordinary files, any of your ordinary (non-Git) commands can make any arbitrary changes they like to these ordinary files.
A another scenario is that once I push the feature into remote mainline, is it ok that I could use second approach git diff commmit-hash1 commit-hash2 to generate the valid patch?
You can do this any time you like. How valid it is depends on who has which commit and what they want to do with those commits.
The thing to keep in mind is that names can change over time. A name like mainline or origin/mainline refers to some commit by hash ID. You can find out which one they name right now using git rev-parse. Tomorrow, after people do things with branches, they may refer to different commit hash IDs.
The hash IDs, however, remain fixed forever. Given any particular hash ID, that hash ID represents that commit. No other commit can ever have that hash ID. The snapshot for that commit is frozen for all time; that hash ID will always represent those files, in that form.
Hash IDs are long and ugly and random-looking and very hard to type in. That's one reason we use names. Branch names, however, change over time. That's one reason we use tag names: tag names are designed to be stable, to not-change over time. Use a commit hash; or, use a tag name to specify one particular commit, and don't move tag names around manually (Git won't move them on its own) and you have a steady way to refer to that particular commit.
git format-patch.