Skip to content

Instantly share code, notes, and snippets.

@domartynov
Created January 23, 2021 02:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save domartynov/821d767d1e1b8150bf92108a6591a9b6 to your computer and use it in GitHub Desktop.
Save domartynov/821d767d1e1b8150bf92108a6591a9b6 to your computer and use it in GitHub Desktop.
Merge GIT repos in subdirs while keeping history (using pygit2)
"""
Applied to a repo:
git init
git remote add R1 url1
git fetch R1 master
. . .
git remote add RN urlN
git fetch RN master
Replays commits from remotes in chronological order,
while nesting each remote tree in a subdir with the name of the remote.
So `git checkout head && ls -AF --format=single-column` outputs:
.git/
R1/
. . .
RN/
"""
from typing import Optional
import sys
import os
from pathlib import Path
from pygit2 import Repository, Commit, GIT_FILEMODE_TREE
def main(wd):
repo = Repository(wd.absolute())
heads = []
for rm in repo.remotes:
rr = next(rf for rf in repo.references if rf.startswith(f'refs/remotes/{rm.name}/'))
rc = repo[repo.lookup_reference(rr).target]
heads.append(RemoteHead(rm.name, rc))
tb = repo.TreeBuilder()
try:
pid = repo.lookup_reference_dwim('HEAD').target
except KeyError:
pid = None
while any(1 for h in heads if h.head):
next_head = min((h for h in heads if h.head), key=lambda h: h.head.commit_time)
name, commit = next_head.name, next_head.head
next_head.head = commit.parents[0] if commit.parents else None
if tb.get(name):
tb.remove(name)
tb.insert(name, commit.tree_id, GIT_FILEMODE_TREE)
tid = tb.write()
pid = repo.create_commit('HEAD', commit.author, commit.committer, commit.message, tid, [pid] if pid else [])
print(repo[pid])
class RemoteHead:
def __init__(self, name: str, head: Optional[Commit]):
self.name = name
self.head = head
if __name__ == '__main__':
main(Path(sys.argv[1] if len(sys.argv) > 1 else os.getcwd()))
@domartynov
Copy link
Author

TODO reverse history (and min -> max) 🤪

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment