By default, cp tests if its last argument is an existing directory. If this happens, cp creates a link inside that directory, with the base name of the source. That is, given the command
cp foo/bar wibble
if wibble is an existing directory then cp copies the source to wibble/bar. If wibble does not exist then cp links the source to wibble.
If you want to be sure that the copy is always wibble, then you can specify the --no-target-directory (alias -T) option. That way, if cp succeeds, you can be sure that the copy is called wibble. If wibble already existed as a directory, then cp will fail.
In tabular form:
The target is … Without -T With -T
existing directory copy in the directory error
existing file (not dir) overwrite overwrite
does not exist create create
The only difference is that with -T, in case the target is an existing directory, the command returns an error. This is useful when you expect the directory not to exist: you get an error message instead of something unpredicted happening.
The same applies to mv and ln. If the target is an existing directory, with -T, they signal an error rather than silently doing something different.
With cp, there's a different case. If you do a recursive copy and the source is a directory, then cp -T copies the content of the source into the destination, rather than copying the source itself. That is, given
$ tree source destination
source
└── foo
destination
└── bar
then
$ cp -rv source destination
`source' -> `destination/source'
`source/foo' -> `destination/source/foo'
whereas
% cp -rvT source destination
`source/foo' -> `destination/foo'