diff mbox series

[02/13] Enable builds for z/OS.

Message ID 098b9ca8ece4fdce45a9b48e576b474ed81dced1.1699871056.git.gitgitgadget@gmail.com (mailing list archive)
State New, archived
Headers show
Series Enabling z/OS workflow for git | expand

Commit Message

Haritha D Nov. 13, 2023, 10:24 a.m. UTC
From: Haritha D <harithamma.d@ibm.com>

This commit enables git to build on z/OS.
It takes advantage of enahanced ASCII
services on z/OS to auto-convert input
files to ASCII
It also adds support for
[platform]-working-tree-encoding.
Platform is substituted with uname_info.sysname,
so it will only apply to the given platform.
Also adds support for scripts that are not in
standard locations so that /bin/env bash
can be specified.

Signed-off-by: Harithamma D <harithamma.d@ibm.com>
---
 Makefile              | 21 +++++++++---
 builtin.h             |  3 ++
 builtin/archive.c     |  6 ++++
 builtin/hash-object.c | 28 +++++++++++++++
 combine-diff.c        |  4 +++
 config.c              |  7 ++++
 configure.ac          |  3 ++
 convert.c             | 44 ++++++++++++++++++++----
 copy.c                |  3 ++
 diff.c                | 11 ++++++
 entry.c               | 26 ++++++++++++++
 environment.c         |  3 ++
 git-compat-util.h     |  8 +++++
 negotiator/default.c  |  4 +--
 negotiator/noop.c     |  4 +--
 negotiator/skipping.c |  4 +--
 object-file.c         | 80 ++++++++++++++++++++++++++++++++++++++++++-
 read-cache.c          |  3 ++
 utf8.c                | 11 ++++++
 19 files changed, 255 insertions(+), 18 deletions(-)

Comments

brian m. carlson Nov. 13, 2023, 11:03 p.m. UTC | #1
On 2023-11-13 at 10:24:04, Haritha D via GitGitGadget wrote:
> From: Haritha D <harithamma.d@ibm.com>
> 
> This commit enables git to build on z/OS.
> It takes advantage of enahanced ASCII
> services on z/OS to auto-convert input
> files to ASCII

In general, we don't want to convert files to ASCII, since we assume
text files are UTF-8 unless otherwise specified.  If you're suggesting
that your system is normally EBCDIC, changing that should be an initial
piece of setup code or something used in `xopen` on your platform so it
applies everywhere with minimal changes.

> It also adds support for
> [platform]-working-tree-encoding.
> Platform is substituted with uname_info.sysname,
> so it will only apply to the given platform.

I think this is going to need to be a separate patch and feature with
its own explanation of why it's valuable.

> Also adds support for scripts that are not in
> standard locations so that /bin/env bash
> can be specified.


> Signed-off-by: Harithamma D <harithamma.d@ibm.com>
> ---
>  Makefile              | 21 +++++++++---
>  builtin.h             |  3 ++
>  builtin/archive.c     |  6 ++++
>  builtin/hash-object.c | 28 +++++++++++++++
>  combine-diff.c        |  4 +++
>  config.c              |  7 ++++
>  configure.ac          |  3 ++
>  convert.c             | 44 ++++++++++++++++++++----
>  copy.c                |  3 ++
>  diff.c                | 11 ++++++
>  entry.c               | 26 ++++++++++++++
>  environment.c         |  3 ++
>  git-compat-util.h     |  8 +++++
>  negotiator/default.c  |  4 +--
>  negotiator/noop.c     |  4 +--
>  negotiator/skipping.c |  4 +--
>  object-file.c         | 80 ++++++++++++++++++++++++++++++++++++++++++-
>  read-cache.c          |  3 ++
>  utf8.c                | 11 ++++++
>  19 files changed, 255 insertions(+), 18 deletions(-)
> 
> diff --git a/Makefile b/Makefile
> index 9c6a2f125f8..30aa76da4f4 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -20,6 +20,8 @@ include shared.mak
>  #
>  # Define SHELL_PATH to a POSIX shell if your /bin/sh is broken.
>  #
> +# Define SHELL_PATH_FOR_SCRIPTS to a POSIX shell if your /bin/sh is broken.
> +#
>  # Define SANE_TOOL_PATH to a colon-separated list of paths to prepend
>  # to PATH if your tools in /usr/bin are broken.
>  #
> @@ -215,6 +217,8 @@ include shared.mak
>  #
>  # Define PERL_PATH to the path of your Perl binary (usually /usr/bin/perl).
>  #
> +# Define PERL_PATH_FOR_SCRIPTS to a Perl binary if your /usr/bin/perl is broken.

You will probably want to explain in your commit message why these two
are required to be different from the standard values and explain here
what the relevant difference is so that users can set them
appropriately.

> diff --git a/builtin/archive.c b/builtin/archive.c
> index 90761fdfee0..53ec794356f 100644
> --- a/builtin/archive.c
> +++ b/builtin/archive.c
> @@ -14,6 +14,12 @@
>  static void create_output_file(const char *output_file)
>  {
>  	int output_fd = xopen(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
> +#ifdef __MVS__
> + #if (__CHARSET_LIB == 1)
> +	if (setbinaryfd(output_fd))
> +		die_errno(_("could not tag archive file '%s'"), output_file);
> + #endif
> +#endif

This would be better to place in `xopen` itself so that all files are
correctly configured.  That would do well as its own patch, and you'd
want to explain well in the commit message what this function does, why
it's necessary, and what the consequences of not using it are, as well
as any alternatives that you've rejected.

>  	if (output_fd != 1) {
>  		if (dup2(output_fd, 1) < 0)
>  			die_errno(_("could not redirect output"));
> diff --git a/builtin/hash-object.c b/builtin/hash-object.c
> index 5ffec99dcea..b33b32ff977 100644
> --- a/builtin/hash-object.c
> +++ b/builtin/hash-object.c
> @@ -57,11 +57,39 @@ static void hash_fd(int fd, const char *type, const char *path, unsigned flags,
>  	maybe_flush_or_die(stdout, "hash to stdout");
>  }
>  
> +#ifdef __MVS__
> +#  if (__CHARSET_LIB == 1)
> +#  include <stdio.h>
> +#  include <stdlib.h>

We typically don't include the standard headers here.  Instead, they're
included by git-compat-util.h because on some systems they have to be
included in a certain order with certain options.  If you include that
header instead at the top of the file, or one of the headers that
includes it, then typically that should do the right thing.

> +   int setbinaryfd(int fd)
> +   {
> +     attrib_t attr;
> +     int rc;
> +
> +     memset(&attr, 0, sizeof(attr));
> +     attr.att_filetagchg = 1;
> +     attr.att_filetag.ft_ccsid = FT_BINARY;
> +     attr.att_filetag.ft_txtflag = 0;
> +
> +     rc = __fchattr(fd, &attr, sizeof(attr));
> +     return rc;
> +   }
> +#  endif
> +#endif

I would think a comment explaining what this function does and why it's
necessary would be appropriate, since it's not in POSIX and isn't
typically necessary on POSIX systems.

> diff --git a/combine-diff.c b/combine-diff.c
> index f90f4424829..73445a517c7 100644
> --- a/combine-diff.c
> +++ b/combine-diff.c
> @@ -1082,6 +1082,10 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
>  			ssize_t done;
>  			int is_file, i;
>  
> +#ifdef __MVS__
> +      __disableautocvt(fd);
> +#endif

Again, if this can be centralized, it should be, and it should be
explained well in the commit message.  I'm uncertain what it does or
what value it provides.

> diff --git a/config.c b/config.c
> index f9a1cca4e8a..37c124a37c0 100644
> --- a/config.c
> +++ b/config.c
> @@ -1521,6 +1521,13 @@ static int git_default_core_config(const char *var, const char *value,
>  		return 0;
>  	}
>  
> +	#ifdef __MVS__
> +	if (!strcmp(var, "core.ignorefiletags")) {
> +		ignore_file_tags = git_config_bool(var, value);
> +		return 0;
> +	}
> +	#endif

This should also live in its own patch and the commit message should
explain what it does.  We'd also want it to be documented in the config
options in the Documentation directory.

> diff --git a/convert.c b/convert.c
> index a8870baff36..4f14ff6f1ed 100644
> --- a/convert.c
> +++ b/convert.c
> @@ -377,12 +377,15 @@ static int check_roundtrip(const char *enc_name)
>  static const char *default_encoding = "UTF-8";
>  
>  static int encode_to_git(const char *path, const char *src, size_t src_len,
> -			 struct strbuf *buf, const char *enc, int conv_flags)
> +			 struct strbuf *buf, const char *enc, enum convert_crlf_action attr_action, int conv_flags)
>  {
>  	char *dst;
>  	size_t dst_len;
>  	int die_on_error = conv_flags & CONV_WRITE_OBJECT;
>  
> +  if (attr_action == CRLF_BINARY) {
> +    return 0;
> +  }

I'm pretty sure this is a change in behaviour from what we had before.
It should live in its own patch, with an explanation in the commit
message why it's a compelling and correct change overall, and with
suitable tests.

>  	/*
>  	 * No encoding is specified or there is nothing to encode.
>  	 * Tell the caller that the content was not modified.
> @@ -403,6 +406,11 @@ static int encode_to_git(const char *path, const char *src, size_t src_len,
>  		return 0;
>  
>  	trace_encoding("source", path, enc, src, src_len);
> +#ifdef __MVS__
> +  // Don't convert ISO8859-1 on z/OS
> +  if (strcasecmp("ISO8859-1", enc) == 0)
> +    return 0;
> +#endif

This definitely needs explanation in the commit message and should
probably be its own patch, explaining why z/OS has this compelling need
to not convert ISO8859-1.  Note that ISO8859-1 is not the same as "no
binary conversion", since it doesn't include many control codes.

Note that if, as it says later on, this really means "UTF-8", that's a
platform wart you'd want to paper over in a file in the compat code.  In
general, the compat directory is a good place to put anything that your
platform needs specifically.

> +static const char* get_platform() {
> +	struct utsname uname_info;
> +
> +	if (uname(&uname_info))
> +		die(_("uname() failed with error '%s' (%d)\n"),
> +			    strerror(errno),
> +			    errno);
> +
> +  if (!strcmp(uname_info.sysname, "OS/390"))
> +    return "zos";
> +  return uname_info.sysname;
> +}

This is definitely a new feature, and I'm not sure why it's necessary or
useful.  I suspect there's something about z/OS that makes it valuable,
but I don't know what it is since the commit message doesn't tell me.
I'm also not sure that these values will be correct on Windows.

I think I could go on to make similar comments about the rest of this
series.  I'm not opposed to seeing z/OS changes come in, but you've
amalgamated at least a half-dozen separate patches into one and haven't
explained them very thoroughly in the commit message.

I'd generally want to look at the commit message and understand the
problem the code is trying to solve and then look at the code and think,
"Oh, yes, this seems like the obvious and logical way to solve this
problem," or at least think, "Oh, no, I think we should solve this
problem in a different way," so I can help make a thoughtful review
comment.  Right now, I lack the information to have an informed opinion
and so I can't provide any helpful feedback or analysis of the patches.

When you're adding new features or fixing bugs, we'll also want tests
for those cases to help us avoid regressing that code in the future.
Even if we don't normally run the testsuite on z/OS, at least _you_ will
notice that the tests have failed and then we'll be able to address the
bugs in a timely manner.

I also noted that there were some fixup commits later on in the series
that address whitespace issues.  Typically, we'd want to squash those in
to the earlier patches.  Nobody expects perfection, but squashing errors
into earlier patches helps us keep the history neat and let us pretend
like you never made those errors at all.  It also lets tools like git
blame and git bisect work more nicely for users.

I'd recommend a quick pass over the SubmittingPatches file, which is
also available at https://git-scm.com/docs/SubmittingPatches.  The
sections on making separate commits for separate changes and describing
changes well come to mind as places to focus.

I know this may seem overwhelming and like I'm upset or disappointed;
I'm definitely not.  I'm very much interested in seeing Git available
for more platforms, but right now it's too hard for me to reason about
the changes for z/OS to provide helpful feedback, so I'm hoping you can
send a fixed v2 that helps me (and everyone else) understand these
changes better so you can get a helpful review.
Junio C Hamano Nov. 14, 2023, 12:48 a.m. UTC | #2
"Haritha D via GitGitGadget" <gitgitgadget@gmail.com> writes:

> Subject: Re: [PATCH 02/13] Enable builds for z/OS.

Documentation/CodingGuidelines and Documentation/SubmittingPatches
would help here, I think.

>  # Define SHELL_PATH to a POSIX shell if your /bin/sh is broken.
>  #
> +# Define SHELL_PATH_FOR_SCRIPTS to a POSIX shell if your /bin/sh is broken.

The reason to exist for the _FOR_SCRIPTS variants is not justified
anywhere in the proposed log message.

The former should be sufficient, and our policy is to let the
builder specify exactly what binaries the build products depend on,
(instead of random $PATH interfere with the choice by using
"#!/bin/env tool" that also has to assume that everybody's "env" is
installed in "/bin").

This patch has too many #ifdefs in the primary codepaths.  Your
porting strategy may need to be rethought.  Our usual convention is
to encapsulate these platform differences as much as possible in
git-compat-util.h and platform specific files in compat/ directory.

> diff --git a/builtin/hash-object.c b/builtin/hash-object.c
> index 5ffec99dcea..b33b32ff977 100644
> --- a/builtin/hash-object.c
> +++ b/builtin/hash-object.c
> @@ -57,11 +57,39 @@ static void hash_fd(int fd, const char *type, const char *path, unsigned flags,
>  	maybe_flush_or_die(stdout, "hash to stdout");
>  }
>  
> +#ifdef __MVS__
> +#  if (__CHARSET_LIB == 1)
> +#  include <stdio.h>
> +#  include <stdlib.h>
> +
> +   int setbinaryfd(int fd)
> +   {
> +     attrib_t attr;
> +     int rc;

Ahh, OK, I saw [03/13] first and was utterly confused by this thing.
Do not send in such a mess that introduces broken code in an early
step that you need to later say "oops that one was broken and I need
to fix it up with this patch".  "rebase -i" is your friend to clean
up your mess into a logical progression to help readers better
understand what you wrote.

I'll stop here.
Junio C Hamano Nov. 14, 2023, 1:47 a.m. UTC | #3
"brian m. carlson" <sandals@crustytoothpaste.net> writes:

> I'd generally want to look at the commit message and understand the
> problem the code is trying to solve and then look at the code and think,
> "Oh, yes, this seems like the obvious and logical way to solve this
> problem," or at least think, "Oh, no, I think we should solve this
> problem in a different way," so I can help make a thoughtful review
> comment.  Right now, I lack the information to have an informed opinion
> and so I can't provide any helpful feedback or analysis of the patches.
> ...
> I'd recommend a quick pass over the SubmittingPatches file, which is
> also available at https://git-scm.com/docs/SubmittingPatches.  The
> sections on making separate commits for separate changes and describing
> changes well come to mind as places to focus.
>
> I know this may seem overwhelming and like I'm upset or disappointed;
> I'm definitely not.  I'm very much interested in seeing Git available
> for more platforms, but right now it's too hard for me to reason about
> the changes for z/OS to provide helpful feedback, so I'm hoping you can
> send a fixed v2 that helps me (and everyone else) understand these
> changes better so you can get a helpful review.

All very good pieces of advice.  I suspect we are missing some of
them from our SubmittingPatches or CodingGuidelines documents and
may want to add them there.

Thanks.
diff mbox series

Patch

diff --git a/Makefile b/Makefile
index 9c6a2f125f8..30aa76da4f4 100644
--- a/Makefile
+++ b/Makefile
@@ -20,6 +20,8 @@  include shared.mak
 #
 # Define SHELL_PATH to a POSIX shell if your /bin/sh is broken.
 #
+# Define SHELL_PATH_FOR_SCRIPTS to a POSIX shell if your /bin/sh is broken.
+#
 # Define SANE_TOOL_PATH to a colon-separated list of paths to prepend
 # to PATH if your tools in /usr/bin are broken.
 #
@@ -215,6 +217,8 @@  include shared.mak
 #
 # Define PERL_PATH to the path of your Perl binary (usually /usr/bin/perl).
 #
+# Define PERL_PATH_FOR_SCRIPTS to a Perl binary if your /usr/bin/perl is broken.
+#
 # Define NO_PERL if you do not want Perl scripts or libraries at all.
 #
 # Define NO_PERL_CPAN_FALLBACKS if you do not want to install bundled
@@ -903,9 +907,15 @@  BINDIR_PROGRAMS_NO_X += git-cvsserver
 ifndef SHELL_PATH
 	SHELL_PATH = /bin/sh
 endif
+ifndef SHELL_PATH_FOR_SCRIPTS
+	SHELL_PATH_FOR_SCRIPTS = /bin/sh
+endif
 ifndef PERL_PATH
 	PERL_PATH = /usr/bin/perl
 endif
+ifndef PERL_PATH_FOR_SCRIPTS
+	PERL_PATH_FOR_SCRIPTS = /usr/bin/perl
+endif
 ifndef PYTHON_PATH
 	PYTHON_PATH = /usr/bin/python
 endif
@@ -1336,7 +1346,7 @@  THIRD_PARTY_SOURCES += sha1dc/%
 
 # xdiff and reftable libs may in turn depend on what is in libgit.a
 GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE)
-EXTLIBS =
+EXTLIBS = $(ZOPEN_EXTRA_LIBS)
 
 GIT_USER_AGENT = git/$(GIT_VERSION)
 
@@ -2226,9 +2236,10 @@  perllibdir_relative_SQ = $(subst ','\'',$(perllibdir_relative))
 gitwebdir_SQ = $(subst ','\'',$(gitwebdir))
 gitwebstaticdir_SQ = $(subst ','\'',$(gitwebstaticdir))
 
-SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH_FOR_SCRIPTS))
 TEST_SHELL_PATH_SQ = $(subst ','\'',$(TEST_SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+PERL_PATH_FOR_SCRIPTS_SQ = $(subst ','\'',$(PERL_PATH_FOR_SCRIPTS))
 PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
 TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
 DIFF_SQ = $(subst ','\'',$(DIFF))
@@ -2448,7 +2459,7 @@  hook-list.h: generate-hooklist.sh Documentation/githooks.txt
 
 SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):\
 	$(localedir_SQ):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
-	$(gitwebdir_SQ):$(PERL_PATH_SQ):$(PAGER_ENV):\
+	$(gitwebdir_SQ):$(PERL_PATH_FOR_SCRIPTS_SQ):$(PAGER_ENV):\
 	$(perllibdir_SQ)
 GIT-SCRIPT-DEFINES: FORCE
 	@FLAGS='$(SCRIPT_DEFINES)'; \
@@ -2465,7 +2476,7 @@  sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
     -e 's/@@USE_GETTEXT_SCHEME@@/$(USE_GETTEXT_SCHEME)/g' \
     -e $(BROKEN_PATH_FIX) \
     -e 's|@@GITWEBDIR@@|$(gitwebdir_SQ)|g' \
-    -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
+    -e 's|@@PERL@@|$(PERL_PATH_FOR_SCRIPTS_SQ)|g' \
     -e 's|@@PAGER_ENV@@|$(PAGER_ENV_SQ)|g' \
     $@.sh >$@+
 endef
@@ -2519,7 +2530,7 @@  PERL_DEFINES += $(gitexecdir) $(perllibdir) $(localedir)
 $(SCRIPT_PERL_GEN): % : %.perl GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE
 	$(QUIET_GEN) \
 	sed -e '1{' \
-	    -e '	s|#!.*perl|#!$(PERL_PATH_SQ)|' \
+	    -e '	s|#!.*perl|#!$(PERL_PATH_FOR_SCRIPTS_SQ)|' \
 	    -e '	r GIT-PERL-HEADER' \
 	    -e '	G' \
 	    -e '}' \
diff --git a/builtin.h b/builtin.h
index d560baa6618..806af1a262d 100644
--- a/builtin.h
+++ b/builtin.h
@@ -250,5 +250,8 @@  int cmd_verify_pack(int argc, const char **argv, const char *prefix);
 int cmd_show_ref(int argc, const char **argv, const char *prefix);
 int cmd_pack_refs(int argc, const char **argv, const char *prefix);
 int cmd_replace(int argc, const char **argv, const char *prefix);
+#ifdef __MVS__
+  extern int setbinaryfd(int);
+#endif
 
 #endif
diff --git a/builtin/archive.c b/builtin/archive.c
index 90761fdfee0..53ec794356f 100644
--- a/builtin/archive.c
+++ b/builtin/archive.c
@@ -14,6 +14,12 @@ 
 static void create_output_file(const char *output_file)
 {
 	int output_fd = xopen(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
+#ifdef __MVS__
+ #if (__CHARSET_LIB == 1)
+	if (setbinaryfd(output_fd))
+		die_errno(_("could not tag archive file '%s'"), output_file);
+ #endif
+#endif
 	if (output_fd != 1) {
 		if (dup2(output_fd, 1) < 0)
 			die_errno(_("could not redirect output"));
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index 5ffec99dcea..b33b32ff977 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -57,11 +57,39 @@  static void hash_fd(int fd, const char *type, const char *path, unsigned flags,
 	maybe_flush_or_die(stdout, "hash to stdout");
 }
 
+#ifdef __MVS__
+#  if (__CHARSET_LIB == 1)
+#  include <stdio.h>
+#  include <stdlib.h>
+
+   int setbinaryfd(int fd)
+   {
+     attrib_t attr;
+     int rc;
+
+     memset(&attr, 0, sizeof(attr));
+     attr.att_filetagchg = 1;
+     attr.att_filetag.ft_ccsid = FT_BINARY;
+     attr.att_filetag.ft_txtflag = 0;
+
+     rc = __fchattr(fd, &attr, sizeof(attr));
+     return rc;
+   }
+#  endif
+#endif
+
+
 static void hash_object(const char *path, const char *type, const char *vpath,
 			unsigned flags, int literally)
 {
 	int fd;
 	fd = xopen(path, O_RDONLY);
+#ifdef __MVS__
+#  if (__CHARSET_LIB == 1)
+  if (setbinaryfd(fd))
+		die_errno("Cannot set to binary '%s'", path);
+#  endif
+#endif
 	hash_fd(fd, type, vpath, flags, literally);
 }
 
diff --git a/combine-diff.c b/combine-diff.c
index f90f4424829..73445a517c7 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -1082,6 +1082,10 @@  static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
 			ssize_t done;
 			int is_file, i;
 
+#ifdef __MVS__
+      __disableautocvt(fd);
+#endif
+
 			elem->mode = canon_mode(st.st_mode);
 			/* if symlinks don't work, assume symlink if all parents
 			 * are symlinks
diff --git a/config.c b/config.c
index f9a1cca4e8a..37c124a37c0 100644
--- a/config.c
+++ b/config.c
@@ -1521,6 +1521,13 @@  static int git_default_core_config(const char *var, const char *value,
 		return 0;
 	}
 
+	#ifdef __MVS__
+	if (!strcmp(var, "core.ignorefiletags")) {
+		ignore_file_tags = git_config_bool(var, value);
+		return 0;
+	}
+	#endif
+
 	if (!strcmp(var, "core.safecrlf")) {
 		int eol_rndtrp_die;
 		if (value && !strcasecmp(value, "warn")) {
diff --git a/configure.ac b/configure.ac
index 276593cd9dd..ed380504be6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -463,6 +463,9 @@  else
             CC_LD_DYNPATH=-Wl,+b,
           else
              CC_LD_DYNPATH=
+             if test "$(uname -s)" = "OS/390"; then
+                CC_LD_DYNPATH=-L
+             fi
              AC_MSG_WARN([linker does not support runtime path to dynamic libraries])
           fi
       fi
diff --git a/convert.c b/convert.c
index a8870baff36..4f14ff6f1ed 100644
--- a/convert.c
+++ b/convert.c
@@ -377,12 +377,15 @@  static int check_roundtrip(const char *enc_name)
 static const char *default_encoding = "UTF-8";
 
 static int encode_to_git(const char *path, const char *src, size_t src_len,
-			 struct strbuf *buf, const char *enc, int conv_flags)
+			 struct strbuf *buf, const char *enc, enum convert_crlf_action attr_action, int conv_flags)
 {
 	char *dst;
 	size_t dst_len;
 	int die_on_error = conv_flags & CONV_WRITE_OBJECT;
 
+  if (attr_action == CRLF_BINARY) {
+    return 0;
+  }
 	/*
 	 * No encoding is specified or there is nothing to encode.
 	 * Tell the caller that the content was not modified.
@@ -403,6 +406,11 @@  static int encode_to_git(const char *path, const char *src, size_t src_len,
 		return 0;
 
 	trace_encoding("source", path, enc, src, src_len);
+#ifdef __MVS__
+  // Don't convert ISO8859-1 on z/OS
+  if (strcasecmp("ISO8859-1", enc) == 0)
+    return 0;
+#endif
 	dst = reencode_string_len(src, src_len, default_encoding, enc,
 				  &dst_len);
 	if (!dst) {
@@ -468,11 +476,14 @@  static int encode_to_git(const char *path, const char *src, size_t src_len,
 }
 
 static int encode_to_worktree(const char *path, const char *src, size_t src_len,
-			      struct strbuf *buf, const char *enc)
+			      struct strbuf *buf, enum convert_crlf_action attr_action, const char *enc)
 {
 	char *dst;
 	size_t dst_len;
 
+  if (attr_action == CRLF_BINARY) {
+    return 0;
+  }
 	/*
 	 * No encoding is specified or there is nothing to encode.
 	 * Tell the caller that the content was not modified.
@@ -1302,18 +1313,37 @@  static int git_path_check_ident(struct attr_check_item *check)
 
 static struct attr_check *check;
 
+static const char* get_platform() {
+	struct utsname uname_info;
+
+	if (uname(&uname_info))
+		die(_("uname() failed with error '%s' (%d)\n"),
+			    strerror(errno),
+			    errno);
+
+  if (!strcmp(uname_info.sysname, "OS/390"))
+    return "zos";
+  return uname_info.sysname;
+}
+
+
 void convert_attrs(struct index_state *istate,
 		   struct conv_attrs *ca, const char *path)
 {
 	struct attr_check_item *ccheck = NULL;
+  struct strbuf platform_working_tree_encoding = STRBUF_INIT;
+
+	strbuf_addf(&platform_working_tree_encoding, "%s-working-tree-encoding", get_platform());
+
 
 	if (!check) {
 		check = attr_check_initl("crlf", "ident", "filter",
-					 "eol", "text", "working-tree-encoding",
+					 "eol", "text", "working-tree-encoding", platform_working_tree_encoding.buf,
 					 NULL);
 		user_convert_tail = &user_convert;
 		git_config(read_convert_config, NULL);
 	}
+	strbuf_release(&platform_working_tree_encoding);
 
 	git_check_attr(istate, path, check);
 	ccheck = check->items;
@@ -1334,6 +1364,8 @@  void convert_attrs(struct index_state *istate,
 			ca->crlf_action = CRLF_TEXT_CRLF;
 	}
 	ca->working_tree_encoding = git_path_check_encoding(ccheck + 5);
+  if (git_path_check_encoding(ccheck + 6))
+    ca->working_tree_encoding = git_path_check_encoding(ccheck + 6);
 
 	/* Save attr and make a decision for action */
 	ca->attr_action = ca->crlf_action;
@@ -1427,7 +1459,7 @@  int convert_to_git(struct index_state *istate,
 		len = dst->len;
 	}
 
-	ret |= encode_to_git(path, src, len, dst, ca.working_tree_encoding, conv_flags);
+	ret |= encode_to_git(path, src, len, dst, ca.working_tree_encoding, ca.attr_action, conv_flags);
 	if (ret && dst) {
 		src = dst->buf;
 		len = dst->len;
@@ -1455,7 +1487,7 @@  void convert_to_git_filter_fd(struct index_state *istate,
 	if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN, NULL, NULL))
 		die(_("%s: clean filter '%s' failed"), path, ca.drv->name);
 
-	encode_to_git(path, dst->buf, dst->len, dst, ca.working_tree_encoding, conv_flags);
+	encode_to_git(path, dst->buf, dst->len, dst, ca.working_tree_encoding, ca.attr_action, conv_flags);
 	crlf_to_git(istate, path, dst->buf, dst->len, dst, ca.crlf_action, conv_flags);
 	ident_to_git(dst->buf, dst->len, dst, ca.ident);
 }
@@ -1487,7 +1519,7 @@  static int convert_to_working_tree_ca_internal(const struct conv_attrs *ca,
 		}
 	}
 
-	ret |= encode_to_worktree(path, src, len, dst, ca->working_tree_encoding);
+	ret |= encode_to_worktree(path, src, len, dst, ca->attr_action, ca->working_tree_encoding);
 	if (ret) {
 		src = dst->buf;
 		len = dst->len;
diff --git a/copy.c b/copy.c
index 23d84c6c1db..63546aecf81 100644
--- a/copy.c
+++ b/copy.c
@@ -14,6 +14,9 @@  int copy_fd(int ifd, int ofd)
 		if (write_in_full(ofd, buffer, len) < 0)
 			return COPY_WRITE_ERROR;
 	}
+#ifdef __MVS__
+  __copyfdccsid(ifd, ofd);
+#endif
 	return 0;
 }
 
diff --git a/diff.c b/diff.c
index 2c602df10a3..28b96d53dbc 100644
--- a/diff.c
+++ b/diff.c
@@ -4083,6 +4083,9 @@  int diff_populate_filespec(struct repository *r,
 	int check_binary = options ? options->check_binary : 0;
 	int err = 0;
 	int conv_flags = global_conv_flags_eol;
+#ifdef __MVS__
+	int autocvtToASCII;
+#endif
 	/*
 	 * demote FAIL to WARN to allow inspecting the situation
 	 * instead of refusing.
@@ -4155,9 +4158,17 @@  int diff_populate_filespec(struct repository *r,
 			s->is_binary = 1;
 			return 0;
 		}
+#ifdef __MVS__
+    validate_codeset(r->index, s->path, &autocvtToASCII);
+#endif
 		fd = open(s->path, O_RDONLY);
 		if (fd < 0)
 			goto err_empty;
+
+#ifdef __MVS__
+    if (!autocvtToASCII)
+      __disableautocvt(fd);
+#endif
 		s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
 		close(fd);
 		s->should_munmap = 1;
diff --git a/entry.c b/entry.c
index 076e97eb89c..df6feb2234b 100644
--- a/entry.c
+++ b/entry.c
@@ -126,6 +126,24 @@  int fstat_checkout_output(int fd, const struct checkout *state, struct stat *st)
 	return 0;
 }
 
+#ifdef __MVS__
+void tag_file_as_working_tree_encoding(struct index_state *istate, char* path, int fd) {
+	struct conv_attrs ca;
+	convert_attrs(istate, &ca, path);
+  if (ca.attr_action != CRLF_BINARY) {
+    if (ca.working_tree_encoding)
+      __chgfdcodeset(fd, ca.working_tree_encoding);
+    else
+      __setfdtext(fd);
+  }
+  else {
+    __setfdbinary(fd);
+  }
+
+  __disableautocvt(fd);
+}
+#endif
+
 static int streaming_write_entry(const struct cache_entry *ce, char *path,
 				 struct stream_filter *filter,
 				 const struct checkout *state, int to_tempfile,
@@ -138,6 +156,10 @@  static int streaming_write_entry(const struct cache_entry *ce, char *path,
 	if (fd < 0)
 		return -1;
 
+#ifdef __MVS__
+  tag_file_as_working_tree_encoding(state->istate, path, fd);
+#endif
+
 	result |= stream_blob_to_fd(fd, &ce->oid, filter, 1);
 	*fstat_done = fstat_checkout_output(fd, state, statbuf);
 	result |= close(fd);
@@ -374,6 +396,10 @@  static int write_entry(struct cache_entry *ce, char *path, struct conv_attrs *ca
 			return error_errno("unable to create file %s", path);
 		}
 
+#ifdef __MVS__
+    tag_file_as_working_tree_encoding(state->istate, path, fd);
+#endif
+
 		wrote = write_in_full(fd, new_blob, size);
 		if (!to_tempfile)
 			fstat_done = fstat_checkout_output(fd, state, &st);
diff --git a/environment.c b/environment.c
index bb3c2a96a33..2e4d3a1e058 100644
--- a/environment.c
+++ b/environment.c
@@ -51,6 +51,9 @@  const char *git_hooks_path;
 int zlib_compression_level = Z_BEST_SPEED;
 int pack_compression_level = Z_DEFAULT_COMPRESSION;
 int fsync_object_files = -1;
+#ifdef __MVS__
+int ignore_file_tags = 0;
+#endif
 int use_fsync = -1;
 enum fsync_method fsync_method = FSYNC_METHOD_DEFAULT;
 enum fsync_component fsync_components = FSYNC_COMPONENTS_DEFAULT;
diff --git a/git-compat-util.h b/git-compat-util.h
index 3e7a59b5ff1..66e0abec24b 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -223,7 +223,15 @@  struct strbuf;
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <stddef.h>
+#ifdef __MVS__
+#define release stdlib_release
+#define fetch stdlib_fetch
+#endif
 #include <stdlib.h>
+#ifdef __MVS__
+#undef fetch
+#undef release
+#endif
 #include <stdarg.h>
 #include <string.h>
 #ifdef HAVE_STRINGS_H
diff --git a/negotiator/default.c b/negotiator/default.c
index 9a5b6963272..b1f9f153372 100644
--- a/negotiator/default.c
+++ b/negotiator/default.c
@@ -174,7 +174,7 @@  static int ack(struct fetch_negotiator *n, struct commit *c)
 	return known_to_be_common;
 }
 
-static void release(struct fetch_negotiator *n)
+static void release_negotiator(struct fetch_negotiator *n)
 {
 	clear_prio_queue(&((struct negotiation_state *)n->data)->rev_list);
 	FREE_AND_NULL(n->data);
@@ -187,7 +187,7 @@  void default_negotiator_init(struct fetch_negotiator *negotiator)
 	negotiator->add_tip = add_tip;
 	negotiator->next = next;
 	negotiator->ack = ack;
-	negotiator->release = release;
+	negotiator->release_negotiator = release_negotiator;
 	negotiator->data = CALLOC_ARRAY(ns, 1);
 	ns->rev_list.compare = compare_commits_by_commit_date;
 
diff --git a/negotiator/noop.c b/negotiator/noop.c
index de39028ab7f..82089654d8b 100644
--- a/negotiator/noop.c
+++ b/negotiator/noop.c
@@ -30,7 +30,7 @@  static int ack(struct fetch_negotiator *n UNUSED, struct commit *c UNUSED)
 	return 0;
 }
 
-static void release(struct fetch_negotiator *n UNUSED)
+static void release_negotiator(struct fetch_negotiator *n UNUSED)
 {
 	/* nothing to release */
 }
@@ -41,6 +41,6 @@  void noop_negotiator_init(struct fetch_negotiator *negotiator)
 	negotiator->add_tip = add_tip;
 	negotiator->next = next;
 	negotiator->ack = ack;
-	negotiator->release = release;
+	negotiator->release_negotiator = release_negotiator;
 	negotiator->data = NULL;
 }
diff --git a/negotiator/skipping.c b/negotiator/skipping.c
index 5b91520430c..783b3f27e63 100644
--- a/negotiator/skipping.c
+++ b/negotiator/skipping.c
@@ -243,7 +243,7 @@  static int ack(struct fetch_negotiator *n, struct commit *c)
 	return known_to_be_common;
 }
 
-static void release(struct fetch_negotiator *n)
+static void release_negotiator(struct fetch_negotiator *n)
 {
 	clear_prio_queue(&((struct data *)n->data)->rev_list);
 	FREE_AND_NULL(n->data);
@@ -256,7 +256,7 @@  void skipping_negotiator_init(struct fetch_negotiator *negotiator)
 	negotiator->add_tip = add_tip;
 	negotiator->next = next;
 	negotiator->ack = ack;
-	negotiator->release = release;
+	negotiator->release_negotiator = release_negotiator;
 	negotiator->data = CALLOC_ARRAY(data, 1);
 	data->rev_list.compare = compare;
 
diff --git a/object-file.c b/object-file.c
index 7c7afe57936..28e69ed1e33 100644
--- a/object-file.c
+++ b/object-file.c
@@ -43,7 +43,9 @@ 
 #include "setup.h"
 #include "submodule.h"
 #include "fsck.h"
-
+#ifdef __MVS__
+#include <_Ccsid.h>
+#endif
 /* The maximum size for an object header. */
 #define MAX_HEADER_LEN 32
 
@@ -2478,6 +2480,68 @@  int index_fd(struct index_state *istate, struct object_id *oid,
 	return ret;
 }
 
+#ifdef __MVS__
+void validate_codeset(struct index_state *istate, const char *path, int* autoconvertToASCII) {
+       struct conv_attrs ca;
+  struct stat st;
+  unsigned short attr_ccsid;
+  unsigned short file_ccsid;
+
+  if (ignore_file_tags)
+   return;
+
+  *autoconvertToASCII = 0;
+       convert_attrs(istate, &ca, path);
+  if (ca.attr_action == CRLF_BINARY) {
+    attr_ccsid = FT_BINARY;
+  }
+  else if (ca.working_tree_encoding) {
+    attr_ccsid = __toCcsid(ca.working_tree_encoding);
+  }
+  else
+    attr_ccsid = 819;
+
+  if (stat(path, &st) < 0)
+    return;
+
+  file_ccsid = st.st_tag.ft_ccsid;
+
+  if (file_ccsid == FT_UNTAGGED) {
+    die("File %s is untagged, set the correct file tag (using the chtag command).", path);
+  }
+
+  if (attr_ccsid != file_ccsid) {
+    if (file_ccsid == 1047 && attr_ccsid == 819) {
+      *autoconvertToASCII = 1;
+      return;
+    }
+    // Allow tag mixing of 819 and 1208
+    if ((file_ccsid == 819 || file_ccsid == 1208) && (attr_ccsid == 1208 || attr_ccsid == 819)) {
+      return;
+    }
+    // Don't check for binary files, just add them
+    if (attr_ccsid == FT_BINARY)
+      return;
+
+    char attr_csname[_XOPEN_PATH_MAX] = {0};
+    char file_csname[_XOPEN_PATH_MAX] = {0};
+    if (attr_ccsid != FT_BINARY) {
+      __toCSName(attr_ccsid, attr_csname);
+    } else {
+      snprintf(attr_csname, _XOPEN_PATH_MAX, "%s", "binary");
+    }
+    if (file_ccsid != FT_BINARY) {
+      __toCSName(file_ccsid, file_csname);
+    } else {
+      snprintf(file_csname, _XOPEN_PATH_MAX, "%s", "binary");
+    }
+    die("%s added file: file tag (%s) does not match working-tree-encoding (%s)", path, file_csname, attr_csname);
+  }
+}
+#endif
+
+
+
 int index_path(struct index_state *istate, struct object_id *oid,
 	       const char *path, struct stat *st, unsigned flags)
 {
@@ -2485,11 +2549,25 @@  int index_path(struct index_state *istate, struct object_id *oid,
 	struct strbuf sb = STRBUF_INIT;
 	int rc = 0;
 
+#ifdef __MVS__
+	struct conv_attrs ca;
+	int autocvtToASCII;
+#endif
+
 	switch (st->st_mode & S_IFMT) {
 	case S_IFREG:
+#ifdef __MVS__
+    validate_codeset(istate, path, &autocvtToASCII);
+#endif
 		fd = open(path, O_RDONLY);
 		if (fd < 0)
 			return error_errno("open(\"%s\")", path);
+
+#ifdef __MVS__
+   if (!autocvtToASCII)
+     __disableautocvt(fd);
+#endif
+
 		if (index_fd(istate, oid, fd, st, OBJ_BLOB, path, flags) < 0)
 			return error(_("%s: failed to insert into database"),
 				     path);
diff --git a/read-cache.c b/read-cache.c
index 080bd39713b..75c06121302 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -205,6 +205,9 @@  static int ce_compare_data(struct index_state *istate,
 	int fd = git_open_cloexec(ce->name, O_RDONLY);
 
 	if (fd >= 0) {
+#ifdef __MVS__
+    __disableautocvt(fd);
+#endif
 		struct object_id oid;
 		if (!index_fd(istate, &oid, fd, st, OBJ_BLOB, ce->name, 0))
 			match = !oideq(&oid, &ce->oid);
diff --git a/utf8.c b/utf8.c
index 6a0dd25b0fe..b9cb56abf14 100644
--- a/utf8.c
+++ b/utf8.c
@@ -590,6 +590,17 @@  char *reencode_string_len(const char *in, size_t insz,
 #endif
 	}
 
+#ifdef __MVS__
+  //HACK: For backwards compat, ISO8859-1 really means utf-8 in the z/OS world
+  if (strcasecmp("ISO8859-1", in_encoding) == 0) {
+    in_encoding = "UTF-8";
+    out_encoding = "UTF-8";
+  }
+  if (strcasecmp("ISO8859-1", out_encoding) == 0) {
+    in_encoding = "UTF-8";
+    out_encoding = "UTF-8";
+  }
+#endif
 	conv = iconv_open(out_encoding, in_encoding);
 	if (conv == (iconv_t) -1) {
 		in_encoding = fallback_encoding(in_encoding);