Files
Bubberstation/tools/mapmerge2/precommit.py
Alex 'Avunia' Takiya bea83b2999 [MANUAL MIRROR] Use portable Python for map merge hooks, other tools / Add .dmm merge driver (#2681)
* Use portable Python for map merge hooks, other tools (#55658)

* Add .dmm merge driver (#55699)

This is kind of a prototype. It only fully handles a few situations, 
and doesn't produce particularly easy-to-read conflict markers when it 
fails. I hope that it can be useful at least some of the time, can be 
improved over time, and that the lessons learned can influence a future 
interactive GUI conflict resolver (integrated into StrongDMM?). In the 
worst case, one can fall back to the tried and true "manually re-do one 
side's changes" strategy. 

**Automatic use**: In `tools/hooks/`, run `Install.bat`

**Manual use**, for Git GUIs that don't run merge drivers: while a 
merge is in progress, in `tools/mapmerge2/`, double-click `Resolve Map 
Conflicts.bat`

This PR also removes the error-prone "Prepare Maps.bat" / 
"mapmerge.bat" workflow. Those who aren't using the hooks should 
instead use `Run Before Committing.bat` before committing. First-time 
contributors who opened a PR without map merging can be advised to run 
`I Forgot To Map Merge.bat`.

* Fix loose double-quot in tradership_faction.dmm

Co-authored-by: Tad Hardesty <tad@platymuus.com>
2021-01-14 16:52:24 +01:00

70 lines
2.5 KiB
Python

#!/usr/bin/env python3
import os
import sys
import pygit2
from . import dmm
from .mapmerge import merge_map
def main(repo, *, use_workdir=False):
if repo.index.conflicts:
print("You need to resolve merge conflicts first.")
return 1
try:
repo.lookup_reference('MERGE_HEAD')
print("Not running mapmerge for merge commit.")
return 0
except KeyError:
pass
target_statuses = pygit2.GIT_STATUS_INDEX_MODIFIED | pygit2.GIT_STATUS_INDEX_NEW
skip_to_file_statuses = pygit2.GIT_STATUS_WT_DELETED | pygit2.GIT_STATUS_WT_MODIFIED
if use_workdir:
target_statuses |= pygit2.GIT_STATUS_WT_MODIFIED | pygit2.GIT_STATUS_WT_NEW
skip_to_file_statuses &= ~pygit2.GIT_STATUS_WT_MODIFIED
changed = 0
for path, status in repo.status().items():
if path.endswith(".dmm") and (status & target_statuses):
# read the index
index_entry = repo.index[path]
if use_workdir:
index_map = dmm.DMM.from_file(os.path.join(repo.workdir, path))
else:
index_map = dmm.DMM.from_bytes(repo[index_entry.id].read_raw())
try:
head_blob = repo[repo[repo.head.target].tree[path].id]
except KeyError:
# New map, no entry in HEAD
print(f"Converting new map: {path}", flush=True)
assert (status & pygit2.GIT_STATUS_INDEX_NEW)
merged_map = index_map
else:
# Entry in HEAD, merge the index over it
print(f"Converting map: {path}", flush=True)
assert not (status & pygit2.GIT_STATUS_INDEX_NEW)
head_map = dmm.DMM.from_bytes(head_blob.read_raw())
merged_map = merge_map(index_map, head_map)
# write to the index
blob_id = repo.create_blob(merged_map.to_bytes())
repo.index.add(pygit2.IndexEntry(path, blob_id, index_entry.mode))
changed += 1
# write to the working directory if that's clean
if status & skip_to_file_statuses:
print(f"Warning: {path} has unindexed changes, not overwriting them")
else:
merged_map.to_file(os.path.join(repo.workdir, path))
if changed:
repo.index.write()
return 0
if __name__ == '__main__':
repo = pygit2.Repository(pygit2.discover_repository(os.getcwd()))
exit(main(repo, use_workdir='--use-workdir' in sys.argv))