16

I am currently working on a project with multiple others. The problem is that we created a fork of an existed project and need to squash all our commits from after the forking process to a single commit for a pull-request on Bitbucket.

Is there any way (preferably using SourceTree, otherwise terminal) to squash already pushed commits to a single commit, such that the history within Bitbucket of all the commits are also only that one commit plus the commits which were already there before we forked the project?

Take as example just a simple project with a few files in only a master branch.

Suriyaa
  • 2,222
  • 2
  • 25
  • 44
Joshua
  • 303
  • 1
  • 2
  • 11

6 Answers6

14

Found a nice summary of the posted answers. This one is a bit more clear: squash pushed commits. It will require creating a second branch upon the creation of the fork. This second branch can have as many pushes to the remote server as needed.

  1. Create a new personal branch that will be squashed.

    # Start with the existing personal branch that contains all of your commits.
    $ git checkout {ExistingBranchName}
    # Create a new personal branch that will be squashed.
    $ git checkout -b {BranchName}
    
  2. Identify the first commit where your personal branch diverged from an existing CEF branch.

    # Replace {BranchName} with your new branch name.
    # Replace "master" with a different CEF branch as appropriate
    # (e.g. "2272", "2171", etc).
    $ git merge-base {BranchName} master
    
  3. Start an interactive rebase using the commit hash returned from step 2.

    $ git rebase --interactive {hash}
    

    This will launch a text editor with a list of all commits in your personal branch. It should look something like this:

    pick 59d2a23 Initial implementation
    pick 752ae4c Add more features
    pick cd302a3 Fix something
    pick 5410de3 Fix something else
    
  4. Change all but the first line to say squash instead of pick. The contents should now look like this:

    pick 59d2a23 Initial implementation
    squash 752ae4c Add more features
    squash cd302a3 Fix something
    squash 5410de3 Fix something else
    
  5. Save the changes and close the file (Can be done by pressing esc and type: :wq.

    A new file will now open containing the commit messages from all of the commits. Reword the commit message then save the changes and close the file.

  6. Push the modifications to your personal remote repository.

    # If the branch has already been pushed to the remote repository
    # you will need to add the  `--force` argument.
    git push origin {BranchName}
    # or git push origin {BranchName} --force
    
phuclv
  • 37,963
  • 15
  • 156
  • 475
Joshua
  • 303
  • 1
  • 2
  • 11
5

There is a simpler way.

If you are absolutely certain that no one will ever use that specific branch you can do the following:

  1. Create a local branch that has everyone's commits.
  2. do git log on the local branch to determine the hash of the non-branch commit preceding the first branch commit.
  3. Do: git reset --soft <hash>
  4. Do: git commit to create the single commit message that you want.
  5. Do: git rebase -i to move your local branch to the tip of master.
  6. Fix any merge conflicts.
  7. git push your changes to your remote branch used for the PR. Use -f for force push if it already exists.
Alwyn Schoeman
  • 467
  • 7
  • 13
4

Use git rebase!

Bitbucket use the Git software.

alex said it already in his post: Use the git rebase -i ourbranchname command!


Commands

Use these commands in the terminal, command prompt or in your cmd:

  1. git rebase -i yourbranchname
  2. A file in a editor program will be automatcally open.
  3. Change all commits from "pick" to "fixup" but one commit must have "pick"!
  4. Save the file and close.
  5. git push -f origin yourbranchname

Helpful resources

Community
  • 1
  • 1
Suriyaa
  • 2,222
  • 2
  • 25
  • 44
4

Rebasing is easy to do with SourceTree, in sourcetree select the commit that you based your work on, then press the right mouse button.

Select the commit in the other branch

Then a menu will popup, select "interactively rebase children of "

This will open a screen where we can select what to do with every commit. Since we want to squash every commit together, we click the top commit and select the button "Squash with previous" at the bottom of the screen. Sourcetree will update the top screen in response to the actions you did so you can see its doing what you want.

Before:

Before

After:

After

Usually, you also want to change the message of the commit when you squash them together, double click on the message to change it. If you are done with the changes, press "ok" at the right bottom corner. Source Tree will now rebase the history for you.

Finished result:

Result

Now you can force push your branch to your own fork of the other project and try to make another pull request.

Ferrybig
  • 18,194
  • 6
  • 57
  • 79
4

You can use this script for squash all commit of current branch and with final commit message.

 #!/bin/sh 

 # brief: This script should be run from within your git repo. You need to have   your feature branch

 # checked out. This will squash all the commits on your feature branch (assuming you 

 # branched from master). Then you can just use the github PR UI to merge the PR in. You need to have 
 # feature branch checked out when you run this script (git checkout feature branch).

 # usage: git-helper-squash-all-commits <final commit messsage ex: "reorganizing directory structure"

 # example: git-helper-squash-all-commits "reorganizing directory structure"

 set -ex

 FINAL_COMMIT_MESSAGE=$1

 BRANCH_YOU_BRANCHED_FROM=master

 CURRENT_BRANCH=`git rev-parse --abbrev-ref HEAD`

 COMMIT_HASH=`git merge-base HEAD $BRANCH_YOU_BRANCHED_FROM`

 git reset --soft $COMMIT_HASH

 git commit -am "$FINAL_COMMIT_MESSAGE"

 git push origin $CURRENT_BRANCH --force
phuclv
  • 37,963
  • 15
  • 156
  • 475
Ravi Prajapati
  • 2,536
  • 1
  • 11
  • 9
  • Thanks, it really worked for me. I was facing challenges to squash in bit bucket because they dont provide the feature like gitub "squash and merge" when we hit the merge button. – viveksharma Apr 26 '22 at 04:11
2

...we created a fork of an existed project and need to squash all our commits from after the forking process to a single commit for a pull-request on Bitbucket

Note that you don't need a single commit for a pull request. A bunch of commits can constitute a pull request.

Is there any way (preferably using SourceTree, otherwise terminal) to squash already pushed commits to a single commit?

Yes, you can use an interactive rebase git rebase -i.

You can then mark the commits you want to squash (follow the on-screen instructions that appear in your editor) and then write a new commit message when you have finished (use git rebase --continue after each commit you have worked on).

If you have pushed them already, you will need to force push with the -f option. Note that this is frowned upon, as anyone else that has pulled down these changes has to jump through hoops potentially to resync with the new history.

There may be a way to do it in SourceTree.

Further reading.

alex
  • 479,566
  • 201
  • 878
  • 984
  • The problem is that the company i work for wants only a single-commit pull request. This strategy with git rebase -i will still keep all the pushed commits in the history. – Joshua Jan 08 '16 at 09:57
  • @Joshua It won't if you squash them to one. – alex Jan 08 '16 at 10:00
  • @Joshua: Use `fixup` instead of `pick` to squash them to one commit. See my post above. – Suriyaa Jan 08 '16 at 10:08
  • @alex this method completely works for local repositories. But the Bitbucket is a remote repository. Using this method it will still keep all the past commits in its history. In the reading you gave it also stated that you need to avoid changing the history of remote repositories, but I asked again and the company really wants everythin in a single commit, which I think is not possible after push. Before pushing, it is easy and doable in SourceTree as well – Joshua Jan 08 '16 at 10:13
  • @SuriyaaKudo Don't forget *above* doesn't work with Stack Overflow's sorting orders. – alex Jan 08 '16 at 10:13
  • @Joshua It *is* possible. Git will complain about the commit not being downstream, so you will need to use the `--force` option which will send the new history as-is. – alex Jan 08 '16 at 10:15
  • @alex: Ok. I understand. Thanks for your response. – Suriyaa Jan 08 '16 at 10:17