Created
January 23, 2021 02:46
-
-
Save domartynov/821d767d1e1b8150bf92108a6591a9b6 to your computer and use it in GitHub Desktop.
Merge GIT repos in subdirs while keeping history (using pygit2)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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())) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
TODO reverse history (and min -> max) 🤪