To avoid losing any work, I have a habit of never keeping branches local-only for long. Additionally this relies on https://docs.github.com/en/repositories/configuring-branches...
> What tools are the best to do the equivalent but for squash-merged branches detections?
Hooking on remote branch deletion is what most people do, under the assumption that you tend to clean out the branches of your PRs after a while. But of course if you don't do that it doesn't work.
prunable = "!f() { \
: git log ; \
target=\"$1\"; \
[ -z \"$target\" ] && target=$(git for-each-ref --format=\"%(refname:short)\" --count=1 refs/remotes/m/); \
if [ -z \"$target\" ]; then echo \"No remote branches found in refs/remotes/m/\"; return 1; fi; \
echo \"# git branch --merged shows merged if same commit ID only\" ;\
echo \"# if rebased, git cherry can show branch HEAD is merged\" ;\
echo \"# git log grep will check latest commit subject only. if amended, this status won't be accurate\" ;\
echo \"# Comparing against $target...\"; \
echo \"# git branch --merged:\"; \
git branch --merged $target ;\
echo \" ,- git cherry\" ; \
echo \" | ,- git log grep latest message\"; \
for branch in $(git for-each-ref --format='%(refname:short)' refs/heads/); do \
if git cherry \"$target\" \"$branch\" | tail -n 1 | grep -q \"^-\"; then \
cr=""; \
else \
cr=""; \
fi ; \
c=$(git rev-parse --short $branch) ; \
subject=$(git log -1 --format=%s \"$branch\" | sed 's/[][(){}.^$\*+?|\\/]/\\\\&/g') ; \
if git log --grep=\"^$subject$\" --oneline \"$target\" | grep -q .; then \
printf \"$cr $c %-20s $subject\\n\" $branch; \
else \
printf \"$cr \\033[0;33m$c \\033[0;32m%-20s\\033[0m $subject\\n\" $branch; \
fi; \
done; \
}; f"
(some emojis missing in above. see gist)
https://gist.github.com/lawm/8087252b4372759b2fe3b4052bf7e45...It prints the results of 3 methods:
1. git branch --merged
2. git cherry
3. grep upstream git log for a commit with the same commit subject
Has some caveats, like if upstream's commit was amended or the actual code change is different, it can have a false positive, or if there are multiple commits on your local branch, only the top commit is checked
# ~/.gitconfig
[alias]
gone = ! "git fetch -p && git for-each-ref --format '%(refname:short) %(upstream:track)' | awk '$2 == \"[gone]\" {print $1}' | xargs -r git branch -D"
Then you just `git gone` every once in a while, when you're between features. function Rename-GitBranches {
git branch --list "my-branch-prefix/*" | Out-GridView -Title "Branches to Zoo?" -OutputMode Multiple | % { git branch -m $_.Trim() "zoo/$($_.Trim())" }
}
`Out-GridView` gives a very simple dialog box to (multi) select branch names I want to mark finished.I'm a branch hoarder in a squash merge repo and just prepend a `zoo/` prefix. `zoo/` generally sorts to the bottom of branch lists and I can collapse it as a folder in many UIs. I have found this useful in several ways:
1) It makes `git rebase --interactive` much easier when working with stacked branches by taking advantage of `--update-refs`. Merges do all that work for you by finding their common base/ancestor. Squash merging you have to remember which commits already merged to drop from your branch. With `--update-refs` if I find it trying to update a `zoo/` branch I know I can drop/delete every commit up to that update-ref line and also delete the update-ref.
2) I sometimes do want to find code in intermediate commits that never made it into the squashed version. Maybe I tried an experiment in a commit in a branch, then deleted that experiment in switching directions in a later commit. Squashing removes all evidence of that deleted experiment, but I can still find it if I remember the `zoo/` branch name.
All this extra work for things that merge commits gives you for free/simpler just makes me dislike squash merging repos more.
let t = "origin/dev"; git for-each-ref refs/heads/ --format="%(refname:short)" | lines | where {|b| $b !~ 'dev' and (git merge-tree --write-tree $t $b | lines | first) == (git rev-parse $"($t)^{tree}") }
Does a 3-way in-mem merge against (in my case) dev. If there's code in the branch that isn't in the target it won't show up.Pipe right to deletion if brave, or to a choice-thingy if prudent :)