简体   繁体   中英

how to rename multiple git branches into lowercase using bash

I want to rename multiple branches in lowercase, as before migration we have to convert all uppercase branches to lowercase. git branch:

CPORT_121

GC_211

Lower-Upper_DEMO

Now I tried the below one but it is not working.

git branch | tr A-Z a-z | xargs -n 2 git branch -m

error: refname refs/heads/cport_121 not found
fatal: Branch rename failed
fatal: '*' is not a valid branch name.

This is the wrong way to code the loop. (Your checkout loop in the comments is also not really the right way to go.) For script purposes we should use git for-each-ref whenever possible, and here it's entirely possible. We would start with this, if we want to create local branch names for each refs/remotes/origin/* remote-tracking name that needs renaming:

git for-each-ref \
  --format="%(if)%(symref)%(then)%(else)%(refname:strip=3)%(end)" \
  refs/remotes/origin | sed '/^$/d' > /tmp/branchlist

This creates a file ( /tmp/branchlist ) containing all the remote-tracking names. The if/then/else/end sequence drops the HEAD symbolic ref and the sed cleans out the corresponding blank line (not strictly necessary, but useful).

Some of these branch names might already be fine! We probably don't want to fuss with those. So now we can grep for uppercase:

LC_ALL=C grep -E '^[A-Z]+$' /tmp/branchlist > /tmp/upper

The LC_ALL here is necessary in case your grep uses POSIX collation (mine does on at least some systems).

It's now probably wise to inspect /tmp/upper to make sure that none of the names in it also appear as lowercase in /tmp/branchlist . This is a one-time manual operation, but you can automate it if you like:

tr A-Z a-z < /tmp/upper | grep -F -f - /tmp/branchlist

Note that this will erroneously match substrings, eg, if there's a "bad" name OOPS and a "good" name whoops , this will find the "good" name even though it's different; fixing that is harder and I won't bother.

We now want to create, in our local repository, one lowercase-name branch whose name matches the uppercase name:

awk '{ print "git branch --no-track " tolower($0) " refs/remotes/origin/" $0 }' \
  < /tmp/upper

Make sure this generates the desired commands and then pipe into sh :

awk '{ print "git branch --no-track " tolower($0) " refs/remotes/origin/" $0 }' \
  < /tmp/upper | sh

and we've generated the desired branch names.

Rather than creating such branch names, though, it makes more sense to me to run a git push command that creates the names directly on the remote.

To do that, we want:

git push origin

followed by a series of local remote-tracking names and the desired branch names: for each uppercase name, we want a lowercase-ified version of that name as a branch name. So that would be:

(printf "git push origin"
awk '{ printf(" %s:%s", "refs/remotes/origin/" $0, "refs/heads/" tolower($0)) }' \
  < /tmp/upper
echo "")

Verify that this produces the desired git push command, then pipe to sh.

You may wish to git push --delete the uppercase-only names later; it should now be obvious how to do this. After that, other users of this repository might want to run git fetch --prune to update their remote-tracking names.

I will try but I'm really afraid about actions like this :|. So, be careful and ensure you are not going to loose your data.

# Create three random branches for the test
git branch www_FUNK0 origin/master
git branch WWW_funk1 origin/master
git branch WWW_FuNk2 origin/master

git branch | grep -i www_funk
# This is the output of this command:
#  www_FUNK0
#  WWW_funk1
#  WWW_FuNk2

# Iterating through all WWW_FUNK* branches one by one
for b in $(git branch | grep -i www_funk | tr -d ' ')
do 
    # Storing the $b branch into the tmp branch and then
    # delete $b branch
    # and rename tmp branch to the lowercase version of $b
    git checkout -B tmp $b
    git branch -D $b
    git branch -m ${b,,}
done

git checkout master
git branch -D tmp

git branch | grep -i www_funk
# Finally we have this result:
#  www_funk0
#  www_funk1
#  www_funk2

Please do not try those drills in a repo with useful data!!!

git branch --format='%(refname:short)' \
| sed -n '/[A-Z]/s/.*/git branch -m & \L&/p'

to do branches; if you want other refs you'll have to get more finicky about it, for instance changing remote-tracking names locally won't do anyone any good at all, you need to push the update:

git for-each-ref --format='%(if)%(symref)%(then)%(else)%(refname:short)%(end)'  \
        refs/remotes \
|  sed -nr 's,([^/]*)/(.*),git push \1 refs/remotes/&:refs/heads/\L\2,p'

and then git fetch --prune .

Using bash , here are two ways to parse branch names correctly .

Simple way: Read the branch list line by line, relying on the fact that branch names can't contain new lines:

git branch --format '%(refname:lstrip=-1)' |
while IFS= read -r name; do
    git branch -m "$name" "${name,,}"
done

"Proper" way: Use git for-each-ref , with --shell option to safely quote each name, such that it can be eval ed. The quoted names can be added to an array, to loop over:

eval "names=($(
    git for-each-ref refs/heads \
    --format '%(refname:lstrip=-1)' \
    --shell
))"

for name in "${names[@]}"; do
    git branch -m "$name" "${name,,}"
done
  • The format string strips parent directories from the branch name ( refs/heads ).
  • For git branch , it also prints names only, without the leading spaces and asterix.
  • ${name,,} is bash syntax to lowercase all characters in the variable.
  • I prefer the first command, as it's simpler and less error-prone.
  • Note that if you manually renamed the file refs/heads/branch-name to contain a new line, git refuses to list that branch and prints a broken name error (for both git branch and git for-each-ref ). That is good.
  • Both these commands require bash, and won't work in /bin/sh .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM