Chapter 6
Tracking Changes

In the last chapter, you learned how to get your content into Git and how to move it through the different levels (working directory, staging area, local repository). Because you can have different versions of files at the different levels in Git, you need a way to keep track of where everything is and how the versions at the different levels may differ from each other. In short, you need easy ways to keep track of all of your work that's in progress. Git has two commands that can help you with this: status and diff. Using these two commands allows users to quickly understand the state of their changes in the local environment and to ensure that the correct changes are tracked and stored in Git.

GIT STATUS

As the name implies, the git status command provides status information on changes in the local environment that have not been committed yet. Let's start with a quick look at the general form of the command.

git status [<options>…] [--] [<pathspec>…]

Like other commands, this command can take path specifications, but those are not required. The “--” is a separator used to note where options end and path specifications start. It sits in between and is not required if the specifications are unambiguous enough.

For files that are in the working directory or staging area, the status command answers three questions: whether or not a file is tracked, what is in the staging area, and whether or not a file is modified.

Is the File Tracked or Untracked? This designation refers to whether or not Git knows about the file—that is, has someone previously added this file to Git? If this file has at least been added to the staging area at some point (and not removed), then Git knows about it, and is managing a version of it, so it is tracked by Git. Otherwise, the file is untracked—Git doesn't know about it and isn't managing any versions of it.

An example of an untracked file would be a new file that hasn't been added to Git. Files in the .gitignore file do not count as untracked because they are ignored by Git.

Git can report the status of untracked files in a couple of different ways, depending on whether or not something is staged. This brings up the next question.

What Is in the Staging Area? For this question, you are interested in whether there is anything that has been staged (put into the staging area via git add), and if so, which versions of which files. For these criteria, you'll need to understand some of the more specific terminology that Git uses to describe the status of files. I'll cover that in the example workflow shortly.

Is a Particular File Modified or Unmodified? For this designation, I am talking about whether a file in the working area is the same as, or different from, the latest version in Git. Think of modified here as simply meaning different. If it is the same, then it is not different, or un-modified. If it is different, that implies that the version in the working directory has been changed (modified) since it was last updated in Git.

Workflow Example with Status

To better understand file status, let's look at an example of staging and committing multiple versions of a file with its status at each step. Figure 6.1 shows a typical example of the local Git environment with the working directory, staging area, and local repository levels.

A process diagram of a typical example of the local Git environment with the working directory, staging area, and local repository levels.

Figure 6.1 Empty local environment levels.

Initially, when you start with an empty directory (for example, just after a git init command), issuing a git status command will provide a message like this:

$ git status
On branch master
Initial commit
nothing to commit (create/copy files and use "git add" to track)

This is telling you that you are on the master branch (the default branch in Git) and there are no changes (files) eligible to commit. Let's execute a few further steps to migrate content through the promotion model.

Step 1: You create a file in the working directory with the command echo new > file1.txt. But you don't stage it yet. Your local environment looks like Figure 6.2. The “(1)a” notation on the file indicates this is your first step in the workflow and the file is at version a.

A process diagram for file created in working directory of the local Git environment with the notation (1)a in the working directory.

Figure 6.2 File created in working directory

Let's look at the three questions to determine how Git sees the status:

  1. 1. Is Git aware of the file? No, you haven't done anything to make Git aware of it. (That is, you haven't done a git add command to tell Git about the file.) Because Git doesn't know about the file yet, it is untracked.
  2. 2. What's in the staging area? Nothing yet—you haven't done a git add command.
  3. 3. What is the relationship of the version in the working directory to the latest version in Git? Currently, there isn't a version in Git. (The only version exists in the working directory.) So, this one doesn't really apply.

Issuing the git status command, you'll see something like this:

$ git status
On branch master
Initial commit
Untracked files:
  (use "git add <file>…" to include in what will be committed)

     file1.txt
     
nothing added to commit but untracked files present (use "git add" to track)

Note that it tells you there's nothing added to commit, meaning you haven't staged anything so there's nothing to commit (promote) to the local repository yet. It also notes the new untracked file that Git doesn't know about yet.

Step 2: If you now stage the file with the command git add, your local environment looks like Figure 6.3.

A process diagram for version a of the file staged in the local Git environment with the notation (2)a in the staging area.

Figure 6.3 Version a of the file is staged.

Looking at the three questions:

  1. 1. Is Git aware of the file? Yes, because you've done a command that tells Git about it—that is, the git add command. The file is now considered tracked by Git.
  2. 2. What's in the staging area? Revision a. Notice that it is perfectly valid in Git to have the same revision of a file existing in multiple levels.
  3. 3. What is the relationship of the version in the working directory to the latest version in Git? The version in Git (in the staging area) is the same as the version in the working directory, so it is not different (not modified). Thus, it is unmodified.

When you run the status command git status, Git will list the new file in the staging area.

On branch master
Initial commit
Changes to be committed:
  (use "git rm --cached <file>…" to unstage)
  
        new file:   file1.txt

Notice the terminology here of Changes to be committed. Whenever you see this phrase, Git is telling you about things in the staging area. You can read the to be committed part as indicating the next level in your promotion model. There are changes where the next level is committing into the local repository.

Step 3: An update is made to the version of the file in the working directory using the command echo update ≫ file1.txt. The result is shown in Figure 6.4.

A process diagram for update made to working directory version in the local Git environment with the notation (3)b in the working directory.

Figure 6.4 Update made to working directory version

Looking at the questions:

  1. 1. Is Git aware of the file? Yes, nothing has changed for this one. You added it in Git and haven't removed it, so it is still tracked.
  2. 2. What's in the staging area? Revision a. Nothing's changed—no additional add or commit commands have been done.
  3. 3. What is the relationship of the version in the working directory to the latest version in Git? The version in Git (in the staging area) is still the previous version (a). The version in the working directory has been updated to a new version (b). Thus, the file is different, so it is modified.

The output from running git status now looks like this:

On branch master
Initial commit

Changes to be committed:
  (use "git rm --cached <file>…" to unstage)
        new file:   file1.txt
	
Changes not staged for commit:
  (use "git add <file>…" to update what will be committed)
  (use "git checkout -- <file>…" to discard changes in working directory)
        modified:   file1.txt

This is an interesting one because it shows the filename listed twice. This can be confusing, especially if you're new to Git. The simple explanation here is that you have two different versions of the file in two different levels of Git. Version a is still in the staging area, and the new version (b) is in the working directory. So, Git is reporting status on the different versions in the two levels.

Notice Git's terminology here for the two versions. The one in the staging area is listed as Changes to be committed. Again, you can think of this as indicating the next level; the next step in promoting this change is to commit it into the local repository. The version in the working directory is listed as Changes not staged for commit. The next step in promoting this one would be to stage it, as it is not staged yet.

Step 4: Stage the new version from the working directory into the staging area. This is done with git add. The results are shown in Figure 6.5.

A process diagram for version b staged in the local Git environment with the notation (4)b in the staging area.

Figure 6.5 Version b staged

  1. 1. Is Git aware of the file? Yes, nothing has changed for this one.
  2. 2. What's in the staging area? Version b. Notice that version b overwrote version a in the staging area. There can only be one version of a file in the staging area.
  3. 3. What is the relationship of the version in the working directory to the latest version in Git? The version in Git (in the staging area) is b—the same as the version in the working directory. So, you're back to a status of unmodified (not different) for this part.

After running git status, the output looks the same as for Step 2.

On branch master
Initial commit

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

Step 5: If you now commit the file with git commit -m "new file", you'll see the output that I described in my discussion on commits in Chapter 5.

 [master (root-commit) 8f8da3e] new file
 1 file changed, 1 insertion(+)
 create mode 100644 file1.txt

Your local environment looks like Figure 6.6.

A process diagram for file committed in the local Git environment with Commit in between the staging area and local repository.

Figure 6.6 The file is committed.

The answers to the questions can be fairly simple at this point.

  1. 1. Is Git aware of the file? Yes.
  2. 2. What's in the staging area? Nothing; you've committed what was in the staging area.
  3. 3. What is the relationship of the version in the working directory to the latest version in Git? The version in Git (in the local repository now) is b—the same as the version in the working directory. So, it's unmodified.

Following that with a git status command, Git will tell you that there is nothing to commit, working directory clean. This is another bit of terminology that Git uses. It means that Git has the latest version of everything in its local repository that's eligible to be committed from the working directory. Basically, the local repository and working directory are in sync. There are no new changes or untracked files in the working directory that are eligible to update in Git.

Status Command Short Form

Up until now, you've been running the git status command in its default form. This form displays a verbose listing of status information. As you become familiar with using the status command, you may want to see more concise output. For this, you can use git status -s, which is the short form of the command.

The -s option causes Git to display a simpler format: one line of status per file. It displays a one- or two-character code preceding the filename to indicate the status. In most cases, you can think of the first character (from left to right) as indicating the status of the file in the staging area, and the second character as representing the status of the file in the working directory if different.

Table 6.1 lists the common status values for commands you have been working with and the representative codes.

Table 6.1 Git Status Codes for Short Options

Status Column 1 Code Column 2 Code
Empty working directory blank blank
File staged and unmodified A blank
File staged and modified A M
Untracked file ? ?

Key: A = added, M = modified, ? = untracked, blank = unmodified

So, if you map the short status to the different steps you did earlier, it would look something like this. Starting out with an empty working directory and no files in Git, git status -s would not have anything to show.

To mirror what you did before , you can create a file using the command echo version1 > file1.txt. After this, git status -s shows the untracked file with the output "?? file1.txt".

In step 2, you staged the file via git add file1.txt. The status shows the file as added (another word for staged) via the A character in the first column.

$ git status -s
A  file1.txt

In step 3, you updated the version of the file in the working directory (echo version2 > file1.txt). Status now has the two versions to report on: the staged version (indicated by the A) and the modified version in the working directory (indicated by the M).

$ git status -s
AM file1.txt

Step 4 used git add . to stage the updated version over the top of the previously staged version. So, the status is back to just having one version staged (the A), and the version in the working directory is the same as that version (indicated by the blank second column).

$ git status -s
A  file1.txt

Finally, step 5 brought you to the point of committing the file (git commit -m "first file"). Because Git has the same versions of everything that exists in the working directory—that is, Working directory clean—there is no status information to report on and thus no output from the short form of the command.

$ git status –s

Git Diff

In addition to git status, the other operation that allows you to have a full picture across your local environment is git diff. As the name implies, this command shows differences between content at the different levels in your local environment.

Let's first look at the general forms of the command:

git diff [options] [<commit>] [--] [<path>…]
git diff [options] --cached [<commit>] [--] [<path>…]
git diff [options] <commit> <commit> [--] [<path>…]
git diff [options] [--no-index] [--] <path> <path>

You'll learn about several of these forms as you go along. This command can take commits, paths, or both as arguments. If a commit is specified, that refers to an entire snapshot, and can be further qualified with paths to particular files in the snapshot if needed.

To fully understand this command, you first need to understand a couple of symbolic names that Git uses.

Important Symbolic Names in Git

Git uses symbolic names to refer to various items or commits. The most common one is HEAD. HEAD generally refers to the latest commit on the current branch, and if you think of it that way, you'll be well served. It's a shortcut for the most current thing in the repository in the branch you're working in now.

HEAD is actually a pointer or reference to a SHA1—the SHA1 for the latest commit on the current branch. HEAD is used extensively when working with Git, especially as a point to reference other commits from.

I previously discussed the staging area. Cache and index are two other terms used to reference this area; both are legacy terms in Git, and have now been replaced by the staging area terminology. So, when working with the staging area and supplying options, you may be expected to supply one of these legacy terms as the option. For all intents and purposes, you can think of staging area, cache, and index as referring to the same level in Git.

How to Think about Git's Approach to Diffing

From time to time in this book, I present concepts in a visual way to explain them, even though that may not be exactly how things work internally. One way to think about how Git compares things when performing a basic diff operation is by thinking about going up the promotion model to find content to diff against. Let's walk through a set of steps, as you did with the status examples, to see how this works.

In Figure 6.7, you pick up where you left off in the status example. You have a file at version a in the working directory that has been staged and committed so that you also have revision a in the repository. (Again, letters refer to versions as you go through the workflow.)

A process diagram for starting point for diffing in the local Git environment with the notation a in the working directory and local repository.

Figure 6.7 Starting point for diffing—working directory clean

Step 1: First, let's do a diff between the working directory version and the current version in Git. In the working directory, you issue a git diff command. As a convenient representation, you can think of the command as starting in the working directory, and looking up to the next level (the staging area) to see if there is anything to compare against there (Figure 6.8). In this case there isn't, so it continues to look up to the next level, and finds the revision in the local repository (Figure 6.9). Comparing the version in the working directory against the one in the local repository, Git finds that they are the same.

A process diagram for workflow of git diff between working directory and Git (checking the staging area).

Figure 6.8 Workflow of git diff between working directory and Git (checking the staging area)

A process diagram for workflow of git diff between working directory and Git (checking the local repository).

Figure 6.9 Workflow of git diff between working directory and Git (checking the local repository)

Command-line Git is less than user-friendly in its output when the two things it's comparing have no differences. It just returns nothing—no output messages. So, in this case, the output from running git diff on the command line is nothing—indicating no differences.

Suppose you now update the local file's version to b (Figure 6.10) and run the diff. Afterward, it continues to search up the chain until it finds the one in the local repository (Figure 6.12).

A process diagram for local version updated to b in the working directory of the local Git environment.

Figure 6.10 Local version updated to b

You can think of the workflow occurring in the same way again: Git starts at the working directory, searches up the chain for a version, and doesn't find one in the staging area (Figure 6.11).

A process diagram for diff between modified local version and Git.

Figure 6.11 Diff between modified local version and Git

A process diagram for diffing further up the chain in the local Git environment.

Figure 6.12 Diffing further up the chain

Just as before, finding nothing in the staging area, you can think of Git as continuing up to the next level, where it finds the version in the local repository to diff against.

This time they are different, so by default, git diff shows the differences in patch format. Patch format means displaying the lines that are different, added, or deleted between the two versions. The output from the diff looks like the following:

diff --git a/file1.txt b/file1.txt
index df7af2c..126b36c 100644
--- a/file1.txt
+++ b/file1.txt
@@ -1 +1,2 @@
 first line
+second line

Now, I'll go ahead and stage version b so that it is in both the staging area and the working directory. Version a is still the latest in the local repository. This is where things get interesting.

If you simply do your normal git diff command, then Git starts at the working directory and searches up the chain to find the version in the staging area (Figure 6.13).

Those two versions are the same, so a git diff again shows no output (no differences).

Starting at the Staging Area

The other option you may want to use when you have content in the staging area is to diff the staging area against the local repository. To do this, you add either the --cached or the --staged option to the command. (Recall that I said you can think of the cache, index, and staging area as the same things for your purposes.)

Adding the --cached or --staged option tells Git to start at the staging area. From there, going up the promotion levels, it compares against the local repository version. Figure 6.14 illustrates this concept.

A process diagram for diffing from the working directory with a version in the staging area in the local Git environment.

Figure 6.13 Diffing from the working directory with a version in the staging area

Image described by caption and surrounding text.

Figure 6.14 Diffing starting at the staging area

From the command line, you execute git diff --staged. The output looks like this:

diff --git a/file1.txt b/file1.txt
index df7af2c..126b36c 100644
--- a/file1.txt
+++ b/file1.txt
@@ -1 +1,2 @@
 version2
+second line

Because the version in the staging area is different from the version in the local repository (a is different from b), you see the differences.

Diffing against a Specific Version (SHA1)

One of the other useful functions of the diff command is being able to specify a revision to compare against on the diff command. The syntax is git diff <identifier>.

Normally the identifier will be a SHA1 (because each commit has its own unique SHA1 value) or a reference to it.

So how might this be useful? Consider a case where you might want to diff directly against the version in the local repository, bypassing the staging area. In that case, you want to diff against the latest revision in the repository. How do you specify that? Recall that I said HEAD is a pointer or reference to the latest commit (SHA1) on the current branch. So, you can use that symbolic name in your command here. If you say git diff HEAD then instead of going up to the staging area to check for differences, Git will bypass the staging area and compare the working directory against what's pointed to by HEAD. This would look just like Figure 6.15.

A process diagram of diffing directly against a SHA1 (HEAD) in the local Git environment.

Figure 6.15 Diffing directly against a SHA1 (HEAD)

Again, the results would just show the differences.

$ git diff HEAD
diff --git a/file1.txt b/file1.txt
index df7af2c..126b36c 100644
--- a/file1.txt
+++ b/file1.txt
@@ -1 +1,2 @@
 version2
+second line

Diff Names Only

Like the status command, there are shorter versions of the diff output. If you only want to see the names of the files that are different, you can use the --name-only option. Running git diff --name-only produces the output "file1.txt".

If you want to get a quick summary of which files are different and their shorthand status information, you can use the --name-status option. Running git diff --name-status produces the output "M file1.txt"

Here the M means that the local version of the file has been modified—just as with the shorthand version of the status command that I covered earlier in the chapter.

Word-diff

Another option that may be useful is turning on differences at the granularity of words. You can do this with the command git diff --word-diff.

This will tell the diff to show differences at the granularity of words where words are tokenized by whitespace. Here's an example of output from the command.

diff --git a/file1.txt b/file1.txt
index 8e2235c..7823155 100644
--- a/file1.txt
+++ b/file1.txt
@@ -1 +1 @@
The [-quick brown-]{+hasty+} fox [-jumped-]{+leapt+} over the [-lazy-]{+peacefully resting+} brown [-dog.-]{+canine.+}

The items in “[- -]” indicate things that were removed in the latest version, and the items in “{+ +}” indicate things that were added. Additional options are available for changing the way words are represented and tokenized (parsed) if desired or needed. See the help for git diff for more information on these options.

Ignoring Non-critical Changes

Obviously, the definition of what is non-critical or critical will vary from user to user and case to case. However, there is a set of common areas that can be useful to ignore when doing diffs.

Whitespace Changes

Git has a number of options to help with ignoring whitespace changes. The options are as follows:

-w | --ignore-all-space. This tells Git to ignore all whitespace changes when comparing lines.

--ignore-space-at-eol. This tells Git to ignore whitespace changes at the ends of lines.

--ignore-blank-lines. This tells Git to ignore changes in lines that are all blank.

-b | --ignore-space-change. This tells Git to ignore changes at the ends of lines and treat corresponding areas of whitespace changes as equivalent, regardless of whether they have the same amount of whitespace.

Let's look at a couple of examples. You first create a file using a simple echo command to dump the string abcdef into a file: echo "abcdef" > file1.txt. Then you stage it to get it under Git's control: git add .

Next, you update the file locally to have some whitespace changes (in the middle and at the end): echo "abc def " > file1.txt

A regular git diff shows differences as follows:

diff --git a/file1.txt b/file1.txt
index 0373d93..b6cdfe4 100644
--- a/file1.txt
+++ b/file1.txt
@@ -1 +1 @@
-abcdef
+abc def

Adding the option to ignore all whitespace changes (git diff -w) shows no differences because the only changes to the content were the addition of whitespace.

Telling Git to ignore only the whitespace at the end of each line shows the diff with the whitespace changes in the middle: git diff --ignore-space-at-eol

diff --git a/file1.txt b/file1.txt
index 0373d93..b6cdfe4 100644
--- a/file1.txt
+++ b/file1.txt
@@ -1 +1 @@
-abcdef
+abc def

Next, I update the local copy of the file in the working directory to only have whitespace changes at the end of each line: echo "abcdef " > file1.txt. If I diff it again with the same option (git diff --ignore-space-at-eol), it shows no differences—as expected.

Now, suppose you stage the current change and then update the file locally to a new version that only has changes in the amount of whitespace between the two versions. This can be done with a git add . followed by something like echo "abc def" > file1.txt. Then, a git diff between the two versions shows the expected differences.

diff --git a/file1.txt b/file1.txt
index d4bbed0..f9686f5 100644
--- a/file1.txt
+++ b/file1.txt
@@ -1 +1 @@
-abc  def
+abc def

Adding the option to ignore changes in whitespace and the amount of whitespace (git diff -b) shows no difference, also as expected.

Filemode Changes

Another area where Git can sometimes identify files as changed that you may want to ignore is the executable bit of the filemode (that is, chmod+x). You can work around this with git diff by setting the configuration value core.filemode to false.

$ git config --global core.filemode false

Or, if you need to just apply this command once for a diff, you can use the one-shot configuration setting I talked about in Chapter 4.

$ git -c core.filemode=false git diff

Diffing Two Commits

The diff command can also be used to diff two different commits in the local repository. The commits could be in the same branch, or, as you'll see later, in separate branches. The output you'll see depends on the order in which you supply the SHA1 values representing the commits.

Consider the following example where you make three sets of changes for two files, doing a commit after each change.

  1. First commit
    $ echo "line 1" > file1.txt
    $ echo "first line" > file2.txt
    $ git add .
    $ git commit -m "change 1"
    [master (root-commit) c25a62d] change 1
     2 files changed, 2 insertions(+)
     create mode 100644 file1.txt
     create mode 100644 file2.txt
  2. Second commit
    $ echo "line 2" >> file1.txt
    $ echo "second line" >> file2.txt
    $ git commit -am "change 2"
    [master 965b004] change 2
     2 files changed, 2 insertions(+)
  3. Third commit
    $ echo "line 3" >> file1.txt
    $ echo "third line" >> file2.txt
    $ git commit -am "change 3"
    [master fc5c99f] change 3
     2 files changed, 2 insertions(+)

Noting the SHA1s of the first and last commits, you can now do some interesting diffing.

$ git diff c25a62d fc5c99f
diff --git a/file1.txt b/file1.txt
index 89b24ec..a92d664 100644
--- a/file1.txt
+++ b/file1.txt
@@ -1 +1,3 @@
 line 1
+line 2
+line 3
diff --git a/file2.txt b/file2.txt
index 08fe272..20aeba2 100644
--- a/file2.txt
+++ b/file2.txt
@@ -1 +1,3 @@
 first line
+second line
+third line

Here you see the differences for the two files between the first and last commits. Notice the lines that were added in the second and third commits.

Switching the order that you specify the commits gives the opposite representation: showing the lines that would be deleted to go from the most recent version to the first version.

$ git diff fc5c99f c25a62d
diff --git a/file1.txt b/file1.txt
index a92d664..89b24ec 100644
--- a/file1.txt
+++ b/file1.txt
@@ -1,3 +1 @@
 line 1
-line 2
-line 3
diff --git a/file2.txt b/file2.txt
index 20aeba2..08fe272 100644
--- a/file2.txt
+++ b/file2.txt
@@ -1,3 +1 @@
 first line
-second line
-third line

Note that you could also filter this by an individual file.

$ git diff fc5c99f c25a62d file2.txt
diff --git a/file2.txt b/file2.txt
index 20aeba2..08fe272 100644
--- a/file2.txt
+++ b/file2.txt
@@ -1,3 +1 @@
 first line
-second line
-third line

You could have also included the separator “--” between the second SHA1 and the filename, as follows:

$ git diff c25a62d fc5c99f -- file2.txt

However, that is not necessary in this case because the form of the filename is different enough from a SHA1 value to not be confused for a revision.

Visual Diffing

As you have seen, the default presentation for the diff command is showing the changes in a standard patch format. While you are focusing primarily on the command line usage, there are times when a visual interface adds significant value or convenience. One of these cases is diffing.

Git includes a special command for working visually with differences: difftool. This command is actually an extended frontend for the git diff command and it can accept all the options and arguments that diff can accept.

To invoke the diffing tool, you run the command git difftool.

The idea is that you have one or more diffing tools installed, configured, and available for Git to use. (More about how that works in a moment.) Then, you use the git difftool command or a configuration value to select the one you want to use. The difftool command then starts up the desired tool with the appropriate arguments.

Figures 6.16 through 19 show some screenshots of several commonly used visual diffing applications (and applications that Git understands out of the box if they are installed and in the path).

Image described by caption and surrounding text.

Figure 6.16 Vimdiff

Image described by caption and surrounding text.

Figure 6.17 WinMerge

Image described by caption and surrounding text.

Figure 6.18 Meld

Image described by caption and surrounding text.

Figure 6.19 KDiff3

Selecting a Diffing Tool

From a list of installed, configured, and available tools, a particular tool can be selected in several different ways. However, to the difftool command, you always specify just the simple name of the tool (for example, kdiff3, vimdiff, or meld).

If no default tool has been specified, then Git will attempt to use a sensible default (usually something like vimdiff on Linux systems). One way to specify a default is to configure a particular tool via the diff.tool configuration value, as follows:

$ git config --global diff.tool vimdiff

Once this setting is configured, running git difftool will start that selected tool: vimdiff.

By default, Git prompts for confirmation to run the difftool for each file to be diffed. An example prompt looks like “Viewing (1/2): ‘file2.txt’ Launch ‘meld’ [Y/n]: Y”. This prompt can be suppressed either by supplying a no-prompt option when running difftool, as in git difftool --no-prompt or by setting the configuration value, difftool.prompt, to false.

$ git config --global difftool.prompt false

Another way to select a particular tool is to specify the name of the desired tool via the -t option at the time you run difftool, as in git difftool -t meld.

Making Diff Tools Available to Git

Git comes preconfigured to be able to work with a number of different tools for diffing. To see a list of these tools, you can run the command git difftool --tool-help.

Note that this does not mean that all of these tools are installed (or even if installed, that they can be used—they might not be in the path). What this means is that Git understands how to use these tools to do diffing without additional configuration if the tools are available on the system. The tool-help option tells you which tools are available to use (under may be set to the following) and which are not (under The following tools are valid, but not currently available).

To make one of the tools available that is marked as not currently available, you can install the application and make sure it is in the path. Once this is done, if it is a tool that Git knows about, it will show up in the available section.

If a tool is not available in the path, then you can set a configuration value named difftool.<tool>.path (where <tool> is the name of the application) to specify the location where Git can find it.

For example, Git knows how to work with an application named Meld for diffing when it can find it on the system. Suppose you install the Meld application on Windows in c:meld (instead of the default Program Files location that would be in the path). To tell Git where the Meld application can be found, you would set the path value for it as follows:

(On a Windows command prompt)

$ git config --global difftool.meld.path c:meldMeld.exe

(On a Bash shell)

$ git config --global difftool.meld.path /c/meld/Meld.exe

There is also a way to add an application that Git does not already know about. To do this, you specify a custom command to run the application in the configuration value for difftool.<tool>.cmd. Here again, <tool> is the name of the application. Git passes several variables to use with the command string. $LOCAL is set to the name of the temporary file containing the contents of the diff before, and $REMOTE is set to the name of the temporary file containing the contents of the diff afterwards.

Other Diff Tricks

In addition to the forms of the git diff command that you have already looked at, there are a couple of others that might be useful.

Suppose that you have multiple files committed into your local repository and several of them are different. A standard invocation git diff may show the following:

diff --git i/file2.txt w/file2.txt
index ef49dd8..4ea5e4d 100644
--- i/file2.txt
+++ w/file2.txt
@@ -1 +1 @@
-more
+update
diff --git i/file3.txt w/file3.txt
index ef49dd8..4ea5e4d 100644
--- i/file3.txt
+++ w/file3.txt
@@ -1 +1 @@
-more
+update

An invocation of the diff command with the two files (git diff file2.txt file3.txt) would show the same output, assuming that file2.txt and file3.txt are the only ones that are different.

Now consider the case where you pass file1.txt and file2.txt to the command: file1.txt is not different, so there is no output for it.

$ git diff file1.txt file2.txt
diff --git i/file2.txt w/file2.txt
index ef49dd8..4ea5e4d 100644
--- i/file2.txt
+++ w/file2.txt
@@ -1 +1 @@
-more
+update

Git also supports specifying a qualified version of the file to compare against. This is similar to comparing two versions in Git. For example, to compare the version of a specific file (say, file2.txt) in the HEAD revision (the local repository's current revision) against the version in the working directory, you can use this syntax:

$ git diff HEAD:file2.txt file2.txt
diff --git o/file2.txt w/file2.txt
index ef49dd8..4ea5e4d 100644
--- o/file2.txt
+++ w/file2.txt
@@ -1 +1 @@
-more
+update

Note the HEAD: qualifier in front of the filename. Also, note the mnemonic prefix here of o for object, because you're referring to a specific object in the repository.

Even more interesting, though, is that you can use this same form to compare a qualified version of one file in Git against a completely different file locally. Here's an example:

$ git diff HEAD:file1.txt file3.txt
diff --git o/file3.txt w/file3.txt
index 13fd43b..4ea5e4d 100644
--- o/file3.txt
+++ w/file3.txt
@@ -1,3 +1 @@
-version2
-second line
-more
+update

Notice that your command actually invoked the diff against the version of file1.txt from HEAD and a completely different file (file3.txt) from the working directory. This might be useful in certain cases.

SUMMARY

In this chapter, I covered how to use two Git commands, git status and git diff, to gain a complete picture of the different content at different levels in your local Git environment. The staging area adds an extra level here that has to be taken into account. I also covered the terminology and checks that Git uses when ascertaining and reporting status. I then introduced a couple of special symbolic names or references that Git uses. Putting this all together allows you to gain a comprehensive understanding of how and where content is positioned in Git.

About Connected Lab 3: Tracking Content through the File Status Life Cycle

This Connected Lab will give you hands-on practice with the commands you've explored in this chapter, building on what you've already learned. You'll get a chance to use the status and diff commands on a project and become more familiar with Git's responses and behavior. After that, in Chapter 7, you'll move on to working with changes in Git over time.

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

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