Pull requests, Squashed commits, Remote Merges

Because Stash doesn't allow users to edit the commit messages when merging pull requests, and because Stash uses the default commit message generated when squashing a branch onto another and thus the message is a sum total of all commit messages on that branch, we perform squashed merges manually.

However, if the source branch has more than one commit on it, the pull request is not marked as "remotely merged" when the Branch B is squashed onto Branch A and A is pushed to the remote. Even when I include the boilerplate text that Stash adds to pull request merge messages such as "Merge pull request #27 in FUBAR/adapter from feature/2.3-FUBAR-395-FuBar-FileMetrics-Payload to develop" or include a list of all of the shortened commit hashes ("git log --pretty=format:'%h'"), the pull request is still set to open after I push Branch A, now with a single squashed commit representing the merged pull request, to the remote.

I understand that Stash probably looks for the commit IDs on Branch A that are members of the pull request for Branch B, and if they appear in both places Stash will likely consider the pull request merged. The question is, what happens when those commit IDs disappear because you've squashed them? Clearly Stash can handle it internally since we have our pull request behavior set to ff-only-squash, and when we click the Merge button Stash knows it's merged the thing. But again, because of the noisy commit message generated, we merge by hand. We just need to know how to let Stash know it's been merged.

Thanks!

3 answers

1 accepted

1 vote
Accepted answer

Hi Andrew,

When a push is received that updates either the source or target branch of a pull request, Stash updates the pull request. As part of this 'rescoping' logic, it checks:

  • Has either branch been deleted? --> pull request is declined automatically
  • Are there any commits on the source branch that are not on target branch? --> pull request is closed and marked as 'merged remotely'

In your case, I assume you're doing the following locally:

git checkout target-branch
git merge --ff-only --squash source-branch
git commit -m "Your custom message"
git push origin HEAD

This only updates the target branch; the source branch is unchanged and Stash will still see unmerged commits (the original commits prior to squashing) and keep the pull request open. It will rescope the pull request, which should result in an empty diff.

If you change your command a little bit, Stash will be able to recognize the fact that your pull request has been merged remotely:

git checkout target-branch
git merge --ff-only --squash source-branch
git commit -m "Your custom message"
git push origin HEAD
git checkout -B source-branch
git push --force origin HEAD

The last two lines will recreate source-branch at the squashed commit. You'll have to force-push that branch to update it on the server because it's not a fast-forward merge (even though the diff is empty).

I hope this explains the way Stash does 'remote merge' detection!

Cheers,

Michael

Hi Michael,

Thanks for the help! I think on line #5 of your suggested help you actually mean:

git checkout -B target-branch

The man page for git-checkout states "If -B is given, <new_branch> is created if it doesn't exist; otherwise, it is reset" whereas there is no -B option for git-branch.

Also, the last line of the suggested help might need to include the remote:

git push --force origin HEAD

However, when I do these things they don't actually work:

[0]akutz@pax:vcopsadapter$ git checkout -B develop
Reset branch 'develop'
Your branch is up-to-date with 'origin/develop'.

[0]akutz@pax:vcopsadapter$ git push --force origin HEAD
Everything up-to-date

No matter the variations I try, the last command always indicates everything is up to date.

Yeah, you're right about the checkout and providing a remote on the force push. I've edited my answer to reflect that.

I've just tested the steps and they work for me. Here's a script of what I've done:

# First create a new repo in Stash 
git init test-repo
git remote add origin http://localhost:7990/stash/scm/proj/test.git
echo test &gt; test
git add test
git commit -m "Initial commit"
git checkout -b develop
echo testing &gt;&gt; test &amp;&amp; git commit -am "Change"
echo testing &gt;&gt; test &amp;&amp; git commit -am "Change"
git push origin --all

# create pull request in Stash
git checkout master
git merge --ff-only --squash develop
git commit -m "Squashed changes"
git push origin HEAD

# verify pull request in Stash - still open, diff is empty
git checkout -B develop
git push --force origin HEAD  

# verify pull request in Stash - closed, marked as merged remotely

Hi Michael,

I'll try that as well. You know, I wonder if it was because the open pull request I tried it on only had a single commit on the source branch. It didn't need a squash, but I did it anyway just to see if the suggestion worked. I wonder if the scenario with squashing a single commit doesn't work. I mean, at that point I'm really just changing the commit ID of a single commit.

The other difference is that developers often rebase their feature branches from develop prior to opening a pull request or often after they open the pull request. However, the pull requests *do* update themselves when a developer rebases the branch for the pull request. For example, it will say 12 added, 12 deleted. Still, I don't know if that could cause issues.

Like I said, I'll create a new repo and try your steps on our set up. I do have the following configured in the stash propreties file:

# The default merge policy for the server is to only accept pull 
# requests if they are fast-foward only capable and then to process
# them as squashed commits.
plugin.stash-scm-git.pullrequest.merge.strategy=squash-ff-only

Would that have an effect on how Stash resolve the pull request if it's merged remotely?

Re: only a single commit, it shouldn't matter. Perhaps you forgot to add the --squash option on the merge command?

Re: the plugin.stash-scm-git.pullrequest.merge.strategy=squash-ff-only option, that option only affects the merge Stash does when you merge the pull request through the UI. It should not matter for remotely merged pull requests.

Hi Michael,

No, it was definitely squashed. I know because I hard reset develop after that didn't work and merged it sans squash and pushed develop. That worked as expected since the same commit ID was now on develop that was on the source branch.

Hi Michael,

I also have the Workzone plug-in installed. I wonder if that affects the remote merge logic? For the repo in question the only setting I have enabled on Workzone are the automatic branch reviewers and to automatically withdraw any approvals for pull requests if a change is pushed to a branch with an open pull request.

Hi Michael,

That worked! I misunderstood your original instructions:

git checkout target-branch
git merge --ff-only --squash source-branch
git commit -m "Your custom message"
git push origin HEAD
git checkout -B target-branch
git push --force origin HEAD

Or rather I guess maybe they had an error. Line #5 should have actually been "git checkout -B source-branch"

In your last script you checked out and reset develop, and in your latest example that was the source branch. That's when I realized that the original instructions had me reset the target branch. So I tried the script and reset the source branch and force pushed it after I performed the squash and it worked perfectly!

Thanks!

Hi Michael,

May I suggest you edit your original answer to reflect the correct command? That way if people find this page they won't have to read the entire thread to find the correct method. Thanks again!

Done! Sorry about the confusion

Hi Michael,

One more nit to pick. I think "The last two lines will recreate target-branch at the squashed commit" should read "The last two lines will recreate source-branch at the squashed commit."

Thanks again!

I believe a squashed merge commit is different from a normal merge commit resulting from a non-fast-forward merge. I think a squashed merge commit does not reference the source branch as one of its parents.

From the Git docs: "Produce the working tree and index state as if a real merge happened (except for the merge information)"

So if instead of doing `git merge --squash` you were to instead do `git merge --no-ff --no-commit`, you would be able to edit your merge commit message and retain the merge metadata that would allow Stash to link things together.

Hi Gordon,

That's an idea, but since Stash does actually perform a squashed commit when you configure it to ff-only and squash, there has to be a way of doing that remotely.

And more specifically, I don't want all of the history from the feature branch, hence the squashed commit. When you perform a merge with --no-commit all you're doing is giving yourself the ability to edit the merge commit message. The merge still occurs as it normally would -- all of the commit IDs that existed on the source branch now exist on the destination branch.

I tried this solution, and it no longer works. It appears that the fix for Atlassian BSERV-4219 (https://jira.atlassian.com/browse/BSERV-4219) has caused the server to decline the pull -requests if you push the release/target branch before you push your feature branch.

In order for Bitbucket to recognize the request as merged, I had to push the feature branch before pushing the release branch:


git merge --ff-only --squash $FEATURE_BRANCH
git commit -m "B-$BLINUM: $MESSAGE" -m"    Delivery of $FEATURE_BRANCH"
git branch --force $FEATURE_BRANCH HEAD
git push --force origin $FEATURE_BRANCH
git push origin $RELEASE_BRANCH

 

See this SO discussion for more info:

https://stackoverflow.com/questions/14727837/manually-closing-bitbuckets-pull-request#21625362

thomas.v.judkins@boeing.com

Can you explain this a bit more?

You squash the changes on the feature branch. I assume you have checked out the target branch, which is the RELEASE_BRANCH?

Then this line which I find confusing.

git commit -m "B-$BLINUM: $MESSAGE" -m"    Delivery of $FEATURE_BRANCH"

Then force push the feature branch head. But have you modified it?  

Then force push the target branch. Which you have modified. 

Sorry if i am being dense, but I have this requirement and want to understand.

The commands as written effectively squash-rebase the feature branch, push the feature branch, then push the target branch with the same commit.

Making the squash merge in git itself leaves no references to the original branch. You need to have the same commit on both branches for BB to recognize the branch (and pull requests) as being related to a commit on master / $RELEASE_BRANCH. So you squash merge your TASK-001 branch to master, commit the result, and push it to origin. This is effectively doing a squash-rebase from master. You then set master to point to the same commit, and push that to origin.

You are effectively setting both the master and  TASK-001 branches to the same commit, but you need to push the TASK-001 reference to origin first otherwise Bitbucket will not recognize the merge as having occurred.

With the additional features Atlassian has delivered for updating merge messages, and with a few add-ons to verify our issues are present, in the correct state, and referenced in the merge message, we have stopped doing this client side, and are now using the native Bitbucket merge function.

Suggest an answer

Log in or Sign up to answer
Community showcase
Posted 5 hours ago in Agile

What is Scrum? A good, bad, and ugly answer.

In a world of dark-scrum, faux-scrum, and scrum-butt, the question still remains: What is scrum and how do you do it “right?” That’s the question we set out to answer. I'm Max, I've been teaching c...

44 views 0 1
Join discussion

Atlassian User Groups

Connect with like-minded Atlassian users at free events near you!

Find a group

Connect with like-minded Atlassian users at free events near you!

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you