Reputation: 20212
I try to push only the commit with ID 9973b14ca19e4571046d8d25bc986ec07199e39c.
git push bbef 9973b14ca19e4571046d8d25bc986ec07199e39c:refs/heads/feature/25062018_offline-seite_ef
I logged in to Bitbucket to create the pull request, but I noticed, that there are multiple commits, even though this is a new remote branch.
So I deleted the remote branch in bitbucket and pushed it again, same result...
Why does it not work and how can I push only the single commit instead of multiple ones? Why does it happen?
Upvotes: 0
Views: 496
Reputation: 487755
When you run git push
, you have your Git offer to the other Git:
(The other Git will tell your Git when to stop offering previous commits because the other Git has them already.) This is precisely what you are seeing.
It will help to stop, back up, and draw the commit graph (or at least part of it). You can run git log --graph
—I generally prefer the "help from A DOG" variant, git log --all --decorate --online --graph
, All Decorate Oneline Graph = A D O G—to have Git draw the graph rather crudely for you. Some GUIs draw the graph their own way.
Remember that in Git, a branch name is simply a human-readable name for one (1) hash ID. Each commit has a unique hash ID assigned at the time the commit is made, but each commit also contains the hash ID of its parent commits—usually just one, or two for merge commits—and these string the commits together into chains that branch and, at merge commits, merge back together. It does not matter whether you draw the graph vertically, with newer commits at the top, as Git does, or horizontally, with newer commits at the right, as usually I do in StackOverflow postings. What matters is actually drawing the graph, because once you have done that, what Git is doing becomes obvious:
A <-B <-C <--master
This represents the total sum of commits in a tiny three-commit repository. Each uppercase letter stands in for the actual hash ID. The branch name, master
, remembers the actual hash ID of the last commit, commit C
. Commit C
itself remembers the hash ID of its previous or parent commit B
. B
remembers the hash ID of A
, and since A
is the very first commit, it has no parent.
Whenever Git needs to, it starts at whatever commit you tell it—or your current commit by default—and then works backwards through this graph. If you have your Git push one particular commit to some other Git, your Git must push that commit and every parent up to the point where your commits match their commits. So suppose you have, for instance:
...--*--*------o--o---o--X--o--o <-- branch
\ /
o--o------o
where *
represents a commit that the server already has—you pushed those already, or you got them from the server, for instance—and o
represents a commit you have that they don't. At this point, if you push the third commit (towards the right) along the bottom row, they will receive three commits: all three from the bottom row. That's because starting from that commit and working backwards, it takes three commits to make your new one join up with their existing ones. If you push one of the two top row commits just after the *
, your Git will send one or two commits. If you push a commit that comes after the merge, your Git will push at least six commits. Pushing commit X
here pushes seven: X
itself, and the required preceding six.
If you want to push a single commit that adds on to the second starred commit, you must first make a single commit that adds on to the second starred commit:
Y <-- new-name
/
...--*--*------o--o---o--X--o--o <-- branch
\ /
o--o------o
Once you have this setup, if you push commit Y
, they already have the starred commits (of course) so your Git will in fact push a single commit, namely commit Y
.
In order to create commit Y
, first you need1 to create a new branch name pointing to the final commit *
. Find its hash ID, or any other name that uniquely identifies it, and check out that particular commit and create a name for it:
git checkout -b new-name <hash-id>
This leaves the graph itself unchanged but makes the new name pointing to that commit.2 The git checkout -b
command puts you on this new branch at the same time as creating the name. (Note that at this point, pushing the new branch to the server would send the server no commits, and merely create the name on the server as well.)
/----------------------------- new-name (HEAD)
|
v
...--*--*------o--o---o--X--o--o <-- branch
\ /
o--o------o
Now you can run:
git cherry-pick <hash-of-desired-commit-X>
which in this case is:
git cherry-pick 9973b14ca19e4571046d8d25bc986ec07199e39c
This has Git examine commit 9973b14ca19e4571046d8d25bc986ec07199e39c
. Git will extract its parent commit's snapshot, and this commit's snapshot, and diff them (a la git diff
).3 Git will then apply whatever these changes are to the current commit, to make a new commit that is a copy of X
:
Y <-- new-name (HEAD)
/
...--*--*------o--o---o--X--o--o <-- branch
\ /
o--o------o
and now you have what you need: a commit that can be pushed by itself, because Y
's history—its parent and grandparent and so on—already exists on the server.
1Technically, you can do this without a name, using "detached HEAD" mode. I'd advise against that, at this point.
2You can also create the branch name using git branch
:
git branch new-name <hash-id>
You then have to run git checkout new-name
to get on that branch, i.e., to attach your HEAD
to that name.
3Technically, cherry-pick is actually a merge operation that results in a non-merge commit: merge-as-a-verb, to merge, but not as an adjective or noun. This lets the cherry-pick work in a situation in which a simple patch would not. However, this particular complication is mostly irrelevant here: it only matters if the cherry-pick results in a merge conflict.
Upvotes: 2
Reputation: 22311
You think you pushed only one commit but acctually you pushed a lot of commits instead. The commit 9973b14 commit depends on other commits that don't exist in the feature/25062018_offline-seite_ef branch so they will be pushed together. This is the way Git works.
You need to create a local branch based on the remote feature/25062018_offline-seite_ef branch then cherrypick the 9973b14 commit and then push to remote.
Upvotes: -1