Motivation
The default merge method on GitHub pull requests creates a merge commit. But GitHub also provides us with severalmerge optionsand the Squash and Merge option is one of them. Since a squash merged commit is not the same as commits on a local branch,git
cannot list local branch merged with squash bygit branch - merged
. Therefore, we cannot clean up stale local branches with the following commands.
$ git fetch --prune$ git branch --merged | egrep -v "(^\*|maestro|ANY_BRANCH_YOU_WANT_TO_EXCLUDE)" | xargs -I % rama git -d %
These commands can remove stale remote branches on GitHub and local branches if we use standard merge commits.
To clear local branch corresponding to PR merged with squash I usegeh cherry
,git-merge-base
, jgit-commit-tree
. Since these are the commands that I rarely use in my software development process, I would like to introduce the usage of the commands.
meta
- Explain
geh cherry
,git-merge-base
, jgit-commit-tree
- Please share shell script to clean up stale branches from my local git
Strategy to detect a merged branch with squash
Let's say we have a topic branchA
, jA
it is zipped and merged into a master on GitHub. For this situation, we can detect the local git squash merged branch with the following steps.
- Find a master commit for
Maestro
and subjectA
. - Create a zipped temporary commit from the main commit to the latest commits of
A
. - Check if zipped commit is already applied
Maestro
.
strategy review
Based on the strategy described above, I will try and develop the steps on how we can detect the merged branch with squash in my local git.
Preparation before the experience
The repository that I will use during the experiment is prepared with the following commands. First, a Git initialization is performed and the first commit is added.
$ mkdir remote-squash-merge-branch$ cd remote-squash-merge-branch$ git init$ echo "1" > README.md$ git add README.md$ git commit -m "Initial commit"$ echo "parent commit to master and A" >> README.md$ git commit -am "parent commit to master and A"$ git log --oneline546d16d (HEAD -> master) parent commit to master and A6669578 Initial commit
Second, a topic branch is calledA
created and added two commits.
$ git checkout -b A$ echo "First commit in topic A" >> README.md$ git commit -am "First commit in topic A"$ echo "Second commit in topic A" >> README.md$ git commit - on "Second commit with topic branch A"
We can check the current state of Git trees withgit log --graph
. We can confirm this from the following tree diagramA
it's two commits before the master branch.
$ git log --graph --date-order --all --pretty=format:'%h %Cred%d %Cgreen%ad %Cblue%cn %Creset%s' --date=short* 709b12e (HEAD - > A) 2020-04-18 Takayuki WATANABE Second commit with topic branch A* 6a55918 2020-04-18 Takayuki WATANABE First commit with topic branch A* 546d16d (master) 2020-04-18 Takayuki WATANABE Main commit for teachers and A* 6669578 04/18/2020 Takayuki WATANABE First confirmation
Third, we have to pushA
to a remote repository and create a pull request.
$ git remote add origin git@github.com:takanabe/remote-squash-merged-branch.git$ git push origin master
Finally, on GitHub, click squash and merge and review the commit and merge it into your local master branch.
$ git checkout master$ git fetch --all$ git merge origin/master
The graphic below illustrates the Git tree I want to use for the experiment. As you can see in the graph, the local master contains the squash combo commit. The ancestor owes himself to himMaestro
jA
Branches is the commit hash546d16d
.
$ git log --graph --date-order --all --pretty=format:'%h %Cred%d %Cgreen%ad %Cblue%cn %Creset%s' --date=short* 2b3d4ae (HEAD - > master, origin/master) 2020-04-18 GitHub A (#1)| * 709b12e (Source/A, A) 2020-04-18 Takayuki WATANABE Second commit with topic branch A| * 6a55918 2020-04-18 Takayuki WATANABE First commit with topic branch A|/* 546d16d 2020-04-18 Takayuki WATANABE Major commit for master and A* 6669578 2020-04-18 Takayuki WATANABE First commit
Step 1. Find a parent commit for master and topic A
Okay, let's start the experiment. As a first step, we need to find a master commit forMaestro
and subjectA
' whose commit hash is546d16d
. There is a useful Git subcommand calledmerged base
. Literally,merged base git
find as many common ancestors as possible for a mergerMaestro
jA
.
$ git merge-base master A546d16de58d48d045a24649817ab9e0e256d4f7a$ ANCESTOR=`git merge-base master A`$ git show --oneline $ANCESTOR546d16d Confirmar principal para mestre e adiff --git a/README.md b/README.mdindex d00491f..817a9344 - -- a/README.md+++ b/README.md@@ -1 +1,2 @@ 1+compromiso principal para maestro y A
due togit merge-base maestro A
, array variableANCESTOR
You now have the commit hash for the main commit.
Step 2. Create a temporary compressed commit
The next step is to create a temporary commit that will contain all the commitsA
but it is squeezed into a commit. When we create the temporary commit, we don't want to change the commits and the treeA
.Git-Commit-BaumNamecreates a new commit object using an ancestor commit object and a tree object.
$ TEMP_TREE=`git commit-tree $(git rev-parse A^{tree}) -p $ANCESTOR -m "temp tree object"`$ git log --oneline $TEMP_TREEbe75fbc temp tree object546d16d main commit to master e A6669578 inicial commit$ git show --oneline $TEMP_TREEbe75fbc árvore temporária objectdiff --git a/README.md b/README.mdindex 817a93f..e5f7cf8 100644--- a/README.md+++ b/README.md@@ -1,2 + 1.4 @@ 1 commit pai para master e A+Primeiro commit com branch de tópico A+Segundo commit com branch de tópico A
As I stated above,commit tree
it does not change the state of existing commit and tree objects and creates a new commit object in their place. Therefore, if you search the tree for the master, you cannot find the graph's temporary commit.
$ git log --graph --date-order --all --pretty=format:'%h %Cred%d %Cgreen%ad %Cblue%cn %Creset%s' --date=short* 2b3d4ae (HEAD - > master, origin/master) 2020-04-18 GitHub A (#1)| * 709b12e (Source/A, A) 2020-04-18 Takayuki WATANABE Second commit with topic branch A| * 6a55918 2020-04-18 Takayuki WATANABE First commit with topic branch A|/* 546d16d 2020-04-18 Takayuki WATANABE Major commit for master and A* 6669578 2020-04-18 Takayuki WATANABE First commit
If you specify the temporary commit hash forGit registration
, you can confirm the existence.
$ git log --graph --date-order --all --pretty=format:'%h %Cred%d %Cgreen%ad %Cblue%cn %Creset%s' --date=short $TEMP_TREE* 9f8364a ( HEAD -> master) 2020-04-18 Takayuki WATANABE One step ahead of confirmation, merged with Squash | *be75fbc 4/18/2020 Takayuki WATANABE temp tree object <- This is the commit temp object* | 2b3d4ae (Source/Master) 2020-04-18 GitHub A (#1)|/| * 709b12e (Source/A, A) 2020-04-18 Takayuki WATANABE Second commit with topic branch A| * 6a55918 2020-04-18 Takayuki WATANABE First commit with topic branch A|/* 546d16d 2020-04-18 Takayuki WATANABE Major commit for master and A* 6669578 2020-04-18 Takayuki WATANABE First commit
Step 3. Validate the pumpkin merged branch
Finally, we can compare the current master and the temp commit created in step 2. Since a squash creates a new commit and changes the commit hash, commit hashes don't work. But since the temporary commit includes all commits forA
On commit, merge validation is possible on GitHub by checking if the master branch contains a commit that has the sameContentas a temporary appointment.
geh cherryallows us to determine if the master has an equivalent commit. This command displays the SHA1 of each commit<head>..<upstream>
. Therefore, if the temporary commit matches one of the master commits, the command generates a SHA1 with the prefix-
.
$ git cherry master $TEMP_TREE-be75fbc3b3e3136e622b44936c391fc0f7252c33
Cool. Now we can verify that the master branch contains the merged commit on GitHub.
additional attempt
geh cherry
Output from SHA1 with-
Prefix. We might instinctively expect this when comparing commitsgeh cherry
are not equivalent, there is a different prefix-
. Let's add another commit to master and rungeh cherry
.
$ echo "One step ahead of commit merged with squash" >> README.md$ git commit -am "One step ahead of commit merged with squash"$ git log --graph --date-order --all -- pretty= format:'%h %Cred%d %Cgreen%ad %Cblue%cn %Creset%s' --date=short $TEMP_TREE* 9f8364a (HEAD -> master) 2020-04-18 Takayuki WATANABE One step ahead squash merge commit | * be75fbc 2020-04-18 Takayuki WATANABE temp tree object * | 2b3d4ae (Source/Master) 2020-04-18 GitHub A (#1)|/| * 709b12e (Source/A, A) 2020-04-18 Takayuki WATANABE Second commit with topic branch A| * 6a55918 2020-04-18 Takayuki Watanabe First appointment to topic Branch A |/* 546d16d 2020-04-18 Takayuki Watanabe Main Committee for Master and A* 6669578 2020-04-18 Takayuki Watanab Initial Chery 9f8364a3e99911527d20ada97ce.face0a97ce.face0a97ce.
+
is the prefix used to represent different commits.
Removal of obsolete local and remote branches
We can remove stale local branches with the combination of merged and merged branch deletes.git for each reference
jgit rev-parse
are useful for this type of automation. Here is an example shell script that removes stale local and remote branches at the same time.
#!/usr/bin/env zsh# gcl:git-cleanup-remote-and-local-branches## Remote and local branch cleanup is provided as follows:#1. Remove removed branches when deleted or merged#2. Delete local branches when your remote branches are deleted#3. Remove local branches if a master contains squash and merge commitsfunctiongit_prune_remote() { Eco "Start removing stale remote merged branches"git search --pruneEco "Complete deletion of stale remote merged branches"}functiongit_remove_merged_local_branch() { Eco "Start removing stale local merged branches"git branch - merged|egrep-v"(^\*|maestro|ANY_BRANCH_YOU_WANT_TO_EXCLUDE)" |xargs -I % rama git -d %Eco "Complete removal of stale local merged branches"}# Se usarmos `Squash and merge` no GitHub,# `git branch --merged` cannot detect merged branches.# As a result, git_remove_merged_local_branch() fails to clean up# unused local branches. This feature detects and removes local branches# when merging remote branches.## There is a borderline case. If you add suggested commits on GitHub,# Local and remote content are different. As a result,# This cleanup function cannot remove local branch merged with squash.functiongit_remove_squash_merged_local_branch() { Eco "Start removing obsolete local branches in combination with squash"git checkout -q mestre&&gitfor-each-references/headers/"--format=%(refname:short)" | while filerama; Again ancestor=p.smaestro git merge-base$rama) && [[ p.sgit cherry masterp.sGit-Commit-BaumNamep.sgit rev-parse$rama^{baum})- book page$ancestral-M_)) == "-"*]] &&rama git-D$rama done Eco "Stop removing stale local squash merged branches"}# Clean up remote and local branchesfunctiongl() {git_prune_remote git_remove_merged_local_branch git_remove_squash_merged_local_branch}gl
Tips for suggested commits on GitHub
With pull request reviews on GitHub, reviewers can do just thatSuggest code changes, and an author can integrate them. If we want to remove the local branches, we must also preserve the proposed changes to the local branch. Otherwise the above script will not be able to delete the local branch.
Diploma
We can see a branch of pumpkin mixed withgit merge-base
,Git-Commit-BaumName
, jgeh cherry
. So we can remove all deprecated local branches even if we use the squash and merge method on GitHub.
Thanks
This article is inspired bynor tamanduá/git-delete-squashed
references
FAQs
How do I delete all merged branches locally? ›
- Open git bash and navigate to your git repository that you want to clean up.
- Fetch the latest from the git. Copy git fetch.
- See the list of local git branches. Copy git branch.
- Delete all local branches that have been merged to main branch. ...
- See the list of local git branches that remain.
When you're done with a branch and it has been merged into master, delete it. A new branch can be made off of the most recent commit on the master branch. Also, while it is ok to hang onto branches after you've merged them into the master they will begin to pile up.
How do I remove a merged branch in git? ›You can use the Git reset command to undo a merge.
Can I delete local branch after merge? ›Deleting a branch LOCALLY
Delete a branch with git branch -d <branch> . The -d option will delete the branch only if it has already been pushed and merged with the remote branch. Use -D instead if you want to force the branch to be deleted, even if it hasn't been pushed or merged yet. The branch is now deleted locally.
- Open a Git BASH window or Command Window in the root of your Git repository.
- If necessary, use the git switch or checkout command to move off the branch you wish to delete.
- Issue the git branch --delete <branchname> ...
- Run the git branch -a command to verify the local Git branch is deleted.
With git branch --merged <commit> , your local list of branches will be filtered by all the branches who have been merged into a given branch or commit. Similar to above, you could type git branch --no-merged <commit> and only the branches not merged into the named commit would be listed.