From 10d3ea65877ab9a499536fe2dd5d6f98fc03e5bb Mon Sep 17 00:00:00 2001 From: Google Code Exporter Date: Sat, 18 Apr 2015 05:07:41 -0400 Subject: [PATCH 1/6] Migrating wiki contents from Google Code --- APIUseCases.md | 55 ++++++++++++ Adoptions.md | 8 ++ DESIGN.md | 231 +++++++++++++++++++++++++++++++++++++++++++++++++ FrontPage.md | 5 ++ ProjectHome.md | 12 +++ README.md | 200 ++++++++++++++++++++++++++++++++++++++++++ Source.md | 14 +++ 7 files changed, 525 insertions(+) create mode 100644 APIUseCases.md create mode 100644 Adoptions.md create mode 100644 DESIGN.md create mode 100644 FrontPage.md create mode 100644 ProjectHome.md create mode 100644 README.md create mode 100644 Source.md diff --git a/APIUseCases.md b/APIUseCases.md new file mode 100644 index 0000000..00ec0bd --- /dev/null +++ b/APIUseCases.md @@ -0,0 +1,55 @@ + +``` +import patch +``` + +### Detect if a file is a valid patch ### +``` +>>> bool( patch.fromfile('doc/example.diff.diff') ) +True +``` + +### Print diffstat ### +``` +>>> print(patch.fromfile('tests/01uni_multi.patch').diffstat()) + updatedlg.cpp | 20 ++++++++++++++++++-- + updatedlg.h | 1 + + manifest.xml | 15 ++++++++------- + conf.cpp | 23 +++++++++++++++++------ + conf.h | 7 ++++--- + 5 files changed, 48 insertions(+), 18 deletions(-), +1203 bytes +``` + +### Find all patch files and do something about them ### +``` +import patch, os + +matched = 0 +for file in os.listdir('.'): + if os.path.isdir(file): + continue + + with open(file, "rb") as fp: + ps = patch.PatchSet() + if not ps.parse(fp): + pass + else: + print(file) + for each in ps: + print(" " + each.target) + for h in each.hunks: + if h.desc: + print(" " + h.desc) + matched += 1 + +print('Found %s' % matched) +``` + +## User stories ## +### 01. Upgrade Trac environment files ### + +**Storyline:** I want to upgrade Trac environment while upgrading Trac itself from 0.9 to 0.11. This requires patching documentation files (README) in environment to a new version. It may not worth to distribute full version of documentation, so I want to detect what files are not patched, ensure that patch applies clearly before processing. + +**Proposed API:** + +**Example:** \ No newline at end of file diff --git a/Adoptions.md b/Adoptions.md new file mode 100644 index 0000000..126e931 --- /dev/null +++ b/Adoptions.md @@ -0,0 +1,8 @@ +| Project | Description | patch.py version | Reviewed | +|:--------|:------------|:-----------------|:---------| +| [collective.recipe.patch](https://pypi.python.org/pypi/collective.recipe.patch/0.2.2) | buildout recipe for patching eggs | [8.06-1+](https://github.com/garbas/collective.recipe.patch/blob/master/collective/recipe/patch/patch.py) | 2014-01-17 | +| [Linux Kernel Backports](https://backports.wiki.kernel.org/index.php/Documentation) | backporting Linux upstream device drivers for usage on older kernels | [1.12.12dev+](https://git.kernel.org/cgit/linux/kernel/git/backports/backports.git/tree/lib/patch.py) | 2014-01-17 | +| [LuaPatch](http://lua-users.org/wiki/LuaPatch) | rewrite of patch.py for Lua by David Manura | 8.06-1| 2014-01-17 | +| [OpenHatch](https://openhatch.org/) | help wannabe open source developers find interesting projects | [10.04-2+](https://github.com/openhatch/oh-mainline/blob/master/vendor/packages/python-patch/patch.py) | 2014-01-17 | +| [nose](https://nose.readthedocs.org/en/latest/) | `nose` extends unittest to make testing easier | [10.04-2+](https://github.com/nose-devs/nose/blob/master/patch.py) | 2014-01-17 | +| [pypatch](https://pypi.python.org/pypi/pypatch/0.5.1) | automatically patch installed python modules | 1.12.11 | 2014-01-17 | \ No newline at end of file diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..9d2e8f5 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,231 @@ +#author Anatoly Techtonik + +The initial goal was to make cross-platform alternative to unix patch +utility. This utility should not require compiling, must be easy to +extend and should be run as standalone tool or as server process. That's +why Python was chosen. + +Another goal was to make a library for automation of patching tasks as +part of rainforce project to encourage development of tools that will +make "patchwork" more intuitive and easy. + +And the last goal was to reinvert line parser theory from scratch, because +you need to reinvent things to prove that there is no better way to do +something (or to understand what do you need to look for). + + +## Design decisions ## + +#### Patch target file if source is not found #### + +If the source file is not found while patching, **patch.py** tries to patch +target filename. This logic is used to process old style manual patches +that were made by comparing backup file (with some fancy .old extension) +with modified version that holds original name. + +#### "Already patched" detection #### + +_unified diff format_ doesn't allow to correctly validate if file is +already patched, but only to check if it could be patched. It is because +in some rare cases it is possible to apply the same patch several times +(see 04check\_patched example in tests). + +Only checksum can reliably detect if file is not patched, but in this case +linefeed differences or minor changes in file will mark file as +non-patchable, while in fact it could be possible if these minor changes +do not intersect with unified diff contents. This is the feature of +unified format - non-conlicting patches can be applied in different +order. Some conflicts can even be resolved automatically (not by this lib +so far). Version control systems actually do this during merges. + + +## Parsers overview ## + + +## Parser no.0 - Brute Force Line By Line Regexp ## + +Versions before 8.06. + +Initially the process was very straightforward. Main cycle reads one line +at a time from the input stream. Detects to which part of Diff it belongs +(free header, filename header, hunk) and parses it into corresponding data +structure. The line is discarded at the end of each cycle. This guarantees +no endless loops or recursions as long as input stream is finite. + +The parser code is one big `for` loop with a series of "parsing blocks" +at the root level. After a line is read at the loop start, each parsing +block then tested it with an `if` condition to see if it should process +the line. If condition matched the block then extracted text into Python +structure. Blocks can't request more lines, but they can use `continue` +command to start new cycle without waiting until the end of cycle (this +also prevented the line from being accidentally processed by blocks below). + +Testing the line with `if` had two major drawbacks: + + 1. regular expressions used in condition checks made the code obscure + 1. line could be intercepted by wrong block, and an extra effort required to place blocks in proper order + +To illustrate second problem take lines starting with "+" or "-" for +example. They should be parsed differently depending on where in diff they +are located - in header block they are just usual lines that are not +parsed at all, but for hunk they are the main data. + +## Parser no.1 - Line By Line State Machine ## + +Versions 8.06 up to 10.04. + +To make parser code more clear, regular expression checks were replaced +with checks of state variables. These local boolean variables were named +`header`, `filenames`, `hunkhead`, `hunkbody` after the regions in unified +diff format. Only one variable is set to true at any given time, so parser +is said to be in one given state at any moment. This made debug process +significantly easier. + +After this change `if`s at the top level of main cycle started to check +state variables instead of probing line content to delegate line processing +into their parsing block. When block finishes processing, it is responsible +for switching state to the next one. Sometime the next state should be +chosen from several possible alternatives, and parsing block needs content +of the following line to make the decision. As the line is discarded at the +end of cycle - blocks are still required to be placed in proper order. + +Main cycle turned to be more readable, but checks at the end of parsing +blocks become more sophisticated. + +So, while state machine isolated parsing blocks from stealing lines from +eash other, it still has drawbacks: + 1. blocks should be placed in proper order + 1. parsing blocks that make a decision when switching state should know about lines (i.e. context) of successor blocks + 1. state checks are made for every input line, because block can't request more lines in the middle of input cycle + +Let's not forget benefits: + 1. debug is easier, code is more readable + 1. parser is still non-recursive + 1. no risk of endless loops + +This state machine allowed to introduce new `hunkskip` state for recovery +from corrupted or invalid hunk. When such hunk is encountered - parser +switches to `hunkskip` state and skips input lines until it finds header of +the next hunk or filenames section. It appeared that the same check is done +when hunk ends as usual, so the state check after hunk was delegated to +`hunkskip` block as well. + +The parsing block for `hunkskip` state doesn't actually parse any data - it +exists solely for making branching decision. Until 'hunkskip' state primary +purpose of blocks in main cycle was extracting data, that's why they were +named "parsing blocks", but 'hunkskip' introduced new class of blocks that +can be named "decider blocks". + +The order of `hunkskip` is after `hunkbody` parser and before `filenames` +and `hunkhead` parsing blocks. This guarantees that these blocks get their +line after the state switch and before the line is discarded. + +``` +TODO: describe "missing line" problem in state recovery with two + interleaving blocks, when it is impossible to choose which block + should be placed ahead of the other +``` + +This line by line parser with lines that "fall through" arranged parsing +blocks may be the fastest possible implementation. There are no calls, no +repeated checks after state switching. But the code is still hard to read +and extend due to these implicit arrangements. This can be a minor issue +though as unified diff format is simple and such optimizations could be +the way to go in the future. + + +To summarize: + * static code analysis is easier thanks to states instead of regexps for branching execution; + * state checks are run for every line of the input stream (blocks can't request more lines); + * blocks are not explicitly chained; + * but still arranged in specific order; + * every parsing block knows to which state it should switch after processing; + * this makes state switching complicated when there is choice; + * because block should fetch the next line to make a decision; + * line should be processed by the right block until the end of the cycle; + * if it is impossible to rearrange blocks in the appropriate order then a "decider block" is needed (e.g `hunkskip`). + +Absence of function calls should speed up things a little. Function calls +could make block chaining explicit, but this exposes parser to stack +depletion problem. It is not actual for this specific parser, where amount +of parsed data in memory is bigger anyway, but it is still worthy to keep +this non-recursive and stackless. + +Development of parser is still complicated, because you need to keep in +mind the correct order of blocks while making modifications, and know about +interleaving blocks corner case to be able to manually detect them before +they hack your mind. + + +## Parser no.2 - Block Context ## + +Versions + +The need to extend patch parser for processing Mercurial and Git formats +required changes to allow easy extension without sacrificing non-recursive +non-looping behavior. The first enhancement was to allow parser blocks +fetch lines from input stream directly without waiting for the line in the +main cycle. Block reads as many lines as it needs, and after that switches +state. All lines that belong to this block are called "block context" and +are not exposed to main `if` cycle. This way there are less chances that +line could be intercepted by the wrong block. + +This feature can be called "isolation of block context". + +While it sounds good, block context can not be fully separated. When block +doesn't know how many lines it needs it just reads the input until a line +out of context is encountered. This line already belongs to another "block +context". Current block switches state to pass control to owner, but the +owner need to catch the line before it is discarded at the end of cycle. + +For example, when header parser reads line that starts with "--- " - it +should switch state and pass this line to the owner - filename parser. +The filename parser should be called before the end of the cycle to avoid +line being overwritten. In case of `if`s structure that means owner's +block check should be located under part of the code that switched state. + +So each block still knows about the next state or states. It should make a +decision where to switch next. Hence it should know about "block contexts" +of its successors. This bloats and complicates code. + +To simplify the code, it is possible to prevent blocks from analysing +each other's context for making switch. The block should only check that +line doesn't belong to its context and switch state to pass control +further to "decider's block", which in turn make the proper state switch. + +"block context separation" is another feature of Parser no.2. + +It's not a complete separation in a sense that header parser still knows +that line starting with "--- " doesn't belong to it. It switches state by +setting `headscan` to False and `filescan` to True and that's all. No +parsing block makes assumptions or decisions where to pass the processing. +There is only one way to switch from parsing block. If there should be a +decision what is to be parsed next, then this decision should be made by +"decider's block" that doesn't parse, but just analyzes context to make a +switch. The problem with arranging pieces in correct order still persists. + +To solve rearrangement problem it is necessary to either reinvent GOTO or +to be able to reinsert analyzed line back into stream for fetching in +the next cycle after state had already been switched. + +Rearrangement problem can be illustrated with two blocks that analyze each +others context to pass control to each other. It is impossible to place +them in the correct order in main cycle, because at the end of processing +the current block should always be higher than the other to pass the line. + +The solution can be in: + * skip fetching line on the next cycle + * add buffer for discarded lines + * reinsert line into the stream + * wrapper switch that judges who gets the next line, this requires one more state variable, and processing lines one by one from the main cycle + +Reinserting lines can provide some performance overhead, wrapper switch +complicates parser, so skip line fetching may be a good solution. + + +So, this parser overcomes strict requirement when line should processed in +the same cycle to avoid being overwritten. Every block requests as many +lines as it needs, switches state to "finished" and returns control to the +beginning of the main cycle. Main cycle analyzes state and passes control +to the next appropriate block. As a side effect there is now a line number +that can be used for error messages and debugging. \ No newline at end of file diff --git a/FrontPage.md b/FrontPage.md new file mode 100644 index 0000000..7161968 --- /dev/null +++ b/FrontPage.md @@ -0,0 +1,5 @@ +Cross-platform alternative to unix **patch** utility capable to apply unified diffs. + +_Patches are welcome._ + +<wiki:gadget url="https://bitbucket.org/techtonik/discovery/raw/5482cbe619ce/web/gadgets/flattr.xml" border="0" width="110" height="20"/> \ No newline at end of file diff --git a/ProjectHome.md b/ProjectHome.md new file mode 100644 index 0000000..95faa8f --- /dev/null +++ b/ProjectHome.md @@ -0,0 +1,12 @@ +Cross-platform **patch** utility capable to apply unified diffs. + +_Patches are welcome._ + + +If you need to patch Python itself, do: +``` +hg clone https://hg.python.org/cpython +``` +And follow the [devguide](https://docs.python.org/devguide/). + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..fa29828 --- /dev/null +++ b/README.md @@ -0,0 +1,200 @@ +## History ## + +The project started because Windows platform lacks native tool to apply +patches, and there was no cross-platform solution that could be safely +run by web server process. + +Usually patches are applied with a UNIX **patch** utility +[from GNU tools](http://www.gnu.org/software/patch/). It is +[ported to windows](http://gnuwin32.sourceforge.net/packages/patch.htm), +but still feels +[buggy and insecure](http://www.google.com/search?q=Assertion+failed%3A+hunk%2C+file+patch.c) for web server process. It is also not customizable +without a C compiler, which is a problem for Windows. So it was good to have a good utility written in Python that can be potential +[\*diff.py\* counterpart](http://bugs.python.org/issue2057). + +**patch.py** is meant to be a command line tool with intuitive defaults, +taking care of the most problems (e.g. line end differences) automatically. + + +## Status ## + +[![](https://drone.io/techtonik/python-patch/status.png)](https://drone.io/techtonik/python-patch) + +**API status**: API is unstable, so **use strict dependencies on major +version number** when using this tool as a library. + +It understands only _unified diffs_. Currently it doesn't support file +renames, creation and removals. + +Note that **patch.py** was not designed to reproduce original files. Parsing +is a lossy process where data is normalized to be cross-platform. Absolute +paths are stripped as well as references to parent directories, backslashes +are converted to forward slashes and so on. + +**patch.py** is designed to transparently handle line end differences. Line +endings from patch are converted into +best suitable format for patched file. patch.py scans line endings in source +file, and if they are consistent - lines from patch are applied with the +same ending. If source linefeeds are inconsistend - lines from patch are +applied "as is". + + +Parsing of diff is done in a in very straightforward manner as an exercise +to approach the problem of parsing on my own before learning the 'proper +ways'. Thanks creators, _the format of unified diff_ is rather simple (an +illustration of Subversion style unified diff is included in +[source doc/](http://python-patch.googlecode.com/svn/trunk/doc/) directory). + +## Features ## + + * Automatic correction of + * Linefeeds according to patched file + * Diffs broken by stripping trailing whitespace + * a/ and b/ prefixes + * Single file, which is a command line tool and library + * Python 2.5+ compatible, 2.7 tested, 3 not supported + * No dependencies outside Python stdlib + * Patch format detection (SVN, HG, GIT) + * Test coverage for easy modification + * Nice diffstat histogram + +Things that don't work: + + * Python 3 + * File renaming, creation and removal + * Directory tree operations + * Version control specific properties + * Non-unified diff formats + +## Library usage ## + +See [APIUseCases](APIUseCases.md). + +## Changes ## +``` + +1.xx.x + + - --revert option to apply patches in reverse order (unpatch) + - support for broken patches generated by online Google Code editor + - API changes: + + PatchSet and Patch objects are now iterable + + new PatchSet.findfile() contains logic detecting filename to patch + + PatchSet.revert() + - make directory based tests easier to create and run manually + - fix xnormpath with Windows paths on Linux + (issue #24, found by Philippe Ombredanne) + +1.13 + + - diffstat output now also shows size delta in bytes + - added --directory (-d) option to specify root when applying patches + - hunk headers produced by `diff -p` option are now parsed and accessible + (issue #22, found by Philippe Ombredanne) + - API changes: + + Hunk.desc field to access hunk headers content + + PatchSet.apply() gets `root` keyword argument for the working dir + when applying patches (issue #7) + - improve error message for missing files + - improve docs (fix issue #5) + +1.12.11 Major API Break + + - patch.py can read patches from stdin + - patch.py can show nice histogram with --diffstat option + - added detection of SVN, GIT and HG patch types, unrecognized + patches marked as PLAIN + - added error reporting for parsing functions and helpers (now they + return False if parsing failed) - make sure you handle this correctly + - added normalization to filenames to protect against patching files + using absolute paths or files in parent directories + - test run patch.py on all patches submitted to Python bug tracker, which + resulted in improved parsing and error handling for some corner cases + - improved logging + - API changes + * fromfile(), fromstring() and fromurl() now return False on errors + * previous Patch is renamed to PatchSet, new Patch is single file entry + * Patch.header is now a list of strings + * PatchSet.parse() now returns True if parsing completed without errors + + PatchSet.__len__() + + PatchSet.diffstat() + + PatchSet.type and Patch.type + + PatchSet.errors and + + xisabs() cross-platform version of `os.path.isabs()` + + xnormpath() forward slashed version of `os.path.normpath()` + + xstrip() to strip absolute path prefixes + +11.01 + - patch.py can read patches from web + - patch.py returns -1 if there were errors during patching + - store patch headers (necessary for future DIFF/SVN/HG/GIT detection) + - report about extra bytes at the end after patch is parsed + - API changes + + fromurl() + * Patch.apply() now returns True on success +10.11 + - fixed fromstring() failure due to invalid StringIO import (issue #9) + (thanks john.stumpo for reporting) + - added --verbose and --quiet options + - improved message logging + - change "successfully patched..." message to INFO instead of WARN + (thanks Alex Stewart for reporting and patch) + - skip __main__ imports when used as a library (patch by Alex Stewart) + - API changes + * renamed class HunkInfo to Hunk + + Patch.type placeholder (no detection yet - parser is not ready) + + constants for patch types DIFF/PLAIN, HG/MERCURIAL, SVN/SUBVERSION + + Patch.header for saving headers which can be used later to extract + additional meta information such as commit message + - internal: improving parser speed by allowing blocks fetch lines on + demand + - test suite improvements +10.04 + - renamed debug option to --debug + - API changes + * method names are now underscored for consistency with difflib + + addded Patch.can_patch(filename) to test if source file is in list + of source filenames and can be patched + * use designated logger "python_patch" instead of default +9.08-2 + - compatibility fix for Python 2.4 +9.08-1 + - fixed issue #2 - remove trailing whitespaces from filename + (thanks James from Twisted Fish) + - API changes + + added Patch and HunkInfo classes + * moved utility methods into Patch + + build Patch object by specifying stream to constructor + or use top level functions fromfile() and fromstring() + - added test suite +8.06-2 + - compatibility fix for Python 2.4 +8.06-1 + - initial release +``` + + +## Future ## + +Patch utility in Python makes it possible to implement online "submit, +review and apply" module. Similar to [Review Board](http://www.reviewboard.org/) +for code, but suitable for all kind of textual content that uses +unified diffs as an interchange format between users, website, and version +control system. With this system patches can be applied after on site +review, automatically storing the names of patch contributors in SVN +history logs without requiring write access for these contributors. This +system is not the scope of this project though. + +Additional unified diff parsers may be added in future to compare different +parsing techniques (with [pyparsing](http://pyparsing.wikispaces.com/), +[SPARK](http://www.ibm.com/developerworks/library/l-spark.html) or +[others](http://www.google.com/Top/Computers/Programming/Languages/Python/Modules/Text_Processing/) +as example). + +See also https://code.google.com/p/rainforce/wiki/ModerationQueue + +It would be nice to further simplify parser, make it more modular to allow easy +customization and extension, but the primary focus for now is to figure out +an API that will make it usable as a library. There is separate TODO item to +check behavior of "\ No newline at end of file" cases. Other goals is to +expand test coverage, and try to make script more interactive. \ No newline at end of file diff --git a/Source.md b/Source.md new file mode 100644 index 0000000..a8a37c7 --- /dev/null +++ b/Source.md @@ -0,0 +1,14 @@ +| `[` [Browse Files](https://code.google.com/p/python-patch/source/browse/) `]` | `[` [Commit Log ](https://code.google.com/p/python-patch/source/list) `]` | [![](https://drone.io/techtonik/python-patch/status.png)](https://drone.io/techtonik/python-patch) | +|:------------------------------------------------------------------------------|:--------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------| + +Official sources for patch.py are hosted in Google Code Subversion: +``` +svn checkout http://python-patch.googlecode.com/svn/trunk/ python-patch +``` + +It can be accessed with [Mercurial](http://mercurial.selenic.com/) + [hgsubversion](https://bitbucket.org/durin42/hgsubversion/) plugin: +``` +hg clone http://python-patch.googlecode.com/svn/trunk/ python-patch +``` + +(for committing to Google Code, the URL needs to be changed to https) \ No newline at end of file From 73435b5615513e3c6a400a6b45ff5d454466b739 Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Wed, 29 Apr 2015 20:04:04 +0300 Subject: [PATCH 2/6] Delete FrontPage.md --- FrontPage.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 FrontPage.md diff --git a/FrontPage.md b/FrontPage.md deleted file mode 100644 index 7161968..0000000 --- a/FrontPage.md +++ /dev/null @@ -1,5 +0,0 @@ -Cross-platform alternative to unix **patch** utility capable to apply unified diffs. - -_Patches are welcome._ - -<wiki:gadget url="https://bitbucket.org/techtonik/discovery/raw/5482cbe619ce/web/gadgets/flattr.xml" border="0" width="110" height="20"/> \ No newline at end of file From a739cbe7b0b8453cf0f668513fc74a19c5cef0b2 Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Mon, 4 May 2015 16:29:04 +0300 Subject: [PATCH 3/6] Delete Adoptions.md Moved to https://github.com/techtonik/python-patch/blob/master/doc/ADOPTIONS.md --- Adoptions.md | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 Adoptions.md diff --git a/Adoptions.md b/Adoptions.md deleted file mode 100644 index 126e931..0000000 --- a/Adoptions.md +++ /dev/null @@ -1,8 +0,0 @@ -| Project | Description | patch.py version | Reviewed | -|:--------|:------------|:-----------------|:---------| -| [collective.recipe.patch](https://pypi.python.org/pypi/collective.recipe.patch/0.2.2) | buildout recipe for patching eggs | [8.06-1+](https://github.com/garbas/collective.recipe.patch/blob/master/collective/recipe/patch/patch.py) | 2014-01-17 | -| [Linux Kernel Backports](https://backports.wiki.kernel.org/index.php/Documentation) | backporting Linux upstream device drivers for usage on older kernels | [1.12.12dev+](https://git.kernel.org/cgit/linux/kernel/git/backports/backports.git/tree/lib/patch.py) | 2014-01-17 | -| [LuaPatch](http://lua-users.org/wiki/LuaPatch) | rewrite of patch.py for Lua by David Manura | 8.06-1| 2014-01-17 | -| [OpenHatch](https://openhatch.org/) | help wannabe open source developers find interesting projects | [10.04-2+](https://github.com/openhatch/oh-mainline/blob/master/vendor/packages/python-patch/patch.py) | 2014-01-17 | -| [nose](https://nose.readthedocs.org/en/latest/) | `nose` extends unittest to make testing easier | [10.04-2+](https://github.com/nose-devs/nose/blob/master/patch.py) | 2014-01-17 | -| [pypatch](https://pypi.python.org/pypi/pypatch/0.5.1) | automatically patch installed python modules | 1.12.11 | 2014-01-17 | \ No newline at end of file From 6d751bd9140eb2d1f59f49af565eb5e05e31108e Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Fri, 25 Dec 2015 19:17:14 +0300 Subject: [PATCH 4/6] Delete ProjectHome.md --- ProjectHome.md | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 ProjectHome.md diff --git a/ProjectHome.md b/ProjectHome.md deleted file mode 100644 index 95faa8f..0000000 --- a/ProjectHome.md +++ /dev/null @@ -1,12 +0,0 @@ -Cross-platform **patch** utility capable to apply unified diffs. - -_Patches are welcome._ - - -If you need to patch Python itself, do: -``` -hg clone https://hg.python.org/cpython -``` -And follow the [devguide](https://docs.python.org/devguide/). - - \ No newline at end of file From 62a0bcd381ec377e27b8c9217ce55e54384cf1db Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Fri, 25 Dec 2015 19:17:38 +0300 Subject: [PATCH 5/6] Delete Source.md --- Source.md | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 Source.md diff --git a/Source.md b/Source.md deleted file mode 100644 index a8a37c7..0000000 --- a/Source.md +++ /dev/null @@ -1,14 +0,0 @@ -| `[` [Browse Files](https://code.google.com/p/python-patch/source/browse/) `]` | `[` [Commit Log ](https://code.google.com/p/python-patch/source/list) `]` | [![](https://drone.io/techtonik/python-patch/status.png)](https://drone.io/techtonik/python-patch) | -|:------------------------------------------------------------------------------|:--------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------| - -Official sources for patch.py are hosted in Google Code Subversion: -``` -svn checkout http://python-patch.googlecode.com/svn/trunk/ python-patch -``` - -It can be accessed with [Mercurial](http://mercurial.selenic.com/) + [hgsubversion](https://bitbucket.org/durin42/hgsubversion/) plugin: -``` -hg clone http://python-patch.googlecode.com/svn/trunk/ python-patch -``` - -(for committing to Google Code, the URL needs to be changed to https) \ No newline at end of file From 8132b1570e23559351c7f916a13e306fb6e2c7a4 Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Fri, 25 Dec 2015 19:18:38 +0300 Subject: [PATCH 6/6] Update README.md Moved to `master` branch --- README.md | 145 +----------------------------------------------------- 1 file changed, 1 insertion(+), 144 deletions(-) diff --git a/README.md b/README.md index fa29828..57945c9 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,3 @@ -## History ## - -The project started because Windows platform lacks native tool to apply -patches, and there was no cross-platform solution that could be safely -run by web server process. - -Usually patches are applied with a UNIX **patch** utility -[from GNU tools](http://www.gnu.org/software/patch/). It is -[ported to windows](http://gnuwin32.sourceforge.net/packages/patch.htm), -but still feels -[buggy and insecure](http://www.google.com/search?q=Assertion+failed%3A+hunk%2C+file+patch.c) for web server process. It is also not customizable -without a C compiler, which is a problem for Windows. So it was good to have a good utility written in Python that can be potential -[\*diff.py\* counterpart](http://bugs.python.org/issue2057). - -**patch.py** is meant to be a command line tool with intuitive defaults, -taking care of the most problems (e.g. line end differences) automatically. - - ## Status ## [![](https://drone.io/techtonik/python-patch/status.png)](https://drone.io/techtonik/python-patch) @@ -45,135 +27,10 @@ ways'. Thanks creators, _the format of unified diff_ is rather simple (an illustration of Subversion style unified diff is included in [source doc/](http://python-patch.googlecode.com/svn/trunk/doc/) directory). -## Features ## - - * Automatic correction of - * Linefeeds according to patched file - * Diffs broken by stripping trailing whitespace - * a/ and b/ prefixes - * Single file, which is a command line tool and library - * Python 2.5+ compatible, 2.7 tested, 3 not supported - * No dependencies outside Python stdlib - * Patch format detection (SVN, HG, GIT) - * Test coverage for easy modification - * Nice diffstat histogram - -Things that don't work: - - * Python 3 - * File renaming, creation and removal - * Directory tree operations - * Version control specific properties - * Non-unified diff formats - ## Library usage ## See [APIUseCases](APIUseCases.md). -## Changes ## -``` - -1.xx.x - - - --revert option to apply patches in reverse order (unpatch) - - support for broken patches generated by online Google Code editor - - API changes: - + PatchSet and Patch objects are now iterable - + new PatchSet.findfile() contains logic detecting filename to patch - + PatchSet.revert() - - make directory based tests easier to create and run manually - - fix xnormpath with Windows paths on Linux - (issue #24, found by Philippe Ombredanne) - -1.13 - - - diffstat output now also shows size delta in bytes - - added --directory (-d) option to specify root when applying patches - - hunk headers produced by `diff -p` option are now parsed and accessible - (issue #22, found by Philippe Ombredanne) - - API changes: - + Hunk.desc field to access hunk headers content - + PatchSet.apply() gets `root` keyword argument for the working dir - when applying patches (issue #7) - - improve error message for missing files - - improve docs (fix issue #5) - -1.12.11 Major API Break - - - patch.py can read patches from stdin - - patch.py can show nice histogram with --diffstat option - - added detection of SVN, GIT and HG patch types, unrecognized - patches marked as PLAIN - - added error reporting for parsing functions and helpers (now they - return False if parsing failed) - make sure you handle this correctly - - added normalization to filenames to protect against patching files - using absolute paths or files in parent directories - - test run patch.py on all patches submitted to Python bug tracker, which - resulted in improved parsing and error handling for some corner cases - - improved logging - - API changes - * fromfile(), fromstring() and fromurl() now return False on errors - * previous Patch is renamed to PatchSet, new Patch is single file entry - * Patch.header is now a list of strings - * PatchSet.parse() now returns True if parsing completed without errors - + PatchSet.__len__() - + PatchSet.diffstat() - + PatchSet.type and Patch.type - + PatchSet.errors and - + xisabs() cross-platform version of `os.path.isabs()` - + xnormpath() forward slashed version of `os.path.normpath()` - + xstrip() to strip absolute path prefixes - -11.01 - - patch.py can read patches from web - - patch.py returns -1 if there were errors during patching - - store patch headers (necessary for future DIFF/SVN/HG/GIT detection) - - report about extra bytes at the end after patch is parsed - - API changes - + fromurl() - * Patch.apply() now returns True on success -10.11 - - fixed fromstring() failure due to invalid StringIO import (issue #9) - (thanks john.stumpo for reporting) - - added --verbose and --quiet options - - improved message logging - - change "successfully patched..." message to INFO instead of WARN - (thanks Alex Stewart for reporting and patch) - - skip __main__ imports when used as a library (patch by Alex Stewart) - - API changes - * renamed class HunkInfo to Hunk - + Patch.type placeholder (no detection yet - parser is not ready) - + constants for patch types DIFF/PLAIN, HG/MERCURIAL, SVN/SUBVERSION - + Patch.header for saving headers which can be used later to extract - additional meta information such as commit message - - internal: improving parser speed by allowing blocks fetch lines on - demand - - test suite improvements -10.04 - - renamed debug option to --debug - - API changes - * method names are now underscored for consistency with difflib - + addded Patch.can_patch(filename) to test if source file is in list - of source filenames and can be patched - * use designated logger "python_patch" instead of default -9.08-2 - - compatibility fix for Python 2.4 -9.08-1 - - fixed issue #2 - remove trailing whitespaces from filename - (thanks James from Twisted Fish) - - API changes - + added Patch and HunkInfo classes - * moved utility methods into Patch - + build Patch object by specifying stream to constructor - or use top level functions fromfile() and fromstring() - - added test suite -8.06-2 - - compatibility fix for Python 2.4 -8.06-1 - - initial release -``` - - ## Future ## Patch utility in Python makes it possible to implement online "submit, @@ -197,4 +54,4 @@ It would be nice to further simplify parser, make it more modular to allow easy customization and extension, but the primary focus for now is to figure out an API that will make it usable as a library. There is separate TODO item to check behavior of "\ No newline at end of file" cases. Other goals is to -expand test coverage, and try to make script more interactive. \ No newline at end of file +expand test coverage, and try to make script more interactive.