There's one way I could see it happening. Let's say you have a feature branch named "feature". You merged "master" into "feature". So the graph looks something like:
---o---o---o master \ -o---o---o---* feature
If you the checkout "feature" and merge master, you'll likely get a fast-forward merge, since the new commit on "master" is directly ahead of the last commit in "feature". So the graph ends up looking like this:
---o---o---o \ -o---o---o---* master, feature
It's not an extra commit, it's just that "master" and "feature" now point at the same commit. FWIW, my team and I found this behavior confusing, so I've outlined what we do below to help prevent the issue.
Also, I'm not sure what you mean by using "gitk" to do your commits. As far as I'm aware, gitk is only for viewing. Do you mean "git gui"? I do recommend that you take a look at the graph with "gitk" though. I tend use something like the following:
gitk --date-order master feature
That will show a picture with the master and feature branches on there, and helps me to discern the relationship. If I see master and feature branch labels on the same commit, that's a good sign that I accidentally fast-forward merged feature onto my master branch.
If you're seeing something different, then posting a picture would be helpful. But I suspect it's this fast-forward merge on the feature branch that has happened. You can back up the feature branch a step with:
git checkout master git reset --hard HEAD^
You'll likely have to git push --force origin master. I recommend using this full form because Git's default push behavior was to push all local tracking branches which can result in rewinding the branches. Also, force pushing "master" isn't a good answer when working with others, so you'll need to coordinate with your team, if you are. The simple answer might be to just leave it as-is.
Depending on the situation, there may be more involved than that.
Also, if you want a merge commit, then you can do:
git checkout master git merge --no-ff feature
This will force a merge commit to be created even though it could be fast-forwarded. My team and I like this so much, that we made it the default. I explain this below.
EDIT: I flipped the sense around to match the question and expanded on some parts.
As an aside, since I used Bazaar with Subversion for a while, I became very sensitive who was the mainline (left-most) parent in the history. Git and it's fast-forward merges made this more difficult, and I found that my team really found it off-putting as well. So we ended up doing a few things.
First, we changed our configuration around so that merge always made a merge commit. You can do this from the command line with:
git config merge.ff false
Or globally, with:
git config --global merge.ff false
The next bit was that we do want fast forward merges when bringing in updates from upstream. So we have a couple of aliases to help:
[alias] # Fetch the remote and update the current branch. up = !git remote update -p && git merge --ff --ff-only @{u} # Fast-forward pull locally ff = !sh -c 'git merge --ff --ff-only ${1:-@\\{u\\}}' -
For the most part, the team has upstream branches set to where they like to push and pull from. So a git up would then fetch updates from the server and fast-forward the current branch. This was typically done on "master". git ff could be used to do something similar, but without the fetch step. It also made it easier to fast-forward another branch on top of yours, if you needed such a thing (it has come up on occasion).
Some of this was still more involved than I cared for, so I wrote git ffwd to help us. git ffwd will do the fetching, and then fast-forward all of my local branches that have equivalents on the remote. It also takes care of pruning your remotes when branches have been removed server-side, making it easier to groom the refs and keep them it from getting disorderly. git ffwd will do fast-forward merges only (git up did the same too), and will fail if the branches have diverged, so it ends being a good marker about something interesting happening and you need to step in and figure out the right course of action.
The net effect of all of this is that now when we type git merge foo, there will be a merge commit. Git will ask for a message, and if you're about to do the wrong thing, you can simply delete the contents of the buffer and save (causing Git to abort) or exit with :cq in Vim which is a little easier but causes the editor to exit with an error. Either way, you're a little more in control, and it's gone a long way towards making it much easier to track things and keep our history looking sharp.
One other note, we also set git config push.default upstream if we all work on the same project together in the same repo, or git config push.default current if we're going to be in separate repos. If you're on Git < 2.0, then it's important to set this setting, otherwise you might do something like git push -f on your master branch, but end up rewinding other branches you have locally that track remote branches. This is because in Git < 2.0, the default was matching and would push all of your remote tracking branches, and they were rarely up-to-date.
This is a bit long-winded, but I wanted to show you that there is another way to work that gives you a better shape, removes some of the confusion, and feels easier in the long run--at least it does for me and my team.
I have more of my configuration here, if you're interested.
masterto the commit before the merge commit (which normally hasmasterandfeatureprevious commits as parent).git reset --hardto that commit (possiblyHEAD~1, not quite sure right now) while being on the master branch should do.