diff options
author | Andreas Gruenbacher <agruen@linbit.com> | 2012-04-17 13:26:19 +0200 |
---|---|---|
committer | Andreas Gruenbacher <agruen@linbit.com> | 2012-04-17 16:48:19 +0200 |
commit | c43eeb22a45171fc5bab28d460f73497fde24602 (patch) | |
tree | 97a11a5aa8944e0208c1e0bcd77f8e5d1eda9e2b | |
parent | 309ece2e5a2a8df85ef0328934693e28e83480ef (diff) | |
download | patch-c43eeb22a45171fc5bab28d460f73497fde24602.tar.gz |
For git-style patch files, do not output files immediately
In git-style patch files, all patches refer to the initial state of the input
files; files cannot be modified more than once. Implement these semantics by
creating all output files once all patches in the patch file have been
processed.
* src/patch.c (init_files_to_output, output_files): Add prototypes.
(main): Remember which type of patch file we are processing. Initialize the
output files list. Output files of git-style patches once all patches have
been read, or when from git-style to normal patches.
(file_to_output): New struct.
(files_to_output): List of the files to output.
(output_file, output_file_now, output_file_later): Either queue a file for
deletion, remember to output a file later (git-style), or output the file
immediately (normal).
(dispose_file_to_output, init_files_to_output, output_files,
forget_output_files): New functions.
(gl_list_clear): Should be provided by gnulib but isn't.
(cleanup): Clean up any left-over temporary output files as well.
* tests/Makefile-am (XFAIL_TESTS): Remove criss-cross; this test case works now.
* tests/mixed-patch-types: Patch files that change from normal to git-style, or
from git-style to normal.
-rw-r--r-- | src/patch.c | 123 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/mixed-patch-types | 52 |
3 files changed, 170 insertions, 7 deletions
diff --git a/src/patch.c b/src/patch.c index 59c0cd1..cc9b268 100644 --- a/src/patch.c +++ b/src/patch.c @@ -59,7 +59,9 @@ static void output_file (char const *, int *, const struct stat *, char const *, const struct stat *, mode_t, bool); static void init_files_to_delete (void); +static void init_files_to_output (void); static void delete_files (void); +static void output_files (void); #ifdef ENABLE_MERGE static bool merge; @@ -112,6 +114,7 @@ main (int argc, char **argv) bool apply_empty_patch = false; mode_t file_type; int outfd = -1; + bool have_git_diff = false; exit_failure = 2; set_program_name (argv[0]); @@ -159,6 +162,7 @@ main (int argc, char **argv) init_backup_hash_table (); init_files_to_delete (); + init_files_to_output (); init_output (&outstate); if (outfile) @@ -189,6 +193,12 @@ main (int argc, char **argv) bool mismatch = false; char const *outname = NULL; + if (have_git_diff != pch_git_diff ()) + { + have_git_diff = ! have_git_diff; + output_files (); + } + if (TMPREJNAME_needs_removal) { if (rejfp) @@ -585,6 +595,7 @@ main (int argc, char **argv) } if (outstate.ofp && (ferror (outstate.ofp) || fclose (outstate.ofp) != 0)) write_fatal (); + output_files (); delete_files (); cleanup (); if (somefailed) @@ -1685,14 +1696,39 @@ delete_files (void) /* Putting output files into place and removing them. */ +struct file_to_output { + char *from; + struct stat from_st; + char *to; + mode_t mode; + bool backup; +}; + +static gl_list_t files_to_output; + static void -output_file (char const *from, int *from_needs_removal, - const struct stat *from_st, char const *to, - const struct stat *to_st, mode_t mode, bool backup) +output_file_later (char const *from, int *from_needs_removal, const struct stat *from_st, + char const *to, mode_t mode, bool backup) { - if (from == NULL) - delete_file_later (to, to_st, backup); - else if (to == NULL) + struct file_to_output *file_to_output; + + file_to_output = xmalloc (sizeof *file_to_output); + file_to_output->from = xstrdup (from); + file_to_output->from_st = *from_st; + file_to_output->to = xstrdup (to); + file_to_output->mode = mode; + file_to_output->backup = backup; + gl_list_add_last (files_to_output, file_to_output); + if (from_needs_removal) + *from_needs_removal = 0; +} + +static void +output_file_now (char const *from, int *from_needs_removal, + const struct stat *from_st, char const *to, + mode_t mode, bool backup) +{ + if (to == NULL) { if (backup) create_backup (from, from_st, true); @@ -1704,6 +1740,80 @@ output_file (char const *from, int *from_needs_removal, } } +static void +output_file (char const *from, int *from_needs_removal, + const struct stat *from_st, char const *to, + const struct stat *to_st, mode_t mode, bool backup) +{ + if (from == NULL) + delete_file_later (to, to_st, backup); + else if (pch_git_diff()) + output_file_later (from, from_needs_removal, from_st, to, mode, backup); + else + output_file_now (from, from_needs_removal, from_st, to, mode, backup); +} + +static void +dispose_file_to_output (const void *elt) +{ + const struct file_to_output *file_to_output = elt; + + free (file_to_output->from); + free (file_to_output->to); +} + +static void +init_files_to_output (void) +{ + files_to_output = gl_list_create_empty (GL_LINKED_LIST, NULL, NULL, + dispose_file_to_output, true); +} + +static void +gl_list_clear (gl_list_t list) +{ + while (gl_list_size (list) > 0) + gl_list_remove_at (list, 0); +} + +static void +output_files (void) +{ + gl_list_iterator_t iter; + const void *elt; + + iter = gl_list_iterator (files_to_output); + while (gl_list_iterator_next (&iter, &elt, NULL)) + { + const struct file_to_output *file_to_output = elt; + int from_needs_removal = 1; + + output_file_now (file_to_output->from, &from_needs_removal, + &file_to_output->from_st, file_to_output->to, + file_to_output->mode, file_to_output->backup); + if (from_needs_removal) + unlink (file_to_output->from); + } + gl_list_iterator_free (&iter); + gl_list_clear (files_to_output); +} + +static void +forget_output_files (void) +{ + gl_list_iterator_t iter = gl_list_iterator (files_to_output); + const void *elt; + + while (gl_list_iterator_next (&iter, &elt, NULL)) + { + const struct file_to_output *file_to_output = elt; + + unlink (file_to_output->from); + } + gl_list_iterator_free (&iter); + gl_list_clear (files_to_output); +} + /* Fatal exit with cleanup. */ void @@ -1734,4 +1844,5 @@ cleanup (void) remove_if_needed (TMPOUTNAME, &TMPOUTNAME_needs_removal); remove_if_needed (TMPPATNAME, &TMPPATNAME_needs_removal); remove_if_needed (TMPREJNAME, &TMPREJNAME_needs_removal); + forget_output_files (); } diff --git a/tests/Makefile.am b/tests/Makefile.am index 7eb5b67..fba38b0 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -37,6 +37,7 @@ TESTS = \ line-numbers \ merge \ mangled-numbers-abort \ + mixed-patch-types \ munged-context-format \ need-filename \ no-newline-triggers-assert \ @@ -51,7 +52,6 @@ TESTS = \ unmodified-files XFAIL_TESTS = \ - criss-cross \ dash-o-append EXTRA_DIST = \ diff --git a/tests/mixed-patch-types b/tests/mixed-patch-types new file mode 100644 index 0000000..1339e9f --- /dev/null +++ b/tests/mixed-patch-types @@ -0,0 +1,52 @@ +# Copyright (C) 2010-2012 Free Software Foundation, Inc. +# +# Copying and distribution of this file, with or without modification, +# in any medium, are permitted without royalty provided the copyright +# notice and this notice are preserved. + +. $srcdir/test-lib.sh + +require_cat +use_local_patch +use_tmpdir +umask 022 + +# ============================================================== + +cat > mixed1.diff <<EOF +diff a/f b/f +--- a/f ++++ b/f +@@ -0,0 +1 @@ ++f +diff --git a/g b/g +--- a/g ++++ b/g +@@ -0,0 +1 @@ ++g +EOF + +check 'patch --dry-run < mixed1.diff || echo "Status: $?"' <<EOF +patching file f +patching file g +EOF + +# -------------------------------------------------------------- + +cat > mixed2.diff <<EOF +diff --git a/f b/f +--- a/f ++++ b/f +@@ -0,0 +1 @@ ++f +diff a/g b/g +--- a/g ++++ b/g +@@ -0,0 +1 @@ ++g +EOF + +check 'patch --dry-run < mixed2.diff || echo "Status: $?"' <<EOF +patching file f +patching file g +EOF |