summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndré Glüpker <git@wgmd.de>2018-06-04 18:57:06 +0200
committerAndré Glüpker <git@wgmd.de>2018-06-04 18:57:06 +0200
commit3080f80ea6c444073cd956d07bcf1d342d8b95b5 (patch)
tree72c0c075a2f6d6c2bec2ea8221a68a7974363ccd
downloadupdate-git-3080f80ea6c444073cd956d07bcf1d342d8b95b5.tar.gz
update-git-3080f80ea6c444073cd956d07bcf1d342d8b95b5.tar.bz2
update-git-3080f80ea6c444073cd956d07bcf1d342d8b95b5.zip
Initial released version
-rwxr-xr-xupdateGit.py185
1 files changed, 185 insertions, 0 deletions
diff --git a/updateGit.py b/updateGit.py
new file mode 100755
index 0000000..10acc45
--- /dev/null
+++ b/updateGit.py
@@ -0,0 +1,185 @@
+#!/usr/bin/env python3
+
+import os
+import subprocess
+import sys
+from concurrent.futures import ThreadPoolExecutor
+
+SAFEBRANCHES = ['master', 'develop', 'rc']
+DEBUG = False
+
+# TODO: local changes, branch updated. Is there a way to know, whether rebase would be successful? If yes: do so
+
+def dPrint(*args, **kwargs):
+ if DEBUG:
+ print(' '.join(map(str, args)), **kwargs)
+
+def getStdout(command):
+ process = None
+ process = subprocess.run(command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ if process.returncode != 0:
+ print('Failed to execute command:', command)
+ if process.stdout:
+ print(' Stdout:')
+ print(process.stdout.decode('UTF-8'))
+ if process.stderr:
+ print(' Stderr:')
+ print(process.stderr.decode('UTF-8'))
+ return ''
+ if process.stdout:
+ return process.stdout.decode('UTF-8').strip()
+ return ''
+
+def getGitStdout(path, command):
+ dPrint('[ running', command, 'on', path, ']')
+ if isinstance(command, str):
+ cmd = 'git -C %s %s' % (path, command)
+ return getStdout(cmd.split())
+ if isinstance(command, list):
+ cmd = ['git', '-C', path]
+ cmd.extend(command)
+ return getStdout(cmd)
+
+def findChanges(statusoutput):
+ dPrint('Parsing for changes:')
+ for line in statusoutput.split('\n'):
+ dPrint(' Status Line:', line)
+ if line.startswith('A ') or line.startswith('D ') or line.startswith('M '):
+ return True
+ return False
+
+def updatePath(path):
+ friendly_path = os.path.basename(path)
+ dPrint('Repository:', path)
+ head = getGitStdout(path, 'rev-parse --abbrev-ref HEAD')
+ changed = findChanges(getGitStdout(path, 'status --porcelain'))
+ cur_branch = getGitStdout(path, 'rev-parse --abbrev-ref HEAD')
+
+ # Fetch changes, delete removed repositories
+ getGitStdout(path, 'fetch --all --prune')
+
+ # Go through branches and merge with upstream
+ branches = getGitStdout(path, [
+ 'for-each-ref',
+ '--format=%(refname:short)#%(upstream)#%(upstream:track,nobracket)',
+ 'refs/heads'
+ ])
+ for branch in branches.split('\n'):
+ local_branch, upstream_branch, tracking = branch.split('#')
+ if not upstream_branch or tracking == 'gone':
+ print('[%s]' % friendly_path, local_branch, 'has no upstream branch.')
+ continue
+ if local_branch == cur_branch and changed:
+ print('[%s]' % friendly_path, 'has a working branch with changes, skipped.')
+ continue
+ dPrint('Update for', friendly_path, local_branch, upstream_branch)
+ local = getGitStdout(path, ['rev-parse', '--quiet', '--verify', local_branch])
+ remote = getGitStdout(path, ['rev-parse', '--quiet', '--verify', upstream_branch])
+ base = getGitStdout(path, ['merge-base', local_branch, upstream_branch])
+ dPrint(' Branch:', local_branch)
+ dPrint(' Upstream:', upstream_branch)
+ dPrint(' Local', local)
+ dPrint(' Remote', remote)
+ dPrint(' Base', base)
+ if local == remote:
+ continue
+ if local == base:
+ if local_branch == cur_branch:
+ print('[%s]' % friendly_path, 'Updated branch ', cur_branch,
+ getGitStdout(path, ['merge', remote]))
+ else:
+ print('[%s] Updated branch %s:' % (friendly_path, cur_branch),
+ getGitStdout(path, ['branch', '-f', local_branch, remote]))
+ if remote == base:
+ print('[%s] %s is ahead. Consider pushing your changes.' % (friendly_path, local_branch))
+
+ # Find safe branch (master/development/rc)
+ branches = getGitStdout(path, [
+ 'for-each-ref',
+ '--format=%(refname:short)',
+ 'refs/heads'
+ ]).split('\n')
+ for branch in SAFEBRANCHES:
+ if branch in branches:
+ safe_branch = branch
+ break
+
+ # No safe branch found. What is this?
+ if not safe_branch:
+ print('Does the repository', friendly_path, 'not have any safe branch?')
+ return
+
+ # print('Safe branch:', safe_branch)
+
+ # Checkout master, if detached HEAD and not proceeded
+ if head == 'HEAD':
+ if changed:
+ print('[%s] Head detached, but changes found. Stash them to checkout %s.' % (
+ friendly_path,
+ safe_branch
+ ))
+ else:
+ headrev = getGitStdout(path, 'rev-parse --quiet --verify HEAD')
+ mergerev = getGitStdout(path, ['merge-base', 'HEAD', safe_branch])
+ if headrev == mergerev:
+ print('[%s] Head detached, checking %s out.' % (friendly_path, safe_branch),
+ getGitStdout(path, 'checkout ' + safe_branch))
+ else:
+ print('[%s] Head detached, but not part of safe branch!' % friendly_path)
+
+ # Delete branches, that were already merged to master
+ merged_branches = getGitStdout(path, [
+ 'branch',
+ '--format=%(refname:short)',
+ '--merged=%s' % safe_branch
+ ]).split('\n')
+ # print('[%s]' % friendly_path, 'Deletion of branches', merged_branches, SAFEBRANCHES, cur_branch)
+ for merged_branch in merged_branches:
+ if merged_branch in SAFEBRANCHES:
+ continue
+ if merged_branch == cur_branch:
+ continue
+ get_tracking_status = getGitStdout(path, [
+ 'for-each-ref',
+ '--format=%(upstream:track,nobracket)',
+ 'refs/heads/%s' % merged_branch
+ ])
+ if get_tracking_status.strip() == 'gone':
+ print('[%s]' % friendly_path, 'Removing branch by force, upstream is gone.')
+ print('[%s]' % friendly_path,
+ getGitStdout(path, ['branch', '-D', merged_branch]))
+ else:
+ print('[%s]' % friendly_path,
+ getGitStdout(path, ['branch', '-d', merged_branch]))
+
+def main():
+ paths = []
+ if len(sys.argv) == 1:
+ paths.append('.')
+ else:
+ for path in sys.argv[1:]:
+ paths.append(path)
+
+ repos = []
+ for path in paths:
+ for subdir, dirs, _ in os.walk(path):
+ # dPrint(subdir, dir, files)
+ if '.git' in dirs:
+ repos.append(subdir)
+
+ if len(repos) == 0:
+ print('No repositories found.')
+ return
+
+ print('Updating', len(repos), 'repositories.')
+ executor = ThreadPoolExecutor(max_workers=len(repos))
+ for repo in repos:
+ # updatePath(subdir)
+ executor.submit(updatePath, repo)
+ executor.shutdown()
+
+
+if __name__ == "__main__":
+ main()