1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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.")
|