How do you squash your entire repository down to the first commit?
I can rebase to the first commit, but that would leave me with 2 commits. Is there a way to reference the commit before the first one?
As of git 1.6.2, you can use git rebase --root -i
For each commit except the first, change pick to squash in the editor that pops up.
git push -fg. You only want to replace the first instance of the word pick on each line. Even better is :%s/^pick/squash/.You can create a squash-all commit right from HEAD, without rebase at all, just run:
git reset $(git commit-tree "HEAD^{tree}" -m "A new start") Note: this requires a POSIX compatible shell like bash/zsh, or Git Bash on Windows.
~/.gitconfig[alias] squash-all = "!f(){ git reset $(git commit-tree \"HEAD^{tree}\" \"$@\");};f" Then just run: git squash-all -m "a brand new start"
Note: Either provide the commit message from standard input, or via the -m/-F options, just like the git commit command. See git-commit-tree manual.
Alternatively, you can create the alias with the following command:
git config --global alias.squash-all '!f(){ git reset $(git commit-tree "HEAD^{tree}" "$@");};f' To update/overwrite the remote repository, use git push --force-with-lease, or specify which remote and branch to update if needed.
create a single commit via git commit-tree
What git commit-tree "HEAD^{tree}" -m "A new start" does is:
Creates a new commit object based on the provided tree object and emits the new commit object id on stdout. The log message is read from the standard input, unless -m or -F options are given.
The expression HEAD^{tree} represents the tree object corresponding to HEAD, namely the tip of your current branch. see Tree-Objects and Commit-Objects.
reset the current branch to the new commit
Then git reset simply reset the current branch to the newly created commit object.
This way, nothing in the workspace is touched, nor there's need for rebase/squash, which makes it really fast. And the time needed is irrelevant to the repository size or history depth.
This is useful to create the "initial commit" in a new project using another repository as the template/archetype/seed/skeleton. For example:
cd my-new-project git init git fetch --depth=1 -n https://github.com/toolbear/panda.git git reset --hard $(git commit-tree "FETCH_HEAD^{tree}" -m "initial commit") This avoids adding the template repo as a remote (origin or otherwise) and collapses the template repo's history into your initial commit.
git push -f for propagation.git clone. If you add --hard to git reset and switch HEAD with FETCH_HEAD in the git commit-tree you can create an initial commit after fetching the template repo. I've edited the answer with a section at the end demonstrating this.${1?Please enter a message}If all you want to do is squash all of your commits down to the root commit, then while
git rebase --interactive --root can work, it's impractical for a large number of commits (for example, hundreds of commits), because the rebase operation will probably run very slowly to generate the interactive rebase editor commit list, as well as run the rebase itself.
Here are two quicker and more efficient solutions when you're squashing a large number of commits:
You can simply create a new orphan branch at the tip (i.e. the most recent commit) of your current branch. This orphan branch forms the initial root commit of an entirely new and separate commit history tree, which is effectively equivalent to squashing all of your commits:
git checkout --orphan new-master master git commit -m "Enter commit message for your new initial commit" # Overwrite the old master branch reference with the new one git branch -M new-master master Documentation:
Another efficient solution is to simply use a mixed or soft reset to the root commit <root>:
git branch beforeReset git reset --soft <root> git commit --amend # Verify that the new amended root is no different # from the previous branch state git diff beforeReset Documentation:
git push origin master --force.git push --forcePerhaps the easiest way is to just create a new repository with current state of the working copy. If you want to keep all the commit messages you could first do git log > original.log and then edit that for your initial commit message in the new repository:
rm -rf .git git init git add . git commit or
git log > original.log # edit original.log as desired rm -rf .git git init git add . git commit -F original.log git rebase -i --root. See: stackoverflow.com/a/9254257/109618echo "message" | git commit-tree HEAD^{tree} This will create an orphaned commit with the tree of HEAD, and output its name (SHA-1) on stdout. Then just reset your branch there.
git reset SHA-1 To do the above in a single step:
git reset $(git commit-tree HEAD^{tree} -m "Initial commit.") git reset $(git commit-tree HEAD^{tree} -m "commit message") would make it easier.echo "message" | git commit-tree "HEAD^{tree}"Here's how I ended up doing this, just in case it works for someone else:
Remember that there's always risk in doing things like this, and its never a bad idea to create a save branch before starting.
Start by logging
git log --oneline Scroll to first commit, copy SHA
git reset --soft <#sha#> Replace <#sha#> w/ the SHA copied from the log
git status Make sure everything's green, otherwise run git add -A
git commit --amend Amend all current changes to current first commit
Now force push this branch and it will overwrite what's there.
The easiest way is to use the 'plumbing' command update-ref to delete the current branch.
You can't use git branch -D as it has a safety valve to stop you deleting the current branch.
This puts you back into the 'initial commit' state where you can start with a fresh initial commit.
git update-ref -d refs/heads/master git commit -m "New initial commit" In one line of 6 words:
git checkout --orphan new_root_branch && git commit git help checkout about --orphangit help checkout --orphancreate a backup
git branch backup reset to specified commit
git reset --soft <#root> then add all files to staging
git add . commit without updating the message
git commit --amend --no-edit push new branch with squashed commits to repo
git push -f <root> commit you reset back to. git commit --amend --no-edit will commit all changes to the current commit which is <root> without needing to edit the commit message.To do this, you can reset you local git repository to the first commit hashtag, so all your changes after that commit will be unstaged, then you can commit with --amend option.
git reset your-first-commit-hashtag git add . git commit --amend And then edit the first commit nam if needed and save file.
First, squash all your commits into a single commit using git rebase --interactive. Now you're left with two commits to squash. To do so, read any of
Let's say you have 3 commits in your branch and it is already pushed to remote branch.
Example:
git log -4 Will display results like:
<your_third_commit_sha> <your_second_commit_sha> <your_first_commit_sha> <master_branch_commit_sha - your branch created from master> You want to squash your last 3 commits to one commit and push to remote branch. Here are the steps.
git reset --soft <master_branch_commit_sha> Now all commits changes are integrated but uncommitted. Verify by:
git status Commit all changes with a message:
git commit -m 'specify details' Forcefully push the single commit to remote branch:
git push -f Let's say, you have these commits from 06 to 10.
You want to combine the commits post 06 to a single commit Id.
commit 10 commit 09 commit 08 commit 07 commit 06 You can do a git reset soft.
git reset --soft "06" Then, run the below command to push these changes to remote branch.
git push origin HEAD --force Now, all the commits you have made before should be available as your local changes and you can combine all these commits to a single commit.
Now, the new commit structure should like below:
commit <new_commit_containing_all_7_8_9_10> commit 06 I usually do it like this:
Make sure everything is committed, and write down the latest commit id in case something goes wrong, or create a separate branch as the backup
Run git reset --soft `git rev-list --max-parents=0 --abbrev-commit HEAD` to reset your head to the first commit, but leave your index unchanged. All changes since the first commit will now appear ready to be committed.
Run git commit --amend -m "initial commit" to amend your commit to the first commit and change the commit message, or if you want to keep the existing commit message, you can run git commit --amend --no-edit
Run git push -f to force push your changes
For me it worked like this: I had 4 commits in total, and used interactive rebase:
git rebase -i HEAD~3 The very first commit remains and i took 3 latest commits.
In case you're stuck in editor which appears next, you see smth like:
pick fda59df commit 1 pick x536897 commit 2 pick c01a668 commit 3 You have to take first commit and squash others onto it. What you should have is:
pick fda59df commit 1 squash x536897 commit 2 squash c01a668 commit 3 For that use INSERT key to change 'insert' and 'edit' mode.
To save and exit the editor use :wq. If your cursor is between those commit lines or somewhere else push ESC and try again.
As a result i had two commits: the very first which remained and the second with message "This is a combination of 3 commits.".
Check for details here: https://makandracards.com/makandra/527-squash-several-git-commits-into-a-single-commit
I tried different commands with HEAD^{tree} but got this error from zsh:
zsh: no matches found: HEAD^{tree} It was how ohmyzsh handles expansion, see this issue for more details: https://github.com/ohmyzsh/ohmyzsh/issues/449
Easiest fix is to run this command:
unsetopt nomatch ^ like so \^ git reset $(git commit-tree HEAD\^{tree} -m "initial commit")Since I had some trouble with the solutions proposed here, I want to share a really simple solution (to squash all commits on a feature branch into one):
git merge origin/master && git reset --soft origin/master The preceding merge cmd ensures, that no recent changes from master will go on your head when committing! After that, just commit the changes and do git push -f
I usually squash the entire tree when I restore a template from a git repository, to get a cleaner history and to ensure legal compliance. My workflow looks like this:
git clone https://git.invalid/my-awesome-template.git my-awesome-foo cd !$ git branch -M master my-awesome-template/master git checkout --orphan master git rm -rf / git commit --allow-empty --allow-empty-message -m 'Initial commit' git merge --squash --allow-unrelated-histories my-awesome-template/master git commit git branch -D my-awesome-template/master # you can now `drop' that "Initial commit": git rebase -i --root This squashes your entire history into a single large commit message.
In this particular example:
master is the working branchmy-awesome-template/master is an intermediate branchThis answer improves on a couple above (please vote them up), assuming that in addition to creating the one commit (no-parents no-history), you also want to retain all of the commit-data of that commit:
Of course the commit-SHA of the new/single commit will change, because it represents a new (non-)history, becoming a parentless/root-commit.
This can be done by reading git log and setting some variables for git commit-tree. Assuming that you want to create a single commit from master in a new branch one-commit, retaining the commit-data above:
git checkout -b one-commit master ## create new branch to reset git reset --hard \ $(eval "$(git log master -n1 --format='\ COMMIT_MESSAGE="%B" \ GIT_AUTHOR_NAME="%an" \ GIT_AUTHOR_EMAIL="%ae" \ GIT_AUTHOR_DATE="%ad" \ GIT_COMMITTER_NAME="%cn" \ GIT_COMMITTER_EMAIL="%ce" \ GIT_COMMITTER_DATE="%cd"')" 'git commit-tree master^{tree} <<COMMITMESSAGE $COMMIT_MESSAGE COMMITMESSAGE ') This worked best for me.
git rebase -X ours -i master This will git will prefer your feature branch to master; avoiding the arduous merge edits. Your branch needs to be up to date with master.
ours This resolves any number of heads, but the resulting tree of the merge is always that of the current branch head, effectively ignoring all changes from all other branches. It is meant to be used to supersede old development history of side branches. Note that this is different from the -Xours option to the recursive merge strategy. In my opinion the easiest way:
git checkout --orphan <your new branch> A response containing this idea
A more technical though still generic way:
In order to squash all the commits into one by doing following commands.
All the answers are awesome but I would like to put another approach with simple commands
git clone --depth 1 <remote-url> cd <directory where repository is cloned> git commit --amend -m <commit message you want> git push --force A detailed explanation is available in this answer
--rootis actually not the best solution to squashing all commits if there are a lot of them to squash): Combine the first two commits of a Git repository?.