Skip to content

Commit

Permalink
Finish first implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
sharyanto committed Sep 7, 2012
1 parent c961196 commit 2723dd0
Show file tree
Hide file tree
Showing 13 changed files with 371 additions and 201 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
File-Prepend-Undoable-*
File-Patch-Undoable-*
.build
*~
Empty file removed .unreleased
Empty file.
4 changes: 2 additions & 2 deletions Changes
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Revision history for File-Prepend-Undoable
Revision history for File-Patch-Undoable

0.01 2012-09-06 (SHARYANTO) (not yet released)
0.01 2012-09-07 (SHARYANTO) (not yet released)

First release.
10 changes: 6 additions & 4 deletions dist.ini
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
version=0.01

name = File-Prepend-Undoable
name = File-Patch-Undoable
author = Steven Haryanto <stevenharyanto@gmail.com>
license = Perl_5
copyright_holder = Steven Haryanto

[MetaResources]
homepage = http://metacpan.org/release/File-Prepend-Undoable
repository = http://github.com/sharyanto/perl-File-Prepend-Undoable
homepage = http://metacpan.org/release/File-Patch-Undoable
repository = http://github.com/sharyanto/perl-File-Patch-Undoable

[@SHARYANTO]

Expand All @@ -22,5 +22,7 @@ Test::Perinci::Tx::Manager=0.42
Rinci=1.1.29

perl=5.010001
File::Trash::Undoable=0.08
Builtin::Logged=0
Log::Any=0
SHARYANTO::Proc::ChildError=0
Text::Patch=1.8
156 changes: 156 additions & 0 deletions lib/File/Patch/Undoable.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package File::Patch::Undoable;

use 5.010;
use strict;
use warnings;
use Log::Any '$log';

use Builtin::Logged qw(system);
use File::Temp qw(tempfile);
use SHARYANTO::Proc::ChildError qw(explain_child_error);

# VERSION

our %SPEC;

$SPEC{patch} = {
v => 1.1,
summary => 'Patch a file, with undo support',
description => <<'_',
On do, will patch file with the supplied patch. On undo, will apply the reverse
of the patch.
Note: Symlink is currently not permitted (except for the patch file). Patching
is currently done with the `patch` program.
Unfixable state: file does not exist or not a regular file (directory and
symlink included), patch file does not exist or not a regular file (but symlink
allowed).
Fixed state: file exists, patch file exists, and patch has been applied.
Fixable state: file exists, patch file exists, and patch has not been applied.
_
args => {
# naming the args 'path' and 'patch' can be rather error prone
file => {
summary => 'Path to file to be patched',
schema => 'str*',
req => 1,
pos => 0,
},
patch => {
summary => 'Path to patch file',
description => <<'_',
Patch can be in unified or context format, it will be autodetected.
_
schema => 'str*',
req => 1,
pos => 1,
},
reverse => {
summary => 'Whether to apply reverse of patch',
schema => [bool => {default=>0}],
cmdline_aliases => {R=>{}},
},
},
features => {
tx => {v=>2},
idempotent => 1,
},
deps => {
prog => 'patch',
},
};
sub patch {
my %args = @_;

# TMP, schema
my $tx_action = $args{-tx_action} // '';
my $dry_run = $args{-dry_run};
my $file = $args{file};
defined($file) or return [400, "Please specify file"];
my $patch = $args{patch};
defined($patch) or return [400, "Please specify patch"];
my $rev = !!$args{reverse};

my $is_sym = (-l $file);
my @st = stat($file);
my $exists = $is_sym || (-e _);
my $is_file = (-f _);
my $patch_exists = (-e $patch);
my $patch_is_file = (-f _);

my @cmd;

if ($tx_action eq 'check_state') {
return [412, "File $file does not exist"] unless $exists;
return [412, "File $file is not a regular file"] if $is_sym||!$is_file;
return [412, "Patch $patch does not exist"] unless $patch_exists;
return [412,"Patch $patch is not a regular file"] unless $patch_is_file;

# check whether patch has been applied by testing the reverse patch
@cmd = ("patch", "--dry-run", "-sf", "-r","-", ("-R")x!$rev,
$file, "-i",$patch);
system @cmd;
if (!$?) {
return [304, "Patch $patch already applied to $file"];
} elsif (($? >> 8) == 1) {
$log->info("(DRY) Patching file $file with $patch ...") if $dry_run;
return [200, "File $file needs to be patched with $patch", undef,
{undo_actions=>[
[patch=>{file=>$file, patch=>$patch, reverse=>!$rev}],
]}];
} else {
return [500, "Can't patch: ".explain_child_error()];
}

} elsif ($tx_action eq 'fix_state') {
$log->info("Patching file $file with $patch ...");

# first patch to a temporary output first, because patch can produce
# half-patched file.
my ($tmpfh, $tmpname) = tempfile(DIR=>".");

@cmd = ("patch", "-sf","-r","-", ("-R")x!!$rev,
$file, "-i",$patch, "-o", $tmpname);
system @cmd;
if ($?) {
unlink $tmpname;
return [500, "Can't patch: ".explain_child_error()];
}

# now rename the temp file to the original file
unless (rename $tmpname, $file) {
unlink $tmpname;
return [500, "Can't rename $tmpname -> $file: $!"];
}

return [200, "OK"];
}
[400, "Invalid -tx_action"];
}

1;
# ABSTRACT: Patch a file, with undo support

=head1 FAQ
=head2 Why use the patch program? Why not use a Perl module like Text::Patch?
The B<patch> program has many nice features that L<Text::Patch> lacks, e.g.
applying reverse patch (needed to check fixed state and to undo), autodetection
of patch type, ignoring whitespace and fuzz factor, etc.
=head1 SEE ALSO
L<Rinci::Transaction>
L<Text::Patch>, L<PatchReader>, L<Text::Patch::Rred>
=cut
97 changes: 0 additions & 97 deletions lib/File/Prepend/Undoable.pm

This file was deleted.

19 changes: 19 additions & 0 deletions t/data/file.c.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
*** file.old 2012-09-07 12:58:02.000000000 +0700
--- file.new 2012-09-07 12:58:39.000000000 +0700
***************
*** 1,6 ****
line 1
- line 2
line 3
! line 4
! line 5
line 6
--- 1,8 ----
line 1
line 3
!
! line 4 - modified
! modified line 5
line 6
+ line 7
+ line 8
10 changes: 10 additions & 0 deletions t/data/file.ed.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
6a
line 7
line 8
.
4,5c

line 4 - modified
modified line 5
.
2d
8 changes: 8 additions & 0 deletions t/data/file.new
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
line 1
line 3

line 4 - modified
modified line 5
line 6
line 7
line 8
6 changes: 6 additions & 0 deletions t/data/file.old
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
line 1
line 2
line 3
line 4
line 5
line 6
14 changes: 14 additions & 0 deletions t/data/file.u.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--- file.old 2012-09-07 12:58:02.000000000 +0700
+++ file.new 2012-09-07 12:58:39.000000000 +0700
@@ -1,6 +1,8 @@
line 1
-line 2
line 3
-line 4
-line 5
+
+ line 4 - modified
+ modified line 5
line 6
+line 7
+line 8
Loading

0 comments on commit 2723dd0

Please sign in to comment.