git-obsolete-branch.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. #!/usr/bin/python
  2. #
  3. # Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
  4. #
  5. # Permission to use, copy, modify, and/or distribute this software for any
  6. # purpose with or without fee is hereby granted, provided that the above
  7. # copyright notice and this permission notice appear in all copies.
  8. #
  9. # THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  10. # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  11. # AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  12. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  13. # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  14. # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  15. # PERFORMANCE OF THIS SOFTWARE.
  16. #
  17. # This script lists obsolete (fully merged) branches. It is useful for periodic maintenance
  18. # of our GIT tree.
  19. # It is good to use following command before running this script:
  20. #
  21. # git pull
  22. # git remote prune origin
  23. #
  24. # This script requires python 2.7 or 3.
  25. #
  26. # I have limited experience in Python. If things are done in a strange or uncommon way, there
  27. # are no obscure reasons to do it that way, just plain lack of experience.
  28. #
  29. # tomek
  30. import string
  31. import subprocess
  32. import sys
  33. class Branch:
  34. MERGED=1
  35. NOTMERGED=2
  36. name = ""
  37. status = NOTMERGED
  38. last_commit = ""
  39. def branch_list_get(verbose):
  40. txt_list = subprocess.check_output(["git", "branch", "-r"])
  41. txt_list = txt_list.split(b"\n")
  42. out = []
  43. for branch in txt_list:
  44. if len(branch) == 0:
  45. continue
  46. if branch.find(b"->") != -1:
  47. continue
  48. if branch == b"origin/master":
  49. continue
  50. branch_info = Branch()
  51. # get branch name
  52. branch_info.name = branch.strip(b" ")
  53. branch_info.name = branch_info.name.decode("utf-8")
  54. # check if branch is merged or not
  55. if verbose:
  56. print("Checking branch %s" % branch_info.name)
  57. cmd = ["git", "diff", "master..." + branch_info.name ]
  58. diff = subprocess.check_output(cmd)
  59. if (len(diff) == 0):
  60. branch_info.status = Branch.MERGED
  61. # let's get the last contributor
  62. cmd = [ "git" , "log", "-n", "1", "--pretty=\"%ai,%ae,%an\"", branch_info.name ]
  63. offender = subprocess.check_output(cmd)
  64. offender = offender.strip(b"\n\"")
  65. # comment out this 2 lines to disable obfuscation
  66. offender = offender.replace(b"@", b"(at)")
  67. offender = offender.replace(b".", b"(dot)")
  68. branch_info.last_commit = offender.decode("utf-8")
  69. else:
  70. branch_info.status = Branch.NOTMERGED
  71. out.append(branch_info)
  72. # return (out)
  73. return (out)
  74. def branch_print(branches, csv, print_merged, print_notmerged, print_stats):
  75. merged = 0
  76. notmerged = 0
  77. merged_str = ""
  78. notmerged_str = ""
  79. for branch in branches:
  80. if (branch.status==Branch.MERGED):
  81. merged = merged + 1
  82. if (not print_merged):
  83. continue
  84. if (csv):
  85. print("%s,merged,%s" % (branch.name, branch.last_commit) )
  86. else:
  87. merged_str = merged_str + " " + branch.name
  88. else:
  89. # NOT MERGED
  90. notmerged = notmerged + 1
  91. if (not print_notmerged):
  92. continue
  93. if (csv):
  94. print("%s,notmerged,%s" % (branch.name, branch.last_commit) )
  95. else:
  96. notmerged_str = notmerged_str + " " + branch.name
  97. if (not csv):
  98. if (print_merged):
  99. print("Merged branches : %s" % (merged_str))
  100. if (print_notmerged):
  101. print("NOT merged branches: %s" % (notmerged_str))
  102. if (print_stats):
  103. print("#----------")
  104. print("#Merged : %d" % merged)
  105. print("#Not merged: %d" % notmerged)
  106. def show_help():
  107. print("This script prints out merged and/or unmerged branches of a GIT tree.")
  108. print("Supported command-line options:")
  109. print("")
  110. print("--csv produce CSV (coma separated value) output")
  111. print("--unmerged lists umerged branches")
  112. print("--skip-merged do not print merged branches (that are listed by default)")
  113. print("--stats prints out statistics")
  114. print("--help prints out this help")
  115. def main():
  116. usage = """%prog
  117. Lists all obsolete (fully merged into master) branches.
  118. """
  119. csv = False
  120. merged = True
  121. unmerged = False
  122. stats = False
  123. for x in sys.argv[1:]:
  124. if x == "--csv":
  125. csv = True
  126. elif x == "--unmerged":
  127. unmerged = True
  128. elif x == "--skip-merged":
  129. merged = False
  130. elif x == "--stats":
  131. stats = True
  132. elif x == "--help":
  133. show_help()
  134. return
  135. else:
  136. print("Invalid parameter: %s" % x)
  137. print("")
  138. show_help()
  139. return
  140. if csv:
  141. print("branch name,status,date,last commit(mail),last commit(name)")
  142. branch_list = branch_list_get(not csv)
  143. # Uncomment this to print out also merged branches
  144. # branch_print(branch_list, False, True, False)
  145. branch_print(branch_list, csv, merged, unmerged, stats)
  146. if __name__ == '__main__':
  147. main()