#[1]gnikyt feed [2]gnikyt Code ramblings. Git release branches /* Mar 10, 2021 — 3.4KB */ A method I like to utilize internally, which I’ve been slowly pushing into my open source work, is to create branches for each major/minor release. Many developers will say branches are meant to be short-lived, to be eventually merged into your main branch. I believe this is true for features and bugfixes, but there is a benefit to keeping branches around for the long term. Essentially, if you follow symver ([major].[minor].[patch]) for versioning, you would create and maintain a branch for each [major].[minor] release. For example… if I tagged the production branch with v9.0.0, a new major release, I would create a supplementary branch 9.0. If I then released a bugfix, v9.0.1, I would then issue a pull request to merge code for v9.0.1 into branch 9.0 to keep it updated with patches. $ git branch -l | sort -V # ... 8.2 8.3 9.0 develop master By having your releases tagged and supplementing it with release branches, you will: * Have the ability to rollback easily * Have the ability to support multiple versions easily * Have a easily navigable snapshot of previous releases This may be overkill for small and personal projects, but when you’re working with a team and potentially have to support multiple versions of a codebase, then this flow is easy to adopt. Below is a script you can use to walk through your existing repository (assuming everything is tagged in symver format) and create the [major].[minor] branches for you. #!/usr/bin/env python # # Usage: `python release_branches.py` # For a "dry run": `DRY_RUN=1 python release_branches.py` # import subprocess import re from os import environ from typing import List, Dict def get_tag_list() -> List[str]: """ Get list of tags, sorted. """ # Ensure tags are in vX.Y.Z format mmp = re.compile(r"^v([0-9]+).([0-9]+).([0-9]+)$") # Get all tags that match the format tags = subprocess.getoutput(" ".join(("git", "tag", "-l"))).split("\n") result = list(filter(lambda tag: (mmp.match(tag) is not None), tags)) result = [tag[1:] for tag in result] result.sort(reverse=True) return result def major_minor_list(tags: List[str]) -> Dict[str, str]: """ Create a list of major.minor versions. """ mm = {} for tag in tags: # Strip off patch from tag cleaned_tag = ".".join(tag.split(".")[:-1]) if cleaned_tag not in mm: # Add to dict if major.minor doesn't exist yet mm[cleaned_tag] = tag return mm def make_branch(branch: str, tag: str) -> None: """ Make a branch from a tag. """ # Checkout tag, create branch of major.minor based on tag, push to BB subprocess.run(["git", "checkout", f"v{tag}"]) subprocess.run(["git", "checkout", "-b", branch]) subprocess.run(["git", "push", "origin", branch]) tags = get_tag_list() mm = major_minor_list(tags) for branch, tag in mm.items(): print(f"Making branch '{branch}' based on tag of '{tag}'...") if environ.get('DRY_RUN') is None: make_branch(branch, tag) print(">> Complete") Running with the DRY_RUN env supplied, you’ll be able to see the branches it will created based on your existing tags before proceeding with the creation. [3]MD | [4]TXT | [5]CC-4.0 This post is 4 years old and may contain outdated information. __________________________________________________________________ [6]Ty King Ty King A self-taught, seasoned, and versatile developer from Newfoundland. Crafting innovative solutions with care and expertise. See more [7]about me. [8]Github [9]LinkedIn [10]CV [11]RSS * * * * * * * * * * References Visible links: 1. /rss.xml 2. / 3. /git-release-branches/index.md 4. /git-release-branches/index.txt 5. https://creativecommons.org/licenses/by/4.0/ 6. /about 7. /about 8. https://github.com/gnikyt 9. https://linkedin.com/in/gnikyt 10. /assets/files/cv.pdf 11. /rss.xml Hidden links: 13. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb1-1 14. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb1-2 15. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb1-3 16. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb1-4 17. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb1-5 18. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb1-6 19. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb1-7 20. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-1 21. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-2 22. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-3 23. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-4 24. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-5 25. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-6 26. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-7 27. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-8 28. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-9 29. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-10 30. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-11 31. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-12 32. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-13 33. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-14 34. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-15 35. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-16 36. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-17 37. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-18 38. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-19 39. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-20 40. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-21 41. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-22 42. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-23 43. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-24 44. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-25 45. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-26 46. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-27 47. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-28 48. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-29 49. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-30 50. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-31 51. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-32 52. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-33 53. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-34 54. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-35 55. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-36 56. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-37 57. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-38 58. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-39 59. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-40 60. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-41 61. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-42 62. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-43 63. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-44 64. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-45 65. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-46 66. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-47 67. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-48 68. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-49 69. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-50 70. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-51 71. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-52 72. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-53 73. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-54 74. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-55 75. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-56 76. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-57 77. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-58 78. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-59 79. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-60 80. localhost/tmp/lynxXXXXnfzi54/L760633-5688TMP.html#cb2-61