Update: The poster changed the question after I composed this answer. This update answers to one of the questions the rest of the answer doesn't cover.
OK.. Now, weird thing is; now I call "git status"; it says I'm 4 commits ahead of the remote branch. How is this happening? Shouldn't I be only one commit ahead?
After you merged (--no-ff
) the commits A
and D
into E
, the local branch is 4 commits ahead of the remote branch (I suppose the remote branch still points to A
). This is correct.
Let's count the commits that are on the local branch (now pointing to E
) and aren't on the remote branch (pointing to A
). E
is obviously one of them. E
, being a merge commit, has two parents: A
(existing on the remote branch) and D
(not existing on the remote branch). When git
pushes E
to the remote server it needs to push both its parents (otherwise E
is invalid on the remote server).
D
requires C
(its parent) that requires B
that requires A
. A
already exists on the remote server and the chain ends here. B
, C
, D
and E
do not exist on the remote server. All of them must be pushed in order to keep the consistency of the data on the remote server.
The rest of the response treats the original question that was not very clear defined. The poster made references to git log --graph
and (probably) Atlassian Stash. Both these tools (and all the other git
interfaces I know) present the most recent commit first in the commit history. This answer uses the same convention.
git merge --no-ff
doesn't have any effect in the situation you described.
Having br1
as current branch, git merge br2
brings into br1
the changes introduced by the commits that are reachable from br2
and are not reachable from br1
. Depending on the relative position of br1
regarding br2
, git merge
can create a new commit (that contains all the changes introduced by the commits unreachable from br1
) or it can just move the br1
branch head into a new position (and doesn't create any new commit).
In your situation, br2
is behind br1
with 1 commit and because A
is a merge commit, all the commits accessible from br2
can also be reached from br1
.
Anyway, git merge --no-ff
is used in a different situation.
Let's say we have this graph:
B <-- br2
|
C
|
D
|
E <-- br1
|
...
The branch br2
was created from branch br1
(both were at commit E
at that time) then br2
was checked out and three new commits (D
, C
and B
) were added.
In this situation, the commands:
git checkout br1
git merge br2
do not create a new branch. The branch br1
is moved to commit B
(where br2
is) and that's all. Now all the commits that are reachable from br2
are also accessible from br1
. br2
was successfully merged into br1
.
This is how the graph looks like:
br1 --> B <-- br2
|
C
|
D
|
E
|
...
This kind of merge is called fast-forward
because the branch br1
was moved "fast forward" from its previous position to a new position (probably opposed to branch b2
that moved from E
to B
one commit at a time). It is possible only if br2
is the only one child path of br1
(starting from E
, each commit has a single child commit and the path reaches B
).
Here comes --no-ff
into play. If you want this merge to create a merge commit that contains all the changes introduced by commits D
, C
and B
then you run:
git checkout br1
git merge --no-ff br2
The presence of --no-ff
option prohibits the fast forwarding of branch b1
and forces the creation of a new merge commit. The graph will look like this:
A <-- br1
|\
| B <-- br2
| |
| C
| |
| D
|/
E
|
...