summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Gruenbacher <agruen@linbit.com>2012-04-17 13:26:19 +0200
committerAndreas Gruenbacher <agruen@linbit.com>2012-04-17 16:48:19 +0200
commitc43eeb22a45171fc5bab28d460f73497fde24602 (patch)
tree97a11a5aa8944e0208c1e0bcd77f8e5d1eda9e2b
parent309ece2e5a2a8df85ef0328934693e28e83480ef (diff)
downloadpatch-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.c123
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/mixed-patch-types52
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