Chapter 10
Supporting Files in Git

In this chapter, I explore two types of supporting files that allow users to customize how Git interacts with different kinds of content. The Git attributes file allows you to define settings to apply to certain operations for particular files or file types. The Git ignore file allows you to tell Git which files it should ignore and not try to manage. I cover the intent, usage, scope, and format for both types of files.

Having these two files with each repository is a best practice for Git. There are also many customized versions that have been created by users for different types of work. For example, an attributes file for Java development could include specifications on line endings for *.java files. The corresponding ignore file could include lines to tell Git not to track or manage *.jar, *.war, and other files, because those are generated files in the java workflow.

Over the years, Git users doing different kinds of development have created versions of these files that have worked well for them. In the spirit of open source, users commonly contribute the files that they have used back to public places on the Internet. One popular site for these contributions from users is GitHub. Normally, GitHub is thought of as a place to host Git repositories. However, it is also a place for users to share supporting items for development.

You can find some sample Git attributes files for different languages at https://github.com/Danimoth/gitattributes and sample Git ignore files for different kinds of development at https://github.com/github/gitignore.

Now, let's dive into the specifics of an attributes file.

THE GIT ATTRIBUTES FILE

One thing that is not clear with Git out of the box is a way to specify how to handle specific file types. The most obvious example of this is being able to tell Git which files are binary. (Note that Git has an algorithm built-in to try and detect if a file is binary, but it cannot be claimed to be 100% accurate.)

To provide this kind of functionality, Git supports specifying options about how to treat different kinds of files using a Git attributes file. As the name implies, this file allows users to specify attributes for files that Git manages. These attributes serve as specifications that drive or modify the behavior of particular Git operations when those operations are done on the matching files. For example, if a file or file type is noted as binary, that tells Git not to do the usual diffing and EOL processing against it.

What Can You Do with This File?

A Git attributes file can be used for a number of purposes. Here are a few of the main applications:

  • Specifying which files or file types Git should treat as binary
  • Specifying how to handle line endings for particular files or file types
  • Specifying unique filters to associate with files or file types to perform custom operations

I'll spend some time talking about these uses in the following pages. First, though, it's useful to understand the different places that versions of this file can exist—and how their location determines which files get which attributes.

The Scope of Git Attributes

In your working directory, at the granularity of a subdirectory, you can create a Git attributes file. These files are named .gitattributes. Having these exist in the working directory is useful because this file can be committed into your local repository, then pushed to the remote repository, and reside with your code. So, any user or process that clones this repository will get the same .gitattributes file telling Git how to handle files in this repository.

The other useful part about having a Git Attributes file in your working directory is that, for things like line endings, the Git attributes file overrides individual configuration values. This means you can specify how to handle line endings in the Git attributes file, regardless of how a user may have their configuration values set. Because the Git attributes file is pulled in when the project is cloned, the project will always use the settings from the file. Thus, you have consistent line-ending behavior across all users of the repository.

Similar to how you have local, global, and system configuration scopes, Git attributes files can also have different scopes. The same file format as a .gitattributes file can be put in the .git directory—in .git/info/attributes (where attributes is a filename, not a directory). This will have higher precedence than a .gitattributes file in the working directory.

Within the subdirectories of the working directory, there can be individual .gitattributes files (one per directory). When it comes time to evaluate them, Git will use the attributes in the .git/info/attributes file first (if present) and then the .gitattributes file it finds that's closest to the path being evaluated.

So, suppose I have a structure like dir1/dir2/dir3 and I have .gitattributes files in all three subdirectories. If I am working with a file in dir3, Git will first use any attribute information from .git/info/attributes, then any information from the .gitattributes file in dir3, then from the one in dir2, and finally from the one in dir1.

Beyond that, you can have a global Git attributes file. This one, like global configuration values, applies to all repositories for a user. The location of this file can be set explicitly by setting the core.attributesFile configuration value. This has a default value of $XDG_CONFIG_HOME/git/attributes. (It is rare to have the $XDG_CONFIG_HOME reference set. See the note in this chapter on “Git's Fourth Configuration File.”) If the $XDG_CONFIG_HOME reference is not set, then you fall back to the more common $HOME/.config/git/attributes file (where $HOME is the user's home directory).

That just leaves the system-level Git attributes file—for all users on a system. Like system configuration files, this file resides in the etc area, such as /etc/gitattributes.

Table 10.1 summarizes the scopes for Git attributes files, starting from the highest priority to the lowest.

Table 10.1 The File Scope for Git Attributes

Scope Name and Location Stored with Project Use/Notes
Internal .git/info/attributes No Overrides all others but not stored with project (user-specific)
Local subdir/.gitattributes Yes (committed) Defines attribute handling for files in this <subdir> and any below it that don't have their own .gitattributes. This file is committed into source control and resides with the project. It overrides certain configuration values.
Global Usually $HOME/.config/git/attributes or as specified by core.attributesFile configuration value No All repositories for a user
System etc/gitattributes No System attributes for all repositories if not overridden by one of the others

The File Format

The file format for a Git attributes file is fairly simple. There are only a few rules:

  • Lines are composed of filenames or patterns followed by attribute specifications.
  • Any line starting with a # character is a comment.
  • Lines that specify attribute settings have a format of
<file pattern or filename> <attribute and setting>

Some examples of a file pattern or filename include *.obj, foo.txt, and a*.java.

On each line of a Git attributes file, attributes can be defined in multiple ways. Table 10.2 summarizes them.

Table 10.2 Options for Specifying Attributes

Setting Format Example Meaning
Set Attribute name by itself *.obj binary Attribute is turned on (all object files are binary in the example).
Unset Attribute name preceded by minus sign *.java -crlf Attribute is turned off (no java files should have CRLFs in the example).
Configured (set to a value) Attribute name = value eol = crlf Attribute is configured with a specific value (set line endings to crlf on checkout for matching files in the example).
Unspecified Attribute isn't specified for any paths Behavior falls back to what Git would do without the attributes file.

Here are a couple of other points to remember about the file format:

  • Later lines that match override earlier lines.
  • More than one attribute can be specified (exist on the same line) for a file or file type.

Common Use Cases

In this section, I'll survey some common use cases for a Git attributes file. First, though, there are some things you need to consider when creating or amending one of these files.

Putting together a Git attributes file is a combination of several steps:

  1. Deciding on the scope: Should this be managed with the repository (reside in the working directory); be global (reside in $HOME); apply to the working directory, but not be managed (reside in git/attributes); or reside in one of the subdirectories?
  2. Identifying the file or pattern that you want to apply an attribute to.
  3. Choosing the appropriate attribute.
  4. Choosing whether the attribute should be set, unset, set to a value (string), or unspecified to accomplish what you want.
  5. If the attribute is set to a custom value or a filter is specified, defining the value or filter for Git—this includes creating external programs if needed and setting configuration values.
  6. If custom supporting pieces are needed (as in step 5) and this setup is intended to be pushed into a remote repository for use by others, ensuring that the supporting pieces are accessible and any additional needed configuration is well documented.

Now, let's look at a couple of common use cases and useful tricks to illustrate what you can do with the file. You will implement these use cases through the use of attributes. Although not explicitly stated each time, the idea is that these attributes apply to the file or patterns on the corresponding line in the Git attributes file.

Use Case 1: Identifying Files as Binary

This one is fairly simple. To tell Git that a file is binary, simply set the binary attribute in the Git attributes file. For example, *.exe binary . Binary here actually corresponds to the deprecated settings of -crlf and -diff. So, if you think about the dash (-) on the front of those attributes as meaning don't do it, you could interpret binary as don't try to convert or fix eol issues (-crlf) and don't try to execute or print a diff (-diff) for executable files.

Use Case 2: Specifying Handling for Line Endings

In this case, you have several attributes to choose from:

  • text—This attribute has to do with line normalization and indicating text file types. Here are some examples of how it can be specified, along with their meanings.
    • text (setting the value) tells Git to normalize line endings and that files like this are text files.
    • -text (unsetting the value) tells Git not to attempt to normalize line endings.
    • text=auto (set to value) tells Git to normalize line endings if it thinks the file is text. (To determine this, Git is checking for a NULL value in a significant part of the file. If the NULL value is found, the file is considered to be binary.)

      If text is not specified, then Git falls back to using the core.autocrlf value as configured by the user.

  • eol—This attribute tells Git which style of line normalization to apply. Its presence tells Git that this is a text file.
    • eol=crlf (set to value)—llike autocrlf=true, this value tells Git to normalize files (to just LF) at add or commit, and insert CRLFs on checkout.
    • eol=lf (set to value)—like autocrlf=input, this value tells Git to normalize files (to just LF) at add or commit, and leave as LFs on checkout.

Use Case 3: Creating a Custom Filter

If you simply define the attribute as filter=<name>, Git assumes you are defining a custom filter called <name>. This allows you to have two filtering actions, known as smudge and clean.

Smudge is essentially a way to say, “For the matching items in the Git attributes file, run a set of code (a filter) when those items are checked out of Git.” Clean is the same, but it runs designated code when the matching items are committed back into Git. Figure 10.1 shows where these actions fit into your Git model.

A schematic diagram of Git model with smudge and clean filters.

Figure 10.1 The Git model with smudge and clean filters

Git assumes that the name you provide to the filter actually refers to a filter driver. A filter driver is a program or set of commands that are defined to execute for the smudge and clean operations when this filter is run. To map the filter names to the actual code that is executed, you just use the standard git config operation.

Let's take a look at a simple example to illustrate how all of this works. Suppose you have a couple of HTML files that you use as a header and footer across multiple divisions in your company. They contain a placeholder in the form of the text string %div to indicate where the proper division name should be inserted.

You want to use the smudge and clean filters to automatically replace the placeholder with your division name (ABC) when you check the file out of Git, and make sure to set it back to the generic version if you make any other changes and commit those changes.

The following listing shows my two source files:

$ cat div_test_header.html
<H1>Running tests for division:%div</H1>

$ cat div_test_footer.html
<H1>Division:%div testing summary</H1>

To make the appropriate transformations, I'm going to create a custom filter that will have associated clean and smudge actions. I'll call this custom filter insertDivisionABC.

This filter will simply use the sed command to substitute ABC for %div, or vice-versa, as required. To define this filter and its actions for Git, you can use the config command as follows:

$ git config filter.insertDivisionABC.smudge "sed 's/%div/ABC/'"
$ git config filter.insertDivisionABC.clean "sed 's/ision:ABC/ision:%div/'"

These commands say, “When you check out these files, replace the occurrence of %div with ABC. And, if you commit a changed version of this file, set it back to the generic form (with the %div) where you had ABC.” (I include a part of division here in the replacement string just to be more precise.)

With this form of the config command, the parts after git config are organized as [item class, item name, subitem, subitem value]. So, if you were to look at the actual config file after these commands, you would find the following section:

$ cat .git/config
       …
 [filter "insertDivisionABC"]
        smudge = sed 's/%div/ABC/'
        clean = sed 's/ision:ABC/ision:%div/'

The last part of connecting everything together is to create a (or append to an existing) Git attributes file with a line that tells Git to run your filter for files matching the desired pattern. The simplest kind of file would be one such as created by the following command:

$ echo "div*.html filter=insertDivisionABC" > .gitattributes

The presence of a .gitattributes file with the line div*.html filter=insertDivisionABC tells Git, “For files matching the pattern, run the specified filter.” This then points to the commands you have configured, depending on whether a checkout or add is being done.

After a checkout with the attributes file and the filter is in place, your local file contents would look like the following output:

$ cat div_test_header.html
<H1>Running tests for division:ABC</H1>

$ cat div_test_footer.html
<H1>Division:ABC testing summary</H1>

Suppose that you now make changes to the local files, adding the text full in one and all in the other. A diff will take into account the filter and just show you the differences without the substitution being applied.

$ git diff
diff --git a/div_test_footer.html b/div_test_footer.html
index 8a5bb93..60e32b7 100644
--- a/div_test_footer.html
+++ b/div_test_footer.html
@@ -1 +1 @@
-<H1>Division:%div testing summary</H1>
+<H1>Division:%div full testing summary</H1>
diff --git a/div_test_header.html b/div_test_header.html
index 124625e..fd1aeb6 100644
--- a/div_test_header.html
+++ b/div_test_header.html
@@ -1 +1 @@
-<H1>Running tests for division:%div</H1>
+<H1>Running all tests for division:%div</H1>

If you now add the files, a status command shows them as staged and modified.

$ git add div*.html

$ git status
On branch feature
Changes to be committed:
  (use "git reset HEAD <file>…" to unstage)

        modified:   div_test_footer.html
        modified:   div_test_header.html

A diff command shows no differences. If you cat the local versions of the files, they still show the substitution from the smudge.

$ git diff


$ cat div_test_footer.html
<H1>Division:ABC full testing summary</H1>

$ cat div_test_header.html
<H1>Running all tests for division:ABC</H1>

You know that the clean filter should have removed your substitution, but the question is, “How do you verify that?” There is a trick you can use with the show command to see the version in the staging area—show :0:<filename>. Using this command, you can verify that the clean filter was applied when the files were staged.

$ git show :0:div_test_footer.html
<H1>Division:%div full testing summary</H1>

$ git show :0:div_test_header.html
<H1>Running all tests for division:%div</H1>

Use Case 4: Tricking Git Merge into Ignoring Files or Paths

For this use case, you can use a trick to tell Git not to merge a file or path when a merge operation is run on the larger set of files. This is not necessarily a common, intended use case, but it can be very useful and illustrates how to use filters on attributes to accomplish custom tasks.

Another type of attribute available in the Git attributes file is merge. Normally, the merge attribute, if specified, is resolved to text or binary. However, using a filter in another way, you can specify a program or command to run when the merge attribute is specified. Normally, handling the merge filter would be similar to defining a custom mergetool (see Chapter 9) that expects certain arguments and performs certain behaviors.

For your purposes here, though, you just need to specify a tool or command that returns a value of true. This is because Git expects any kind of merge filter like this to return true to indicate that the custom merge behavior was successful and you are done. Another characteristic of the merge filter implementation is that when the merge has been completed, the result is stored in the original filename. So, if you just return true here, this is effectively a NO-OP as far as changing the file is concerned. However, Git thinks it has done its job and processed this file as part of the merge.

To set this functionality up, in the Git attributes file, you can define a custom filter for the merge attribute to use. One common name that is often used for this purpose is ours to correspond with the merge option for the recursive strategy and the Ours strategy. For your purposes here, to be a little clearer, you'll use the name unchanged.

Then, to make this all work, you set a configuration value that tells Git to define a filter driver for the merge attribute value of unchanged. This filter driver just has to return true (assuming you have a true command available on your system). The command to configure this value would look like this:

$ git config [--global] merge.unchanged.driver true

Then, in your Git attributes file, you can just specify merge=unchanged for the file or path.

$ cat .gitattributes
filename.ext merge=unchanged

When Git is running a merge that includes this file or path, it assumes from the attributes file that it should run the filter program named unchanged, which then just evaluates to true. Git is done at that point, and so the file or path is not changed.

Note that this trick depends on a particular configuration that needs to be shared and set if other users will use this Git attributes file in this way.

Other Attributes

There are other attributes I haven't covered that are available to use in a Git attributes file. Two attributes that you may encounter are ident and diff:

  • ident—This attribute causes a substitution to occur if the string $Id$ is found in a file on checkout. In other source management systems, this kind of substitution results in items like userids, dates, and so on being substituted. In Git, this is less useful because the string is just expanded to include the SHA1 value as $Id: <sha1 value> $. On checkin, the substitution is reverted.
  • diff—This attribute describes how diffs are generated for the matching file or file types. In short, if it is set (diff), the diff is generated as text. If it is unset (-diff), it is treated as a binary file when a diff is attempted. If it is unspecified, then the default diff behavior is performed. And if it is set to a string, that string is treated as a filter driver that can be defined to do whatever operations are desired when a diff for the matching files or file type is invoked.

Example File

Combining all of these attributes into an example Git attributes file might look like this:

$ cat .gitattributes
# Default is to detect text files and perform normalization to LF
* text=auto

# Treat exe files as binary
*.exe binary

# Don't insert CRLFs in our sh files
*.sh -crlf

# Set eol sequence to CRLF for *.txt files
*.txt eol=crlf

# Don't merge our home page file
index.html merge=unchanged

# Use our custom smudge and clean filters for these files
div*.html filter=insertDivisionABC

# Update text files in the misc directory
misc/*.txt filter=change_text

Note that you have the text=auto attribute setting for everything as your first entry. This acts as a sensible default for normalizing files that Git detects as text. This setting can be overwritten for specific files and file types by later entries in the file.

The last entry here is one I didn't use in the text. However, it illustrates the idea that subdirectory paths can be used to restrict attributes. In this case, the change_text filter will be applied to files with the .txt extension, but only files in the misc subdirectory. Files with the same extension in other subdirectories will not have the filter applied.

Getting Attribute Information for Files

When your Git attribute files start to become large or complicated, or you are dealing with many of these files, you may want to have a way to quickly summarize which attributes are being applied to which files without having to read through the files. Fortunately, Git provides a plumbing command designed to do just that: check-attr. (Recall that plumbing commands are hyphenated, action-object names.)

The format for the check-attr command is pretty simple. There are basically two forms.

git check-attr [-a | --all | attr… ] [--] pathname…
git check-attr --stdin [-z] [-a | --all | attr… ]

For each pathname, the command displays information about what attributes apply to it, and whether they are set, unset, unspecified, or set to a specific value. The attributes can be filtered by a particular attribute, or --all to see all of them. Some simple examples from your custom filter setup are shown here.

$ git check-attr filter div*.html
div_test_footer.html: filter: insertDivisionABC
div_test_header.html: filter: insertDivisionABC

$ git check-attr div*.html --all
div_test_footer.html: filter: insertDivisionABC
div_test_header.html: filter: insertDivisionABC

Now you'll look at the other main support file that Git repositories use, Git ignore. This file tells Git what not to track.

THE GIT IGNORE FILE

In addition to being able to specify particular attributes for files or file types, Git also provides a way to ignore particular files and file types. Items to ignore can be specified in a text file called a Git ignore file.

Ignore in this case means to exclude from tracking and processing. In other words, Git does not attempt to manage or change the indicated files or directories. They are automatically ignored if they exist in an ignore file.

The Scope of Git Ignore

At the directory granularity, you can create a Git ignore file as a file named .gitignore in your working directory. This is useful because this file can be committed into your local repository and then pushed to the remote repository. It resides in your repository, with your code. So, any user or process that clones this repository will get the .gitignore file that tells Git what to ignore.

Similar to how you have local, global, and system configuration scopes, Git ignore files can also have different scopes. The same file format can be stored internally in the .git directory, in .git/info/exclude (where exclude is a filename, not a directory). This is not staged and committed with the source files because it is in the internal repository, so it's useful for personal ignore preferences that wouldn't apply to other users. It also has a lower precedence than a .gitignore file in the working directory.

Within the subdirectories of the working directory, there can be individual .gitignore files (one per directory). When it comes time to evaluate them, Git uses the Git ignore file in the current directory first and then the Git ignore file it finds that's closest to the path being evaluated.

So, suppose I have a structure like dir1/dir2/dir3 and I have .gitignore files in all three subdirectories. If I am working with a file in dir3, Git first uses any ignore information from there, then from the file in dir2, and finally from the file in dir1.

Beyond that, you can have the internal Git ignore file in .git/info/exclude, and also a global Git ignore file. That one, like global configuration values, applies to all repositories for a user. The location of this file can be set explicitly by specifying the core.excludesFile configuration value. This has a default value of $XDG_CONFIG_HOME/git/ignore. (It is rare to have the $XDG_CONFIG_HOME reference set. See the note in this chapter on “Git's Fourth Configuration File.”) If the $XDG_CONFIG_HOME reference is not set, then you fall back to $HOME/.config/git/ignore.

Table 10.3 summarizes the scopes for Git ignore files, starting from the highest priority to the lowest.

Table 10.3 Scopes and Precedence for Git Ignore Files

Scope Name and Location Stored with Project Use
Local subdir/.gitignore Yes (committed) Defines ignore values for files in this subdirectory and any subdirectories below it that don't have their own .gitignore file. This file resides with the project.
Internal .git/info/exclude No For settings that should apply to the repository, but not be stored with it (that is, files resulting from custom workflow, experimentation, and so on)
Global Usually $HOME/.config/git/ignore or as specified by core.excludesFile configuration value No Files to ignore across all repositories for a user (for example, the user's text editor's backup files, which might be consistent across all repositories for one user but different for another user)

The File Format

The file format for a Git ignore file is fairly simple. Here are the basic features:

  • Blank lines can be used as separators for readability.
  • Any line starting with a # character is a comment. (Add a backslash in front of the # if there is a filename that actually starts with #.)
  • If a line ends with a forward slash (/), Git recognizes that forward slash as a directory. It matches the directory path and all things under the directory, but not any files or links with the same name as the directory.
  • Without a forward slash (/), Git tries to match up any paths and patterns specified in the file relative to the repository path.
  • Two consecutive asterisks (**) generally mean match 0 or more subdirectory levels. For example, b/**/e matches b/e, b/c/e, b/c/d/e, and so on.
  • An exclamation point (!) at the start of the line tells Git to negate the pattern. Any file in that pattern that was previously excluded (ignored) becomes included again—unless a parent directory has been excluded, which prevents the file's re-inclusion.

The use cases for this last kind of format deserve some more explanation.

Use Cases for Pattern Negation

At first glance, the idea of pattern negation might seem strange in a Git ignore file. If you are going to include the file, why list it and negate it instead of just not putting it in the ignore file in the first place?

There are two typical uses for this format. One is to override a more general exclusion in a specific path, and the other is to allow targeted inclusion in cases where you want to exclude most other items.

Overrides

Suppose that you have a long global gitignore file that includes a rule to ignore backup files that your editor produces.

$ git config --list --global | grep excludes
core.excludesfile=/Users/dev/.config/git/ignore

$ cat /Users/dev/.config/git/ignore
# Ignore bak files no matter where in the tree
*.bak

Now, in one of your repositories, you decide that the information is critical and it would be useful to track and manage the backup files in this one area. Rather than re-creating a local copy of the entire global Git ignore file, you can just create the internal ignore file and have it override that one line.

$ cat /Users/dev/myrepo/.git/info/exclude
# Track *.bak files for this repository
!*.bak

Now, instances of the backup files in this repository are tracked on your system. Note that since neither the global ignore file or the one internal to the repository is committed into Git, this will not affect other users that clone the repository.

Targeted Inclusion

The use case here would revolve around wanting to exclude most items in an area, but include a small set of targeted items. One approach is to try and list everything that needs to be ignored down to a low level of detail, leaving only the small subset of items to be included out of the file.

However, a simpler approach is to list the higher-level areas in the ignore file and then create a negated pattern for each level down to the set of items that you want included. Effectively, you are cutting a hole through the hierarchy by negating the explicit subpaths to the desired set of items. I said earlier that it's not possible to negate a file if the directory above has been excluded. This trick of negating the directory paths along the way allows you to get around that limitation.

As an example, suppose you have the following structure in your local Git repository:

myrepo
│   .gitignore
│
└———subdir1
    │   filea.txt
    │   fileb.txt
    │
    └———subdir2
            file1.txt
            file2.txt

And you have the following text in your .gitignore file:

$ cat .gitignore
# ignore all of the things under this directory
subdir1/*
# except for this child directory
!subdir1/subdir2
# but ignore everything under it
subdir1/subdir2/*
# except for this file
!subdir1/subdir2/file1.txt

If you perform an add operation, because you have created a path for the one file, you'll have it and the .gitignore file staged. (The .gitignore file will be staged because it's in the current directory.)

$ git add

$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>…" to unstage)

        new file:   .gitignore
        new file:   subdir1/subdir2/file1.txt

Getting Ignore Information for Files

Like the check-attr plumbing command to get attribute information for paths, Git also provides a plumbing command to get ignore information and status for paths. The command is named check-ignore.

The format for the check-ignore command is pretty simple; there are basically two forms.

git check-ignore [options] pathname
git check-ignore [options] --stdin

For each pathname, the command displays information about whether that file matches a specification in the set of available Git ignore files that affect the repository. This command reports back on matches for both valid ignore rules and negated ignore rules for unstaged files—printing the filename if there is a match.

If you want more detailed information, you can add the --verbose option. This creates output in the following format:

<source> <COLON> <linenum> <COLON> <pattern> <HT> <pathname>

where <source> refers to the location of the Git ignore file that matched, along with the particular line number as noted in <linenum> and the <pattern> that matched. This is followed by the pathname that was provided for the command that matched.

git check-ignore --verbose *.bak
/Users/dev/.config/git/ignore:2:*.bak   *.bak

SUMMARY

In this chapter, I've covered the ideas, scope, format, and practical uses for two supporting files that you can use to tell Git how to deal with specific content. These files can exist in different parts of a Git environment—primarily stored with the files in the repository, or stored internally in the .git repository space. Instances of these two supporting files that are stored in the repository are part of the set of files that everyone gets when they clone. This then provides a consistent specification regardless of each user's environment and personal configuration because they will get it automatically as part of the clone. Instances of these two supporting files stored only within the .git directory apply only to the current user.

A Git attributes file allows you to specify different attributes that dictate how certain operations behave for files that match specified patterns or names. It is primarily used for specifying which items are binary, specifying line endings, and creating custom filters. The custom filters can be one or both of two forms: a smudge operation is a filter that takes place when a file is checked out of a repository; a clean operation is a filter that takes place when a file is checked in to Git.

A Git ignore file specifies files that Git should not track or manage. Examples might include generated files or backup files. Ignore files that are stored with the source files in the repository provide a consistent set of items to ignore for anyone who clones the repository. An exclude pattern can be negated to override a previous ignore or filter a larger set to include a more precise subset.

Example attribute and ignore files for many different types of development have been created and contributed back to public sites like GitHub. They are available to download from those sites and use as starting points.

In the next chapter, you'll look at some other useful commands for manipulating files in Git that are similar to operating system commands. You'll also explore some advanced commands that are useful for performing functions like finding out where a problem was first introduced and teaching Git how to automatically resolve complex merge scenarios.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset