Merge commit 'mingw/master' into devel

Conflicts:
	Makefile
	daemon.c
	exec_cmd.c
	exec_cmd.h
	fast-import.c
	git.c
	hash-object.c
	http-push.c
	imap-send.c
	index-pack.c
	merge-index.c
	merge-tree.c
	mktag.c
	mktree.c
	pack-redundant.c
	patch-id.c
	unpack-file.c
	update-server-info.c
	upload-pack.c
	var.c
This commit is contained in:
Steffen Prohaska
2009-02-06 23:56:24 +01:00
206 changed files with 5794 additions and 1333 deletions

1
.gitignore vendored
View File

@@ -144,6 +144,7 @@ git-core-*/?*
gitk-wish
gitweb/gitweb.cgi
test-chmtime
test-ctype
test-date
test-delta
test-dump-cache-tree

View File

@@ -21,8 +21,13 @@ code. For git in general, three rough rules are:
As for more concrete guidelines, just imitate the existing code
(this is a good guideline, no matter which project you are
contributing to). But if you must have a list of rules,
here they are.
contributing to). It is always preferable to match the _local_
convention. New code added to git suite is expected to match
the overall style of existing code. Modifications to existing
code is expected to match the style the surrounding code already
uses (even if it doesn't match the overall style of existing code).
But if you must have a list of rules, here they are.
For shell scripts specifically (not exhaustive):

View File

@@ -32,6 +32,7 @@ DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
prefix?=$(HOME)
bindir?=$(prefix)/bin
htmldir?=$(prefix)/share/doc/git-doc
pdfdir?=$(prefix)/share/doc/git-doc
mandir?=$(prefix)/share/man
man1dir=$(mandir)/man1
man5dir=$(mandir)/man5
@@ -50,6 +51,7 @@ infodir?=$(prefix)/share/info
MAKEINFO=makeinfo
INSTALL_INFO=install-info
DOCBOOK2X_TEXI=docbook2x-texi
DBLATEX=dblatex
ifndef PERL_PATH
PERL_PATH = /usr/bin/perl
endif
@@ -87,6 +89,8 @@ man7: $(DOC_MAN7)
info: git.info gitman.info
pdf: user-manual.pdf
install: install-man
install-man: man
@@ -107,6 +111,10 @@ install-info: info
echo "No directory found in $(DESTDIR)$(infodir)" >&2 ; \
fi
install-pdf: pdf
$(INSTALL) -d -m 755 $(DESTDIR)$(pdfdir)
$(INSTALL) -m 644 user-manual.pdf $(DESTDIR)$(pdfdir)
install-html: html
sh ./install-webdoc.sh $(DESTDIR)$(htmldir)
@@ -187,17 +195,23 @@ git.info: user-manual.texi
user-manual.texi: user-manual.xml
$(RM) $@+ $@
$(DOCBOOK2X_TEXI) user-manual.xml --to-stdout | $(PERL_PATH) fix-texi.perl >$@+
$(DOCBOOK2X_TEXI) user-manual.xml --encoding=UTF-8 --to-stdout | \
$(PERL_PATH) fix-texi.perl >$@+
mv $@+ $@
user-manual.pdf: user-manual.xml
$(RM) $@+ $@
$(DBLATEX) -o $@+ -p /etc/asciidoc/dblatex/asciidoc-dblatex.xsl -s /etc/asciidoc/dblatex/asciidoc-dblatex.sty $<
mv $@+ $@
gitman.texi: $(MAN_XML) cat-texi.perl
$(RM) $@+ $@
($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --to-stdout $(xml);)) | \
$(PERL_PATH) cat-texi.perl $@ >$@+
($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --encoding=UTF-8 \
--to-stdout $(xml);)) | $(PERL_PATH) cat-texi.perl $@ >$@+
mv $@+ $@
gitman.info: gitman.texi
$(MAKEINFO) --no-split $*.texi
$(MAKEINFO) --no-split --no-validate $*.texi
$(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml
$(RM) $@+ $@

View File

@@ -0,0 +1,59 @@
GIT v1.6.1.1 Release Notes
==========================
Fixes since v1.6.1
------------------
* "git add frotz/nitfol" when "frotz" is a submodule should have errored
out, but it didn't.
* "git apply" took file modes from the patch text and updated the mode
bits of the target tree even when the patch was not about mode changes.
* "git bisect view" on Cygwin did not launch gitk
* "git checkout $tree" did not trigger an error.
* "git commit" tried to remove COMMIT_EDITMSG from the work tree by mistake.
* "git describe --all" complained when a commit is described with a tag,
which was nonsense.
* "git diff --no-index --" did not trigger no-index (aka "use git-diff as
a replacement of diff on untracked files") behaviour.
* "git format-patch -1 HEAD" on a root commit failed to produce patch
text.
* "git fsck branch" did not work as advertised; instead it behaved the same
way as "git fsck".
* "git log --pretty=format:%s" did not handle a multi-line subject the
same way as built-in log listers (i.e. shortlog, --pretty=oneline, etc.)
* "git daemon", and "git merge-file" are more careful when freopen fails
and barf, instead of going on and writing to unopened filehandle.
* "git http-push" did not like some RFC 4918 compliant DAV server
responses.
* "git merge -s recursive" mistakenly overwritten an untracked file in the
work tree upon delete/modify conflict.
* "git merge -s recursive" didn't leave the index unmerged for entries with
rename/delete conflictd.
* "git merge -s recursive" clobbered untracked files in the work tree.
* "git mv -k" with more than one errorneous paths misbehaved.
* "git read-tree -m -u" hence branch switching incorrectly lost a
subdirectory in rare cases.
* "git rebase -i" issued an unnecessary error message upon a user error of
marking the first commit to be "squash"ed.
* "git shortlog" did not format a commit message with multi-line
subject correctly.
Many documentation updates.

View File

@@ -0,0 +1,65 @@
GIT v1.6.2 Release Notes
========================
Updates since v1.6.1
--------------------
(subsystems)
(portability)
(performance)
* pack-objects autodetects the number of CPUs available and uses threaded
version.
(usability, bells and whistles)
* "git-add -p" learned 'g'oto action to jump directly to a hunk.
* git-cherry defaults to HEAD when the <upstream> argument is not given.
* git-cvsserver can be told not to add extra "via git-CVS emulator" to the
commit log message it serves via gitcvs.commitmsgannotation configuration.
* git-diff learned a new option --inter-hunk-context to coalesce close
hunks together and show context between them.
* git-filter-branch learned --prune-empty option that discards commits
that do not change the contents.
* git-ls-tree learned --full-tree option that shows the path in full
regardless of where in the work tree hierarchy the command was started.
* git-mergetool learned -y(--no-prompt) option to disable prompting.
* "git-reset --merge" is a new mode that works similar to the way
"git checkout" switches branches, taking the local changes while
switching to another commit.
(internal)
Fixes since v1.6.1
------------------
All of the fixes in v1.6.1.X maintenance series are included in this
release, unless otherwise noted.
* "git-add sub/file" when sub is a submodule incorrectly added the path to
the superproject.
* git-bundle did not exclude annotated tags even when a range given from the
command line wanted to.
* git-grep did not work correctly for index entries with assume-unchanged bit.
* branch switching and merges had a silly bug that did not validate
the correct directory when making sure an existing subdirectory is
clean.
--
exec >/var/tmp/1
O=v1.6.1-134-ge98c6a1
echo O=$(git describe master)
git shortlog --no-merges $O..master ^maint

View File

@@ -18,8 +18,12 @@ close TMP;
printf '\input texinfo
@setfilename gitman.info
@documentencoding us-ascii
@node Top,,%s
@documentencoding UTF-8
@dircategory Development
@direntry
* Git Man Pages: (gitman). Manual pages for Git revision control system
@end direntry
@node Top,,, (dir)
@top Git Manual Pages
@documentlanguage en
@menu

View File

@@ -635,10 +635,16 @@ diff.renames::
will enable basic rename detection. If set to "copies" or
"copy", it will detect copies, as well.
diff.suppress-blank-empty::
diff.suppressBlankEmpty::
A boolean to inhibit the standard behavior of printing a space
before each empty output line. Defaults to false.
diff.wordRegex::
A POSIX Extended Regular Expression used to determine what is a "word"
when performing word-by-word difference calculations. Character
sequences that match the regular expression are "words", all other
characters are *ignorable* whitespace.
fetch.unpackLimit::
If the number of objects fetched over the git native
transfer is below this
@@ -702,7 +708,9 @@ gc.packrefs::
gc.pruneexpire::
When 'git-gc' is run, it will call 'prune --expire 2.weeks.ago'.
Override the grace period with this config variable.
Override the grace period with this config variable. The value
"now" may be used to disable this grace period and always prune
unreachable objects immediately.
gc.reflogexpire::
'git-reflog expire' removes reflog entries older than
@@ -723,6 +731,10 @@ gc.rerereunresolved::
kept for this many days when 'git-rerere gc' is run.
The default is 15 days. See linkgit:git-rerere[1].
gitcvs.commitmsgannotation::
Append this string to each commit message. Set to empty string
to disable this feature. Defaults to "via git-CVS emulator".
gitcvs.enabled::
Whether the CVS server interface is enabled for this repository.
See linkgit:git-cvsserver[1].
@@ -1044,6 +1056,16 @@ mergetool.keepBackup::
is set to `false` then this file is not preserved. Defaults to
`true` (i.e. keep the backup files).
mergetool.keepTemporaries::
When invoking a custom merge tool, git uses a set of temporary
files to pass to the tool. If the tool returns an error and this
variable is set to `true`, then these temporary files will be
preserved, otherwise they will be removed after the tool has
exited. Defaults to `false`.
mergetool.prompt::
Prompt before each invocation of the merge resolution program.
pack.window::
The size of the window used by linkgit:git-pack-objects[1] when no
window size is given on the command line. Defaults to 10.

View File

@@ -19,16 +19,12 @@ endif::git-format-patch[]
ifndef::git-format-patch[]
-p::
-u::
Generate patch (see section on generating patches).
{git-diff? This is the default.}
endif::git-format-patch[]
-u::
Synonym for "-p".
-U<n>::
Shorthand for "--unified=<n>".
--unified=<n>::
Generate diffs with <n> lines of context instead of
the usual three. Implies "-p".
@@ -40,6 +36,9 @@ endif::git-format-patch[]
--patch-with-raw::
Synonym for "-p --raw".
--patience::
Generate a diff using the "patience diff" algorithm.
--stat[=width[,name-width]]::
Generate a diffstat. You can override the default
output width for 80-column terminal by "--stat=width".
@@ -95,8 +94,22 @@ endif::git-format-patch[]
Turn off colored diff, even when the configuration file
gives the default to color output.
--color-words::
Show colored word diff, i.e. color words which have changed.
--color-words[=<regex>]::
Show colored word diff, i.e., color words which have changed.
By default, words are separated by whitespace.
+
When a <regex> is specified, every non-overlapping match of the
<regex> is considered a word. Anything between these matches is
considered whitespace and ignored(!) for the purposes of finding
differences. You may want to append `|[^[:space:]]` to your regular
expression to make sure that it matches all non-whitespace characters.
A match that contains a newline is silently truncated(!) at the
newline.
+
The regex can also be set via a diff driver or configuration option, see
linkgit:gitattributes[1] or linkgit:git-config[1]. Giving it explicitly
overrides any diff driver or configuration setting. Diff drivers
override configuration settings.
--no-renames::
Turn off rename detection, even when the configuration
@@ -120,7 +133,7 @@ endif::git-format-patch[]
--abbrev[=<n>]::
Instead of showing the full 40-byte hexadecimal object
name in diff-raw format output and diff-tree header
lines, show only handful hexdigits prefix. This is
lines, show only a partial prefix. This is
independent of --full-index option above, which controls
the diff-patch output format. Non default number of
digits can be specified with --abbrev=<n>.
@@ -190,30 +203,28 @@ endif::git-format-patch[]
can name which subdirectory to make the output relative
to by giving a <path> as an argument.
-a::
--text::
Treat all files as text.
-a::
Shorthand for "--text".
--ignore-space-at-eol::
Ignore changes in whitespace at EOL.
-b::
--ignore-space-change::
Ignore changes in amount of whitespace. This ignores whitespace
at line end, and considers all other sequences of one or
more whitespace characters to be equivalent.
-b::
Shorthand for "--ignore-space-change".
-w::
--ignore-all-space::
Ignore whitespace when comparing lines. This ignores
differences even if one line has whitespace where the other
line has none.
-w::
Shorthand for "--ignore-all-space".
--inter-hunk-context=<lines>::
Show the context between diff hunks, up to the specified number
of lines, thereby fusing hunks that are close to each other.
--exit-code::
Make the program exit with codes similar to diff(1).

View File

@@ -11,7 +11,8 @@ SYNOPSIS
[verse]
'git am' [--signoff] [--keep] [--utf8 | --no-utf8]
[--3way] [--interactive]
[--whitespace=<option>] [-C<n>] [-p<n>]
[--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
[--reject]
[<mbox> | <Maildir>...]
'git am' (--skip | --resolved | --abort)
@@ -60,12 +61,10 @@ default. You could use `--no-utf8` to override this.
available locally.
--whitespace=<option>::
This flag is passed to the 'git-apply' (see linkgit:git-apply[1])
program that applies
the patch.
-C<n>::
-p<n>::
--directory=<dir>::
--reject::
These flags are passed to the 'git-apply' (see linkgit:git-apply[1])
program that applies
the patch.

View File

@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git apply' [--stat] [--numstat] [--summary] [--check] [--index]
[--apply] [--no-add] [--build-fake-ancestor <file>] [-R | --reverse]
[--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
[--allow-binary-replacement | --binary] [--reject] [-z]
[-pNUM] [-CNUM] [--inaccurate-eof] [--recount] [--cached]
[--whitespace=<nowarn|warn|fix|error|error-all>]
@@ -64,7 +64,7 @@ OPTIONS
cached data, apply the patch, and store the result in the index,
without using the working tree. This implies '--index'.
--build-fake-ancestor <file>::
--build-fake-ancestor=<file>::
Newer 'git-diff' output has embedded 'index information'
for each blob to help identify the original version that
the patch applies to. When this flag is given, and if

View File

@@ -7,7 +7,7 @@ git-cherry - Find commits not merged upstream
SYNOPSIS
--------
'git cherry' [-v] <upstream> [<head>] [<limit>]
'git cherry' [-v] [<upstream> [<head> [<limit>]]]
DESCRIPTION
-----------
@@ -51,6 +51,7 @@ OPTIONS
<upstream>::
Upstream branch to compare against.
Defaults to the first tracked remote branch, if available.
<head>::
Working branch; defaults to HEAD.

View File

@@ -43,7 +43,7 @@ OPTIONS
Automatically implies --tags.
--abbrev=<n>::
Instead of using the default 8 hexadecimal digits as the
Instead of using the default 7 hexadecimal digits as the
abbreviated object name, use <n> digits.
--candidates=<n>::
@@ -87,7 +87,7 @@ With something like git.git current tree, I get:
v1.0.4-14-g2414721
i.e. the current head of my "parent" branch is based on v1.0.4,
but since it has a handful commits on top of that,
but since it has a few commits on top of that,
describe has added the number of additional commits ("14") and
an abbreviated object name for the commit itself ("2414721")
at the end.

View File

@@ -21,7 +21,10 @@ OPTIONS
-------
include::diff-options.txt[]
-1 -2 -3 or --base --ours --theirs, and -0::
-1 --base::
-2 --ours::
-3 --theirs::
-0::
Diff against the "base" version, "our branch" or "their
branch" respectively. With these options, diffs for
merged entries are not shown.

View File

@@ -122,6 +122,10 @@ You can use the 'map' convenience function in this filter, and other
convenience functions, too. For example, calling 'skip_commit "$@"'
will leave out the current commit (but not its changes! If you want
that, use 'git-rebase' instead).
+
You can also use the 'git_commit_non_empty_tree "$@"' instead of
'git commit-tree "$@"' if you don't wish to keep commits with a single parent
and that makes no change to the tree.
--tag-name-filter <command>::
This is the filter for rewriting tag names. When passed,
@@ -151,6 +155,16 @@ to other tags will be rewritten to point to the underlying commit.
The result will contain that directory (and only that) as its
project root.
--prune-empty::
Some kind of filters will generate empty commits, that left the tree
untouched. This switch allow git-filter-branch to ignore such
commits. Though, this switch only applies for commits that have one
and only one parent, it will hence keep merges points. Also, this
option is not compatible with the use of '--commit-filter'. Though you
just need to use the function 'git_commit_non_empty_tree "$@"' instead
of the 'git commit-tree "$@"' idiom in your commit filter to make that
happen.
--original <namespace>::
Use this option to set the namespace where the original commits
will be stored. The default value is 'refs/original'.

View File

@@ -126,7 +126,7 @@ OPTIONS
--abbrev[=<n>]::
Instead of showing the full 40-byte hexadecimal object
lines, show only handful hexdigits prefix.
lines, show only a partial prefix.
Non default number of digits can be specified with --abbrev=<n>.
\--::

View File

@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git ls-tree' [-d] [-r] [-t] [-l] [-z]
[--name-only] [--name-status] [--full-name] [--abbrev=[<n>]]
[--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev=[<n>]]
<tree-ish> [paths...]
DESCRIPTION
@@ -30,6 +30,8 @@ in the current working directory. Note that:
'sub/dir' in 'HEAD'). You don't want to give a tree that is not at the
root level (e.g. 'git ls-tree -r HEAD:sub dir') in this case, as that
would result in asking for 'sub/sub/dir' in the 'HEAD' commit.
However, the current working directory can be ignored by passing
--full-tree option.
OPTIONS
-------
@@ -59,13 +61,17 @@ OPTIONS
--abbrev[=<n>]::
Instead of showing the full 40-byte hexadecimal object
lines, show only handful hexdigits prefix.
lines, show only a partial prefix.
Non default number of digits can be specified with --abbrev=<n>.
--full-name::
Instead of showing the path names relative to the current working
directory, show the full path names.
--full-tree::
Do not limit the listing to the current working directory.
Implies --full-name.
paths::
When paths are given, show them (note that this isn't really raw
pathnames, but rather a list of patterns to match). Otherwise

View File

@@ -7,7 +7,7 @@ git-mergetool - Run merge conflict resolution tools to resolve merge conflicts
SYNOPSIS
--------
'git mergetool' [--tool=<tool>] [<file>]...
'git mergetool' [--tool=<tool>] [-y|--no-prompt|--prompt] [<file>]...
DESCRIPTION
-----------
@@ -22,7 +22,8 @@ with merge conflicts.
OPTIONS
-------
-t or --tool=<tool>::
-t <tool>::
--tool=<tool>::
Use the merge resolution program specified by <tool>.
Valid merge tools are:
kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
@@ -60,6 +61,16 @@ variable `mergetool.<tool>.trustExitCode` can be set to `true`.
Otherwise, 'git-mergetool' will prompt the user to indicate the
success of the resolution after the custom tool has exited.
-y::
--no-prompt::
Don't prompt before each invocation of the merge resolution
program.
--prompt::
Prompt before each invocation of the merge resolution program.
This is the default behaviour; the option is provided to
override any configuration settings.
Author
------
Written by Theodore Y Ts'o <tytso@mit.edu>

View File

@@ -28,7 +28,9 @@ OPTIONS
-------
<repository>::
The "remote" repository that is destination of a push
operation. See the section <<URLS,GIT URLS>> below.
operation. This parameter can be either a URL
(see the section <<URLS,GIT URLS>> below) or the name
of a remote (see the section <<REMOTES,REMOTES>> below).
<refspec>...::
The canonical format of a <refspec> parameter is
@@ -42,22 +44,21 @@ tip of `master` branch); see linkgit:git-rev-parse[1]) that you
want to push. The <dst> side represents the destination location.
+
The local ref that matches <src> is used
to fast forward the remote ref that matches <dst> (or, if no <dst> was
specified, the same ref that <src> referred to locally). If
to fast forward the remote ref that matches <dst>. If
the optional leading plus `+` is used, the remote ref is updated
even if it does not result in a fast forward update.
+
`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
+
A parameter <ref> without a colon pushes the <ref> from the source
repository to the destination repository under the same name.
A lonely <src> parameter (without a colon and a destination) pushes
the <src> to the same name in the destination repository.
+
Pushing an empty <src> allows you to delete the <dst> ref from
the remote repository.
+
The special refspec `:` (or `+:` to allow non-fast forward updates)
directs git to push "matching" heads: for every head that exists on
the local side, the remote side is updated if a head of the same name
directs git to push "matching" branches: for every branch that exists on
the local side, the remote side is updated if a branch of the same name
already exists on the remote side. This is the default operation mode
if no explicit refspec is found (that is neither on the command line
nor in any Push line of the corresponding remotes file---see below).
@@ -86,14 +87,12 @@ nor in any Push line of the corresponding remotes file---see below).
line.
--receive-pack=<git-receive-pack>::
--exec=<git-receive-pack>::
Path to the 'git-receive-pack' program on the remote
end. Sometimes useful when pushing to a remote
repository over ssh, and you do not have the program in
a directory on the default $PATH.
--exec=<git-receive-pack>::
Same as \--receive-pack=<git-receive-pack>.
-f::
--force::
Usually, the command refuses to update a remote ref that is

View File

@@ -8,10 +8,11 @@ git-rebase - Forward-port local commits to the updated upstream head
SYNOPSIS
--------
[verse]
'git rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
[-s <strategy> | --strategy=<strategy>] [--no-verify]
[-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
[--onto <newbase>] <upstream> [<branch>]
'git rebase' [-i | --interactive] [options] [--onto <newbase>]
<upstream> [<branch>]
'git rebase' [-i | --interactive] [options] --onto <newbase>
--root [<branch>]
'git rebase' --continue | --skip | --abort
DESCRIPTION
@@ -22,7 +23,8 @@ it remains on the current branch.
All changes made by commits in the current branch but that are not
in <upstream> are saved to a temporary area. This is the same set
of commits that would be shown by `git log <upstream>..HEAD`.
of commits that would be shown by `git log <upstream>..HEAD` (or
`git log HEAD`, if --root is specified).
The current branch is reset to <upstream>, or <newbase> if the
--onto option was supplied. This has the exact same effect as
@@ -255,6 +257,15 @@ OPTIONS
--preserve-merges::
Instead of ignoring merges, try to recreate them.
--root::
Rebase all commits reachable from <branch>, instead of
limiting them with an <upstream>. This allows you to rebase
the root commit(s) on a branch. Must be used with --onto, and
will skip changes already contained in <newbase> (instead of
<upstream>). When used together with --preserve-merges, 'all'
root commits will be rewritten to have <newbase> as parent
instead.
include::merge-strategies.txt[]
NOTES

View File

@@ -8,7 +8,7 @@ git-reset - Reset current HEAD to the specified state
SYNOPSIS
--------
[verse]
'git reset' [--mixed | --soft | --hard] [-q] [<commit>]
'git reset' [--mixed | --soft | --hard | --merge] [-q] [<commit>]
'git reset' [-q] [<commit>] [--] <paths>...
DESCRIPTION
@@ -45,6 +45,11 @@ OPTIONS
switched to. Any changes to tracked files in the working tree
since <commit> are lost.
--merge::
Resets the index to match the tree recorded by the named commit,
and updates the files that are different between the named commit
and the current commit in the working tree.
-q::
Be quiet, only report errors.
@@ -152,6 +157,28 @@ tip of the current branch in ORIG_HEAD, so resetting hard to it
brings your index file and the working tree back to that state,
and resets the tip of the branch to that commit.
Undo a merge or pull inside a dirty work tree::
+
------------
$ git pull <1>
Auto-merging nitfol
Merge made by recursive.
nitfol | 20 +++++----
...
$ git reset --merge ORIG_HEAD <2>
------------
+
<1> Even if you may have local modifications in your
working tree, you can safely say "git pull" when you know
that the change in the other branch does not overlap with
them.
<2> After inspecting the result of the merge, you may find
that the change in the other branch is unsatisfactory. Running
"git reset --hard ORIG_HEAD" will let you go back to where you
were, but it will discard your local changes, which you do not
want. "git reset --merge" keeps your local changes.
Interrupted workflow::
+
Suppose you are interrupted by an urgent fix request while you

View File

@@ -34,6 +34,7 @@ The --bcc option must be repeated for each user you want on the bcc list.
--cc::
Specify a starting "Cc:" value for each email.
Default is the value of 'sendemail.cc'.
+
The --cc option must be repeated for each user you want on the cc list.
@@ -197,12 +198,6 @@ Administering
--[no-]validate::
Perform sanity checks on patches.
Currently, validation means the following:
--[no-]format-patch::
When an argument may be understood either as a reference or as a file name,
choose to understand it as a format-patch argument ('--format-patch')
or as a file name ('--no-format-patch'). By default, when such a conflict
occurs, git send-email will fail.
+
--
* Warn of patches that contain lines longer than 998 characters; this
@@ -212,6 +207,12 @@ Administering
Default is the value of 'sendemail.validate'; if this is not set,
default to '--validate'.
--[no-]format-patch::
When an argument may be understood either as a reference or as a file name,
choose to understand it as a format-patch argument ('--format-patch')
or as a file name ('--no-format-patch'). By default, when such a conflict
occurs, git send-email will fail.
CONFIGURATION
-------------

View File

@@ -18,8 +18,9 @@ of server-side GIT commands implementing the pull/push functionality.
The commands can be executed only by the '-c' option; the shell is not
interactive.
Currently, only the 'git-receive-pack' and 'git-upload-pack' commands
are permitted to be called, with a single required argument.
Currently, only three commands are permitted to be called, 'git-receive-pack'
'git-upload-pack' with a single required argument or 'cvs server' (to invoke
'git-cvsserver').
Author
------

View File

@@ -48,15 +48,41 @@ OPTIONS
FILES
-----
If the file `.mailmap` exists, it will be used for mapping author
email addresses to a real author name. One mapping per line, first
the author name followed by the email address enclosed by
'<' and '>'. Use hash '#' for comments. Example:
If a file `.mailmap` exists at the toplevel of the repository,
it is used to map an author email address to a canonical real name. This
can be used to coalesce together commits by the same person where their
name was spelled differently (whether with the same email address or
not).
Each line in the file consists, in this order, of the canonical real name
of an author, whitespace, and an email address (enclosed by '<' and '>')
to map to the name. Use hash '#' for comments, either on their own line,
or after the email address.
A canonical name may appear in more than one line, associated with
different email addresses, but it doesn't make sense for a given address
to appear more than once (if that happens, a later line overrides the
earlier ones).
So, for example, if your history contains commits by two authors, Jane
and Joe, whose names appear in the repository under several forms:
------------
# Keep alphabetized
Adam Morrow <adam@localhost.localdomain>
Eve Jones <eve@laptop.(none)>
Joe Developer <joe@example.com>
Joe R. Developer <joe@example.com>
Jane Doe <jane@example.com>
Jane Doe <jane@laptop.(none)>
Jane D. <jane@desktop.(none)>
------------
Then, supposing Joe wants his middle name initial used, and Jane prefers
her family name fully spelled out, a proper `.mailmap` file would look like:
------------
# Note how we don't need an entry for <jane@laptop.(none)>, because the
# real name of that author is correct already, and coalesced directly.
Jane Doe <jane@desktop.(none)>
Joe R. Developer <joe@random.com>
------------
Author

View File

@@ -92,6 +92,30 @@ COMMANDS
.git/config file may be specified as an optional command-line
argument.
--localtime;;
Store Git commit times in the local timezone instead of UTC. This
makes 'git-log' (even without --date=local) show the same times
that `svn log` would in the local timezone.
This doesn't interfere with interoperating with the Subversion
repository you cloned from, but if you wish for your local Git
repository to be able to interoperate with someone else's local Git
repository, either don't use this option or you should both use it in
the same local timezone.
--ignore-paths=<regex>;;
This allows one to specify Perl regular expression that will
cause skipping of all matching paths from checkout from SVN.
Examples:
--ignore-paths="^doc" - skip "doc*" directory for every fetch.
--ignore-paths="^[^/]+/(?:branches|tags)" - skip "branches"
and "tags" of first level directories.
Regular expression is not persistent, you should specify
it every time when fetching.
'clone'::
Runs 'init' and 'fetch'. It will automatically create a
directory based on the basename of the URL passed to it;

View File

@@ -70,7 +70,7 @@ OPTIONS
-m <msg>::
Use the given tag message (instead of prompting).
If multiple `-m` options are given, there values are
If multiple `-m` options are given, their values are
concatenated as separate paragraphs.
Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
is given.
@@ -207,7 +207,7 @@ determines who are interested in whose tags.
A one-shot pull is a sign that a commit history is now crossing
the boundary between one circle of people (e.g. "people who are
primarily interested in networking part of the kernel") who may
primarily interested in the networking part of the kernel") who may
have their own set of tags (e.g. "this is the third release
candidate from the networking group to be proposed for general
consumption with 2.6.21 release") to another circle of people

View File

@@ -43,9 +43,10 @@ unreleased) version of git, that is available from 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
* link:v1.6.1/git.html[documentation for release 1.6.1]
* link:v1.6.1.1/git.html[documentation for release 1.6.1.1]
* release notes for
link:RelNotes-1.6.1.1.txt[1.6.1.1],
link:RelNotes-1.6.1.txt[1.6.1].
* link:v1.6.0.6/git.html[documentation for release 1.6.0.6]

View File

@@ -317,6 +317,8 @@ patterns are available:
- `bibtex` suitable for files with BibTeX coded references.
- `cpp` suitable for source code in the C and C++ languages.
- `html` suitable for HTML/XHTML documents.
- `java` suitable for source code in the Java language.
@@ -334,6 +336,25 @@ patterns are available:
- `tex` suitable for source code for LaTeX documents.
Customizing word diff
^^^^^^^^^^^^^^^^^^^^^
You can customize the rules that `git diff --color-words` uses to
split words in a line, by specifying an appropriate regular expression
in the "diff.*.wordRegex" configuration variable. For example, in TeX
a backslash followed by a sequence of letters forms a command, but
several such commands can be run together without intervening
whitespace. To separate them, use a regular expression such as
------------------------
[diff "tex"]
wordRegex = "\\\\[a-zA-Z]+|[{}]|\\\\.|[^\\{}[:space:]]+"
------------------------
A built-in pattern is provided for all languages listed in the
previous section.
Performing text diffs of binary files
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@@ -1243,10 +1243,10 @@ $ git ls-files --stage
------------
In our example of only two files, we did not have unchanged
files so only 'example' resulted in collapsing, but in real-life
large projects, only small number of files change in one commit,
and this 'collapsing' tends to trivially merge most of the paths
fairly quickly, leaving only a handful the real changes in non-zero
files so only 'example' resulted in collapsing. But in real-life
large projects, when only a small number of files change in one commit,
this 'collapsing' tends to trivially merge most of the paths
fairly quickly, leaving only a handful of real changes in non-zero
stages.
To look at only non-zero stages, use `\--unmerged` flag:

View File

@@ -15,7 +15,7 @@ DESCRIPTION
Hooks are little scripts you can place in `$GIT_DIR/hooks`
directory to trigger action at certain points. When
'git-init' is run, a handful example hooks are copied in the
'git-init' is run, a handful of example hooks are copied into the
`hooks` directory of the new repository, but by default they are
all disabled. To enable a hook, rename it by removing its `.sample`
suffix.
@@ -90,7 +90,7 @@ This hook is invoked by 'git-commit' right after preparing the
default log message, and before the editor is started.
It takes one to three parameters. The first is the name of the file
that the commit log message. The second is the source of the commit
that contains the commit log message. The second is the source of the commit
message, and can be: `message` (if a `-m` or `-F` option was
given); `template` (if a `-t` option was given or the
configuration option `commit.template` is set); `merge` (if the

View File

@@ -32,12 +32,12 @@ Initialized empty Git repository in .git/
$ echo 'hello world' > file.txt
$ git add .
$ git commit -a -m "initial commit"
[master (root-commit)] created 54196cc: "initial commit"
[master (root-commit) 54196cc] initial commit
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 file.txt
$ echo 'hello world!' >file.txt
$ git commit -a -m "add emphasis"
[master] created c4d59f3: "add emphasis"
[master c4d59f3] add emphasis
1 files changed, 1 insertions(+), 1 deletions(-)
------------------------------------------------

View File

@@ -308,9 +308,7 @@ alice$ git pull /home/bob/myrepo master
This merges the changes from Bob's "master" branch into Alice's
current branch. If Alice has made her own changes in the meantime,
then she may need to manually fix any conflicts. (Note that the
"master" argument in the above command is actually unnecessary, as it
is the default.)
then she may need to manually fix any conflicts.
The "pull" command thus performs two operations: it fetches changes
from a remote branch, then merges them into the current branch.
@@ -590,7 +588,7 @@ list. When the history has lines of development that diverged and
then merged back together, the order in which 'git-log' presents
those commits is meaningless.
Most projects with multiple contributors (such as the linux kernel,
Most projects with multiple contributors (such as the Linux kernel,
or git itself) have frequent merges, and 'gitk' does a better job of
visualizing their history. For example,
@@ -642,7 +640,7 @@ digressions that may be interesting at this point are:
* linkgit:git-format-patch[1], linkgit:git-am[1]: These convert
series of git commits into emailed patches, and vice versa,
useful for projects such as the linux kernel which rely heavily
useful for projects such as the Linux kernel which rely heavily
on emailed patches.
* linkgit:git-bisect[1]: When there is a regression in your

View File

@@ -27,7 +27,7 @@ the kind of task StGIT is designed to do.
I just have done a simpler one, this time using only the core
GIT tools.
I had a handful commits that were ahead of master in pu, and I
I had a handful of commits that were ahead of master in pu, and I
wanted to add some documentation bypassing my usual habit of
placing new things in pu first. At the beginning, the commit
ancestry graph looked like this:

View File

@@ -124,6 +124,7 @@ The placeholders are:
- '%Cgreen': switch color to green
- '%Cblue': switch color to blue
- '%Creset': reset color
- '%C(...)': color specification, as described in color.branch.* config option
- '%m': left, right or boundary mark
- '%n': newline
- '%x00': print a byte from a hex code

View File

@@ -10,7 +10,7 @@ configuration (see linkgit:git-config[1]).
--abbrev-commit::
Instead of showing the full 40-byte hexadecimal commit object
name, show only handful hexdigits prefix. Non default number of
name, show only a partial prefix. Non default number of
digits can be specified with "--abbrev=<n>" (which also modifies
diff output, if it is displayed).
+

View File

@@ -52,6 +52,21 @@ Functions
Wait for the completion of an asynchronous function that was
started with start_async().
`run_hook`::
Run a hook.
The first argument is a pathname to an index file, or NULL
if the hook uses the default index file or no index is needed.
The second argument is the name of the hook.
The further arguments correspond to the hook arguments.
The last argument has to be NULL to terminate the arguments list.
If the hook does not exist or is not executable, the return
value will be zero.
If it is executable, the hook will be executed and the exit
status of the hook is returned.
On execution, .stdout_to_stderr and .no_stdin will be set.
(See below.)
Data structures
---------------

View File

@@ -133,8 +133,10 @@ Functions
* Adding data to the buffer
NOTE: All of these functions in this section will grow the buffer as
necessary.
NOTE: All of the functions in this section will grow the buffer as necessary.
If they fail for some reason other than memory shortage and the buffer hadn't
been allocated before (i.e. the `struct strbuf` was set to `STRBUF_INIT`),
then they will free() it.
`strbuf_addch`::
@@ -235,6 +237,11 @@ same behaviour as well.
Read the contents of a file, specified by its path. The third argument
can be used to give a hint about the file size, to avoid reallocs.
`strbuf_readlink`::
Read the target of a symbolic link, specified by its path. The third
argument can be used to give a hint about the size, to avoid reallocs.
`strbuf_getline`::
Read a line from a FILE* pointer. The second argument specifies the line

View File

@@ -59,7 +59,7 @@ project in mind, here are some interesting examples:
------------------------------------------------
# git itself (approx. 10MB download):
$ git clone git://git.kernel.org/pub/scm/git/git.git
# the linux kernel (approx. 150MB download):
# the Linux kernel (approx. 150MB download):
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
------------------------------------------------
@@ -1009,7 +1009,7 @@ $ git init
If you have some initial content (say, a tarball):
-------------------------------------------------
$ tar -xzvf project.tar.gz
$ tar xzvf project.tar.gz
$ cd project
$ git init
$ git add . # include everything below ./ in the first commit:
@@ -1340,7 +1340,7 @@ These will display all commits which exist only on HEAD or on
MERGE_HEAD, and which touch an unmerged file.
You may also use linkgit:git-mergetool[1], which lets you merge the
unmerged files using external tools such as emacs or kdiff3.
unmerged files using external tools such as Emacs or kdiff3.
Each time you resolve the conflicts in a file and update the index:

View File

@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
DEF_VER=v1.6.0.2.GIT
DEF_VER=v1.6.1.GIT
LF='
'

View File

@@ -101,6 +101,9 @@ Issues of note:
Building and installing the info file additionally requires
makeinfo and docbook2X. Version 0.8.3 is known to work.
Building and installing the pdf file additionally requires
dblatex. Version 0.2.7 with asciidoc >= 8.2.7 is known to work.
The documentation is written for AsciiDoc 7, but "make
ASCIIDOC8=YesPlease doc" will let you format with AsciiDoc 8.

View File

@@ -822,6 +822,7 @@ ifeq ($(uname_S),Darwin)
BASIC_LDFLAGS += -L/opt/local/lib
endif
endif
PTHREAD_LIBS =
endif
ifndef CC_LD_DYNPATH
@@ -1301,7 +1302,7 @@ $(LIB_FILE): $(LIB_OBJS)
$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
xdiff/xmerge.o
xdiff/xmerge.o xdiff/xpatience.o
$(XDIFF_OBJS): xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \
xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h
@@ -1321,6 +1322,9 @@ html:
info:
$(MAKE) -C Documentation info
pdf:
$(MAKE) -C Documentation pdf
TAGS:
$(RM) TAGS
$(FIND) . -name '*.[hcS]' -print | xargs etags -a
@@ -1367,7 +1371,15 @@ endif
### Testing rules
TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-parse-options$X test-path-utils$X
TEST_PROGRAMS += test-chmtime$X
TEST_PROGRAMS += test-ctype$X
TEST_PROGRAMS += test-date$X
TEST_PROGRAMS += test-delta$X
TEST_PROGRAMS += test-genrandom$X
TEST_PROGRAMS += test-match-trees$X
TEST_PROGRAMS += test-parse-options$X
TEST_PROGRAMS += test-path-utils$X
TEST_PROGRAMS += test-sha1$X
all:: $(TEST_PROGRAMS)
@@ -1380,6 +1392,8 @@ export NO_SVN_TESTS
test: all
$(MAKE) -C t/ all
test-ctype$X: ctype.o
test-date$X: date.o ctype.o
test-delta$X: diff-delta.o patch-delta.o
@@ -1445,10 +1459,12 @@ endif
{ $(RM) "$$execdir/git-add$X" && \
ln git-add$X "$$execdir/git-add$X" 2>/dev/null || \
cp git-add$X "$$execdir/git-add$X"; } && \
{ $(foreach p,$(filter-out git-add$X,$(BUILT_INS)), $(RM) "$$execdir/$p" && \
ln "$$execdir/git-add$X" "$$execdir/$p" 2>/dev/null || \
ln -s "git-add$X" "$$execdir/$p" 2>/dev/null || \
cp "$$execdir/git-add$X" "$$execdir/$p" || exit;) } && \
{ for p in $(filter-out git-add$X,$(BUILT_INS)); do \
$(RM) "$$execdir/$$p" && \
ln "$$execdir/git-add$X" "$$execdir/$$p" 2>/dev/null || \
ln -s "git-add$X" "$$execdir/$$p" 2>/dev/null || \
cp "$$execdir/git-add$X" "$$execdir/$$p" || exit; \
done } && \
./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
install-doc:
@@ -1463,6 +1479,9 @@ install-html:
install-info:
$(MAKE) -C Documentation install-info
install-pdf:
$(MAKE) -C Documentation install-pdf
quick-install-doc:
$(MAKE) -C Documentation quick-install

2
README
View File

@@ -24,7 +24,7 @@ It was originally written by Linus Torvalds with help of a group of
hackers around the net. It is currently maintained by Junio C Hamano.
Please read the file INSTALL for installation instructions.
See Documentation/tutorial.txt to get started, then see
See Documentation/gittutorial.txt to get started, then see
Documentation/everyday.txt for a useful minimum set of commands,
and "man git-commandname" for documentation of each command.
CVS users may also want to read Documentation/cvs-migration.txt.

View File

@@ -1 +1 @@
Documentation/RelNotes-1.6.1.txt
Documentation/RelNotes-1.6.2.txt

View File

@@ -68,6 +68,33 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
free(seen);
}
static void treat_gitlinks(const char **pathspec)
{
int i;
if (!pathspec || !*pathspec)
return;
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
if (S_ISGITLINK(ce->ce_mode)) {
int len = ce_namelen(ce), j;
for (j = 0; pathspec[j]; j++) {
int len2 = strlen(pathspec[j]);
if (len2 <= len || pathspec[j][len] != '/' ||
memcmp(ce->name, pathspec[j], len))
continue;
if (len2 == len + 1)
/* strip trailing slash */
pathspec[j] = xstrndup(ce->name, len);
else
die ("Path '%s' is in submodule '%.*s'",
pathspec[j], len, ce->name);
}
}
}
}
static void fill_directory(struct dir_struct *dir, const char **pathspec,
int ignored_too)
{
@@ -261,6 +288,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (read_cache() < 0)
die("index file corrupt");
treat_gitlinks(pathspec);
if (add_new_files)
/* This picks up the paths that are not tracked */

View File

@@ -14,6 +14,7 @@
#include "builtin.h"
#include "string-list.h"
#include "dir.h"
#include "parse-options.h"
/*
* --check turns on checking that the working tree matches the
@@ -45,9 +46,11 @@ static int apply_verbosely;
static int no_add;
static const char *fake_ancestor;
static int line_termination = '\n';
static unsigned long p_context = ULONG_MAX;
static const char apply_usage[] =
"git apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [--reverse] [--reject] [--verbose] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|fix|error|error-all>] <patch>...";
static unsigned int p_context = UINT_MAX;
static const char * const apply_usage[] = {
"git apply [options] [<patch>...]",
NULL
};
static enum ws_error_action {
nowarn_ws_error,
@@ -61,6 +64,8 @@ static int applied_after_fixing_ws;
static const char *patch_input_file;
static const char *root;
static int root_len;
static int read_stdin = 1;
static int options;
static void parse_whitespace_option(const char *option)
{
@@ -630,7 +635,7 @@ static int gitdiff_index(const char *line, struct patch *patch)
memcpy(patch->new_sha1_prefix, line, len);
patch->new_sha1_prefix[len] = 0;
if (*ptr == ' ')
patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8);
patch->old_mode = strtoul(ptr+1, NULL, 8);
return 0;
}
@@ -1253,8 +1258,9 @@ static char *inflate_it(const void *data, unsigned long size,
stream.avail_in = size;
stream.next_out = out = xmalloc(inflated_size);
stream.avail_out = inflated_size;
inflateInit(&stream);
st = inflate(&stream, Z_FINISH);
git_inflate_init(&stream);
st = git_inflate(&stream, Z_FINISH);
git_inflate_end(&stream);
if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
free(out);
return NULL;
@@ -2447,6 +2453,8 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
if (st_mode != patch->old_mode)
fprintf(stderr, "warning: %s has type %o, expected %o\n",
old_name, st_mode, patch->old_mode);
if (!patch->new_mode)
patch->new_mode = st_mode;
return 0;
is_new:
@@ -3135,151 +3143,160 @@ static int git_apply_config(const char *var, const char *value, void *cb)
return git_default_config(var, value, cb);
}
static int option_parse_exclude(const struct option *opt,
const char *arg, int unset)
{
add_name_limit(arg, 1);
return 0;
}
static int option_parse_include(const struct option *opt,
const char *arg, int unset)
{
add_name_limit(arg, 0);
has_include = 1;
return 0;
}
static int option_parse_p(const struct option *opt,
const char *arg, int unset)
{
p_value = atoi(arg);
p_value_known = 1;
return 0;
}
static int option_parse_z(const struct option *opt,
const char *arg, int unset)
{
if (unset)
line_termination = '\n';
else
line_termination = 0;
return 0;
}
static int option_parse_whitespace(const struct option *opt,
const char *arg, int unset)
{
const char **whitespace_option = opt->value;
*whitespace_option = arg;
parse_whitespace_option(arg);
return 0;
}
static int option_parse_directory(const struct option *opt,
const char *arg, int unset)
{
root_len = strlen(arg);
if (root_len && arg[root_len - 1] != '/') {
char *new_root;
root = new_root = xmalloc(root_len + 2);
strcpy(new_root, arg);
strcpy(new_root + root_len++, "/");
} else
root = arg;
return 0;
}
int cmd_apply(int argc, const char **argv, const char *unused_prefix)
{
int i;
int read_stdin = 1;
int options = 0;
int errs = 0;
int is_not_gitdir;
int binary;
int force_apply = 0;
const char *whitespace_option = NULL;
struct option builtin_apply_options[] = {
{ OPTION_CALLBACK, 0, "exclude", NULL, "path",
"don´t apply changes matching the given path",
0, option_parse_exclude },
{ OPTION_CALLBACK, 0, "include", NULL, "path",
"apply changes matching the given path",
0, option_parse_include },
{ OPTION_CALLBACK, 'p', NULL, NULL, "num",
"remove <num> leading slashes from traditional diff paths",
0, option_parse_p },
OPT_BOOLEAN(0, "no-add", &no_add,
"ignore additions made by the patch"),
OPT_BOOLEAN(0, "stat", &diffstat,
"instead of applying the patch, output diffstat for the input"),
OPT_BOOLEAN(0, "allow-binary-replacement", &binary,
"now no-op"),
OPT_BOOLEAN(0, "binary", &binary,
"now no-op"),
OPT_BOOLEAN(0, "numstat", &numstat,
"shows number of added and deleted lines in decimal notation"),
OPT_BOOLEAN(0, "summary", &summary,
"instead of applying the patch, output a summary for the input"),
OPT_BOOLEAN(0, "check", &check,
"instead of applying the patch, see if the patch is applicable"),
OPT_BOOLEAN(0, "index", &check_index,
"make sure the patch is applicable to the current index"),
OPT_BOOLEAN(0, "cached", &cached,
"apply a patch without touching the working tree"),
OPT_BOOLEAN(0, "apply", &force_apply,
"also apply the patch (use with --stat/--summary/--check)"),
OPT_STRING(0, "build-fake-ancestor", &fake_ancestor, "file",
"build a temporary index based on embedded index information"),
{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
"paths are separated with NUL character",
PARSE_OPT_NOARG, option_parse_z },
OPT_INTEGER('C', NULL, &p_context,
"ensure at least <n> lines of context match"),
{ OPTION_CALLBACK, 0, "whitespace", &whitespace_option, "action",
"detect new or modified lines that have whitespace errors",
0, option_parse_whitespace },
OPT_BOOLEAN('R', "reverse", &apply_in_reverse,
"apply the patch in reverse"),
OPT_BOOLEAN(0, "unidiff-zero", &unidiff_zero,
"don't expect at least one line of context"),
OPT_BOOLEAN(0, "reject", &apply_with_reject,
"leave the rejected hunks in corresponding *.rej files"),
OPT__VERBOSE(&apply_verbosely),
OPT_BIT(0, "inaccurate-eof", &options,
"tolerate incorrectly detected missing new-line at the end of file",
INACCURATE_EOF),
OPT_BIT(0, "recount", &options,
"do not trust the line counts in the hunk headers",
RECOUNT),
{ OPTION_CALLBACK, 0, "directory", NULL, "root",
"prepend <root> to all filenames",
0, option_parse_directory },
OPT_END()
};
prefix = setup_git_directory_gently(&is_not_gitdir);
prefix_length = prefix ? strlen(prefix) : 0;
git_config(git_apply_config, NULL);
if (apply_default_whitespace)
parse_whitespace_option(apply_default_whitespace);
for (i = 1; i < argc; i++) {
argc = parse_options(argc, argv, builtin_apply_options,
apply_usage, 0);
if (apply_with_reject)
apply = apply_verbosely = 1;
if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))
apply = 0;
if (check_index && is_not_gitdir)
die("--index outside a repository");
if (cached) {
if (is_not_gitdir)
die("--cached outside a repository");
check_index = 1;
}
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
char *end;
int fd;
if (!strcmp(arg, "-")) {
errs |= apply_patch(0, "<stdin>", options);
read_stdin = 0;
continue;
}
if (!prefixcmp(arg, "--exclude=")) {
add_name_limit(arg + 10, 1);
continue;
}
if (!prefixcmp(arg, "--include=")) {
add_name_limit(arg + 10, 0);
has_include = 1;
continue;
}
if (!prefixcmp(arg, "-p")) {
p_value = atoi(arg + 2);
p_value_known = 1;
continue;
}
if (!strcmp(arg, "--no-add")) {
no_add = 1;
continue;
}
if (!strcmp(arg, "--stat")) {
apply = 0;
diffstat = 1;
continue;
}
if (!strcmp(arg, "--allow-binary-replacement") ||
!strcmp(arg, "--binary")) {
continue; /* now no-op */
}
if (!strcmp(arg, "--numstat")) {
apply = 0;
numstat = 1;
continue;
}
if (!strcmp(arg, "--summary")) {
apply = 0;
summary = 1;
continue;
}
if (!strcmp(arg, "--check")) {
apply = 0;
check = 1;
continue;
}
if (!strcmp(arg, "--index")) {
if (is_not_gitdir)
die("--index outside a repository");
check_index = 1;
continue;
}
if (!strcmp(arg, "--cached")) {
if (is_not_gitdir)
die("--cached outside a repository");
check_index = 1;
cached = 1;
continue;
}
if (!strcmp(arg, "--apply")) {
apply = 1;
continue;
}
if (!strcmp(arg, "--build-fake-ancestor")) {
apply = 0;
if (++i >= argc)
die ("need a filename");
fake_ancestor = argv[i];
continue;
}
if (!strcmp(arg, "-z")) {
line_termination = 0;
continue;
}
if (!prefixcmp(arg, "-C")) {
p_context = strtoul(arg + 2, &end, 0);
if (*end != '\0')
die("unrecognized context count '%s'", arg + 2);
continue;
}
if (!prefixcmp(arg, "--whitespace=")) {
whitespace_option = arg + 13;
parse_whitespace_option(arg + 13);
continue;
}
if (!strcmp(arg, "-R") || !strcmp(arg, "--reverse")) {
apply_in_reverse = 1;
continue;
}
if (!strcmp(arg, "--unidiff-zero")) {
unidiff_zero = 1;
continue;
}
if (!strcmp(arg, "--reject")) {
apply = apply_with_reject = apply_verbosely = 1;
continue;
}
if (!strcmp(arg, "-v") || !strcmp(arg, "--verbose")) {
apply_verbosely = 1;
continue;
}
if (!strcmp(arg, "--inaccurate-eof")) {
options |= INACCURATE_EOF;
continue;
}
if (!strcmp(arg, "--recount")) {
options |= RECOUNT;
continue;
}
if (!prefixcmp(arg, "--directory=")) {
arg += strlen("--directory=");
root_len = strlen(arg);
if (root_len && arg[root_len - 1] != '/') {
char *new_root;
root = new_root = xmalloc(root_len + 2);
strcpy(new_root, arg);
strcpy(new_root + root_len++, "/");
} else
root = arg;
continue;
}
if (0 < prefix_length)
} else if (0 < prefix_length)
arg = prefix_filename(prefix, prefix_length, arg);
fd = open(arg, O_RDONLY);

View File

@@ -137,7 +137,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
break;
default:
die("git cat-file: unknown option: %s\n", exp_type);
die("git cat-file: unknown option: %s", exp_type);
}
if (!buf)

View File

@@ -38,23 +38,13 @@ struct checkout_opts {
static int post_checkout_hook(struct commit *old, struct commit *new,
int changed)
{
struct child_process proc;
const char *name = git_path("hooks/post-checkout");
const char *argv[5];
return run_hook(NULL, "post-checkout",
sha1_to_hex(old ? old->object.sha1 : null_sha1),
sha1_to_hex(new ? new->object.sha1 : null_sha1),
changed ? "1" : "0", NULL);
/* "new" can be NULL when checking out from the index before
a commit exists. */
if (access(name, X_OK) < 0)
return 0;
memset(&proc, 0, sizeof(proc));
argv[0] = name;
argv[1] = xstrdup(sha1_to_hex(old ? old->object.sha1 : null_sha1));
argv[2] = xstrdup(sha1_to_hex(new->object.sha1));
argv[3] = changed ? "1" : "0";
argv[4] = NULL;
proc.argv = argv;
proc.no_stdin = 1;
proc.stdout_to_stderr = 1;
return run_command(&proc);
}
static int update_some(const unsigned char *sha1, const char *base, int baselen,
@@ -240,7 +230,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
pathspec_match(pathspec, ps_matched, ce->name, 0);
match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched);
}
if (report_path_error(ps_matched, pathspec, 0))
@@ -249,7 +239,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
/* Any unmerged paths? */
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
if (pathspec_match(pathspec, NULL, ce->name, 0)) {
if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
if (!ce_stage(ce))
continue;
if (opts->force) {
@@ -274,7 +264,7 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
state.refresh_cache = 1;
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
if (pathspec_match(pathspec, NULL, ce->name, 0)) {
if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
if (!ce_stage(ce)) {
errs |= checkout_entry(ce, &state, NULL);
continue;
@@ -681,8 +671,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
argv++;
argc--;
new.name = arg;
if ((new.commit = lookup_commit_reference_gently(rev, 1))) {
new.name = arg;
setup_branch_path(&new);
if (resolve_ref(new.path, rev, 1, NULL))
new.commit = lookup_commit_reference(rev);

View File

@@ -192,15 +192,15 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
dir = opendir(src->buf);
if (!dir)
die("failed to open %s\n", src->buf);
die("failed to open %s", src->buf);
if (mkdir(dest->buf, 0777)) {
if (errno != EEXIST)
die("failed to create directory %s\n", dest->buf);
die("failed to create directory %s", dest->buf);
else if (stat(dest->buf, &buf))
die("failed to stat %s\n", dest->buf);
die("failed to stat %s", dest->buf);
else if (!S_ISDIR(buf.st_mode))
die("%s exists and is not a directory\n", dest->buf);
die("%s exists and is not a directory", dest->buf);
}
strbuf_addch(src, '/');
@@ -224,16 +224,16 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
}
if (unlink(dest->buf) && errno != ENOENT)
die("failed to unlink %s\n", dest->buf);
die("failed to unlink %s", dest->buf);
if (!option_no_hardlinks) {
if (!link(src->buf, dest->buf))
continue;
if (option_local)
die("failed to create link %s\n", dest->buf);
die("failed to create link %s", dest->buf);
option_no_hardlinks = 1;
}
if (copy_file(dest->buf, src->buf, 0666))
die("failed to copy file to %s\n", dest->buf);
die("failed to copy file to %s", dest->buf);
}
closedir(dir);
}
@@ -357,6 +357,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
struct stat buf;
const char *repo_name, *repo, *work_tree, *git_dir;
char *path, *dir;
int dest_exists;
const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
@@ -406,8 +407,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
dir = guess_dir_name(repo_name, is_bundle, option_bare);
strip_trailing_slashes(dir);
if (!stat(dir, &buf))
die("destination directory '%s' already exists.", dir);
dest_exists = !stat(dir, &buf);
if (dest_exists && !is_empty_dir(dir))
die("destination path '%s' already exists and is not "
"an empty directory.", dir);
strbuf_addf(&reflog_msg, "clone: from %s", repo);
@@ -431,7 +434,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (safe_create_leading_directories_const(work_tree) < 0)
die("could not create leading directories of '%s': %s",
work_tree, strerror(errno));
if (mkdir(work_tree, 0755))
if (!dest_exists && mkdir(work_tree, 0755))
die("could not create work tree dir '%s': %s.",
work_tree, strerror(errno));
set_git_work_tree(work_tree);
@@ -519,14 +522,23 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
option_upload_pack);
refs = transport_get_remote_refs(transport);
transport_fetch_refs(transport, refs);
if(refs)
transport_fetch_refs(transport, refs);
}
clear_extra_refs();
if (refs) {
clear_extra_refs();
mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf);
mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf);
head_points_at = locate_head(refs, mapped_refs, &remote_head);
head_points_at = locate_head(refs, mapped_refs, &remote_head);
}
else {
warning("You appear to have cloned an empty repository.");
head_points_at = NULL;
remote_head = NULL;
option_no_checkout = 1;
}
if (head_points_at) {
/* Local default branch link */

View File

@@ -166,7 +166,7 @@ static int list_paths(struct string_list *list, const char *with_tree,
struct cache_entry *ce = active_cache[i];
if (ce->ce_flags & CE_UPDATE)
continue;
if (!pathspec_match(pattern, m, ce->name, 0))
if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m))
continue;
string_list_insert(ce->name, list);
}
@@ -361,40 +361,6 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
return s.commitable;
}
static int run_hook(const char *index_file, const char *name, ...)
{
struct child_process hook;
const char *argv[10], *env[2];
char index[PATH_MAX];
va_list args;
int i;
va_start(args, name);
argv[0] = git_path("hooks/%s", name);
i = 0;
do {
if (++i >= ARRAY_SIZE(argv))
die ("run_hook(): too many arguments");
argv[i] = va_arg(args, const char *);
} while (argv[i]);
va_end(args);
snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
env[0] = index;
env[1] = NULL;
if (access(argv[0], X_OK) < 0)
return 0;
memset(&hook, 0, sizeof(hook));
hook.argv = argv;
hook.no_stdin = 1;
hook.stdout_to_stderr = 1;
hook.env = env;
return run_command(&hook);
}
static int is_a_merge(const unsigned char *sha1)
{
struct commit *commit = lookup_commit(sha1);
@@ -624,7 +590,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
if (!commitable && !in_merge && !allow_empty &&
!(amend && is_a_merge(head_sha1))) {
run_status(stdout, index_file, prefix, 0);
unlink(commit_editmsg);
return 0;
}
@@ -866,6 +831,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
if (wt_status_use_color == -1)
wt_status_use_color = git_use_color_default;
if (diff_use_color_default == -1)
diff_use_color_default = git_use_color_default;
argc = parse_and_validate_options(argc, argv, builtin_status_usage, prefix);
index_file = prepare_index(argc, argv, prefix);
@@ -881,7 +849,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
{
struct rev_info rev;
struct commit *commit;
static const char *format = "format:%h: \"%s\"";
static const char *format = "format:%h] %s";
unsigned char junk_sha1[20];
const char *head = resolve_ref("HEAD", junk_sha1, 0, NULL);
@@ -908,7 +876,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
rev.diffopt.break_opt = 0;
diff_setup_done(&rev.diffopt);
printf("[%s%s]: created ",
printf("[%s%s ",
!prefixcmp(head, "refs/heads/") ?
head + 11 :
!strcmp(head, "HEAD") ?
@@ -945,6 +913,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
git_config(git_commit_config, NULL);
if (wt_status_use_color == -1)
wt_status_use_color = git_use_color_default;
argc = parse_and_validate_options(argc, argv, builtin_commit_usage, prefix);
index_file = prepare_index(argc, argv, prefix);

View File

@@ -5,6 +5,7 @@
*/
#include "cache.h"
#include "dir.h"
#include "builtin.h"
#include "parse-options.h"
@@ -21,9 +22,7 @@ static void count_objects(DIR *d, char *path, int len, int verbose,
const char *cp;
int bad = 0;
if ((ent->d_name[0] == '.') &&
(ent->d_name[1] == 0 ||
((ent->d_name[1] == '.') && (ent->d_name[2] == 0))))
if (is_dot_or_dotdot(ent->d_name))
continue;
for (cp = ent->d_name; *cp; cp++) {
int ch = *cp;

View File

@@ -158,7 +158,7 @@ static void display_name(struct commit_name *n)
n->tag = lookup_tag(n->sha1);
if (!n->tag || parse_tag(n->tag) || !n->tag->tag)
die("annotated tag %s not available", n->path);
if (strcmp(n->tag->tag, n->path))
if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
}

View File

@@ -497,6 +497,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
OPT_END()
};
if (argc == 1)
usage_with_options (fast_export_usage, options);
/* we handle encodings */
git_config(git_default_config, NULL);

View File

@@ -607,7 +607,7 @@ static void set_option(const char *name, const char *value)
{
int r = transport_set_option(transport, name, value);
if (r < 0)
die("Option \"%s\" value \"%s\" is not valid for %s\n",
die("Option \"%s\" value \"%s\" is not valid for %s",
name, value, transport->url);
if (r > 0)
warning("Option \"%s\" is ignored for %s\n",

View File

@@ -10,6 +10,7 @@
#include "tree-walk.h"
#include "fsck.h"
#include "parse-options.h"
#include "dir.h"
#define REACHABLE 0x0001
#define SEEN 0x0002
@@ -395,19 +396,12 @@ static void fsck_dir(int i, char *path)
while ((de = readdir(dir)) != NULL) {
char name[100];
unsigned char sha1[20];
int len = strlen(de->d_name);
switch (len) {
case 2:
if (de->d_name[1] != '.')
break;
case 1:
if (de->d_name[0] != '.')
break;
if (is_dot_or_dotdot(de->d_name))
continue;
case 38:
if (strlen(de->d_name) == 38) {
sprintf(name, "%02x", i);
memcpy(name+2, de->d_name, len+1);
memcpy(name+2, de->d_name, 39);
if (get_sha1_hex(name, sha1) < 0)
break;
add_sha1_list(sha1, DIRENT_SORT_HINT(de));
@@ -628,7 +622,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
}
heads = 0;
for (i = 1; i < argc; i++) {
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
if (!get_sha1(arg, head_sha1)) {
struct object *obj = lookup_object(head_sha1);

View File

@@ -144,34 +144,6 @@ static int too_many_packs(void)
return gc_auto_pack_limit <= cnt;
}
static int run_hook(void)
{
const char *argv[2];
struct child_process hook;
int ret;
argv[0] = git_path("hooks/pre-auto-gc");
argv[1] = NULL;
if (access(argv[0], X_OK) < 0)
return 0;
memset(&hook, 0, sizeof(hook));
hook.argv = argv;
hook.no_stdin = 1;
hook.stdout_to_stderr = 1;
ret = start_command(&hook);
if (ret) {
warning("Could not spawn %s", argv[0]);
return ret;
}
ret = finish_command(&hook);
if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL)
warning("%s exited due to uncaught signal", argv[0]);
return ret;
}
static int need_to_gc(void)
{
/*
@@ -188,11 +160,13 @@ static int need_to_gc(void)
* there is no need.
*/
if (too_many_packs())
append_option(argv_repack, "-A", MAX_ADD);
append_option(argv_repack,
!strcmp(prune_expire, "now") ? "-a" : "-A",
MAX_ADD);
else if (!too_many_loose_objects())
return 0;
if (run_hook())
if (run_hook(NULL, "pre-auto-gc", NULL))
return 0;
return 1;
}
@@ -243,7 +217,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
"run \"git gc\" manually. See "
"\"git help gc\" for more information.\n");
} else
append_option(argv_repack, "-A", MAX_ADD);
append_option(argv_repack,
!strcmp(prune_expire, "now") ? "-a" : "-A",
MAX_ADD);
if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
return error(FAILED_RUN, argv_pack_refs[0]);

View File

@@ -20,6 +20,8 @@
#endif
#endif
static int builtin_grep;
/*
* git grep pathspecs are somewhat different from diff-tree pathspecs;
* pathname wildcards are allowed.
@@ -389,7 +391,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
* we grep through the checked-out files. It tends to
* be a lot more optimized
*/
if (!cached) {
if (!cached && !builtin_grep) {
hit = external_grep(opt, paths, cached);
if (hit >= 0)
return hit;
@@ -402,7 +404,12 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
continue;
if (!pathspec_matches(paths, ce->name))
continue;
if (cached) {
/*
* If CE_VALID is on, we assume worktree file and its cache entry
* are identical, even if worktree file has been modified, so use
* cache version instead
*/
if (cached || (ce->ce_flags & CE_VALID)) {
if (ce_stage(ce))
continue;
hit |= grep_sha1(opt, ce->sha1, ce->name, 0);
@@ -545,6 +552,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
cached = 1;
continue;
}
if (!strcmp("--no-ext-grep", arg)) {
builtin_grep = 1;
continue;
}
if (!strcmp("-a", arg) ||
!strcmp("--text", arg)) {
opt.binary = GREP_BINARY_TEXT;

View File

@@ -29,7 +29,7 @@ static void safe_create_dir(const char *dir, int share)
}
}
else if (share && adjust_shared_perm(dir))
die("Could not make %s writable by group\n", dir);
die("Could not make %s writable by group", dir);
}
static void copy_templates_1(char *path, int baselen,

View File

@@ -16,6 +16,7 @@
#include "patch-ids.h"
#include "run-command.h"
#include "shortlog.h"
#include "remote.h"
/* Set a default date-time format for git log ("log.date" config variable) */
static const char *default_date_mode = NULL;
@@ -249,22 +250,13 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
static void show_tagger(char *buf, int len, struct rev_info *rev)
{
char *email_end, *p;
unsigned long date;
int tz;
struct strbuf out = STRBUF_INIT;
email_end = memchr(buf, '>', len);
if (!email_end)
return;
p = ++email_end;
while (isspace(*p))
p++;
date = strtoul(p, &p, 10);
while (isspace(*p))
p++;
tz = (int)strtol(p, NULL, 10);
printf("Tagger: %.*s\nDate: %s\n", (int)(email_end - buf), buf,
show_date(date, tz, rev->date_mode));
pp_user_info("Tagger", rev->commit_format, &out, buf, rev->date_mode,
git_log_output_encoding ?
git_log_output_encoding: git_commit_encoding);
printf("%s\n", out.buf);
strbuf_release(&out);
}
static int show_object(const unsigned char *sha1, int show_tag_object,
@@ -553,6 +545,7 @@ static const char *get_oneline_for_filename(struct commit *commit,
static FILE *realstdout = NULL;
static const char *output_directory = NULL;
static int outdir_offset;
static int reopen_stdout(const char *oneline, int nr, int total)
{
@@ -579,7 +572,7 @@ static int reopen_stdout(const char *oneline, int nr, int total)
strcpy(filename + len, fmt_patch_suffix);
}
fprintf(realstdout, "%s\n", filename);
fprintf(realstdout, "%s\n", filename + outdir_offset);
if (freopen(filename, "w", stdout) == NULL)
return error("Cannot open patch file %s",filename);
@@ -740,6 +733,27 @@ static const char *clean_message_id(const char *msg_id)
return xmemdupz(a, z - a);
}
static const char *set_outdir(const char *prefix, const char *output_directory)
{
if (output_directory && is_absolute_path(output_directory))
return output_directory;
if (!prefix || !*prefix) {
if (output_directory)
return output_directory;
/* The user did not explicitly ask for "./" */
outdir_offset = 2;
return "./";
}
outdir_offset = strlen(prefix);
if (!output_directory)
return prefix;
return xstrdup(prefix_filename(prefix, outdir_offset,
output_directory));
}
int cmd_format_patch(int argc, const char **argv, const char *prefix)
{
struct commit *commit;
@@ -824,7 +838,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
endpos = strchr(committer, '>');
if (!endpos)
die("bogus committer info %s\n", committer);
die("bogus committer info %s", committer);
add_signoff = xmemdupz(committer, endpos - committer + 1);
}
else if (!strcmp(argv[i], "--attach")) {
@@ -917,8 +931,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
DIFF_OPT_SET(&rev.diffopt, BINARY);
if (!output_directory && !use_stdout)
output_directory = prefix;
if (!use_stdout)
output_directory = set_outdir(prefix, output_directory);
if (output_directory) {
if (use_stdout)
@@ -944,6 +958,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
* get_revision() to do the usual traversal.
*/
}
/*
* We cannot move this anywhere earlier because we do want to
* know if --root was given explicitly from the comand line.
*/
rev.show_root_diff = 1;
if (cover_letter) {
/* remember the range */
int i;
@@ -1070,13 +1091,14 @@ static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
}
static const char cherry_usage[] =
"git cherry [-v] <upstream> [<head>] [<limit>]";
"git cherry [-v] [<upstream> [<head> [<limit>]]]";
int cmd_cherry(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
struct patch_ids ids;
struct commit *commit;
struct commit_list *list = NULL;
struct branch *current_branch;
const char *upstream;
const char *head = "HEAD";
const char *limit = NULL;
@@ -1099,7 +1121,17 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
upstream = argv[1];
break;
default:
usage(cherry_usage);
current_branch = branch_get(NULL);
if (!current_branch || !current_branch->merge
|| !current_branch->merge[0]
|| !current_branch->merge[0]->dst) {
fprintf(stderr, "Could not find a tracked"
" remote branch, please"
" specify <upstream> manually.\n");
usage(cherry_usage);
}
upstream = current_branch->merge[0]->dst;
}
init_revisions(&revs, prefix);

View File

@@ -36,42 +36,6 @@ static const char *tag_other = "";
static const char *tag_killed = "";
static const char *tag_modified = "";
/*
* Match a pathspec against a filename. The first "skiplen" characters
* are the common prefix
*/
int pathspec_match(const char **spec, char *ps_matched,
const char *filename, int skiplen)
{
const char *m;
while ((m = *spec++) != NULL) {
int matchlen = strlen(m + skiplen);
if (!matchlen)
goto matched;
if (!strncmp(m + skiplen, filename + skiplen, matchlen)) {
if (m[skiplen + matchlen - 1] == '/')
goto matched;
switch (filename[skiplen + matchlen]) {
case '/': case '\0':
goto matched;
}
}
if (!fnmatch(m + skiplen, filename + skiplen, 0))
goto matched;
if (ps_matched)
ps_matched++;
continue;
matched:
if (ps_matched)
*ps_matched = 1;
return 1;
}
return 0;
}
static void show_dir_entry(const char *tag, struct dir_entry *ent)
{
int len = prefix_len;
@@ -80,7 +44,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
if (len >= ent->len)
die("git ls-files: internal error - directory entry not superset of prefix");
if (pathspec && !pathspec_match(pathspec, ps_matched, ent->name, len))
if (!match_pathspec(pathspec, ent->name, ent->len, len, ps_matched))
return;
fputs(tag, stdout);
@@ -156,7 +120,7 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
if (len >= ce_namelen(ce))
die("git ls-files: internal error - cache entry not superset of prefix");
if (pathspec && !pathspec_match(pathspec, ps_matched, ce->name, len))
if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), len, ps_matched))
return;
if (tag && *tag && show_valid_bit &&

View File

@@ -23,7 +23,7 @@ static int chomp_prefix;
static const char *ls_tree_prefix;
static const char ls_tree_usage[] =
"git ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]";
"git ls-tree [-d] [-r] [-t] [-l] [-z] [--name-only] [--name-status] [--full-name] [--full-tree] [--abbrev[=<n>]] <tree-ish> [path...]";
static int show_recursive(const char *base, int baselen, const char *pathname)
{
@@ -156,6 +156,11 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
chomp_prefix = 0;
break;
}
if (!strcmp(argv[1]+2, "full-tree")) {
ls_tree_prefix = prefix = NULL;
chomp_prefix = 0;
break;
}
if (!prefixcmp(argv[1]+2, "abbrev=")) {
abbrev = strtoul(argv[1]+9, NULL, 10);
if (abbrev && abbrev < MINIMUM_ABBREV)

View File

@@ -494,7 +494,7 @@ static void convert_to_utf8(struct strbuf *line, const char *charset)
return;
out = reencode_string(line->buf, metainfo_charset, charset);
if (!out)
die("cannot convert from %s to %s\n",
die("cannot convert from %s to %s",
charset, metainfo_charset);
strbuf_attach(line, out, strlen(out), strlen(out));
}

View File

@@ -51,8 +51,11 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, options, merge_file_usage, 0);
if (argc != 3)
usage_with_options(merge_file_usage, options);
if (quiet)
freopen("/dev/null", "w", stderr);
if (quiet) {
if (!freopen("/dev/null", "w", stderr))
return error("failed to redirect stderr to /dev/null: "
"%s\n", strerror(errno));
}
for (i = 0; i < 3; i++) {
if (!names[i])

View File

@@ -33,7 +33,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
}
if (argc < 4)
die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]);
die("Usage: %s <base>... -- <head> <remote> ...", argv[0]);
for (i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "--"))

View File

@@ -300,35 +300,6 @@ static void squash_message(void)
strbuf_release(&out);
}
static int run_hook(const char *name)
{
struct child_process hook;
const char *argv[3], *env[2];
char index[PATH_MAX];
argv[0] = git_path("hooks/%s", name);
if (access(argv[0], X_OK) < 0)
return 0;
snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", get_index_file());
env[0] = index;
env[1] = NULL;
if (squash)
argv[1] = "1";
else
argv[1] = "0";
argv[2] = NULL;
memset(&hook, 0, sizeof(hook));
hook.argv = argv;
hook.no_stdin = 1;
hook.stdout_to_stderr = 1;
hook.env = env;
return run_command(&hook);
}
static void finish(const unsigned char *new_head, const char *msg)
{
struct strbuf reflog_message = STRBUF_INIT;
@@ -374,7 +345,7 @@ static void finish(const unsigned char *new_head, const char *msg)
}
/* Run a post-merge hook */
run_hook("post-merge");
run_hook(NULL, "post-merge", squash ? "1" : "0", NULL);
strbuf_release(&reflog_message);
}

View File

@@ -192,6 +192,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
memmove(destination + i,
destination + i + 1,
(argc - i) * sizeof(char *));
i--;
}
} else
die ("%s, source=%s, destination=%s",

View File

@@ -78,7 +78,7 @@ static int progress = 1;
static int window = 10;
static uint32_t pack_size_limit, pack_size_limit_cfg;
static int depth = 50;
static int delta_search_threads = 1;
static int delta_search_threads;
static int pack_to_stdout;
static int num_preferred_base;
static struct progress *progress_state;
@@ -195,16 +195,16 @@ static int check_pack_inflate(struct packed_git *p,
int st;
memset(&stream, 0, sizeof(stream));
inflateInit(&stream);
git_inflate_init(&stream);
do {
in = use_pack(p, w_curs, offset, &stream.avail_in);
stream.next_in = in;
stream.next_out = fakebuf;
stream.avail_out = sizeof(fakebuf);
st = inflate(&stream, Z_FINISH);
st = git_inflate(&stream, Z_FINISH);
offset += stream.next_in - in;
} while (st == Z_OK || st == Z_BUF_ERROR);
inflateEnd(&stream);
git_inflate_end(&stream);
return (st == Z_STREAM_END &&
stream.total_out == expect &&
stream.total_in == len) ? 0 : -1;
@@ -1612,11 +1612,18 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
find_deltas(list, &list_size, window, depth, processed);
return;
}
if (progress > pack_to_stdout)
fprintf(stderr, "Delta compression using %d threads.\n",
delta_search_threads);
/* Partition the work amongst work threads. */
for (i = 0; i < delta_search_threads; i++) {
unsigned sub_size = list_size / (delta_search_threads - i);
/* don't use too small segments or no deltas will be found */
if (sub_size < 2*window && i+1 < delta_search_threads)
sub_size = 0;
p[i].window = window;
p[i].depth = depth;
p[i].processed = processed;

View File

@@ -5,6 +5,7 @@
#include "builtin.h"
#include "reachable.h"
#include "parse-options.h"
#include "dir.h"
static const char * const prune_usage[] = {
"git prune [-n] [-v] [--expire <time>] [--] [<head>...]",
@@ -61,19 +62,12 @@ static int prune_dir(int i, char *path)
while ((de = readdir(dir)) != NULL) {
char name[100];
unsigned char sha1[20];
int len = strlen(de->d_name);
switch (len) {
case 2:
if (de->d_name[1] != '.')
break;
case 1:
if (de->d_name[0] != '.')
break;
if (is_dot_or_dotdot(de->d_name))
continue;
case 38:
if (strlen(de->d_name) == 38) {
sprintf(name, "%02x", i);
memcpy(name+2, de->d_name, len+1);
memcpy(name+2, de->d_name, 39);
if (get_sha1_hex(name, sha1) < 0)
break;

View File

@@ -136,7 +136,7 @@ static int hook_status(int code, const char *hook_name)
}
}
static int run_hook(const char *hook_name)
static int run_receive_hook(const char *hook_name)
{
static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
struct command *cmd;
@@ -358,7 +358,7 @@ static void execute_commands(const char *unpacker_error)
return;
}
if (run_hook(pre_receive_hook)) {
if (run_receive_hook(pre_receive_hook)) {
while (cmd) {
cmd->error_string = "pre-receive hook declined";
cmd = cmd->next;
@@ -630,7 +630,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
unlink(pack_lockfile);
if (report_status)
report(unpack_status);
run_hook(post_receive_hook);
run_receive_hook(post_receive_hook);
run_update_post_hook(commands);
}
return 0;

View File

@@ -1,5 +1,6 @@
#include "builtin.h"
#include "cache.h"
#include "dir.h"
#include "string-list.h"
#include "rerere.h"
#include "xdiff/xdiff.h"
@@ -59,17 +60,15 @@ static void garbage_collect(struct string_list *rr)
git_config(git_rerere_gc_config, NULL);
dir = opendir(git_path("rr-cache"));
while ((e = readdir(dir))) {
const char *name = e->d_name;
if (name[0] == '.' &&
(name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
if (is_dot_or_dotdot(e->d_name))
continue;
then = rerere_created_at(name);
then = rerere_created_at(e->d_name);
if (!then)
continue;
cutoff = (has_resolution(name)
cutoff = (has_resolution(e->d_name)
? cutoff_resolve : cutoff_noresolve);
if (then < now - cutoff * 86400)
string_list_append(name, &to_remove);
string_list_append(e->d_name, &to_remove);
}
for (i = 0; i < to_remove.nr; i++)
unlink_rr_item(to_remove.items[i].string);

View File

@@ -20,11 +20,14 @@
#include "parse-options.h"
static const char * const git_reset_usage[] = {
"git reset [--mixed | --soft | --hard] [-q] [<commit>]",
"git reset [--mixed | --soft | --hard | --merge] [-q] [<commit>]",
"git reset [--mixed] <commit> [--] <paths>...",
NULL
};
enum reset_type { MIXED, SOFT, HARD, MERGE, NONE };
static const char *reset_type_names[] = { "mixed", "soft", "hard", "merge", NULL };
static char *args_to_str(const char **argv)
{
char *buf = NULL;
@@ -49,7 +52,7 @@ static inline int is_merge(void)
return !access(git_path("MERGE_HEAD"), F_OK);
}
static int reset_index_file(const unsigned char *sha1, int is_hard_reset, int quiet)
static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet)
{
int i = 0;
const char *args[6];
@@ -57,9 +60,17 @@ static int reset_index_file(const unsigned char *sha1, int is_hard_reset, int qu
args[i++] = "read-tree";
if (!quiet)
args[i++] = "-v";
args[i++] = "--reset";
if (is_hard_reset)
switch (reset_type) {
case MERGE:
args[i++] = "-u";
args[i++] = "-m";
break;
case HARD:
args[i++] = "-u";
/* fallthrough */
default:
args[i++] = "--reset";
}
args[i++] = sha1_to_hex(sha1);
args[i] = NULL;
@@ -169,9 +180,6 @@ static void prepend_reflog_action(const char *action, char *buf, size_t size)
warning("Reflog action message too long: %.*s...", 50, buf);
}
enum reset_type { MIXED, SOFT, HARD, NONE };
static const char *reset_type_names[] = { "mixed", "soft", "hard", NULL };
int cmd_reset(int argc, const char **argv, const char *prefix)
{
int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
@@ -186,6 +194,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT),
OPT_SET_INT(0, "hard", &reset_type,
"reset HEAD, index and working tree", HARD),
OPT_SET_INT(0, "merge", &reset_type,
"reset HEAD, index and working tree", MERGE),
OPT_BOOLEAN('q', NULL, &quiet,
"disable showing new HEAD in hard reset and progress message"),
OPT_END()
@@ -266,7 +276,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
if (is_merge() || read_cache() < 0 || unmerged_cache())
die("Cannot do a soft reset in the middle of a merge.");
}
else if (reset_index_file(sha1, (reset_type == HARD), quiet))
else if (reset_index_file(sha1, reset_type, quiet))
die("Could not reset index file to revision '%s'.", rev);
/* Any resets update HEAD to the head being switched to,

View File

@@ -29,6 +29,9 @@ static int compare_by_number(const void *a1, const void *a2)
return -1;
}
const char *format_subject(struct strbuf *sb, const char *msg,
const char *line_separator);
static void insert_one_record(struct shortlog *log,
const char *author,
const char *oneline)
@@ -36,11 +39,11 @@ static void insert_one_record(struct shortlog *log,
const char *dot3 = log->common_repo_prefix;
char *buffer, *p;
struct string_list_item *item;
struct string_list *onelines;
char namebuf[1024];
size_t len;
const char *eol;
const char *boemail, *eoemail;
struct strbuf subject = STRBUF_INIT;
boemail = strchr(author, '<');
if (!boemail)
@@ -68,12 +71,9 @@ static void insert_one_record(struct shortlog *log,
snprintf(namebuf + len, room, " %.*s", maillen, boemail);
}
buffer = xstrdup(namebuf);
item = string_list_insert(buffer, &log->list);
item = string_list_insert(namebuf, &log->list);
if (item->util == NULL)
item->util = xcalloc(1, sizeof(struct string_list));
else
free(buffer);
/* Skip any leading whitespace, including any blank lines. */
while (*oneline && isspace(*oneline))
@@ -89,9 +89,8 @@ static void insert_one_record(struct shortlog *log,
while (*oneline && isspace(*oneline) && *oneline != '\n')
oneline++;
len = eol - oneline;
while (len && isspace(oneline[len-1]))
len--;
buffer = xmemdupz(oneline, len);
format_subject(&subject, oneline, " ");
buffer = strbuf_detach(&subject, NULL);
if (dot3) {
int dot3len = strlen(dot3);
@@ -104,16 +103,7 @@ static void insert_one_record(struct shortlog *log,
}
}
onelines = item->util;
if (onelines->nr >= onelines->alloc) {
onelines->alloc = alloc_nr(onelines->nr);
onelines->items = xrealloc(onelines->items,
onelines->alloc
* sizeof(struct string_list_item));
}
onelines->items[onelines->nr].util = NULL;
onelines->items[onelines->nr++].string = buffer;
string_list_append(buffer, item->util);
}
static void read_from_stdin(struct shortlog *log)
@@ -323,7 +313,7 @@ void shortlog_output(struct shortlog *log)
}
onelines->strdup_strings = 1;
string_list_clear(onelines, 1);
string_list_clear(onelines, 0);
free(onelines);
log->list.items[i].util = NULL;
}

View File

@@ -99,10 +99,10 @@ static void *get_data(unsigned long size)
stream.avail_out = size;
stream.next_in = fill(1);
stream.avail_in = len;
inflateInit(&stream);
git_inflate_init(&stream);
for (;;) {
int ret = inflate(&stream, 0);
int ret = git_inflate(&stream, 0);
use(len - stream.avail_in);
if (stream.total_out == size && ret == Z_STREAM_END)
break;
@@ -118,7 +118,7 @@ static void *get_data(unsigned long size)
stream.next_in = fill(1);
stream.avail_in = len;
}
inflateEnd(&stream);
git_inflate_end(&stream);
return buf;
}

View File

@@ -486,7 +486,7 @@ static int unresolve_one(const char *path)
static void read_head_pointers(void)
{
if (read_ref("HEAD", head_sha1))
die("No HEAD -- no initial commit yet?\n");
die("No HEAD -- no initial commit yet?");
if (read_ref("MERGE_HEAD", merge_head_sha1)) {
fprintf(stderr, "Not in the middle of a merge.\n");
exit(0);

View File

@@ -167,6 +167,32 @@ int list_bundle_refs(struct bundle_header *header, int argc, const char **argv)
return list_refs(&header->references, argc, argv);
}
static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
{
unsigned long size;
enum object_type type;
char *buf, *line, *lineend;
unsigned long date;
if (revs->max_age == -1 && revs->min_age == -1)
return 1;
buf = read_sha1_file(tag->sha1, &type, &size);
if (!buf)
return 1;
line = memmem(buf, size, "\ntagger ", 8);
if (!line++)
return 1;
lineend = memchr(line, buf + size - line, '\n');
line = memchr(line, lineend ? lineend - line : buf + size - line, '>');
if (!line++)
return 1;
date = strtoul(line, NULL, 10);
free(buf);
return (revs->max_age == -1 || revs->max_age < date) &&
(revs->min_age == -1 || revs->min_age > date);
}
int create_bundle(struct bundle_header *header, const char *path,
int argc, const char **argv)
{
@@ -240,6 +266,8 @@ int create_bundle(struct bundle_header *header, const char *path,
return error("unrecognized argument: %s'", argv[i]);
}
object_array_remove_duplicates(&revs.pending);
for (i = 0; i < revs.pending.nr; i++) {
struct object_array_entry *e = revs.pending.objects + i;
unsigned char sha1[20];
@@ -255,6 +283,12 @@ int create_bundle(struct bundle_header *header, const char *path,
flag = 0;
display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
if (e->item->type == OBJ_TAG &&
!is_tag_in_date_range(e->item, &revs)) {
e->item->flags |= UNINTERESTING;
continue;
}
/*
* Make sure the refs we wrote out is correct; --max-count and
* other limiting options could have prevented all the tips

12
cache.h
View File

@@ -18,6 +18,10 @@
#define deflateBound(c,s) ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
#endif
void git_inflate_init(z_streamp strm);
void git_inflate_end(z_streamp strm);
int git_inflate(z_streamp strm, int flush);
#if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
#define DTYPE(de) ((de)->d_type)
#else
@@ -631,9 +635,6 @@ extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsig
extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
extern int force_object_loose(const unsigned char *sha1, time_t mtime);
/* just like read_sha1_file(), but non fatal in presence of bad objects */
extern void *read_object(const unsigned char *sha1, enum object_type *type, unsigned long *size);
/* global flag to enable extra checks when accessing packed objects */
extern int do_check_packed_object_crc;
@@ -720,6 +721,10 @@ struct checkout {
extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
extern int has_symlink_leading_path(int len, const char *name);
extern int has_symlink_or_noent_leading_path(int len, const char *name);
extern int has_dirs_only_path(int len, const char *name, int prefix_len);
extern void invalidate_lstat_cache(int len, const char *name);
extern void clear_lstat_cache(void);
extern struct alternate_object_database {
struct alternate_object_database *next;
@@ -936,7 +941,6 @@ extern int ws_fix_copy(char *, const char *, int, unsigned, int *);
extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
/* ls-files */
int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
void overlay_tree_on_cache(const char *tree_name, const char *prefix);

59
color.c
View File

@@ -40,30 +40,41 @@ static int parse_attr(const char *name, int len)
}
void color_parse(const char *value, const char *var, char *dst)
{
color_parse_mem(value, strlen(value), var, dst);
}
void color_parse_mem(const char *value, int value_len, const char *var,
char *dst)
{
const char *ptr = value;
int len = value_len;
int attr = -1;
int fg = -2;
int bg = -2;
if (!strcasecmp(value, "reset")) {
if (!strncasecmp(value, "reset", len)) {
strcpy(dst, "\033[m");
return;
}
/* [fg [bg]] [attr] */
while (*ptr) {
while (len > 0) {
const char *word = ptr;
int val, len = 0;
int val, wordlen = 0;
while (word[len] && !isspace(word[len]))
len++;
while (len > 0 && !isspace(word[wordlen])) {
wordlen++;
len--;
}
ptr = word + len;
while (*ptr && isspace(*ptr))
ptr = word + wordlen;
while (len > 0 && isspace(*ptr)) {
ptr++;
len--;
}
val = parse_color(word, len);
val = parse_color(word, wordlen);
if (val >= -1) {
if (fg == -2) {
fg = val;
@@ -75,7 +86,7 @@ void color_parse(const char *value, const char *var, char *dst)
}
goto bad;
}
val = parse_attr(word, len);
val = parse_attr(word, wordlen);
if (val < 0 || attr != -1)
goto bad;
attr = val;
@@ -115,7 +126,7 @@ void color_parse(const char *value, const char *var, char *dst)
*dst = 0;
return;
bad:
die("bad config value '%s' for variable '%s'", value, var);
die("bad color value '%.*s' for variable '%s'", value_len, value, var);
}
int git_config_colorbool(const char *var, const char *value, int stdout_is_tty)
@@ -191,3 +202,31 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)
va_end(args);
return r;
}
/*
* This function splits the buffer by newlines and colors the lines individually.
*
* Returns 0 on success.
*/
int color_fwrite_lines(FILE *fp, const char *color,
size_t count, const char *buf)
{
if (!*color)
return fwrite(buf, count, 1, fp) != 1;
while (count) {
char *p = memchr(buf, '\n', count);
if (p != buf && (fputs(color, fp) < 0 ||
fwrite(buf, p ? p - buf : count, 1, fp) != 1 ||
fputs(COLOR_RESET, fp) < 0))
return -1;
if (!p)
return 0;
if (fputc('\n', fp) < 0)
return -1;
count -= p + 1 - buf;
buf = p + 1;
}
return 0;
}

View File

@@ -16,8 +16,10 @@ extern int git_use_color_default;
int git_color_default_config(const char *var, const char *value, void *cb);
int git_config_colorbool(const char *var, const char *value, int stdout_is_tty);
void color_parse(const char *var, const char *value, char *dst);
void color_parse(const char *value, const char *var, char *dst);
void color_parse_mem(const char *value, int len, const char *var, char *dst);
int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);
#endif /* COLOR_H */

View File

@@ -52,4 +52,5 @@ NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
FREAD_READS_DIRECTORIES=@FREAD_READS_DIRECTORIES@
SNPRINTF_RETURNS_BOGUS=@SNPRINTF_RETURNS_BOGUS@
NO_PTHREADS=@NO_PTHREADS@
THREADED_DELTA_SEARCH=@THREADED_DELTA_SEARCH@
PTHREAD_LIBS=@PTHREAD_LIBS@

View File

@@ -114,31 +114,31 @@ AC_MSG_NOTICE([CHECKS for programs])
#
AC_PROG_CC([cc gcc])
# which switch to pass runtime path to dynamic libraries to the linker
AC_CACHE_CHECK([if linker supports -R], ld_dashr, [
AC_CACHE_CHECK([if linker supports -R], git_cv_ld_dashr, [
SAVE_LDFLAGS="${LDFLAGS}"
LDFLAGS="${SAVE_LDFLAGS} -R /"
AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [ld_dashr=yes], [ld_dashr=no])
AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [git_cv_ld_dashr=yes], [git_cv_ld_dashr=no])
LDFLAGS="${SAVE_LDFLAGS}"
])
if test "$ld_dashr" = "yes"; then
if test "$git_cv_ld_dashr" = "yes"; then
AC_SUBST(CC_LD_DYNPATH, [-R])
else
AC_CACHE_CHECK([if linker supports -Wl,-rpath,], ld_wl_rpath, [
AC_CACHE_CHECK([if linker supports -Wl,-rpath,], git_cv_ld_wl_rpath, [
SAVE_LDFLAGS="${LDFLAGS}"
LDFLAGS="${SAVE_LDFLAGS} -Wl,-rpath,/"
AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [ld_wl_rpath=yes], [ld_wl_rpath=no])
LDFLAGS="${SAVE_LD_FLAGS}"
AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [git_cv_ld_wl_rpath=yes], [git_cv_ld_wl_rpath=no])
LDFLAGS="${SAVE_LDFLAGS}"
])
if test "$ld_wl_rpath" = "yes"; then
if test "$git_cv_ld_wl_rpath" = "yes"; then
AC_SUBST(CC_LD_DYNPATH, [-Wl,-rpath,])
else
AC_CACHE_CHECK([if linker supports -rpath], ld_rpath, [
AC_CACHE_CHECK([if linker supports -rpath], git_cv_ld_rpath, [
SAVE_LDFLAGS="${LDFLAGS}"
LDFLAGS="${SAVE_LDFLAGS} -rpath /"
AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [ld_rpath=yes], [ld_rpath=no])
LDFLAGS="${SAVE_LD_FLAGS}"
AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [git_cv_ld_rpath=yes], [git_cv_ld_rpath=no])
LDFLAGS="${SAVE_LDFLAGS}"
])
if test "$ld_rpath" = "yes"; then
if test "$git_cv_ld_rpath" = "yes"; then
AC_SUBST(CC_LD_DYNPATH, [-rpath])
else
AC_MSG_WARN([linker does not support runtime path to dynamic libraries])
@@ -492,7 +492,8 @@ AC_SUBST(NO_MKDTEMP)
#
# Define NO_PTHREADS if we do not have pthreads
#
# Define PTHREAD_LIBS to the linker flag used for Pthread support.
# Define PTHREAD_LIBS to the linker flag used for Pthread support and define
# THREADED_DELTA_SEARCH if Pthreads are available.
AC_LANG_CONFTEST([AC_LANG_PROGRAM(
[[#include <pthread.h>]],
[[pthread_mutex_t test_mutex;]]
@@ -500,16 +501,19 @@ AC_LANG_CONFTEST([AC_LANG_PROGRAM(
${CC} -pthread conftest.c -o conftest.o > /dev/null 2>&1
if test $? -eq 0;then
PTHREAD_LIBS="-pthread"
THREADED_DELTA_SEARCH=YesPlease
else
${CC} -lpthread conftest.c -o conftest.o > /dev/null 2>&1
if test $? -eq 0;then
PTHREAD_LIBS="-lpthread"
THREADED_DELTA_SEARCH=YesPlease
else
NO_PTHREADS=UnfortunatelyYes
fi
fi
AC_SUBST(PTHREAD_LIBS)
AC_SUBST(NO_PTHREADS)
AC_SUBST(THREADED_DELTA_SEARCH)
## Site configuration (override autodetection)
## --with-PACKAGE[=ARG] and --without-PACKAGE

View File

@@ -315,7 +315,7 @@ static int git_tcp_connect_sock(char *host, int flags)
/* Not numeric */
struct servent *se = getservbyname(port,"tcp");
if ( !se )
die("Unknown port %s\n", port);
die("Unknown port %s", port);
nport = se->s_port;
}

View File

@@ -1,3 +1,4 @@
#!bash
#
# bash completion support for core Git.
#
@@ -50,9 +51,11 @@ case "$COMP_WORDBREAKS" in
*) COMP_WORDBREAKS="$COMP_WORDBREAKS:"
esac
# __gitdir accepts 0 or 1 arguments (i.e., location)
# returns location of .git repo
__gitdir ()
{
if [ -z "$1" ]; then
if [ -z "${1-}" ]; then
if [ -n "$__git_dir" ]; then
echo "$__git_dir"
elif [ -d .git ]; then
@@ -67,6 +70,8 @@ __gitdir ()
fi
}
# __git_ps1 accepts 0 or 1 arguments (i.e., format string)
# returns text to add to bash PS1 prompt (includes branch name)
__git_ps1 ()
{
local g="$(git rev-parse --git-dir 2>/dev/null)"
@@ -111,7 +116,7 @@ __git_ps1 ()
fi
fi
if [ -n "$1" ]; then
if [ -n "${1-}" ]; then
printf "$1" "${b##refs/heads/}$r"
else
printf " (%s)" "${b##refs/heads/}$r"
@@ -119,6 +124,7 @@ __git_ps1 ()
fi
}
# __gitcomp_1 requires 2 arguments
__gitcomp_1 ()
{
local c IFS=' '$'\t'$'\n'
@@ -131,6 +137,8 @@ __gitcomp_1 ()
done
}
# __gitcomp accepts 1, 2, 3, or 4 arguments
# generates completion reply with compgen
__gitcomp ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -143,22 +151,23 @@ __gitcomp ()
;;
*)
local IFS=$'\n'
COMPREPLY=($(compgen -P "$2" \
-W "$(__gitcomp_1 "$1" "$4")" \
COMPREPLY=($(compgen -P "${2-}" \
-W "$(__gitcomp_1 "${1-}" "${4-}")" \
-- "$cur"))
;;
esac
}
# __git_heads accepts 0 or 1 arguments (to pass to __gitdir)
__git_heads ()
{
local cmd i is_hash=y dir="$(__gitdir "$1")"
local cmd i is_hash=y dir="$(__gitdir "${1-}")"
if [ -d "$dir" ]; then
git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
refs/heads
return
fi
for i in $(git ls-remote "$1" 2>/dev/null); do
for i in $(git ls-remote "${1-}" 2>/dev/null); do
case "$is_hash,$i" in
y,*) is_hash=n ;;
n,*^{}) is_hash=y ;;
@@ -168,15 +177,16 @@ __git_heads ()
done
}
# __git_tags accepts 0 or 1 arguments (to pass to __gitdir)
__git_tags ()
{
local cmd i is_hash=y dir="$(__gitdir "$1")"
local cmd i is_hash=y dir="$(__gitdir "${1-}")"
if [ -d "$dir" ]; then
git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
refs/tags
return
fi
for i in $(git ls-remote "$1" 2>/dev/null); do
for i in $(git ls-remote "${1-}" 2>/dev/null); do
case "$is_hash,$i" in
y,*) is_hash=n ;;
n,*^{}) is_hash=y ;;
@@ -186,9 +196,10 @@ __git_tags ()
done
}
# __git_refs accepts 0 or 1 arguments (to pass to __gitdir)
__git_refs ()
{
local i is_hash=y dir="$(__gitdir "$1")"
local i is_hash=y dir="$(__gitdir "${1-}")"
local cur="${COMP_WORDS[COMP_CWORD]}" format refs
if [ -d "$dir" ]; then
case "$cur" in
@@ -218,6 +229,7 @@ __git_refs ()
done
}
# __git_refs2 requires 1 argument (to pass to __git_refs)
__git_refs2 ()
{
local i
@@ -226,6 +238,7 @@ __git_refs2 ()
done
}
# __git_refs_remotes requires 1 argument (to pass to ls-remote)
__git_refs_remotes ()
{
local cmd i is_hash=y
@@ -470,6 +483,7 @@ __git_aliases ()
done
}
# __git_aliased_command requires 1 argument
__git_aliased_command ()
{
local word cmdline=$(git --git-dir="$(__gitdir)" \
@@ -482,6 +496,7 @@ __git_aliased_command ()
done
}
# __git_find_subcommand requires 1 argument
__git_find_subcommand ()
{
local word subcommand c=1
@@ -563,7 +578,7 @@ _git_add ()
--*)
__gitcomp "
--interactive --refresh --patch --update --dry-run
--ignore-errors
--ignore-errors --intent-to-add
"
return
esac
@@ -628,7 +643,6 @@ _git_branch ()
done
case "${COMP_WORDS[COMP_CWORD]}" in
--*=*) COMPREPLY=() ;;
--*)
__gitcomp "
--color --no-color --verbose --abbrev= --no-abbrev
@@ -759,6 +773,20 @@ _git_describe ()
__gitcomp "$(__git_refs)"
}
__git_diff_common_options="--stat --numstat --shortstat --summary
--patch-with-stat --name-only --name-status --color
--no-color --color-words --no-renames --check
--full-index --binary --abbrev --diff-filter=
--find-copies-harder
--text --ignore-space-at-eol --ignore-space-change
--ignore-all-space --exit-code --quiet --ext-diff
--no-ext-diff
--no-prefix --src-prefix= --dst-prefix=
--inter-hunk-context=
--patience
--raw
"
_git_diff ()
{
__git_has_doubledash && return
@@ -766,16 +794,9 @@ _git_diff ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--*)
__gitcomp "--cached --stat --numstat --shortstat --summary
--patch-with-stat --name-only --name-status --color
--no-color --color-words --no-renames --check
--full-index --binary --abbrev --diff-filter=
--find-copies-harder --pickaxe-all --pickaxe-regex
--text --ignore-space-at-eol --ignore-space-change
--ignore-all-space --exit-code --quiet --ext-diff
--no-ext-diff
--no-prefix --src-prefix= --dst-prefix=
__gitcomp "--cached --pickaxe-all --pickaxe-regex
--base --ours --theirs
$__git_diff_common_options
"
return
;;
@@ -823,6 +844,8 @@ _git_format_patch ()
--not --all
--cover-letter
--no-prefix --src-prefix= --dst-prefix=
--inline --suffix= --ignore-if-in-upstream
--subject-prefix=
"
return
;;
@@ -930,6 +953,8 @@ _git_ls_tree ()
__git_complete_file
}
__git_log_pretty_formats="oneline short medium full fuller email raw format:"
_git_log ()
{
__git_has_doubledash && return
@@ -937,8 +962,7 @@ _git_log ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--pretty=*)
__gitcomp "
oneline short medium full fuller email raw
__gitcomp "$__git_log_pretty_formats
" "" "${cur##--pretty=}"
return
;;
@@ -958,15 +982,16 @@ _git_log ()
--relative-date --date=
--author= --committer= --grep=
--all-match
--pretty= --name-status --name-only --raw
--pretty=
--not --all
--left-right --cherry-pick
--graph
--stat --numstat --shortstat
--decorate --diff-filter=
--color-words --walk-reflogs
--decorate
--walk-reflogs
--parents --children --full-history
--merge
$__git_diff_common_options
--pickaxe-all --pickaxe-regex
"
return
;;
@@ -1367,7 +1392,7 @@ _git_config ()
_git_remote ()
{
local subcommands="add rm show prune update"
local subcommands="add rename rm show prune update"
local subcommand="$(__git_find_subcommand "$subcommands")"
if [ -z "$subcommand" ]; then
__gitcomp "$subcommands"
@@ -1375,7 +1400,7 @@ _git_remote ()
fi
case "$subcommand" in
rm|show|prune)
rename|rm|show|prune)
__gitcomp "$(__git_remotes)"
;;
update)
@@ -1403,7 +1428,7 @@ _git_reset ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--*)
__gitcomp "--mixed --hard --soft"
__gitcomp "--merge --mixed --hard --soft"
return
;;
esac
@@ -1465,13 +1490,14 @@ _git_show ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--pretty=*)
__gitcomp "
oneline short medium full fuller email raw
__gitcomp "$__git_log_pretty_formats
" "" "${cur##--pretty=}"
return
;;
--*)
__gitcomp "--pretty="
__gitcomp "--pretty=
$__git_diff_common_options
"
return
;;
esac
@@ -1558,7 +1584,7 @@ _git_svn ()
--follow-parent --authors-file= --repack=
--no-metadata --use-svm-props --use-svnsync-props
--log-window-size= --no-checkout --quiet
--repack-flags --user-log-author $remote_opts
--repack-flags --user-log-author --localtime $remote_opts
"
local init_opts="
--template= --shared= --trunk= --tags=
@@ -1672,7 +1698,6 @@ _git ()
if [ -z "$command" ]; then
case "${COMP_WORDS[COMP_CWORD]}" in
--*=*) COMPREPLY=() ;;
--*) __gitcomp "
--paginate
--no-pager
@@ -1736,6 +1761,7 @@ _git ()
show) _git_show ;;
show-branch) _git_show_branch ;;
stash) _git_stash ;;
stage) _git_add ;;
submodule) _git_submodule ;;
svn) _git_svn ;;
tag) _git_tag ;;
@@ -1763,13 +1789,16 @@ _gitk ()
__git_complete_revlist
}
complete -o default -o nospace -F _git git
complete -o default -o nospace -F _gitk gitk
complete -o bashdefault -o default -o nospace -F _git git 2>/dev/null \
|| complete -o default -o nospace -F _git git
complete -o bashdefault -o default -o nospace -F _gitk gitk 2>/dev/null \
|| complete -o default -o nospace -F _gitk gitk
# The following are necessary only for Cygwin, and only are needed
# when the user has tab-completed the executable name and consequently
# included the '.exe' suffix.
#
if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
complete -o default -o nospace -F _git git.exe
complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
|| complete -o default -o nospace -F _git git.exe
fi

73
contrib/difftool/git-difftool Executable file
View File

@@ -0,0 +1,73 @@
#!/usr/bin/env perl
# Copyright (c) 2009 David Aguilar
#
# This is a wrapper around the GIT_EXTERNAL_DIFF-compatible
# git-difftool-helper script. This script exports
# GIT_EXTERNAL_DIFF and GIT_PAGER for use by git, and
# GIT_DIFFTOOL_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper.
# Any arguments that are unknown to this script are forwarded to 'git diff'.
use strict;
use warnings;
use Cwd qw(abs_path);
use File::Basename qw(dirname);
my $DIR = abs_path(dirname($0));
sub usage
{
print << 'USAGE';
usage: git difftool [--tool=<tool>] [--no-prompt] ["git diff" options]
USAGE
exit 1;
}
sub setup_environment
{
$ENV{PATH} = "$DIR:$ENV{PATH}";
$ENV{GIT_PAGER} = '';
$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool-helper';
}
sub exe
{
my $exe = shift;
return defined $ENV{COMSPEC} ? "$exe.exe" : $exe;
}
sub generate_command
{
my @command = (exe('git'), 'diff');
my $skip_next = 0;
my $idx = -1;
for my $arg (@ARGV) {
$idx++;
if ($skip_next) {
$skip_next = 0;
next;
}
if ($arg eq '-t' or $arg eq '--tool') {
usage() if $#ARGV <= $idx;
$ENV{GIT_MERGE_TOOL} = $ARGV[$idx + 1];
$skip_next = 1;
next;
}
if ($arg =~ /^--tool=/) {
$ENV{GIT_MERGE_TOOL} = substr($arg, 7);
next;
}
if ($arg eq '--no-prompt') {
$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
next;
}
if ($arg eq '-h' or $arg eq '--help') {
usage();
}
push @command, $arg;
}
return @command
}
setup_environment();
exec(generate_command());

View File

@@ -0,0 +1,243 @@
#!/bin/sh
# git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
# It supports kdiff3, tkdiff, xxdiff, meld, opendiff, emerge, ecmerge,
# vimdiff, gvimdiff, and custom user-configurable tools.
# This script is typically launched by using the 'git difftool'
# convenience command.
#
# Copyright (c) 2009 David Aguilar
# Set GIT_DIFFTOOL_NO_PROMPT to bypass the per-file prompt.
should_prompt () {
! test -n "$GIT_DIFFTOOL_NO_PROMPT"
}
# Should we keep the backup .orig file?
keep_backup_mode="$(git config --bool merge.keepBackup || echo true)"
keep_backup () {
test "$keep_backup_mode" = "true"
}
# This function manages the backup .orig file.
# A backup $MERGED.orig file is created if changes are detected.
cleanup_temp_files () {
if test -n "$MERGED"; then
if keep_backup && test "$MERGED" -nt "$BACKUP"; then
test -f "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
else
rm -f -- "$BACKUP"
fi
fi
}
# This is called when users Ctrl-C out of git-difftool-helper
sigint_handler () {
cleanup_temp_files
exit 1
}
# This function prepares temporary files and launches the appropriate
# merge tool.
launch_merge_tool () {
# Merged is the filename as it appears in the work tree
# Local is the contents of a/filename
# Remote is the contents of b/filename
# Custom merge tool commands might use $BASE so we provide it
MERGED="$1"
LOCAL="$2"
REMOTE="$3"
BASE="$1"
ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
BACKUP="$MERGED.BACKUP.$ext"
# Create and ensure that we clean up $BACKUP
test -f "$MERGED" && cp -- "$MERGED" "$BACKUP"
trap sigint_handler INT
# $LOCAL and $REMOTE are temporary files so prompt
# the user with the real $MERGED name before launching $merge_tool.
if should_prompt; then
printf "\nViewing: '$MERGED'\n"
printf "Hit return to launch '%s': " "$merge_tool"
read ans
fi
# Run the appropriate merge tool command
case "$merge_tool" in
kdiff3)
basename=$(basename "$MERGED")
"$merge_tool_path" --auto \
--L1 "$basename (A)" \
--L2 "$basename (B)" \
-o "$MERGED" "$LOCAL" "$REMOTE" \
> /dev/null 2>&1
;;
tkdiff)
"$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
;;
meld)
"$merge_tool_path" "$LOCAL" "$REMOTE"
;;
vimdiff)
"$merge_tool_path" -c "wincmd l" "$LOCAL" "$REMOTE"
;;
gvimdiff)
"$merge_tool_path" -c "wincmd l" -f "$LOCAL" "$REMOTE"
;;
xxdiff)
"$merge_tool_path" \
-X \
-R 'Accel.SaveAsMerged: "Ctrl-S"' \
-R 'Accel.Search: "Ctrl+F"' \
-R 'Accel.SearchForward: "Ctrl-G"' \
--merged-file "$MERGED" \
"$LOCAL" "$REMOTE"
;;
opendiff)
"$merge_tool_path" "$LOCAL" "$REMOTE" \
-merge "$MERGED" | cat
;;
ecmerge)
"$merge_tool_path" "$LOCAL" "$REMOTE" \
--default --mode=merge2 --to="$MERGED"
;;
emerge)
"$merge_tool_path" -f emerge-files-command \
"$LOCAL" "$REMOTE" "$(basename "$MERGED")"
;;
*)
if test -n "$merge_tool_cmd"; then
( eval $merge_tool_cmd )
fi
;;
esac
cleanup_temp_files
}
# Verifies that mergetool.<tool>.cmd exists
valid_custom_tool() {
merge_tool_cmd="$(git config mergetool.$1.cmd)"
test -n "$merge_tool_cmd"
}
# Verifies that the chosen merge tool is properly setup.
# Built-in merge tools are always valid.
valid_tool() {
case "$1" in
kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
;; # happy
*)
if ! valid_custom_tool "$1"
then
return 1
fi
;;
esac
}
# Sets up the merge_tool_path variable.
# This handles the mergetool.<tool>.path configuration.
init_merge_tool_path() {
merge_tool_path=$(git config mergetool."$1".path)
if test -z "$merge_tool_path"; then
case "$1" in
emerge)
merge_tool_path=emacs
;;
*)
merge_tool_path="$1"
;;
esac
fi
}
# Allow the GIT_MERGE_TOOL variable to provide a default value
test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
# If not merge tool was specified then use the merge.tool
# configuration variable. If that's invalid then reset merge_tool.
if test -z "$merge_tool"; then
merge_tool=$(git config merge.tool)
if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then
echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
echo >&2 "Resetting to default..."
unset merge_tool
fi
fi
# Try to guess an appropriate merge tool if no tool has been set.
if test -z "$merge_tool"; then
# We have a $DISPLAY so try some common UNIX merge tools
if test -n "$DISPLAY"; then
merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
# If gnome then prefer meld
if test -n "$GNOME_DESKTOP_SESSION_ID"; then
merge_tool_candidates="meld $merge_tool_candidates"
fi
# If KDE then prefer kdiff3
if test "$KDE_FULL_SESSION" = "true"; then
merge_tool_candidates="kdiff3 $merge_tool_candidates"
fi
fi
# $EDITOR is emacs so add emerge as a candidate
if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
merge_tool_candidates="$merge_tool_candidates emerge"
fi
# $EDITOR is vim so add vimdiff as a candidate
if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
merge_tool_candidates="$merge_tool_candidates vimdiff"
fi
merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
echo "merge tool candidates: $merge_tool_candidates"
# Loop over each candidate and stop when a valid merge tool is found.
for i in $merge_tool_candidates
do
init_merge_tool_path $i
if type "$merge_tool_path" > /dev/null 2>&1; then
merge_tool=$i
break
fi
done
if test -z "$merge_tool" ; then
echo "No known merge resolution program available."
exit 1
fi
else
# A merge tool has been set, so verify that it's valid.
if ! valid_tool "$merge_tool"; then
echo >&2 "Unknown merge tool $merge_tool"
exit 1
fi
init_merge_tool_path "$merge_tool"
if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
exit 1
fi
fi
# Launch the merge tool on each path provided by 'git diff'
while test $# -gt 6
do
launch_merge_tool "$1" "$2" "$5"
shift 7
done

View File

@@ -0,0 +1,104 @@
git-difftool(1)
===============
NAME
----
git-difftool - compare changes using common merge tools
SYNOPSIS
--------
'git difftool' [--tool=<tool>] [--no-prompt] ['git diff' options]
DESCRIPTION
-----------
'git-difftool' is a git command that allows you to compare and edit files
between revisions using common merge tools. At its most basic level,
'git-difftool' does what 'git-mergetool' does but its use is for non-merge
situations such as when preparing commits or comparing changes against
the index.
'git difftool' is a frontend to 'git diff' and accepts the same
arguments and options.
See linkgit:git-diff[1] for the full list of supported options.
OPTIONS
-------
-t <tool>::
--tool=<tool>::
Use the merge resolution program specified by <tool>.
Valid merge tools are:
kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
+
If a merge resolution program is not specified, 'git-difftool'
will use the configuration variable `merge.tool`. If the
configuration variable `merge.tool` is not set, 'git difftool'
will pick a suitable default.
+
You can explicitly provide a full path to the tool by setting the
configuration variable `mergetool.<tool>.path`. For example, you
can configure the absolute path to kdiff3 by setting
`mergetool.kdiff3.path`. Otherwise, 'git-difftool' assumes the
tool is available in PATH.
+
Instead of running one of the known merge tool programs,
'git-difftool' can be customized to run an alternative program
by specifying the command line to invoke in a configuration
variable `mergetool.<tool>.cmd`.
+
When 'git-difftool' is invoked with this tool (either through the
`-t` or `--tool` option or the `merge.tool` configuration variable)
the configured command line will be invoked with the following
variables available: `$LOCAL` is set to the name of the temporary
file containing the contents of the diff pre-image and `$REMOTE`
is set to the name of the temporary file containing the contents
of the diff post-image. `$BASE` is provided for compatibility
with custom merge tool commands and has the same value as `$LOCAL`.
--no-prompt::
Do not prompt before launching a diff tool.
CONFIG VARIABLES
----------------
merge.tool::
The default merge tool to use.
+
See the `--tool=<tool>` option above for more details.
merge.keepBackup::
The original, unedited file content can be saved to a file with
a `.orig` extension. Defaults to `true` (i.e. keep the backup files).
mergetool.<tool>.path::
Override the path for the given tool. This is useful in case
your tool is not in the PATH.
mergetool.<tool>.cmd::
Specify the command to invoke the specified merge tool.
+
See the `--tool=<tool>` option above for more details.
SEE ALSO
--------
linkgit:git-diff[1]::
Show changes between commits, commit and working tree, etc
linkgit:git-mergetool[1]::
Run merge conflict resolution tools to resolve merge conflicts
linkgit:git-config[1]::
Get and set repository or global options
AUTHOR
------
Written by David Aguilar <davvid@gmail.com>.
Documentation
--------------
Documentation by David Aguilar and the git-list <git@vger.kernel.org>.
GIT
---
Part of the linkgit:git[1] suite

3
contrib/examples/README Normal file
View File

@@ -0,0 +1,3 @@
These are original scripted implementations, kept primarily for their
reference value to any aspiring plumbing users who want to learn how
pieces can be fit together.

View File

@@ -5,11 +5,13 @@ automatically.
If you have an older version of vim, you can get the latest syntax
files from the vim project:
http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/git.vim
http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitcommit.vim
http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitconfig.vim
http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitrebase.vim
http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitsendemail.vim
http://ftp.vim.org/pub/vim/runtime/syntax/git.vim
http://ftp.vim.org/pub/vim/runtime/syntax/gitcommit.vim
http://ftp.vim.org/pub/vim/runtime/syntax/gitconfig.vim
http://ftp.vim.org/pub/vim/runtime/syntax/gitrebase.vim
http://ftp.vim.org/pub/vim/runtime/syntax/gitsendemail.vim
These files are also available via FTP at the same location.
To install:

33
ctype.c
View File

@@ -5,25 +5,22 @@
*/
#include "cache.h"
/* Just so that no insane platform contaminate namespace with these symbols */
#undef SS
#undef AA
#undef DD
#undef GS
#define SS GIT_SPACE
#define AA GIT_ALPHA
#define DD GIT_DIGIT
#define GS GIT_SPECIAL /* \0, *, ?, [, \\ */
enum {
S = GIT_SPACE,
A = GIT_ALPHA,
D = GIT_DIGIT,
G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */
R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | * */
};
unsigned char sane_ctype[256] = {
GS, 0, 0, 0, 0, 0, 0, 0, 0, SS, SS, 0, 0, SS, 0, 0, /* 0-15 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-15 */
SS, 0, 0, 0, 0, 0, 0, 0, 0, 0, GS, 0, 0, 0, 0, 0, /* 32-15 */
DD, DD, DD, DD, DD, DD, DD, DD, DD, DD, 0, 0, 0, 0, 0, GS, /* 48-15 */
0, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, /* 64-15 */
AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, GS, GS, 0, 0, 0, /* 80-15 */
0, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, /* 96-15 */
AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, 0, 0, 0, 0, 0, /* 112-15 */
0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0, /* 0.. 15 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16.. 31 */
S, 0, 0, 0, R, 0, 0, 0, R, R, G, R, 0, 0, R, 0, /* 32.. 47 */
D, D, D, D, D, D, D, D, D, D, 0, 0, 0, 0, 0, G, /* 48.. 63 */
0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */
A, A, A, A, A, A, A, A, A, A, A, G, G, 0, R, 0, /* 80.. 95 */
0, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */
A, A, A, A, A, A, A, A, A, A, A, R, R, 0, 0, 0, /* 112..127 */
/* Nothing in the 128.. range */
};

View File

@@ -150,7 +150,6 @@ static char *path_ok(char *directory)
{
static char rpath[PATH_MAX];
static char interp_path[PATH_MAX];
int retried_path = 0;
char *path;
char *dir;
@@ -219,22 +218,15 @@ static char *path_ok(char *directory)
dir = rpath;
}
do {
path = enter_repo(dir, strict_paths);
if (path)
break;
path = enter_repo(dir, strict_paths);
if (!path && base_path && base_path_relaxed) {
/*
* if we fail and base_path_relaxed is enabled, try without
* prefixing the base path
*/
if (base_path && base_path_relaxed && !retried_path) {
dir = directory;
retried_path = 1;
continue;
}
break;
} while (1);
dir = directory;
path = enter_repo(dir, strict_paths);
}
if (!path) {
logerror("'%s': unable to chdir or not a git archive", dir);
@@ -405,6 +397,14 @@ static void make_service_overridable(const char *name, int ena)
die("No such service %s", name);
}
static char *xstrdup_tolower(const char *str)
{
char *p, *dup = xstrdup(str);
for (p = dup; *p; p++)
*p = tolower(*p);
return dup;
}
/*
* Separate the "extra args" information as supplied by the client connection.
*/
@@ -413,7 +413,6 @@ static void parse_extra_args(char *extra_args, int buflen)
char *val;
int vallen;
char *end = extra_args + buflen;
char *hp;
while (extra_args < end && *extra_args) {
saw_extended_args = 1;
@@ -431,7 +430,7 @@ static void parse_extra_args(char *extra_args, int buflen)
tcp_port = xstrdup(port);
}
free(hostname);
hostname = xstrdup(host);
hostname = xstrdup_tolower(host);
}
/* On to the next one */
@@ -439,20 +438,11 @@ static void parse_extra_args(char *extra_args, int buflen)
}
}
/*
* Replace literal host with lowercase-ized hostname.
*/
hp = hostname;
if (!hp)
return;
for ( ; *hp; hp++)
*hp = tolower(*hp);
/*
* Locate canonical hostname and its IP address.
*/
if (hostname) {
#ifndef NO_IPV6
{
struct addrinfo hints;
struct addrinfo *ai, *ai0;
int gai;
@@ -476,9 +466,7 @@ static void parse_extra_args(char *extra_args, int buflen)
}
freeaddrinfo(ai0);
}
}
#else
{
struct hostent *hent;
struct sockaddr_in sa;
char **ap;
@@ -499,8 +487,8 @@ static void parse_extra_args(char *extra_args, int buflen)
canon_hostname = xstrdup(hent->h_name);
free(ip_address);
ip_address = xstrdup(addrbuf);
}
#endif
}
}
@@ -728,7 +716,7 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
gai = getaddrinfo(listen_addr, pbuf, &hints, &ai0);
if (gai)
die("getaddrinfo() failed: %s\n", gai_strerror(gai));
die("getaddrinfo() failed: %s", gai_strerror(gai));
for (ai = ai0; ai; ai = ai->ai_next) {
int sockfd;
@@ -949,19 +937,14 @@ int main(int argc, char **argv)
gid_t gid = 0;
int i;
if (argv[0] && *argv[0])
git_extract_argv0_path(argv[0]);
git_extract_argv0_path(argv[0]);
for (i = 1; i < argc; i++) {
char *arg = argv[i];
if (!prefixcmp(arg, "--listen=")) {
char *p = arg + 9;
char *ph = listen_addr = xmalloc(strlen(arg + 9) + 1);
while (*p)
*ph++ = tolower(*p++);
*ph = 0;
continue;
listen_addr = xstrdup_tolower(arg + 9);
continue;
}
if (!prefixcmp(arg, "--port=")) {
char *end;
@@ -1121,7 +1104,9 @@ int main(int argc, char **argv)
struct sockaddr *peer = (struct sockaddr *)&ss;
socklen_t slen = sizeof(ss);
freopen("/dev/null", "w", stderr);
if (!freopen("/dev/null", "w", stderr))
die("failed to redirect stderr to /dev/null: %s",
strerror(errno));
if (getpeername(0, peer, &slen))
peer = NULL;

View File

@@ -61,14 +61,12 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
int silent_on_removed = option & DIFF_SILENT_ON_REMOVED;
unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED)
? CE_MATCH_RACY_IS_DIRTY : 0);
char symcache[PATH_MAX];
diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/");
if (diff_unmerged_stage < 0)
diff_unmerged_stage = 2;
entries = active_nr;
symcache[0] = '\0';
for (i = 0; i < entries; i++) {
struct stat st;
unsigned int oldmode, newmode;
@@ -198,11 +196,6 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
* diff-index
*/
struct oneway_unpack_data {
struct rev_info *revs;
char symcache[PATH_MAX];
};
/* A file entry went away or appeared */
static void diff_index_show_file(struct rev_info *revs,
const char *prefix,
@@ -216,8 +209,7 @@ static void diff_index_show_file(struct rev_info *revs,
static int get_stat_data(struct cache_entry *ce,
const unsigned char **sha1p,
unsigned int *modep,
int cached, int match_missing,
struct oneway_unpack_data *cbdata)
int cached, int match_missing)
{
const unsigned char *sha1 = ce->sha1;
unsigned int mode = ce->ce_mode;
@@ -248,25 +240,24 @@ static int get_stat_data(struct cache_entry *ce,
return 0;
}
static void show_new_file(struct oneway_unpack_data *cbdata,
static void show_new_file(struct rev_info *revs,
struct cache_entry *new,
int cached, int match_missing)
{
const unsigned char *sha1;
unsigned int mode;
struct rev_info *revs = cbdata->revs;
/*
* New file in the index: it might actually be different in
* the working copy.
*/
if (get_stat_data(new, &sha1, &mode, cached, match_missing, cbdata) < 0)
if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0)
return;
diff_index_show_file(revs, "+", new, sha1, mode);
}
static int show_modified(struct oneway_unpack_data *cbdata,
static int show_modified(struct rev_info *revs,
struct cache_entry *old,
struct cache_entry *new,
int report_missing,
@@ -274,9 +265,8 @@ static int show_modified(struct oneway_unpack_data *cbdata,
{
unsigned int mode, oldmode;
const unsigned char *sha1;
struct rev_info *revs = cbdata->revs;
if (get_stat_data(new, &sha1, &mode, cached, match_missing, cbdata) < 0) {
if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) {
if (report_missing)
diff_index_show_file(revs, "-", old,
old->sha1, old->ce_mode);
@@ -344,8 +334,7 @@ static void do_oneway_diff(struct unpack_trees_options *o,
struct cache_entry *idx,
struct cache_entry *tree)
{
struct oneway_unpack_data *cbdata = o->unpack_data;
struct rev_info *revs = cbdata->revs;
struct rev_info *revs = o->unpack_data;
int match_missing, cached;
/*
@@ -368,7 +357,7 @@ static void do_oneway_diff(struct unpack_trees_options *o,
* Something added to the tree?
*/
if (!tree) {
show_new_file(cbdata, idx, cached, match_missing);
show_new_file(revs, idx, cached, match_missing);
return;
}
@@ -381,7 +370,7 @@ static void do_oneway_diff(struct unpack_trees_options *o,
}
/* Show difference between old and new */
show_modified(cbdata, tree, idx, 1, cached, match_missing);
show_modified(revs, tree, idx, 1, cached, match_missing);
}
static inline void skip_same_name(struct cache_entry *ce, struct unpack_trees_options *o)
@@ -418,8 +407,7 @@ static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
{
struct cache_entry *idx = src[0];
struct cache_entry *tree = src[1];
struct oneway_unpack_data *cbdata = o->unpack_data;
struct rev_info *revs = cbdata->revs;
struct rev_info *revs = o->unpack_data;
if (idx && ce_stage(idx))
skip_same_name(idx, o);
@@ -446,7 +434,6 @@ int run_diff_index(struct rev_info *revs, int cached)
const char *tree_name;
struct unpack_trees_options opts;
struct tree_desc t;
struct oneway_unpack_data unpack_cb;
mark_merge_entries();
@@ -456,14 +443,12 @@ int run_diff_index(struct rev_info *revs, int cached)
if (!tree)
return error("bad tree object %s", tree_name);
unpack_cb.revs = revs;
unpack_cb.symcache[0] = '\0';
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
opts.index_only = cached;
opts.merge = 1;
opts.fn = oneway_diff;
opts.unpack_data = &unpack_cb;
opts.unpack_data = revs;
opts.src_index = &the_index;
opts.dst_index = NULL;
@@ -486,7 +471,6 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
struct cache_entry *last = NULL;
struct unpack_trees_options opts;
struct tree_desc t;
struct oneway_unpack_data unpack_cb;
/*
* This is used by git-blame to run diff-cache internally;
@@ -515,14 +499,12 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
if (!tree)
die("bad tree object %s", sha1_to_hex(tree_sha1));
unpack_cb.revs = &revs;
unpack_cb.symcache[0] = '\0';
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
opts.index_only = 1;
opts.merge = 1;
opts.fn = oneway_diff;
opts.unpack_data = &unpack_cb;
opts.unpack_data = &revs;
opts.src_index = &the_index;
opts.dst_index = &the_index;

View File

@@ -173,8 +173,10 @@ void diff_no_index(struct rev_info *revs,
/* Were we asked to do --no-index explicitly? */
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--"))
return;
if (!strcmp(argv[i], "--")) {
i++;
break;
}
if (!strcmp(argv[i], "--no-index"))
no_index = 1;
if (argv[i][0] != '-')
@@ -198,13 +200,6 @@ void diff_no_index(struct rev_info *revs,
die("git diff %s takes two paths",
no_index ? "--no-index" : "[--no-index]");
/*
* If the user asked for our exit code then don't start a
* pager or we would end up reporting its exit code instead.
*/
if (!DIFF_OPT_TST(&revs->diffopt, EXIT_WITH_STATUS))
setup_pager();
diff_setup(&revs->diffopt);
if (!revs->diffopt.output_format)
revs->diffopt.output_format = DIFF_FORMAT_PATCH;
@@ -212,8 +207,12 @@ void diff_no_index(struct rev_info *revs,
int j;
if (!strcmp(argv[i], "--no-index"))
i++;
else if (!strcmp(argv[1], "-q"))
else if (!strcmp(argv[i], "-q")) {
options |= DIFF_SILENT_ON_REMOVED;
i++;
}
else if (!strcmp(argv[i], "--"))
i++;
else {
j = diff_opt_parse(&revs->diffopt, argv + i, argc - i);
if (!j)
@@ -222,6 +221,13 @@ void diff_no_index(struct rev_info *revs,
}
}
/*
* If the user asked for our exit code then don't start a
* pager or we would end up reporting its exit code instead.
*/
if (!DIFF_OPT_TST(&revs->diffopt, EXIT_WITH_STATUS))
setup_pager();
if (prefix) {
int len = strlen(prefix);

251
diff.c
View File

@@ -23,6 +23,7 @@ static int diff_detect_rename_default;
static int diff_rename_limit_default = 200;
static int diff_suppress_blank_empty;
int diff_use_color_default = -1;
static const char *diff_word_regex_cfg;
static const char *external_diff_cmd_cfg;
int diff_auto_refresh_index = 1;
static int diff_mnemonic_prefix;
@@ -92,6 +93,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
}
if (!strcmp(var, "diff.external"))
return git_config_string(&external_diff_cmd_cfg, var, value);
if (!strcmp(var, "diff.wordregex"))
return git_config_string(&diff_word_regex_cfg, var, value);
return git_diff_basic_config(var, value, cb);
}
@@ -118,7 +121,9 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
}
/* like GNU diff's --suppress-blank-empty option */
if (!strcmp(var, "diff.suppress-blank-empty")) {
if (!strcmp(var, "diff.suppressblankempty") ||
/* for backwards compatibility */
!strcmp(var, "diff.suppress-blank-empty")) {
diff_suppress_blank_empty = git_config_bool(var, value);
return 0;
}
@@ -319,82 +324,138 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
struct diff_words_buffer {
mmfile_t text;
long alloc;
long current; /* output pointer */
int suppressed_newline;
struct diff_words_orig {
const char *begin, *end;
} *orig;
int orig_nr, orig_alloc;
};
static void diff_words_append(char *line, unsigned long len,
struct diff_words_buffer *buffer)
{
if (buffer->text.size + len > buffer->alloc) {
buffer->alloc = (buffer->text.size + len) * 3 / 2;
buffer->text.ptr = xrealloc(buffer->text.ptr, buffer->alloc);
}
ALLOC_GROW(buffer->text.ptr, buffer->text.size + len, buffer->alloc);
line++;
len--;
memcpy(buffer->text.ptr + buffer->text.size, line, len);
buffer->text.size += len;
buffer->text.ptr[buffer->text.size] = '\0';
}
struct diff_words_data {
struct diff_words_buffer minus, plus;
const char *current_plus;
FILE *file;
regex_t *word_regex;
};
static void print_word(FILE *file, struct diff_words_buffer *buffer, int len, int color,
int suppress_newline)
{
const char *ptr;
int eol = 0;
if (len == 0)
return;
ptr = buffer->text.ptr + buffer->current;
buffer->current += len;
if (ptr[len - 1] == '\n') {
eol = 1;
len--;
}
fputs(diff_get_color(1, color), file);
fwrite(ptr, len, 1, file);
fputs(diff_get_color(1, DIFF_RESET), file);
if (eol) {
if (suppress_newline)
buffer->suppressed_newline = 1;
else
putc('\n', file);
}
}
static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
{
struct diff_words_data *diff_words = priv;
int minus_first, minus_len, plus_first, plus_len;
const char *minus_begin, *minus_end, *plus_begin, *plus_end;
if (diff_words->minus.suppressed_newline) {
if (line[0] != '+')
putc('\n', diff_words->file);
diff_words->minus.suppressed_newline = 0;
if (line[0] != '@' || parse_hunk_header(line, len,
&minus_first, &minus_len, &plus_first, &plus_len))
return;
/* POSIX requires that first be decremented by one if len == 0... */
if (minus_len) {
minus_begin = diff_words->minus.orig[minus_first].begin;
minus_end =
diff_words->minus.orig[minus_first + minus_len - 1].end;
} else
minus_begin = minus_end =
diff_words->minus.orig[minus_first].end;
if (plus_len) {
plus_begin = diff_words->plus.orig[plus_first].begin;
plus_end = diff_words->plus.orig[plus_first + plus_len - 1].end;
} else
plus_begin = plus_end = diff_words->plus.orig[plus_first].end;
if (diff_words->current_plus != plus_begin)
fwrite(diff_words->current_plus,
plus_begin - diff_words->current_plus, 1,
diff_words->file);
if (minus_begin != minus_end)
color_fwrite_lines(diff_words->file,
diff_get_color(1, DIFF_FILE_OLD),
minus_end - minus_begin, minus_begin);
if (plus_begin != plus_end)
color_fwrite_lines(diff_words->file,
diff_get_color(1, DIFF_FILE_NEW),
plus_end - plus_begin, plus_begin);
diff_words->current_plus = plus_end;
}
/* This function starts looking at *begin, and returns 0 iff a word was found. */
static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex,
int *begin, int *end)
{
if (word_regex && *begin < buffer->size) {
regmatch_t match[1];
if (!regexec(word_regex, buffer->ptr + *begin, 1, match, 0)) {
char *p = memchr(buffer->ptr + *begin + match[0].rm_so,
'\n', match[0].rm_eo - match[0].rm_so);
*end = p ? p - buffer->ptr : match[0].rm_eo + *begin;
*begin += match[0].rm_so;
return *begin >= *end;
}
return -1;
}
len--;
switch (line[0]) {
case '-':
print_word(diff_words->file,
&diff_words->minus, len, DIFF_FILE_OLD, 1);
break;
case '+':
print_word(diff_words->file,
&diff_words->plus, len, DIFF_FILE_NEW, 0);
break;
case ' ':
print_word(diff_words->file,
&diff_words->plus, len, DIFF_PLAIN, 0);
diff_words->minus.current += len;
break;
/* find the next word */
while (*begin < buffer->size && isspace(buffer->ptr[*begin]))
(*begin)++;
if (*begin >= buffer->size)
return -1;
/* find the end of the word */
*end = *begin + 1;
while (*end < buffer->size && !isspace(buffer->ptr[*end]))
(*end)++;
return 0;
}
/*
* This function splits the words in buffer->text, stores the list with
* newline separator into out, and saves the offsets of the original words
* in buffer->orig.
*/
static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out,
regex_t *word_regex)
{
int i, j;
long alloc = 0;
out->size = 0;
out->ptr = NULL;
/* fake an empty "0th" word */
ALLOC_GROW(buffer->orig, 1, buffer->orig_alloc);
buffer->orig[0].begin = buffer->orig[0].end = buffer->text.ptr;
buffer->orig_nr = 1;
for (i = 0; i < buffer->text.size; i++) {
if (find_word_boundaries(&buffer->text, word_regex, &i, &j))
return;
/* store original boundaries */
ALLOC_GROW(buffer->orig, buffer->orig_nr + 1,
buffer->orig_alloc);
buffer->orig[buffer->orig_nr].begin = buffer->text.ptr + i;
buffer->orig[buffer->orig_nr].end = buffer->text.ptr + j;
buffer->orig_nr++;
/* store one word */
ALLOC_GROW(out->ptr, out->size + j - i + 1, alloc);
memcpy(out->ptr + out->size, buffer->text.ptr + i, j - i);
out->ptr[out->size + j - i] = '\n';
out->size += j - i + 1;
i = j - 1;
}
}
@@ -405,38 +466,36 @@ static void diff_words_show(struct diff_words_data *diff_words)
xdemitconf_t xecfg;
xdemitcb_t ecb;
mmfile_t minus, plus;
int i;
/* special case: only removal */
if (!diff_words->plus.text.size) {
color_fwrite_lines(diff_words->file,
diff_get_color(1, DIFF_FILE_OLD),
diff_words->minus.text.size, diff_words->minus.text.ptr);
diff_words->minus.text.size = 0;
return;
}
diff_words->current_plus = diff_words->plus.text.ptr;
memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
minus.size = diff_words->minus.text.size;
minus.ptr = xmalloc(minus.size);
memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size);
for (i = 0; i < minus.size; i++)
if (isspace(minus.ptr[i]))
minus.ptr[i] = '\n';
diff_words->minus.current = 0;
plus.size = diff_words->plus.text.size;
plus.ptr = xmalloc(plus.size);
memcpy(plus.ptr, diff_words->plus.text.ptr, plus.size);
for (i = 0; i < plus.size; i++)
if (isspace(plus.ptr[i]))
plus.ptr[i] = '\n';
diff_words->plus.current = 0;
diff_words_fill(&diff_words->minus, &minus, diff_words->word_regex);
diff_words_fill(&diff_words->plus, &plus, diff_words->word_regex);
xpp.flags = XDF_NEED_MINIMAL;
xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
/* as only the hunk header will be parsed, we need a 0-context */
xecfg.ctxlen = 0;
xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
&xpp, &xecfg, &ecb);
free(minus.ptr);
free(plus.ptr);
if (diff_words->current_plus != diff_words->plus.text.ptr +
diff_words->plus.text.size)
fwrite(diff_words->current_plus,
diff_words->plus.text.ptr + diff_words->plus.text.size
- diff_words->current_plus, 1,
diff_words->file);
diff_words->minus.text.size = diff_words->plus.text.size = 0;
if (diff_words->minus.suppressed_newline) {
putc('\n', diff_words->file);
diff_words->minus.suppressed_newline = 0;
}
}
typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
@@ -460,7 +519,10 @@ static void free_diff_words_data(struct emit_callback *ecbdata)
diff_words_show(ecbdata->diff_words);
free (ecbdata->diff_words->minus.text.ptr);
free (ecbdata->diff_words->minus.orig);
free (ecbdata->diff_words->plus.text.ptr);
free (ecbdata->diff_words->plus.orig);
free(ecbdata->diff_words->word_regex);
free(ecbdata->diff_words);
ecbdata->diff_words = NULL;
}
@@ -1323,6 +1385,12 @@ static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespe
return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
}
static const char *userdiff_word_regex(struct diff_filespec *one)
{
diff_filespec_load_driver(one);
return one->driver->word_regex;
}
void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
{
if (!options->a_prefix)
@@ -1469,6 +1537,7 @@ static void builtin_diff(const char *name_a,
ecbdata.file = o->file;
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
xecfg.ctxlen = o->context;
xecfg.interhunkctxlen = o->interhunkcontext;
xecfg.flags = XDL_EMIT_FUNCNAMES;
if (pe)
xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
@@ -1482,6 +1551,21 @@ static void builtin_diff(const char *name_a,
ecbdata.diff_words =
xcalloc(1, sizeof(struct diff_words_data));
ecbdata.diff_words->file = o->file;
if (!o->word_regex)
o->word_regex = userdiff_word_regex(one);
if (!o->word_regex)
o->word_regex = userdiff_word_regex(two);
if (!o->word_regex)
o->word_regex = diff_word_regex_cfg;
if (o->word_regex) {
ecbdata.diff_words->word_regex = (regex_t *)
xmalloc(sizeof(regex_t));
if (regcomp(ecbdata.diff_words->word_regex,
o->word_regex,
REG_EXTENDED | REG_NEWLINE))
die ("Invalid regular expression: %s",
o->word_regex);
}
}
xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
&xpp, &xecfg, &ecb);
@@ -2039,7 +2123,7 @@ static void diff_fill_sha1_info(struct diff_filespec *one)
if (lstat(one->path, &st) < 0)
die("stat %s", one->path);
if (index_path(one->sha1, one->path, &st, 0))
die("cannot hash %s\n", one->path);
die("cannot hash %s", one->path);
}
}
else
@@ -2471,6 +2555,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
else if (!strcmp(arg, "--ignore-space-at-eol"))
options->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL;
else if (!strcmp(arg, "--patience"))
options->xdl_opts |= XDF_PATIENCE_DIFF;
/* flags options */
else if (!strcmp(arg, "--binary")) {
@@ -2493,6 +2579,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
DIFF_OPT_CLR(options, COLOR_DIFF);
else if (!strcmp(arg, "--color-words"))
options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS;
else if (!prefixcmp(arg, "--color-words=")) {
options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS;
options->word_regex = arg + 14;
}
else if (!strcmp(arg, "--exit-code"))
DIFF_OPT_SET(options, EXIT_WITH_STATUS);
else if (!strcmp(arg, "--quiet"))
@@ -2538,6 +2628,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
options->b_prefix = arg + 13;
else if (!strcmp(arg, "--no-prefix"))
options->a_prefix = options->b_prefix = "";
else if (opt_arg(arg, '\0', "inter-hunk-context",
&options->interhunkcontext))
;
else if (!prefixcmp(arg, "--output=")) {
options->file = fopen(arg + strlen("--output="), "w");
options->close_file = 1;

2
diff.h
View File

@@ -78,6 +78,7 @@ struct diff_options {
const char *a_prefix, *b_prefix;
unsigned flags;
int context;
int interhunkcontext;
int break_opt;
int detect_rename;
int skip_stat_unmatch;
@@ -97,6 +98,7 @@ struct diff_options {
int stat_width;
int stat_name_width;
const char *word_regex;
/* this is set by diffcore for DIFF_FORMAT_PATCH */
int found_changes;

View File

@@ -153,9 +153,9 @@ static int estimate_similarity(struct diff_filespec *src,
* is a possible size - we really should have a flag to
* say whether the size is valid or not!)
*/
if (!src->cnt_data && diff_populate_filespec(src, 0))
if (!src->cnt_data && diff_populate_filespec(src, 1))
return 0;
if (!dst->cnt_data && diff_populate_filespec(dst, 0))
if (!dst->cnt_data && diff_populate_filespec(dst, 1))
return 0;
max_size = ((src->size > dst->size) ? src->size : dst->size);
@@ -173,6 +173,11 @@ static int estimate_similarity(struct diff_filespec *src,
if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
return 0;
if (!src->cnt_data && diff_populate_filespec(src, 0))
return 0;
if (!dst->cnt_data && diff_populate_filespec(dst, 0))
return 0;
delta_limit = (unsigned long)
(base_size * (MAX_SCORE-minimum_score) / MAX_SCORE);
if (diffcore_count_changes(src, dst,

54
dir.c
View File

@@ -75,7 +75,7 @@ static int match_one(const char *match, const char *name, int namelen)
for (;;) {
unsigned char c1 = *match;
unsigned char c2 = *name;
if (isspecial(c1))
if (c1 == '\0' || is_glob_special(c1))
break;
if (c1 != c2)
return 0;
@@ -108,25 +108,28 @@ static int match_one(const char *match, const char *name, int namelen)
* and a mark is left in seen[] array for pathspec element that
* actually matched anything.
*/
int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen)
int match_pathspec(const char **pathspec, const char *name, int namelen,
int prefix, char *seen)
{
int retval;
const char *match;
int i, retval = 0;
if (!pathspec)
return 1;
name += prefix;
namelen -= prefix;
for (retval = 0; (match = *pathspec++) != NULL; seen++) {
for (i = 0; pathspec[i] != NULL; i++) {
int how;
if (retval && *seen == MATCHED_EXACTLY)
const char *match = pathspec[i] + prefix;
if (seen && seen[i] == MATCHED_EXACTLY)
continue;
match += prefix;
how = match_one(match, name, namelen);
if (how) {
if (retval < how)
retval = how;
if (*seen < how)
*seen = how;
if (seen && seen[i] < how)
seen[i] = how;
}
}
return retval;
@@ -585,10 +588,8 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
int len, dtype;
int exclude;
if ((de->d_name[0] == '.') &&
(de->d_name[1] == 0 ||
!strcmp(de->d_name + 1, ".") ||
!strcmp(de->d_name + 1, "git")))
if (is_dot_or_dotdot(de->d_name) ||
!strcmp(de->d_name, ".git"))
continue;
len = strlen(de->d_name);
/* Ignore overly long pathnames! */
@@ -680,7 +681,7 @@ static int simple_length(const char *match)
for (;;) {
unsigned char c = *match++;
len++;
if (isspecial(c))
if (c == '\0' || is_glob_special(c))
return len;
}
}
@@ -779,6 +780,25 @@ int is_inside_dir(const char *dir)
return get_relative_cwd(buffer, sizeof(buffer), dir) != NULL;
}
int is_empty_dir(const char *path)
{
DIR *dir = opendir(path);
struct dirent *e;
int ret = 1;
if (!dir)
return 0;
while ((e = readdir(dir)) != NULL)
if (!is_dot_or_dotdot(e->d_name)) {
ret = 0;
break;
}
closedir(dir);
return ret;
}
int remove_dir_recursively(struct strbuf *path, int only_empty)
{
DIR *dir = opendir(path->buf);
@@ -793,10 +813,8 @@ int remove_dir_recursively(struct strbuf *path, int only_empty)
len = path->len;
while ((e = readdir(dir)) != NULL) {
struct stat st;
if ((e->d_name[0] == '.') &&
((e->d_name[1] == 0) ||
((e->d_name[1] == '.') && e->d_name[2] == 0)))
continue; /* "." and ".." */
if (is_dot_or_dotdot(e->d_name))
continue;
strbuf_setlen(path, len);
strbuf_addstr(path, e->d_name);

9
dir.h
View File

@@ -77,6 +77,15 @@ extern int file_exists(const char *);
extern char *get_relative_cwd(char *buffer, int size, const char *dir);
extern int is_inside_dir(const char *dir);
static inline int is_dot_or_dotdot(const char *name)
{
return (name[0] == '.' &&
(name[1] == '\0' ||
(name[1] == '.' && name[2] == '\0')));
}
extern int is_empty_dir(const char *dir);
extern void setup_standard_excludes(struct dir_struct *dir);
extern int remove_dir_recursively(struct strbuf *path, int only_empty);

39
entry.c
View File

@@ -1,5 +1,6 @@
#include "cache.h"
#include "blob.h"
#include "dir.h"
static void create_directories(const char *path, const struct checkout *state)
{
@@ -8,35 +9,25 @@ static void create_directories(const char *path, const struct checkout *state)
const char *slash = path;
while ((slash = strchr(slash+1, '/')) != NULL) {
struct stat st;
int stat_status;
len = slash - path;
memcpy(buf, path, len);
buf[len] = 0;
if (len <= state->base_dir_len)
/*
* checkout-index --prefix=<dir>; <dir> is
* allowed to be a symlink to an existing
* directory.
*/
stat_status = stat(buf, &st);
else
/*
* if there currently is a symlink, we would
* want to replace it with a real directory.
*/
stat_status = lstat(buf, &st);
if (!stat_status && S_ISDIR(st.st_mode))
/*
* For 'checkout-index --prefix=<dir>', <dir> is
* allowed to be a symlink to an existing directory,
* and we set 'state->base_dir_len' below, such that
* we test the path components of the prefix with the
* stat() function instead of the lstat() function.
*/
if (has_dirs_only_path(len, buf, state->base_dir_len))
continue; /* ok, it is already a directory. */
/*
* We know stat_status == 0 means something exists
* there and this mkdir would fail, but that is an
* error codepath; we do not care, as we unlink and
* mkdir again in such a case.
* If this mkdir() would fail, it could be that there
* is already a symlink or something else exists
* there, therefore we then try to unlink it and try
* one more time to create the directory.
*/
if (mkdir(buf, 0777)) {
if (errno == EEXIST && state->force &&
@@ -62,9 +53,7 @@ static void remove_subtree(const char *path)
*name++ = '/';
while ((de = readdir(dir)) != NULL) {
struct stat st;
if ((de->d_name[0] == '.') &&
((de->d_name[1] == 0) ||
((de->d_name[1] == '.') && de->d_name[2] == 0)))
if (is_dot_or_dotdot(de->d_name))
continue;
strcpy(name, de->d_name);
if (lstat(pathbuf, &st))

View File

@@ -9,11 +9,15 @@ static const char *argv0_path;
const char *system_path(const char *path)
{
#ifdef RUNTIME_PREFIX
static const char *prefix;
#else
static const char *prefix = PREFIX;
#endif
struct strbuf d = STRBUF_INIT;
if (is_absolute_path(path)) {
if (is_absolute_path(path))
return path;
}
#ifdef RUNTIME_PREFIX
assert(argv0_path);
@@ -53,11 +57,8 @@ const char *system_path(const char *path)
"but prefix computation failed. "
"Using static fallback '%s'.\n", prefix);
}
#else
prefix = PREFIX;
#endif
struct strbuf d = STRBUF_INIT;
strbuf_addf(&d, "%s/%s", prefix, path);
path = strbuf_detach(&d, NULL);
return path;
@@ -65,11 +66,14 @@ const char *system_path(const char *path)
const char *git_extract_argv0_path(const char *argv0)
{
const char *slash = argv0 + strlen(argv0);
const char *slash;
do
--slash;
while (slash >= argv0 && !is_dir_sep(*slash));
if (!argv0 || !*argv0)
return NULL;
slash = argv0 + strlen(argv0);
while (argv0 <= slash && !is_dir_sep(*slash))
slash--;
if (slash >= argv0) {
argv0_path = xstrndup(argv0, slash - argv0);

View File

@@ -2,8 +2,8 @@
#define GIT_EXEC_CMD_H
extern void git_set_argv_exec_path(const char *exec_path);
extern const char* git_extract_argv0_path(const char *path);
extern const char* git_exec_path(void);
extern const char *git_extract_argv0_path(const char *path);
extern const char *git_exec_path(void);
extern void setup_path(void);
extern const char **prepare_git_cmd(const char **argv);
extern int execv_git_cmd(const char **argv); /* NULL terminated */

View File

@@ -1873,12 +1873,13 @@ static void file_change_m(struct branch *b)
if (!p)
die("Corrupt mode: %s", command_buf.buf);
switch (mode) {
case 0644:
case 0755:
mode |= S_IFREG;
case S_IFREG | 0644:
case S_IFREG | 0755:
case S_IFLNK:
case S_IFGITLINK:
case 0644:
case 0755:
/* ok */
break;
default:
@@ -1945,7 +1946,7 @@ static void file_change_m(struct branch *b)
typename(type), command_buf.buf);
}
tree_content_set(&b->branch_tree, p, sha1, S_IFREG | mode, NULL);
tree_content_set(&b->branch_tree, p, sha1, mode, NULL);
}
static void file_change_d(struct branch *b)
@@ -2406,8 +2407,7 @@ int main(int argc, const char **argv)
{
unsigned int i, show_stats = 1;
if (argv[0] && *argv[0])
git_extract_argv0_path(argv[0]);
git_extract_argv0_path(argv[0]);
setup_git_directory();
git_config(git_pack_config, NULL);

View File

@@ -800,6 +800,7 @@ y - stage this hunk
n - do not stage this hunk
a - stage this and all the remaining hunks in the file
d - do not stage this hunk nor any of the remaining hunks in the file
g - select a hunk to go to
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
@@ -836,6 +837,47 @@ sub patch_update_cmd {
}
}
# Generate a one line summary of a hunk.
sub summarize_hunk {
my $rhunk = shift;
my $summary = $rhunk->{TEXT}[0];
# Keep the line numbers, discard extra context.
$summary =~ s/@@(.*?)@@.*/$1 /s;
$summary .= " " x (20 - length $summary);
# Add some user context.
for my $line (@{$rhunk->{TEXT}}) {
if ($line =~ m/^[+-].*\w/) {
$summary .= $line;
last;
}
}
chomp $summary;
return substr($summary, 0, 80) . "\n";
}
# Print a one-line summary of each hunk in the array ref in
# the first argument, starting wih the index in the 2nd.
sub display_hunks {
my ($hunks, $i) = @_;
my $ctr = 0;
$i ||= 0;
for (; $i < @$hunks && $ctr < 20; $i++, $ctr++) {
my $status = " ";
if (defined $hunks->[$i]{USE}) {
$status = $hunks->[$i]{USE} ? "+" : "-";
}
printf "%s%2d: %s",
$status,
$i + 1,
summarize_hunk($hunks->[$i]);
}
return $i;
}
sub patch_update_file {
my ($ix, $num);
my $path = shift;
@@ -904,6 +946,9 @@ sub patch_update_file {
if ($ix < $num - 1) {
$other .= '/J';
}
if ($num > 1) {
$other .= '/g';
}
for ($i = 0; $i < $num; $i++) {
if (!defined $hunk[$i]{USE}) {
$undecided = 1;
@@ -937,6 +982,28 @@ sub patch_update_file {
}
next;
}
elsif ($other =~ /g/ && $line =~ /^g(.*)/) {
my $response = $1;
my $no = $ix > 10 ? $ix - 10 : 0;
while ($response eq '') {
my $extra = "";
$no = display_hunks(\@hunk, $no);
if ($no < $num) {
$extra = " (<ret> to see more)";
}
print "go to which hunk$extra? ";
$response = <STDIN>;
chomp $response;
}
if ($response !~ /^\s*\d+\s*$/) {
print STDERR "Invalid number: '$response'\n";
} elsif (0 < $response && $response <= $num) {
$ix = $response - 1;
} else {
print STDERR "Sorry, only $num hunks available.\n";
}
next;
}
elsif ($line =~ /^d/i) {
while ($ix < $num) {
if (!defined $hunk[$ix]{USE}) {

View File

@@ -16,8 +16,10 @@ s,signoff add a Signed-off-by line to the commit message
u,utf8 recode into utf8 (default)
k,keep pass -k flag to git-mailinfo
whitespace= pass it through git-apply
directory= pass it through git-apply
C= pass it through git-apply
p= pass it through git-apply
reject pass it through git-apply
resolvemsg= override error message when patch failure occurs
r,resolved to be used after a patch failure
skip skip the current patch
@@ -33,6 +35,14 @@ cd_to_toplevel
git var GIT_COMMITTER_IDENT >/dev/null ||
die "You need to set your committer info first"
sq () {
for sqarg
do
printf "%s" "$sqarg" |
sed -e 's/'\''/'\''\\'\'''\''/g' -e 's/.*/ '\''&'\''/'
done
}
stop_here () {
echo "$1" >"$dotest/next"
exit 1
@@ -155,10 +165,12 @@ do
;;
--resolvemsg)
shift; resolvemsg=$1 ;;
--whitespace)
git_apply_opt="$git_apply_opt $1=$2"; shift ;;
--whitespace|--directory)
git_apply_opt="$git_apply_opt $(sq "$1=$2")"; shift ;;
-C|-p)
git_apply_opt="$git_apply_opt $1$2"; shift ;;
git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;;
--reject)
git_apply_opt="$git_apply_opt $1" ;;
--)
shift; break ;;
*)
@@ -459,7 +471,7 @@ do
case "$resolved" in
'')
git apply $git_apply_opt --index "$dotest/patch"
eval 'git apply '"$git_apply_opt"' --index "$dotest/patch"'
apply_status=$?
;;
t)
@@ -501,7 +513,7 @@ do
fi
if test $apply_status != 0
then
echo Patch failed at $msgnum.
printf 'Patch failed at %s %s\n' "$msgnum" "$FIRSTLINE"
stop_here_user_resolve $this
fi

Some files were not shown because too many files have changed in this diff Show More