local-to-remote sync is working
authorRalf Jung <post@ralfj.de>
Sat, 21 Feb 2015 18:27:49 +0000 (19:27 +0100)
committerRalf Jung <post@ralfj.de>
Sat, 21 Feb 2015 18:27:49 +0000 (19:27 +0100)
update.py [new file with mode: 0755]

diff --git a/update.py b/update.py
new file mode 100755 (executable)
index 0000000..80334f5
--- /dev/null
+++ b/update.py
@@ -0,0 +1,96 @@
+#!/usr/bin/python3
+import sys, os, subprocess, argparse
+
+class GitCommand:
+    def __getattr__(self, name):
+        def call(*args, get_stderr = False):
+            cmd = ["git", name.replace('_', '-')] + list(args)
+            output = subprocess.check_output(cmd, stderr=subprocess.STDOUT if get_stderr else None)
+            return output.decode('utf-8').strip('\n')
+        return call
+    
+    def branches(self, *args):
+        b = self.branch(*args).split('\n')
+        b = map(lambda s: s[2:], b)
+        return list(b)
+
+git = GitCommand()
+
+def is_all_zero(str):
+    return len(str.replace('0', '')) == 0
+
+class Repo:
+    def __init__(self, local, mirrors):
+        '''<local> is the directory containing the repository locally, <mirrors> a list of remote repositories'''
+        self.local = local
+        self.mirrors = mirrors
+    
+#    This is old code, that may be useful again if we decide to care about racy pushes loosing commits.
+#    def pull(self, slavenr):
+#        slave = self.slaves[slavenr]
+#        slavename = "slave-"+str(slavenr)
+#        # make sure we have the remote
+#        try:
+#            git.remote("add", slavename, slave, get_stderr=True)
+#        except subprocess.CalledProcessError: # the remote already exists
+#            git.remote("set-url", slavename, slave)
+#        # get all the changes
+#        git.fetch(slavename, get_stderr=True)
+#        # merge them... or hope so...
+#        branches = git.branches("-r")
+#        for branch in filter(lambda s: s.startswith(slavename+"/"), branches):
+#            local = branch[len(slavename+"/"):]
+#            print(local, branch)
+
+    def update_mirror_ref(self, ref, mirror):
+        '''Update <ref> on <mirror> to the local state. If <newsha> is all-zero, the ref should be deleted.'''
+        git.push('--force', self.mirrors[mirror], ref)
+    
+    def update_ref(self, newsha, ref, source):
+        '''Update the <ref> to <newsha> everywhere. <source> is None if this update comes from the local repository,
+           or the name of a mirror. If <newsha> is all-zero, the ref should be deleted.'''
+        os.chdir(self.local)
+        if source is None:
+            # We already have the latest version locally. Update all the mirrors.
+            for mirror in self.mirrors:
+                self.update_mirror_ref(ref, mirror)
+        else:
+            raise Exception("Help, what should I do?")
+
+# for now, configuration is hard-coded here...
+
+repos = {
+    'sync-test': Repo('/home/git/repositories/test.git', {'github': 'git@github.com:RalfJung/sync-test.git'}),
+}
+
+def find_repo_by_directory(dir):
+    for (name, repo) in repos.items():
+        if dir == repo.local:
+            return name
+    return None
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description='Keep git repositories in sync')
+    parser.add_argument("--hook",
+                        action="store_true", dest="hook",
+                        help="Act as git hook: Auto-detect the repository based on the working directoy, and fetch information from stdin")
+    parser.add_argument("-r", "--repository",
+                        dest="repository",
+                        help="The name of the repository to act on")
+    args = parser.parse_args()
+    
+    reponame = args.repository
+    if reponame is None and args.hook:
+        reponame = find_repo_by_directory(os.getcwd())
+    if reponame is None:
+        raise Exception("Unable to detect repository, please use --repository.")
+    
+    # now sync this repository
+    repo = repos[reponame]
+    if args.hook:
+        # parse the information we get from stdin
+        for line in sys.stdin:
+            (oldsha, newsha, ref) = line.split()
+            repo.update_ref(newsha, ref, source=None)
+    else:
+        raise Exception("I am unsure what to do here.")