Skip to content
This repository has been archived by the owner on Oct 9, 2023. It is now read-only.

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
KOLANICH committed Oct 9, 2023
0 parents commit e586ae2
Show file tree
Hide file tree
Showing 22 changed files with 499 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
root = true

[*]
charset = utf-8
indent_style = tab
indent_size = 4
insert_final_newline = true
end_of_line = lf

[*.{yml,yaml}]
indent_style = space
indent_size = 2
1 change: 1 addition & 0 deletions .github/.templateMarker
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
KOLANICH/python_project_boilerplate.py
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "daily"
allow:
- dependency-type: "all"
15 changes: 15 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: CI
on:
push:
branches: [master]
pull_request:
branches: [master]

jobs:
build:
runs-on: ubuntu-22.04
steps:
- name: typical python workflow
uses: KOLANICH-GHActions/typical-python-workflow@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
__pycache__
*.pyc
*.pyo
/*.egg-info
/build
/dist
/.eggs
/monkeytype.sqlite3
/*.srctrldb
/*.srctrlbm
/*.srctrlprj
53 changes: 53 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
image: registry.gitlab.com/kolanich-subgroups/docker-images/fixed_python:latest
#image: registry.gitlab.com/kolanich-subgroups/docker-images/python_graal:latest

stages:
- build
- trigger
- test

variables:
DOCKER_DRIVER: overlay2
SAST_ANALYZER_IMAGE_TAG: latest
SAST_DISABLE_DIND: "true"

include:
- template: SAST.gitlab-ci.yml
#- template: DAST.gitlab-ci.yml
- template: License-Management.gitlab-ci.yml
#- template: Container-Scanning.gitlab-ci.yml
#- template: Dependency-Scanning.gitlab-ci.yml
- template: Code-Quality.gitlab-ci.yml

build:
tags:
- shared
- linux
stage: build
variables:
GIT_DEPTH: "1"
PYTHONUSERBASE: ${CI_PROJECT_DIR}/python_user_packages

before_script:
- export PATH="$PATH:$PYTHONUSERBASE/bin" # don't move into `variables`

script:
- python3 setup.py bdist_wheel
- mv ./dist/*.whl ./dist/patchSeq-0.CI-py3-none-any.whl
- pip install --user --upgrade --pre ./dist/*.whl
- coverage run -a --source=patchSeq -m pytest --junitxml=./rspec.xml ./tests/tests.py
#- coverage run -a --source=patchSeq -m patchSeq
- coverage xml

cache:
paths:
- /usr/local/site-packages
- /usr/local/lib/python*/site-packages

artifacts:
paths:
- dist
reports:
junit: ./rspec.xml
cobertura: ./coverage.xml

1 change: 1 addition & 0 deletions Code_Of_Conduct.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
No codes of conduct!
5 changes: 5 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
include UNLICENSE
include *.md
exclude tests
include .editorconfig
global-include *.json
31 changes: 31 additions & 0 deletions ReadMe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
patchSeq.py [![Unlicensed work](https://raw.githubusercontent.com/unlicense/unlicense.org/master/static/favicon.png)](https://unlicense.org/)
===============
~~[![GitLab Build Status](https://gitlab.com/KOLANICH/patchSeq.py/badges/master/pipeline.svg)](https://gitlab.com/KOLANICH/patchSeq.py/-/jobs/artifacts/master/raw/dist/patchSeq.py-0.CI-py3-none-any.whl?job=build)~~
~~![GitLab Coverage](https://gitlab.com/KOLANICH/patchSeq.py/badges/master/coverage.svg)~~
[![Libraries.io Status](https://img.shields.io/librariesio/github/KOLANICH/patchSeq.py.svg)](https://libraries.io/github/KOLANICH/patchSeq.py)
[![Code style: antiflash](https://img.shields.io/badge/code%20style-antiflash-FFF.svg)](https://codeberg.org/KOLANICH-tools/antiflash.py)

[![Stale](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/) Please consider using https://gitlab.com/esr/reposurgeon instead of this unfinished tool first, it is likely that `reposurgeon` has everything you need, and it deals with the repo formats of a VCSs it supports directly, which minimizes information loss and improves speed. For me it has solved all my current needs so this tool is left unfinished.

`git filter-branch` has proven to be **too** limited and slow. We need something better.

It turned out that even exporting commits into a dir as patches and then applying them is faster.

So I have written this tool. It works on patches and probably can work with any VCS that can import and export patches in unified diff format, not only git.

It provides the following primitives:
* importing patches
* remove patches not touching certain files.
* merge identically named patches in 2 separate folders
* renumbering patches
* removing empty patches
* renaming/moving files

Using them you can for example extract parts of other projects preserving linear history - something that `git-filter-branch` cannot do efficiently for now: `--subdirectory-filter` currently allows only a single dir, and disallows a file at all, other stuff is damn slow.

BFG repo cleaner is limited, its development has stopped and is written in shitty Scala I don't want to touch.


Requirements
------------
* [`unidiff`](https://github.com/matiasb/python-unidiff)[![PyPi Status](https://img.shields.io/pypi/v/unidiff.svg)](https://pypi.org/pypi/unidiff)[![Travis build](https://img.shields.io/travis/matiasb/python-unidiff/master.svg)](https://travis-ci.org/matiasb/python-unidiff)[![Coveralls Coverage](https://img.shields.io/coveralls/matiasb/python-unidiff.svg)](https://coveralls.io/r/matiasb/python-unidiff)[![Libraries.io Status](https://img.shields.io/librariesio/github/matiasb/python-unidiff.svg)](https://libraries.io/github/matiasb/python-unidiff)![Licence](https://img.shields.io/github/license/matiasb/python-unidiff.svg)
24 changes: 24 additions & 0 deletions UNLICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <https://unlicense.org/>
Empty file added patchSeq/__init__.py
Empty file.
23 changes: 23 additions & 0 deletions patchSeq/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from plumbum import cli

from . import *
from .passes.ImportPass import ImportPass
from .passes.RemoveTheRest import RemoveTheRestPass


class PatchSeqCLI(cli.Application):
pass


@PatchSeqCLI.subcommand("remove-the-rest")
class PatchSeqRemoveTheRestCLI(cli.Application):
inputDir = cli.SwitchAttr("-i", help="Input dir")
outputDir = cli.SwitchAttr("-o", help="Output dir")

def main(self, *whatToKeep):
s = PassesStack([ImportPass(self.inputDir), RemoveTheRestPass(whatToKeep)], self.outputDir)
s()


if __name__ == "__main__":
PatchSeqCLI.run()
132 changes: 132 additions & 0 deletions patchSeq/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import typing
from abc import ABC, abstractmethod
from itertools import tee
from pathlib import Path, PurePath

import unidiff
from fsutilz import isGlobPattern, isNestedIn, nestPath, relativePath

from .utils import parsePatchName


class Processor(ABC):
__slots__ = ()

@abstractmethod
def __call__(self):
raise NotImplementedError


class PatchInfo:
__slots__ = ("name", "no")

def __init__(self, path: Path):
self.fileName = path.name

@property
def fileName(self):
return assemblePatchName(self.no, self.name)

@fileName.setter
def fileName(self, name):
self.no, self.name = parsePatchName(name)

def toTuple(self):
return (self.no, self.name)

def __hash__(self):
# return hash(self.toTuple())
return hash(self.no)

def __cmp__(self, other: "PatchInfo"):
return self.no - other.no


class PopulatedPatchInfo:
__slots__ = ("info", "patch")

def __init__(self, info: PatchInfo):
self.info = info
self.patch = patch

def __cmp__(self, other: PatchInfo):
return info.__cmp__(other.info)


class KeepingStrategy(ABC):
__slots__ = ()

def keep(self, ppi: PopulatedPatchInfo):
self[ppi.info] = ppi.patch

@abstractmethod
def __getitem__(self, pi: PatchInfo):
raise NotImplementedError

@abstractmethod
def __setitem__(self, pi: PatchInfo, p):
raise NotImplementedError


class InMemoryKeepingStrategy(KeepingStrategy):
__slots__ = ("storage",)

def __init__(self):
self.storage = []

def __getitem__(self, pi: PatchInfo):
return self.storage[pi.no]

def __setitem__(self, pi: PatchInfo, p):
self.storage[pi.no] = p


class OnDiskKeepingStrategy(KeepingStrategy):
__slots__ = ("tmpDirs",)

def __init__(self):
self.storage = []

def __getitem__(self, pi: PatchInfo):
return unidiff.PatchSet.from_filename(pfn)

def __setitem__(self, pi: PatchInfo, p):
storage[pi.no] = p


class Pass:
__slots__ = ("patchesProcessors",)

def __init__(self):
self.patchesProcessors = []

def __call__(self, inList: typing.Iterable[Path], tmpDir: Path):
return self.globalPostProcess(self.middleProcess(self.globalPreProcess(inList, tmpDir), tmpDir), tmpDir)

def globalPostProcess(patchesList: typing.Iterable[Path], tmpDir: Path):
return patchesList

def globalPreProcess(patchesList: typing.Iterable[Path], tmpDir: Path):
return patchesList

def middleProcess(self, midList: typing.Iterable[Path], tmpDir: Path):
for pfn in midList:
patch = unidiff.PatchSet.from_filename(pfn)
for p in self.patchesProcessors:
patch = p(patch)
outFN = outDir / pf.name
outFN.write_text(str(patch))
yield outFN


class PassesStack:
__slots__ = ("passes", "outDir", "tmpDir")

def __init__(self, passes, outDir, tmpDir):
self.passes = passes
self.outDir = outDir
self.tmpDir = tmpDir

def __call__(self, patches=None):
for pas in passes:
patches = pas(patches)
25 changes: 25 additions & 0 deletions patchSeq/keepingStrategies/InDirKeepingStrategy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import tempfile
from pathlib import Path

from ..core import KeepingStrategy, PatchInfo


class InDirKeepingStrategy(KeepingStrategy):
__slots__ = ("tmpDirs", "currentDir", "currentDirIdx")

def __init__(self, tmpdir):
self.tmpDirs = []
self.currentDirIdx = None
self.currentDir = None

def __enter__(self):
pass

def currentTmpDir(self):
pass

def __getitem__(self, pi: PatchInfo):
return unidiff.PatchSet.from_filename(pi.fileName)

def __setitem__(self, pi: PatchInfo, p):
pi.fileName
14 changes: 14 additions & 0 deletions patchSeq/keepingStrategies/InMemoryKeepingStrategy.py.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from ..core import KeepingStrategy, PatchInfo


class InMemoryKeepingStrategy(KeepingStrategy):
__slots__ = ("storage",)

def __init__(self):
self.storage = []

def __getitem__(self, pi: PatchInfo):
return self.storage[pi.no]

def __setitem__(self, pi: PatchInfo, p):
self.storage[pi.no] = p
Empty file.
23 changes: 23 additions & 0 deletions patchSeq/passes/ExportPass.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import typing
from pathlib import Path

from fsutilz import isGlobPattern, isNestedIn, nestPath, relativePath

from ..core import Pass, Processor


class ExportPass(Pass):
__slots__ = ("dir",)

def __init__(self, dir: Path):
self.dir = dir

def __call__(self):
return super().__call__([])

def globalPostProcess(patchesList: typing.Iterable[Path], tmpDir: Path):
for p in patchesList:
pass
newPath = tmpDir
Path.rename(target)
return sorted(getPatchFilesInADir(self.dir), key=lambda f: extractInitialNumber(f.name))
Loading

0 comments on commit e586ae2

Please sign in to comment.